@elliemae/ds-floating-context 3.57.14 → 3.57.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -82,6 +82,8 @@ const useComputedPositionStyles = (config) => {
82
82
  }, debounceMs);
83
83
  return d;
84
84
  }, [debounceMs]);
85
+ const mutableDebouncedStyles = (0, import_react.useRef)(debouncedUpdateStyles);
86
+ mutableDebouncedStyles.current = debouncedUpdateStyles;
85
87
  (0, import_react.useLayoutEffect)(
86
88
  () => () => {
87
89
  debouncedUpdateStyles.cancel();
@@ -110,7 +112,7 @@ const useComputedPositionStyles = (config) => {
110
112
  hasComputedOnce,
111
113
  updateStyles: forceUpdatePosition,
112
114
  debouncedUpdateStyles,
113
- mutableUpdateStyles,
115
+ mutableUpdateStyles: mutableDebouncedStyles,
114
116
  resetVisibilityOnly
115
117
  }),
116
118
  [arrowStyles, floatingStyles, hasComputedOnce, forceUpdatePosition, debouncedUpdateStyles, resetVisibilityOnly]
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/useComputedPositionStyles.tsx", "../../../../../scripts/build/transpile/react-shim.js"],
4
- "sourcesContent": ["/* eslint-disable max-statements */\nimport { useLayoutEffect, useMemo, useRef, useState, useCallback } from 'react';\nimport { debounce } from 'lodash-es';\nimport { type CSSProperties } from 'styled-components';\nimport { computePosition } from './utils/computePosition.js';\nimport type { DSHookFloatingContextT } from './react-desc-prop-types.js';\nimport type { PopoverArrowT } from './parts/PopoverArrow.js';\n\ntype UseComputedPositionStylesT = {\n /** Prevent computing when closed (optimization + avoids unnecessary frames) */\n preventComputing?: boolean;\n reference: Element | null;\n floating: HTMLElement | null;\n placement: DSHookFloatingContextT.PopperPlacementsT;\n placementOrderPreference?: DSHookFloatingContextT.PopperPlacementsT[];\n customOffset: [number, number];\n withoutPortal: boolean;\n /** Debounce ms for scroll/resize/observer events */\n debounceMs?: number;\n};\n\nexport const useComputedPositionStyles = (config: UseComputedPositionStylesT) => {\n const {\n reference,\n floating,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n preventComputing = false,\n debounceMs = 150,\n } = config;\n\n const [arrowStyles, setArrowStyles] = useState<PopoverArrowT>({ style: { left: 0 }, placement: 'top' });\n\n // Initial state: invisible (opacity:0) but FOCUSABLE.\n // We intentionally use opacity instead of visibility:hidden \u2014 `visibility:hidden` blocks\n // programmatic focus on descendants (including React's `autoFocus` attribute on inputs),\n // which causes a race on first-open: the floating content's autoFocus fires before the\n // position-computation useLayoutEffect can flip visibility to `visible`, so the focus\n // silently no-ops. Opacity:0 keeps the element invisible while letting `.focus()` work.\n // pointer-events:none prevents accidental clicks on the still-unpositioned (0,0) area.\n const [floatingStyles, setFloatingStyles] = useState<CSSProperties>({\n position: 'absolute',\n zIndex: 3000,\n opacity: 0,\n pointerEvents: 'none',\n willChange: 'transform',\n });\n\n const [hasComputedOnce, setHasComputedOnce] = useState(false);\n\n const canCompute = reference !== null && floating !== null && !preventComputing;\n\n const updateStyles = useCallback(() => {\n if (!canCompute) return;\n\n const { coordsStyle, finalPlacement, coordsArrow } = computePosition({\n reference,\n floating,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n });\n // INTENTIONAL explicit destructure \u2014 do NOT replace with `...coordsStyle`.\n //\n // PUI-18470 is a ghost bug: an infinite ResizeObserver \u2192 setState \u2192 re-render loop\n // that only triggers at specific viewport pixel combinations (\"magic pixel\") and is\n // nearly impossible to reproduce consistently across machines. It took a synthetic\n // Playwright ResizeObserver-intercept test to surface it at all.\n //\n // Part of the fix is the bail-out below (`prev.transform === transform`): if the\n // computed position hasn't changed we return the same state reference, preventing a\n // re-render and breaking the loop. That bail-out only works if we know exactly which\n // properties coordsStyle contributes. Spreading `...coordsStyle` hides that contract:\n // if computePosition ever extends coordsStyle with a new property, the bail-out\n // silently becomes incomplete and the loop can re-emerge with no obvious cause.\n //\n // Keeping the destructure explicit forces any future change to computePosition's\n // coordsStyle contract to be a conscious, visible decision here \u2014 not a silent pass-through.\n const { transform, top, left } = coordsStyle;\n\n // Do not touch visibility here; it is managed outside depending on open/hasComputedOnce\n setFloatingStyles((prev) => {\n if (prev.transform === transform) return prev;\n return { position: 'absolute', zIndex: 3000, ...prev, transform, top, left };\n });\n setArrowStyles({ style: coordsArrow, placement: finalPlacement });\n setHasComputedOnce(true);\n }, [canCompute, reference, floating, placement, placementOrderPreference, customOffset, withoutPortal]);\n\n // Store latest update function in a ref to keep debounced stable\n const mutableUpdateStyles = useRef(updateStyles);\n mutableUpdateStyles.current = updateStyles;\n\n const debouncedUpdateStyles = useMemo(() => {\n const d = debounce(() => {\n mutableUpdateStyles.current();\n }, debounceMs);\n return d;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [debounceMs]);\n\n // Clean up debounce on unmount\n useLayoutEffect(\n () => () => {\n debouncedUpdateStyles.cancel();\n },\n [debouncedUpdateStyles],\n );\n\n // Recalculate BEFORE paint when dependencies change\n useLayoutEffect(() => {\n if (canCompute) {\n mutableUpdateStyles.current();\n }\n }, [canCompute, reference, floating, placement, placementOrderPreference, customOffset, withoutPortal]);\n\n const forceUpdatePosition = useCallback(() => {\n mutableUpdateStyles.current();\n }, []);\n\n // Do not reset coordinates when closing; just hide via opacity (keeps element focusable\n // if anything inside needs to remain focusable during animations).\n const resetVisibilityOnly = useCallback(() => {\n setFloatingStyles((prev) => ({\n ...prev,\n opacity: 0,\n pointerEvents: 'none',\n }));\n }, []);\n\n return useMemo(\n () => ({\n arrowStyles,\n floatingStyles,\n hasComputedOnce,\n updateStyles: forceUpdatePosition,\n debouncedUpdateStyles,\n mutableUpdateStyles,\n resetVisibilityOnly,\n }),\n [arrowStyles, floatingStyles, hasComputedOnce, forceUpdatePosition, debouncedUpdateStyles, resetVisibilityOnly],\n );\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADCvB,mBAAwE;AACxE,uBAAyB;AAEzB,6BAAgC;AAiBzB,MAAM,4BAA4B,CAAC,WAAuC;AAC/E,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB,aAAa;AAAA,EACf,IAAI;AAEJ,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAwB,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,WAAW,MAAM,CAAC;AAStG,QAAM,CAAC,gBAAgB,iBAAiB,QAAI,uBAAwB;AAAA,IAClE,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,eAAe;AAAA,IACf,YAAY;AAAA,EACd,CAAC;AAED,QAAM,CAAC,iBAAiB,kBAAkB,QAAI,uBAAS,KAAK;AAE5D,QAAM,aAAa,cAAc,QAAQ,aAAa,QAAQ,CAAC;AAE/D,QAAM,mBAAe,0BAAY,MAAM;AACrC,QAAI,CAAC,WAAY;AAEjB,UAAM,EAAE,aAAa,gBAAgB,YAAY,QAAI,wCAAgB;AAAA,MACnE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAiBD,UAAM,EAAE,WAAW,KAAK,KAAK,IAAI;AAGjC,sBAAkB,CAAC,SAAS;AAC1B,UAAI,KAAK,cAAc,UAAW,QAAO;AACzC,aAAO,EAAE,UAAU,YAAY,QAAQ,KAAM,GAAG,MAAM,WAAW,KAAK,KAAK;AAAA,IAC7E,CAAC;AACD,mBAAe,EAAE,OAAO,aAAa,WAAW,eAAe,CAAC;AAChE,uBAAmB,IAAI;AAAA,EACzB,GAAG,CAAC,YAAY,WAAW,UAAU,WAAW,0BAA0B,cAAc,aAAa,CAAC;AAGtG,QAAM,0BAAsB,qBAAO,YAAY;AAC/C,sBAAoB,UAAU;AAE9B,QAAM,4BAAwB,sBAAQ,MAAM;AAC1C,UAAM,QAAI,2BAAS,MAAM;AACvB,0BAAoB,QAAQ;AAAA,IAC9B,GAAG,UAAU;AACb,WAAO;AAAA,EAET,GAAG,CAAC,UAAU,CAAC;AAGf;AAAA,IACE,MAAM,MAAM;AACV,4BAAsB,OAAO;AAAA,IAC/B;AAAA,IACA,CAAC,qBAAqB;AAAA,EACxB;AAGA,oCAAgB,MAAM;AACpB,QAAI,YAAY;AACd,0BAAoB,QAAQ;AAAA,IAC9B;AAAA,EACF,GAAG,CAAC,YAAY,WAAW,UAAU,WAAW,0BAA0B,cAAc,aAAa,CAAC;AAEtG,QAAM,0BAAsB,0BAAY,MAAM;AAC5C,wBAAoB,QAAQ;AAAA,EAC9B,GAAG,CAAC,CAAC;AAIL,QAAM,0BAAsB,0BAAY,MAAM;AAC5C,sBAAkB,CAAC,UAAU;AAAA,MAC3B,GAAG;AAAA,MACH,SAAS;AAAA,MACT,eAAe;AAAA,IACjB,EAAE;AAAA,EACJ,GAAG,CAAC,CAAC;AAEL,aAAO;AAAA,IACL,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,aAAa,gBAAgB,iBAAiB,qBAAqB,uBAAuB,mBAAmB;AAAA,EAChH;AACF;",
4
+ "sourcesContent": ["/* eslint-disable max-statements */\nimport { useLayoutEffect, useMemo, useRef, useState, useCallback } from 'react';\nimport { debounce } from 'lodash-es';\nimport { type CSSProperties } from 'styled-components';\nimport { computePosition } from './utils/computePosition.js';\nimport type { DSHookFloatingContextT } from './react-desc-prop-types.js';\nimport type { PopoverArrowT } from './parts/PopoverArrow.js';\n\ntype UseComputedPositionStylesT = {\n /** Prevent computing when closed (optimization + avoids unnecessary frames) */\n preventComputing?: boolean;\n reference: Element | null;\n floating: HTMLElement | null;\n placement: DSHookFloatingContextT.PopperPlacementsT;\n placementOrderPreference?: DSHookFloatingContextT.PopperPlacementsT[];\n customOffset: [number, number];\n withoutPortal: boolean;\n /** Debounce ms for scroll/resize/observer events */\n debounceMs?: number;\n};\n\nexport const useComputedPositionStyles = (config: UseComputedPositionStylesT) => {\n const {\n reference,\n floating,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n preventComputing = false,\n debounceMs = 150,\n } = config;\n\n const [arrowStyles, setArrowStyles] = useState<PopoverArrowT>({ style: { left: 0 }, placement: 'top' });\n\n // Initial state: invisible (opacity:0) but FOCUSABLE.\n // We intentionally use opacity instead of visibility:hidden \u2014 `visibility:hidden` blocks\n // programmatic focus on descendants (including React's `autoFocus` attribute on inputs),\n // which causes a race on first-open: the floating content's autoFocus fires before the\n // position-computation useLayoutEffect can flip visibility to `visible`, so the focus\n // silently no-ops. Opacity:0 keeps the element invisible while letting `.focus()` work.\n // pointer-events:none prevents accidental clicks on the still-unpositioned (0,0) area.\n const [floatingStyles, setFloatingStyles] = useState<CSSProperties>({\n position: 'absolute',\n zIndex: 3000,\n opacity: 0,\n pointerEvents: 'none',\n willChange: 'transform',\n });\n\n const [hasComputedOnce, setHasComputedOnce] = useState(false);\n\n const canCompute = reference !== null && floating !== null && !preventComputing;\n\n const updateStyles = useCallback(() => {\n if (!canCompute) return;\n\n const { coordsStyle, finalPlacement, coordsArrow } = computePosition({\n reference,\n floating,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n });\n // INTENTIONAL explicit destructure \u2014 do NOT replace with `...coordsStyle`.\n //\n // PUI-18470 is a ghost bug: an infinite ResizeObserver \u2192 setState \u2192 re-render loop\n // that only triggers at specific viewport pixel combinations (\"magic pixel\") and is\n // nearly impossible to reproduce consistently across machines. It took a synthetic\n // Playwright ResizeObserver-intercept test to surface it at all.\n //\n // Part of the fix is the bail-out below (`prev.transform === transform`): if the\n // computed position hasn't changed we return the same state reference, preventing a\n // re-render and breaking the loop. That bail-out only works if we know exactly which\n // properties coordsStyle contributes. Spreading `...coordsStyle` hides that contract:\n // if computePosition ever extends coordsStyle with a new property, the bail-out\n // silently becomes incomplete and the loop can re-emerge with no obvious cause.\n //\n // Keeping the destructure explicit forces any future change to computePosition's\n // coordsStyle contract to be a conscious, visible decision here \u2014 not a silent pass-through.\n const { transform, top, left } = coordsStyle;\n\n // Do not touch visibility here; it is managed outside depending on open/hasComputedOnce\n setFloatingStyles((prev) => {\n if (prev.transform === transform) return prev;\n return { position: 'absolute', zIndex: 3000, ...prev, transform, top, left };\n });\n setArrowStyles({ style: coordsArrow, placement: finalPlacement });\n setHasComputedOnce(true);\n }, [canCompute, reference, floating, placement, placementOrderPreference, customOffset, withoutPortal]);\n\n // Store latest update function in a ref to keep debounced stable\n const mutableUpdateStyles = useRef(updateStyles);\n mutableUpdateStyles.current = updateStyles;\n\n const debouncedUpdateStyles = useMemo(() => {\n const d = debounce(() => {\n mutableUpdateStyles.current();\n }, debounceMs);\n return d;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [debounceMs]);\n\n const mutableDebouncedStyles = useRef(debouncedUpdateStyles);\n mutableDebouncedStyles.current = debouncedUpdateStyles;\n\n // Clean up debounce on unmount\n useLayoutEffect(\n () => () => {\n debouncedUpdateStyles.cancel();\n },\n [debouncedUpdateStyles],\n );\n\n // Recalculate BEFORE paint when dependencies change\n useLayoutEffect(() => {\n if (canCompute) {\n mutableUpdateStyles.current();\n }\n }, [canCompute, reference, floating, placement, placementOrderPreference, customOffset, withoutPortal]);\n\n const forceUpdatePosition = useCallback(() => {\n mutableUpdateStyles.current();\n }, []);\n\n // Do not reset coordinates when closing; just hide via opacity (keeps element focusable\n // if anything inside needs to remain focusable during animations).\n const resetVisibilityOnly = useCallback(() => {\n setFloatingStyles((prev) => ({\n ...prev,\n opacity: 0,\n pointerEvents: 'none',\n }));\n }, []);\n\n return useMemo(\n () => ({\n arrowStyles,\n floatingStyles,\n hasComputedOnce,\n updateStyles: forceUpdatePosition,\n debouncedUpdateStyles,\n mutableUpdateStyles: mutableDebouncedStyles,\n resetVisibilityOnly,\n }),\n [arrowStyles, floatingStyles, hasComputedOnce, forceUpdatePosition, debouncedUpdateStyles, resetVisibilityOnly],\n );\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADCvB,mBAAwE;AACxE,uBAAyB;AAEzB,6BAAgC;AAiBzB,MAAM,4BAA4B,CAAC,WAAuC;AAC/E,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB,aAAa;AAAA,EACf,IAAI;AAEJ,QAAM,CAAC,aAAa,cAAc,QAAI,uBAAwB,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,WAAW,MAAM,CAAC;AAStG,QAAM,CAAC,gBAAgB,iBAAiB,QAAI,uBAAwB;AAAA,IAClE,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,eAAe;AAAA,IACf,YAAY;AAAA,EACd,CAAC;AAED,QAAM,CAAC,iBAAiB,kBAAkB,QAAI,uBAAS,KAAK;AAE5D,QAAM,aAAa,cAAc,QAAQ,aAAa,QAAQ,CAAC;AAE/D,QAAM,mBAAe,0BAAY,MAAM;AACrC,QAAI,CAAC,WAAY;AAEjB,UAAM,EAAE,aAAa,gBAAgB,YAAY,QAAI,wCAAgB;AAAA,MACnE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAiBD,UAAM,EAAE,WAAW,KAAK,KAAK,IAAI;AAGjC,sBAAkB,CAAC,SAAS;AAC1B,UAAI,KAAK,cAAc,UAAW,QAAO;AACzC,aAAO,EAAE,UAAU,YAAY,QAAQ,KAAM,GAAG,MAAM,WAAW,KAAK,KAAK;AAAA,IAC7E,CAAC;AACD,mBAAe,EAAE,OAAO,aAAa,WAAW,eAAe,CAAC;AAChE,uBAAmB,IAAI;AAAA,EACzB,GAAG,CAAC,YAAY,WAAW,UAAU,WAAW,0BAA0B,cAAc,aAAa,CAAC;AAGtG,QAAM,0BAAsB,qBAAO,YAAY;AAC/C,sBAAoB,UAAU;AAE9B,QAAM,4BAAwB,sBAAQ,MAAM;AAC1C,UAAM,QAAI,2BAAS,MAAM;AACvB,0BAAoB,QAAQ;AAAA,IAC9B,GAAG,UAAU;AACb,WAAO;AAAA,EAET,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,6BAAyB,qBAAO,qBAAqB;AAC3D,yBAAuB,UAAU;AAGjC;AAAA,IACE,MAAM,MAAM;AACV,4BAAsB,OAAO;AAAA,IAC/B;AAAA,IACA,CAAC,qBAAqB;AAAA,EACxB;AAGA,oCAAgB,MAAM;AACpB,QAAI,YAAY;AACd,0BAAoB,QAAQ;AAAA,IAC9B;AAAA,EACF,GAAG,CAAC,YAAY,WAAW,UAAU,WAAW,0BAA0B,cAAc,aAAa,CAAC;AAEtG,QAAM,0BAAsB,0BAAY,MAAM;AAC5C,wBAAoB,QAAQ;AAAA,EAC9B,GAAG,CAAC,CAAC;AAIL,QAAM,0BAAsB,0BAAY,MAAM;AAC5C,sBAAkB,CAAC,UAAU;AAAA,MAC3B,GAAG;AAAA,MACH,SAAS;AAAA,MACT,eAAe;AAAA,IACjB,EAAE;AAAA,EACJ,GAAG,CAAC,CAAC;AAEL,aAAO;AAAA,IACL,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA,qBAAqB;AAAA,MACrB;AAAA,IACF;AAAA,IACA,CAAC,aAAa,gBAAgB,iBAAiB,qBAAqB,uBAAuB,mBAAmB;AAAA,EAChH;AACF;",
6
6
  "names": []
7
7
  }
@@ -49,6 +49,8 @@ const useComputedPositionStyles = (config) => {
49
49
  }, debounceMs);
50
50
  return d;
51
51
  }, [debounceMs]);
52
+ const mutableDebouncedStyles = useRef(debouncedUpdateStyles);
53
+ mutableDebouncedStyles.current = debouncedUpdateStyles;
52
54
  useLayoutEffect(
53
55
  () => () => {
54
56
  debouncedUpdateStyles.cancel();
@@ -77,7 +79,7 @@ const useComputedPositionStyles = (config) => {
77
79
  hasComputedOnce,
78
80
  updateStyles: forceUpdatePosition,
79
81
  debouncedUpdateStyles,
80
- mutableUpdateStyles,
82
+ mutableUpdateStyles: mutableDebouncedStyles,
81
83
  resetVisibilityOnly
82
84
  }),
83
85
  [arrowStyles, floatingStyles, hasComputedOnce, forceUpdatePosition, debouncedUpdateStyles, resetVisibilityOnly]
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/useComputedPositionStyles.tsx"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable max-statements */\nimport { useLayoutEffect, useMemo, useRef, useState, useCallback } from 'react';\nimport { debounce } from 'lodash-es';\nimport { type CSSProperties } from 'styled-components';\nimport { computePosition } from './utils/computePosition.js';\nimport type { DSHookFloatingContextT } from './react-desc-prop-types.js';\nimport type { PopoverArrowT } from './parts/PopoverArrow.js';\n\ntype UseComputedPositionStylesT = {\n /** Prevent computing when closed (optimization + avoids unnecessary frames) */\n preventComputing?: boolean;\n reference: Element | null;\n floating: HTMLElement | null;\n placement: DSHookFloatingContextT.PopperPlacementsT;\n placementOrderPreference?: DSHookFloatingContextT.PopperPlacementsT[];\n customOffset: [number, number];\n withoutPortal: boolean;\n /** Debounce ms for scroll/resize/observer events */\n debounceMs?: number;\n};\n\nexport const useComputedPositionStyles = (config: UseComputedPositionStylesT) => {\n const {\n reference,\n floating,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n preventComputing = false,\n debounceMs = 150,\n } = config;\n\n const [arrowStyles, setArrowStyles] = useState<PopoverArrowT>({ style: { left: 0 }, placement: 'top' });\n\n // Initial state: invisible (opacity:0) but FOCUSABLE.\n // We intentionally use opacity instead of visibility:hidden \u2014 `visibility:hidden` blocks\n // programmatic focus on descendants (including React's `autoFocus` attribute on inputs),\n // which causes a race on first-open: the floating content's autoFocus fires before the\n // position-computation useLayoutEffect can flip visibility to `visible`, so the focus\n // silently no-ops. Opacity:0 keeps the element invisible while letting `.focus()` work.\n // pointer-events:none prevents accidental clicks on the still-unpositioned (0,0) area.\n const [floatingStyles, setFloatingStyles] = useState<CSSProperties>({\n position: 'absolute',\n zIndex: 3000,\n opacity: 0,\n pointerEvents: 'none',\n willChange: 'transform',\n });\n\n const [hasComputedOnce, setHasComputedOnce] = useState(false);\n\n const canCompute = reference !== null && floating !== null && !preventComputing;\n\n const updateStyles = useCallback(() => {\n if (!canCompute) return;\n\n const { coordsStyle, finalPlacement, coordsArrow } = computePosition({\n reference,\n floating,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n });\n // INTENTIONAL explicit destructure \u2014 do NOT replace with `...coordsStyle`.\n //\n // PUI-18470 is a ghost bug: an infinite ResizeObserver \u2192 setState \u2192 re-render loop\n // that only triggers at specific viewport pixel combinations (\"magic pixel\") and is\n // nearly impossible to reproduce consistently across machines. It took a synthetic\n // Playwright ResizeObserver-intercept test to surface it at all.\n //\n // Part of the fix is the bail-out below (`prev.transform === transform`): if the\n // computed position hasn't changed we return the same state reference, preventing a\n // re-render and breaking the loop. That bail-out only works if we know exactly which\n // properties coordsStyle contributes. Spreading `...coordsStyle` hides that contract:\n // if computePosition ever extends coordsStyle with a new property, the bail-out\n // silently becomes incomplete and the loop can re-emerge with no obvious cause.\n //\n // Keeping the destructure explicit forces any future change to computePosition's\n // coordsStyle contract to be a conscious, visible decision here \u2014 not a silent pass-through.\n const { transform, top, left } = coordsStyle;\n\n // Do not touch visibility here; it is managed outside depending on open/hasComputedOnce\n setFloatingStyles((prev) => {\n if (prev.transform === transform) return prev;\n return { position: 'absolute', zIndex: 3000, ...prev, transform, top, left };\n });\n setArrowStyles({ style: coordsArrow, placement: finalPlacement });\n setHasComputedOnce(true);\n }, [canCompute, reference, floating, placement, placementOrderPreference, customOffset, withoutPortal]);\n\n // Store latest update function in a ref to keep debounced stable\n const mutableUpdateStyles = useRef(updateStyles);\n mutableUpdateStyles.current = updateStyles;\n\n const debouncedUpdateStyles = useMemo(() => {\n const d = debounce(() => {\n mutableUpdateStyles.current();\n }, debounceMs);\n return d;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [debounceMs]);\n\n // Clean up debounce on unmount\n useLayoutEffect(\n () => () => {\n debouncedUpdateStyles.cancel();\n },\n [debouncedUpdateStyles],\n );\n\n // Recalculate BEFORE paint when dependencies change\n useLayoutEffect(() => {\n if (canCompute) {\n mutableUpdateStyles.current();\n }\n }, [canCompute, reference, floating, placement, placementOrderPreference, customOffset, withoutPortal]);\n\n const forceUpdatePosition = useCallback(() => {\n mutableUpdateStyles.current();\n }, []);\n\n // Do not reset coordinates when closing; just hide via opacity (keeps element focusable\n // if anything inside needs to remain focusable during animations).\n const resetVisibilityOnly = useCallback(() => {\n setFloatingStyles((prev) => ({\n ...prev,\n opacity: 0,\n pointerEvents: 'none',\n }));\n }, []);\n\n return useMemo(\n () => ({\n arrowStyles,\n floatingStyles,\n hasComputedOnce,\n updateStyles: forceUpdatePosition,\n debouncedUpdateStyles,\n mutableUpdateStyles,\n resetVisibilityOnly,\n }),\n [arrowStyles, floatingStyles, hasComputedOnce, forceUpdatePosition, debouncedUpdateStyles, resetVisibilityOnly],\n );\n};\n"],
5
- "mappings": "AAAA,YAAY,WAAW;ACCvB,SAAS,iBAAiB,SAAS,QAAQ,UAAU,mBAAmB;AACxE,SAAS,gBAAgB;AAEzB,SAAS,uBAAuB;AAiBzB,MAAM,4BAA4B,CAAC,WAAuC;AAC/E,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB,aAAa;AAAA,EACf,IAAI;AAEJ,QAAM,CAAC,aAAa,cAAc,IAAI,SAAwB,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,WAAW,MAAM,CAAC;AAStG,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAwB;AAAA,IAClE,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,eAAe;AAAA,IACf,YAAY;AAAA,EACd,CAAC;AAED,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAAS,KAAK;AAE5D,QAAM,aAAa,cAAc,QAAQ,aAAa,QAAQ,CAAC;AAE/D,QAAM,eAAe,YAAY,MAAM;AACrC,QAAI,CAAC,WAAY;AAEjB,UAAM,EAAE,aAAa,gBAAgB,YAAY,IAAI,gBAAgB;AAAA,MACnE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAiBD,UAAM,EAAE,WAAW,KAAK,KAAK,IAAI;AAGjC,sBAAkB,CAAC,SAAS;AAC1B,UAAI,KAAK,cAAc,UAAW,QAAO;AACzC,aAAO,EAAE,UAAU,YAAY,QAAQ,KAAM,GAAG,MAAM,WAAW,KAAK,KAAK;AAAA,IAC7E,CAAC;AACD,mBAAe,EAAE,OAAO,aAAa,WAAW,eAAe,CAAC;AAChE,uBAAmB,IAAI;AAAA,EACzB,GAAG,CAAC,YAAY,WAAW,UAAU,WAAW,0BAA0B,cAAc,aAAa,CAAC;AAGtG,QAAM,sBAAsB,OAAO,YAAY;AAC/C,sBAAoB,UAAU;AAE9B,QAAM,wBAAwB,QAAQ,MAAM;AAC1C,UAAM,IAAI,SAAS,MAAM;AACvB,0BAAoB,QAAQ;AAAA,IAC9B,GAAG,UAAU;AACb,WAAO;AAAA,EAET,GAAG,CAAC,UAAU,CAAC;AAGf;AAAA,IACE,MAAM,MAAM;AACV,4BAAsB,OAAO;AAAA,IAC/B;AAAA,IACA,CAAC,qBAAqB;AAAA,EACxB;AAGA,kBAAgB,MAAM;AACpB,QAAI,YAAY;AACd,0BAAoB,QAAQ;AAAA,IAC9B;AAAA,EACF,GAAG,CAAC,YAAY,WAAW,UAAU,WAAW,0BAA0B,cAAc,aAAa,CAAC;AAEtG,QAAM,sBAAsB,YAAY,MAAM;AAC5C,wBAAoB,QAAQ;AAAA,EAC9B,GAAG,CAAC,CAAC;AAIL,QAAM,sBAAsB,YAAY,MAAM;AAC5C,sBAAkB,CAAC,UAAU;AAAA,MAC3B,GAAG;AAAA,MACH,SAAS;AAAA,MACT,eAAe;AAAA,IACjB,EAAE;AAAA,EACJ,GAAG,CAAC,CAAC;AAEL,SAAO;AAAA,IACL,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,aAAa,gBAAgB,iBAAiB,qBAAqB,uBAAuB,mBAAmB;AAAA,EAChH;AACF;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable max-statements */\nimport { useLayoutEffect, useMemo, useRef, useState, useCallback } from 'react';\nimport { debounce } from 'lodash-es';\nimport { type CSSProperties } from 'styled-components';\nimport { computePosition } from './utils/computePosition.js';\nimport type { DSHookFloatingContextT } from './react-desc-prop-types.js';\nimport type { PopoverArrowT } from './parts/PopoverArrow.js';\n\ntype UseComputedPositionStylesT = {\n /** Prevent computing when closed (optimization + avoids unnecessary frames) */\n preventComputing?: boolean;\n reference: Element | null;\n floating: HTMLElement | null;\n placement: DSHookFloatingContextT.PopperPlacementsT;\n placementOrderPreference?: DSHookFloatingContextT.PopperPlacementsT[];\n customOffset: [number, number];\n withoutPortal: boolean;\n /** Debounce ms for scroll/resize/observer events */\n debounceMs?: number;\n};\n\nexport const useComputedPositionStyles = (config: UseComputedPositionStylesT) => {\n const {\n reference,\n floating,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n preventComputing = false,\n debounceMs = 150,\n } = config;\n\n const [arrowStyles, setArrowStyles] = useState<PopoverArrowT>({ style: { left: 0 }, placement: 'top' });\n\n // Initial state: invisible (opacity:0) but FOCUSABLE.\n // We intentionally use opacity instead of visibility:hidden \u2014 `visibility:hidden` blocks\n // programmatic focus on descendants (including React's `autoFocus` attribute on inputs),\n // which causes a race on first-open: the floating content's autoFocus fires before the\n // position-computation useLayoutEffect can flip visibility to `visible`, so the focus\n // silently no-ops. Opacity:0 keeps the element invisible while letting `.focus()` work.\n // pointer-events:none prevents accidental clicks on the still-unpositioned (0,0) area.\n const [floatingStyles, setFloatingStyles] = useState<CSSProperties>({\n position: 'absolute',\n zIndex: 3000,\n opacity: 0,\n pointerEvents: 'none',\n willChange: 'transform',\n });\n\n const [hasComputedOnce, setHasComputedOnce] = useState(false);\n\n const canCompute = reference !== null && floating !== null && !preventComputing;\n\n const updateStyles = useCallback(() => {\n if (!canCompute) return;\n\n const { coordsStyle, finalPlacement, coordsArrow } = computePosition({\n reference,\n floating,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n });\n // INTENTIONAL explicit destructure \u2014 do NOT replace with `...coordsStyle`.\n //\n // PUI-18470 is a ghost bug: an infinite ResizeObserver \u2192 setState \u2192 re-render loop\n // that only triggers at specific viewport pixel combinations (\"magic pixel\") and is\n // nearly impossible to reproduce consistently across machines. It took a synthetic\n // Playwright ResizeObserver-intercept test to surface it at all.\n //\n // Part of the fix is the bail-out below (`prev.transform === transform`): if the\n // computed position hasn't changed we return the same state reference, preventing a\n // re-render and breaking the loop. That bail-out only works if we know exactly which\n // properties coordsStyle contributes. Spreading `...coordsStyle` hides that contract:\n // if computePosition ever extends coordsStyle with a new property, the bail-out\n // silently becomes incomplete and the loop can re-emerge with no obvious cause.\n //\n // Keeping the destructure explicit forces any future change to computePosition's\n // coordsStyle contract to be a conscious, visible decision here \u2014 not a silent pass-through.\n const { transform, top, left } = coordsStyle;\n\n // Do not touch visibility here; it is managed outside depending on open/hasComputedOnce\n setFloatingStyles((prev) => {\n if (prev.transform === transform) return prev;\n return { position: 'absolute', zIndex: 3000, ...prev, transform, top, left };\n });\n setArrowStyles({ style: coordsArrow, placement: finalPlacement });\n setHasComputedOnce(true);\n }, [canCompute, reference, floating, placement, placementOrderPreference, customOffset, withoutPortal]);\n\n // Store latest update function in a ref to keep debounced stable\n const mutableUpdateStyles = useRef(updateStyles);\n mutableUpdateStyles.current = updateStyles;\n\n const debouncedUpdateStyles = useMemo(() => {\n const d = debounce(() => {\n mutableUpdateStyles.current();\n }, debounceMs);\n return d;\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [debounceMs]);\n\n const mutableDebouncedStyles = useRef(debouncedUpdateStyles);\n mutableDebouncedStyles.current = debouncedUpdateStyles;\n\n // Clean up debounce on unmount\n useLayoutEffect(\n () => () => {\n debouncedUpdateStyles.cancel();\n },\n [debouncedUpdateStyles],\n );\n\n // Recalculate BEFORE paint when dependencies change\n useLayoutEffect(() => {\n if (canCompute) {\n mutableUpdateStyles.current();\n }\n }, [canCompute, reference, floating, placement, placementOrderPreference, customOffset, withoutPortal]);\n\n const forceUpdatePosition = useCallback(() => {\n mutableUpdateStyles.current();\n }, []);\n\n // Do not reset coordinates when closing; just hide via opacity (keeps element focusable\n // if anything inside needs to remain focusable during animations).\n const resetVisibilityOnly = useCallback(() => {\n setFloatingStyles((prev) => ({\n ...prev,\n opacity: 0,\n pointerEvents: 'none',\n }));\n }, []);\n\n return useMemo(\n () => ({\n arrowStyles,\n floatingStyles,\n hasComputedOnce,\n updateStyles: forceUpdatePosition,\n debouncedUpdateStyles,\n mutableUpdateStyles: mutableDebouncedStyles,\n resetVisibilityOnly,\n }),\n [arrowStyles, floatingStyles, hasComputedOnce, forceUpdatePosition, debouncedUpdateStyles, resetVisibilityOnly],\n );\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACCvB,SAAS,iBAAiB,SAAS,QAAQ,UAAU,mBAAmB;AACxE,SAAS,gBAAgB;AAEzB,SAAS,uBAAuB;AAiBzB,MAAM,4BAA4B,CAAC,WAAuC;AAC/E,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB,aAAa;AAAA,EACf,IAAI;AAEJ,QAAM,CAAC,aAAa,cAAc,IAAI,SAAwB,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,WAAW,MAAM,CAAC;AAStG,QAAM,CAAC,gBAAgB,iBAAiB,IAAI,SAAwB;AAAA,IAClE,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,eAAe;AAAA,IACf,YAAY;AAAA,EACd,CAAC;AAED,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAAS,KAAK;AAE5D,QAAM,aAAa,cAAc,QAAQ,aAAa,QAAQ,CAAC;AAE/D,QAAM,eAAe,YAAY,MAAM;AACrC,QAAI,CAAC,WAAY;AAEjB,UAAM,EAAE,aAAa,gBAAgB,YAAY,IAAI,gBAAgB;AAAA,MACnE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAiBD,UAAM,EAAE,WAAW,KAAK,KAAK,IAAI;AAGjC,sBAAkB,CAAC,SAAS;AAC1B,UAAI,KAAK,cAAc,UAAW,QAAO;AACzC,aAAO,EAAE,UAAU,YAAY,QAAQ,KAAM,GAAG,MAAM,WAAW,KAAK,KAAK;AAAA,IAC7E,CAAC;AACD,mBAAe,EAAE,OAAO,aAAa,WAAW,eAAe,CAAC;AAChE,uBAAmB,IAAI;AAAA,EACzB,GAAG,CAAC,YAAY,WAAW,UAAU,WAAW,0BAA0B,cAAc,aAAa,CAAC;AAGtG,QAAM,sBAAsB,OAAO,YAAY;AAC/C,sBAAoB,UAAU;AAE9B,QAAM,wBAAwB,QAAQ,MAAM;AAC1C,UAAM,IAAI,SAAS,MAAM;AACvB,0BAAoB,QAAQ;AAAA,IAC9B,GAAG,UAAU;AACb,WAAO;AAAA,EAET,GAAG,CAAC,UAAU,CAAC;AAEf,QAAM,yBAAyB,OAAO,qBAAqB;AAC3D,yBAAuB,UAAU;AAGjC;AAAA,IACE,MAAM,MAAM;AACV,4BAAsB,OAAO;AAAA,IAC/B;AAAA,IACA,CAAC,qBAAqB;AAAA,EACxB;AAGA,kBAAgB,MAAM;AACpB,QAAI,YAAY;AACd,0BAAoB,QAAQ;AAAA,IAC9B;AAAA,EACF,GAAG,CAAC,YAAY,WAAW,UAAU,WAAW,0BAA0B,cAAc,aAAa,CAAC;AAEtG,QAAM,sBAAsB,YAAY,MAAM;AAC5C,wBAAoB,QAAQ;AAAA,EAC9B,GAAG,CAAC,CAAC;AAIL,QAAM,sBAAsB,YAAY,MAAM;AAC5C,sBAAkB,CAAC,UAAU;AAAA,MAC3B,GAAG;AAAA,MACH,SAAS;AAAA,MACT,eAAe;AAAA,IACjB,EAAE;AAAA,EACJ,GAAG,CAAC,CAAC;AAEL,SAAO;AAAA,IACL,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd;AAAA,MACA,qBAAqB;AAAA,MACrB;AAAA,IACF;AAAA,IACA,CAAC,aAAa,gBAAgB,iBAAiB,qBAAqB,uBAAuB,mBAAmB;AAAA,EAChH;AACF;",
6
6
  "names": []
7
7
  }
@@ -25,7 +25,7 @@ declare const useFloatingContext: {
25
25
  portalDOMContainer: HTMLElement | undefined;
26
26
  animationDuration: number;
27
27
  };
28
- mutableUpdateStyles: React.MutableRefObject<() => void>;
28
+ mutableUpdateStyles: React.MutableRefObject<import("lodash").DebouncedFunc<() => void>>;
29
29
  forceUpdatePosition: () => void;
30
30
  };
31
31
  displayName: string;
@@ -19,7 +19,7 @@ export declare const useComputedPositionStyles: (config: UseComputedPositionStyl
19
19
  hasComputedOnce: boolean;
20
20
  updateStyles: () => void;
21
21
  debouncedUpdateStyles: import("lodash").DebouncedFunc<() => void>;
22
- mutableUpdateStyles: import("react").MutableRefObject<() => void>;
22
+ mutableUpdateStyles: import("react").MutableRefObject<import("lodash").DebouncedFunc<() => void>>;
23
23
  resetVisibilityOnly: () => void;
24
24
  };
25
25
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elliemae/ds-floating-context",
3
- "version": "3.57.14",
3
+ "version": "3.57.15",
4
4
  "license": "MIT",
5
5
  "description": "ICE MT - Dimsum - Popper Hook",
6
6
  "files": [
@@ -36,15 +36,15 @@
36
36
  "indent": 4
37
37
  },
38
38
  "dependencies": {
39
- "@elliemae/ds-hooks-headless-tooltip": "3.57.14",
40
- "@elliemae/ds-props-helpers": "3.57.14",
41
- "@elliemae/ds-system": "3.57.14",
42
- "@elliemae/ds-typescript-helpers": "3.57.14"
39
+ "@elliemae/ds-props-helpers": "3.57.15",
40
+ "@elliemae/ds-hooks-headless-tooltip": "3.57.15",
41
+ "@elliemae/ds-typescript-helpers": "3.57.15",
42
+ "@elliemae/ds-system": "3.57.15"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@elliemae/pui-cli": "9.0.0-next.65",
46
46
  "jest": "~29.7.0",
47
- "@elliemae/ds-monorepo-devops": "3.57.14"
47
+ "@elliemae/ds-monorepo-devops": "3.57.15"
48
48
  },
49
49
  "peerDependencies": {
50
50
  "lodash-es": "^4.17.21",