@elliemae/ds-floating-context 3.70.0-next.53 → 3.70.0-next.54

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 (51) hide show
  1. package/dist/cjs/DSFloatingContext.js +83 -33
  2. package/dist/cjs/DSFloatingContext.js.map +2 -2
  3. package/dist/cjs/hooks/useFloatingClickOutside.js +8 -8
  4. package/dist/cjs/hooks/useFloatingClickOutside.js.map +2 -2
  5. package/dist/cjs/hooks/useFloatingResizeObserver.js +8 -4
  6. package/dist/cjs/hooks/useFloatingResizeObserver.js.map +2 -2
  7. package/dist/cjs/hooks/useResolvedReference.js +2 -2
  8. package/dist/cjs/hooks/useResolvedReference.js.map +2 -2
  9. package/dist/cjs/hooks/{useFloatingEscape.js → useSyntheticEventFromControlledState.js} +19 -40
  10. package/dist/cjs/hooks/useSyntheticEventFromControlledState.js.map +7 -0
  11. package/dist/cjs/parts/FloatingWrapper/react-desc-prop-types.js +1 -1
  12. package/dist/cjs/parts/FloatingWrapper/react-desc-prop-types.js.map +2 -2
  13. package/dist/cjs/parts/PopoverArrow.js.map +2 -2
  14. package/dist/cjs/react-desc-prop-types.js +8 -4
  15. package/dist/cjs/react-desc-prop-types.js.map +2 -2
  16. package/dist/cjs/useComputedPositionStyles.js +24 -8
  17. package/dist/cjs/useComputedPositionStyles.js.map +2 -2
  18. package/dist/cjs/utils/computePosition.js +14 -7
  19. package/dist/cjs/utils/computePosition.js.map +2 -2
  20. package/dist/esm/DSFloatingContext.js +84 -34
  21. package/dist/esm/DSFloatingContext.js.map +2 -2
  22. package/dist/esm/hooks/useFloatingClickOutside.js +8 -8
  23. package/dist/esm/hooks/useFloatingClickOutside.js.map +2 -2
  24. package/dist/esm/hooks/useFloatingResizeObserver.js +8 -4
  25. package/dist/esm/hooks/useFloatingResizeObserver.js.map +2 -2
  26. package/dist/esm/hooks/useResolvedReference.js +2 -2
  27. package/dist/esm/hooks/useResolvedReference.js.map +2 -2
  28. package/dist/esm/hooks/useSyntheticEventFromControlledState.js +23 -0
  29. package/dist/esm/hooks/useSyntheticEventFromControlledState.js.map +7 -0
  30. package/dist/esm/parts/FloatingWrapper/react-desc-prop-types.js +1 -1
  31. package/dist/esm/parts/FloatingWrapper/react-desc-prop-types.js.map +2 -2
  32. package/dist/esm/parts/PopoverArrow.js.map +2 -2
  33. package/dist/esm/react-desc-prop-types.js +8 -4
  34. package/dist/esm/react-desc-prop-types.js.map +2 -2
  35. package/dist/esm/useComputedPositionStyles.js +24 -8
  36. package/dist/esm/useComputedPositionStyles.js.map +2 -2
  37. package/dist/esm/utils/computePosition.js +14 -7
  38. package/dist/esm/utils/computePosition.js.map +2 -2
  39. package/dist/types/DSFloatingContext.d.ts +46 -6
  40. package/dist/types/hooks/useFloatingClickOutside.d.ts +5 -5
  41. package/dist/types/hooks/useFloatingResizeObserver.d.ts +3 -3
  42. package/dist/types/hooks/useResolvedReference.d.ts +1 -1
  43. package/dist/types/hooks/useSyntheticEventFromControlledState.d.ts +32 -0
  44. package/dist/types/react-desc-prop-types.d.ts +39 -4
  45. package/dist/types/useComputedPositionStyles.d.ts +3 -3
  46. package/dist/types/utils/computePosition.d.ts +2 -2
  47. package/package.json +6 -6
  48. package/dist/cjs/hooks/useFloatingEscape.js.map +0 -7
  49. package/dist/esm/hooks/useFloatingEscape.js +0 -44
  50. package/dist/esm/hooks/useFloatingEscape.js.map +0 -7
  51. package/dist/types/hooks/useFloatingEscape.d.ts +0 -19
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/react-desc-prop-types.ts", "../../../../../scripts/build/transpile/react-shim.js"],
4
- "sourcesContent": ["/* eslint-disable @typescript-eslint/no-empty-interface */\nimport type { DSPropTypesSchema } from '@elliemae/ds-props-helpers';\nimport { PropTypes } from '@elliemae/ds-props-helpers';\nexport declare namespace DSHookFloatingContextT {\n export interface DefaultProps {\n withoutPortal: boolean;\n withoutAnimation: boolean;\n portalDOMContainer?: HTMLElement;\n animationDuration: number;\n placement: PopperPlacementsT;\n customOffset: [number, number];\n closeOnEscape: boolean;\n returnFocusToReference: boolean;\n }\n\n export interface OptionalProps {\n placementOrderPreference?: PopperPlacementsT[];\n onOpen?: () => void;\n onClose?: () => void;\n externallyControlledIsOpen?: boolean;\n /**\n * Pre-resolved reference element. When provided, the hook uses this as the\n * source of truth for positioning and ignores its internal reference state.\n * Eliminates the need for a follow-up `refs.setReference(...)` effect and\n * removes the visibility:hidden race that breaks programmatic focus on open.\n */\n externalReferenceElement?: Element | null;\n /**\n * Called when the user clicks/taps outside both the floating element and\n * the reference element while the floating is open.\n */\n onClickOutside?: (event: MouseEvent | TouchEvent) => void;\n /**\n * Called when Escape is pressed while focus is within the floating element.\n * Only fires when `closeOnEscape` is true.\n */\n onEscape?: (event: KeyboardEvent) => void;\n }\n export interface Props extends Partial<DefaultProps>, OptionalProps {}\n\n export interface InternalProps extends DefaultProps, OptionalProps {}\n\n export type PopperPlacementsT =\n | 'top-start'\n | 'top'\n | 'top-end'\n | 'right-start'\n | 'right'\n | 'right-end'\n | 'bottom-end'\n | 'bottom'\n | 'bottom-start'\n | 'left-end'\n | 'left'\n | 'left-start';\n}\n\nexport const defaultProps: DSHookFloatingContextT.DefaultProps = {\n withoutAnimation: false,\n animationDuration: 300,\n withoutPortal: false,\n placement: 'top',\n customOffset: [0, 12],\n closeOnEscape: false,\n returnFocusToReference: false,\n};\n\nexport const DSFloatingContextPropTypes: DSPropTypesSchema<DSHookFloatingContextT.Props> = {\n withoutPortal: PropTypes.bool\n .description('If true, the tooltip will not be rendered inside a portal.')\n .defaultValue(false),\n withoutAnimation: PropTypes.bool.description('If true, the tooltip will not have an animation.').defaultValue(false),\n portalDOMContainer: PropTypes.instanceOf(HTMLElement)\n .description('The DOM element where the tooltip will be rendered.')\n .defaultValue('the first \"main\" landmark available in the document or the body if no \"main\" is found'),\n animationDuration: PropTypes.number.description('The duration of the animation in milliseconds.').defaultValue(300),\n placement: PropTypes.oneOf([\n 'top-start',\n 'top',\n 'top-end',\n 'right-start',\n 'right',\n 'right-end',\n 'bottom-end',\n 'bottom',\n 'bottom-start',\n 'left-end',\n 'left',\n 'left-start',\n ])\n .description('The placement of the tooltip.')\n .defaultValue('top'),\n customOffset: PropTypes.arrayOf(PropTypes.number)\n .description('The custom offset of the tooltip.')\n .defaultValue([12, 12]),\n placementOrderPreference: PropTypes.oneOfType([\n PropTypes.tuple([PropTypes.oneOf(['top-start'])]),\n PropTypes.tuple([PropTypes.oneOf(['top'])]),\n PropTypes.tuple([PropTypes.oneOf(['top-end'])]),\n PropTypes.tuple([PropTypes.oneOf(['right-start'])]),\n PropTypes.tuple([PropTypes.oneOf(['right'])]),\n PropTypes.tuple([PropTypes.oneOf(['right-end'])]),\n PropTypes.tuple([PropTypes.oneOf(['bottom-end'])]),\n PropTypes.tuple([PropTypes.oneOf(['bottom'])]),\n PropTypes.tuple([PropTypes.oneOf(['bottom-start'])]),\n PropTypes.tuple([PropTypes.oneOf(['left-end'])]),\n PropTypes.tuple([PropTypes.oneOf(['left'])]),\n PropTypes.tuple([PropTypes.oneOf(['left-start'])]),\n ]).description('The order of the placement preference.'),\n onOpen: PropTypes.func.description('Callback when the tooltip is opened.'),\n onClose: PropTypes.func.description('Callback when the tooltip is closed.'),\n externallyControlledIsOpen: PropTypes.bool.description(\n 'If true, the context open/close state will be controlled externally.',\n ),\n externalReferenceElement: PropTypes.instanceOf(Element).description(\n 'Pre-resolved reference element. When provided, used as the source of truth for positioning, ' +\n 'avoiding the need for a follow-up refs.setReference() effect and the visibility:hidden race on open.',\n ),\n onClickOutside: PropTypes.func.description(\n 'Called on mousedown/touchstart outside both the floating element and the reference element while open.',\n ),\n closeOnEscape: PropTypes.bool\n .description('If true, listens for Escape on the floating element and calls onEscape (or onClose).')\n .defaultValue(false),\n onEscape: PropTypes.func.description('Called when Escape is pressed while focus is within the floating element.'),\n returnFocusToReference: PropTypes.bool\n .description('If true, returns focus to the reference element after Escape-close.')\n .defaultValue(false),\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADEvB,8BAA0B;AAuDnB,MAAM,eAAoD;AAAA,EAC/D,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,eAAe;AAAA,EACf,WAAW;AAAA,EACX,cAAc,CAAC,GAAG,EAAE;AAAA,EACpB,eAAe;AAAA,EACf,wBAAwB;AAC1B;AAEO,MAAM,6BAA8E;AAAA,EACzF,eAAe,kCAAU,KACtB,YAAY,4DAA4D,EACxE,aAAa,KAAK;AAAA,EACrB,kBAAkB,kCAAU,KAAK,YAAY,kDAAkD,EAAE,aAAa,KAAK;AAAA,EACnH,oBAAoB,kCAAU,WAAW,WAAW,EACjD,YAAY,qDAAqD,EACjE,aAAa,uFAAuF;AAAA,EACvG,mBAAmB,kCAAU,OAAO,YAAY,gDAAgD,EAAE,aAAa,GAAG;AAAA,EAClH,WAAW,kCAAU,MAAM;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC,EACE,YAAY,+BAA+B,EAC3C,aAAa,KAAK;AAAA,EACrB,cAAc,kCAAU,QAAQ,kCAAU,MAAM,EAC7C,YAAY,mCAAmC,EAC/C,aAAa,CAAC,IAAI,EAAE,CAAC;AAAA,EACxB,0BAA0B,kCAAU,UAAU;AAAA,IAC5C,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;AAAA,IAChD,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAAA,IAC1C,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;AAAA,IAC9C,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;AAAA,IAClD,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;AAAA,IAC5C,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;AAAA,IAChD,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;AAAA,IACjD,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAAA,IAC7C,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;AAAA,IACnD,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;AAAA,IAC/C,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;AAAA,IAC3C,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;AAAA,EACnD,CAAC,EAAE,YAAY,wCAAwC;AAAA,EACvD,QAAQ,kCAAU,KAAK,YAAY,sCAAsC;AAAA,EACzE,SAAS,kCAAU,KAAK,YAAY,sCAAsC;AAAA,EAC1E,4BAA4B,kCAAU,KAAK;AAAA,IACzC;AAAA,EACF;AAAA,EACA,0BAA0B,kCAAU,WAAW,OAAO,EAAE;AAAA,IACtD;AAAA,EAEF;AAAA,EACA,gBAAgB,kCAAU,KAAK;AAAA,IAC7B;AAAA,EACF;AAAA,EACA,eAAe,kCAAU,KACtB,YAAY,sFAAsF,EAClG,aAAa,KAAK;AAAA,EACrB,UAAU,kCAAU,KAAK,YAAY,2EAA2E;AAAA,EAChH,wBAAwB,kCAAU,KAC/B,YAAY,qEAAqE,EACjF,aAAa,KAAK;AACvB;",
4
+ "sourcesContent": ["/* eslint-disable @typescript-eslint/no-empty-interface */\nimport type React from 'react';\nimport type { DSPropTypesSchema } from '@elliemae/ds-props-helpers';\nimport { PropTypes } from '@elliemae/ds-props-helpers';\nexport declare namespace DSHookFloatingContextT {\n export interface DefaultProps {\n withoutPortal: boolean;\n withoutAnimation: boolean;\n portalDOMContainer?: HTMLElement;\n animationDuration: number;\n placement: PopperPlacementsT;\n customOffset: [number, number];\n closeOnEscape: boolean;\n returnFocusToReference: boolean;\n }\n\n /**\n * Describes why onOpen/onClose fired.\n * - `interaction`: driven by this hook's own interaction handling (focus/hover/escape, via\n * ds-hooks-headless-tooltip). Fires synchronously in the event catch phase and carries the\n * originating DOM event when there is one (absent for imperative show/hide). Fires for\n * controlled contexts too \u2014 it is the pre-flip, event-carrying signal a controlled consumer can\n * choose to act on.\n * - `controlled-flip`: driven by the consumer flipping `externallyControlledIsOpen`. Fires from a\n * post-commit effect \u2014 the change originates outside this hook, so there is no event to carry.\n *\n * A controlled consumer may see BOTH for one logical transition (the pre-flip interaction, then\n * the post-flip controlled-flip when they echo it into the prop); discriminate on `reason` (and\n * `isControlled`) to pick which one your logic cares about. That choice is the consumer's \u2014 this\n * hook does not decide it for them.\n */\n export type OpenChange =\n | {\n reason: 'interaction';\n event?: React.FocusEvent | React.MouseEvent | React.KeyboardEvent | KeyboardEvent;\n isControlled: boolean;\n }\n | {\n reason: 'controlled-flip';\n isControlled: true;\n };\n\n export interface OptionalProps {\n placementOrderPreference?: PopperPlacementsT[];\n /**\n * Called on every open transition \u2014 both interaction-driven (pre-flip, with `event`) and\n * controlled-flip-driven (post-flip). Discriminate on the `OpenChange` argument's `reason`.\n */\n onOpen?: (info: OpenChange) => void;\n /**\n * Called on every close transition \u2014 both interaction-driven and controlled-flip-driven.\n * Discriminate on `reason`. Note a scoped Escape close routes to `onEscape` instead of `onClose`\n * (see `onEscape`).\n */\n onClose?: (info: OpenChange) => void;\n externallyControlledIsOpen?: boolean;\n /**\n * Pre-resolved reference element. When provided, the hook uses this as the\n * source of truth for positioning and ignores its internal reference state.\n * Eliminates the need for a follow-up `refs.setReference(...)` effect and\n * removes the visibility:hidden race that breaks programmatic focus on open.\n */\n externalReferenceElement?: Element | null;\n /**\n * Called when the user clicks/taps outside both the floating element and\n * the reference element while the floating is open.\n */\n onClickOutside?: (event: MouseEvent | TouchEvent) => void;\n /**\n * Called when Escape is pressed while the floating element is open and focus is within the\n * floating element or the reference element. Only fires when `closeOnEscape` is true.\n * Receives the native KeyboardEvent \u2014 this is delivered via a document-level listener, not a\n * JSX handler, so there is no React SyntheticEvent to hand back.\n */\n onEscape?: (event: KeyboardEvent) => void;\n }\n export interface Props extends Partial<DefaultProps>, OptionalProps {}\n\n export interface InternalProps extends DefaultProps, OptionalProps {}\n\n export type PopperPlacementsT =\n | 'top-start'\n | 'top'\n | 'top-end'\n | 'right-start'\n | 'right'\n | 'right-end'\n | 'bottom-end'\n | 'bottom'\n | 'bottom-start'\n | 'left-end'\n | 'left'\n | 'left-start';\n}\n\nexport const defaultProps: DSHookFloatingContextT.DefaultProps = {\n withoutAnimation: false,\n animationDuration: 300,\n withoutPortal: false,\n placement: 'top',\n customOffset: [0, 12],\n closeOnEscape: false,\n returnFocusToReference: false,\n};\n\nexport const DSFloatingContextPropTypes: DSPropTypesSchema<DSHookFloatingContextT.Props> = {\n withoutPortal: PropTypes.bool\n .description('If true, the tooltip will not be rendered inside a portal.')\n .defaultValue(false),\n withoutAnimation: PropTypes.bool.description('If true, the tooltip will not have an animation.').defaultValue(false),\n portalDOMContainer: PropTypes.object\n .description('The DOM element where the tooltip will be rendered.')\n .defaultValue('the first \"main\" landmark available in the document or the body if no \"main\" is found'),\n animationDuration: PropTypes.number.description('The duration of the animation in milliseconds.').defaultValue(300),\n placement: PropTypes.oneOf([\n 'top-start',\n 'top',\n 'top-end',\n 'right-start',\n 'right',\n 'right-end',\n 'bottom-end',\n 'bottom',\n 'bottom-start',\n 'left-end',\n 'left',\n 'left-start',\n ])\n .description('The placement of the tooltip.')\n .defaultValue('top'),\n customOffset: PropTypes.arrayOf(PropTypes.number)\n .description('The custom offset of the tooltip.')\n .defaultValue([12, 12]),\n placementOrderPreference: PropTypes.oneOfType([\n PropTypes.tuple([PropTypes.oneOf(['top-start'])]),\n PropTypes.tuple([PropTypes.oneOf(['top'])]),\n PropTypes.tuple([PropTypes.oneOf(['top-end'])]),\n PropTypes.tuple([PropTypes.oneOf(['right-start'])]),\n PropTypes.tuple([PropTypes.oneOf(['right'])]),\n PropTypes.tuple([PropTypes.oneOf(['right-end'])]),\n PropTypes.tuple([PropTypes.oneOf(['bottom-end'])]),\n PropTypes.tuple([PropTypes.oneOf(['bottom'])]),\n PropTypes.tuple([PropTypes.oneOf(['bottom-start'])]),\n PropTypes.tuple([PropTypes.oneOf(['left-end'])]),\n PropTypes.tuple([PropTypes.oneOf(['left'])]),\n PropTypes.tuple([PropTypes.oneOf(['left-start'])]),\n ]).description('The order of the placement preference.'),\n onOpen: PropTypes.func.description(\n 'Called on every open transition (interaction or controlled-flip); receives arguments with a `reason`.',\n ),\n onClose: PropTypes.func.description(\n 'Called on every close transition (interaction or controlled-flip); receives arguments with a `reason`.',\n ),\n externallyControlledIsOpen: PropTypes.bool.description(\n 'If true, the context open/close state will be controlled externally.',\n ),\n externalReferenceElement: PropTypes.object.description(\n 'Pre-resolved reference element. When provided, used as the source of truth for positioning, ' +\n 'avoiding the need for a follow-up refs.setReference() effect and the visibility:hidden race on open.',\n ),\n onClickOutside: PropTypes.func.description(\n 'Called on mousedown/touchstart outside both the floating element and the reference element while open.',\n ),\n closeOnEscape: PropTypes.bool\n .description('If true, listens for Escape on the floating element and calls onEscape (or onClose).')\n .defaultValue(false),\n onEscape: PropTypes.func.description('Called when Escape is pressed while focus is within the floating element.'),\n returnFocusToReference: PropTypes.bool\n .description('If true, returns focus to the reference element after Escape-close.')\n .defaultValue(false),\n};\n", "import * as React from 'react';\nexport { React };\n"],
5
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;ACAA,YAAuB;ADGvB,8BAA0B;AA4FnB,MAAM,eAAoD;AAAA,EAC/D,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,eAAe;AAAA,EACf,WAAW;AAAA,EACX,cAAc,CAAC,GAAG,EAAE;AAAA,EACpB,eAAe;AAAA,EACf,wBAAwB;AAC1B;AAEO,MAAM,6BAA8E;AAAA,EACzF,eAAe,kCAAU,KACtB,YAAY,4DAA4D,EACxE,aAAa,KAAK;AAAA,EACrB,kBAAkB,kCAAU,KAAK,YAAY,kDAAkD,EAAE,aAAa,KAAK;AAAA,EACnH,oBAAoB,kCAAU,OAC3B,YAAY,qDAAqD,EACjE,aAAa,uFAAuF;AAAA,EACvG,mBAAmB,kCAAU,OAAO,YAAY,gDAAgD,EAAE,aAAa,GAAG;AAAA,EAClH,WAAW,kCAAU,MAAM;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC,EACE,YAAY,+BAA+B,EAC3C,aAAa,KAAK;AAAA,EACrB,cAAc,kCAAU,QAAQ,kCAAU,MAAM,EAC7C,YAAY,mCAAmC,EAC/C,aAAa,CAAC,IAAI,EAAE,CAAC;AAAA,EACxB,0BAA0B,kCAAU,UAAU;AAAA,IAC5C,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;AAAA,IAChD,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAAA,IAC1C,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;AAAA,IAC9C,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC;AAAA,IAClD,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;AAAA,IAC5C,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC;AAAA,IAChD,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;AAAA,IACjD,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAAA,IAC7C,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;AAAA,IACnD,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;AAAA,IAC/C,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;AAAA,IAC3C,kCAAU,MAAM,CAAC,kCAAU,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;AAAA,EACnD,CAAC,EAAE,YAAY,wCAAwC;AAAA,EACvD,QAAQ,kCAAU,KAAK;AAAA,IACrB;AAAA,EACF;AAAA,EACA,SAAS,kCAAU,KAAK;AAAA,IACtB;AAAA,EACF;AAAA,EACA,4BAA4B,kCAAU,KAAK;AAAA,IACzC;AAAA,EACF;AAAA,EACA,0BAA0B,kCAAU,OAAO;AAAA,IACzC;AAAA,EAEF;AAAA,EACA,gBAAgB,kCAAU,KAAK;AAAA,IAC7B;AAAA,EACF;AAAA,EACA,eAAe,kCAAU,KACtB,YAAY,sFAAsF,EAClG,aAAa,KAAK;AAAA,EACrB,UAAU,kCAAU,KAAK,YAAY,2EAA2E;AAAA,EAChH,wBAAwB,kCAAU,KAC/B,YAAY,qEAAqE,EACjF,aAAa,KAAK;AACvB;",
6
6
  "names": []
7
7
  }
@@ -37,13 +37,13 @@ var import_lodash_es = require("lodash-es");
37
37
  var import_computePosition = require("./utils/computePosition.js");
38
38
  const useComputedPositionStyles = (config) => {
39
39
  const {
40
- reference,
41
- floating,
40
+ triggerElementReference,
41
+ floatingWrapperNode,
42
42
  placement,
43
43
  placementOrderPreference,
44
44
  customOffset,
45
45
  withoutPortal,
46
- preventComputing = false,
46
+ isClose = false,
47
47
  debounceMs = 150
48
48
  } = config;
49
49
  const [arrowStyles, setArrowStyles] = (0, import_react.useState)({ style: { left: 0 }, placement: "top" });
@@ -55,12 +55,12 @@ const useComputedPositionStyles = (config) => {
55
55
  willChange: "transform"
56
56
  });
57
57
  const [hasComputedOnce, setHasComputedOnce] = (0, import_react.useState)(false);
58
- const canCompute = reference !== null && floating !== null && !preventComputing;
58
+ const canCompute = triggerElementReference !== null && floatingWrapperNode !== null && !isClose;
59
59
  const updateStyles = (0, import_react.useCallback)(() => {
60
60
  if (!canCompute) return;
61
61
  const { coordsStyle, finalPlacement, coordsArrow } = (0, import_computePosition.computePosition)({
62
- reference,
63
- floating,
62
+ triggerElementReference,
63
+ floatingWrapperNode,
64
64
  placement,
65
65
  placementOrderPreference,
66
66
  customOffset,
@@ -73,7 +73,15 @@ const useComputedPositionStyles = (config) => {
73
73
  });
74
74
  setArrowStyles({ style: coordsArrow, placement: finalPlacement });
75
75
  setHasComputedOnce(true);
76
- }, [canCompute, reference, floating, placement, placementOrderPreference, customOffset, withoutPortal]);
76
+ }, [
77
+ canCompute,
78
+ triggerElementReference,
79
+ floatingWrapperNode,
80
+ placement,
81
+ placementOrderPreference,
82
+ customOffset,
83
+ withoutPortal
84
+ ]);
77
85
  const mutableUpdateStyles = (0, import_react.useRef)(updateStyles);
78
86
  mutableUpdateStyles.current = updateStyles;
79
87
  const debouncedUpdateStyles = (0, import_react.useMemo)(() => {
@@ -94,7 +102,15 @@ const useComputedPositionStyles = (config) => {
94
102
  if (canCompute) {
95
103
  mutableUpdateStyles.current();
96
104
  }
97
- }, [canCompute, reference, floating, placement, placementOrderPreference, customOffset, withoutPortal]);
105
+ }, [
106
+ canCompute,
107
+ triggerElementReference,
108
+ floatingWrapperNode,
109
+ placement,
110
+ placementOrderPreference,
111
+ customOffset,
112
+ withoutPortal
113
+ ]);
98
114
  const forceUpdatePosition = (0, import_react.useCallback)(() => {
99
115
  mutableUpdateStyles.current();
100
116
  }, []);
@@ -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 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;",
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 isClose?: boolean;\n triggerElementReference: Element | null;\n floatingWrapperNode: 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 triggerElementReference,\n floatingWrapperNode,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n isClose = 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 = triggerElementReference !== null && floatingWrapperNode !== null && !isClose;\n\n const updateStyles = useCallback(() => {\n if (!canCompute) return;\n\n const { coordsStyle, finalPlacement, coordsArrow } = computePosition({\n triggerElementReference,\n floatingWrapperNode,\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 }, [\n canCompute,\n triggerElementReference,\n floatingWrapperNode,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n ]);\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 }, [\n canCompute,\n triggerElementReference,\n floatingWrapperNode,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n ]);\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,UAAU;AAAA,IACV,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,4BAA4B,QAAQ,wBAAwB,QAAQ,CAAC;AAExF,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;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAGD,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;AAAA,IACD;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,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
  }
@@ -37,15 +37,22 @@ var import_getArrowOffset = require("./getArrowOffset.js");
37
37
  var import_detectOverflow = require("./detectOverflow.js");
38
38
  var import_floatingPositioning = require("./floatingPositioning.js");
39
39
  const computePosition = (props) => {
40
- const { reference, floating, placement, placementOrderPreference, customOffset, withoutPortal } = props;
41
- const parentOffsets = withoutPortal ? (0, import_floatingPositioning.adjustForFixedParent)(reference) : { top: 0, left: 0 };
42
- const referenceRect = reference.getBoundingClientRect();
43
- const floatingRect = floating.getBoundingClientRect();
40
+ const {
41
+ triggerElementReference,
42
+ floatingWrapperNode,
43
+ placement,
44
+ placementOrderPreference,
45
+ customOffset,
46
+ withoutPortal
47
+ } = props;
48
+ const parentOffsets = withoutPortal ? (0, import_floatingPositioning.adjustForFixedParent)(triggerElementReference) : { top: 0, left: 0 };
49
+ const referenceRect = triggerElementReference.getBoundingClientRect();
50
+ const floatingRect = floatingWrapperNode.getBoundingClientRect();
44
51
  const fallbackPlacements = placementOrderPreference || (0, import_getExpandedFallbackPlacements.getExpandedFallbackPlacements)(placement);
45
52
  const placements = (0, import_floatingPositioning.expandWithVariations)(
46
53
  [placement].concat(fallbackPlacements)
47
54
  );
48
- const clippingParent = withoutPortal ? (0, import_floatingPositioning.getClippingParent)(reference) : null;
55
+ const clippingParent = withoutPortal ? (0, import_floatingPositioning.getClippingParent)(triggerElementReference) : null;
49
56
  const clippingRect = clippingParent ? clippingParent.getBoundingClientRect() : (0, import_floatingPositioning.getViewportRect)();
50
57
  let bestPlacement = placement;
51
58
  let bestOverflows = null;
@@ -74,7 +81,7 @@ const computePosition = (props) => {
74
81
  x += window.scrollX;
75
82
  y += window.scrollY;
76
83
  } else {
77
- const op = (0, import_floatingPositioning.getOffsetParentData)(floating);
84
+ const op = (0, import_floatingPositioning.getOffsetParentData)(floatingWrapperNode);
78
85
  x = x + clippingRect.left - op.left + op.scrollLeft;
79
86
  y = y + clippingRect.top - op.top + op.scrollTop;
80
87
  }
@@ -87,7 +94,7 @@ const computePosition = (props) => {
87
94
  y,
88
95
  withoutPortal,
89
96
  parentOffsets,
90
- floatingEl: floating,
97
+ floatingEl: floatingWrapperNode,
91
98
  arrowPadding: 12
92
99
  });
93
100
  return {
@@ -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 // 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;",
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 triggerElementReference: Element;\n floatingWrapperNode: 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 {\n triggerElementReference,\n floatingWrapperNode,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n } = 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(triggerElementReference) : { top: 0, left: 0 };\n\n const referenceRect = triggerElementReference.getBoundingClientRect();\n const floatingRect = floatingWrapperNode.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(triggerElementReference) : 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(floatingWrapperNode);\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: floatingWrapperNode,\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;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAGJ,QAAM,gBAAgB,oBAAgB,iDAAqB,uBAAuB,IAAI,EAAE,KAAK,GAAG,MAAM,EAAE;AAExG,QAAM,gBAAgB,wBAAwB,sBAAsB;AACpE,QAAM,eAAe,oBAAoB,sBAAsB;AAE/D,QAAM,qBAAqB,gCAA4B,oEAA8B,SAAS;AAE9F,QAAM,iBAAa;AAAA,IACjB,CAAC,SAAS,EAAE,OAAO,kBAAgE;AAAA,EACrF;AAKA,QAAM,iBAAiB,oBAAgB,8CAAkB,uBAAuB,IAAI;AACpF,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,mBAAmB;AAClD,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 } from "react";
2
+ import React2, { useCallback, useMemo } from "react";
3
3
  import {
4
4
  useMemoMergePropsWithDefault,
5
5
  useValidateTypescriptPropTypes,
@@ -12,8 +12,9 @@ import { usePositionObserver } from "./utils/positionObserver.js";
12
12
  import { useLatestRef } from "./hooks/useLatestRef.js";
13
13
  import { useResolvedReference } from "./hooks/useResolvedReference.js";
14
14
  import { useFloatingClickOutside } from "./hooks/useFloatingClickOutside.js";
15
- import { useFloatingEscape } from "./hooks/useFloatingEscape.js";
16
15
  import { useFloatingResizeObserver } from "./hooks/useFloatingResizeObserver.js";
16
+ import { useSyntheticEventFromControlledState } from "./hooks/useSyntheticEventFromControlledState.js";
17
+ const isKeyboardEvent = (event) => !!event?.key;
17
18
  const useFloatingContext = (props = {}) => {
18
19
  const { onClickOutside, onEscape, onOpen, onClose, externalReferenceElement, ...stableProps } = props;
19
20
  const propsWithDefault = useMemoMergePropsWithDefault(
@@ -33,6 +34,9 @@ const useFloatingContext = (props = {}) => {
33
34
  closeOnEscape,
34
35
  returnFocusToReference
35
36
  } = propsWithDefault;
37
+ const isControlled = externallyControlledIsOpen !== void 0;
38
+ const [floatingWrapperNode, setFloatingWrapperNode] = React2.useState(null);
39
+ const triggerElementReferenceRef = React2.useRef(null);
36
40
  const onClickOutsideRef = useLatestRef(onClickOutside);
37
41
  const onEscapeRef = useLatestRef(onEscape);
38
42
  const onOpenRef = useLatestRef(onOpen);
@@ -42,60 +46,106 @@ const useFloatingContext = (props = {}) => {
42
46
  () => externallyControlledIsOpen !== void 0 ? externallyControlledIsOpen : internalIsOpen,
43
47
  [externallyControlledIsOpen, internalIsOpen]
44
48
  );
45
- const handleOpen = React2.useCallback(() => {
46
- setInternalIsOpen(true);
47
- onOpenRef.current?.();
48
- }, [onOpenRef]);
49
- const handleClose = React2.useCallback(() => {
50
- setInternalIsOpen(false);
51
- onCloseRef.current?.();
52
- }, [onCloseRef]);
53
- const tooltipHelpers = useHeadlessTooltip({ onOpen: handleOpen, onClose: handleClose });
49
+ const isControlledRef = useLatestRef(isControlled);
50
+ const closeOnEscapeRef = useLatestRef(closeOnEscape);
51
+ const returnFocusToReferenceRef = useLatestRef(returnFocusToReference);
52
+ const floatingWrapperNodeRef = useLatestRef(floatingWrapperNode);
53
+ const isOpenRef = useLatestRef(isOpen);
54
+ const handleOpen = React2.useCallback(
55
+ (event) => {
56
+ setInternalIsOpen(true);
57
+ onOpenRef.current?.({ reason: "interaction", event, isControlled: isControlledRef.current });
58
+ },
59
+ [onOpenRef, isControlledRef]
60
+ );
61
+ const isFocusWithinFloatingScope = useCallback(() => {
62
+ const active = document.activeElement;
63
+ if (!active) return false;
64
+ if (floatingWrapperNodeRef.current?.contains(active)) return true;
65
+ const reference = triggerElementReferenceRef.current;
66
+ return reference === active || !!reference?.contains(active);
67
+ }, [floatingWrapperNodeRef]);
68
+ const handleClose = React2.useCallback(
69
+ (event) => {
70
+ const wasOpen = isOpenRef.current;
71
+ setInternalIsOpen(false);
72
+ const isScopedEscapeClose = wasOpen && closeOnEscapeRef.current && isKeyboardEvent(event) && event.key === "Escape" && isFocusWithinFloatingScope();
73
+ if (isScopedEscapeClose) {
74
+ onEscapeRef.current?.(event);
75
+ if (returnFocusToReferenceRef.current && triggerElementReferenceRef.current instanceof HTMLElement) {
76
+ triggerElementReferenceRef.current.focus();
77
+ }
78
+ } else {
79
+ onCloseRef.current?.({ reason: "interaction", event, isControlled: isControlledRef.current });
80
+ }
81
+ },
82
+ [
83
+ isOpenRef,
84
+ closeOnEscapeRef,
85
+ isFocusWithinFloatingScope,
86
+ returnFocusToReferenceRef,
87
+ onCloseRef,
88
+ onEscapeRef,
89
+ isControlledRef
90
+ ]
91
+ );
92
+ const shouldFocusReferenceOnEscape = useCallback(
93
+ () => closeOnEscapeRef.current && returnFocusToReferenceRef.current && isFocusWithinFloatingScope(),
94
+ [closeOnEscapeRef, returnFocusToReferenceRef, isFocusWithinFloatingScope]
95
+ );
96
+ const tooltipHelpersConfig = useMemo(
97
+ () => ({ onOpen: handleOpen, onClose: handleClose, focusReferenceOnEscape: shouldFocusReferenceOnEscape }),
98
+ [handleOpen, handleClose, shouldFocusReferenceOnEscape]
99
+ );
100
+ const tooltipHelpers = useHeadlessTooltip(tooltipHelpersConfig);
54
101
  const {
55
102
  setReferenceElement: setInternalReferenceElement,
56
103
  referenceElement: internalReferenceElement,
57
104
  hideTooltip,
58
105
  showTooltip
59
106
  } = tooltipHelpers;
60
- const { referenceElement, setReferenceElement } = useResolvedReference({
107
+ const { triggerElementReference, setReferenceElement } = useResolvedReference({
61
108
  externalReferenceElement,
62
109
  internalReferenceElement,
63
110
  setInternalReferenceElement
64
111
  });
65
- const [floating, setFloating] = React2.useState(null);
112
+ triggerElementReferenceRef.current = triggerElementReference;
66
113
  const { arrowStyles, floatingStyles, hasComputedOnce, updateStyles, debouncedUpdateStyles, mutableUpdateStyles } = useComputedPositionStyles({
67
- reference: referenceElement,
68
- floating,
114
+ triggerElementReference,
115
+ floatingWrapperNode,
69
116
  placement,
70
117
  placementOrderPreference,
71
118
  customOffset,
72
119
  withoutPortal,
73
- preventComputing: !isOpen,
120
+ isClose: !isOpen,
74
121
  debounceMs: 150
75
122
  });
76
123
  const floatingResizeOberverConfig = useMemo(
77
- () => ({ enabled: isOpen, floating, onResize: () => mutableUpdateStyles.current() }),
78
- [isOpen, floating, mutableUpdateStyles]
124
+ () => ({ isOpen, floatingWrapperNode, onResize: () => mutableUpdateStyles.current() }),
125
+ [isOpen, floatingWrapperNode, mutableUpdateStyles]
79
126
  );
80
127
  useFloatingResizeObserver(floatingResizeOberverConfig);
81
128
  const onObservedMovement = React2.useCallback(() => {
82
129
  if (!isOpen) return;
83
130
  debouncedUpdateStyles();
84
131
  }, [isOpen, debouncedUpdateStyles]);
85
- usePositionObserver(referenceElement, onObservedMovement);
132
+ usePositionObserver(triggerElementReference, onObservedMovement);
86
133
  useFloatingClickOutside({
87
- enabled: isOpen,
88
- floating,
89
- reference: referenceElement,
90
- callbackRef: onClickOutsideRef
134
+ isOpen,
135
+ floatingWrapperNode,
136
+ triggerElementReference,
137
+ onClickOutsideRef
91
138
  });
92
- useFloatingEscape({
93
- enabled: isOpen && closeOnEscape,
94
- floating,
95
- reference: referenceElement,
96
- onEscapeRef,
97
- handleClose,
98
- returnFocusToReference
139
+ const handleControlledOpen = useCallback(() => {
140
+ onOpenRef.current?.({ reason: "controlled-flip", isControlled: true });
141
+ }, [onOpenRef]);
142
+ const handleControlledClose = useCallback(() => {
143
+ onCloseRef.current?.({ reason: "controlled-flip", isControlled: true });
144
+ }, [onCloseRef]);
145
+ useSyntheticEventFromControlledState({
146
+ controlledValue: externallyControlledIsOpen,
147
+ onEnter: handleControlledOpen,
148
+ onExit: handleControlledClose
99
149
  });
100
150
  const computedFloatingStyles = useMemo(() => {
101
151
  const isInteractive = isOpen && hasComputedOnce;
@@ -108,11 +158,11 @@ const useFloatingContext = (props = {}) => {
108
158
  const refs = useMemo(
109
159
  () => ({
110
160
  setReference: setReferenceElement,
111
- setFloating,
112
- floating,
113
- reference: referenceElement
161
+ setFloating: setFloatingWrapperNode,
162
+ floating: floatingWrapperNode,
163
+ reference: triggerElementReference
114
164
  }),
115
- [setReferenceElement, floating, referenceElement]
165
+ [setReferenceElement, floatingWrapperNode, triggerElementReference]
116
166
  );
117
167
  const handlers = useMemo(
118
168
  () => ({
@@ -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-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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",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "/* eslint-disable max-lines */\n/* eslint-disable max-statements */\nimport React, { useCallback, useMemo } from 'react';\nimport {\n useMemoMergePropsWithDefault,\n useValidateTypescriptPropTypes,\n describe,\n type ValidationMap,\n} from '@elliemae/ds-props-helpers';\nimport { useHeadlessTooltip, type UseHeadlessTooltipConfig } 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 { useFloatingResizeObserver } from './hooks/useFloatingResizeObserver.js';\nimport { useSyntheticEventFromControlledState } from './hooks/useSyntheticEventFromControlledState.js';\n\nconst isKeyboardEvent = (\n event: React.FocusEvent | React.MouseEvent | KeyboardEvent | undefined,\n): event is KeyboardEvent => !!(event as KeyboardEvent)?.key;\n\n/**\n * useFloatingContext \u2014 headless positioning + open/close lifecycle for a floating surface\n * (popover / tooltip / dialog-like panel). Returns positioning styles + refs, imperative show/hide\n * helpers, interaction handlers to spread on a trigger, and a discriminated open/close notification.\n *\n * COUPLING: this hook is the shared substrate under ds-app-picker, ds-tooltip and others; its\n * open/close/escape/reason contract is relied on across packages. The consumer that leans hardest on\n * the exact semantics below is ds-app-picker\n * (layout/ds-app-picker/src/parts/AppPickerFloatingContext/useAppPickerFloatingContext.ts). Treat\n * changes to these semantics as cross-package.\n *\n * OPEN STATE \u2014 controlled vs uncontrolled\n * - Uncontrolled: `externallyControlledIsOpen` undefined \u2192 open state lives here (in the underlying\n * ds-hooks-headless-tooltip) and is driven by the focus/hover handlers + the show/hide helpers.\n * - Controlled: `externallyControlledIsOpen` is a boolean \u2192 the consumer owns visibility and the\n * internal open state is inert. `isControlled` is surfaced on every OpenChange payload.\n *\n * NOTIFICATION \u2014 one onOpen/onClose pair, two reasons (see OpenChange in react-desc-prop-types)\n * - 'interaction': fired synchronously in the event catch phase by this hook's own interaction\n * handling \u2014 focus/hover/blur/mouseleave, an imperative show/hide, or a NON-scoped Escape falling\n * through to onClose \u2014 carrying the DOM event when there is one. Fires for controlled contexts too\n * (the pre-flip signal a controlled consumer may act on).\n * - 'controlled-flip': fired from a post-commit effect (useSyntheticEventFromControlledState) when\n * `externallyControlledIsOpen` transitions. No event. NEVER fires on mount \u2014 a surface rendered\n * already-open gets no onOpen and must seed any initial focus itself.\n *\n * ESCAPE \u2014 one listener, not two\n * Escape dismissal rides ds-hooks-headless-tooltip's single always-on document keydown listener (it\n * calls hideTooltip on ANY Escape, even when this context never opened). handleClose is the one place\n * that interprets it:\n * - SCOPED Escape (was open + closeOnEscape + key 'Escape' + focus within the panel/reference)\n * \u2192 routes to `onEscape` (NOT onClose); if returnFocusToReference, focuses the reference.\n * - anything else, including a non-scoped Escape \u2192 `onClose` with reason 'interaction'.\n * returnFocusToReference is a SCOPED-ESCAPE-ONLY affordance; no other close path moves focus.\n *\n * FRAGILITY: closeOnEscape is honored independently of who owns open state. A CONTROLLED consumer that\n * leaves closeOnEscape on but does not itself close on Escape will have focus returned to the reference\n * on a scoped Escape while the consumer-owned open state has not changed \u2014 focus can leave a panel that\n * stays open. Controlled consumers should own Escape (ds-app-picker uses `closeOnEscape: !onKeyDown`).\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 // Whether the consumer owns the open state. Surfaced on the OpenChange payload so a handler can\n // branch without re-reading props.\n const isControlled = externallyControlledIsOpen !== undefined;\n // Anchor and floating refs\n const [floatingWrapperNode, setFloatingWrapperNode] = React.useState<HTMLElement | null>(null);\n\n // Resolved below (after useResolvedReference) into a stable ref rather than referenced directly,\n // because isFocusWithinFloatingScope / the escape-handling below need to read it from a config\n // object built BEFORE useResolvedReference (and the useHeadlessTooltip call it depends on) runs.\n // A ref sidesteps that ordering constraint; the value it holds is always current by the time\n // anything actually reads it (Escape handling only ever happens after mount, never during this\n // synchronous render pass).\n const triggerElementReferenceRef = React.useRef<Element | null>(null);\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 // Latest-refs for the reactive VALUES that handleOpen/handleClose read. These handlers are wired\n // into ds-hooks-headless-tooltip's always-on document keydown listener; if a handler's identity\n // changed whenever one of these values changed (isOpen flips on every open/close,\n // floatingWrapperNode on every mount/unmount), that listener would detach + re-attach on every\n // transition. Reading through refs keeps the handlers identity-stable so the listener attaches\n // once. Handlers only ever run post-commit, so the ref always holds the current value when read \u2014\n // same rationale as the consumer-callback refs above.\n const isControlledRef = useLatestRef(isControlled);\n const closeOnEscapeRef = useLatestRef(closeOnEscape);\n const returnFocusToReferenceRef = useLatestRef(returnFocusToReference);\n const floatingWrapperNodeRef = useLatestRef(floatingWrapperNode);\n const isOpenRef = useLatestRef(isOpen);\n\n const handleOpen = React.useCallback<Required<UseHeadlessTooltipConfig>['onOpen']>(\n (event) => {\n setInternalIsOpen(true);\n onOpenRef.current?.({ reason: 'interaction', event, isControlled: isControlledRef.current });\n },\n [onOpenRef, isControlledRef],\n );\n\n // \"In scope\" = the active element is inside the floating (popover) content, or is/inside the\n // reference (trigger) element. Used to decide whether an Escape is this floating context's concern.\n const isFocusWithinFloatingScope = useCallback((): boolean => {\n const active = document.activeElement;\n if (!active) return false;\n if (floatingWrapperNodeRef.current?.contains(active)) return true;\n const reference = triggerElementReferenceRef.current;\n return reference === active || !!reference?.contains(active);\n }, [floatingWrapperNodeRef]);\n\n const handleClose = React.useCallback<Required<UseHeadlessTooltipConfig>['onClose']>(\n (event) => {\n // Read before flipping state: a global Escape press can arrive while this context is\n // already closed (the underlying listener is always-on, see ds-hooks-headless-tooltip), and\n // onEscape must not fire for that \u2014 only a close that was actually transitioning from open.\n const wasOpen = isOpenRef.current;\n setInternalIsOpen(false);\n\n // Escape routing: when the context was open, `closeOnEscape` is set, the event is a keyboard\n // Escape, AND focus was within scope, we route to the dedicated `onEscape` callback (instead\n // of `onClose`) and, if asked, return focus to the reference. Every other close \u2014 including a\n // non-opted-in Escape \u2014 goes through `onClose` with `reason: 'interaction'`. Whether that\n // interaction is meaningful to a controlled consumer is THEIR call: they discriminate on the\n // OpenChange `reason` (interaction vs controlled-flip). We do not decide that for them here.\n // All reactive values are read through refs (see the latest-ref block above) so this handler's\n // identity stays stable and the always-on keydown listener is not re-attached per transition.\n const isScopedEscapeClose =\n wasOpen &&\n closeOnEscapeRef.current &&\n isKeyboardEvent(event) &&\n event.key === 'Escape' &&\n isFocusWithinFloatingScope();\n\n if (isScopedEscapeClose) {\n onEscapeRef.current?.(event);\n // Focus the resolved reference directly rather than relying solely on\n // ds-hooks-headless-tooltip's own focusReferenceOnEscape: that mechanism focuses the\n // hook's *internal* reference element, which stays null whenever this context is\n // configured with `externalReferenceElement` (useResolvedReference makes\n // refs.setReference a no-op in that mode \u2014 see its docstring). triggerElementReferenceRef\n // is always the correctly-resolved element (internal or external), so focusing it here\n // works for both.\n if (returnFocusToReferenceRef.current && triggerElementReferenceRef.current instanceof HTMLElement) {\n triggerElementReferenceRef.current.focus();\n }\n } else {\n onCloseRef.current?.({ reason: 'interaction', event, isControlled: isControlledRef.current });\n }\n },\n [\n isOpenRef,\n closeOnEscapeRef,\n isFocusWithinFloatingScope,\n returnFocusToReferenceRef,\n onCloseRef,\n onEscapeRef,\n isControlledRef,\n ],\n );\n\n // Passed as a thunk (not a pre-computed boolean) into useHeadlessTooltip's config: this config\n // object is built before useResolvedReference runs, so a boolean computed here would be evaluated\n // one render behind the reference element it depends on. Deferring the check to actual\n // Escape-handling time (inside ds-hooks-headless-tooltip) avoids that staleness.\n // Only drives ds-hooks-headless-tooltip's *internal*-reference focus path (see handleClose above\n // for why the externally-resolved reference is focused directly instead).\n const shouldFocusReferenceOnEscape = useCallback(\n () => closeOnEscapeRef.current && returnFocusToReferenceRef.current && isFocusWithinFloatingScope(),\n [closeOnEscapeRef, returnFocusToReferenceRef, isFocusWithinFloatingScope],\n );\n const tooltipHelpersConfig: UseHeadlessTooltipConfig = useMemo(\n () => ({ onOpen: handleOpen, onClose: handleClose, focusReferenceOnEscape: shouldFocusReferenceOnEscape }),\n [handleOpen, handleClose, shouldFocusReferenceOnEscape],\n );\n // Headless tooltip exposes show/hide helpers + manages its own internal state.\n const tooltipHelpers = useHeadlessTooltip(tooltipHelpersConfig);\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 { triggerElementReference, setReferenceElement } = useResolvedReference({\n externalReferenceElement,\n internalReferenceElement,\n setInternalReferenceElement,\n });\n // Keep the ref declared above in sync \u2014 see its declaration comment for why this can't just be\n // read directly from isFocusWithinFloatingScope/handleClose without going through a ref first.\n triggerElementReferenceRef.current = triggerElementReference;\n\n // Compute position (skip when closed)\n const { arrowStyles, floatingStyles, hasComputedOnce, updateStyles, debouncedUpdateStyles, mutableUpdateStyles } =\n useComputedPositionStyles({\n triggerElementReference,\n floatingWrapperNode,\n placement,\n placementOrderPreference,\n customOffset,\n withoutPortal,\n isClose: !isOpen,\n debounceMs: 150,\n });\n\n const floatingResizeOberverConfig = useMemo(\n () => ({ isOpen, floatingWrapperNode, onResize: () => mutableUpdateStyles.current() }),\n [isOpen, floatingWrapperNode, 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(triggerElementReference, onObservedMovement);\n\n // Click-outside dismissal (opt-in via `onClickOutside`).\n useFloatingClickOutside({\n isOpen,\n floatingWrapperNode,\n triggerElementReference,\n onClickOutsideRef,\n });\n\n // Route controlled-flip transitions through the SAME onOpen/onClose callbacks, tagged\n // `reason: 'controlled-flip'`. The interaction handlers above emit `reason: 'interaction'` in the\n // event catch phase; these cover the transitions driven by the consumer flipping\n // `externallyControlledIsOpen`, which have no interaction to hang off \u2014 the sanctioned \"sync with\n // external system\" effect (see the hook). One callback, one contract; `reason` discriminates.\n const handleControlledOpen = useCallback(() => {\n onOpenRef.current?.({ reason: 'controlled-flip', isControlled: true });\n }, [onOpenRef]);\n const handleControlledClose = useCallback(() => {\n onCloseRef.current?.({ reason: 'controlled-flip', isControlled: true });\n }, [onCloseRef]);\n useSyntheticEventFromControlledState({\n controlledValue: externallyControlledIsOpen,\n onEnter: handleControlledOpen,\n onExit: handleControlledClose,\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: setFloatingWrapperNode,\n floating: floatingWrapperNode,\n reference: triggerElementReference,\n }),\n [setReferenceElement, floatingWrapperNode, triggerElementReference],\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
  }
@@ -1,19 +1,19 @@
1
1
  import * as React from "react";
2
2
  import { useEffect } from "react";
3
3
  const useFloatingClickOutside = ({
4
- enabled,
5
- floating,
6
- reference,
7
- callbackRef
4
+ isOpen,
5
+ floatingWrapperNode,
6
+ triggerElementReference,
7
+ onClickOutsideRef
8
8
  }) => {
9
9
  useEffect(() => {
10
- if (!enabled || !floating || !reference) return void 0;
10
+ if (!isOpen || !floatingWrapperNode || !triggerElementReference) return void 0;
11
11
  const listener = (event) => {
12
- const cb = callbackRef.current;
12
+ const cb = onClickOutsideRef.current;
13
13
  if (!cb) return;
14
14
  const target = event.target;
15
15
  if (!target) return;
16
- if (floating.contains(target) || reference.contains(target)) return;
16
+ if (floatingWrapperNode.contains(target) || triggerElementReference.contains(target)) return;
17
17
  cb(event);
18
18
  };
19
19
  document.addEventListener("mousedown", listener, true);
@@ -22,7 +22,7 @@ const useFloatingClickOutside = ({
22
22
  document.removeEventListener("mousedown", listener, true);
23
23
  document.removeEventListener("touchstart", listener, true);
24
24
  };
25
- }, [enabled, floating, reference, callbackRef]);
25
+ }, [floatingWrapperNode, triggerElementReference, onClickOutsideRef, isOpen]);
26
26
  };
27
27
  export {
28
28
  useFloatingClickOutside
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
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;",
4
+ "sourcesContent": ["import * as React from 'react';\nexport { React };\n", "import { useEffect, type MutableRefObject } from 'react';\n\ninterface UseFloatingClickOutsideParams {\n isOpen: boolean;\n floatingWrapperNode: HTMLElement | null;\n triggerElementReference: 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 onClickOutsideRef: 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 isOpen,\n floatingWrapperNode,\n triggerElementReference,\n onClickOutsideRef,\n}: UseFloatingClickOutsideParams) => {\n useEffect(() => {\n if (!isOpen || !floatingWrapperNode || !triggerElementReference) return undefined;\n\n const listener = (event: MouseEvent | TouchEvent) => {\n const cb = onClickOutsideRef.current;\n if (!cb) return;\n const target = event.target as Node | null;\n if (!target) return;\n if (floatingWrapperNode.contains(target) || triggerElementReference.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 }, [floatingWrapperNode, triggerElementReference, onClickOutsideRef, isOpen]);\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,UAAU,CAAC,uBAAuB,CAAC,wBAAyB,QAAO;AAExE,UAAM,WAAW,CAAC,UAAmC;AACnD,YAAM,KAAK,kBAAkB;AAC7B,UAAI,CAAC,GAAI;AACT,YAAM,SAAS,MAAM;AACrB,UAAI,CAAC,OAAQ;AACb,UAAI,oBAAoB,SAAS,MAAM,KAAK,wBAAwB,SAAS,MAAM,EAAG;AACtF,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,qBAAqB,yBAAyB,mBAAmB,MAAM,CAAC;AAC9E;",
6
6
  "names": []
7
7
  }
@@ -1,16 +1,20 @@
1
1
  import * as React from "react";
2
2
  import { useEffect, useRef } from "react";
3
- const useFloatingResizeObserver = ({ enabled, floating, onResize }) => {
3
+ const useFloatingResizeObserver = ({
4
+ isOpen,
5
+ floatingWrapperNode,
6
+ onResize
7
+ }) => {
4
8
  const onResizeRef = useRef(onResize);
5
9
  onResizeRef.current = onResize;
6
10
  useEffect(() => {
7
- if (!enabled || !floating) return void 0;
11
+ if (!isOpen || !floatingWrapperNode) return void 0;
8
12
  const observer = new ResizeObserver(() => {
9
13
  onResizeRef.current();
10
14
  });
11
- observer.observe(floating);
15
+ observer.observe(floatingWrapperNode);
12
16
  return () => observer.disconnect();
13
- }, [enabled, floating]);
17
+ }, [isOpen, floatingWrapperNode]);
14
18
  };
15
19
  export {
16
20
  useFloatingResizeObserver