UNPKG

@evg-b/evg-ui

Version:

EVG-UI library inspired by Material Design.

361 lines (318 loc) 10.4 kB
import _extends from '@babel/runtime/helpers/extends'; import _slicedToArray from '@babel/runtime/helpers/slicedToArray'; import _objectWithoutProperties from '@babel/runtime/helpers/objectWithoutProperties'; import React, { useRef, useState, useCallback, useEffect } from 'react'; import PropTypes from 'prop-types'; import classNames from 'classnames'; import { createPortal } from 'react-dom'; import withStyles from '../styles/withStyles.js'; import '@babel/runtime/helpers/construct'; import '@babel/runtime/helpers/toConsumableArray'; import '@babel/runtime/helpers/defineProperty'; import '@babel/runtime/helpers/classCallCheck'; import '@babel/runtime/helpers/createClass'; import getParentsNode from '../utils/dom/getParentsNode.js'; import getIsScrollParentNode from '../utils/dom/getIsScrollParentNode.js'; var styles = { base: { zIndex: 1400, position: 'absolute', top: 0, left: 0, visibility: 'hidden', fontFamily: 'Roboto, sans-serif' }, surfaceClose: { zIndex: 1399, position: 'fixed', top: 0, left: 0, right: 0, bottom: 0 }, fade: { animation: "$fade 300ms cubic-bezier(0.4, 0, 0.2, 1)" }, '@keyframes fade': { '0%': { opacity: 0 }, '100%': { opacity: 1 } }, docking: { animation: "$docking 225ms cubic-bezier(0.4, 0, 0.2, 1),$fade 300ms cubic-bezier(0.4, 0, 0.2, 1)" }, '@keyframes docking': { '0%': { transform: 'translate3d(0,-10px,0)' }, '100%': { transform: 'translate3d(0,0,0)' } }, zoom: { animation: "$zoom 250ms cubic-bezier(0.4, 0, 0.2, 1),$fade 300ms cubic-bezier(0.4, 0, 0.2, 1)" }, '@keyframes zoom': { '0%': { transform: 'scale(0.6)' }, '100%': { transform: 'scale(1)' } } }; /** * Popup - компонент для создания всплывающего окна. */ var Popup = /*#__PURE__*/React.forwardRef(function Popup(props, ref) { var classes = props.classes, className = props.className, children = props.children, target = props.target, Open = props.open, onClose = props.onClose, position = props.position; props.autoHide; var shift = props.shift, animation = props.animation, mode = props.mode, otherProps = _objectWithoutProperties(props, ["classes", "className", "children", "target", "open", "onClose", "position", "autoHide", "shift", "animation", "mode"]); var Popup_ref = useRef(); Popup_ref = ref || Popup_ref; var _useState = useState(false), _useState2 = _slicedToArray(_useState, 2), show = _useState2[0], setShow = _useState2[1]; var _useState3 = useState(false), _useState4 = _slicedToArray(_useState3, 2), mountNode = _useState4[0], setMountNode = _useState4[1]; var parents_ref = useRef({ all: [], scroll: [] }); var visible_ref = useRef({ showRef: false, mountRef: false }); var observerTarget_ref = useRef({ observes: { Intersection: undefined, Resize: undefined } }); visible_ref.current.showRef = show; visible_ref.current.mountRef = mountNode; var calcPositionTooltip = useCallback(function () { var Popup_S = Popup_ref.current; var _Popup_ref$current$ge = Popup_ref.current.getBoundingClientRect(), popupWidth = _Popup_ref$current$ge.width, popupHeight = _Popup_ref$current$ge.height; var _target$current$getBo = target.current.getBoundingClientRect(), top = _target$current$getBo.top, left = _target$current$getBo.left, targetWidth = _target$current$getBo.width, targetHeight = _target$current$getBo.height; var diffX = targetWidth / 2 - popupWidth / 2; var diffY = targetHeight / 2 - popupHeight / 2; var centerX = left + diffX + window.pageXOffset; var centerY = top + diffY + window.pageYOffset; popupHeight += shift; popupWidth += shift; if (position.includes('top')) { centerY -= diffY + popupHeight; } if (position.includes('-top')) { centerY += popupHeight; } if (position.includes('bottom')) { centerY += diffY + popupHeight; } if (position.includes('-bottom')) { centerY -= popupHeight; } if (position.includes('left')) { centerX -= diffX + popupWidth; } if (position.includes('-left')) { centerX += popupWidth; } if (position.includes('right')) { centerX += diffX + popupWidth; } if (position.includes('-right')) { centerX -= popupWidth; } Popup_S.style.transform = "translate(".concat(centerX, "px,").concat(centerY, "px)"); Popup_S.style.visibility = "visible"; }, [target, shift, position]); var onOpenPopup = function onOpenPopup() { if (Open === undefined) { setShow(true); } }; var onClosePopup = function onClosePopup() { if (onClose) { onClose(); } else { Open === undefined && setShow(false); } }; var newOpenHover = function newOpenHover() { mode === 'hover' && onOpenPopup(); }; var newCloseHover = function newCloseHover() { mode === 'hover' && onClosePopup(); }; var newOpenClick = function newOpenClick() { mode === 'click' && onOpenPopup(); }; var newCloseClick = function newCloseClick() { mode === 'click' && onClosePopup(); }; var handleResize = useCallback(function () { var _visible_ref$current = visible_ref.current, showRef = _visible_ref$current.showRef, mountRef = _visible_ref$current.mountRef; showRef && mountRef && calcPositionTooltip(); }, [calcPositionTooltip]); useEffect(function () { handleResize(); }, [mountNode, handleResize]); useEffect(function () { if (target.current) { var observes = observerTarget_ref.current.observes; var target_S = target.current; var parent_S = parents_ref.current; parent_S.all = getParentsNode(target_S); parent_S.scroll = parent_S.all.filter(function (parent) { return getIsScrollParentNode(parent); }); target_S.addEventListener('mouseenter', newOpenHover); target_S.addEventListener('mouseleave', newCloseHover); target_S.addEventListener('click', newOpenClick); return function () { target_S.removeEventListener('mouseenter', newOpenHover); target_S.removeEventListener('mouseleave', newCloseHover); target_S.removeEventListener('click', newOpenClick); observes.Resize.disconnect(); observes.Intersection.disconnect(); parent_S.scroll.forEach(function (p) { p.removeEventListener('scroll', handleResize); }); }; } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(function () { var observes = observerTarget_ref.current.observes; if (!observes.Resize) { // CREATE observes.Intersection observes.Resize = new ResizeObserver(handleResize); } if (!observes.Intersection) { // CREATE observes.Intersection observes.Intersection = new IntersectionObserver(function (_ref) { var _ref2 = _slicedToArray(_ref, 1), entries = _ref2[0]; setMountNode(entries.isIntersecting); }, { // root: parents_ref.current.all[0], root: parents_ref.current.scroll[0], threshold: 1.0 }); } if (show) { parents_ref.current.scroll.forEach(function (p) { p.addEventListener('scroll', handleResize); }); // observes WATCH observes.Intersection.observe(target.current); parents_ref.current.all.forEach(function (p) { observes.Resize.observe(p); }); } else { parents_ref.current.scroll.forEach(function (p) { p.removeEventListener('scroll', handleResize); }); observes.Intersection.disconnect(); observes.Resize.disconnect(); } }, [show, target, calcPositionTooltip, handleResize]); useEffect(function () { typeof Open === 'boolean' && setShow(Open); }, [Open]); useEffect(function () { // SSR defender setMountNode(true); }, []); var surfaceClose = /*#__PURE__*/React.createElement("div", { className: classes.surfaceClose, onClick: newCloseClick }); var Portal = show && mountNode ? /*#__PURE__*/createPortal( /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement("div", _extends({ ref: Popup_ref, className: classNames(classes.base, className) }, otherProps), /*#__PURE__*/React.createElement("div", { className: classNames(classes[animation]) }, children)), Open === undefined && mode === 'click' && show && surfaceClose), document.body) : null; return Object.prototype.hasOwnProperty.call(target, 'current') && show && Portal; }); Popup.propTypes = { /** * Это контент между открывающим и закрывающим тегом компонента. */ children: PropTypes.node, /** * Объект содержит jss стили компонента. */ classes: PropTypes.object, /** * Чтобы указать CSS классы, используйте этот атрибут. */ className: PropTypes.string, /** * Это ref ссылка по которой прицепляется Popup. */ target: PropTypes.object, /** * Если true, Popup будет виден. */ open: PropTypes.bool, /** * Если есть onClose(), onClose */ onClose: PropTypes.func, /** * Позиционирование относительно target. */ position: PropTypes.oneOf(['top', 'top-left', 'top-right', 'bottom', 'bottom-left', 'bottom-right', 'left', 'left-top', 'left-bottom', 'right', 'right-top', 'right-bottom']), /** * Если true, Popup закроется сам если убрать курсор. */ autoHide: PropTypes.bool, /** * Смещение в px от target. */ shift: PropTypes.number, /** * Вид анимации. */ animation: PropTypes.oneOf(['fade', 'docking', 'zoom']), /** * Режим открытия. */ mode: PropTypes.oneOf(['hover', 'click']) }; Popup.defaultProps = { target: {}, position: 'bottom', // autoHide: true, animation: 'fade', shift: 0, mode: 'hover' }; Popup.displayName = 'PopupEVG'; var Popup$1 = withStyles(styles)(Popup); export default Popup$1;