@elliemae/ds-floating-context 3.61.5-rc.5 → 3.61.5-rc.7

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.
Files changed (49) hide show
  1. package/dist/cjs/DSFloatingContext.js +58 -27
  2. package/dist/cjs/DSFloatingContext.js.map +2 -2
  3. package/dist/cjs/hooks/useFloatingClickOutside.js +60 -0
  4. package/dist/cjs/hooks/useFloatingClickOutside.js.map +7 -0
  5. package/dist/cjs/hooks/useFloatingEscape.js +74 -0
  6. package/dist/cjs/hooks/useFloatingEscape.js.map +7 -0
  7. package/dist/cjs/hooks/useFloatingResizeObserver.js +46 -0
  8. package/dist/cjs/hooks/useFloatingResizeObserver.js.map +7 -0
  9. package/dist/cjs/hooks/useLatestRef.js +41 -0
  10. package/dist/cjs/hooks/useLatestRef.js.map +7 -0
  11. package/dist/cjs/hooks/useResolvedReference.js +52 -0
  12. package/dist/cjs/hooks/useResolvedReference.js.map +7 -0
  13. package/dist/cjs/react-desc-prop-types.js +13 -2
  14. package/dist/cjs/react-desc-prop-types.js.map +2 -2
  15. package/dist/cjs/typescript-testing/typescript-floating-context-valid.js +5 -1
  16. package/dist/cjs/typescript-testing/typescript-floating-context-valid.js.map +2 -2
  17. package/dist/cjs/useComputedPositionStyles.js +12 -9
  18. package/dist/cjs/useComputedPositionStyles.js.map +2 -2
  19. package/dist/cjs/utils/computePosition.js +2 -2
  20. package/dist/cjs/utils/computePosition.js.map +2 -2
  21. package/dist/esm/DSFloatingContext.js +59 -28
  22. package/dist/esm/DSFloatingContext.js.map +2 -2
  23. package/dist/esm/hooks/useFloatingClickOutside.js +30 -0
  24. package/dist/esm/hooks/useFloatingClickOutside.js.map +7 -0
  25. package/dist/esm/hooks/useFloatingEscape.js +44 -0
  26. package/dist/esm/hooks/useFloatingEscape.js.map +7 -0
  27. package/dist/esm/hooks/useFloatingResizeObserver.js +16 -0
  28. package/dist/esm/hooks/useFloatingResizeObserver.js.map +7 -0
  29. package/dist/esm/hooks/useLatestRef.js +11 -0
  30. package/dist/esm/hooks/useLatestRef.js.map +7 -0
  31. package/dist/esm/hooks/useResolvedReference.js +22 -0
  32. package/dist/esm/hooks/useResolvedReference.js.map +7 -0
  33. package/dist/esm/react-desc-prop-types.js +13 -2
  34. package/dist/esm/react-desc-prop-types.js.map +2 -2
  35. package/dist/esm/typescript-testing/typescript-floating-context-valid.js +5 -1
  36. package/dist/esm/typescript-testing/typescript-floating-context-valid.js.map +2 -2
  37. package/dist/esm/useComputedPositionStyles.js +12 -9
  38. package/dist/esm/useComputedPositionStyles.js.map +2 -2
  39. package/dist/esm/utils/computePosition.js +2 -2
  40. package/dist/esm/utils/computePosition.js.map +2 -2
  41. package/dist/types/DSFloatingContext.d.ts +2 -2
  42. package/dist/types/hooks/useFloatingClickOutside.d.ts +20 -0
  43. package/dist/types/hooks/useFloatingEscape.d.ts +19 -0
  44. package/dist/types/hooks/useFloatingResizeObserver.d.ts +11 -0
  45. package/dist/types/hooks/useLatestRef.d.ts +7 -0
  46. package/dist/types/hooks/useResolvedReference.d.ts +19 -0
  47. package/dist/types/react-desc-prop-types.d.ts +19 -0
  48. package/dist/types/useComputedPositionStyles.d.ts +1 -1
  49. package/package.json +6 -6
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/typescript-testing/typescript-floating-context-valid.tsx"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */\nimport React from 'react';\nimport { FloatingWrapper } from '../index.js';\nimport type { DSHookFloatingContextT } from '../index.js';\n\n// test we expose the namespace and the namespace follows our deliverable conventions\ntype ComponentPropsForApp = DSHookFloatingContextT.Props;\ntype ComponentPropsInternals = DSHookFloatingContextT.InternalProps;\ntype ComponentPropsDefaultProps = DSHookFloatingContextT.DefaultProps;\ntype ComponentPropsOptionalProps = DSHookFloatingContextT.OptionalProps;\n\nconst testOptionalProps: ComponentPropsOptionalProps = {};\n\n// difference Props and InternalProps is that InternalProps has all the default props filled in\n// Props allows for partial defaults\nconst testPartialDefaults: Partial<ComponentPropsDefaultProps> = {\n withoutAnimation: false,\n withoutPortal: false,\n animationDuration: 300,\n};\n\n// InternalProps requires all defaults to be filled in\nconst testCompleteDefaults: Required<ComponentPropsDefaultProps> = {\n withoutAnimation: false,\n withoutPortal: false,\n animationDuration: 300,\n placement: 'top',\n customOffset: [10, 10],\n portalDOMContainer: document.body,\n};\n\nconst testInternalProps: ComponentPropsInternals = {\n ...testOptionalProps,\n ...testCompleteDefaults,\n};\n\nconst testInternalPropsAsSyntax = {\n ...testOptionalProps,\n ...testCompleteDefaults,\n} as ComponentPropsInternals;\n\n// using the explicit type definition, if there is an error, it will be marked on the key that is wrong\nconst testExplicitDefinition: ComponentPropsForApp = {\n animationDuration: 300,\n withoutAnimation: false,\n withoutPortal: false,\n placement: 'top',\n customOffset: [10, 10],\n portalDOMContainer: document.body,\n ...testOptionalProps,\n};\n\n// using the \"as\" syntax, if there is an error, it will be marking the whole object as wrong because it is not compatible with the type\nconst testInferedTypeCompatibility = {\n ...testOptionalProps,\n ...testPartialDefaults,\n animationDuration: 300,\n} as ComponentPropsForApp;\n\nconst testDefinitionAsConst = {\n animationDuration: 300,\n withoutAnimation: false,\n withoutPortal: false,\n placement: 'top',\n portalDOMContainer: document.body,\n} as const;\n\nconst ExampleUsageComponent = () => (\n <>\n {/* works with explicitly casted props, all syntaxes */}\n <FloatingWrapper\n {...testExplicitDefinition}\n innerRef={React.createRef<HTMLDivElement>()}\n isOpen\n floatingStyles={{}}\n >\n <div>Content</div>\n </FloatingWrapper>\n <FloatingWrapper\n {...testInferedTypeCompatibility}\n innerRef={React.createRef<HTMLDivElement>()}\n isOpen\n floatingStyles={{}}\n >\n <div>Content</div>\n </FloatingWrapper>\n <FloatingWrapper {...testDefinitionAsConst} innerRef={React.createRef<HTMLDivElement>()} isOpen floatingStyles={{}}>\n <div>Content</div>\n </FloatingWrapper>\n {/* works with inline values */}\n </>\n);\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;AAAA,YAAuB;ACoErB;AAnEF,mBAAkB;AAClB,eAAgC;AAShC,MAAM,oBAAiD,CAAC;AAIxD,MAAM,sBAA2D;AAAA,EAC/D,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,mBAAmB;AACrB;AAGA,MAAM,uBAA6D;AAAA,EACjE,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,WAAW;AAAA,EACX,cAAc,CAAC,IAAI,EAAE;AAAA,EACrB,oBAAoB,SAAS;AAC/B;AAEA,MAAM,oBAA6C;AAAA,EACjD,GAAG;AAAA,EACH,GAAG;AACL;AAEA,MAAM,4BAA4B;AAAA,EAChC,GAAG;AAAA,EACH,GAAG;AACL;AAGA,MAAM,yBAA+C;AAAA,EACnD,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,WAAW;AAAA,EACX,cAAc,CAAC,IAAI,EAAE;AAAA,EACrB,oBAAoB,SAAS;AAAA,EAC7B,GAAG;AACL;AAGA,MAAM,+BAA+B;AAAA,EACnC,GAAG;AAAA,EACH,GAAG;AAAA,EACH,mBAAmB;AACrB;AAEA,MAAM,wBAAwB;AAAA,EAC5B,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,WAAW;AAAA,EACX,oBAAoB,SAAS;AAC/B;AAEA,MAAM,wBAAwB,MAC5B,4EAEE;AAAA;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,UAAU,aAAAA,QAAM,UAA0B;AAAA,MAC1C,QAAM;AAAA,MACN,gBAAgB,CAAC;AAAA,MAEjB,sDAAC,SAAI,qBAAO;AAAA;AAAA,EACd;AAAA,EACA;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,UAAU,aAAAA,QAAM,UAA0B;AAAA,MAC1C,QAAM;AAAA,MACN,gBAAgB,CAAC;AAAA,MAEjB,sDAAC,SAAI,qBAAO;AAAA;AAAA,EACd;AAAA,EACA,4CAAC,4BAAiB,GAAG,uBAAuB,UAAU,aAAAA,QAAM,UAA0B,GAAG,QAAM,MAAC,gBAAgB,CAAC,GAC/G,sDAAC,SAAI,qBAAO,GACd;AAAA,GAEF;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable @typescript-eslint/no-unused-vars, no-unused-vars */\nimport React from 'react';\nimport { FloatingWrapper } from '../index.js';\nimport type { DSHookFloatingContextT } from '../index.js';\n\n// test we expose the namespace and the namespace follows our deliverable conventions\ntype ComponentPropsForApp = DSHookFloatingContextT.Props;\ntype ComponentPropsInternals = DSHookFloatingContextT.InternalProps;\ntype ComponentPropsDefaultProps = DSHookFloatingContextT.DefaultProps;\ntype ComponentPropsOptionalProps = DSHookFloatingContextT.OptionalProps;\n\nconst testOptionalProps: ComponentPropsOptionalProps = {};\n\n// difference Props and InternalProps is that InternalProps has all the default props filled in\n// Props allows for partial defaults\nconst testPartialDefaults: Partial<ComponentPropsDefaultProps> = {\n withoutAnimation: false,\n withoutPortal: false,\n animationDuration: 300,\n};\n\n// InternalProps requires all defaults to be filled in\nconst testCompleteDefaults: Required<ComponentPropsDefaultProps> = {\n withoutAnimation: false,\n withoutPortal: false,\n animationDuration: 300,\n placement: 'top',\n customOffset: [10, 10],\n portalDOMContainer: document.body,\n closeOnEscape: false,\n returnFocusToReference: false,\n};\n\nconst testInternalProps: ComponentPropsInternals = {\n ...testOptionalProps,\n ...testCompleteDefaults,\n};\n\nconst testInternalPropsAsSyntax = {\n ...testOptionalProps,\n ...testCompleteDefaults,\n} as ComponentPropsInternals;\n\n// using the explicit type definition, if there is an error, it will be marked on the key that is wrong\nconst testExplicitDefinition: ComponentPropsForApp = {\n animationDuration: 300,\n withoutAnimation: false,\n withoutPortal: false,\n placement: 'top',\n customOffset: [10, 10],\n portalDOMContainer: document.body,\n closeOnEscape: false,\n returnFocusToReference: false,\n ...testOptionalProps,\n};\n\n// using the \"as\" syntax, if there is an error, it will be marking the whole object as wrong because it is not compatible with the type\nconst testInferedTypeCompatibility = {\n ...testOptionalProps,\n ...testPartialDefaults,\n animationDuration: 300,\n} as ComponentPropsForApp;\n\nconst testDefinitionAsConst = {\n animationDuration: 300,\n withoutAnimation: false,\n withoutPortal: false,\n placement: 'top',\n portalDOMContainer: document.body,\n} as const;\n\nconst ExampleUsageComponent = () => (\n <>\n {/* works with explicitly casted props, all syntaxes */}\n <FloatingWrapper\n {...testExplicitDefinition}\n innerRef={React.createRef<HTMLDivElement>()}\n isOpen\n floatingStyles={{}}\n >\n <div>Content</div>\n </FloatingWrapper>\n <FloatingWrapper\n {...testInferedTypeCompatibility}\n innerRef={React.createRef<HTMLDivElement>()}\n isOpen\n floatingStyles={{}}\n >\n <div>Content</div>\n </FloatingWrapper>\n <FloatingWrapper {...testDefinitionAsConst} innerRef={React.createRef<HTMLDivElement>()} isOpen floatingStyles={{}}>\n <div>Content</div>\n </FloatingWrapper>\n {/* works with inline values */}\n </>\n);\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;AAAA,YAAuB;ACwErB;AAvEF,mBAAkB;AAClB,eAAgC;AAShC,MAAM,oBAAiD,CAAC;AAIxD,MAAM,sBAA2D;AAAA,EAC/D,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,mBAAmB;AACrB;AAGA,MAAM,uBAA6D;AAAA,EACjE,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,mBAAmB;AAAA,EACnB,WAAW;AAAA,EACX,cAAc,CAAC,IAAI,EAAE;AAAA,EACrB,oBAAoB,SAAS;AAAA,EAC7B,eAAe;AAAA,EACf,wBAAwB;AAC1B;AAEA,MAAM,oBAA6C;AAAA,EACjD,GAAG;AAAA,EACH,GAAG;AACL;AAEA,MAAM,4BAA4B;AAAA,EAChC,GAAG;AAAA,EACH,GAAG;AACL;AAGA,MAAM,yBAA+C;AAAA,EACnD,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,WAAW;AAAA,EACX,cAAc,CAAC,IAAI,EAAE;AAAA,EACrB,oBAAoB,SAAS;AAAA,EAC7B,eAAe;AAAA,EACf,wBAAwB;AAAA,EACxB,GAAG;AACL;AAGA,MAAM,+BAA+B;AAAA,EACnC,GAAG;AAAA,EACH,GAAG;AAAA,EACH,mBAAmB;AACrB;AAEA,MAAM,wBAAwB;AAAA,EAC5B,mBAAmB;AAAA,EACnB,kBAAkB;AAAA,EAClB,eAAe;AAAA,EACf,WAAW;AAAA,EACX,oBAAoB,SAAS;AAC/B;AAEA,MAAM,wBAAwB,MAC5B,4EAEE;AAAA;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,UAAU,aAAAA,QAAM,UAA0B;AAAA,MAC1C,QAAM;AAAA,MACN,gBAAgB,CAAC;AAAA,MAEjB,sDAAC,SAAI,qBAAO;AAAA;AAAA,EACd;AAAA,EACA;AAAA,IAAC;AAAA;AAAA,MACE,GAAG;AAAA,MACJ,UAAU,aAAAA,QAAM,UAA0B;AAAA,MAC1C,QAAM;AAAA,MACN,gBAAgB,CAAC;AAAA,MAEjB,sDAAC,SAAI,qBAAO;AAAA;AAAA,EACd;AAAA,EACA,4CAAC,4BAAiB,GAAG,uBAAuB,UAAU,aAAAA,QAAM,UAA0B,GAAG,QAAM,MAAC,gBAAgB,CAAC,GAC/G,sDAAC,SAAI,qBAAO,GACd;AAAA,GAEF;",
6
6
  "names": ["React"]
7
7
  }
@@ -50,7 +50,8 @@ const useComputedPositionStyles = (config) => {
50
50
  const [floatingStyles, setFloatingStyles] = (0, import_react.useState)({
51
51
  position: "absolute",
52
52
  zIndex: 3e3,
53
- visibility: "hidden",
53
+ opacity: 0,
54
+ pointerEvents: "none",
54
55
  willChange: "transform"
55
56
  });
56
57
  const [hasComputedOnce, setHasComputedOnce] = (0, import_react.useState)(false);
@@ -65,12 +66,11 @@ const useComputedPositionStyles = (config) => {
65
66
  customOffset,
66
67
  withoutPortal
67
68
  });
68
- setFloatingStyles((prev) => ({
69
- position: "absolute",
70
- zIndex: 3e3,
71
- ...prev,
72
- ...coordsStyle
73
- }));
69
+ const { transform, top, left } = coordsStyle;
70
+ setFloatingStyles((prev) => {
71
+ if (prev.transform === transform) return prev;
72
+ return { position: "absolute", zIndex: 3e3, ...prev, transform, top, left };
73
+ });
74
74
  setArrowStyles({ style: coordsArrow, placement: finalPlacement });
75
75
  setHasComputedOnce(true);
76
76
  }, [canCompute, reference, floating, placement, placementOrderPreference, customOffset, withoutPortal]);
@@ -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();
@@ -99,7 +101,8 @@ const useComputedPositionStyles = (config) => {
99
101
  const resetVisibilityOnly = (0, import_react.useCallback)(() => {
100
102
  setFloatingStyles((prev) => ({
101
103
  ...prev,
102
- visibility: "hidden"
104
+ opacity: 0,
105
+ pointerEvents: "none"
103
106
  }));
104
107
  }, []);
105
108
  return (0, import_react.useMemo)(
@@ -109,7 +112,7 @@ const useComputedPositionStyles = (config) => {
109
112
  hasComputedOnce,
110
113
  updateStyles: forceUpdatePosition,
111
114
  debouncedUpdateStyles,
112
- mutableUpdateStyles,
115
+ mutableUpdateStyles: mutableDebouncedStyles,
113
116
  resetVisibilityOnly
114
117
  }),
115
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 // Important: do not initialize top/left to (0,0); keep hidden until the first computation\n const [floatingStyles, setFloatingStyles] = useState<CSSProperties>({\n position: 'absolute',\n zIndex: 3000,\n visibility: 'hidden',\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\n // Do not touch visibility here; it is managed outside depending on open/hasComputedOnce\n setFloatingStyles((prev) => ({\n position: 'absolute',\n zIndex: 3000,\n ...prev,\n ...coordsStyle,\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\n const resetVisibilityOnly = useCallback(() => {\n setFloatingStyles((prev) => ({\n ...prev,\n visibility: 'hidden',\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;AAGtG,QAAM,CAAC,gBAAgB,iBAAiB,QAAI,uBAAwB;AAAA,IAClE,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,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;AAGD,sBAAkB,CAAC,UAAU;AAAA,MAC3B,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,GAAG;AAAA,MACH,GAAG;AAAA,IACL,EAAE;AACF,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;AAGL,QAAM,0BAAsB,0BAAY,MAAM;AAC5C,sBAAkB,CAAC,UAAU;AAAA,MAC3B,GAAG;AAAA,MACH,YAAY;AAAA,IACd,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
  }
@@ -75,8 +75,8 @@ const computePosition = (props) => {
75
75
  y += window.scrollY;
76
76
  } else {
77
77
  const op = (0, import_floatingPositioning.getOffsetParentData)(floating);
78
- x = x - op.left + op.scrollLeft;
79
- y = y - op.top + op.scrollTop;
78
+ x = x + clippingRect.left - op.left + op.scrollLeft;
79
+ y = y + clippingRect.top - op.top + op.scrollTop;
80
80
  }
81
81
  ({ x, y } = (0, import_floatingPositioning.applyShift)(x, y, overflows));
82
82
  const coordsArrow = (0, import_getArrowOffset.getArrowOffsetDynamic)({
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../src/utils/computePosition.ts", "../../../../../../scripts/build/transpile/react-shim.js"],
4
- "sourcesContent": ["/* eslint-disable max-lines */\n/* eslint-disable no-nested-ternary */\n/* eslint-disable complexity */\n/* eslint-disable max-statements */\n/* eslint-disable @typescript-eslint/no-use-before-define */\n/* eslint-disable max-params */\n/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable arrow-body-style */\nimport type { DSHookFloatingContextT } from '../react-desc-prop-types.js';\nimport { getExpandedFallbackPlacements } from './getExpandedFallbackPlacements.js';\nimport { getArrowOffsetDynamic } from './getArrowOffset.js';\nimport { detectOverflow } from './detectOverflow.js';\nimport {\n applyShift,\n adjustForFixedParent,\n expandWithVariations,\n fits,\n getClippingParent,\n getOverflowScore,\n getOffsetParentData,\n getViewportRect,\n type RectLike,\n type OverflowOffsets,\n} from './floatingPositioning.js';\n\ninterface ComputePositionProps {\n reference: Element;\n floating: HTMLElement;\n placement: DSHookFloatingContextT.PopperPlacementsT;\n placementOrderPreference?: DSHookFloatingContextT.PopperPlacementsT[];\n customOffset: [number, number];\n withoutPortal: boolean;\n}\n\nexport const computePosition = (props: ComputePositionProps) => {\n const { reference, floating, placement, placementOrderPreference, customOffset, withoutPortal } = props;\n\n // When WITHOUT portal: only apply fixed-parent offsets (absolute parents scroll and must NOT be treated as fixed)\n const parentOffsets = withoutPortal ? adjustForFixedParent(reference) : { top: 0, left: 0 };\n\n const referenceRect = reference.getBoundingClientRect();\n const floatingRect = floating.getBoundingClientRect();\n\n const fallbackPlacements = placementOrderPreference || getExpandedFallbackPlacements(placement);\n\n const placements = expandWithVariations(\n [placement].concat(fallbackPlacements as DSHookFloatingContextT.PopperPlacementsT[]),\n );\n\n // Boundary selection:\n // - Portal => viewport (inset by body padding for Storybook)\n // - No portal => nearest clipping/scroll container (fallback viewport rect)\n const clippingParent = withoutPortal ? getClippingParent(reference) : null;\n const clippingRect: RectLike = clippingParent ? clippingParent.getBoundingClientRect() : getViewportRect();\n\n // Best-fit selection:\n // 1) choose first placement that fully fits\n // 2) otherwise choose placement with smallest max overflow, tie-break by total overflow\n let bestPlacement = placement;\n let bestOverflows: OverflowOffsets | null = null;\n let bestScore = { total: Number.POSITIVE_INFINITY, maxSide: Number.POSITIVE_INFINITY };\n\n for (let i = 0; i < placements.length; i += 1) {\n const currentPlacement = placements[i];\n\n const overflows = detectOverflow(referenceRect, floatingRect, currentPlacement, customOffset, clippingRect);\n\n if (fits(overflows)) {\n bestPlacement = currentPlacement;\n bestOverflows = overflows;\n break;\n }\n\n const score = getOverflowScore(overflows);\n\n const isBetter =\n score.maxSide < bestScore.maxSide || (score.maxSide === bestScore.maxSide && score.total < bestScore.total);\n\n if (isBetter) {\n bestPlacement = currentPlacement;\n bestOverflows = overflows;\n bestScore = score;\n }\n }\n\n const finalPlacement = bestPlacement;\n\n const overflows =\n bestOverflows ?? detectOverflow(referenceRect, floatingRect, finalPlacement, customOffset, clippingRect);\n\n // Convert overflow -> coordinates.\n // detectOverflow uses viewport/clipping-rect coordinates.\n //\n // - If tooltip is rendered IN A PORTAL (withoutPortal === false) and is positioned with `position: absolute`,\n // convert viewport coords to page coords by adding window.scrollX/Y.\n //\n // - If tooltip is rendered WITHOUT portal, convert viewport coords to offsetParent coords\n // (subtract offsetParent rect, add its scroll).\n let x = -overflows.left - parentOffsets.left;\n let y = -overflows.top - parentOffsets.top;\n\n if (!withoutPortal) {\n x += window.scrollX;\n y += window.scrollY;\n } else {\n const op = getOffsetParentData(floating);\n x = x - op.left + op.scrollLeft;\n y = y - op.top + op.scrollTop;\n }\n\n // Always shift back inside boundary\n ({ x, y } = applyShift(x, y, overflows));\n\n const coordsArrow = getArrowOffsetDynamic({\n placement: finalPlacement,\n referenceRect,\n floatingRect,\n x,\n y,\n withoutPortal,\n parentOffsets,\n floatingEl: floating,\n arrowPadding: 12,\n });\n\n return {\n coordsStyle: {\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n top: 0,\n left: 0,\n },\n finalPlacement,\n coordsArrow,\n };\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADSvB,2CAA8C;AAC9C,4BAAsC;AACtC,4BAA+B;AAC/B,iCAWO;AAWA,MAAM,kBAAkB,CAAC,UAAgC;AAC9D,QAAM,EAAE,WAAW,UAAU,WAAW,0BAA0B,cAAc,cAAc,IAAI;AAGlG,QAAM,gBAAgB,oBAAgB,iDAAqB,SAAS,IAAI,EAAE,KAAK,GAAG,MAAM,EAAE;AAE1F,QAAM,gBAAgB,UAAU,sBAAsB;AACtD,QAAM,eAAe,SAAS,sBAAsB;AAEpD,QAAM,qBAAqB,gCAA4B,oEAA8B,SAAS;AAE9F,QAAM,iBAAa;AAAA,IACjB,CAAC,SAAS,EAAE,OAAO,kBAAgE;AAAA,EACrF;AAKA,QAAM,iBAAiB,oBAAgB,8CAAkB,SAAS,IAAI;AACtE,QAAM,eAAyB,iBAAiB,eAAe,sBAAsB,QAAI,4CAAgB;AAKzG,MAAI,gBAAgB;AACpB,MAAI,gBAAwC;AAC5C,MAAI,YAAY,EAAE,OAAO,OAAO,mBAAmB,SAAS,OAAO,kBAAkB;AAErF,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK,GAAG;AAC7C,UAAM,mBAAmB,WAAW,CAAC;AAErC,UAAMA,iBAAY,sCAAe,eAAe,cAAc,kBAAkB,cAAc,YAAY;AAE1G,YAAI,iCAAKA,UAAS,GAAG;AACnB,sBAAgB;AAChB,sBAAgBA;AAChB;AAAA,IACF;AAEA,UAAM,YAAQ,6CAAiBA,UAAS;AAExC,UAAM,WACJ,MAAM,UAAU,UAAU,WAAY,MAAM,YAAY,UAAU,WAAW,MAAM,QAAQ,UAAU;AAEvG,QAAI,UAAU;AACZ,sBAAgB;AAChB,sBAAgBA;AAChB,kBAAY;AAAA,IACd;AAAA,EACF;AAEA,QAAM,iBAAiB;AAEvB,QAAM,YACJ,qBAAiB,sCAAe,eAAe,cAAc,gBAAgB,cAAc,YAAY;AAUzG,MAAI,IAAI,CAAC,UAAU,OAAO,cAAc;AACxC,MAAI,IAAI,CAAC,UAAU,MAAM,cAAc;AAEvC,MAAI,CAAC,eAAe;AAClB,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd,OAAO;AACL,UAAM,SAAK,gDAAoB,QAAQ;AACvC,QAAI,IAAI,GAAG,OAAO,GAAG;AACrB,QAAI,IAAI,GAAG,MAAM,GAAG;AAAA,EACtB;AAGA,GAAC,EAAE,GAAG,EAAE,QAAI,uCAAW,GAAG,GAAG,SAAS;AAEtC,QAAM,kBAAc,6CAAsB;AAAA,IACxC,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,cAAc;AAAA,EAChB,CAAC;AAED,SAAO;AAAA,IACL,aAAa;AAAA,MACX,WAAW,eAAe,KAAK,MAAM,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC;AAAA,MAC3D,KAAK;AAAA,MACL,MAAM;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["/* eslint-disable max-lines */\n/* eslint-disable no-nested-ternary */\n/* eslint-disable complexity */\n/* eslint-disable max-statements */\n/* eslint-disable @typescript-eslint/no-use-before-define */\n/* eslint-disable max-params */\n/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable arrow-body-style */\nimport type { DSHookFloatingContextT } from '../react-desc-prop-types.js';\nimport { getExpandedFallbackPlacements } from './getExpandedFallbackPlacements.js';\nimport { getArrowOffsetDynamic } from './getArrowOffset.js';\nimport { detectOverflow } from './detectOverflow.js';\nimport {\n applyShift,\n adjustForFixedParent,\n expandWithVariations,\n fits,\n getClippingParent,\n getOverflowScore,\n getOffsetParentData,\n getViewportRect,\n type RectLike,\n type OverflowOffsets,\n} from './floatingPositioning.js';\n\ninterface ComputePositionProps {\n reference: Element;\n floating: HTMLElement;\n placement: DSHookFloatingContextT.PopperPlacementsT;\n placementOrderPreference?: DSHookFloatingContextT.PopperPlacementsT[];\n customOffset: [number, number];\n withoutPortal: boolean;\n}\n\nexport const computePosition = (props: ComputePositionProps) => {\n const { reference, floating, placement, placementOrderPreference, customOffset, withoutPortal } = props;\n\n // When WITHOUT portal: only apply fixed-parent offsets (absolute parents scroll and must NOT be treated as fixed)\n const parentOffsets = withoutPortal ? adjustForFixedParent(reference) : { top: 0, left: 0 };\n\n const referenceRect = reference.getBoundingClientRect();\n const floatingRect = floating.getBoundingClientRect();\n\n const fallbackPlacements = placementOrderPreference || getExpandedFallbackPlacements(placement);\n\n const placements = expandWithVariations(\n [placement].concat(fallbackPlacements as DSHookFloatingContextT.PopperPlacementsT[]),\n );\n\n // Boundary selection:\n // - Portal => viewport (inset by body padding for Storybook)\n // - No portal => nearest clipping/scroll container (fallback viewport rect)\n const clippingParent = withoutPortal ? getClippingParent(reference) : null;\n const clippingRect: RectLike = clippingParent ? clippingParent.getBoundingClientRect() : getViewportRect();\n\n // Best-fit selection:\n // 1) choose first placement that fully fits\n // 2) otherwise choose placement with smallest max overflow, tie-break by total overflow\n let bestPlacement = placement;\n let bestOverflows: OverflowOffsets | null = null;\n let bestScore = { total: Number.POSITIVE_INFINITY, maxSide: Number.POSITIVE_INFINITY };\n\n for (let i = 0; i < placements.length; i += 1) {\n const currentPlacement = placements[i];\n\n const overflows = detectOverflow(referenceRect, floatingRect, currentPlacement, customOffset, clippingRect);\n\n if (fits(overflows)) {\n bestPlacement = currentPlacement;\n bestOverflows = overflows;\n break;\n }\n\n const score = getOverflowScore(overflows);\n\n const isBetter =\n score.maxSide < bestScore.maxSide || (score.maxSide === bestScore.maxSide && score.total < bestScore.total);\n\n if (isBetter) {\n bestPlacement = currentPlacement;\n bestOverflows = overflows;\n bestScore = score;\n }\n }\n\n const finalPlacement = bestPlacement;\n\n const overflows =\n bestOverflows ?? detectOverflow(referenceRect, floatingRect, finalPlacement, customOffset, clippingRect);\n\n // Convert overflow -> coordinates.\n // detectOverflow uses viewport/clipping-rect coordinates.\n //\n // - If tooltip is rendered IN A PORTAL (withoutPortal === false) and is positioned with `position: absolute`,\n // convert viewport coords to page coords by adding window.scrollX/Y.\n //\n // - If tooltip is rendered WITHOUT portal, convert viewport coords to offsetParent coords\n // (subtract offsetParent rect, add its scroll).\n let x = -overflows.left - parentOffsets.left;\n let y = -overflows.top - parentOffsets.top;\n\n if (!withoutPortal) {\n x += window.scrollX;\n y += window.scrollY;\n } else {\n // clippingRect.top/left may be non-zero (e.g. a scroll container below the viewport top).\n // The overflow values already subtracted it; add it back so we convert pure viewport coords\n // to offset-parent-relative coords: viewportCoord - offsetParent.top + offsetParent.scrollTop\n const op = getOffsetParentData(floating);\n x = x + clippingRect.left - op.left + op.scrollLeft;\n y = y + clippingRect.top - op.top + op.scrollTop;\n }\n\n // Always shift back inside boundary\n ({ x, y } = applyShift(x, y, overflows));\n\n const coordsArrow = getArrowOffsetDynamic({\n placement: finalPlacement,\n referenceRect,\n floatingRect,\n x,\n y,\n withoutPortal,\n parentOffsets,\n floatingEl: floating,\n arrowPadding: 12,\n });\n\n return {\n coordsStyle: {\n transform: `translate3d(${Math.round(x)}px, ${Math.round(y)}px, 0)`,\n top: 0,\n left: 0,\n },\n finalPlacement,\n coordsArrow,\n };\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADSvB,2CAA8C;AAC9C,4BAAsC;AACtC,4BAA+B;AAC/B,iCAWO;AAWA,MAAM,kBAAkB,CAAC,UAAgC;AAC9D,QAAM,EAAE,WAAW,UAAU,WAAW,0BAA0B,cAAc,cAAc,IAAI;AAGlG,QAAM,gBAAgB,oBAAgB,iDAAqB,SAAS,IAAI,EAAE,KAAK,GAAG,MAAM,EAAE;AAE1F,QAAM,gBAAgB,UAAU,sBAAsB;AACtD,QAAM,eAAe,SAAS,sBAAsB;AAEpD,QAAM,qBAAqB,gCAA4B,oEAA8B,SAAS;AAE9F,QAAM,iBAAa;AAAA,IACjB,CAAC,SAAS,EAAE,OAAO,kBAAgE;AAAA,EACrF;AAKA,QAAM,iBAAiB,oBAAgB,8CAAkB,SAAS,IAAI;AACtE,QAAM,eAAyB,iBAAiB,eAAe,sBAAsB,QAAI,4CAAgB;AAKzG,MAAI,gBAAgB;AACpB,MAAI,gBAAwC;AAC5C,MAAI,YAAY,EAAE,OAAO,OAAO,mBAAmB,SAAS,OAAO,kBAAkB;AAErF,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK,GAAG;AAC7C,UAAM,mBAAmB,WAAW,CAAC;AAErC,UAAMA,iBAAY,sCAAe,eAAe,cAAc,kBAAkB,cAAc,YAAY;AAE1G,YAAI,iCAAKA,UAAS,GAAG;AACnB,sBAAgB;AAChB,sBAAgBA;AAChB;AAAA,IACF;AAEA,UAAM,YAAQ,6CAAiBA,UAAS;AAExC,UAAM,WACJ,MAAM,UAAU,UAAU,WAAY,MAAM,YAAY,UAAU,WAAW,MAAM,QAAQ,UAAU;AAEvG,QAAI,UAAU;AACZ,sBAAgB;AAChB,sBAAgBA;AAChB,kBAAY;AAAA,IACd;AAAA,EACF;AAEA,QAAM,iBAAiB;AAEvB,QAAM,YACJ,qBAAiB,sCAAe,eAAe,cAAc,gBAAgB,cAAc,YAAY;AAUzG,MAAI,IAAI,CAAC,UAAU,OAAO,cAAc;AACxC,MAAI,IAAI,CAAC,UAAU,MAAM,cAAc;AAEvC,MAAI,CAAC,eAAe;AAClB,SAAK,OAAO;AACZ,SAAK,OAAO;AAAA,EACd,OAAO;AAIL,UAAM,SAAK,gDAAoB,QAAQ;AACvC,QAAI,IAAI,aAAa,OAAO,GAAG,OAAO,GAAG;AACzC,QAAI,IAAI,aAAa,MAAM,GAAG,MAAM,GAAG;AAAA,EACzC;AAGA,GAAC,EAAE,GAAG,EAAE,QAAI,uCAAW,GAAG,GAAG,SAAS;AAEtC,QAAM,kBAAc,6CAAsB;AAAA,IACxC,WAAW;AAAA,IACX;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,YAAY;AAAA,IACZ,cAAc;AAAA,EAChB,CAAC;AAED,SAAO;AAAA,IACL,aAAa;AAAA,MACX,WAAW,eAAe,KAAK,MAAM,CAAC,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC;AAAA,MAC3D,KAAK;AAAA,MACL,MAAM;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;",
6
6
  "names": ["overflows"]
7
7
  }
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import React2, { useMemo, useEffect } from "react";
2
+ import React2, { useMemo } from "react";
3
3
  import {
4
4
  useMemoMergePropsWithDefault,
5
5
  useValidateTypescriptPropTypes,
@@ -9,8 +9,17 @@ import { useHeadlessTooltip } from "@elliemae/ds-hooks-headless-tooltip";
9
9
  import { defaultProps, DSFloatingContextPropTypes } from "./react-desc-prop-types.js";
10
10
  import { useComputedPositionStyles } from "./useComputedPositionStyles.js";
11
11
  import { usePositionObserver } from "./utils/positionObserver.js";
12
+ import { useLatestRef } from "./hooks/useLatestRef.js";
13
+ import { useResolvedReference } from "./hooks/useResolvedReference.js";
14
+ import { useFloatingClickOutside } from "./hooks/useFloatingClickOutside.js";
15
+ import { useFloatingEscape } from "./hooks/useFloatingEscape.js";
16
+ import { useFloatingResizeObserver } from "./hooks/useFloatingResizeObserver.js";
12
17
  const useFloatingContext = (props = {}) => {
13
- const propsWithDefault = useMemoMergePropsWithDefault(props, defaultProps);
18
+ const { onClickOutside, onEscape, onOpen, onClose, externalReferenceElement, ...stableProps } = props;
19
+ const propsWithDefault = useMemoMergePropsWithDefault(
20
+ stableProps,
21
+ defaultProps
22
+ );
14
23
  useValidateTypescriptPropTypes(propsWithDefault, DSFloatingContextPropTypes, "FloatingContext");
15
24
  const {
16
25
  withoutPortal,
@@ -20,10 +29,14 @@ const useFloatingContext = (props = {}) => {
20
29
  placement,
21
30
  customOffset,
22
31
  placementOrderPreference,
23
- onOpen,
24
- onClose,
25
- externallyControlledIsOpen
32
+ externallyControlledIsOpen,
33
+ closeOnEscape,
34
+ returnFocusToReference
26
35
  } = propsWithDefault;
36
+ const onClickOutsideRef = useLatestRef(onClickOutside);
37
+ const onEscapeRef = useLatestRef(onEscape);
38
+ const onOpenRef = useLatestRef(onOpen);
39
+ const onCloseRef = useLatestRef(onClose);
27
40
  const [internalIsOpen, setInternalIsOpen] = React2.useState(false);
28
41
  const isOpen = useMemo(
29
42
  () => externallyControlledIsOpen !== void 0 ? externallyControlledIsOpen : internalIsOpen,
@@ -31,14 +44,24 @@ const useFloatingContext = (props = {}) => {
31
44
  );
32
45
  const handleOpen = React2.useCallback(() => {
33
46
  setInternalIsOpen(true);
34
- onOpen?.();
35
- }, [onOpen]);
47
+ onOpenRef.current?.();
48
+ }, [onOpenRef]);
36
49
  const handleClose = React2.useCallback(() => {
37
50
  setInternalIsOpen(false);
38
- onClose?.();
39
- }, [onClose]);
51
+ onCloseRef.current?.();
52
+ }, [onCloseRef]);
40
53
  const tooltipHelpers = useHeadlessTooltip({ onOpen: handleOpen, onClose: handleClose });
41
- const { setReferenceElement, referenceElement, hideTooltip, showTooltip } = tooltipHelpers;
54
+ const {
55
+ setReferenceElement: setInternalReferenceElement,
56
+ referenceElement: internalReferenceElement,
57
+ hideTooltip,
58
+ showTooltip
59
+ } = tooltipHelpers;
60
+ const { referenceElement, setReferenceElement } = useResolvedReference({
61
+ externalReferenceElement,
62
+ internalReferenceElement,
63
+ setInternalReferenceElement
64
+ });
42
65
  const [floating, setFloating] = React2.useState(null);
43
66
  const { arrowStyles, floatingStyles, hasComputedOnce, updateStyles, debouncedUpdateStyles, mutableUpdateStyles } = useComputedPositionStyles({
44
67
  reference: referenceElement,
@@ -50,30 +73,38 @@ const useFloatingContext = (props = {}) => {
50
73
  preventComputing: !isOpen,
51
74
  debounceMs: 150
52
75
  });
53
- useEffect(() => {
54
- if (!isOpen) return;
55
- const observers = [];
56
- if (floating) {
57
- const roFloating = new ResizeObserver(() => {
58
- mutableUpdateStyles.current();
59
- });
60
- roFloating.observe(floating);
61
- observers.push(roFloating);
62
- }
63
- return () => observers.forEach((o) => o.disconnect());
64
- }, [isOpen, floating, updateStyles, mutableUpdateStyles]);
76
+ const floatingResizeOberverConfig = useMemo(
77
+ () => ({ enabled: isOpen, floating, onResize: () => mutableUpdateStyles.current() }),
78
+ [isOpen, floating, mutableUpdateStyles]
79
+ );
80
+ useFloatingResizeObserver(floatingResizeOberverConfig);
65
81
  const onObservedMovement = React2.useCallback(() => {
66
82
  if (!isOpen) return;
67
83
  debouncedUpdateStyles();
68
84
  }, [isOpen, debouncedUpdateStyles]);
69
85
  usePositionObserver(referenceElement, onObservedMovement);
70
- const computedFloatingStyles = useMemo(
71
- () => ({
86
+ useFloatingClickOutside({
87
+ enabled: isOpen,
88
+ floating,
89
+ reference: referenceElement,
90
+ callbackRef: onClickOutsideRef
91
+ });
92
+ useFloatingEscape({
93
+ enabled: isOpen && closeOnEscape,
94
+ floating,
95
+ reference: referenceElement,
96
+ onEscapeRef,
97
+ handleClose,
98
+ returnFocusToReference
99
+ });
100
+ const computedFloatingStyles = useMemo(() => {
101
+ const isInteractive = isOpen && hasComputedOnce;
102
+ return {
72
103
  ...floatingStyles,
73
- visibility: isOpen && hasComputedOnce ? "visible" : "hidden"
74
- }),
75
- [floatingStyles, hasComputedOnce, isOpen]
76
- );
104
+ opacity: isInteractive ? 1 : 0,
105
+ pointerEvents: isInteractive ? "auto" : "none"
106
+ };
107
+ }, [floatingStyles, hasComputedOnce, isOpen]);
77
108
  const refs = useMemo(
78
109
  () => ({
79
110
  setReference: setReferenceElement,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../scripts/build/transpile/react-shim.js", "../../src/DSFloatingContext.tsx"],
4
- "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable max-lines */\n/* eslint-disable consistent-return */\n/* eslint-disable max-statements */\nimport React, { useMemo, useEffect } from 'react';\nimport {\n useMemoMergePropsWithDefault,\n useValidateTypescriptPropTypes,\n describe,\n type ValidationMap,\n} from '@elliemae/ds-props-helpers';\nimport { useHeadlessTooltip } from '@elliemae/ds-hooks-headless-tooltip';\nimport type { DSHookFloatingContextT } from './react-desc-prop-types.js';\nimport { defaultProps, DSFloatingContextPropTypes } from './react-desc-prop-types.js';\nimport { useComputedPositionStyles } from './useComputedPositionStyles.js';\nimport { usePositionObserver } from './utils/positionObserver.js';\n\nconst useFloatingContext = (props: DSHookFloatingContextT.Props = {}) => {\n const propsWithDefault = useMemoMergePropsWithDefault<DSHookFloatingContextT.InternalProps>(props, defaultProps);\n useValidateTypescriptPropTypes(propsWithDefault, DSFloatingContextPropTypes, 'FloatingContext');\n\n const {\n withoutPortal,\n withoutAnimation,\n portalDOMContainer,\n animationDuration,\n placement,\n customOffset,\n placementOrderPreference,\n onOpen,\n onClose,\n externallyControlledIsOpen,\n } = propsWithDefault;\n\n const [internalIsOpen, setInternalIsOpen] = React.useState<boolean>(false);\n const isOpen = useMemo(\n () => (externallyControlledIsOpen !== undefined ? externallyControlledIsOpen : internalIsOpen),\n [externallyControlledIsOpen, internalIsOpen],\n );\n\n const handleOpen = React.useCallback(() => {\n setInternalIsOpen(true);\n onOpen?.();\n }, [onOpen]);\n\n const handleClose = React.useCallback(() => {\n setInternalIsOpen(false);\n onClose?.();\n }, [onClose]);\n\n const tooltipHelpers = useHeadlessTooltip({ onOpen: handleOpen, onClose: handleClose });\n const { setReferenceElement, referenceElement, hideTooltip, showTooltip } = tooltipHelpers;\n\n // Anchor and floating refs\n const [floating, setFloating] = React.useState<HTMLElement | null>(null);\n\n // Compute position (skip when closed)\n const { arrowStyles, floatingStyles, hasComputedOnce, updateStyles, debouncedUpdateStyles, mutableUpdateStyles } =\n useComputedPositionStyles({\n reference: referenceElement,\n floating,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n preventComputing: !isOpen,\n debounceMs: 150,\n });\n\n useEffect(() => {\n if (!isOpen) return;\n\n const observers: ResizeObserver[] = [];\n\n if (floating) {\n const roFloating = new ResizeObserver(() => {\n mutableUpdateStyles.current();\n });\n roFloating.observe(floating);\n observers.push(roFloating);\n }\n\n return () => observers.forEach((o) => o.disconnect());\n }, [isOpen, floating, updateStyles, mutableUpdateStyles]);\n\n const onObservedMovement = React.useCallback(() => {\n if (!isOpen) return;\n debouncedUpdateStyles();\n }, [isOpen, debouncedUpdateStyles]);\n usePositionObserver(referenceElement, onObservedMovement);\n\n // Expose visibility: only visible if open and already computed at least once\n const computedFloatingStyles = useMemo<React.CSSProperties>(\n () => ({\n ...floatingStyles,\n visibility: isOpen && hasComputedOnce ? 'visible' : 'hidden',\n }),\n [floatingStyles, hasComputedOnce, isOpen],\n );\n\n const refs = useMemo(\n () => ({\n setReference: setReferenceElement,\n setFloating,\n floating,\n reference: referenceElement,\n }),\n [setReferenceElement, floating, referenceElement],\n );\n\n const handlers = useMemo(\n () => ({\n onMouseEnter: tooltipHelpers.onMouseEnter,\n onMouseLeave: tooltipHelpers.onMouseLeave,\n onFocus: tooltipHelpers.onFocus,\n onBlur: tooltipHelpers.onBlur,\n }),\n [tooltipHelpers.onBlur, tooltipHelpers.onFocus, tooltipHelpers.onMouseEnter, tooltipHelpers.onMouseLeave],\n );\n\n return useMemo(\n () => ({\n refs,\n floatingStyles: computedFloatingStyles,\n handlers,\n isOpen,\n arrowStyles,\n hideTooltip,\n showTooltip,\n context: {\n withoutPortal,\n withoutAnimation,\n portalDOMContainer,\n animationDuration,\n },\n mutableUpdateStyles,\n forceUpdatePosition: updateStyles,\n }),\n [\n refs,\n computedFloatingStyles,\n handlers,\n isOpen,\n arrowStyles,\n hideTooltip,\n showTooltip,\n withoutPortal,\n withoutAnimation,\n portalDOMContainer,\n animationDuration,\n mutableUpdateStyles,\n updateStyles,\n ],\n );\n};\n\nuseFloatingContext.displayName = 'FloatingContext';\nconst UseFloatingContextWithSchema = describe(useFloatingContext);\nUseFloatingContextWithSchema.propTypes =\n DSFloatingContextPropTypes as unknown as ValidationMap<DSHookFloatingContextT.Props>;\n\nexport { useFloatingContext, UseFloatingContextWithSchema };\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable max-statements */\nimport React, { useMemo } from 'react';\nimport {\n useMemoMergePropsWithDefault,\n useValidateTypescriptPropTypes,\n describe,\n type ValidationMap,\n} from '@elliemae/ds-props-helpers';\nimport { useHeadlessTooltip } from '@elliemae/ds-hooks-headless-tooltip';\nimport type { DSHookFloatingContextT } from './react-desc-prop-types.js';\nimport { defaultProps, DSFloatingContextPropTypes } from './react-desc-prop-types.js';\nimport { useComputedPositionStyles } from './useComputedPositionStyles.js';\nimport { usePositionObserver } from './utils/positionObserver.js';\nimport { useLatestRef } from './hooks/useLatestRef.js';\nimport { useResolvedReference } from './hooks/useResolvedReference.js';\nimport { useFloatingClickOutside } from './hooks/useFloatingClickOutside.js';\nimport { useFloatingEscape } from './hooks/useFloatingEscape.js';\nimport { useFloatingResizeObserver } from './hooks/useFloatingResizeObserver.js';\n\nconst useFloatingContext = (props: DSHookFloatingContextT.Props = {}) => {\n // Extract unstable callback refs BEFORE useMemoMergePropsWithDefault.\n // useMemoMergePropsWithDefault compares with fast-deep-equal which compares functions by\n // reference; an inline callback from the consumer would invalidate propsWithDefault on every\n // render, which in turn would invalidate the destructured `customOffset` array and re-trigger\n // the position-computation useLayoutEffect on every render \u2192 infinite loop.\n const { onClickOutside, onEscape, onOpen, onClose, externalReferenceElement, ...stableProps } = props;\n\n const propsWithDefault = useMemoMergePropsWithDefault<DSHookFloatingContextT.InternalProps>(\n stableProps,\n defaultProps,\n );\n useValidateTypescriptPropTypes(propsWithDefault, DSFloatingContextPropTypes, 'FloatingContext');\n\n const {\n withoutPortal,\n withoutAnimation,\n portalDOMContainer,\n animationDuration,\n placement,\n customOffset,\n placementOrderPreference,\n externallyControlledIsOpen,\n closeOnEscape,\n returnFocusToReference,\n } = propsWithDefault;\n\n // Latest-refs for consumer callbacks (see useLatestRef docs).\n const onClickOutsideRef = useLatestRef(onClickOutside);\n const onEscapeRef = useLatestRef(onEscape);\n const onOpenRef = useLatestRef(onOpen);\n const onCloseRef = useLatestRef(onClose);\n\n // Open/close state \u2014 externally controlled when `externallyControlledIsOpen` is provided.\n const [internalIsOpen, setInternalIsOpen] = React.useState(false);\n const isOpen = useMemo(\n () => (externallyControlledIsOpen !== undefined ? externallyControlledIsOpen : internalIsOpen),\n [externallyControlledIsOpen, internalIsOpen],\n );\n\n const handleOpen = React.useCallback(() => {\n setInternalIsOpen(true);\n onOpenRef.current?.();\n }, [onOpenRef]);\n\n const handleClose = React.useCallback(() => {\n setInternalIsOpen(false);\n onCloseRef.current?.();\n }, [onCloseRef]);\n\n // Headless tooltip exposes show/hide helpers + manages its own internal state.\n const tooltipHelpers = useHeadlessTooltip({ onOpen: handleOpen, onClose: handleClose });\n const {\n setReferenceElement: setInternalReferenceElement,\n referenceElement: internalReferenceElement,\n hideTooltip,\n showTooltip,\n } = tooltipHelpers;\n\n // Reference element resolution: external takes precedence over internal.\n const { referenceElement, setReferenceElement } = useResolvedReference({\n externalReferenceElement,\n internalReferenceElement,\n setInternalReferenceElement,\n });\n\n // Anchor and floating refs\n const [floating, setFloating] = React.useState<HTMLElement | null>(null);\n\n // Compute position (skip when closed)\n const { arrowStyles, floatingStyles, hasComputedOnce, updateStyles, debouncedUpdateStyles, mutableUpdateStyles } =\n useComputedPositionStyles({\n reference: referenceElement,\n floating,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n preventComputing: !isOpen,\n debounceMs: 150,\n });\n\n const floatingResizeOberverConfig = useMemo(\n () => ({ enabled: isOpen, floating, onResize: () => mutableUpdateStyles.current() }),\n [isOpen, floating, mutableUpdateStyles],\n );\n\n // Re-compute position when the floating element's size changes (e.g. async content).\n useFloatingResizeObserver(floatingResizeOberverConfig);\n\n // Re-compute position when the reference element moves in the viewport.\n const onObservedMovement = React.useCallback(() => {\n if (!isOpen) return;\n debouncedUpdateStyles();\n }, [isOpen, debouncedUpdateStyles]);\n usePositionObserver(referenceElement, onObservedMovement);\n\n // Click-outside dismissal (opt-in via `onClickOutside`).\n useFloatingClickOutside({\n enabled: isOpen,\n floating,\n reference: referenceElement,\n callbackRef: onClickOutsideRef,\n });\n\n // Escape dismissal (opt-in via `closeOnEscape`).\n useFloatingEscape({\n enabled: isOpen && closeOnEscape,\n floating,\n reference: referenceElement,\n onEscapeRef,\n handleClose,\n returnFocusToReference,\n });\n\n // Expose visibility via opacity (NOT visibility:hidden \u2014 that blocks programmatic focus,\n // breaking autoFocus on first-open before the position-computation effect can run).\n // When `externalReferenceElement` is provided, computation runs on the first useLayoutEffect\n // (reference is known synchronously), so the popper becomes interactive in the same commit.\n const computedFloatingStyles = useMemo<React.CSSProperties>(() => {\n const isInteractive = isOpen && hasComputedOnce;\n return {\n ...floatingStyles,\n opacity: isInteractive ? 1 : 0,\n pointerEvents: isInteractive ? 'auto' : 'none',\n };\n }, [floatingStyles, hasComputedOnce, isOpen]);\n\n const refs = useMemo(\n () => ({\n setReference: setReferenceElement,\n setFloating,\n floating,\n reference: referenceElement,\n }),\n [setReferenceElement, floating, referenceElement],\n );\n\n const handlers = useMemo(\n () => ({\n onMouseEnter: tooltipHelpers.onMouseEnter,\n onMouseLeave: tooltipHelpers.onMouseLeave,\n onFocus: tooltipHelpers.onFocus,\n onBlur: tooltipHelpers.onBlur,\n }),\n [tooltipHelpers.onBlur, tooltipHelpers.onFocus, tooltipHelpers.onMouseEnter, tooltipHelpers.onMouseLeave],\n );\n\n return useMemo(\n () => ({\n refs,\n floatingStyles: computedFloatingStyles,\n handlers,\n isOpen,\n arrowStyles,\n hideTooltip,\n showTooltip,\n context: {\n withoutPortal,\n withoutAnimation,\n portalDOMContainer,\n animationDuration,\n },\n mutableUpdateStyles,\n forceUpdatePosition: updateStyles,\n }),\n [\n refs,\n computedFloatingStyles,\n handlers,\n isOpen,\n arrowStyles,\n hideTooltip,\n showTooltip,\n withoutPortal,\n withoutAnimation,\n portalDOMContainer,\n animationDuration,\n mutableUpdateStyles,\n updateStyles,\n ],\n );\n};\n\nuseFloatingContext.displayName = 'FloatingContext';\nconst UseFloatingContextWithSchema = describe(useFloatingContext);\nUseFloatingContextWithSchema.propTypes =\n DSFloatingContextPropTypes as unknown as ValidationMap<DSHookFloatingContextT.Props>;\n\nexport { useFloatingContext, UseFloatingContextWithSchema };\n"],
5
+ "mappings": "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",
6
6
  "names": ["React"]
7
7
  }
@@ -0,0 +1,30 @@
1
+ import * as React from "react";
2
+ import { useEffect } from "react";
3
+ const useFloatingClickOutside = ({
4
+ enabled,
5
+ floating,
6
+ reference,
7
+ callbackRef
8
+ }) => {
9
+ useEffect(() => {
10
+ if (!enabled || !floating || !reference) return void 0;
11
+ const listener = (event) => {
12
+ const cb = callbackRef.current;
13
+ if (!cb) return;
14
+ const target = event.target;
15
+ if (!target) return;
16
+ if (floating.contains(target) || reference.contains(target)) return;
17
+ cb(event);
18
+ };
19
+ document.addEventListener("mousedown", listener, true);
20
+ document.addEventListener("touchstart", listener, true);
21
+ return () => {
22
+ document.removeEventListener("mousedown", listener, true);
23
+ document.removeEventListener("touchstart", listener, true);
24
+ };
25
+ }, [enabled, floating, reference, callbackRef]);
26
+ };
27
+ export {
28
+ useFloatingClickOutside
29
+ };
30
+ //# sourceMappingURL=useFloatingClickOutside.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/hooks/useFloatingClickOutside.ts"],
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { useEffect, type MutableRefObject } from 'react';\n\ninterface UseFloatingClickOutsideParams {\n enabled: boolean;\n floating: HTMLElement | null;\n reference: Element | null;\n /**\n * Latest-ref of the click-outside callback. Passed as a ref (rather than a value)\n * so consumers can pass inline functions without re-attaching listeners on every render.\n * Use `useLatestRef(onClickOutside)` to construct it.\n */\n callbackRef: MutableRefObject<((event: MouseEvent | TouchEvent) => void) | undefined>;\n}\n\n/**\n * Attaches mousedown/touchstart listeners on the document that fire when the click target\n * is outside BOTH the floating element and the reference element.\n *\n * Only active when `enabled === true` and both elements exist.\n */\nexport const useFloatingClickOutside = ({\n enabled,\n floating,\n reference,\n callbackRef,\n}: UseFloatingClickOutsideParams) => {\n useEffect(() => {\n if (!enabled || !floating || !reference) return undefined;\n\n const listener = (event: MouseEvent | TouchEvent) => {\n const cb = callbackRef.current;\n if (!cb) return;\n const target = event.target as Node | null;\n if (!target) return;\n if (floating.contains(target) || reference.contains(target)) return;\n cb(event);\n };\n\n document.addEventListener('mousedown', listener, true);\n document.addEventListener('touchstart', listener, true);\n return () => {\n document.removeEventListener('mousedown', listener, true);\n document.removeEventListener('touchstart', listener, true);\n };\n }, [enabled, floating, reference, callbackRef]);\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,iBAAwC;AAoB1C,MAAM,0BAA0B,CAAC;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAAqC;AACnC,YAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,YAAY,CAAC,UAAW,QAAO;AAEhD,UAAM,WAAW,CAAC,UAAmC;AACnD,YAAM,KAAK,YAAY;AACvB,UAAI,CAAC,GAAI;AACT,YAAM,SAAS,MAAM;AACrB,UAAI,CAAC,OAAQ;AACb,UAAI,SAAS,SAAS,MAAM,KAAK,UAAU,SAAS,MAAM,EAAG;AAC7D,SAAG,KAAK;AAAA,IACV;AAEA,aAAS,iBAAiB,aAAa,UAAU,IAAI;AACrD,aAAS,iBAAiB,cAAc,UAAU,IAAI;AACtD,WAAO,MAAM;AACX,eAAS,oBAAoB,aAAa,UAAU,IAAI;AACxD,eAAS,oBAAoB,cAAc,UAAU,IAAI;AAAA,IAC3D;AAAA,EACF,GAAG,CAAC,SAAS,UAAU,WAAW,WAAW,CAAC;AAChD;",
6
+ "names": []
7
+ }
@@ -0,0 +1,44 @@
1
+ import * as React from "react";
2
+ import { useCallback, useEffect } from "react";
3
+ const useFloatingEscape = ({
4
+ enabled,
5
+ floating,
6
+ reference,
7
+ onEscapeRef,
8
+ handleClose,
9
+ returnFocusToReference
10
+ }) => {
11
+ const isFocusWithinFloatingScope = useCallback(
12
+ (active) => {
13
+ if (!active) return false;
14
+ if (floating && floating.contains(active)) return true;
15
+ if (!reference) return false;
16
+ return reference === active || reference.contains(active);
17
+ },
18
+ [floating, reference]
19
+ );
20
+ useEffect(() => {
21
+ if (!enabled) return void 0;
22
+ const listener = (event) => {
23
+ if (event.key !== "Escape") return;
24
+ if (!isFocusWithinFloatingScope(document.activeElement)) return;
25
+ const cb = onEscapeRef.current;
26
+ if (cb) {
27
+ cb(event);
28
+ } else {
29
+ handleClose();
30
+ }
31
+ if (returnFocusToReference && reference instanceof HTMLElement) {
32
+ reference.focus();
33
+ }
34
+ };
35
+ document.addEventListener("keydown", listener, true);
36
+ return () => {
37
+ document.removeEventListener("keydown", listener, true);
38
+ };
39
+ }, [enabled, handleClose, isFocusWithinFloatingScope, onEscapeRef, reference, returnFocusToReference]);
40
+ };
41
+ export {
42
+ useFloatingEscape
43
+ };
44
+ //# sourceMappingURL=useFloatingEscape.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/hooks/useFloatingEscape.ts"],
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { useCallback, useEffect, type MutableRefObject } from 'react';\n\ninterface UseFloatingEscapeParams {\n enabled: boolean;\n floating: HTMLElement | null;\n reference: Element | null;\n /** Latest-ref of the consumer's onEscape callback. Construct with `useLatestRef(onEscape)`. */\n onEscapeRef: MutableRefObject<((event: KeyboardEvent) => void) | undefined>;\n /** Fallback close handler invoked when no `onEscape` is provided. */\n handleClose: () => void;\n /** When true, calls `reference.focus()` after the escape close. */\n returnFocusToReference: boolean;\n}\n\n/**\n * Listens for `Escape` on the document and dismisses the floating element when focus is\n * within the floating element or on the reference element. Outside that scope the event is\n * ignored \u2014 we don't hijack Escape for unrelated UI on the page.\n */\nexport const useFloatingEscape = ({\n enabled,\n floating,\n reference,\n onEscapeRef,\n handleClose,\n returnFocusToReference,\n}: UseFloatingEscapeParams) => {\n const isFocusWithinFloatingScope = useCallback(\n (active: Element | null): boolean => {\n if (!active) return false;\n if (floating && floating.contains(active)) return true;\n if (!reference) return false;\n return reference === active || reference.contains(active);\n },\n [floating, reference],\n );\n\n useEffect(() => {\n if (!enabled) return undefined;\n\n const listener = (event: KeyboardEvent) => {\n if (event.key !== 'Escape') return;\n if (!isFocusWithinFloatingScope(document.activeElement)) return;\n\n const cb = onEscapeRef.current;\n if (cb) {\n cb(event);\n } else {\n handleClose();\n }\n if (returnFocusToReference && reference instanceof HTMLElement) {\n reference.focus();\n }\n };\n\n document.addEventListener('keydown', listener, true);\n return () => {\n document.removeEventListener('keydown', listener, true);\n };\n }, [enabled, handleClose, isFocusWithinFloatingScope, onEscapeRef, reference, returnFocusToReference]);\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,aAAa,iBAAwC;AAmBvD,MAAM,oBAAoB,CAAC;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,MAA+B;AAC7B,QAAM,6BAA6B;AAAA,IACjC,CAAC,WAAoC;AACnC,UAAI,CAAC,OAAQ,QAAO;AACpB,UAAI,YAAY,SAAS,SAAS,MAAM,EAAG,QAAO;AAClD,UAAI,CAAC,UAAW,QAAO;AACvB,aAAO,cAAc,UAAU,UAAU,SAAS,MAAM;AAAA,IAC1D;AAAA,IACA,CAAC,UAAU,SAAS;AAAA,EACtB;AAEA,YAAU,MAAM;AACd,QAAI,CAAC,QAAS,QAAO;AAErB,UAAM,WAAW,CAAC,UAAyB;AACzC,UAAI,MAAM,QAAQ,SAAU;AAC5B,UAAI,CAAC,2BAA2B,SAAS,aAAa,EAAG;AAEzD,YAAM,KAAK,YAAY;AACvB,UAAI,IAAI;AACN,WAAG,KAAK;AAAA,MACV,OAAO;AACL,oBAAY;AAAA,MACd;AACA,UAAI,0BAA0B,qBAAqB,aAAa;AAC9D,kBAAU,MAAM;AAAA,MAClB;AAAA,IACF;AAEA,aAAS,iBAAiB,WAAW,UAAU,IAAI;AACnD,WAAO,MAAM;AACX,eAAS,oBAAoB,WAAW,UAAU,IAAI;AAAA,IACxD;AAAA,EACF,GAAG,CAAC,SAAS,aAAa,4BAA4B,aAAa,WAAW,sBAAsB,CAAC;AACvG;",
6
+ "names": []
7
+ }
@@ -0,0 +1,16 @@
1
+ import * as React from "react";
2
+ import { useEffect } from "react";
3
+ const useFloatingResizeObserver = ({ enabled, floating, onResize }) => {
4
+ useEffect(() => {
5
+ if (!enabled || !floating) return void 0;
6
+ const observer = new ResizeObserver(() => {
7
+ onResize();
8
+ });
9
+ observer.observe(floating);
10
+ return () => observer.disconnect();
11
+ }, [enabled, floating, onResize]);
12
+ };
13
+ export {
14
+ useFloatingResizeObserver
15
+ };
16
+ //# sourceMappingURL=useFloatingResizeObserver.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/hooks/useFloatingResizeObserver.ts"],
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { useEffect } from 'react';\n\ninterface UseFloatingResizeObserverParams {\n enabled: boolean;\n floating: HTMLElement | null;\n onResize: () => void;\n}\n\n/**\n * Observes the floating element's size and calls `onResize` when its bounding box changes.\n * Used to re-run position computation when the floating content reflows (e.g. async-loaded data).\n */\nexport const useFloatingResizeObserver = ({ enabled, floating, onResize }: UseFloatingResizeObserverParams) => {\n useEffect(() => {\n if (!enabled || !floating) return undefined;\n\n const observer = new ResizeObserver(() => {\n onResize();\n });\n observer.observe(floating);\n\n return () => observer.disconnect();\n }, [enabled, floating, onResize]);\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,iBAAiB;AAYnB,MAAM,4BAA4B,CAAC,EAAE,SAAS,UAAU,SAAS,MAAuC;AAC7G,YAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,SAAU,QAAO;AAElC,UAAM,WAAW,IAAI,eAAe,MAAM;AACxC,eAAS;AAAA,IACX,CAAC;AACD,aAAS,QAAQ,QAAQ;AAEzB,WAAO,MAAM,SAAS,WAAW;AAAA,EACnC,GAAG,CAAC,SAAS,UAAU,QAAQ,CAAC;AAClC;",
6
+ "names": []
7
+ }
@@ -0,0 +1,11 @@
1
+ import * as React from "react";
2
+ import { useRef } from "react";
3
+ const useLatestRef = (value) => {
4
+ const ref = useRef(value);
5
+ ref.current = value;
6
+ return ref;
7
+ };
8
+ export {
9
+ useLatestRef
10
+ };
11
+ //# sourceMappingURL=useLatestRef.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/hooks/useLatestRef.ts"],
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { useRef } from 'react';\n\n/**\n * Latest-ref pattern: keeps the value in a ref that's updated on every render.\n * Use this when an effect/listener needs to read the latest value of a prop or callback\n * without including it in the effect's dependency array \u2014 avoids re-attaching listeners\n * (and infinite loops via deep-equal comparators) when consumers pass inline callbacks.\n */\nexport const useLatestRef = <T>(value: T) => {\n const ref = useRef(value);\n ref.current = value;\n return ref;\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,cAAc;AAQhB,MAAM,eAAe,CAAI,UAAa;AAC3C,QAAM,MAAM,OAAO,KAAK;AACxB,MAAI,UAAU;AACd,SAAO;AACT;",
6
+ "names": []
7
+ }
@@ -0,0 +1,22 @@
1
+ import * as React from "react";
2
+ import { useCallback } from "react";
3
+ const useResolvedReference = ({
4
+ externalReferenceElement,
5
+ internalReferenceElement,
6
+ setInternalReferenceElement
7
+ }) => {
8
+ const hasExternalReference = externalReferenceElement !== void 0;
9
+ const referenceElement = hasExternalReference ? externalReferenceElement : internalReferenceElement;
10
+ const setReferenceElement = useCallback(
11
+ (el) => {
12
+ if (hasExternalReference) return;
13
+ setInternalReferenceElement(el);
14
+ },
15
+ [hasExternalReference, setInternalReferenceElement]
16
+ );
17
+ return { referenceElement, setReferenceElement };
18
+ };
19
+ export {
20
+ useResolvedReference
21
+ };
22
+ //# sourceMappingURL=useResolvedReference.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../scripts/build/transpile/react-shim.js", "../../../src/hooks/useResolvedReference.ts"],
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { useCallback } from 'react';\n\ninterface UseResolvedReferenceParams {\n externalReferenceElement: Element | null | undefined;\n internalReferenceElement: Element | null;\n setInternalReferenceElement: (el: Element | null) => void;\n}\n\n/**\n * Resolves the active reference element for the floating context.\n *\n * - When `externalReferenceElement` is provided (anything other than `undefined`, including `null`),\n * it is used as the source of truth. `setReference` becomes a no-op so consumers that mistakenly\n * call `refs.setReference()` don't desynchronize the two sources.\n * - Otherwise the internally-managed state is used, populated by the consumer via `refs.setReference()`\n * (typically as a callback ref: `innerRef={refs.setReference}`).\n */\nexport const useResolvedReference = ({\n externalReferenceElement,\n internalReferenceElement,\n setInternalReferenceElement,\n}: UseResolvedReferenceParams) => {\n const hasExternalReference = externalReferenceElement !== undefined;\n const referenceElement = hasExternalReference ? externalReferenceElement : internalReferenceElement;\n\n const setReferenceElement = useCallback(\n (el: Element | null) => {\n if (hasExternalReference) return;\n setInternalReferenceElement(el);\n },\n [hasExternalReference, setInternalReferenceElement],\n );\n\n return { referenceElement, setReferenceElement };\n};\n"],
5
+ "mappings": "AAAA,YAAY,WAAW;ACAvB,SAAS,mBAAmB;AAiBrB,MAAM,uBAAuB,CAAC;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AACF,MAAkC;AAChC,QAAM,uBAAuB,6BAA6B;AAC1D,QAAM,mBAAmB,uBAAuB,2BAA2B;AAE3E,QAAM,sBAAsB;AAAA,IAC1B,CAAC,OAAuB;AACtB,UAAI,qBAAsB;AAC1B,kCAA4B,EAAE;AAAA,IAChC;AAAA,IACA,CAAC,sBAAsB,2BAA2B;AAAA,EACpD;AAEA,SAAO,EAAE,kBAAkB,oBAAoB;AACjD;",
6
+ "names": []
7
+ }
@@ -5,7 +5,9 @@ const defaultProps = {
5
5
  animationDuration: 300,
6
6
  withoutPortal: false,
7
7
  placement: "top",
8
- customOffset: [0, 12]
8
+ customOffset: [0, 12],
9
+ closeOnEscape: false,
10
+ returnFocusToReference: false
9
11
  };
10
12
  const DSFloatingContextPropTypes = {
11
13
  withoutPortal: PropTypes.bool.description("If true, the tooltip will not be rendered inside a portal.").defaultValue(false),
@@ -45,7 +47,16 @@ const DSFloatingContextPropTypes = {
45
47
  onClose: PropTypes.func.description("Callback when the tooltip is closed."),
46
48
  externallyControlledIsOpen: PropTypes.bool.description(
47
49
  "If true, the context open/close state will be controlled externally."
48
- )
50
+ ),
51
+ externalReferenceElement: PropTypes.instanceOf(Element).description(
52
+ "Pre-resolved reference element. When provided, used as the source of truth for positioning, avoiding the need for a follow-up refs.setReference() effect and the visibility:hidden race on open."
53
+ ),
54
+ onClickOutside: PropTypes.func.description(
55
+ "Called on mousedown/touchstart outside both the floating element and the reference element while open."
56
+ ),
57
+ closeOnEscape: PropTypes.bool.description("If true, listens for Escape on the floating element and calls onEscape (or onClose).").defaultValue(false),
58
+ onEscape: PropTypes.func.description("Called when Escape is pressed while focus is within the floating element."),
59
+ returnFocusToReference: PropTypes.bool.description("If true, returns focus to the reference element after Escape-close.").defaultValue(false)
49
60
  };
50
61
  export {
51
62
  DSFloatingContextPropTypes,