UNPKG

react-morphing-modal

Version:

React morphing modal! The easiest way to be fancy!

257 lines (215 loc) 8.42 kB
'use strict'; function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var React = require('react'); var React__default = _interopDefault(React); var bodyNode; function getBodyNode() { if (!bodyNode) { bodyNode = document.querySelector('body'); } return bodyNode; } function computeCoordinateScaleValue(initialCoordinate, nodeSize, windowCoordinate) { var intermediateCoordinate = windowCoordinate - initialCoordinate - nodeSize; var maxCoordinate = Math.max(initialCoordinate, intermediateCoordinate); var scaleValue = (maxCoordinate * 2 + nodeSize) / nodeSize; return Math.ceil(scaleValue * 10) / 10; } function getScaleValues(node, top, left) { return { scaleX: computeCoordinateScaleValue(left, node.offsetWidth, window.innerWidth), scaleY: computeCoordinateScaleValue(top, node.offsetHeight, window.innerHeight) }; } var bodyScrolling = { lock: function lock() { getBodyNode().style.overflow = 'hidden'; }, unlock: function unlock() { getBodyNode().style.removeProperty('overflow'); } }; function getPlaceholderComputedStyle(trigger, placeholder) { var top = trigger.offsetTop - window.scrollY; var left = trigger.offsetLeft - window.scrollX; var placeholderScale = getScaleValues(placeholder, top, left); // 👽1.5 to handle circle and rounded border return "\n top: " + top + "px;\n left: " + left + "px;\n transform: scale(" + placeholderScale.scaleX * 1.5 + "," + placeholderScale.scaleY * 1.5 + ");\n"; } function getBorderRadius(styles) { var borderTopLeftRadius = styles.borderTopLeftRadius, borderTopRightRadius = styles.borderTopRightRadius, borderBottomLeftRadius = styles.borderBottomLeftRadius, borderBottomRightRadius = styles.borderBottomRightRadius; return "\n border-top-left-radius: " + borderTopLeftRadius + ";\n border-top-right-radius: " + borderTopRightRadius + ";\n border-bottom-left-radius: " + borderBottomLeftRadius + ";\n border-bottom-right-radius: " + borderBottomRightRadius + ";\n "; } function getBackground(styles) { // work for chrome only. firefox do not return shorthand prop if (styles.background && styles.background.length > 0) { return styles.background; } // inline style in ff return none. // I could put styles.backgroundColor here so I don't need to check for none // but I don't want to rely on call order if (styles.backgroundImage && styles.backgroundImage.length > 0 && styles.backgroundImage !== 'none') { return styles.backgroundImage; } if (styles.backgroundColor && styles.backgroundColor.length > 0) { return styles.backgroundColor; } return ''; } var STATE = { IS_CLOSE: 0, IS_IN_PROGRESS: 1, IS_OPEN: 2 }; var noop = function noop() {}; function useModal(hookOptions) { if (hookOptions === void 0) { hookOptions = {}; } var placeholderRef = React.useRef(null); var activeTriggerRef = React.useRef({ nodeRef: null, options: null }); var _useState = React.useState(null), activeModal = _useState[0], setActiveModal = _useState[1]; var _useState2 = React.useState(STATE.IS_CLOSE), state = _useState2[0], setState = _useState2[1]; var event = hookOptions.event || 'onClick'; var onOpenCallback = hookOptions.onOpen || noop; var onCloseCallback = hookOptions.onClose || noop; function handleEscapeKey(e) { var key = e.key || e.keyCode; if (key === 'Escape' || key === 'Esc' || key === 27) close(); } // maybe throttle later if needed function handleResize() { requestAnimationFrame(updatePlaceholder); } function updatePlaceholder() { var trigger = activeTriggerRef.current.nodeRef.current; if (placeholderRef.current && trigger) { var placeholderStyle = getPlaceholderComputedStyle(trigger, placeholderRef.current); placeholderRef.current.style.cssText += placeholderStyle; } } React.useEffect(function () { if (state !== STATE.IS_CLOSE) { document.addEventListener('keydown', handleEscapeKey); window.addEventListener('resize', handleResize); } return function () { document.removeEventListener('keydown', handleEscapeKey); window.removeEventListener('resize', handleResize); }; }, [state]); var open = function open(triggerRef, triggerOptions) { activeTriggerRef.current.nodeRef = triggerRef; if (placeholderRef.current && triggerRef.current) { var placeholder = placeholderRef.current; var trigger = triggerRef.current; var triggerStyles = window.getComputedStyle(trigger); var borderRadius = getBorderRadius(triggerStyles); var modalId = null; var background = hookOptions.background || getBackground(triggerStyles); var onOpen = onOpenCallback; if (typeof triggerOptions === 'number' || typeof triggerOptions === 'string' || typeof triggerOptions === 'symbol') { modalId = triggerOptions; } else if (typeof triggerOptions === 'object' && triggerOptions !== null) { activeTriggerRef.current.options = triggerOptions; modalId = triggerOptions.id || modalId; background = triggerOptions.background || background; onOpen = triggerOptions.onOpen || onOpenCallback; } bodyScrolling.lock(); placeholder.style.cssText = "width: " + trigger.offsetWidth + "px; height: " + trigger.offsetHeight + "px; background: " + background + "; " + borderRadius; if (modalId) { setActiveModal(modalId); } setState(STATE.IS_IN_PROGRESS); var placeholderStyle = getPlaceholderComputedStyle(trigger, placeholder); placeholder.style.cssText += placeholderStyle; placeholder.addEventListener('transitionend', function () { onOpen(); setState(STATE.IS_OPEN); }, { once: true }); } }; var close = function close() { if (placeholderRef.current) { var triggerOptions = activeTriggerRef.current.options; var placeholder = placeholderRef.current; var onClose = triggerOptions && triggerOptions.onClose ? triggerOptions.onClose : onCloseCallback; setState(STATE.IS_IN_PROGRESS); bodyScrolling.unlock(); placeholder.style.removeProperty('transform'); placeholder.style.transform = 'scale(1,1);'; placeholder.addEventListener('transitionend', function () { onClose(); setState(STATE.IS_CLOSE); setActiveModal(null); }, { once: true }); } }; var getTriggerProps = function getTriggerProps(options) { var _ref; var ref = React.useRef(); return _ref = { ref: ref }, _ref[typeof options === 'object' && options !== null && options.event ? options.event : event] = open.bind(null, ref, options), _ref; }; return { open: open, close: close, activeModal: activeModal, getTriggerProps: getTriggerProps, modalProps: { placeholderRef: placeholderRef, state: state, close: close } }; } var classname = { container: 'RMM__container', closeButton: 'RMM__close-button', placeholder: 'RMM__placeholder', body: 'RMM__body', noBodyPadding: 'RMM__body--no-padding', get: function get(className, isActive) { return isActive ? className + " " + className + "--is-active" : className; } }; var Modal = function Modal(_ref) { var state = _ref.state, placeholderRef = _ref.placeholderRef, close = _ref.close, closeButton = _ref.closeButton, children = _ref.children, padding = _ref.padding; var bodyPadding = !padding ? " " + classname.noBodyPadding : ''; return React__default.createElement("div", { className: classname.get(classname.container, state === STATE.IS_IN_PROGRESS || state === STATE.IS_OPEN) }, React__default.createElement("div", { className: classname.get(classname.body, STATE.IS_OPEN === state) + bodyPadding }, children), React__default.createElement("div", { className: classname.placeholder, ref: placeholderRef }), closeButton && React__default.createElement("div", { className: classname.get(classname.closeButton, STATE.IS_OPEN === state), onClick: close })); }; Modal.defaultProps = { closeButton: true, padding: true }; exports.Modal = Modal; exports.useModal = useModal; //# sourceMappingURL=react-morphing-modal.cjs.development.js.map