import { computePosition, arrow as arrow$2, offset as offset$1, shift as shift$1, limitShift as limitShift$1, flip as flip$1, size as size$1, autoPlacement as autoPlacement$1, hide as hide$1, inline as inline$1 } from '@floating-ui/dom'; export { autoUpdate, computePosition, detectOverflow, getOverflowAncestors, platform } from '@floating-ui/dom'; import * as React from 'react'; import { useLayoutEffect, useEffect } from 'react'; import * as ReactDOM from 'react-dom'; var index = typeof document !== 'undefined' ? useLayoutEffect : useEffect; // Fork of `fast-deep-equal` that only does the comparisons we need and compares // functions function deepEqual(a, b) { if (a === b) { return true; } if (typeof a !== typeof b) { return false; } if (typeof a === 'function' && a.toString() === b.toString()) { return true; } let length; let i; let keys; if (a && b && typeof a === 'object') { if (Array.isArray(a)) { length = a.length; if (length !== b.length) return false; for (i = length; i-- !== 0;) { if (!deepEqual(a[i], b[i])) { return false; } } return true; } keys = Object.keys(a); length = keys.length; if (length !== Object.keys(b).length) { return false; } for (i = length; i-- !== 0;) { if (!{}.hasOwnProperty.call(b, keys[i])) { return false; } } for (i = length; i-- !== 0;) { const key = keys[i]; if (key === '_owner' && a.$$typeof) { continue; } if (!deepEqual(a[key], b[key])) { return false; } } return true; } return a !== a && b !== b; } function getDPR(element) { if (typeof window === 'undefined') { return 1; } const win = element.ownerDocument.defaultView || window; return win.devicePixelRatio || 1; } function roundByDPR(element, value) { const dpr = getDPR(element); return Math.round(value * dpr) / dpr; } function useLatestRef(value) { const ref = React.useRef(value); index(() => { ref.current = value; }); return ref; } /** * Provides data to position a floating element. * @see https://floating-ui.com/docs/useFloating */ function useFloating(options) { if (options === void 0) { options = {}; } const { placement = 'bottom', strategy = 'absolute', middleware = [], platform, elements: { reference: externalReference, floating: externalFloating } = {}, transform = true, whileElementsMounted, open } = options; const [data, setData] = React.useState({ x: 0, y: 0, strategy, placement, middlewareData: {}, isPositioned: false }); const [latestMiddleware, setLatestMiddleware] = React.useState(middleware); if (!deepEqual(latestMiddleware, middleware)) { setLatestMiddleware(middleware); } const [_reference, _setReference] = React.useState(null); const [_floating, _setFloating] = React.useState(null); const setReference = React.useCallback(node => { if (node !== referenceRef.current) { referenceRef.current = node; _setReference(node); } }, []); const setFloating = React.useCallback(node => { if (node !== floatingRef.current) { floatingRef.current = node; _setFloating(node); } }, []); const referenceEl = externalReference || _reference; const floatingEl = externalFloating || _floating; const referenceRef = React.useRef(null); const floatingRef = React.useRef(null); const dataRef = React.useRef(data); const hasWhileElementsMounted = whileElementsMounted != null; const whileElementsMountedRef = useLatestRef(whileElementsMounted); const platformRef = useLatestRef(platform); const update = React.useCallback(() => { if (!referenceRef.current || !floatingRef.current) { return; } const config = { placement, strategy, middleware: latestMiddleware }; if (platformRef.current) { config.platform = platformRef.current; } computePosition(referenceRef.current, floatingRef.current, config).then(data => { const fullData = { ...data, isPositioned: true }; if (isMountedRef.current && !deepEqual(dataRef.current, fullData)) { dataRef.current = fullData; ReactDOM.flushSync(() => { setData(fullData); }); } }); }, [latestMiddleware, placement, strategy, platformRef]); index(() => { if (open === false && dataRef.current.isPositioned) { dataRef.current.isPositioned = false; setData(data => ({ ...data, isPositioned: false })); } }, [open]); const isMountedRef = React.useRef(false); index(() => { isMountedRef.current = true; return () => { isMountedRef.current = false; }; }, []); index(() => { if (referenceEl) referenceRef.current = referenceEl; if (floatingEl) floatingRef.current = floatingEl; if (referenceEl && floatingEl) { if (whileElementsMountedRef.current) { return whileElementsMountedRef.current(referenceEl, floatingEl, update); } update(); } }, [referenceEl, floatingEl, update, whileElementsMountedRef, hasWhileElementsMounted]); const refs = React.useMemo(() => ({ reference: referenceRef, floating: floatingRef, setReference, setFloating }), [setReference, setFloating]); const elements = React.useMemo(() => ({ reference: referenceEl, floating: floatingEl }), [referenceEl, floatingEl]); const floatingStyles = React.useMemo(() => { const initialStyles = { position: strategy, left: 0, top: 0 }; if (!elements.floating) { return initialStyles; } const x = roundByDPR(elements.floating, data.x); const y = roundByDPR(elements.floating, data.y); if (transform) { return { ...initialStyles, transform: "translate(" + x + "px, " + y + "px)", ...(getDPR(elements.floating) >= 1.5 && { willChange: 'transform' }) }; } return { position: strategy, left: x, top: y }; }, [strategy, transform, elements.floating, data.x, data.y]); return React.useMemo(() => ({ ...data, update, refs, elements, floatingStyles }), [data, update, refs, elements, floatingStyles]); } /** * Provides data to position an inner element of the floating element so that it * appears centered to the reference element. * This wraps the core `arrow` middleware to allow React refs as the element. * @see https://floating-ui.com/docs/arrow */ const arrow$1 = options => { function isRef(value) { return {}.hasOwnProperty.call(value, 'current'); } return { name: 'arrow', options, fn(state) { const { element, padding } = typeof options === 'function' ? options(state) : options; if (element && isRef(element)) { if (element.current != null) { return arrow$2({ element: element.current, padding }).fn(state); } return {}; } if (element) { return arrow$2({ element, padding }).fn(state); } return {}; } }; }; /** * Modifies the placement by translating the floating element along the * specified axes. * A number (shorthand for `mainAxis` or distance), or an axes configuration * object may be passed. * @see https://floating-ui.com/docs/offset */ const offset = (options, deps) => ({ ...offset$1(options), options: [options, deps] }); /** * Optimizes the visibility of the floating element by shifting it in order to * keep it in view when it will overflow the clipping boundary. * @see https://floating-ui.com/docs/shift */ const shift = (options, deps) => ({ ...shift$1(options), options: [options, deps] }); /** * Built-in `limiter` that will stop `shift()` at a certain point. */ const limitShift = (options, deps) => ({ ...limitShift$1(options), options: [options, deps] }); /** * Optimizes the visibility of the floating element by flipping the `placement` * in order to keep it in view when the preferred placement(s) will overflow the * clipping boundary. Alternative to `autoPlacement`. * @see https://floating-ui.com/docs/flip */ const flip = (options, deps) => ({ ...flip$1(options), options: [options, deps] }); /** * Provides data that allows you to change the size of the floating element — * for instance, prevent it from overflowing the clipping boundary or match the * width of the reference element. * @see https://floating-ui.com/docs/size */ const size = (options, deps) => ({ ...size$1(options), options: [options, deps] }); /** * Optimizes the visibility of the floating element by choosing the placement * that has the most space available automatically, without needing to specify a * preferred placement. Alternative to `flip`. * @see https://floating-ui.com/docs/autoPlacement */ const autoPlacement = (options, deps) => ({ ...autoPlacement$1(options), options: [options, deps] }); /** * Provides data to hide the floating element in applicable situations, such as * when it is not in the same clipping context as the reference element. * @see https://floating-ui.com/docs/hide */ const hide = (options, deps) => ({ ...hide$1(options), options: [options, deps] }); /** * Provides improved positioning for inline reference elements that can span * over multiple lines, such as hyperlinks or range selections. * @see https://floating-ui.com/docs/inline */ const inline = (options, deps) => ({ ...inline$1(options), options: [options, deps] }); /** * Provides data to position an inner element of the floating element so that it * appears centered to the reference element. * This wraps the core `arrow` middleware to allow React refs as the element. * @see https://floating-ui.com/docs/arrow */ const arrow = (options, deps) => ({ ...arrow$1(options), options: [options, deps] }); export { arrow, autoPlacement, flip, hide, inline, limitShift, offset, shift, size, useFloating };