UNPKG

react-floater

Version:
1,018 lines (1,017 loc) 31.3 kB
//#region \0rolldown/runtime.js var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); //#endregion let is_lite = require("is-lite"); is_lite = __toESM(is_lite); let react = require("react"); let react_jsx_runtime = require("react/jsx-runtime"); let react_dom = require("react-dom"); let deepmerge_ts = require("deepmerge-ts"); let _popperjs_core = require("@popperjs/core"); //#region src/literals.ts const POSITIONING_PROPS = [ "position", "top", "right", "bottom", "left" ]; const STATUS = { INIT: "init", IDLE: "idle", RENDER: "render", OPENING: "opening", OPEN: "open", CLOSING: "closing", ERROR: "error" }; //#endregion //#region src/components/Floater/Arrow.tsx function FloaterArrow(props) { const { arrow, arrowRef, placement, styles } = props; const { arrow: { base, color, display, position, size } } = styles; const arrowStyles = { display, position }; let points; let x = base; let y = size; if (placement.startsWith("top")) points = `0,0 ${x / 2},${y} ${x},0`; else if (placement.startsWith("bottom")) points = `${x},${y} ${x / 2},0 0,${y}`; else if (placement.startsWith("left")) { y = base; x = size; points = `0,0 ${x},${y / 2} 0,${y}`; } else if (placement.startsWith("right")) { y = base; x = size; points = `${x},${y} ${x},0 0,${y / 2}`; } if (arrow) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { ref: arrowRef, style: { color, height: placement.startsWith("left") || placement.startsWith("right") ? base : size, width: placement.startsWith("left") || placement.startsWith("right") ? size : base }, children: arrow }); return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { ref: arrowRef, className: "__floater__arrow", style: arrowStyles, children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", { height: y, version: "1.1", width: x, xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("polygon", { fill: color, points }) }) }); } //#endregion //#region src/components/Floater/CloseButton.tsx function FloaterCloseButton({ onClick, styles }) { const { color, height, width, ...style } = styles; return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", { "aria-label": "close", onClick, style, type: "button", children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", { height: `${height}px`, preserveAspectRatio: "xMidYMid", version: "1.1", viewBox: "0 0 18 18", width: `${width}px`, xmlns: "http://www.w3.org/2000/svg", children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("g", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M8.13911129,9.00268191 L0.171521827,17.0258467 C-0.0498027049,17.248715 -0.0498027049,17.6098394 0.171521827,17.8327545 C0.28204354,17.9443526 0.427188206,17.9998706 0.572051765,17.9998706 C0.71714958,17.9998706 0.862013139,17.9443526 0.972581703,17.8327545 L9.0000937,9.74924618 L17.0276057,17.8327545 C17.1384085,17.9443526 17.2832721,17.9998706 17.4281356,17.9998706 C17.5729992,17.9998706 17.718097,17.9443526 17.8286656,17.8327545 C18.0499901,17.6098862 18.0499901,17.2487618 17.8286656,17.0258467 L9.86135722,9.00268191 L17.8340066,0.973848225 C18.0553311,0.750979934 18.0553311,0.389855532 17.8340066,0.16694039 C17.6126821,-0.0556467968 17.254037,-0.0556467968 17.0329467,0.16694039 L9.00042166,8.25611765 L0.967006424,0.167268345 C0.745681892,-0.0553188426 0.387317931,-0.0553188426 0.165993399,0.167268345 C-0.0553311331,0.390136635 -0.0553311331,0.751261038 0.165993399,0.974176179 L8.13920499,9.00268191 L8.13911129,9.00268191 Z", fill: color }) }) }) }); } //#endregion //#region src/components/Floater/Container.tsx function FloaterContainer(props) { const { content, footer, onClick, open, positionWrapper, showCloseButton, styles, title } = props; const output = { content: (0, react.isValidElement)(content) ? content : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "__floater__content", style: styles.content, children: content }) }; if (title) output.title = (0, react.isValidElement)(title) ? title : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "__floater__title", style: styles.title, children: title }); if (footer) output.footer = (0, react.isValidElement)(footer) ? footer : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "__floater__footer", style: styles.footer, children: footer }); if ((showCloseButton || positionWrapper) && !is_lite.default.boolean(open)) output.close = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FloaterCloseButton, { onClick, styles: styles.close }); return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { className: "__floater__container", style: styles.container, children: [ output.close, output.title, output.content, output.footer ] }); } //#endregion //#region src/components/Floater/index.tsx function Floater(props) { const { component, content, floaterRef, hideArrow, id, onClick: closeFn, placement, status, styles } = props; const style = (0, react.useMemo)(() => { const { arrow: { size }, floater, floaterCentered, floaterClosing, floaterOpening, floaterWithComponent } = styles; let element = { ...floater }; if (!hideArrow) { if (placement.startsWith("top")) element.padding = `0 0 ${size}px`; else if (placement.startsWith("bottom")) element.padding = `${size}px 0 0`; else if (placement.startsWith("left")) element.padding = `0 ${size}px 0 0`; else if (placement.startsWith("right")) element.padding = `0 0 0 ${size}px`; } if (status === STATUS.CLOSING) element = { ...element, ...floaterClosing }; if (status === STATUS.OPENING || status === STATUS.OPEN) element = { ...element, ...floaterOpening }; if (placement === "center") element = { ...element, ...floaterCentered }; if (component) element = { ...element, ...floaterWithComponent }; return element; }, [ component, hideArrow, placement, status, styles ]); const shouldRender = [ "closing", "open", "opening", "render" ].includes(status); const output = {}; const classes = ["__floater"]; const baseProps = { role: "tooltip" }; if (component) { const componentProps = { closeFn, id, ...baseProps }; output.content = (0, react.isValidElement)(component) ? (0, react.cloneElement)(component, componentProps) : component(componentProps); } else output.content = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FloaterContainer, { ...props, content }); if (status === STATUS.OPEN) classes.push("__floater__open"); if (!hideArrow) output.arrow = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FloaterArrow, { ...props }); if (!shouldRender) return null; return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { ref: floaterRef, id, style: { zIndex: styles.options.zIndex }, children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: classes.join(" "), style, ...baseProps, children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { className: "__floater__body", children: [output.content, output.arrow] }) }) }); } //#endregion //#region src/modules/helpers.ts const portalId = "react-floater-portal"; function canUseDOM() { return !!(typeof window !== "undefined" && window.document?.createElement); } function enhanceProps(props) { return { autoOpen: false, debug: false, disableFlip: false, disableHoverToClick: false, event: "click", eventDelay: .4, hideArrow: false, offset: 15, placement: "bottom", showCloseButton: false, styles: {}, target: null, wrapperOptions: { position: false }, ...props }; } function getFallbackPlacements(placement) { if (placement.startsWith("left")) return ["top", "bottom"]; if (placement.startsWith("right")) return ["bottom", "top"]; } function getModifiers(modifiers) { return (0, deepmerge_ts.deepmerge)({ flip: { name: "flip", enabled: true, options: { padding: 20 } }, preventOverflow: { name: "preventOverflow", enabled: true, options: { padding: 10 } } }, modifiers ?? {}); } function isFixed(el) { if (!el) return false; const { nodeName } = el; if (nodeName === "BODY" || nodeName === "HTML") return false; if (getComputedStyle(el).position === "fixed") return true; return el.parentNode instanceof HTMLElement ? isFixed(el.parentNode) : false; } function isMobile() { return "ontouchstart" in window && /Mobi/.test(navigator.userAgent); } /** * Log method calls if debug is enabled */ function log({ data, debug = false, title }) { const logFn = console.log; if (debug && title && data) { console.groupCollapsed(`%creact-floater: ${title}`, "color: #9b00ff; font-weight: bold; font-size: 12px;"); if (Array.isArray(data)) data.forEach((d) => { if (is_lite.default.plainObject(d) && d.key) logFn.apply(console, [d.key, d.value]); else logFn.apply(console, [d]); }); else logFn.apply(console, [data]); console.groupEnd(); } } function mergeModifier(modifier, customModifier) { return (0, deepmerge_ts.deepmerge)(modifier, customModifier ?? {}); } function off(element, eventType, handler, options = false) { element.removeEventListener(eventType, handler, options); } function on(element, eventType, handler, options = false) { element.addEventListener(eventType, handler, options); } function once(element, eventType, handler, options = false) { let nextCB = (event) => { handler(event); off(element, eventType, nextCB); }; on(element, eventType, nextCB, options); return () => off(element, eventType, nextCB); } function randomId() { return `rf-${Math.round(Math.random() * 1e5)}`; } //#endregion //#region src/modules/hooks.ts function useEffectOnce(effect) { (0, react.useEffect)(effect, []); } function useMount(effect) { useEffectOnce(effect); } function useUnmount(fn) { const fnRef = (0, react.useRef)(fn); fnRef.current = fn; useEffectOnce(() => () => fnRef.current()); } function useUpdateEffect(effect, deps) { const isFirst = (0, react.useRef)(true); (0, react.useEffect)(() => { if (isFirst.current) { isFirst.current = false; return; } return effect(); }, deps); } //#endregion //#region src/components/Portal.tsx function ReactFloaterPortal(props) { const { children, hasChildren, internalId, placement, portalElement, target, zIndex } = props; const node = (0, react.useRef)(null); const initialize = (0, react.useCallback)(() => { if (portalElement) node.current = is_lite.default.string(portalElement) ? document.querySelector(portalElement) : portalElement; if (!portalElement || !node.current) { const portal = document.getElementById(portalId); if (portal) { const ids = portal.dataset.ids?.split(",").filter(Boolean) ?? []; if (!ids.includes(internalId) && internalId) ids.push(internalId); portal.dataset.ids = ids.join(","); node.current = portal; } else { node.current = document.createElement("div"); node.current.id = portalId; node.current.dataset.ids = internalId; node.current.style.zIndex = `${zIndex}`; document.body.appendChild(node.current); } } }, [ internalId, portalElement, zIndex ]); useMount(() => { initialize(); }); (0, react.useEffect)(() => { initialize(); }, [initialize]); useUnmount(() => { if (!node.current) return; try { if (node.current.id === "react-floater-portal") { const remainingIds = (node.current.dataset.ids?.split(",") ?? []).filter((id) => id !== internalId); node.current.dataset.ids = remainingIds.join(","); if (remainingIds.length === 0) { document.body.removeChild(node.current); node.current = null; } } } catch { node.current = null; } }); if (node.current) { if (!hasChildren && !target && placement !== "center") return null; return (0, react_dom.createPortal)(children, node.current); } return null; } //#endregion //#region src/components/Wrapper.tsx function FloaterWrapper(props) { const { childRef, children, id, isControlled, onClick, onMount, onMouseEnter, onMouseLeave, status, style, styles, wrapperRef } = props; useMount(() => { onMount(); }); let element = null; const mergedStyles = { ...styles, ...style, ...(0, react.isValidElement)(children) ? children.props.style : void 0 }; const wrapperId = `${id}-wrapper`; let wrapperProps = { "aria-describedby": [ STATUS.OPENING, STATUS.OPEN, STATUS.CLOSING ].includes(status) ? id : void 0, style: mergedStyles }; if (!isControlled) wrapperProps = { ...wrapperProps, onClick, onMouseEnter, onMouseLeave }; if (children) if (react.Children.count(children) === 1 && (0, react.isValidElement)(children) && children.type !== react.Fragment) element = is_lite.default.function(children.type) ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { ref: wrapperRef, id: wrapperId, ...wrapperProps, children: (0, react.cloneElement)(react.Children.only(children), { innerRef: childRef }) }) : (0, react.cloneElement)(react.Children.only(children), { id: wrapperId, ref: wrapperRef, ...wrapperProps }); else element = /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { ref: wrapperRef, id: wrapperId, ...wrapperProps, children }); return element; } //#endregion //#region src/modules/styles.ts const defaultOptions = { zIndex: 100 }; function getStyles(styles) { const { options = {}, ...rest } = styles ?? {}; return (0, deepmerge_ts.deepmerge)({ arrow: { base: 32, color: "#fff", display: "inline-flex", position: "absolute", size: 16 }, close: { backgroundColor: "transparent", border: 0, borderRadius: 0, color: "#555", fontSize: 0, height: 15, outline: "none", padding: 10, position: "absolute", right: 0, top: 0, width: 15, WebkitAppearance: "none" }, container: { backgroundColor: "#fff", color: "#666", minHeight: 60, minWidth: 200, padding: 20, position: "relative", zIndex: 10 }, content: { fontSize: 15 }, footer: { borderTop: "1px solid #ccc", fontSize: 13, marginTop: 10, paddingTop: 5 }, floater: { display: "inline-block", filter: "drop-shadow(0 0 3px rgba(0, 0, 0, 0.3))", maxWidth: 300, opacity: 0, position: "relative", transition: "opacity 0.3s", visibility: "hidden", zIndex: options.zIndex }, floaterCentered: { left: "50%", position: "fixed", top: "50%", transform: "translate(-50%, -50%)" }, floaterClosing: { opacity: 0, visibility: "visible" }, floaterOpening: { opacity: 1, visibility: "visible" }, floaterWithComponent: { maxWidth: "100%" }, title: { borderBottom: "1px solid #555", color: "#555", fontSize: 18, marginBottom: 5, paddingBottom: 6, paddingRight: 18 }, wrapper: { cursor: "help", zIndex: options.zIndex }, wrapperPosition: { left: -1e3, position: "absolute", top: -1e3, visibility: "hidden" }, options: (0, deepmerge_ts.deepmerge)(defaultOptions, options) }, rest); } //#endregion //#region src/modules/useFloater.ts function useFloater(props) { const { arrow: arrowElement, autoOpen = false, callback, children, component, content, debug = false, disableFlip = false, disableHoverToClick = false, event = "click", eventDelay = .4, footer, getPopper, hideArrow = false, id, modifiers, offset = 15, open, placement = "bottom", portalElement, showCloseButton = false, style, styles, target, title, wrapperOptions } = enhanceProps(props); const [state, setState] = (0, react.useReducer)((previousState, nextState) => ({ ...previousState, ...nextState }), { currentPlacement: placement, positionWrapper: !!wrapperOptions?.position && !!target, status: STATUS.INIT, statusWrapper: STATUS.INIT }); const { currentPlacement, positionWrapper, status, statusWrapper } = state; const arrowRef = (0, react.useRef)(null); const childRef = (0, react.useRef)(null); const floaterRef = (0, react.useRef)(null); const wrapperRef = (0, react.useRef)(null); const eventDelayTimer = (0, react.useRef)(void 0); const internalId = (0, react.useRef)(randomId()); const isMounted = (0, react.useRef)(false); const popperGeneration = (0, react.useRef)(0); const popperRef = (0, react.useRef)(void 0); const previousStatus = (0, react.useRef)(STATUS.INIT); const stateRef = (0, react.useRef)(state); const removeTransitionListener = (0, react.useRef)(void 0); const wrapperPopperRef = (0, react.useRef)(void 0); const wrapperStyles = (0, react.useRef)(void 0); const callbackRef = (0, react.useRef)(callback); const getPopperRef = (0, react.useRef)(getPopper); const initPopperRef = (0, react.useRef)(void 0); const propsRef = (0, react.useRef)(props); const targetElement = (0, react.useRef)(void 0); callbackRef.current = callback; getPopperRef.current = getPopper; propsRef.current = props; targetElement.current = () => { if (target) { if (is_lite.default.domElement(target)) return target; return document.querySelector(target); } return childRef.current ?? wrapperRef.current; }; const currentDebug = debug || !!window.ReactFloaterDebug; const isIdle = status === STATUS.IDLE; const currentEvent = (0, react.useMemo)(() => { if (event === "hover" && isMobile() && !disableHoverToClick) return "click"; return event; }, [disableHoverToClick, event]); const currentStyles = (0, react.useMemo)(() => { const nextStyles = getStyles(styles); const element = targetElement.current(); if (positionWrapper) { let wrapperCurrentStyles; if (!isIdle) wrapperCurrentStyles = nextStyles.wrapperPosition; else if (statusWrapper === STATUS.RENDER) wrapperCurrentStyles = wrapperPopperRef.current?.state.styles; nextStyles.wrapper = { ...nextStyles.wrapper, ...wrapperCurrentStyles }; } if (element) { const targetStyles = window.getComputedStyle(element); if (wrapperStyles.current) nextStyles.wrapper = { ...nextStyles.wrapper, ...wrapperStyles.current }; else if (!["relative", "static"].includes(targetStyles.position)) { wrapperStyles.current = {}; if (!positionWrapper) { POSITIONING_PROPS.forEach((d) => { if (d === "position") wrapperStyles.current[d] = targetStyles[d]; else wrapperStyles.current[d] = targetStyles[d]; }); nextStyles.wrapper = { ...nextStyles.wrapper, ...wrapperStyles.current }; } } } return nextStyles; }, [ positionWrapper, isIdle, statusWrapper, styles ]); const updateState = (0, react.useCallback)((nextState, callback_) => { if (isMounted.current) { setState(nextState); stateRef.current = { ...stateRef.current, ...nextState }; if (callback_) callback_(); } }, []); const toggle = (0, react.useCallback)((forceStatus) => { let nextStatus = stateRef.current.status === STATUS.OPEN ? STATUS.CLOSING : STATUS.RENDER; if (!is_lite.default.undefined(forceStatus)) nextStatus = forceStatus; updateState({ status: nextStatus, statusWrapper: nextStatus === STATUS.CLOSING ? STATUS.RENDER : STATUS.IDLE }); }, [updateState]); const initPopper = (0, react.useCallback)(() => { const nextStatus = stateRef.current.status === STATUS.RENDER ? STATUS.OPENING : STATUS.IDLE; popperGeneration.current++; const generation = popperGeneration.current; const element = targetElement.current(); if (placement === "center") requestAnimationFrame(() => { if (popperGeneration.current !== generation) return; updateState({ status: nextStatus }); }); else if (element) { if (floaterRef.current) { const { arrow, flip, offset: offsetModifier, ...rest } = getModifiers(modifiers); popperRef.current = (0, _popperjs_core.createPopper)(element, floaterRef.current, { placement, strategy: isFixed(targetElement.current()) ? "fixed" : "absolute", modifiers: [ mergeModifier({ name: "arrow", enabled: !hideArrow, options: { element: arrowRef.current, padding: 8 } }, arrow), mergeModifier({ name: "flip", enabled: !disableFlip, options: { altAxis: false, fallbackPlacements: getFallbackPlacements(placement || "bottom") } }, flip), mergeModifier({ name: "offset", enabled: true, options: { offset: [0, offset] } }, offsetModifier), { name: "updatePlacement", enabled: true, phase: "afterWrite", fn: ({ instance, state: popperState }) => { if (popperState.placement !== stateRef.current.currentPlacement) { popperRef.current = instance; updateState({ currentPlacement: popperState.placement }); } } }, { name: "applyArrowStyle", enabled: true, phase: "write", fn: ({ state: popperState }) => { const { elements: { arrow: stateArrow }, placement: statePlacement } = popperState; if (stateArrow) { if (statePlacement.startsWith("top")) { stateArrow.style.bottom = "0px"; stateArrow.style.right = ""; } else if (statePlacement.startsWith("bottom")) { stateArrow.style.top = "0px"; stateArrow.style.right = ""; } else if (statePlacement.startsWith("left")) { stateArrow.style.right = "0px"; stateArrow.style.bottom = ""; } else if (statePlacement.startsWith("right")) { stateArrow.style.left = "0px"; stateArrow.style.bottom = ""; } } } }, ...Object.values(rest) ], onFirstUpdate: (popperState) => { if (popperGeneration.current !== generation) return; updateState({ currentPlacement: popperState.placement, status: nextStatus }); if (placement !== popperState.placement) setTimeout(() => { popperRef.current?.forceUpdate(); }); } }); if (getPopperRef.current && popperRef.current) getPopperRef.current(popperRef.current, "floater"); } else if (stateRef.current.status === STATUS.RENDER) /* v8 ignore start -- @preserve */ requestAnimationFrame(() => { if (isMounted.current && stateRef.current.status === STATUS.RENDER) initPopper(); }); else /* v8 ignore next 3 -- @preserve */ updateState({ status: STATUS.IDLE }); /* v8 ignore start -- @preserve */ if (wrapperRef.current && !wrapperPopperRef.current && stateRef.current.positionWrapper) { const wrapperOffset = wrapperOptions?.offset ?? 0; wrapperPopperRef.current = (0, _popperjs_core.createPopper)(element, wrapperRef.current, { placement: wrapperOptions?.placement ?? placement, strategy: isFixed(targetElement.current()) ? "fixed" : "absolute", modifiers: [ { name: "arrow", enabled: false }, { name: "offset", options: { offset: [0, wrapperOffset] } }, { name: "flip", enabled: false } ], onFirstUpdate: (popperState) => { updateState({ statusWrapper: STATUS.RENDER }); if (placement !== popperState.placement) setTimeout(() => { wrapperPopperRef.current?.forceUpdate(); }); } }); if (getPopperRef.current) getPopperRef.current(wrapperPopperRef.current, "wrapper"); } } }, [ disableFlip, hideArrow, modifiers, offset, placement, updateState, wrapperOptions?.offset, wrapperOptions?.placement ]); const cleanUp = (0, react.useCallback)(() => { if (popperRef.current) { popperRef.current.destroy(); popperRef.current = void 0; } if (wrapperPopperRef.current) { wrapperPopperRef.current.destroy(); wrapperPopperRef.current = void 0; } }, []); initPopperRef.current = initPopper; const handleTransitionEnd = (0, react.useCallback)(() => { /* v8 ignore next 3 -- @preserve */ if (wrapperPopperRef.current) wrapperPopperRef.current.forceUpdate(); updateState({ status: stateRef.current.status === STATUS.OPENING ? STATUS.OPEN : STATUS.IDLE }, () => { if (callbackRef.current) callbackRef.current(stateRef.current.status === STATUS.OPEN ? "open" : "close", enhanceProps(propsRef.current)); }); }, [updateState]); const handleClick = (0, react.useCallback)(() => { if (is_lite.default.boolean(open)) return; if (currentEvent === "click" || currentEvent === "hover" && positionWrapper) { log({ title: "click", data: [{ event, status: status === STATUS.OPEN ? "closing" : "opening" }], debug: currentDebug }); toggle(status === STATUS.IDLE ? STATUS.RENDER : void 0); } }, [ currentDebug, currentEvent, event, open, positionWrapper, status, toggle ]); const handleMouseEnter = (0, react.useCallback)(() => { if (is_lite.default.boolean(open) || isMobile() || currentEvent !== "hover") return; log({ title: "mouseEnter", data: [{ key: "originalEvent", value: event }], debug: currentDebug }); if (status === STATUS.IDLE) { clearTimeout(eventDelayTimer.current); eventDelayTimer.current = void 0; toggle(STATUS.RENDER); } }, [ currentDebug, currentEvent, event, open, status, toggle ]); const handleMouseLeave = (0, react.useCallback)(() => { if (is_lite.default.boolean(open) || isMobile()) return; if (currentEvent === "hover") { log({ title: "mouseLeave", data: [{ key: "originalEvent", value: event }], debug: currentDebug }); const hasOpenStatus = [STATUS.OPENING, STATUS.OPEN].includes(status); if (!eventDelay) toggle(status === STATUS.CLOSING ? STATUS.IDLE : STATUS.CLOSING); else if (!positionWrapper) { if (hasOpenStatus) { clearTimeout(eventDelayTimer.current); eventDelayTimer.current = window.setTimeout(() => { toggle(); eventDelayTimer.current = void 0; }, eventDelay * 1e3); } } } }, [ currentDebug, currentEvent, event, eventDelay, open, positionWrapper, status, toggle ]); const handleWrapperMount = (0, react.useCallback)(() => { if (positionWrapper) initPopper(); }, [initPopper, positionWrapper]); (0, react.useEffect)(() => { isMounted.current = true; initPopperRef.current(); }, []); (0, react.useEffect)(() => { log({ title: "init", data: { hasChildren: !!children, hasTarget: !!target, isControlled: is_lite.default.boolean(open), positionWrapper, target: targetElement.current(), floater: floaterRef.current }, debug: currentDebug }); }, [ children, target, open, positionWrapper, currentDebug ]); (0, react.useEffect)(() => { return () => { isMounted.current = false; clearTimeout(eventDelayTimer.current); removeTransitionListener.current?.(); cleanUp(); }; }, [cleanUp]); useUpdateEffect(() => { if (is_lite.default.boolean(open)) toggle(open ? STATUS.RENDER : STATUS.CLOSING); }, [open, toggle]); (0, react.useEffect)(() => { const next = !!wrapperOptions?.position && !!target; if (stateRef.current.positionWrapper !== next) updateState({ positionWrapper: next }); }, [ wrapperOptions?.position, target, updateState ]); (0, react.useEffect)(() => { if (status === previousStatus.current) return; if (status === STATUS.IDLE && previousStatus.current !== STATUS.IDLE) { if (open || previousStatus.current === STATUS.INIT && autoOpen) toggle(STATUS.RENDER); } if (status === STATUS.RENDER && previousStatus.current !== STATUS.RENDER) { if (popperRef.current) popperRef.current.destroy(); initPopper(); } if (floaterRef.current && (status === STATUS.RENDER || status === STATUS.CLOSING) && previousStatus.current !== status) { removeTransitionListener.current?.(); removeTransitionListener.current = once(floaterRef.current, "transitionend", handleTransitionEnd); } if (status === STATUS.IDLE && previousStatus.current === STATUS.CLOSING) { if (popperRef.current) { popperRef.current.destroy(); popperRef.current = void 0; } /* v8 ignore next 3 -- @preserve */ if (wrapperPopperRef.current) wrapperPopperRef.current.forceUpdate(); } previousStatus.current = status; }, [ autoOpen, handleTransitionEnd, initPopper, open, status, toggle ]); return { arrowRef, childRef, floaterRef, wrapperRef, currentPlacement, positionWrapper, status, currentStyles, internalId: internalId.current, arrowElement, children, component, content, footer, hideArrow, id, open, portalElement, showCloseButton, style, target, title, handleClick, handleMouseEnter, handleMouseLeave, handleWrapperMount }; } //#endregion //#region src/index.tsx function FloaterComponent(props) { const { arrowElement, arrowRef, childRef, children, component, content, currentPlacement, currentStyles, floaterRef, footer, handleClick, handleMouseEnter, handleMouseLeave, handleWrapperMount, hideArrow, id, internalId, open, portalElement, positionWrapper, showCloseButton, status, style, target, title, wrapperRef } = useFloater(props); const wrapper = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FloaterWrapper, { childRef, id: id ?? internalId, isControlled: is_lite.default.boolean(open), onClick: handleClick, onMount: handleWrapperMount, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, status, style, styles: currentStyles.wrapper, wrapperRef, children }); return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)(ReactFloaterPortal, { hasChildren: !!children, internalId, placement: currentPlacement, portalElement, target, zIndex: currentStyles.options.zIndex, children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Floater, { arrow: arrowElement, arrowRef, component, content, floaterRef, footer, hideArrow: hideArrow || currentPlacement === "center", id: id ?? internalId, onClick: handleClick, placement: currentPlacement, positionWrapper, showCloseButton, status, styles: currentStyles, title }), positionWrapper && wrapper] }), !positionWrapper && wrapper] }); } function ReactFloater(props) { if (!canUseDOM()) return null; return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(FloaterComponent, { ...props }); } //#endregion module.exports = ReactFloater; //# sourceMappingURL=index.cjs.map