@mortenbrudvik/waveui 0.11.0 → 0.12.0

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 (116) hide show
  1. package/README.md +97 -2
  2. package/dist/components/button/Link.cjs +1 -1
  3. package/dist/components/button/Link.cjs.map +1 -1
  4. package/dist/components/button/Link.mjs +4 -4
  5. package/dist/components/data-display/AvatarGroup.cjs +1 -1
  6. package/dist/components/data-display/AvatarGroup.cjs.map +1 -1
  7. package/dist/components/data-display/AvatarGroup.mjs +10 -10
  8. package/dist/components/data-display/InfoButton.cjs +1 -1
  9. package/dist/components/data-display/InfoButton.cjs.map +1 -1
  10. package/dist/components/data-display/InfoButton.mjs +12 -12
  11. package/dist/components/feedback/Toaster.cjs +1 -1
  12. package/dist/components/feedback/Toaster.cjs.map +1 -1
  13. package/dist/components/feedback/Toaster.mjs +235 -235
  14. package/dist/components/feedback/Toaster.mjs.map +1 -1
  15. package/dist/components/input/Combobox.cjs +1 -1
  16. package/dist/components/input/Combobox.cjs.map +1 -1
  17. package/dist/components/input/Combobox.mjs +176 -175
  18. package/dist/components/input/Combobox.mjs.map +1 -1
  19. package/dist/components/input/DatePicker.cjs +1 -1
  20. package/dist/components/input/DatePicker.cjs.map +1 -1
  21. package/dist/components/input/DatePicker.mjs +188 -187
  22. package/dist/components/input/DatePicker.mjs.map +1 -1
  23. package/dist/components/input/Dropdown.cjs +1 -1
  24. package/dist/components/input/Dropdown.cjs.map +1 -1
  25. package/dist/components/input/Dropdown.mjs +41 -40
  26. package/dist/components/input/Dropdown.mjs.map +1 -1
  27. package/dist/components/input/Option.cjs +1 -1
  28. package/dist/components/input/Option.cjs.map +1 -1
  29. package/dist/components/input/Option.mjs +89 -87
  30. package/dist/components/input/Option.mjs.map +1 -1
  31. package/dist/components/input/TagPicker.root.cjs +1 -1
  32. package/dist/components/input/TagPicker.root.cjs.map +1 -1
  33. package/dist/components/input/TagPicker.root.mjs +136 -135
  34. package/dist/components/input/TagPicker.root.mjs.map +1 -1
  35. package/dist/components/input/TimePicker.cjs +1 -1
  36. package/dist/components/input/TimePicker.cjs.map +1 -1
  37. package/dist/components/input/TimePicker.mjs +156 -154
  38. package/dist/components/input/TimePicker.mjs.map +1 -1
  39. package/dist/components/navigation/Menu.popover.cjs +1 -1
  40. package/dist/components/navigation/Menu.popover.cjs.map +1 -1
  41. package/dist/components/navigation/Menu.popover.mjs +137 -127
  42. package/dist/components/navigation/Menu.popover.mjs.map +1 -1
  43. package/dist/components/navigation/Menu.root.cjs +1 -1
  44. package/dist/components/navigation/Menu.root.cjs.map +1 -1
  45. package/dist/components/navigation/Menu.root.mjs +4 -4
  46. package/dist/components/overlays/Dialog.cjs +1 -1
  47. package/dist/components/overlays/Dialog.cjs.map +1 -1
  48. package/dist/components/overlays/Dialog.mjs +123 -123
  49. package/dist/components/overlays/Dialog.mjs.map +1 -1
  50. package/dist/components/overlays/Drawer.panel.cjs +1 -1
  51. package/dist/components/overlays/Drawer.panel.cjs.map +1 -1
  52. package/dist/components/overlays/Drawer.panel.mjs +12 -12
  53. package/dist/components/overlays/Drawer.panel.mjs.map +1 -1
  54. package/dist/components/overlays/Popover.cjs +1 -1
  55. package/dist/components/overlays/Popover.cjs.map +1 -1
  56. package/dist/components/overlays/Popover.mjs +173 -162
  57. package/dist/components/overlays/Popover.mjs.map +1 -1
  58. package/dist/components/overlays/TeachingPopover.cjs +1 -1
  59. package/dist/components/overlays/TeachingPopover.cjs.map +1 -1
  60. package/dist/components/overlays/TeachingPopover.mjs +96 -91
  61. package/dist/components/overlays/TeachingPopover.mjs.map +1 -1
  62. package/dist/components/overlays/Tooltip.cjs +1 -1
  63. package/dist/components/overlays/Tooltip.cjs.map +1 -1
  64. package/dist/components/overlays/Tooltip.mjs +47 -47
  65. package/dist/components/overlays/Tooltip.mjs.map +1 -1
  66. package/dist/components/portal/Portal.cjs +1 -1
  67. package/dist/components/portal/Portal.cjs.map +1 -1
  68. package/dist/components/portal/Portal.mjs +5 -5
  69. package/dist/components/table/DataGrid.cjs +1 -1
  70. package/dist/components/table/DataGrid.cjs.map +1 -1
  71. package/dist/components/table/DataGrid.mjs +1 -1
  72. package/dist/hooks/useDismiss.cjs.map +1 -1
  73. package/dist/hooks/useDismiss.mjs.map +1 -1
  74. package/dist/hooks/useFocusTrap.cjs.map +1 -1
  75. package/dist/hooks/useFocusTrap.mjs.map +1 -1
  76. package/dist/hooks/useFocusVisible.cjs +2 -0
  77. package/dist/hooks/useFocusVisible.cjs.map +1 -0
  78. package/dist/hooks/useFocusVisible.mjs +49 -0
  79. package/dist/hooks/useFocusVisible.mjs.map +1 -0
  80. package/dist/hooks/useFocusableGroup.cjs +2 -0
  81. package/dist/hooks/useFocusableGroup.cjs.map +1 -0
  82. package/dist/hooks/useFocusableGroup.mjs +70 -0
  83. package/dist/hooks/useFocusableGroup.mjs.map +1 -0
  84. package/dist/hooks/useModalLayer.cjs +1 -1
  85. package/dist/hooks/useModalLayer.cjs.map +1 -1
  86. package/dist/hooks/useModalLayer.mjs +4 -4
  87. package/dist/hooks/useModalOpen.cjs +2 -0
  88. package/dist/hooks/useModalOpen.cjs.map +1 -0
  89. package/dist/hooks/useModalOpen.mjs +14 -0
  90. package/dist/hooks/useModalOpen.mjs.map +1 -0
  91. package/dist/hooks/usePopupPosition.cjs +1 -1
  92. package/dist/hooks/usePopupPosition.cjs.map +1 -1
  93. package/dist/hooks/usePopupPosition.mjs +82 -53
  94. package/dist/hooks/usePopupPosition.mjs.map +1 -1
  95. package/dist/hooks/useResolvedTarget.cjs +2 -0
  96. package/dist/hooks/useResolvedTarget.cjs.map +1 -0
  97. package/dist/hooks/useResolvedTarget.mjs +37 -0
  98. package/dist/hooks/useResolvedTarget.mjs.map +1 -0
  99. package/dist/hooks/useRovingGrid.cjs +1 -1
  100. package/dist/hooks/useRovingGrid.cjs.map +1 -1
  101. package/dist/hooks/useRovingGrid.mjs +8 -57
  102. package/dist/hooks/useRovingGrid.mjs.map +1 -1
  103. package/dist/hooks/useRovingTabIndex.cjs +1 -1
  104. package/dist/hooks/useRovingTabIndex.cjs.map +1 -1
  105. package/dist/hooks/useRovingTabIndex.mjs +142 -107
  106. package/dist/hooks/useRovingTabIndex.mjs.map +1 -1
  107. package/dist/index.cjs +1 -1
  108. package/dist/index.d.cts +814 -18
  109. package/dist/index.d.ts +814 -18
  110. package/dist/index.mjs +120 -108
  111. package/dist/lib/layers.cjs +1 -1
  112. package/dist/lib/layers.cjs.map +1 -1
  113. package/dist/lib/layers.mjs +112 -109
  114. package/dist/lib/layers.mjs.map +1 -1
  115. package/dist/styles.css +1 -1
  116. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"useModalLayer.cjs","names":["useDismiss","require_useDismiss","require_useFocusTrap","require_useRestoreFocus","require_useModalIsolation","require_useScrollLock","useModalLayer","dismissOptions","options","nonModal","modalType","modal","open","layer","container"],"sources":["../../src/hooks/useModalLayer.ts"],"sourcesContent":["import type * as React from 'react';\nimport type { ModalType } from '../lib/types';\nimport { useDismiss, type DismissLayer, type UseDismissOptions } from './useDismiss';\nimport { useFocusTrap, useInitialFocus, type UseFocusTrapOptions } from './useFocusTrap';\nimport { useModalIsolation } from './useModalIsolation';\nimport { useRestoreFocus } from './useRestoreFocus';\nimport { useScrollLock } from './useScrollLock';\n\n/**\n * Options of {@link useModalLayer}: `useDismiss` options (the kind follows `modalType`) plus focus.\n */\nexport interface UseModalLayerOptions extends Omit<UseDismissOptions, 'kind'> {\n /** The modal surface, held in state via a callback ref. */\n container: HTMLElement | null;\n /**\n * `modal` (default) and `alert` register a `modal` layer, trap focus, isolate the page, lock its\n * scroll and restore focus (an `alert` layer ignores outside presses); `non-modal` registers a\n * `dialog` layer (`escape: 'inside'`, no outside press, no focus-outside), traps, isolates and\n * locks nothing, moves focus in on open with the trap's initial-focus rule and restores focus only\n * from inside (`requireFocusInside`).\n * @default 'modal'\n */\n modalType?: ModalType;\n /** What the focus trap (or a non-modal surface's initial focus) focuses first. @default 'first' */\n initialFocus?: UseFocusTrapOptions['initialFocus'];\n /** The trigger: preferred focus-restore target. */\n triggerRef?: React.RefObject<HTMLElement | null>;\n /** Consumer override for the focus-restore target. */\n finalFocusRef?: React.RefObject<HTMLElement | null>;\n}\n\n/**\n * Everything a modal surface (Dialog, Drawer) needs, composed in the order that works:\n *\n * 1. `useDismiss({ kind: 'modal' })` — Escape and outside presses (the backdrop is outside the\n * content surface, so a click on it dismisses; a drag from the content does not).\n * 2. `useFocusTrap` — initial focus, Tab cycle incl. toasts and descendant layers.\n * 3. `useModalIsolation` — the rest of the page is `inert` (instead of `aria-modal`).\n * 4. `useScrollLock` — ref-counted page scroll lock.\n * 5. `useRestoreFocus` — the opener (or `triggerRef`/`finalFocusRef`) gets focus back.\n *\n * On close the trap is released before the isolation is lifted, and the isolation is lifted\n * before focus is restored (an inert opener cannot take focus). Wrap the surface in\n * `Portal layerId={layerId}` so layers opened inside the modal become its descendants.\n *\n * `modalType: 'alert'` composes the same with `outsidePress: false`. `modalType: 'non-modal'`\n * registers a `dialog` layer (Escape only from inside its tree, no outside press, no focus-outside\n * dismissal), keeps the trap, the isolation and the scroll lock off, moves focus in on open with\n * the trap's initial-focus rule (`useInitialFocus`) and restores focus only when it is inside the\n * surface (or in a layer opened from it) at close, or was lost to `<body>` from there. Every hook\n * is always called and keys on `open`: a `modalType` that changes while open applies at once (the\n * trap, isolation and lock follow). It leaves focus inside the surface where it is; focus outside\n * it moves in, as on open: a switch to `modal` or `alert` starts the trap (the page turns inert),\n * and a switch to `non-modal` runs the initial focus (focus in the toast region, which a trap lets\n * in, moves into the surface). The Tab order of a non-modal surface (its place after the opener)\n * is the component's.\n *\n * A surface that mounts through the presence core (Dialog, Drawer) passes its open state as\n * `open`, never its mount (`isMounted`), and its element as `container` also while it is mounted\n * closed: the layer, the trap, the isolation and the scroll lock end and focus is restored in the\n * commit that closes it, while the element still runs its exit motion (`inert`) or stays mounted\n * (`hidden`, with `unmountOnClose={false}`); the next open starts them again with the same\n * element.\n */\nexport function useModalLayer(options: UseModalLayerOptions): DismissLayer {\n const {\n container,\n modalType = 'modal',\n initialFocus,\n triggerRef,\n finalFocusRef,\n ...dismissOptions\n } = options;\n const { open } = options;\n const nonModal = modalType === 'non-modal';\n const modal = open && !nonModal;\n\n const layer = useDismiss(\n nonModal\n ? {\n ...dismissOptions,\n kind: 'dialog',\n escape: 'inside',\n outsidePress: false,\n focusOutside: false,\n }\n : {\n ...dismissOptions,\n kind: 'modal',\n ...(modalType === 'alert' && { outsidePress: false }),\n },\n );\n useFocusTrap(container, { enabled: modal, layerId: layer.layerId, initialFocus });\n useInitialFocus(container, { enabled: open && nonModal, layerId: layer.layerId, initialFocus });\n useModalIsolation(modal, { layerId: layer.layerId, container });\n useScrollLock(modal);\n useRestoreFocus({\n enabled: open,\n container,\n triggerRef,\n finalFocusRef,\n requireFocusInside: nonModal,\n layerId: layer.layerId,\n });\n\n return layer;\n}\n"],"mappings":"0LAgEA,SAAgBM,EAAc,EAA6C,CACzE,GAAM,CACJ,YACA,YAAY,QACZ,eACA,aACA,gBACA,GAAGC,GACDC,EACE,CAAE,QAASA,EACXC,EAAWC,IAAc,YACzBC,EAAQC,GAAQ,CAACH,EAEjBI,EAAQb,EAAAA,WACZS,EACI,CACE,GAAGF,EACH,KAAM,SACN,OAAQ,SACR,aAAc,GACd,aAAc,EAChB,EACA,CACE,GAAGA,EACH,KAAM,QACN,GAAIG,IAAc,SAAW,CAAE,aAAc,EAAM,CACrD,CACN,EAcA,OAbAR,EAAA,aAAaY,EAAW,CAAE,QAASH,EAAO,QAASE,EAAM,QAAS,cAAa,CAAC,EAChFX,EAAA,gBAAgBY,EAAW,CAAE,QAASF,GAAQH,EAAU,QAASI,EAAM,QAAS,cAAa,CAAC,EAC9FT,EAAA,kBAAkBO,EAAO,CAAE,QAASE,EAAM,QAAS,WAAU,CAAC,EAC9DR,EAAA,cAAcM,CAAK,EACnBR,EAAA,gBAAgB,CACd,QAASS,EACT,YACA,aACA,gBACA,mBAAoBH,EACpB,QAASI,EAAM,OACjB,CAAC,EAEMA,CACT"}
1
+ {"version":3,"file":"useModalLayer.cjs","names":["useDismiss","require_useDismiss","require_useFocusTrap","require_useModalIsolation","require_useRestoreFocus","require_useScrollLock","useModalLayer","dismissOptions","options","nonModal","modalType","modal","open","layer","container"],"sources":["../../src/hooks/useModalLayer.ts"],"sourcesContent":["import type * as React from 'react';\nimport type { ModalType } from '../lib/types';\nimport { useDismiss, type DismissLayer, type UseDismissOptions } from './useDismiss';\nimport { useFocusTrap, useInitialFocus, type UseFocusTrapOptions } from './useFocusTrap';\nimport { useModalIsolation } from './useModalIsolation';\nimport { useRestoreFocus } from './useRestoreFocus';\nimport { useScrollLock } from './useScrollLock';\n\n/**\n * Options of {@link useModalLayer}: `useDismiss` options (the kind follows `modalType`) plus focus.\n */\nexport interface UseModalLayerOptions extends Omit<UseDismissOptions, 'kind'> {\n /** The modal surface, held in state via a callback ref. */\n container: HTMLElement | null;\n /**\n * `modal` (default) and `alert` register a `modal` layer, trap focus, isolate the page, lock its\n * scroll and restore focus (an `alert` layer ignores outside presses); `non-modal` registers a\n * `dialog` layer (`escape: 'inside'`, no outside press, no focus-outside), traps, isolates and\n * locks nothing, moves focus in on open with the trap's initial-focus rule and restores focus only\n * from inside (`requireFocusInside`).\n * @default 'modal'\n */\n modalType?: ModalType;\n /** What the focus trap (or a non-modal surface's initial focus) focuses first. @default 'first' */\n initialFocus?: UseFocusTrapOptions['initialFocus'];\n /** The trigger: preferred focus-restore target. */\n triggerRef?: React.RefObject<HTMLElement | null>;\n /** Consumer override for the focus-restore target. */\n finalFocusRef?: React.RefObject<HTMLElement | null>;\n}\n\n/**\n * Everything a modal surface (Dialog, Drawer) needs, composed in the order that works:\n *\n * 1. `useDismiss({ kind: 'modal' })` — Escape and outside presses (the backdrop is outside the\n * content surface, so a click on it dismisses; a drag from the content does not).\n * 2. `useFocusTrap` — initial focus, Tab cycle incl. toasts and descendant layers.\n * 3. `useModalIsolation` — the rest of the page is `inert` (instead of `aria-modal`).\n * 4. `useScrollLock` — ref-counted page scroll lock.\n * 5. `useRestoreFocus` — the opener (or `triggerRef`/`finalFocusRef`) gets focus back.\n *\n * On close the trap is released before the isolation is lifted, and the isolation is lifted\n * before focus is restored (an inert opener cannot take focus). Wrap the surface in\n * `Portal layerId={layerId}` so layers opened inside the modal become its descendants.\n *\n * `modalType: 'alert'` composes the same with `outsidePress: false`. `modalType: 'non-modal'`\n * registers a `dialog` layer (Escape only from inside its tree, no outside press, no focus-outside\n * dismissal), keeps the trap, the isolation and the scroll lock off, moves focus in on open with\n * the trap's initial-focus rule (`useInitialFocus`) and restores focus only when it is inside the\n * surface (or in a layer opened from it) at close, or was lost to `<body>` from there. Every hook\n * is always called and keys on `open`: a `modalType` that changes while open applies at once (the\n * trap, isolation and lock follow). It leaves focus inside the surface where it is; focus outside\n * it moves in, as on open: a switch to `modal` or `alert` starts the trap (the page turns inert),\n * and a switch to `non-modal` runs the initial focus (focus in the toast region, which a trap lets\n * in, moves into the surface). The Tab order of a non-modal surface (its place after the opener)\n * is the component's.\n *\n * A surface that mounts through the presence core (Dialog, Drawer) passes its open state as\n * `open`, never its mount (`isMounted`), and its element as `container` also while it is mounted\n * closed: the layer, the trap, the isolation and the scroll lock end and focus is restored in the\n * commit that closes it, while the element still runs its exit motion (`inert`) or stays mounted\n * (`hidden`, with `unmountOnClose={false}`); the next open starts them again with the same\n * element.\n */\nexport function useModalLayer(options: UseModalLayerOptions): DismissLayer {\n const {\n container,\n modalType = 'modal',\n initialFocus,\n triggerRef,\n finalFocusRef,\n ...dismissOptions\n } = options;\n const { open } = options;\n const nonModal = modalType === 'non-modal';\n const modal = open && !nonModal;\n\n const layer = useDismiss(\n nonModal\n ? {\n ...dismissOptions,\n kind: 'dialog',\n escape: 'inside',\n outsidePress: false,\n focusOutside: false,\n }\n : {\n ...dismissOptions,\n kind: 'modal',\n ...(modalType === 'alert' && { outsidePress: false }),\n },\n );\n useFocusTrap(container, { enabled: modal, layerId: layer.layerId, initialFocus });\n useInitialFocus(container, { enabled: open && nonModal, layerId: layer.layerId, initialFocus });\n useModalIsolation(modal, { layerId: layer.layerId, container });\n useScrollLock(modal);\n useRestoreFocus({\n enabled: open,\n container,\n triggerRef,\n finalFocusRef,\n requireFocusInside: nonModal,\n layerId: layer.layerId,\n });\n\n return layer;\n}\n"],"mappings":"0LAgEA,SAAgBM,EAAc,EAA6C,CACzE,GAAM,CACJ,YACA,YAAY,QACZ,eACA,aACA,gBACA,GAAGC,GACDC,EACE,CAAE,QAASA,EACXC,EAAWC,IAAc,YACzBC,EAAQC,GAAQ,CAACH,EAEjBI,EAAQb,EAAAA,WACZS,EACI,CACE,GAAGF,EACH,KAAM,SACN,OAAQ,SACR,aAAc,GACd,aAAc,EAChB,EACA,CACE,GAAGA,EACH,KAAM,QACN,GAAIG,IAAc,SAAW,CAAE,aAAc,EAAM,CACrD,CACN,EAcA,OAbAR,EAAA,aAAaY,EAAW,CAAE,QAASH,EAAO,QAASE,EAAM,QAAS,cAAa,CAAC,EAChFX,EAAA,gBAAgBY,EAAW,CAAE,QAASF,GAAQH,EAAU,QAASI,EAAM,QAAS,cAAa,CAAC,EAC9FV,EAAA,kBAAkBQ,EAAO,CAAE,QAASE,EAAM,QAAS,WAAU,CAAC,EAC9DR,EAAA,cAAcM,CAAK,EACnBP,EAAA,gBAAgB,CACd,QAASQ,EACT,YACA,aACA,gBACA,mBAAoBH,EACpB,QAASI,EAAM,OACjB,CAAC,EAEMA,CACT"}
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { useDismiss as e } from "./useDismiss.mjs";
3
3
  import { useFocusTrap as t, useInitialFocus as n } from "./useFocusTrap.mjs";
4
- import { useRestoreFocus as r } from "./useRestoreFocus.mjs";
5
- import { useModalIsolation as i } from "./useModalIsolation.mjs";
4
+ import { useModalIsolation as r } from "./useModalIsolation.mjs";
5
+ import { useRestoreFocus as i } from "./useRestoreFocus.mjs";
6
6
  import { useScrollLock as a } from "./useScrollLock.mjs";
7
7
  //#region src/hooks/useModalLayer.ts
8
8
  function o(o) {
@@ -25,10 +25,10 @@ function o(o) {
25
25
  enabled: p && m,
26
26
  layerId: g.layerId,
27
27
  initialFocus: l
28
- }), i(h, {
28
+ }), r(h, {
29
29
  layerId: g.layerId,
30
30
  container: s
31
- }), a(h), r({
31
+ }), a(h), i({
32
32
  enabled: p,
33
33
  container: s,
34
34
  triggerRef: u,
@@ -0,0 +1,2 @@
1
+ "use client";const e=require("../lib/layers.cjs");let t=require("react");function n(){return!1}function r(){return(0,t.useSyncExternalStore)(e.subscribeLayers,e.hasOpenModal,n)}exports.useModalOpen=r;
2
+ //# sourceMappingURL=useModalOpen.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useModalOpen.cjs","names":["useSyncExternalStore","subscribeLayers","hasOpenModal","require_layers","react","getServerSnapshot","useModalOpen"],"sources":["../../src/hooks/useModalOpen.ts"],"sourcesContent":["import { useSyncExternalStore } from 'react';\nimport { hasOpenModal, subscribeLayers } from '../lib/layers';\n\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\n/**\n * Whether a modal surface is open (a Dialog, an overlay Drawer, an alert, the DatePicker's calendar, or your\n * own `useDismiss({ kind: 'modal' })` layer): `true` from the\n * commit that opens it to the commit that closes it, not until its exit motion ends. A non-modal\n * Dialog or Drawer, a Popover or a Menu is no modal. `false` on the server and during hydration.\n *\n * Use it to hold back app behavior the modal should own, such as global keyboard shortcuts, instead\n * of guessing from the page's `inert` attributes.\n *\n * @example\n * const modalOpen = useModalOpen();\n * useEffect(() => {\n * if (modalOpen) return;\n * const onKey = (event: KeyboardEvent) => { if (event.key === '/') focusSearch(); };\n * document.addEventListener('keydown', onKey);\n * return () => document.removeEventListener('keydown', onKey);\n * }, [modalOpen]);\n */\nexport function useModalOpen(): boolean {\n return useSyncExternalStore(subscribeLayers, hasOpenModal, getServerSnapshot);\n}\n"],"mappings":"yEAGA,SAASK,GAA6B,CACpC,MAAO,EACT,CAoBA,SAAgBC,GAAwB,CACtC,OAAA,EAAON,EAAAA,qBAAAA,CAAqBC,EAAAA,gBAAiBC,EAAAA,aAAcG,CAAiB,CAC9E"}
@@ -0,0 +1,14 @@
1
+ "use client";
2
+ import { hasOpenModal as e, subscribeLayers as t } from "../lib/layers.mjs";
3
+ import { useSyncExternalStore as n } from "react";
4
+ //#region src/hooks/useModalOpen.ts
5
+ function r() {
6
+ return !1;
7
+ }
8
+ function i() {
9
+ return n(t, e, r);
10
+ }
11
+ //#endregion
12
+ export { i as useModalOpen };
13
+
14
+ //# sourceMappingURL=useModalOpen.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useModalOpen.mjs","names":["getServerSnapshot","useModalOpen","useSyncExternalStore","subscribeLayers","hasOpenModal"],"sources":["../../src/hooks/useModalOpen.ts"],"sourcesContent":["import { useSyncExternalStore } from 'react';\nimport { hasOpenModal, subscribeLayers } from '../lib/layers';\n\nfunction getServerSnapshot(): boolean {\n return false;\n}\n\n/**\n * Whether a modal surface is open (a Dialog, an overlay Drawer, an alert, the DatePicker's calendar, or your\n * own `useDismiss({ kind: 'modal' })` layer): `true` from the\n * commit that opens it to the commit that closes it, not until its exit motion ends. A non-modal\n * Dialog or Drawer, a Popover or a Menu is no modal. `false` on the server and during hydration.\n *\n * Use it to hold back app behavior the modal should own, such as global keyboard shortcuts, instead\n * of guessing from the page's `inert` attributes.\n *\n * @example\n * const modalOpen = useModalOpen();\n * useEffect(() => {\n * if (modalOpen) return;\n * const onKey = (event: KeyboardEvent) => { if (event.key === '/') focusSearch(); };\n * document.addEventListener('keydown', onKey);\n * return () => document.removeEventListener('keydown', onKey);\n * }, [modalOpen]);\n */\nexport function useModalOpen(): boolean {\n return useSyncExternalStore(subscribeLayers, hasOpenModal, getServerSnapshot);\n}\n"],"mappings":";;;;AAGA,SAASA,IAA6B;CACpC,OAAO;AACT;AAoBA,SAAgBC,IAAwB;CACtC,OAAOC,EAAqBC,GAAiBC,GAAcJ,CAAiB;AAC9E"}
@@ -1,2 +1,2 @@
1
- "use client";const e=require("./useEventCallback.cjs"),t=require("./useDirection.cjs");let n=require("react"),r=require("@floating-ui/react-dom");var i={top:`bottom`,bottom:`top`,left:`right`,right:`left`},a=`calc(var(--wave-popup-arrow-size, 8px) / -2)`;function o(e,t){return e===`start`?t===`rtl`?`right`:`left`:e===`end`?t===`rtl`?`left`:`right`:e}var s={x:0,y:0,width:0,height:0,top:0,right:0,bottom:0,left:0};function c(e){return typeof e.nodeType==`number`}function l(e,t){return{getBoundingClientRect:()=>e.current?.getBoundingClientRect()??s,contextElement:t}}function u(e,t,n){return(0,r.autoUpdate)(e,t,n,{elementResize:typeof ResizeObserver<`u`,layoutShift:typeof IntersectionObserver<`u`})}var d=({availableWidth:e,availableHeight:t,rects:n,elements:r})=>{let i=r.floating.style;i.setProperty(`--wave-popup-available-width`,`${Math.max(0,Math.floor(e))}px`),i.setProperty(`--wave-popup-available-height`,`${Math.max(0,Math.floor(t))}px`),i.setProperty(`--wave-popup-reference-width`,`${Math.round(n.reference.width)}px`)};function f(s){let{open:f,side:p=`bottom`,align:m=`start`,offset:h=4,flip:g=!0,shift:_={padding:8},matchReferenceWidth:v=!1,fitViewport:y=!1,arrowRef:b,strategy:x=`fixed`}=s,S=t.useDirection(),C=o(p,S),w=m===`center`?C:`${C}-${m}`,T=_===!1?0:_===!0?8:_.padding,E=typeof _==`object`&&_.crossAxis===!0,D=[(0,r.offset)(h)];g&&D.push((0,r.flip)({padding:T})),_!==!1&&D.push((0,r.shift)({padding:T,crossAxis:E})),(v||y)&&D.push((0,r.size)({padding:T,apply:d})),b&&D.push((0,r.arrow)({element:b}));let O=(0,n.useMemo)(()=>({...r.platform,isRTL:()=>S===`rtl`}),[S]),{refs:k,floatingStyles:A,placement:j,middlewareData:M,isPositioned:N,update:P}=(0,r.useFloating)({open:f,placement:w,strategy:x,middleware:D,platform:O,whileElementsMounted:f?u:void 0}),F=(0,n.useRef)(null),I=(0,n.useRef)(null),L=e.useEventCallback(P),{setReference:R,reference:z}=k,B=(0,n.useCallback)(e=>{if(e===null||c(e)){F.current=null,R(e);return}F.current=e;let t=I.current;(!t||t.contextElement!==e.contextElement)&&(t=l(F,e.contextElement),I.current=t),z.current===t?L():R(t)},[R,z,L]),[V,H=`center`]=j.split(`-`),U=(0,n.useMemo)(()=>{let e={...A};return v&&(e.width=`var(--wave-popup-reference-width)`),y&&(e.maxWidth=`var(--wave-popup-available-width)`,e.maxHeight=`var(--wave-popup-available-height)`),e},[A,v,y]),W=M.arrow?.x,G=M.arrow?.y,K=(0,n.useMemo)(()=>{let e={position:`absolute`,left:W==null?``:`${W}px`,top:G==null?``:`${G}px`};return e[i[V]]=a,e},[W,G,V]),q=(0,n.useMemo)(()=>({"data-side":V,"data-align":H,style:U}),[V,H,U]);return{setReference:B,setFloating:k.setFloating,floatingStyles:U,arrowStyles:K,placement:j,side:V,isPositioned:N,floatingProps:q}}exports.usePopupPosition=f;
1
+ "use client";const e=require("./useEventCallback.cjs"),t=require("./useDirection.cjs");let n=require("react"),r=require("@floating-ui/react-dom");function i(e,t){return{offset:e?.offset??t.offset,matchReferenceWidth:e?.matchReferenceWidth??t.matchReferenceWidth,fitViewport:e?.fitViewport??t.fitViewport,fallbackPlacements:e?.fallbackPlacements}}var a={top:`bottom`,bottom:`top`,left:`right`,right:`left`},o=`calc(var(--wave-popup-arrow-size, 8px) / -2)`;function s(e,t){return e===`start`?t===`rtl`?`right`:`left`:e===`end`?t===`rtl`?`left`:`right`:e}var c={x:0,y:0,width:0,height:0,top:0,right:0,bottom:0,left:0};function l(e){return typeof e.nodeType==`number`}function u(e,t){return{getBoundingClientRect:()=>e.current?.getBoundingClientRect()??c,contextElement:t}}function d(e,t,n){return(0,r.autoUpdate)(e,t,n,{elementResize:typeof ResizeObserver<`u`,layoutShift:typeof IntersectionObserver<`u`})}var f=({availableWidth:e,availableHeight:t,rects:n,elements:r})=>{let i=r.floating.style;i.setProperty(`--wave-popup-available-width`,`${Math.max(0,Math.floor(e))}px`),i.setProperty(`--wave-popup-available-height`,`${Math.max(0,Math.floor(t))}px`),i.setProperty(`--wave-popup-reference-width`,`${Math.round(n.reference.width)}px`)};function p(i){let{open:c,side:p=`bottom`,align:m=`start`,offset:h=4,flip:g=!0,shift:_={padding:8},matchReferenceWidth:v=!1,fitViewport:y=!1,arrowRef:b,strategy:x=`fixed`,fallbackPlacements:S,hideWhenDetached:C=!1,onPositioned:w}=i,T=t.useDirection(),E=s(p,T),D=(e,t)=>t===`center`?e:`${e}-${t}`,O=D(E,m),k=_===!1?0:_===!0?8:_.padding,A=typeof _==`object`&&_.crossAxis===!0,j=[(0,r.offset)(h)];g&&j.push((0,r.flip)({padding:k,...S&&{fallbackPlacements:S.map(e=>D(s(e.side,T),e.align??m))}})),_!==!1&&j.push((0,r.shift)({padding:k,crossAxis:A})),(v||y)&&j.push((0,r.size)({padding:k,apply:f})),C&&j.push((0,r.hide)({strategy:`referenceHidden`})),b&&j.push((0,r.arrow)({element:b}));let M=(0,n.useMemo)(()=>({...r.platform,isRTL:()=>T===`rtl`}),[T]),{refs:N,floatingStyles:P,placement:F,middlewareData:I,isPositioned:L,update:R}=(0,r.useFloating)({open:c,placement:O,strategy:x,middleware:j,platform:M,whileElementsMounted:c?d:void 0}),z=(0,n.useRef)(null),B=(0,n.useRef)(null),V=e.useEventCallback(R),H=e.useEventCallback(()=>{c&&R()}),{setReference:U,reference:W}=N,G=(0,n.useCallback)(e=>{if(e===null||l(e)){z.current=null,U(e);return}z.current=e;let t=B.current;(!t||t.contextElement!==e.contextElement)&&(t=u(z,e.contextElement),B.current=t),W.current===t?V():U(t)},[U,W,V]),[K,q=`center`]=F.split(`-`),J=e.useEventCallback(e=>w?.(e));(0,n.useEffect)(()=>{c&&L&&J({placement:F,side:K,isPositioned:!0})},[c,L,F,K,J]);let Y=I.hide?.referenceHidden===!0,X=(0,n.useMemo)(()=>{let e={...P};return v&&(e.width=`var(--wave-popup-reference-width)`),y&&(e.maxWidth=`var(--wave-popup-available-width)`,e.maxHeight=`var(--wave-popup-available-height)`),Y&&(e.visibility=`hidden`),e},[P,v,y,Y]),Z=I.arrow?.x,Q=I.arrow?.y,$=(0,n.useMemo)(()=>{let e={position:`absolute`,left:Z==null?``:`${Z}px`,top:Q==null?``:`${Q}px`};return e[a[K]]=o,e},[Z,Q,K]),ee=(0,n.useMemo)(()=>({"data-side":K,"data-align":q,style:X}),[K,q,X]);return{setReference:G,setFloating:N.setFloating,floatingStyles:X,arrowStyles:$,placement:F,side:K,isPositioned:L,update:H,floatingProps:ee}}exports.resolvePositioning=i,exports.usePopupPosition=p;
2
2
  //# sourceMappingURL=usePopupPosition.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"usePopupPosition.cjs","names":["autoUpdate","useDirection","offsetMiddleware","flipMiddleware","shiftMiddleware","sizeMiddleware","arrowMiddleware","useMemo","platform","useFloating","useRef","useEventCallback","useCallback","require_useEventCallback","require_useDirection","react","_floating_ui_react_dom","STATIC_SIDE","ARROW_OFFSET","resolveSide","side","dir","EMPTY_RECT","isElementAnchor","anchor","createVirtualProxy","latest","whileElementsMounted","reference","floating","update","sizeApply","style","elements","availableWidth","availableHeight","rects","usePopupPosition","options","physicalSide","requestedPlacement","align","padding","shift","shiftCrossAxis","middleware","offset","flip","matchReferenceWidth","fitViewport","arrowRef","directionalPlatform","open","virtualRef","proxyRef","updatePosition","setFloatingReference","floatingReference","refs","setReference","proxy","finalSide","finalAlign","placement","next","floatingStyles","arrowX","middlewareData","arrowY","arrowStyles","styles","floatingProps"],"sources":["../../src/hooks/usePopupPosition.ts"],"sourcesContent":["import { useCallback, useMemo, useRef } from 'react';\nimport type * as React from 'react';\nimport {\n arrow as arrowMiddleware,\n autoUpdate,\n flip as flipMiddleware,\n offset as offsetMiddleware,\n platform,\n shift as shiftMiddleware,\n size as sizeMiddleware,\n useFloating,\n type Middleware,\n type Placement,\n type Platform,\n type ReferenceType,\n type SizeOptions,\n} from '@floating-ui/react-dom';\nimport type { PopupAlign, PopupRect, PopupSide, VirtualElement } from '../lib/types';\nimport type { Direction } from '../lib/direction';\nimport { useDirection } from './useDirection';\nimport { useEventCallback } from './useEventCallback';\n\n/** Options of {@link usePopupPosition}. */\nexport interface UsePopupPositionOptions {\n /** Whether the popup is open; positions are kept up to date only while `true`. */\n open: boolean;\n /** Side of the anchor. `start`/`end` follow the writing direction. @default 'bottom' */\n side?: PopupSide;\n /** Alignment along that side. @default 'start' */\n align?: PopupAlign;\n /** Distance from the anchor in px. @default 4 (tooltips use 8) */\n offset?: number;\n /** Flip to the opposite side when there is not enough room. @default true */\n flip?: boolean;\n /**\n * Shift along the side to stay inside the viewport. Its padding is also the viewport padding of\n * `flip` and of `size` (`fitViewport`, `matchReferenceWidth`): `true` means 8px, and `false`\n * lets the popup flip only once it touches the viewport edge and grow right up to it. With\n * `crossAxis: true` it also shifts across the side, over the anchor, when neither side has room\n * after `flip` (submenus), and `fitViewport` then offers the whole viewport in that direction.\n * @default { padding: 8 }\n */\n shift?: boolean | { padding: number; crossAxis?: boolean };\n /** Make the surface as wide as the anchor (listboxes). @default false */\n matchReferenceWidth?: boolean;\n /** Limit the surface to the available space (`max-width`/`max-height`). @default false */\n fitViewport?: boolean;\n /** An arrow (beak) element inside the surface, positioned through `arrowStyles`. */\n arrowRef?: React.RefObject<HTMLElement | null>;\n /** CSS positioning strategy. @default 'fixed' */\n strategy?: 'absolute' | 'fixed';\n}\n\n/** Returned by {@link usePopupPosition}. */\nexport interface UsePopupPositionResult {\n /**\n * Sets the anchor: a ref callback for the anchor (trigger) element, or a call from a layout\n * effect or an event. The anchor is an element or a `VirtualElement` (a rectangle such as the\n * pointer position; a new object on every render is fine); give it a `contextElement` so\n * scrolling its containers updates the position.\n */\n setReference(el: HTMLElement | VirtualElement | null): void;\n /** Ref callback for the popup surface. */\n setFloating(el: HTMLElement | null): void;\n /** Position styles for the surface (also in `floatingProps.style`). */\n floatingStyles: React.CSSProperties;\n /** Position styles for the arrow element. */\n arrowStyles: React.CSSProperties;\n /** Final placement after flipping, e.g. `'top-start'`. */\n placement: string;\n /** Final physical side after flipping. */\n side: 'top' | 'bottom' | 'left' | 'right';\n /** Whether a position has been computed since the popup opened. */\n isPositioned: boolean;\n /** Spread onto the surface: `data-side`, `data-align` and the position style. */\n floatingProps: { 'data-side': string; 'data-align': string; style: React.CSSProperties };\n}\n\ntype PhysicalSide = 'top' | 'bottom' | 'left' | 'right';\n\nconst STATIC_SIDE: Record<PhysicalSide, PhysicalSide> = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n};\n\n/** Half the arrow size, so the arrow sticks out of the surface towards the anchor. */\nconst ARROW_OFFSET = 'calc(var(--wave-popup-arrow-size, 8px) / -2)';\n\nfunction resolveSide(side: PopupSide, dir: Direction): PhysicalSide {\n if (side === 'start') return dir === 'rtl' ? 'right' : 'left';\n if (side === 'end') return dir === 'rtl' ? 'left' : 'right';\n return side;\n}\n\n/** The rectangle of a virtual anchor that is not set (never reached while one is the reference). */\nconst EMPTY_RECT: PopupRect = {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n};\n\n/** Whether an anchor is a DOM element (duck typed, so an element of another realm counts). */\nfunction isElementAnchor(anchor: HTMLElement | VirtualElement): anchor is HTMLElement {\n return typeof (anchor as Partial<Node>).nodeType === 'number';\n}\n\n/**\n * The stable stand-in floating-ui gets for virtual anchors: it reads the latest `VirtualElement`\n * from `latest`, so a new virtual object sets no floating-ui state. One proxy per\n * `contextElement`, so `autoUpdate` observes the scroll containers of the current one.\n */\ninterface VirtualProxy extends VirtualElement {\n contextElement: Element | undefined;\n}\n\nfunction createVirtualProxy(\n latest: React.RefObject<VirtualElement | null>,\n contextElement: Element | undefined,\n): VirtualProxy {\n return {\n getBoundingClientRect: () => latest.current?.getBoundingClientRect() ?? EMPTY_RECT,\n contextElement,\n };\n}\n\n/** Keeps the position updated while mounted; observers are used only where they exist. */\nfunction whileElementsMounted(\n reference: ReferenceType,\n floating: HTMLElement,\n update: () => void,\n): () => void {\n return autoUpdate(reference, floating, update, {\n elementResize: typeof ResizeObserver !== 'undefined',\n layoutShift: typeof IntersectionObserver !== 'undefined',\n });\n}\n\n/**\n * Writes the available space and the anchor width as CSS variables on the surface:\n * `--wave-popup-available-width`, `--wave-popup-available-height`, `--wave-popup-reference-width`.\n * It captures no options (floating-ui compares middleware functions by source text).\n */\nconst sizeApply: NonNullable<SizeOptions['apply']> = ({\n availableWidth,\n availableHeight,\n rects,\n elements,\n}) => {\n const style = elements.floating.style;\n style.setProperty('--wave-popup-available-width', `${Math.max(0, Math.floor(availableWidth))}px`);\n style.setProperty(\n '--wave-popup-available-height',\n `${Math.max(0, Math.floor(availableHeight))}px`,\n );\n style.setProperty('--wave-popup-reference-width', `${Math.round(rects.reference.width)}px`);\n};\n\n/**\n * Positions a popup surface next to its anchor with `@floating-ui/react-dom` (Tooltip, Popover,\n * Menu, listboxes, the DatePicker calendar, TeachingPopover): `offset`, `flip`, `shift`, optional\n * `size` (reference width / available space as CSS variables) and `arrow`. `flip`, `shift` and\n * `size` keep the same viewport padding, taken from the `shift` option (8px by default). Logical\n * `start`/`end` sides follow `useDirection()` (the WaveProvider `dir`, else the document\n * direction), and so does start/end alignment.\n *\n * Positions update only while `open` (`autoUpdate` on scroll/resize; element resizes and layout\n * shifts only where `ResizeObserver`/`IntersectionObserver` exist). Spread `floatingProps` onto the\n * surface: `data-side`/`data-align` expose the final placement for styling (C-CLASS).\n *\n * The anchor is an element or a `VirtualElement` (a rectangle such as the pointer position; a new\n * object on every render is fine); give it a `contextElement` so scrolling its containers updates\n * the position. A virtual anchor reaches floating-ui through one stable proxy that reads the latest\n * object: a new object only recomputes the position (which renders only when the position\n * changes), and a new proxy is made only when the `contextElement` changes.\n *\n * Destructure the result: `eslint-plugin-react-hooks` (`react-hooks/refs`) treats an object\n * whose member is passed to a `ref` prop as a ref, and then rejects reading its other members\n * during render.\n *\n * @example\n * const { setReference, setFloating, floatingProps } = usePopupPosition({ open, side: 'bottom' });\n * <button ref={setReference} />\n * {open && <Portal><div ref={setFloating} {...floatingProps} /></Portal>}\n */\nexport function usePopupPosition(options: UsePopupPositionOptions): UsePopupPositionResult {\n const {\n open,\n side = 'bottom',\n align = 'start',\n offset = 4,\n flip = true,\n shift = { padding: 8 },\n matchReferenceWidth = false,\n fitViewport = false,\n arrowRef,\n strategy = 'fixed',\n } = options;\n const dir = useDirection();\n const physicalSide = resolveSide(side, dir);\n const requestedPlacement = (\n align === 'center' ? physicalSide : `${physicalSide}-${align}`\n ) as Placement;\n const padding = shift === false ? 0 : shift === true ? 8 : shift.padding;\n const shiftCrossAxis = typeof shift === 'object' && shift.crossAxis === true;\n\n const middleware: Middleware[] = [offsetMiddleware(offset)];\n if (flip) middleware.push(flipMiddleware({ padding }));\n if (shift !== false) {\n middleware.push(shiftMiddleware({ padding, crossAxis: shiftCrossAxis }));\n }\n if (matchReferenceWidth || fitViewport) {\n middleware.push(sizeMiddleware({ padding, apply: sizeApply }));\n }\n if (arrowRef) middleware.push(arrowMiddleware({ element: arrowRef }));\n\n // Alignment follows the hook's direction (not only the surface's computed CSS direction).\n const directionalPlatform = useMemo<Platform>(\n () => ({ ...platform, isRTL: () => dir === 'rtl' }),\n [dir],\n );\n\n const { refs, floatingStyles, placement, middlewareData, isPositioned, update } = useFloating({\n open,\n placement: requestedPlacement,\n strategy,\n middleware,\n platform: directionalPlatform,\n whileElementsMounted: open ? whileElementsMounted : undefined,\n });\n\n // Virtual anchors: the latest object (written in setReference, never during render) and the\n // proxy floating-ui holds for it.\n const virtualRef = useRef<VirtualElement | null>(null);\n const proxyRef = useRef<VirtualProxy | null>(null);\n const updatePosition = useEventCallback(update);\n const { setReference: setFloatingReference, reference: floatingReference } = refs;\n const setReference = useCallback(\n (anchor: HTMLElement | VirtualElement | null) => {\n if (anchor === null || isElementAnchor(anchor)) {\n virtualRef.current = null;\n setFloatingReference(anchor);\n return;\n }\n virtualRef.current = anchor;\n let proxy = proxyRef.current;\n if (!proxy || proxy.contextElement !== anchor.contextElement) {\n proxy = createVirtualProxy(virtualRef, anchor.contextElement);\n proxyRef.current = proxy;\n }\n if (floatingReference.current === proxy) updatePosition();\n else setFloatingReference(proxy);\n },\n [setFloatingReference, floatingReference, updatePosition],\n );\n\n const [finalSide, finalAlign = 'center'] = placement.split('-') as [PhysicalSide, string?];\n\n const style = useMemo<React.CSSProperties>(() => {\n const next: React.CSSProperties = { ...floatingStyles };\n if (matchReferenceWidth) next.width = 'var(--wave-popup-reference-width)';\n if (fitViewport) {\n next.maxWidth = 'var(--wave-popup-available-width)';\n next.maxHeight = 'var(--wave-popup-available-height)';\n }\n return next;\n }, [floatingStyles, matchReferenceWidth, fitViewport]);\n\n const arrowX = middlewareData.arrow?.x;\n const arrowY = middlewareData.arrow?.y;\n const arrowStyles = useMemo<React.CSSProperties>(() => {\n const styles: Record<string, string> = {\n position: 'absolute',\n left: arrowX != null ? `${arrowX}px` : '',\n top: arrowY != null ? `${arrowY}px` : '',\n };\n styles[STATIC_SIDE[finalSide]] = ARROW_OFFSET;\n return styles as React.CSSProperties;\n }, [arrowX, arrowY, finalSide]);\n\n const floatingProps = useMemo(\n () => ({ 'data-side': finalSide, 'data-align': finalAlign, style }),\n [finalSide, finalAlign, style],\n );\n\n return {\n setReference,\n setFloating: refs.setFloating,\n floatingStyles: style,\n arrowStyles,\n placement,\n side: finalSide,\n isPositioned,\n floatingProps,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"usePopupPosition.cjs","names":["autoUpdate","useDirection","offsetMiddleware","flipMiddleware","shiftMiddleware","sizeMiddleware","hideMiddleware","arrowMiddleware","useMemo","platform","useFloating","useRef","useEventCallback","useCallback","require_useEventCallback","require_useDirection","react","_floating_ui_react_dom","resolvePositioning","positioning","defaults","STATIC_SIDE","ARROW_OFFSET","resolveSide","side","dir","EMPTY_RECT","isElementAnchor","anchor","createVirtualProxy","latest","whileElementsMounted","reference","floating","update","sizeApply","style","elements","availableWidth","availableHeight","rects","usePopupPosition","options","physicalSide","toPlacement","placementAlign","placementSide","requestedPlacement","align","padding","shift","shiftCrossAxis","middleware","offset","flip","fallbackPlacements","placement","matchReferenceWidth","fitViewport","hideWhenDetached","arrowRef","directionalPlatform","open","virtualRef","proxyRef","updatePosition","updateWhileOpen","setFloatingReference","floatingReference","refs","setReference","proxy","finalSide","finalAlign","reportPositioned","onPositioned","info","isPositioned","referenceHidden","middlewareData","next","floatingStyles","arrowX","arrowY","arrowStyles","styles","floatingProps"],"sources":["../../src/hooks/usePopupPosition.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef } from 'react';\nimport type * as React from 'react';\nimport {\n arrow as arrowMiddleware,\n autoUpdate,\n flip as flipMiddleware,\n hide as hideMiddleware,\n offset as offsetMiddleware,\n platform,\n shift as shiftMiddleware,\n size as sizeMiddleware,\n useFloating,\n type Middleware,\n type Placement,\n type Platform,\n type ReferenceType,\n type SizeOptions,\n} from '@floating-ui/react-dom';\nimport type {\n PopupAlign,\n PopupPlacement,\n PopupPositioning,\n PopupRect,\n PopupSide,\n VirtualElement,\n} from '../lib/types';\nimport type { Direction } from '../lib/direction';\nimport { useDirection } from './useDirection';\nimport { useEventCallback } from './useEventCallback';\n\n/** Options of {@link usePopupPosition}. */\nexport interface UsePopupPositionOptions {\n /** Whether the popup is open; positions are kept up to date only while `true`. */\n open: boolean;\n /** Side of the anchor. `start`/`end` follow the writing direction. @default 'bottom' */\n side?: PopupSide;\n /** Alignment along that side. @default 'start' */\n align?: PopupAlign;\n /** Distance from the anchor in px. @default 4 (tooltips use 8) */\n offset?: number;\n /** Flip to the opposite side when there is not enough room. @default true */\n flip?: boolean;\n /**\n * Shift along the side to stay inside the viewport. Its padding is also the viewport padding of\n * `flip` and of `size` (`fitViewport`, `matchReferenceWidth`): `true` means 8px, and `false`\n * lets the popup flip only once it touches the viewport edge and grow right up to it. With\n * `crossAxis: true` it also shifts across the side, over the anchor, when neither side has room\n * after `flip` (submenus), and `fitViewport` then offers the whole viewport in that direction.\n * @default { padding: 8 }\n */\n shift?: boolean | { padding: number; crossAxis?: boolean };\n /** Make the surface as wide as the anchor (listboxes). @default false */\n matchReferenceWidth?: boolean;\n /** Limit the surface to the available space (`max-width`/`max-height`). @default false */\n fitViewport?: boolean;\n /** An arrow (beak) element inside the surface, positioned through `arrowStyles`. */\n arrowRef?: React.RefObject<HTMLElement | null>;\n /** CSS positioning strategy. @default 'fixed' */\n strategy?: 'absolute' | 'fixed';\n /**\n * Placements to try, in order, when the requested side has no room, instead of only the opposite\n * side. Ignored with `flip: false`. An omitted `align` is the hook's own `align`.\n */\n fallbackPlacements?: readonly PopupPlacement[];\n /**\n * Hide the surface (`visibility: hidden`: its layout is unaffected, but its content cannot take\n * focus, so a surface that holds focus loses it) while the anchor is scrolled out of view or\n * clipped away. @default false\n */\n hideWhenDetached?: boolean;\n /**\n * Called after the surface is positioned, and again whenever the final placement changes (a flip,\n * a fallback): not on every scroll or resize recompute. It runs from an effect, never during\n * render, and not while closed.\n */\n onPositioned?: (info: PositionedInfo) => void;\n}\n\n/** What {@link UsePopupPositionOptions.onPositioned} receives. */\nexport interface PositionedInfo {\n /** Final placement after flipping, e.g. `'top-start'`. */\n placement: string;\n /** Final physical side after flipping. */\n side: 'top' | 'bottom' | 'left' | 'right';\n /** Always `true`: the callback runs once a position exists. */\n isPositioned: boolean;\n}\n\n/** Returned by {@link usePopupPosition}. */\nexport interface UsePopupPositionResult {\n /**\n * Sets the anchor: a ref callback for the anchor (trigger) element, or a call from a layout\n * effect or an event. The anchor is an element or a `VirtualElement` (a rectangle such as the\n * pointer position; a new object on every render is fine); give it a `contextElement` so\n * scrolling its containers updates the position.\n */\n setReference(el: HTMLElement | VirtualElement | null): void;\n /** Ref callback for the popup surface. */\n setFloating(el: HTMLElement | null): void;\n /** Position styles for the surface (also in `floatingProps.style`). */\n floatingStyles: React.CSSProperties;\n /** Position styles for the arrow element. */\n arrowStyles: React.CSSProperties;\n /** Final placement after flipping, e.g. `'top-start'`. */\n placement: string;\n /** Final physical side after flipping. */\n side: 'top' | 'bottom' | 'left' | 'right';\n /** Whether a position has been computed since the popup opened. */\n isPositioned: boolean;\n /** Recomputes the position now, for a change floating-ui cannot observe. A no-op while closed. */\n update(): void;\n /** Spread onto the surface: `data-side`, `data-align` and the position style. */\n floatingProps: { 'data-side': string; 'data-align': string; style: React.CSSProperties };\n}\n\ntype PhysicalSide = 'top' | 'bottom' | 'left' | 'right';\n\n/**\n * Internal. The options a component's `positioning` prop sets on top of the component's own\n * defaults: a value the consumer gave (also `0` or `false`) wins, `undefined` keeps the default.\n */\nexport function resolvePositioning(\n positioning: PopupPositioning | undefined,\n defaults: { offset?: number; matchReferenceWidth?: boolean; fitViewport?: boolean },\n): Pick<\n UsePopupPositionOptions,\n 'offset' | 'matchReferenceWidth' | 'fitViewport' | 'fallbackPlacements'\n> {\n return {\n offset: positioning?.offset ?? defaults.offset,\n matchReferenceWidth: positioning?.matchReferenceWidth ?? defaults.matchReferenceWidth,\n fitViewport: positioning?.fitViewport ?? defaults.fitViewport,\n fallbackPlacements: positioning?.fallbackPlacements,\n };\n}\n\nconst STATIC_SIDE: Record<PhysicalSide, PhysicalSide> = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n};\n\n/** Half the arrow size, so the arrow sticks out of the surface towards the anchor. */\nconst ARROW_OFFSET = 'calc(var(--wave-popup-arrow-size, 8px) / -2)';\n\nfunction resolveSide(side: PopupSide, dir: Direction): PhysicalSide {\n if (side === 'start') return dir === 'rtl' ? 'right' : 'left';\n if (side === 'end') return dir === 'rtl' ? 'left' : 'right';\n return side;\n}\n\n/** The rectangle of a virtual anchor that is not set (never reached while one is the reference). */\nconst EMPTY_RECT: PopupRect = {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n};\n\n/** Whether an anchor is a DOM element (duck typed, so an element of another realm counts). */\nfunction isElementAnchor(anchor: HTMLElement | VirtualElement): anchor is HTMLElement {\n return typeof (anchor as Partial<Node>).nodeType === 'number';\n}\n\n/**\n * The stable stand-in floating-ui gets for virtual anchors: it reads the latest `VirtualElement`\n * from `latest`, so a new virtual object sets no floating-ui state. One proxy per\n * `contextElement`, so `autoUpdate` observes the scroll containers of the current one.\n */\ninterface VirtualProxy extends VirtualElement {\n contextElement: Element | undefined;\n}\n\nfunction createVirtualProxy(\n latest: React.RefObject<VirtualElement | null>,\n contextElement: Element | undefined,\n): VirtualProxy {\n return {\n getBoundingClientRect: () => latest.current?.getBoundingClientRect() ?? EMPTY_RECT,\n contextElement,\n };\n}\n\n/** Keeps the position updated while mounted; observers are used only where they exist. */\nfunction whileElementsMounted(\n reference: ReferenceType,\n floating: HTMLElement,\n update: () => void,\n): () => void {\n return autoUpdate(reference, floating, update, {\n elementResize: typeof ResizeObserver !== 'undefined',\n layoutShift: typeof IntersectionObserver !== 'undefined',\n });\n}\n\n/**\n * Writes the available space and the anchor width as CSS variables on the surface:\n * `--wave-popup-available-width`, `--wave-popup-available-height`, `--wave-popup-reference-width`.\n * It captures no options (floating-ui compares middleware functions by source text).\n */\nconst sizeApply: NonNullable<SizeOptions['apply']> = ({\n availableWidth,\n availableHeight,\n rects,\n elements,\n}) => {\n const style = elements.floating.style;\n style.setProperty('--wave-popup-available-width', `${Math.max(0, Math.floor(availableWidth))}px`);\n style.setProperty(\n '--wave-popup-available-height',\n `${Math.max(0, Math.floor(availableHeight))}px`,\n );\n style.setProperty('--wave-popup-reference-width', `${Math.round(rects.reference.width)}px`);\n};\n\n/**\n * Positions a popup surface next to its anchor with `@floating-ui/react-dom` (Tooltip, Popover,\n * Menu, listboxes, the DatePicker calendar, TeachingPopover): `offset`, `flip`, `shift`, optional\n * `size` (reference width / available space as CSS variables) and `arrow`. `flip`, `shift` and\n * `size` keep the same viewport padding, taken from the `shift` option (8px by default). Logical\n * `start`/`end` sides follow `useDirection()` (the WaveProvider `dir`, else the document\n * direction), and so does start/end alignment.\n *\n * Positions update only while `open` (`autoUpdate` on scroll/resize; element resizes and layout\n * shifts only where `ResizeObserver`/`IntersectionObserver` exist). Spread `floatingProps` onto the\n * surface: `data-side`/`data-align` expose the final placement for styling (C-CLASS).\n *\n * The anchor is an element or a `VirtualElement` (a rectangle such as the pointer position; a new\n * object on every render is fine); give it a `contextElement` so scrolling its containers updates\n * the position. A virtual anchor reaches floating-ui through one stable proxy that reads the latest\n * object: a new object only recomputes the position (which renders only when the position\n * changes), and a new proxy is made only when the `contextElement` changes.\n *\n * Destructure the result: `eslint-plugin-react-hooks` (`react-hooks/refs`) treats an object\n * whose member is passed to a `ref` prop as a ref, and then rejects reading its other members\n * during render.\n *\n * @example\n * const { setReference, setFloating, floatingProps } = usePopupPosition({ open, side: 'bottom' });\n * <button ref={setReference} />\n * {open && <Portal><div ref={setFloating} {...floatingProps} /></Portal>}\n */\nexport function usePopupPosition(options: UsePopupPositionOptions): UsePopupPositionResult {\n const {\n open,\n side = 'bottom',\n align = 'start',\n offset = 4,\n flip = true,\n shift = { padding: 8 },\n matchReferenceWidth = false,\n fitViewport = false,\n arrowRef,\n strategy = 'fixed',\n fallbackPlacements,\n hideWhenDetached = false,\n onPositioned,\n } = options;\n const dir = useDirection();\n const physicalSide = resolveSide(side, dir);\n const toPlacement = (placementSide: PhysicalSide, placementAlign: PopupAlign) =>\n (placementAlign === 'center'\n ? placementSide\n : `${placementSide}-${placementAlign}`) as Placement;\n const requestedPlacement = toPlacement(physicalSide, align);\n const padding = shift === false ? 0 : shift === true ? 8 : shift.padding;\n const shiftCrossAxis = typeof shift === 'object' && shift.crossAxis === true;\n\n const middleware: Middleware[] = [offsetMiddleware(offset)];\n if (flip) {\n middleware.push(\n flipMiddleware({\n padding,\n ...(fallbackPlacements && {\n fallbackPlacements: fallbackPlacements.map((placement) =>\n toPlacement(resolveSide(placement.side, dir), placement.align ?? align),\n ),\n }),\n }),\n );\n }\n if (shift !== false) {\n middleware.push(shiftMiddleware({ padding, crossAxis: shiftCrossAxis }));\n }\n if (matchReferenceWidth || fitViewport) {\n middleware.push(sizeMiddleware({ padding, apply: sizeApply }));\n }\n if (hideWhenDetached) middleware.push(hideMiddleware({ strategy: 'referenceHidden' }));\n if (arrowRef) middleware.push(arrowMiddleware({ element: arrowRef }));\n\n // Alignment follows the hook's direction (not only the surface's computed CSS direction).\n const directionalPlatform = useMemo<Platform>(\n () => ({ ...platform, isRTL: () => dir === 'rtl' }),\n [dir],\n );\n\n const { refs, floatingStyles, placement, middlewareData, isPositioned, update } = useFloating({\n open,\n placement: requestedPlacement,\n strategy,\n middleware,\n platform: directionalPlatform,\n whileElementsMounted: open ? whileElementsMounted : undefined,\n });\n\n // Virtual anchors: the latest object (written in setReference, never during render) and the\n // proxy floating-ui holds for it.\n const virtualRef = useRef<VirtualElement | null>(null);\n const proxyRef = useRef<VirtualProxy | null>(null);\n const updatePosition = useEventCallback(update);\n const updateWhileOpen = useEventCallback(() => {\n if (open) update();\n });\n const { setReference: setFloatingReference, reference: floatingReference } = refs;\n const setReference = useCallback(\n (anchor: HTMLElement | VirtualElement | null) => {\n if (anchor === null || isElementAnchor(anchor)) {\n virtualRef.current = null;\n setFloatingReference(anchor);\n return;\n }\n virtualRef.current = anchor;\n let proxy = proxyRef.current;\n if (!proxy || proxy.contextElement !== anchor.contextElement) {\n proxy = createVirtualProxy(virtualRef, anchor.contextElement);\n proxyRef.current = proxy;\n }\n if (floatingReference.current === proxy) updatePosition();\n else setFloatingReference(proxy);\n },\n [setFloatingReference, floatingReference, updatePosition],\n );\n\n const [finalSide, finalAlign = 'center'] = placement.split('-') as [PhysicalSide, string?];\n\n const reportPositioned = useEventCallback((info: PositionedInfo) => onPositioned?.(info));\n useEffect(() => {\n if (open && isPositioned) {\n reportPositioned({ placement, side: finalSide, isPositioned: true });\n }\n }, [open, isPositioned, placement, finalSide, reportPositioned]);\n\n const referenceHidden = middlewareData.hide?.referenceHidden === true;\n const style = useMemo<React.CSSProperties>(() => {\n const next: React.CSSProperties = { ...floatingStyles };\n if (matchReferenceWidth) next.width = 'var(--wave-popup-reference-width)';\n if (fitViewport) {\n next.maxWidth = 'var(--wave-popup-available-width)';\n next.maxHeight = 'var(--wave-popup-available-height)';\n }\n if (referenceHidden) next.visibility = 'hidden';\n return next;\n }, [floatingStyles, matchReferenceWidth, fitViewport, referenceHidden]);\n\n const arrowX = middlewareData.arrow?.x;\n const arrowY = middlewareData.arrow?.y;\n const arrowStyles = useMemo<React.CSSProperties>(() => {\n const styles: Record<string, string> = {\n position: 'absolute',\n left: arrowX != null ? `${arrowX}px` : '',\n top: arrowY != null ? `${arrowY}px` : '',\n };\n styles[STATIC_SIDE[finalSide]] = ARROW_OFFSET;\n return styles as React.CSSProperties;\n }, [arrowX, arrowY, finalSide]);\n\n const floatingProps = useMemo(\n () => ({ 'data-side': finalSide, 'data-align': finalAlign, style }),\n [finalSide, finalAlign, style],\n );\n\n return {\n setReference,\n setFloating: refs.setFloating,\n floatingStyles: style,\n arrowStyles,\n placement,\n side: finalSide,\n isPositioned,\n update: updateWhileOpen,\n floatingProps,\n };\n}\n"],"mappings":"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"}
@@ -1,9 +1,17 @@
1
1
  "use client";
2
2
  import { useEventCallback as e } from "./useEventCallback.mjs";
3
3
  import { useDirection as t } from "./useDirection.mjs";
4
- import { useCallback as n, useMemo as r, useRef as i } from "react";
5
- import { arrow as a, autoUpdate as o, flip as s, offset as c, platform as l, shift as u, size as d, useFloating as f } from "@floating-ui/react-dom";
4
+ import { useCallback as n, useEffect as r, useMemo as i, useRef as a } from "react";
5
+ import { arrow as o, autoUpdate as s, flip as ee, hide as te, offset as ne, platform as c, shift as l, size as u, useFloating as d } from "@floating-ui/react-dom";
6
6
  //#region src/hooks/usePopupPosition.ts
7
+ function f(e, t) {
8
+ return {
9
+ offset: e?.offset ?? t.offset,
10
+ matchReferenceWidth: e?.matchReferenceWidth ?? t.matchReferenceWidth,
11
+ fitViewport: e?.fitViewport ?? t.fitViewport,
12
+ fallbackPlacements: e?.fallbackPlacements
13
+ };
14
+ }
7
15
  var p = {
8
16
  top: "bottom",
9
17
  bottom: "top",
@@ -26,92 +34,113 @@ var g = {
26
34
  function _(e) {
27
35
  return typeof e.nodeType == "number";
28
36
  }
29
- function v(e, t) {
37
+ function re(e, t) {
30
38
  return {
31
39
  getBoundingClientRect: () => e.current?.getBoundingClientRect() ?? g,
32
40
  contextElement: t
33
41
  };
34
42
  }
35
- function y(e, t, n) {
36
- return o(e, t, n, {
43
+ function ie(e, t, n) {
44
+ return s(e, t, n, {
37
45
  elementResize: typeof ResizeObserver < "u",
38
46
  layoutShift: typeof IntersectionObserver < "u"
39
47
  });
40
48
  }
41
- var b = ({ availableWidth: e, availableHeight: t, rects: n, elements: r }) => {
49
+ var ae = ({ availableWidth: e, availableHeight: t, rects: n, elements: r }) => {
42
50
  let i = r.floating.style;
43
51
  i.setProperty("--wave-popup-available-width", `${Math.max(0, Math.floor(e))}px`), i.setProperty("--wave-popup-available-height", `${Math.max(0, Math.floor(t))}px`), i.setProperty("--wave-popup-reference-width", `${Math.round(n.reference.width)}px`);
44
52
  };
45
- function x(o) {
46
- let { open: g, side: x = "bottom", align: S = "start", offset: C = 4, flip: w = !0, shift: T = { padding: 8 }, matchReferenceWidth: E = !1, fitViewport: D = !1, arrowRef: O, strategy: k = "fixed" } = o, A = t(), j = h(x, A), M = S === "center" ? j : `${j}-${S}`, N = T === !1 ? 0 : T === !0 ? 8 : T.padding, P = typeof T == "object" && T.crossAxis === !0, F = [c(C)];
47
- w && F.push(s({ padding: N })), T !== !1 && F.push(u({
48
- padding: N,
49
- crossAxis: P
50
- })), (E || D) && F.push(d({
51
- padding: N,
52
- apply: b
53
- })), O && F.push(a({ element: O }));
54
- let I = r(() => ({
55
- ...l,
56
- isRTL: () => A === "rtl"
57
- }), [A]), { refs: L, floatingStyles: R, placement: z, middlewareData: B, isPositioned: V, update: H } = f({
58
- open: g,
59
- placement: M,
60
- strategy: k,
61
- middleware: F,
62
- platform: I,
63
- whileElementsMounted: g ? y : void 0
64
- }), U = i(null), W = i(null), G = e(H), { setReference: K, reference: q } = L, J = n((e) => {
53
+ function v(s) {
54
+ let { open: f, side: g = "bottom", align: v = "start", offset: y = 4, flip: b = !0, shift: x = { padding: 8 }, matchReferenceWidth: S = !1, fitViewport: C = !1, arrowRef: w, strategy: T = "fixed", fallbackPlacements: E, hideWhenDetached: D = !1, onPositioned: O } = s, k = t(), oe = h(g, k), A = (e, t) => t === "center" ? e : `${e}-${t}`, se = A(oe, v), j = x === !1 ? 0 : x === !0 ? 8 : x.padding, M = typeof x == "object" && x.crossAxis === !0, N = [ne(y)];
55
+ b && N.push(ee({
56
+ padding: j,
57
+ ...E && { fallbackPlacements: E.map((e) => A(h(e.side, k), e.align ?? v)) }
58
+ })), x !== !1 && N.push(l({
59
+ padding: j,
60
+ crossAxis: M
61
+ })), (S || C) && N.push(u({
62
+ padding: j,
63
+ apply: ae
64
+ })), D && N.push(te({ strategy: "referenceHidden" })), w && N.push(o({ element: w }));
65
+ let P = i(() => ({
66
+ ...c,
67
+ isRTL: () => k === "rtl"
68
+ }), [k]), { refs: F, floatingStyles: I, placement: L, middlewareData: R, isPositioned: z, update: B } = d({
69
+ open: f,
70
+ placement: se,
71
+ strategy: T,
72
+ middleware: N,
73
+ platform: P,
74
+ whileElementsMounted: f ? ie : void 0
75
+ }), V = a(null), H = a(null), U = e(B), W = e(() => {
76
+ f && B();
77
+ }), { setReference: G, reference: K } = F, ce = n((e) => {
65
78
  if (e === null || _(e)) {
66
- U.current = null, K(e);
79
+ V.current = null, G(e);
67
80
  return;
68
81
  }
69
- U.current = e;
70
- let t = W.current;
71
- (!t || t.contextElement !== e.contextElement) && (t = v(U, e.contextElement), W.current = t), q.current === t ? G() : K(t);
82
+ V.current = e;
83
+ let t = H.current;
84
+ (!t || t.contextElement !== e.contextElement) && (t = re(V, e.contextElement), H.current = t), K.current === t ? U() : G(t);
72
85
  }, [
86
+ G,
73
87
  K,
88
+ U
89
+ ]), [q, J = "center"] = L.split("-"), Y = e((e) => O?.(e));
90
+ r(() => {
91
+ f && z && Y({
92
+ placement: L,
93
+ side: q,
94
+ isPositioned: !0
95
+ });
96
+ }, [
97
+ f,
98
+ z,
99
+ L,
74
100
  q,
75
- G
76
- ]), [Y, X = "center"] = z.split("-"), Z = r(() => {
77
- let e = { ...R };
78
- return E && (e.width = "var(--wave-popup-reference-width)"), D && (e.maxWidth = "var(--wave-popup-available-width)", e.maxHeight = "var(--wave-popup-available-height)"), e;
101
+ Y
102
+ ]);
103
+ let X = R.hide?.referenceHidden === !0, Z = i(() => {
104
+ let e = { ...I };
105
+ return S && (e.width = "var(--wave-popup-reference-width)"), C && (e.maxWidth = "var(--wave-popup-available-width)", e.maxHeight = "var(--wave-popup-available-height)"), X && (e.visibility = "hidden"), e;
79
106
  }, [
80
- R,
81
- E,
82
- D
83
- ]), Q = B.arrow?.x, $ = B.arrow?.y, ee = r(() => {
107
+ I,
108
+ S,
109
+ C,
110
+ X
111
+ ]), Q = R.arrow?.x, $ = R.arrow?.y, le = i(() => {
84
112
  let e = {
85
113
  position: "absolute",
86
114
  left: Q == null ? "" : `${Q}px`,
87
115
  top: $ == null ? "" : `${$}px`
88
116
  };
89
- return e[p[Y]] = m, e;
117
+ return e[p[q]] = m, e;
90
118
  }, [
91
119
  Q,
92
120
  $,
93
- Y
94
- ]), te = r(() => ({
95
- "data-side": Y,
96
- "data-align": X,
121
+ q
122
+ ]), ue = i(() => ({
123
+ "data-side": q,
124
+ "data-align": J,
97
125
  style: Z
98
126
  }), [
99
- Y,
100
- X,
127
+ q,
128
+ J,
101
129
  Z
102
130
  ]);
103
131
  return {
104
- setReference: J,
105
- setFloating: L.setFloating,
132
+ setReference: ce,
133
+ setFloating: F.setFloating,
106
134
  floatingStyles: Z,
107
- arrowStyles: ee,
108
- placement: z,
109
- side: Y,
110
- isPositioned: V,
111
- floatingProps: te
135
+ arrowStyles: le,
136
+ placement: L,
137
+ side: q,
138
+ isPositioned: z,
139
+ update: W,
140
+ floatingProps: ue
112
141
  };
113
142
  }
114
143
  //#endregion
115
- export { x as usePopupPosition };
144
+ export { f as resolvePositioning, v as usePopupPosition };
116
145
 
117
146
  //# sourceMappingURL=usePopupPosition.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"usePopupPosition.mjs","names":["shift","offsetMiddleware","offset","flip","flipMiddleware","shiftMiddleware","sizeMiddleware","arrowMiddleware","STATIC_SIDE","ARROW_OFFSET","resolveSide","side","dir","EMPTY_RECT","isElementAnchor","anchor","createVirtualProxy","latest","whileElementsMounted","autoUpdate","reference","floating","update","sizeApply","style","elements","availableWidth","availableHeight","rects","usePopupPosition","offset$1","flip$1","shift$1","options","useDirection","physicalSide","requestedPlacement","align","padding","shiftCrossAxis","middleware","offset","flip","shift","matchReferenceWidth","fitViewport","size","arrowRef","arrow","directionalPlatform","useMemo","platform","useFloating","open","virtualRef","useRef","proxyRef","updatePosition","useEventCallback","setFloatingReference","floatingReference","refs","setReference","useCallback","proxy","finalSide","finalAlign","placement","next","floatingStyles","arrowX","middlewareData","arrowY","arrowStyles","styles","floatingProps"],"sources":["../../src/hooks/usePopupPosition.ts"],"sourcesContent":["import { useCallback, useMemo, useRef } from 'react';\nimport type * as React from 'react';\nimport {\n arrow as arrowMiddleware,\n autoUpdate,\n flip as flipMiddleware,\n offset as offsetMiddleware,\n platform,\n shift as shiftMiddleware,\n size as sizeMiddleware,\n useFloating,\n type Middleware,\n type Placement,\n type Platform,\n type ReferenceType,\n type SizeOptions,\n} from '@floating-ui/react-dom';\nimport type { PopupAlign, PopupRect, PopupSide, VirtualElement } from '../lib/types';\nimport type { Direction } from '../lib/direction';\nimport { useDirection } from './useDirection';\nimport { useEventCallback } from './useEventCallback';\n\n/** Options of {@link usePopupPosition}. */\nexport interface UsePopupPositionOptions {\n /** Whether the popup is open; positions are kept up to date only while `true`. */\n open: boolean;\n /** Side of the anchor. `start`/`end` follow the writing direction. @default 'bottom' */\n side?: PopupSide;\n /** Alignment along that side. @default 'start' */\n align?: PopupAlign;\n /** Distance from the anchor in px. @default 4 (tooltips use 8) */\n offset?: number;\n /** Flip to the opposite side when there is not enough room. @default true */\n flip?: boolean;\n /**\n * Shift along the side to stay inside the viewport. Its padding is also the viewport padding of\n * `flip` and of `size` (`fitViewport`, `matchReferenceWidth`): `true` means 8px, and `false`\n * lets the popup flip only once it touches the viewport edge and grow right up to it. With\n * `crossAxis: true` it also shifts across the side, over the anchor, when neither side has room\n * after `flip` (submenus), and `fitViewport` then offers the whole viewport in that direction.\n * @default { padding: 8 }\n */\n shift?: boolean | { padding: number; crossAxis?: boolean };\n /** Make the surface as wide as the anchor (listboxes). @default false */\n matchReferenceWidth?: boolean;\n /** Limit the surface to the available space (`max-width`/`max-height`). @default false */\n fitViewport?: boolean;\n /** An arrow (beak) element inside the surface, positioned through `arrowStyles`. */\n arrowRef?: React.RefObject<HTMLElement | null>;\n /** CSS positioning strategy. @default 'fixed' */\n strategy?: 'absolute' | 'fixed';\n}\n\n/** Returned by {@link usePopupPosition}. */\nexport interface UsePopupPositionResult {\n /**\n * Sets the anchor: a ref callback for the anchor (trigger) element, or a call from a layout\n * effect or an event. The anchor is an element or a `VirtualElement` (a rectangle such as the\n * pointer position; a new object on every render is fine); give it a `contextElement` so\n * scrolling its containers updates the position.\n */\n setReference(el: HTMLElement | VirtualElement | null): void;\n /** Ref callback for the popup surface. */\n setFloating(el: HTMLElement | null): void;\n /** Position styles for the surface (also in `floatingProps.style`). */\n floatingStyles: React.CSSProperties;\n /** Position styles for the arrow element. */\n arrowStyles: React.CSSProperties;\n /** Final placement after flipping, e.g. `'top-start'`. */\n placement: string;\n /** Final physical side after flipping. */\n side: 'top' | 'bottom' | 'left' | 'right';\n /** Whether a position has been computed since the popup opened. */\n isPositioned: boolean;\n /** Spread onto the surface: `data-side`, `data-align` and the position style. */\n floatingProps: { 'data-side': string; 'data-align': string; style: React.CSSProperties };\n}\n\ntype PhysicalSide = 'top' | 'bottom' | 'left' | 'right';\n\nconst STATIC_SIDE: Record<PhysicalSide, PhysicalSide> = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n};\n\n/** Half the arrow size, so the arrow sticks out of the surface towards the anchor. */\nconst ARROW_OFFSET = 'calc(var(--wave-popup-arrow-size, 8px) / -2)';\n\nfunction resolveSide(side: PopupSide, dir: Direction): PhysicalSide {\n if (side === 'start') return dir === 'rtl' ? 'right' : 'left';\n if (side === 'end') return dir === 'rtl' ? 'left' : 'right';\n return side;\n}\n\n/** The rectangle of a virtual anchor that is not set (never reached while one is the reference). */\nconst EMPTY_RECT: PopupRect = {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n};\n\n/** Whether an anchor is a DOM element (duck typed, so an element of another realm counts). */\nfunction isElementAnchor(anchor: HTMLElement | VirtualElement): anchor is HTMLElement {\n return typeof (anchor as Partial<Node>).nodeType === 'number';\n}\n\n/**\n * The stable stand-in floating-ui gets for virtual anchors: it reads the latest `VirtualElement`\n * from `latest`, so a new virtual object sets no floating-ui state. One proxy per\n * `contextElement`, so `autoUpdate` observes the scroll containers of the current one.\n */\ninterface VirtualProxy extends VirtualElement {\n contextElement: Element | undefined;\n}\n\nfunction createVirtualProxy(\n latest: React.RefObject<VirtualElement | null>,\n contextElement: Element | undefined,\n): VirtualProxy {\n return {\n getBoundingClientRect: () => latest.current?.getBoundingClientRect() ?? EMPTY_RECT,\n contextElement,\n };\n}\n\n/** Keeps the position updated while mounted; observers are used only where they exist. */\nfunction whileElementsMounted(\n reference: ReferenceType,\n floating: HTMLElement,\n update: () => void,\n): () => void {\n return autoUpdate(reference, floating, update, {\n elementResize: typeof ResizeObserver !== 'undefined',\n layoutShift: typeof IntersectionObserver !== 'undefined',\n });\n}\n\n/**\n * Writes the available space and the anchor width as CSS variables on the surface:\n * `--wave-popup-available-width`, `--wave-popup-available-height`, `--wave-popup-reference-width`.\n * It captures no options (floating-ui compares middleware functions by source text).\n */\nconst sizeApply: NonNullable<SizeOptions['apply']> = ({\n availableWidth,\n availableHeight,\n rects,\n elements,\n}) => {\n const style = elements.floating.style;\n style.setProperty('--wave-popup-available-width', `${Math.max(0, Math.floor(availableWidth))}px`);\n style.setProperty(\n '--wave-popup-available-height',\n `${Math.max(0, Math.floor(availableHeight))}px`,\n );\n style.setProperty('--wave-popup-reference-width', `${Math.round(rects.reference.width)}px`);\n};\n\n/**\n * Positions a popup surface next to its anchor with `@floating-ui/react-dom` (Tooltip, Popover,\n * Menu, listboxes, the DatePicker calendar, TeachingPopover): `offset`, `flip`, `shift`, optional\n * `size` (reference width / available space as CSS variables) and `arrow`. `flip`, `shift` and\n * `size` keep the same viewport padding, taken from the `shift` option (8px by default). Logical\n * `start`/`end` sides follow `useDirection()` (the WaveProvider `dir`, else the document\n * direction), and so does start/end alignment.\n *\n * Positions update only while `open` (`autoUpdate` on scroll/resize; element resizes and layout\n * shifts only where `ResizeObserver`/`IntersectionObserver` exist). Spread `floatingProps` onto the\n * surface: `data-side`/`data-align` expose the final placement for styling (C-CLASS).\n *\n * The anchor is an element or a `VirtualElement` (a rectangle such as the pointer position; a new\n * object on every render is fine); give it a `contextElement` so scrolling its containers updates\n * the position. A virtual anchor reaches floating-ui through one stable proxy that reads the latest\n * object: a new object only recomputes the position (which renders only when the position\n * changes), and a new proxy is made only when the `contextElement` changes.\n *\n * Destructure the result: `eslint-plugin-react-hooks` (`react-hooks/refs`) treats an object\n * whose member is passed to a `ref` prop as a ref, and then rejects reading its other members\n * during render.\n *\n * @example\n * const { setReference, setFloating, floatingProps } = usePopupPosition({ open, side: 'bottom' });\n * <button ref={setReference} />\n * {open && <Portal><div ref={setFloating} {...floatingProps} /></Portal>}\n */\nexport function usePopupPosition(options: UsePopupPositionOptions): UsePopupPositionResult {\n const {\n open,\n side = 'bottom',\n align = 'start',\n offset = 4,\n flip = true,\n shift = { padding: 8 },\n matchReferenceWidth = false,\n fitViewport = false,\n arrowRef,\n strategy = 'fixed',\n } = options;\n const dir = useDirection();\n const physicalSide = resolveSide(side, dir);\n const requestedPlacement = (\n align === 'center' ? physicalSide : `${physicalSide}-${align}`\n ) as Placement;\n const padding = shift === false ? 0 : shift === true ? 8 : shift.padding;\n const shiftCrossAxis = typeof shift === 'object' && shift.crossAxis === true;\n\n const middleware: Middleware[] = [offsetMiddleware(offset)];\n if (flip) middleware.push(flipMiddleware({ padding }));\n if (shift !== false) {\n middleware.push(shiftMiddleware({ padding, crossAxis: shiftCrossAxis }));\n }\n if (matchReferenceWidth || fitViewport) {\n middleware.push(sizeMiddleware({ padding, apply: sizeApply }));\n }\n if (arrowRef) middleware.push(arrowMiddleware({ element: arrowRef }));\n\n // Alignment follows the hook's direction (not only the surface's computed CSS direction).\n const directionalPlatform = useMemo<Platform>(\n () => ({ ...platform, isRTL: () => dir === 'rtl' }),\n [dir],\n );\n\n const { refs, floatingStyles, placement, middlewareData, isPositioned, update } = useFloating({\n open,\n placement: requestedPlacement,\n strategy,\n middleware,\n platform: directionalPlatform,\n whileElementsMounted: open ? whileElementsMounted : undefined,\n });\n\n // Virtual anchors: the latest object (written in setReference, never during render) and the\n // proxy floating-ui holds for it.\n const virtualRef = useRef<VirtualElement | null>(null);\n const proxyRef = useRef<VirtualProxy | null>(null);\n const updatePosition = useEventCallback(update);\n const { setReference: setFloatingReference, reference: floatingReference } = refs;\n const setReference = useCallback(\n (anchor: HTMLElement | VirtualElement | null) => {\n if (anchor === null || isElementAnchor(anchor)) {\n virtualRef.current = null;\n setFloatingReference(anchor);\n return;\n }\n virtualRef.current = anchor;\n let proxy = proxyRef.current;\n if (!proxy || proxy.contextElement !== anchor.contextElement) {\n proxy = createVirtualProxy(virtualRef, anchor.contextElement);\n proxyRef.current = proxy;\n }\n if (floatingReference.current === proxy) updatePosition();\n else setFloatingReference(proxy);\n },\n [setFloatingReference, floatingReference, updatePosition],\n );\n\n const [finalSide, finalAlign = 'center'] = placement.split('-') as [PhysicalSide, string?];\n\n const style = useMemo<React.CSSProperties>(() => {\n const next: React.CSSProperties = { ...floatingStyles };\n if (matchReferenceWidth) next.width = 'var(--wave-popup-reference-width)';\n if (fitViewport) {\n next.maxWidth = 'var(--wave-popup-available-width)';\n next.maxHeight = 'var(--wave-popup-available-height)';\n }\n return next;\n }, [floatingStyles, matchReferenceWidth, fitViewport]);\n\n const arrowX = middlewareData.arrow?.x;\n const arrowY = middlewareData.arrow?.y;\n const arrowStyles = useMemo<React.CSSProperties>(() => {\n const styles: Record<string, string> = {\n position: 'absolute',\n left: arrowX != null ? `${arrowX}px` : '',\n top: arrowY != null ? `${arrowY}px` : '',\n };\n styles[STATIC_SIDE[finalSide]] = ARROW_OFFSET;\n return styles as React.CSSProperties;\n }, [arrowX, arrowY, finalSide]);\n\n const floatingProps = useMemo(\n () => ({ 'data-side': finalSide, 'data-align': finalAlign, style }),\n [finalSide, finalAlign, style],\n );\n\n return {\n setReference,\n setFloating: refs.setFloating,\n floatingStyles: style,\n arrowStyles,\n placement,\n side: finalSide,\n isPositioned,\n floatingProps,\n };\n}\n"],"mappings":";;;;;;AAgFA,IAAMQ,IAAkD;CACtD,KAAK;CACL,QAAQ;CACR,MAAM;CACN,OAAO;AACT,GAGMC,IAAe;AAErB,SAASC,EAAY,GAAiB,GAA8B;CAGlE,OAFIC,MAAS,UAAgBC,MAAQ,QAAQ,UAAU,SACnDD,MAAS,QAAcC,MAAQ,QAAQ,SAAS,UAC7CD;AACT;AAGA,IAAME,IAAwB;CAC5B,GAAG;CACH,GAAG;CACH,OAAO;CACP,QAAQ;CACR,KAAK;CACL,OAAO;CACP,QAAQ;CACR,MAAM;AACR;AAGA,SAASC,EAAgB,GAA6D;CACpF,OAAO,OAAQC,EAAyB,YAAa;AACvD;AAWA,SAASC,EACP,GACA,GACc;CACd,OAAO;EACL,6BAA6BC,EAAO,SAAS,sBAAsB,KAAKJ;EACxE;CACF;AACF;AAGA,SAASK,EACP,GACA,GACA,GACY;CACZ,OAAOC,EAAWC,GAAWC,GAAUC,GAAQ;EAC7C,eAAe,OAAO,iBAAmB;EACzC,aAAa,OAAO,uBAAyB;CAC/C,CAAC;AACH;AAOA,IAAMC,KAAgD,EACpD,mBACA,oBACA,UACA,kBACI;CACJ,IAAMC,IAAQC,EAAS,SAAS;CAMhC,AALAD,EAAM,YAAY,gCAAgC,GAAG,KAAK,IAAI,GAAG,KAAK,MAAME,CAAc,CAAC,EAAE,GAAG,GAChGF,EAAM,YACJ,iCACA,GAAG,KAAK,IAAI,GAAG,KAAK,MAAMG,CAAe,CAAC,EAAE,GAC9C,GACAH,EAAM,YAAY,gCAAgC,GAAG,KAAK,MAAMI,EAAM,UAAU,KAAK,EAAE,GAAG;AAC5F;AA6BA,SAAgBC,EAAiB,GAA0D;CACzF,IAAM,EACJ,SACA,UAAO,UACP,WAAQ,SACR,QAAAC,IAAS,GACT,MAAAC,IAAO,IACP,OAAAC,IAAQ,EAAE,SAAS,EAAE,GACrB,yBAAsB,IACtB,iBAAc,IACd,aACA,cAAW,YACTC,GACErB,IAAMsB,EAAa,GACnBC,IAAezB,EAAYC,GAAMC,CAAG,GACpCwB,IACJC,MAAU,WAAWF,IAAe,GAAGA,EAAa,GAAGE,KAEnDC,IAAUtC,MAAU,KAAQ,IAAIA,MAAU,KAAO,IAAIA,EAAM,SAC3DuC,IAAiB,OAAOvC,KAAU,YAAYA,EAAM,cAAc,IAElEwC,IAA2B,CAACvC,EAAiBC,CAAM,CAAC;CAQ1D,AAPIC,KAAMqC,EAAW,KAAKpC,EAAe,EAAE,WAAQ,CAAC,CAAC,GACjDJ,MAAU,MACZwC,EAAW,KAAKnC,EAAgB;EAAE;EAAS,WAAWkC;CAAe,CAAC,CAAC,IAErEK,KAAuBC,MACzBL,EAAW,KAAKlC,EAAe;EAAE;EAAS,OAAOiB;CAAU,CAAC,CAAC,GAE3DwB,KAAUP,EAAW,KAAKjC,EAAgB,EAAE,SAASwC,EAAS,CAAC,CAAC;CAGpE,IAAME,IAAsBC,SACnB;EAAE,GAAGC;EAAU,aAAavC,MAAQ;CAAM,IACjD,CAACA,CAAG,CACN,GAEM,EAAE,SAAM,mBAAgB,cAAW,mBAAgB,iBAAc,cAAWwC,EAAY;EAC5F;EACA,WAAWhB;EACX;EACA;EACA,UAAUa;EACV,sBAAsBI,IAAOnC,IAAuB,KAAA;CACtD,CAAC,GAIKoC,IAAaC,EAA8B,IAAI,GAC/CC,IAAWD,EAA4B,IAAI,GAC3CE,IAAiBC,EAAiBpC,CAAM,GACxC,EAAE,cAAcqC,GAAsB,WAAWC,MAAsBC,GACvEC,IAAeC,GAClB,MAAgD;EAC/C,IAAIhD,MAAW,QAAQD,EAAgBC,CAAM,GAAG;GAE9C,AADA,EAAW,UAAU,MACrB4C,EAAqB5C,CAAM;GAC3B;EACF;EACA,EAAW,UAAUA;EACrB,IAAIiD,IAAQR,EAAS;EAKrB,CAJI,CAACQ,KAASA,EAAM,mBAAmBjD,EAAO,oBAC5C,IAAQC,EAAmBsC,GAAYvC,EAAO,cAAc,GAC5D,EAAS,UAAUiD,IAEjBJ,EAAkB,YAAYI,IAAOP,EAAe,IACnDE,EAAqBK,CAAK;CACjC,GACA;EAACL;EAAsBC;EAAmBH;CAAc,CAC1D,GAEM,CAACQ,GAAWC,IAAa,YAAYC,EAAU,MAAM,GAAG,GAExD3C,IAAQ0B,QAAmC;EAC/C,IAAMkB,IAA4B,EAAE,GAAGC,EAAe;EAMtD,OALIzB,MAAqB,EAAK,QAAQ,sCAClCC,MACF,EAAK,WAAW,qCAChB,EAAK,YAAY,uCAEZuB;CACT,GAAG;EAACC;EAAgBzB;EAAqBC;CAAW,CAAC,GAE/CyB,IAASC,EAAe,OAAO,GAC/BC,IAASD,EAAe,OAAO,GAC/BE,KAAcvB,QAAmC;EACrD,IAAMwB,IAAiC;GACrC,UAAU;GACV,MAAMJ,KAAU,OAAuB,KAAhB,GAAGA,EAAO;GACjC,KAAKE,KAAU,OAAuB,KAAhB,GAAGA,EAAO;EAClC;EAEA,OADA,EAAOhE,EAAYyD,MAAcxD,GAC1BiE;CACT,GAAG;EAACJ;EAAQE;EAAQP;CAAS,CAAC,GAExBU,KAAgBzB,SACb;EAAE,aAAae;EAAW,cAAcC;EAAY;CAAM,IACjE;EAACD;EAAWC;EAAY1C;CAAK,CAC/B;CAEA,OAAO;EACL;EACA,aAAaqC,EAAK;EAClB,gBAAgBrC;EAChB;EACA;EACA,MAAMyC;EACN;EACA;CACF;AACF"}
1
+ {"version":3,"file":"usePopupPosition.mjs","names":["shift","offsetMiddleware","offset","flip","flipMiddleware","shiftMiddleware","sizeMiddleware","hideMiddleware","arrowMiddleware","resolvePositioning","positioning","defaults","STATIC_SIDE","ARROW_OFFSET","resolveSide","side","dir","EMPTY_RECT","isElementAnchor","anchor","createVirtualProxy","latest","whileElementsMounted","autoUpdate","reference","floating","update","sizeApply","style","elements","availableWidth","availableHeight","rects","usePopupPosition","offset$1","flip$1","shift$1","options","useDirection","physicalSide","toPlacement","placementAlign","placementSide","requestedPlacement","align","padding","shiftCrossAxis","middleware","offset","flip","fallbackPlacements","placement","shift","matchReferenceWidth","fitViewport","size","hideWhenDetached","hide","arrowRef","arrow","directionalPlatform","useMemo","platform","useFloating","open","virtualRef","useRef","proxyRef","updatePosition","useEventCallback","updateWhileOpen","setFloatingReference","floatingReference","refs","setReference","useCallback","proxy","finalSide","finalAlign","reportPositioned","onPositioned","info","isPositioned","referenceHidden","middlewareData","next","floatingStyles","arrowX","arrowY","arrowStyles","styles","floatingProps"],"sources":["../../src/hooks/usePopupPosition.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef } from 'react';\nimport type * as React from 'react';\nimport {\n arrow as arrowMiddleware,\n autoUpdate,\n flip as flipMiddleware,\n hide as hideMiddleware,\n offset as offsetMiddleware,\n platform,\n shift as shiftMiddleware,\n size as sizeMiddleware,\n useFloating,\n type Middleware,\n type Placement,\n type Platform,\n type ReferenceType,\n type SizeOptions,\n} from '@floating-ui/react-dom';\nimport type {\n PopupAlign,\n PopupPlacement,\n PopupPositioning,\n PopupRect,\n PopupSide,\n VirtualElement,\n} from '../lib/types';\nimport type { Direction } from '../lib/direction';\nimport { useDirection } from './useDirection';\nimport { useEventCallback } from './useEventCallback';\n\n/** Options of {@link usePopupPosition}. */\nexport interface UsePopupPositionOptions {\n /** Whether the popup is open; positions are kept up to date only while `true`. */\n open: boolean;\n /** Side of the anchor. `start`/`end` follow the writing direction. @default 'bottom' */\n side?: PopupSide;\n /** Alignment along that side. @default 'start' */\n align?: PopupAlign;\n /** Distance from the anchor in px. @default 4 (tooltips use 8) */\n offset?: number;\n /** Flip to the opposite side when there is not enough room. @default true */\n flip?: boolean;\n /**\n * Shift along the side to stay inside the viewport. Its padding is also the viewport padding of\n * `flip` and of `size` (`fitViewport`, `matchReferenceWidth`): `true` means 8px, and `false`\n * lets the popup flip only once it touches the viewport edge and grow right up to it. With\n * `crossAxis: true` it also shifts across the side, over the anchor, when neither side has room\n * after `flip` (submenus), and `fitViewport` then offers the whole viewport in that direction.\n * @default { padding: 8 }\n */\n shift?: boolean | { padding: number; crossAxis?: boolean };\n /** Make the surface as wide as the anchor (listboxes). @default false */\n matchReferenceWidth?: boolean;\n /** Limit the surface to the available space (`max-width`/`max-height`). @default false */\n fitViewport?: boolean;\n /** An arrow (beak) element inside the surface, positioned through `arrowStyles`. */\n arrowRef?: React.RefObject<HTMLElement | null>;\n /** CSS positioning strategy. @default 'fixed' */\n strategy?: 'absolute' | 'fixed';\n /**\n * Placements to try, in order, when the requested side has no room, instead of only the opposite\n * side. Ignored with `flip: false`. An omitted `align` is the hook's own `align`.\n */\n fallbackPlacements?: readonly PopupPlacement[];\n /**\n * Hide the surface (`visibility: hidden`: its layout is unaffected, but its content cannot take\n * focus, so a surface that holds focus loses it) while the anchor is scrolled out of view or\n * clipped away. @default false\n */\n hideWhenDetached?: boolean;\n /**\n * Called after the surface is positioned, and again whenever the final placement changes (a flip,\n * a fallback): not on every scroll or resize recompute. It runs from an effect, never during\n * render, and not while closed.\n */\n onPositioned?: (info: PositionedInfo) => void;\n}\n\n/** What {@link UsePopupPositionOptions.onPositioned} receives. */\nexport interface PositionedInfo {\n /** Final placement after flipping, e.g. `'top-start'`. */\n placement: string;\n /** Final physical side after flipping. */\n side: 'top' | 'bottom' | 'left' | 'right';\n /** Always `true`: the callback runs once a position exists. */\n isPositioned: boolean;\n}\n\n/** Returned by {@link usePopupPosition}. */\nexport interface UsePopupPositionResult {\n /**\n * Sets the anchor: a ref callback for the anchor (trigger) element, or a call from a layout\n * effect or an event. The anchor is an element or a `VirtualElement` (a rectangle such as the\n * pointer position; a new object on every render is fine); give it a `contextElement` so\n * scrolling its containers updates the position.\n */\n setReference(el: HTMLElement | VirtualElement | null): void;\n /** Ref callback for the popup surface. */\n setFloating(el: HTMLElement | null): void;\n /** Position styles for the surface (also in `floatingProps.style`). */\n floatingStyles: React.CSSProperties;\n /** Position styles for the arrow element. */\n arrowStyles: React.CSSProperties;\n /** Final placement after flipping, e.g. `'top-start'`. */\n placement: string;\n /** Final physical side after flipping. */\n side: 'top' | 'bottom' | 'left' | 'right';\n /** Whether a position has been computed since the popup opened. */\n isPositioned: boolean;\n /** Recomputes the position now, for a change floating-ui cannot observe. A no-op while closed. */\n update(): void;\n /** Spread onto the surface: `data-side`, `data-align` and the position style. */\n floatingProps: { 'data-side': string; 'data-align': string; style: React.CSSProperties };\n}\n\ntype PhysicalSide = 'top' | 'bottom' | 'left' | 'right';\n\n/**\n * Internal. The options a component's `positioning` prop sets on top of the component's own\n * defaults: a value the consumer gave (also `0` or `false`) wins, `undefined` keeps the default.\n */\nexport function resolvePositioning(\n positioning: PopupPositioning | undefined,\n defaults: { offset?: number; matchReferenceWidth?: boolean; fitViewport?: boolean },\n): Pick<\n UsePopupPositionOptions,\n 'offset' | 'matchReferenceWidth' | 'fitViewport' | 'fallbackPlacements'\n> {\n return {\n offset: positioning?.offset ?? defaults.offset,\n matchReferenceWidth: positioning?.matchReferenceWidth ?? defaults.matchReferenceWidth,\n fitViewport: positioning?.fitViewport ?? defaults.fitViewport,\n fallbackPlacements: positioning?.fallbackPlacements,\n };\n}\n\nconst STATIC_SIDE: Record<PhysicalSide, PhysicalSide> = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n};\n\n/** Half the arrow size, so the arrow sticks out of the surface towards the anchor. */\nconst ARROW_OFFSET = 'calc(var(--wave-popup-arrow-size, 8px) / -2)';\n\nfunction resolveSide(side: PopupSide, dir: Direction): PhysicalSide {\n if (side === 'start') return dir === 'rtl' ? 'right' : 'left';\n if (side === 'end') return dir === 'rtl' ? 'left' : 'right';\n return side;\n}\n\n/** The rectangle of a virtual anchor that is not set (never reached while one is the reference). */\nconst EMPTY_RECT: PopupRect = {\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n};\n\n/** Whether an anchor is a DOM element (duck typed, so an element of another realm counts). */\nfunction isElementAnchor(anchor: HTMLElement | VirtualElement): anchor is HTMLElement {\n return typeof (anchor as Partial<Node>).nodeType === 'number';\n}\n\n/**\n * The stable stand-in floating-ui gets for virtual anchors: it reads the latest `VirtualElement`\n * from `latest`, so a new virtual object sets no floating-ui state. One proxy per\n * `contextElement`, so `autoUpdate` observes the scroll containers of the current one.\n */\ninterface VirtualProxy extends VirtualElement {\n contextElement: Element | undefined;\n}\n\nfunction createVirtualProxy(\n latest: React.RefObject<VirtualElement | null>,\n contextElement: Element | undefined,\n): VirtualProxy {\n return {\n getBoundingClientRect: () => latest.current?.getBoundingClientRect() ?? EMPTY_RECT,\n contextElement,\n };\n}\n\n/** Keeps the position updated while mounted; observers are used only where they exist. */\nfunction whileElementsMounted(\n reference: ReferenceType,\n floating: HTMLElement,\n update: () => void,\n): () => void {\n return autoUpdate(reference, floating, update, {\n elementResize: typeof ResizeObserver !== 'undefined',\n layoutShift: typeof IntersectionObserver !== 'undefined',\n });\n}\n\n/**\n * Writes the available space and the anchor width as CSS variables on the surface:\n * `--wave-popup-available-width`, `--wave-popup-available-height`, `--wave-popup-reference-width`.\n * It captures no options (floating-ui compares middleware functions by source text).\n */\nconst sizeApply: NonNullable<SizeOptions['apply']> = ({\n availableWidth,\n availableHeight,\n rects,\n elements,\n}) => {\n const style = elements.floating.style;\n style.setProperty('--wave-popup-available-width', `${Math.max(0, Math.floor(availableWidth))}px`);\n style.setProperty(\n '--wave-popup-available-height',\n `${Math.max(0, Math.floor(availableHeight))}px`,\n );\n style.setProperty('--wave-popup-reference-width', `${Math.round(rects.reference.width)}px`);\n};\n\n/**\n * Positions a popup surface next to its anchor with `@floating-ui/react-dom` (Tooltip, Popover,\n * Menu, listboxes, the DatePicker calendar, TeachingPopover): `offset`, `flip`, `shift`, optional\n * `size` (reference width / available space as CSS variables) and `arrow`. `flip`, `shift` and\n * `size` keep the same viewport padding, taken from the `shift` option (8px by default). Logical\n * `start`/`end` sides follow `useDirection()` (the WaveProvider `dir`, else the document\n * direction), and so does start/end alignment.\n *\n * Positions update only while `open` (`autoUpdate` on scroll/resize; element resizes and layout\n * shifts only where `ResizeObserver`/`IntersectionObserver` exist). Spread `floatingProps` onto the\n * surface: `data-side`/`data-align` expose the final placement for styling (C-CLASS).\n *\n * The anchor is an element or a `VirtualElement` (a rectangle such as the pointer position; a new\n * object on every render is fine); give it a `contextElement` so scrolling its containers updates\n * the position. A virtual anchor reaches floating-ui through one stable proxy that reads the latest\n * object: a new object only recomputes the position (which renders only when the position\n * changes), and a new proxy is made only when the `contextElement` changes.\n *\n * Destructure the result: `eslint-plugin-react-hooks` (`react-hooks/refs`) treats an object\n * whose member is passed to a `ref` prop as a ref, and then rejects reading its other members\n * during render.\n *\n * @example\n * const { setReference, setFloating, floatingProps } = usePopupPosition({ open, side: 'bottom' });\n * <button ref={setReference} />\n * {open && <Portal><div ref={setFloating} {...floatingProps} /></Portal>}\n */\nexport function usePopupPosition(options: UsePopupPositionOptions): UsePopupPositionResult {\n const {\n open,\n side = 'bottom',\n align = 'start',\n offset = 4,\n flip = true,\n shift = { padding: 8 },\n matchReferenceWidth = false,\n fitViewport = false,\n arrowRef,\n strategy = 'fixed',\n fallbackPlacements,\n hideWhenDetached = false,\n onPositioned,\n } = options;\n const dir = useDirection();\n const physicalSide = resolveSide(side, dir);\n const toPlacement = (placementSide: PhysicalSide, placementAlign: PopupAlign) =>\n (placementAlign === 'center'\n ? placementSide\n : `${placementSide}-${placementAlign}`) as Placement;\n const requestedPlacement = toPlacement(physicalSide, align);\n const padding = shift === false ? 0 : shift === true ? 8 : shift.padding;\n const shiftCrossAxis = typeof shift === 'object' && shift.crossAxis === true;\n\n const middleware: Middleware[] = [offsetMiddleware(offset)];\n if (flip) {\n middleware.push(\n flipMiddleware({\n padding,\n ...(fallbackPlacements && {\n fallbackPlacements: fallbackPlacements.map((placement) =>\n toPlacement(resolveSide(placement.side, dir), placement.align ?? align),\n ),\n }),\n }),\n );\n }\n if (shift !== false) {\n middleware.push(shiftMiddleware({ padding, crossAxis: shiftCrossAxis }));\n }\n if (matchReferenceWidth || fitViewport) {\n middleware.push(sizeMiddleware({ padding, apply: sizeApply }));\n }\n if (hideWhenDetached) middleware.push(hideMiddleware({ strategy: 'referenceHidden' }));\n if (arrowRef) middleware.push(arrowMiddleware({ element: arrowRef }));\n\n // Alignment follows the hook's direction (not only the surface's computed CSS direction).\n const directionalPlatform = useMemo<Platform>(\n () => ({ ...platform, isRTL: () => dir === 'rtl' }),\n [dir],\n );\n\n const { refs, floatingStyles, placement, middlewareData, isPositioned, update } = useFloating({\n open,\n placement: requestedPlacement,\n strategy,\n middleware,\n platform: directionalPlatform,\n whileElementsMounted: open ? whileElementsMounted : undefined,\n });\n\n // Virtual anchors: the latest object (written in setReference, never during render) and the\n // proxy floating-ui holds for it.\n const virtualRef = useRef<VirtualElement | null>(null);\n const proxyRef = useRef<VirtualProxy | null>(null);\n const updatePosition = useEventCallback(update);\n const updateWhileOpen = useEventCallback(() => {\n if (open) update();\n });\n const { setReference: setFloatingReference, reference: floatingReference } = refs;\n const setReference = useCallback(\n (anchor: HTMLElement | VirtualElement | null) => {\n if (anchor === null || isElementAnchor(anchor)) {\n virtualRef.current = null;\n setFloatingReference(anchor);\n return;\n }\n virtualRef.current = anchor;\n let proxy = proxyRef.current;\n if (!proxy || proxy.contextElement !== anchor.contextElement) {\n proxy = createVirtualProxy(virtualRef, anchor.contextElement);\n proxyRef.current = proxy;\n }\n if (floatingReference.current === proxy) updatePosition();\n else setFloatingReference(proxy);\n },\n [setFloatingReference, floatingReference, updatePosition],\n );\n\n const [finalSide, finalAlign = 'center'] = placement.split('-') as [PhysicalSide, string?];\n\n const reportPositioned = useEventCallback((info: PositionedInfo) => onPositioned?.(info));\n useEffect(() => {\n if (open && isPositioned) {\n reportPositioned({ placement, side: finalSide, isPositioned: true });\n }\n }, [open, isPositioned, placement, finalSide, reportPositioned]);\n\n const referenceHidden = middlewareData.hide?.referenceHidden === true;\n const style = useMemo<React.CSSProperties>(() => {\n const next: React.CSSProperties = { ...floatingStyles };\n if (matchReferenceWidth) next.width = 'var(--wave-popup-reference-width)';\n if (fitViewport) {\n next.maxWidth = 'var(--wave-popup-available-width)';\n next.maxHeight = 'var(--wave-popup-available-height)';\n }\n if (referenceHidden) next.visibility = 'hidden';\n return next;\n }, [floatingStyles, matchReferenceWidth, fitViewport, referenceHidden]);\n\n const arrowX = middlewareData.arrow?.x;\n const arrowY = middlewareData.arrow?.y;\n const arrowStyles = useMemo<React.CSSProperties>(() => {\n const styles: Record<string, string> = {\n position: 'absolute',\n left: arrowX != null ? `${arrowX}px` : '',\n top: arrowY != null ? `${arrowY}px` : '',\n };\n styles[STATIC_SIDE[finalSide]] = ARROW_OFFSET;\n return styles as React.CSSProperties;\n }, [arrowX, arrowY, finalSide]);\n\n const floatingProps = useMemo(\n () => ({ 'data-side': finalSide, 'data-align': finalAlign, style }),\n [finalSide, finalAlign, style],\n );\n\n return {\n setReference,\n setFloating: refs.setFloating,\n floatingStyles: style,\n arrowStyles,\n placement,\n side: finalSide,\n isPositioned,\n update: updateWhileOpen,\n floatingProps,\n };\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ "use client";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("../lib/dev.cjs");let n=require("react");n=e.__toESM(n,1);function r(e){return typeof e==`object`&&!!e&&`current`in e}function i(e){let[t,i]=n.useState(null),a=r(e)?e:null;return n.useLayoutEffect(()=>{if(!a)return;let e=!1;return queueMicrotask(()=>{e||i(a.current)}),()=>{e=!0}}),r(e)?t:e}function a(e,r,i){let a=r!=null&&i!=null;n.useEffect(()=>{a&&t.warnOnce(`${e}:positioning-target`,`${e}: both target and positioning.target are set; target is used.`)},[e,a])}function o(e,t,n){a(e,t,n?.target);let o=t===void 0?n?.target:t,s=i(o);return{target:s,pending:r(o)&&s===null}}exports.isTargetRef=r,exports.usePopupTarget=o,exports.useResolvedTarget=i,exports.useTargetConflictWarning=a;
2
+ //# sourceMappingURL=useResolvedTarget.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useResolvedTarget.cjs","names":["React","require_runtime","require_dev","react","isTargetRef","target","useResolvedTarget","refElement","setRefElement","ref","cancelled","useTargetConflictWarning","conflict","positioningTarget","component","usePopupTarget","positioning","input","resolved"],"sources":["../../src/hooks/useResolvedTarget.ts"],"sourcesContent":["import * as React from 'react';\nimport type { PopupPositioning, PopupTarget, PopupTargetInput } from '../lib/types';\nimport { warnOnce } from '../lib/dev';\n\n/** Whether a `target` is a ref object rather than an element or a `VirtualElement`. */\nexport function isTargetRef(\n target: PopupTargetInput | undefined,\n): target is { readonly current: HTMLElement | null } {\n return target != null && typeof target === 'object' && 'current' in target;\n}\n\n/**\n * Internal. Turns a popup's `target` prop into a {@link PopupTarget}: an element or a\n * `VirtualElement` is returned as it is, a ref object becomes the element it holds. A ref cannot be\n * observed, so it is read once more after each commit of the popup (deferred, C-HOOKS): until then\n * the previous element is kept, and an element that attaches or detaches without the popup\n * rendering is noticed the next time the popup renders.\n */\nexport function useResolvedTarget(target: PopupTargetInput | undefined): PopupTarget | undefined {\n const [refElement, setRefElement] = React.useState<HTMLElement | null>(null);\n const ref = isTargetRef(target) ? target : null;\n React.useLayoutEffect(() => {\n if (!ref) return;\n let cancelled = false;\n queueMicrotask(() => {\n if (!cancelled) setRefElement(ref.current);\n });\n return () => {\n cancelled = true;\n };\n });\n if (isTargetRef(target)) return refElement;\n return target;\n}\n\n/**\n * Internal. Warns once (from an effect) when both `target` and `positioning.target` are set; the\n * component uses `target`.\n */\nexport function useTargetConflictWarning(\n component: string,\n target: PopupTargetInput | undefined,\n positioningTarget: PopupTargetInput | undefined,\n): void {\n const conflict = target != null && positioningTarget != null;\n React.useEffect(() => {\n if (conflict) {\n warnOnce(\n `${component}:positioning-target`,\n `${component}: both target and positioning.target are set; target is used.`,\n );\n }\n }, [component, conflict]);\n}\n\n/**\n * Internal. The `target` of a popup that also takes `positioning`: the `target` prop, else\n * `positioning.target`, resolved by {@link useResolvedTarget}. `pending` is `true` while the target\n * is a ref object that holds no element: the popup is rendered hidden, as TeachingPopover's is.\n */\nexport function usePopupTarget(\n component: string,\n target: PopupTargetInput | undefined,\n positioning: PopupPositioning | undefined,\n): { target: PopupTarget | undefined; pending: boolean } {\n useTargetConflictWarning(component, target, positioning?.target);\n const input = target !== undefined ? target : positioning?.target;\n const resolved = useResolvedTarget(input);\n // A ref object whose element is not there (yet): the popup stays hidden until it is.\n const pending = isTargetRef(input) && resolved === null;\n return { target: resolved, pending };\n}\n"],"mappings":"sIAKA,SAAgBI,EACd,EACoD,CACpD,OAAyB,OAAOC,GAAW,YAApCA,GAAgD,YAAaA,CACtE,CASA,SAAgBC,EAAkB,EAA+D,CAC/F,GAAM,CAACC,EAAYC,GAAiBR,EAAM,SAA6B,IAAI,EACrES,EAAML,EAAYC,CAAM,EAAIA,EAAS,KAY3C,OAXAF,EAAM,oBAAsB,CAC1B,GAAI,CAACM,EAAK,OACV,IAAIC,EAAY,GAIhB,OAHA,mBAAqB,CACdA,GAAWF,EAAcC,EAAI,OAAO,CAC3C,CAAC,MACY,CACX,EAAY,EACd,CACF,CAAC,EACGL,EAAYC,CAAM,EAAUE,EACzBF,CACT,CAMA,SAAgBM,EACd,EACA,EACA,EACM,CACN,IAAMC,EAAWP,GAAU,MAAQQ,GAAqB,KACxDV,EAAM,cAAgB,CAChBS,GACFV,EAAA,SACE,GAAGY,EAAU,qBACb,GAAGA,EAAU,8DACf,CAEJ,EAAG,CAACA,EAAWF,CAAQ,CAAC,CAC1B,CAOA,SAAgBG,EACd,EACA,EACA,EACuD,CACvDJ,EAAyBG,EAAWT,EAAQW,GAAa,MAAM,EAC/D,IAAMC,EAAQZ,IAAW,IAAA,GAAqBW,GAAa,OAAtBX,EAC/Ba,EAAWZ,EAAkBW,CAAK,EAGxC,MAAO,CAAE,OAAQC,EAAU,QADXd,EAAYa,CAAK,GAAKC,IAAa,IAChB,CACrC"}
@@ -0,0 +1,37 @@
1
+ "use client";
2
+ import { warnOnce as e } from "../lib/dev.mjs";
3
+ import * as t from "react";
4
+ //#region src/hooks/useResolvedTarget.ts
5
+ function n(e) {
6
+ return typeof e == "object" && !!e && "current" in e;
7
+ }
8
+ function r(e) {
9
+ let [r, i] = t.useState(null), a = n(e) ? e : null;
10
+ return t.useLayoutEffect(() => {
11
+ if (!a) return;
12
+ let e = !1;
13
+ return queueMicrotask(() => {
14
+ e || i(a.current);
15
+ }), () => {
16
+ e = !0;
17
+ };
18
+ }), n(e) ? r : e;
19
+ }
20
+ function i(n, r, i) {
21
+ let a = r != null && i != null;
22
+ t.useEffect(() => {
23
+ a && e(`${n}:positioning-target`, `${n}: both target and positioning.target are set; target is used.`);
24
+ }, [n, a]);
25
+ }
26
+ function a(e, t, a) {
27
+ i(e, t, a?.target);
28
+ let o = t === void 0 ? a?.target : t, s = r(o);
29
+ return {
30
+ target: s,
31
+ pending: n(o) && s === null
32
+ };
33
+ }
34
+ //#endregion
35
+ export { n as isTargetRef, a as usePopupTarget, r as useResolvedTarget, i as useTargetConflictWarning };
36
+
37
+ //# sourceMappingURL=useResolvedTarget.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useResolvedTarget.mjs","names":["React","isTargetRef","target","useResolvedTarget","refElement","setRefElement","ref","cancelled","useTargetConflictWarning","conflict","positioningTarget","warnOnce","component","usePopupTarget","positioning","input","resolved"],"sources":["../../src/hooks/useResolvedTarget.ts"],"sourcesContent":["import * as React from 'react';\nimport type { PopupPositioning, PopupTarget, PopupTargetInput } from '../lib/types';\nimport { warnOnce } from '../lib/dev';\n\n/** Whether a `target` is a ref object rather than an element or a `VirtualElement`. */\nexport function isTargetRef(\n target: PopupTargetInput | undefined,\n): target is { readonly current: HTMLElement | null } {\n return target != null && typeof target === 'object' && 'current' in target;\n}\n\n/**\n * Internal. Turns a popup's `target` prop into a {@link PopupTarget}: an element or a\n * `VirtualElement` is returned as it is, a ref object becomes the element it holds. A ref cannot be\n * observed, so it is read once more after each commit of the popup (deferred, C-HOOKS): until then\n * the previous element is kept, and an element that attaches or detaches without the popup\n * rendering is noticed the next time the popup renders.\n */\nexport function useResolvedTarget(target: PopupTargetInput | undefined): PopupTarget | undefined {\n const [refElement, setRefElement] = React.useState<HTMLElement | null>(null);\n const ref = isTargetRef(target) ? target : null;\n React.useLayoutEffect(() => {\n if (!ref) return;\n let cancelled = false;\n queueMicrotask(() => {\n if (!cancelled) setRefElement(ref.current);\n });\n return () => {\n cancelled = true;\n };\n });\n if (isTargetRef(target)) return refElement;\n return target;\n}\n\n/**\n * Internal. Warns once (from an effect) when both `target` and `positioning.target` are set; the\n * component uses `target`.\n */\nexport function useTargetConflictWarning(\n component: string,\n target: PopupTargetInput | undefined,\n positioningTarget: PopupTargetInput | undefined,\n): void {\n const conflict = target != null && positioningTarget != null;\n React.useEffect(() => {\n if (conflict) {\n warnOnce(\n `${component}:positioning-target`,\n `${component}: both target and positioning.target are set; target is used.`,\n );\n }\n }, [component, conflict]);\n}\n\n/**\n * Internal. The `target` of a popup that also takes `positioning`: the `target` prop, else\n * `positioning.target`, resolved by {@link useResolvedTarget}. `pending` is `true` while the target\n * is a ref object that holds no element: the popup is rendered hidden, as TeachingPopover's is.\n */\nexport function usePopupTarget(\n component: string,\n target: PopupTargetInput | undefined,\n positioning: PopupPositioning | undefined,\n): { target: PopupTarget | undefined; pending: boolean } {\n useTargetConflictWarning(component, target, positioning?.target);\n const input = target !== undefined ? target : positioning?.target;\n const resolved = useResolvedTarget(input);\n // A ref object whose element is not there (yet): the popup stays hidden until it is.\n const pending = isTargetRef(input) && resolved === null;\n return { target: resolved, pending };\n}\n"],"mappings":";;;;AAKA,SAAgBC,EACd,GACoD;CACpD,OAAyB,OAAOC,KAAW,cAApCA,KAAgD,aAAaA;AACtE;AASA,SAAgBC,EAAkB,GAA+D;CAC/F,IAAM,CAACC,GAAYC,KAAiBL,EAAM,SAA6B,IAAI,GACrEM,IAAML,EAAYC,CAAM,IAAIA,IAAS;CAY3C,OAXAF,EAAM,sBAAsB;EAC1B,IAAI,CAACM,GAAK;EACV,IAAIC,IAAY;EAIhB,OAHA,qBAAqB;GACnB,AAAKA,KAAWF,EAAcC,EAAI,OAAO;EAC3C,CAAC,SACY;GACX,IAAY;EACd;CACF,CAAC,GACGL,EAAYC,CAAM,IAAUE,IACzBF;AACT;AAMA,SAAgBM,EACd,GACA,GACA,GACM;CACN,IAAMC,IAAWP,KAAU,QAAQQ,KAAqB;CACxD,EAAM,gBAAgB;EACpB,AAAID,KACFE,EACE,GAAGC,EAAU,sBACb,GAAGA,EAAU,8DACf;CAEJ,GAAG,CAACA,GAAWH,CAAQ,CAAC;AAC1B;AAOA,SAAgBI,EACd,GACA,GACA,GACuD;CACvD,EAAyBD,GAAWV,GAAQY,GAAa,MAAM;CAC/D,IAAMC,IAAQb,MAAW,KAAA,IAAqBY,GAAa,SAAtBZ,GAC/Bc,IAAWb,EAAkBY,CAAK;CAGxC,OAAO;EAAE,QAAQC;EAAU,SADXf,EAAYc,CAAK,KAAKC,MAAa;CAChB;AACrC"}
@@ -1,2 +1,2 @@
1
- "use client";const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./useEventCallback.cjs"),n=require("./useRovingTabIndex.cjs");let r=require("react");r=e.__toESM(r,1);var i=`[data-roving-value]`,a=`[role="row"]`;function o(e,t){if(e.disabled===!0)return!1;try{if(e.matches(`:disabled`))return!1}catch{}let n=e.getAttribute(`data-roving-disabled`);if(n!==null&&n!==`false`||!e.hasAttribute(`data-disabled-focusable`)&&e.getAttribute(`aria-disabled`)===`true`)return!1;for(let n=e;n&&n!==t;n=n.parentElement)if(n.hidden||n.hasAttribute(`inert`))return!1;return!0}function s({activeValue:e}){let s=n.useRovingTabIndex({activeValue:e,orientation:`horizontal`,loop:!1,homeEndKeys:!1,tabStop:`last-focused`}),{focusValue:c}=s,l=s.containerProps.onKeyDown,u=t.useEventCallback(e=>{let t=e.currentTarget,n=e.target;if(!n||typeof n.nodeType!=`number`||!t.contains(n))return;let{key:r}=e,s=r===`Home`||r===`End`;if(!(s?!e.metaKey&&!e.altKey:(r===`ArrowUp`||r===`ArrowDown`)&&!e.metaKey&&!e.altKey&&!e.ctrlKey)||e.defaultPrevented){l(e);return}let u=n.closest(i),d=u?.closest(a);if(!u||!d||!t.contains(d))return;let f=Array.from(t.querySelectorAll(a)),p=e=>Array.from(e.querySelectorAll(i)).filter(e=>o(e,t)),m=t=>{e.preventDefault();let n=t?.getAttribute(`data-roving-value`);n!=null&&t!==u&&c(n)};if(s){let t=e.ctrlKey?f.flatMap(p):p(d);m(r===`Home`?t[0]:t[t.length-1]);return}let h=Array.from(d.querySelectorAll(i)).indexOf(u),g=r===`ArrowDown`?1:-1,_=f.indexOf(d);for(let e=_+g;e>=0&&e<f.length;e+=g){let n=Array.from(f[e].querySelectorAll(i)),r,a=1/0;if(n.forEach((e,n)=>{if(!o(e,t))return;let i=Math.abs(n-h);i<a&&(r=e,a=i)}),r){m(r);return}}e.preventDefault()});return{containerProps:r.useMemo(()=>({...s.containerProps,onKeyDown:u}),[s.containerProps,u]),getTabIndex:s.getTabIndex}}exports.useRovingGrid=s;
1
+ "use client";const e=require("./useRovingTabIndex.cjs");function t({activeValue:t}){let{containerProps:n,getTabIndex:r}=e.useRovingTabIndex({activeValue:t,orientation:`grid`,loop:!1,homeEndKeys:!1,tabStop:`last-focused`});return{containerProps:n,getTabIndex:r}}exports.useRovingGrid=t;
2
2
  //# sourceMappingURL=useRovingGrid.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useRovingGrid.cjs","names":["useRovingTabIndex","useEventCallback","React","require_runtime","require_useEventCallback","require_useRovingTabIndex","react","CELL_SELECTOR","ROW_SELECTOR","isEnabledCell","el","marker","node","container","useRovingGrid","roving","baseKeyDown","onKeyDown","event","target","isHomeEnd","key","cell","row","rows","cellsOf","r","goTo","value","focusValue","scope","column","delta","rowIndex","all","best","bestDistance","distance","index"],"sources":["../../src/hooks/useRovingGrid.ts"],"sourcesContent":["import * as React from 'react';\nimport { useEventCallback } from './useEventCallback';\nimport { useRovingTabIndex, type RovingContainerProps } from './useRovingTabIndex';\n\n/** Options of {@link useRovingGrid}. */\nexport interface UseRovingGridOptions {\n /** The selected cell value (the first tab stop), or `null`. */\n activeValue: string | null;\n}\n\n/** Result of {@link useRovingGrid}. */\nexport interface UseRovingGridResult {\n /** Spread onto the grid container; `onKeyDown` already adds the grid keys. */\n containerProps: RovingContainerProps;\n /** `0` for the cell that holds the tab stop (the last focused one, else the selected one, else the first enabled), `-1` for every other cell. */\n getTabIndex: (value: string) => 0 | -1;\n}\n\nconst CELL_SELECTOR = '[data-roving-value]';\nconst ROW_SELECTOR = '[role=\"row\"]';\n\n/** Whether the grid keys may land on `el`: not disabled, not hidden, not inert. */\nfunction isEnabledCell(el: HTMLElement, container: HTMLElement): boolean {\n if ((el as { disabled?: unknown }).disabled === true) return false;\n try {\n if (el.matches(':disabled')) return false; // inside a disabled fieldset\n } catch {\n // A browser without the selector: the other checks apply.\n }\n const marker = el.getAttribute('data-roving-disabled');\n if (marker !== null && marker !== 'false') return false;\n if (!el.hasAttribute('data-disabled-focusable') && el.getAttribute('aria-disabled') === 'true') {\n return false;\n }\n for (let node: HTMLElement | null = el; node && node !== container; node = node.parentElement) {\n if (node.hidden || node.hasAttribute('inert')) return false;\n }\n return true;\n}\n\n/**\n * Internal. Roving focus for a two-dimensional grid of cells (SwatchPicker's grid layout): one\n * `useRovingTabIndex` on the container plus the keys a grid adds. Rows are the `role=\"row\"`\n * elements inside the container and cells their `[data-roving-value]` elements. A cell outside a\n * row gets only the base hook's keys (Left/Right).\n *\n * - Left/Right move through the cells in reading order across rows (mirrored in RTL) and stop at\n * the grid's first and last cell.\n * - Up/Down go to the cell at the same index in the previous/next row, else to the nearest enabled\n * cell of that row (by index; the earlier one on a tie), skipping rows without an enabled cell;\n * they stop at the first/last row.\n * - Home/End go to the first/last enabled cell of the row, Ctrl+Home/End of the grid.\n * - Disabled cells are skipped; Space and Enter are the cell's own. Keys that start outside the\n * container's DOM (a portaled popup) are ignored.\n *\n * @param options - See {@link UseRovingGridOptions}.\n * @returns See {@link UseRovingGridResult}.\n */\nexport function useRovingGrid({ activeValue }: UseRovingGridOptions): UseRovingGridResult {\n const roving = useRovingTabIndex({\n activeValue,\n orientation: 'horizontal',\n loop: false,\n homeEndKeys: false,\n tabStop: 'last-focused',\n });\n const { focusValue } = roving;\n const baseKeyDown = roving.containerProps.onKeyDown;\n\n const onKeyDown = useEventCallback((event: React.KeyboardEvent<HTMLElement>) => {\n const container = event.currentTarget;\n const target = event.target as Node | null;\n // A key from a portaled popup is the popup's: React bubbles it through the container.\n if (!target || typeof target.nodeType !== 'number' || !container.contains(target)) return;\n\n const { key } = event;\n const isHomeEnd = key === 'Home' || key === 'End';\n const isVertical = key === 'ArrowUp' || key === 'ArrowDown';\n const grid = isHomeEnd\n ? !event.metaKey && !event.altKey\n : isVertical && !event.metaKey && !event.altKey && !event.ctrlKey;\n if (!grid || event.defaultPrevented) {\n baseKeyDown(event);\n return;\n }\n\n const cell = (target as Element).closest<HTMLElement>(CELL_SELECTOR);\n const row = cell?.closest<HTMLElement>(ROW_SELECTOR);\n if (!cell || !row || !container.contains(row)) return;\n\n const rows = Array.from(container.querySelectorAll<HTMLElement>(ROW_SELECTOR));\n const cellsOf = (r: HTMLElement) =>\n Array.from(r.querySelectorAll<HTMLElement>(CELL_SELECTOR)).filter((el) =>\n isEnabledCell(el, container),\n );\n const goTo = (el: HTMLElement | undefined) => {\n event.preventDefault();\n const value = el?.getAttribute('data-roving-value');\n if (value != null && el !== cell) focusValue(value);\n };\n\n if (isHomeEnd) {\n const scope = event.ctrlKey ? rows.flatMap(cellsOf) : cellsOf(row);\n goTo(key === 'Home' ? scope[0] : scope[scope.length - 1]);\n return;\n }\n\n // Column distance by index in the row, counting every cell (disabled ones included).\n const column = Array.from(row.querySelectorAll<HTMLElement>(CELL_SELECTOR)).indexOf(cell);\n const delta = key === 'ArrowDown' ? 1 : -1;\n const rowIndex = rows.indexOf(row);\n for (let r = rowIndex + delta; r >= 0 && r < rows.length; r += delta) {\n const all = Array.from(rows[r].querySelectorAll<HTMLElement>(CELL_SELECTOR));\n let best: HTMLElement | undefined;\n let bestDistance = Infinity;\n all.forEach((el, index) => {\n if (!isEnabledCell(el, container)) return;\n const distance = Math.abs(index - column);\n if (distance < bestDistance) {\n best = el;\n bestDistance = distance;\n }\n });\n if (best) {\n goTo(best);\n return;\n }\n }\n // Last (or first) row: the key is the grid's, focus stays.\n event.preventDefault();\n });\n\n const containerProps = React.useMemo<RovingContainerProps>(\n () => ({ ...roving.containerProps, onKeyDown }),\n [roving.containerProps, onKeyDown],\n );\n\n return { containerProps, getTabIndex: roving.getTabIndex };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useRovingGrid.cjs","names":["useRovingTabIndex","require_useRovingTabIndex","useRovingGrid"],"sources":["../../src/hooks/useRovingGrid.ts"],"sourcesContent":["import { useRovingTabIndex, type RovingContainerProps } from './useRovingTabIndex';\n\n/** Options of {@link useRovingGrid}. */\nexport interface UseRovingGridOptions {\n /** The selected cell value (the first tab stop), or `null`. */\n activeValue: string | null;\n}\n\n/** Result of {@link useRovingGrid}. */\nexport interface UseRovingGridResult {\n /** Spread onto the grid container; `onKeyDown` already adds the grid keys. */\n containerProps: RovingContainerProps;\n /** `0` for the cell that holds the tab stop (the last focused one, else the selected one, else the first enabled), `-1` for every other cell. */\n getTabIndex: (value: string) => 0 | -1;\n}\n\n/**\n * Internal. Roving focus for a two-dimensional grid of cells (SwatchPicker's grid layout):\n * `useRovingTabIndex` with `orientation: 'grid'`, which holds the grid keys. Rows are the\n * `role=\"row\"` elements inside the container and cells their `[data-roving-value]` elements; see\n * the hook's `orientation` option for the keys. The tab stop is the last focused cell, else the\n * selected one, else the first enabled.\n *\n * @param options - See {@link UseRovingGridOptions}.\n * @returns See {@link UseRovingGridResult}.\n */\nexport function useRovingGrid({ activeValue }: UseRovingGridOptions): UseRovingGridResult {\n const { containerProps, getTabIndex } = useRovingTabIndex({\n activeValue,\n orientation: 'grid',\n loop: false,\n homeEndKeys: false,\n tabStop: 'last-focused',\n });\n return { containerProps, getTabIndex };\n}\n"],"mappings":"wDA0BA,SAAgBE,EAAc,CAAE,eAA0D,CACxF,GAAM,CAAE,iBAAgB,eAAgBF,EAAAA,kBAAkB,CACxD,cACA,YAAa,OACb,KAAM,GACN,YAAa,GACb,QAAS,cACX,CAAC,EACD,MAAO,CAAE,iBAAgB,aAAY,CACvC"}