UNPKG

monday-ui-react-core

Version:

Official monday.com UI resources for application development in React.js

279 lines (245 loc) • 6.96 kB
import React, { cloneElement, useCallback, useRef, useState, useEffect, useMemo } from "react"; import { usePopper } from "react-popper"; import isFunction from "lodash/isFunction"; import "../Dialog/Dialog.scss"; import { chainFunctions, convertToArray } from "../../utils/function-utils"; import { DialogContent } from "../Dialog/DialogContent/DialogContent"; import { isInsideClass } from "../../utils/dom-utils"; import DialogReference from "./DialogReference/DialogReference"; const NOOP = () => {}; const Dialog = ({ moveBy, children, position, wrapperClassName, startingEdge, showDelay, hideDelay, shouldShowOnMount, instantShowAndHide, onDialogDidShow = NOOP, open, disable, onDialogDidHide = NOOP, showTriggerIgnoreClass, hideTriggerIgnoreClass, showTrigger, hideTrigger, content, preventAnimationOnMount, animationType, showOnDialogEnter }) => { const [referenceElement, setReferenceElement] = useState(null); const [popperElement, setPopperElement] = useState(null); const contentRef = useRef(null); const [isOpen, setIsOpen] = useState(shouldShowOnMount); const hideTimeout = useRef(null); const showTimeout = useRef(null); useEffect(() => { if (!contentRef.current) { return; } setReferenceElement(contentRef.current); }, [contentRef, contentRef.current, setReferenceElement]); const isShown = useMemo(() => { return isOpen || open; }, [open, isOpen]); const onShowDialog = useCallback(() => { if (isShown) return; onDialogDidShow(); }, [onDialogDidShow, isShown]); const showDialog = useCallback(() => { if (instantShowAndHide) { onShowDialog(); return setIsOpen(true); } showTimeout.current = setTimeout(() => { onShowDialog(); setIsOpen(true); }, showDelay); }, [showDelay, instantShowAndHide, setIsOpen, showTimeout, onShowDialog]); const showDialogIfNeeded = useCallback(() => { if (disable) { return; } if (hideTimeout && hideTimeout.current) { clearTimeout(hideTimeout.current); hideTimeout.current = null; } if (showTimeout && !showTimeout.current) { showDialog(); } }, [disable, showDialog, hideTimeout, showTimeout]); const hideDialog = useCallback(() => { if (instantShowAndHide) { onDialogDidHide(); setIsOpen(false); } else { hideTimeout.current = setTimeout(() => { onDialogDidHide(); setIsOpen(false); }, hideDelay); } }, [instantShowAndHide, hideDelay, setIsOpen, hideTimeout, onDialogDidHide]); const hideDialogIfNeeded = useCallback(() => { if (showTimeout && showTimeout.current) { clearTimeout(showTimeout.current); showTimeout.current = null; } if (hideTimeout && hideTimeout.current) { return; } hideDialog(); }, [hideDialog, hideTimeout, showTimeout]); const isShowTrigger = useCallback( eventName => { return convertToArray(showTrigger).indexOf(eventName) > -1; }, [showTrigger] ); const isHideTrigger = useCallback( eventName => { return convertToArray(hideTrigger).indexOf(eventName) > -1; }, [hideTrigger] ); const handleEvent = useCallback( (eventName, target) => { if (isShowTrigger(eventName) && !isShown && !isInsideClass(target, showTriggerIgnoreClass)) { return showDialogIfNeeded(); } if (isHideTrigger(eventName) && isShown && !isInsideClass(target, hideTriggerIgnoreClass)) { return hideDialogIfNeeded(); } }, [ showTriggerIgnoreClass, hideTriggerIgnoreClass, isShown, showDialogIfNeeded, hideDialogIfNeeded, isShowTrigger, isHideTrigger ] ); const onMouseEnter = useCallback( e => { handleEvent("mouseenter", e.target); }, [handleEvent] ); const onMouseLeave = useCallback( e => { handleEvent("mouseleave", e.target); }, [handleEvent] ); const onClick = useCallback( e => { if (e.button) return; handleEvent("click", e.target); }, [handleEvent] ); const onMouseDown = useCallback( e => { if (e.button) return; handleEvent("mousedown", e.target); }, [handleEvent] ); const onFocus = useCallback( e => { handleEvent("focus", e.target); }, [handleEvent] ); const onBlur = useCallback( e => { handleEvent("blur", e.relatedTarget); }, [handleEvent] ); const onEsc = useCallback( e => { handleEvent("esckey", e.target); }, [handleEvent] ); const onClickOutside = useCallback( e => { handleEvent("clickoutside", e.target); }, [handleEvent] ); const disableOnClickOutside = useMemo(() => { return !isHideTrigger("clickoutside"); }, [isHideTrigger]); const animationTypeCalculated = useMemo(() => { return preventAnimationOnMount ? false : animationType; }, [preventAnimationOnMount, animationType]); const onDialogEnter = useCallback(() => { if (showOnDialogEnter) showDialogIfNeeded(); }, [showOnDialogEnter]); const onDialogLeave = useCallback(() => { if (showOnDialogEnter) hideDialogIfNeeded(); }, [showOnDialogEnter]); useEffect(() => { return () => { if (showTimeout && showTimeout.current) { clearTimeout(showTimeout.current); } if (hideTimeout && hideTimeout.current) { clearTimeout(hideTimeout.current); } }; }, [hideTimeout, showTimeout]); const { styles, attributes } = usePopper(referenceElement, popperElement, { placement: position, modifiers: [ { name: "offset", options: { offset: [moveBy.secondary, moveBy.main] } } ] }); console.log("referenceElement, popperElement:", referenceElement, popperElement); return ( <> <DialogReference setReferenceElement={setReferenceElement} onBlur={onBlur} onClick={onClick} onFocus={onFocus} onMouseDown={onMouseDown} onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} > {children} </DialogReference> {isShown && ( <div style={styles.popper} ref={setPopperElement} className="monday-style-dialog-content-wrapper"> <DialogContent onMouseEnter={onDialogEnter} onMouseLeave={onDialogLeave} disableOnClickOutside={disableOnClickOutside} onClickOutside={onClickOutside} onEscKey={onEsc} animationType={animationTypeCalculated} position={attributes.placement} wrapperClassName={wrapperClassName} startingEdge={startingEdge} isOpen={isShown} showDelay={showDelay} > {isFunction(content) ? content() : content} </DialogContent> </div> )} </> ); }; export default Dialog;