@evg-b/evg-ui
Version:
EVG-UI library inspired by Material Design.
361 lines (318 loc) • 10.4 kB
JavaScript
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;