@ahrowe/ui 0.36.0 → 0.38.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 (176) hide show
  1. package/README.md +6 -2
  2. package/dist/cjs-types/common/commandPalette/commandPalette.d.ts +4 -0
  3. package/dist/cjs-types/common/commandPalette/commandPalette.types.d.ts +42 -0
  4. package/dist/cjs-types/common/commandPalette/filterCommands.d.ts +15 -0
  5. package/dist/cjs-types/common/commandPalette/index.d.ts +2 -0
  6. package/dist/cjs-types/common/configProvider/configProvider.types.d.ts +6 -0
  7. package/dist/cjs-types/common/copyButton/copyButton.d.ts +4 -0
  8. package/dist/cjs-types/common/copyButton/copyButton.types.d.ts +36 -0
  9. package/dist/cjs-types/common/copyButton/index.d.ts +2 -0
  10. package/dist/cjs-types/common/errorBoundary/errorBoundary.d.ts +0 -3
  11. package/dist/cjs-types/common/errorBoundary/errorBoundary.types.d.ts +2 -3
  12. package/dist/cjs-types/common/hooks/index.d.ts +15 -0
  13. package/dist/cjs-types/common/hooks/useClipboard.d.ts +16 -0
  14. package/dist/cjs-types/common/hooks/useHotkeys.d.ts +45 -0
  15. package/dist/cjs-types/common/hooks/useSwipeDismiss.d.ts +6 -2
  16. package/dist/cjs-types/common/inputDropdown/inputDropdown.types.d.ts +7 -1
  17. package/dist/cjs-types/common/loading/index.d.ts +0 -1
  18. package/dist/cjs-types/common/menu/index.d.ts +1 -0
  19. package/dist/cjs-types/common/menu/menu.types.d.ts +11 -3
  20. package/dist/cjs-types/common/menu/menuHotkeys.d.ts +20 -0
  21. package/dist/cjs-types/common/popover/usePopoverPosition.d.ts +1 -0
  22. package/dist/cjs-types/common/sticky/sticky.d.ts +1 -1
  23. package/dist/cjs-types/common/sticky/sticky.types.d.ts +15 -0
  24. package/dist/cjs-types/common/sticky/stickyStack.d.ts +5 -0
  25. package/dist/cjs-types/common/themeProvider/themeProvider.d.ts +8 -7
  26. package/dist/cjs-types/common/utils/scrollAncestors.d.ts +10 -0
  27. package/dist/cjs-types/common/virtualList/scroller.d.ts +33 -0
  28. package/dist/cjs-types/common/virtualList/useRowDrag.d.ts +6 -1
  29. package/dist/cjs-types/common/virtualList/useRowNavigation.d.ts +11 -1
  30. package/dist/cjs-types/common/virtualList/useVirtualWindow.d.ts +22 -3
  31. package/dist/cjs-types/common/virtualList/virtualList.d.ts +1 -1
  32. package/dist/cjs-types/common/virtualList/virtualList.types.d.ts +43 -3
  33. package/dist/cjs-types/common/weatherIcon/index.d.ts +3 -0
  34. package/dist/cjs-types/common/weatherIcon/parts.d.ts +26 -0
  35. package/dist/cjs-types/common/weatherIcon/weatherIcon.d.ts +4 -0
  36. package/dist/cjs-types/common/weatherIcon/weatherIcon.types.d.ts +44 -0
  37. package/dist/cjs-types/index.d.ts +7 -0
  38. package/dist/esm/common/buttonGroup/buttonGroup.module.mjs.map +1 -1
  39. package/dist/esm/common/commandPalette/commandPalette.mjs +2 -0
  40. package/dist/esm/common/commandPalette/commandPalette.mjs.map +1 -0
  41. package/dist/esm/common/commandPalette/commandPalette.module.mjs +2 -0
  42. package/dist/esm/common/commandPalette/commandPalette.module.mjs.map +1 -0
  43. package/dist/esm/common/commandPalette/filterCommands.mjs +2 -0
  44. package/dist/esm/common/commandPalette/filterCommands.mjs.map +1 -0
  45. package/dist/esm/common/copyButton/copyButton.mjs +2 -0
  46. package/dist/esm/common/copyButton/copyButton.mjs.map +1 -0
  47. package/dist/esm/common/copyButton/copyButton.module.mjs +2 -0
  48. package/dist/esm/common/copyButton/copyButton.module.mjs.map +1 -0
  49. package/dist/esm/common/dropdown/dropdown.mjs +1 -1
  50. package/dist/esm/common/dropdown/dropdown.module.mjs.map +1 -1
  51. package/dist/esm/common/errorBoundary/errorBoundary.mjs +2 -2
  52. package/dist/esm/common/errorBoundary/errorBoundary.mjs.map +1 -1
  53. package/dist/esm/common/errorBoundary/errorBoundary.module.mjs +1 -1
  54. package/dist/esm/common/errorBoundary/errorBoundary.module.mjs.map +1 -1
  55. package/dist/esm/common/hooks/useClipboard.mjs +2 -0
  56. package/dist/esm/common/hooks/useClipboard.mjs.map +1 -0
  57. package/dist/esm/common/hooks/useHotkeys.mjs +2 -0
  58. package/dist/esm/common/hooks/useHotkeys.mjs.map +1 -0
  59. package/dist/esm/common/hooks/useSwipeDismiss.mjs +1 -1
  60. package/dist/esm/common/hooks/useSwipeDismiss.mjs.map +1 -1
  61. package/dist/esm/common/input/input.mjs +1 -1
  62. package/dist/esm/common/input/input.mjs.map +1 -1
  63. package/dist/esm/common/input/input.module.mjs +1 -1
  64. package/dist/esm/common/input/input.module.mjs.map +1 -1
  65. package/dist/esm/common/inputDropdown/inputDropdown.mjs +1 -1
  66. package/dist/esm/common/inputDropdown/inputDropdown.mjs.map +1 -1
  67. package/dist/esm/common/inputDropdown/inputDropdown.module.mjs +1 -1
  68. package/dist/esm/common/inputDropdown/inputDropdown.module.mjs.map +1 -1
  69. package/dist/esm/common/menu/menu.mjs +1 -1
  70. package/dist/esm/common/menu/menu.mjs.map +1 -1
  71. package/dist/esm/common/menu/menu.types.mjs.map +1 -1
  72. package/dist/esm/common/menu/menuHotkeys.mjs +2 -0
  73. package/dist/esm/common/menu/menuHotkeys.mjs.map +1 -0
  74. package/dist/esm/common/multiSelect/multiSelect.mjs +1 -1
  75. package/dist/esm/common/multiSelect/multiSelect.mjs.map +1 -1
  76. package/dist/esm/common/multiSelect/multiSelect.module.mjs +1 -1
  77. package/dist/esm/common/multiSelect/multiSelect.module.mjs.map +1 -1
  78. package/dist/esm/common/otpInput/otpInput.module.mjs.map +1 -1
  79. package/dist/esm/common/popover/usePopoverPosition.mjs +1 -1
  80. package/dist/esm/common/popover/usePopoverPosition.mjs.map +1 -1
  81. package/dist/esm/common/sticky/sticky.mjs +1 -1
  82. package/dist/esm/common/sticky/sticky.mjs.map +1 -1
  83. package/dist/esm/common/sticky/stickyStack.mjs +1 -1
  84. package/dist/esm/common/sticky/stickyStack.mjs.map +1 -1
  85. package/dist/esm/common/themeProvider/themeProvider.mjs +1 -1
  86. package/dist/esm/common/themeProvider/themeProvider.mjs.map +1 -1
  87. package/dist/esm/common/timer/timer.mjs +1 -1
  88. package/dist/esm/common/timer/timer.mjs.map +1 -1
  89. package/dist/esm/common/utils/scrollAncestors.mjs +1 -1
  90. package/dist/esm/common/utils/scrollAncestors.mjs.map +1 -1
  91. package/dist/esm/common/virtualList/scroller.mjs +2 -0
  92. package/dist/esm/common/virtualList/scroller.mjs.map +1 -0
  93. package/dist/esm/common/virtualList/useRowDrag.mjs +1 -1
  94. package/dist/esm/common/virtualList/useRowDrag.mjs.map +1 -1
  95. package/dist/esm/common/virtualList/useRowNavigation.mjs +1 -1
  96. package/dist/esm/common/virtualList/useRowNavigation.mjs.map +1 -1
  97. package/dist/esm/common/virtualList/useVirtualWindow.mjs +1 -1
  98. package/dist/esm/common/virtualList/useVirtualWindow.mjs.map +1 -1
  99. package/dist/esm/common/virtualList/virtualList.mjs +1 -1
  100. package/dist/esm/common/virtualList/virtualList.mjs.map +1 -1
  101. package/dist/esm/common/virtualList/virtualList.module.mjs +1 -1
  102. package/dist/esm/common/virtualList/virtualList.module.mjs.map +1 -1
  103. package/dist/esm/common/weatherIcon/parts.mjs +2 -0
  104. package/dist/esm/common/weatherIcon/parts.mjs.map +1 -0
  105. package/dist/esm/common/weatherIcon/weatherIcon.mjs +2 -0
  106. package/dist/esm/common/weatherIcon/weatherIcon.mjs.map +1 -0
  107. package/dist/esm/common/weatherIcon/weatherIcon.module.mjs +2 -0
  108. package/dist/esm/common/weatherIcon/weatherIcon.module.mjs.map +1 -0
  109. package/dist/esm/common/weatherIcon/weatherIcon.types.mjs +2 -0
  110. package/dist/esm/common/weatherIcon/weatherIcon.types.mjs.map +1 -0
  111. package/dist/esm/index.mjs +1 -1
  112. package/dist/index.cjs +3 -3
  113. package/dist/index.cjs.map +1 -1
  114. package/dist/style.css +1 -1
  115. package/dist/types/common/commandPalette/commandPalette.d.ts +4 -0
  116. package/dist/types/common/commandPalette/commandPalette.types.d.ts +42 -0
  117. package/dist/types/common/commandPalette/filterCommands.d.ts +15 -0
  118. package/dist/types/common/commandPalette/index.d.ts +2 -0
  119. package/dist/types/common/configProvider/configProvider.types.d.ts +6 -0
  120. package/dist/types/common/copyButton/copyButton.d.ts +4 -0
  121. package/dist/types/common/copyButton/copyButton.types.d.ts +36 -0
  122. package/dist/types/common/copyButton/index.d.ts +2 -0
  123. package/dist/types/common/errorBoundary/errorBoundary.d.ts +0 -3
  124. package/dist/types/common/errorBoundary/errorBoundary.types.d.ts +2 -3
  125. package/dist/types/common/hooks/index.d.ts +15 -0
  126. package/dist/types/common/hooks/useClipboard.d.ts +16 -0
  127. package/dist/types/common/hooks/useHotkeys.d.ts +45 -0
  128. package/dist/types/common/hooks/useSwipeDismiss.d.ts +6 -2
  129. package/dist/types/common/inputDropdown/inputDropdown.types.d.ts +7 -1
  130. package/dist/types/common/loading/index.d.ts +0 -1
  131. package/dist/types/common/menu/index.d.ts +1 -0
  132. package/dist/types/common/menu/menu.types.d.ts +11 -3
  133. package/dist/types/common/menu/menuHotkeys.d.ts +20 -0
  134. package/dist/types/common/popover/usePopoverPosition.d.ts +1 -0
  135. package/dist/types/common/sticky/sticky.d.ts +1 -1
  136. package/dist/types/common/sticky/sticky.types.d.ts +15 -0
  137. package/dist/types/common/sticky/stickyStack.d.ts +5 -0
  138. package/dist/types/common/themeProvider/themeProvider.d.ts +8 -7
  139. package/dist/types/common/utils/scrollAncestors.d.ts +10 -0
  140. package/dist/types/common/virtualList/scroller.d.ts +33 -0
  141. package/dist/types/common/virtualList/useRowDrag.d.ts +6 -1
  142. package/dist/types/common/virtualList/useRowNavigation.d.ts +11 -1
  143. package/dist/types/common/virtualList/useVirtualWindow.d.ts +22 -3
  144. package/dist/types/common/virtualList/virtualList.d.ts +1 -1
  145. package/dist/types/common/virtualList/virtualList.types.d.ts +43 -3
  146. package/dist/types/common/weatherIcon/index.d.ts +3 -0
  147. package/dist/types/common/weatherIcon/parts.d.ts +26 -0
  148. package/dist/types/common/weatherIcon/weatherIcon.d.ts +4 -0
  149. package/dist/types/common/weatherIcon/weatherIcon.types.d.ts +44 -0
  150. package/dist/types/index.d.ts +7 -0
  151. package/docs/ButtonGroup.md +2 -0
  152. package/docs/CLAUDE.md +6 -2
  153. package/docs/CommandPalette.md +84 -0
  154. package/docs/ConfigProvider.md +2 -2
  155. package/docs/CopyButton.md +45 -0
  156. package/docs/ErrorBoundary.md +4 -4
  157. package/docs/Fab.md +1 -2
  158. package/docs/Hooks.md +268 -0
  159. package/docs/InputDropdown.md +9 -4
  160. package/docs/Loading.md +56 -15
  161. package/docs/Menu.md +30 -6
  162. package/docs/Popover.md +2 -0
  163. package/docs/Sticky.md +22 -1
  164. package/docs/VirtualList.md +44 -4
  165. package/docs/WeatherIcon.md +49 -0
  166. package/package.json +1 -2
  167. package/dist/cjs-types/common/loading/iconLoading/iconLoading.d.ts +0 -5
  168. package/dist/cjs-types/common/loading/iconLoading/index.d.ts +0 -2
  169. package/dist/esm/common/loading/iconLoading/iconLoading.mjs +0 -2
  170. package/dist/esm/common/loading/iconLoading/iconLoading.mjs.map +0 -1
  171. package/dist/esm/common/loading/iconLoading/iconLoading.module.mjs +0 -2
  172. package/dist/esm/common/loading/iconLoading/iconLoading.module.mjs.map +0 -1
  173. package/dist/esm/common/loading/iconLoading/index.mjs +0 -2
  174. package/dist/esm/common/loading/iconLoading/index.mjs.map +0 -1
  175. package/dist/types/common/loading/iconLoading/iconLoading.d.ts +0 -5
  176. package/dist/types/common/loading/iconLoading/index.d.ts +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"menu.types.mjs","names":[],"sources":["../../../../package/common/menu/menu.types.ts"],"sourcesContent":["import type { CSSProperties, ReactNode } from 'react';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\nimport type { IconContent } from '../types/icon.types';\nimport type { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport type { Presentation } from '../configProvider/presentation.types';\n\n/** One thing the menu can do. */\nexport interface MenuAction {\n /** Stable id, also what `onAction` reports. */\n id: string;\n label: ReactNode;\n icon?: IconContent;\n /**\n * Keyboard hint on the trailing edge, e.g. `⌘K`. Display only: binding the\n * shortcut is yours, since only you know what else is listening.\n */\n shortcut?: ReactNode;\n disabled?: boolean;\n /** Renders in the destructive colour. For deleting, discarding, revoking. */\n danger?: boolean;\n onClick?: () => void;\n}\n\n/** A line between groups of actions. */\nexport interface MenuSeparator {\n separator: true;\n}\n\n/** A label over the actions that follow it. */\nexport interface MenuHeading {\n heading: ReactNode;\n}\n\nexport type MenuEntry = MenuAction | MenuSeparator | MenuHeading;\n\nexport function isMenuSeparator(entry: MenuEntry): entry is MenuSeparator {\n return 'separator' in entry;\n}\n\nexport function isMenuHeading(entry: MenuEntry): entry is MenuHeading {\n return 'heading' in entry;\n}\n\nexport function isMenuAction(entry: MenuEntry): entry is MenuAction {\n return !isMenuSeparator(entry) && !isMenuHeading(entry);\n}\n\nexport type MenuSlots =\n | 'root'\n | 'trigger'\n | 'menu'\n | 'item'\n | 'itemIcon'\n | 'itemLabel'\n | 'itemShortcut'\n | 'separator'\n | 'heading';\n\nexport interface MenuProps extends Omit<HtmlProps, 'onSelect'> {\n /** What the menu offers, in the order it is read. */\n items: MenuEntry[];\n /** The element that opens the menu. */\n children?: ReactNode;\n /** Called with the id of the action that was picked, after its own `onClick`. */\n onAction?: (id: string) => void;\n /** Controlled open state. Leave it out to let the menu manage its own. */\n isOpen?: boolean;\n onOpenChange?: (isOpen: boolean) => void;\n /** Which side of the trigger the menu opens on (default `Bottom`). */\n placement?: PopoverPlacement;\n /** Which edge it lines up with on the cross axis (default `Start`). */\n align?: PopoverAlign;\n /** Anchored to the trigger, pinned to the bottom of the screen, or the latter only on touch. */\n presentation?: Presentation;\n className?: string;\n style?: CSSProperties;\n classNames?: SlotClassNames<MenuSlots>;\n styles?: SlotStyles<MenuSlots>;\n}\n"],"mappings":"aAmCA,SAAgB,EAAgB,EAA0C,CACxE,MAAO,cAAe,CACxB,CAEA,SAAgB,EAAc,EAAwC,CACpE,MAAO,YAAa,CACtB,CAEA,SAAgB,EAAa,EAAuC,CAClE,MAAO,CAAC,EAAgB,CAAK,GAAK,CAAC,EAAc,CAAK,CACxD"}
1
+ {"version":3,"file":"menu.types.mjs","names":[],"sources":["../../../../package/common/menu/menu.types.ts"],"sourcesContent":["import type { CSSProperties, ReactNode } from 'react';\nimport type { SlotClassNames, SlotStyles, HtmlProps } from '../types/slots.types';\nimport type { IconContent } from '../types/icon.types';\nimport type { PopoverAlign, PopoverPlacement } from '../popover/popover.types';\nimport type { Presentation } from '../configProvider/presentation.types';\n\n/** One thing the menu can do. */\nexport interface MenuAction {\n /** Stable id, also what `onAction` reports. */\n id: string;\n label: ReactNode;\n icon?: IconContent;\n /**\n * A keyboard shortcut for this action, in `useHotkeys` spelling: `'mod+k'`,\n * `'shift+f2'`, `'delete'`. Shown on the trailing edge written for the\n * platform: `⌘K` on an Apple one, `Ctrl+K` elsewhere.\n *\n * The menu draws it and nothing more. Binding is yours, because only the app\n * knows what else is listening and whether the shortcut should work at all\n * times or only while the menu is open. `menuHotkeys` turns these entries\n * into a `useHotkeys` map, so the one spelling covers both ends.\n */\n shortcut?: string;\n /** Extra search terms for `CommandPalette`, which also matches a string `label`. `Menu` ignores them. */\n keywords?: string[];\n disabled?: boolean;\n /** Renders in the destructive colour. For deleting, discarding, revoking. */\n danger?: boolean;\n onClick?: () => void;\n}\n\n/** A line between groups of actions. */\nexport interface MenuSeparator {\n separator: true;\n}\n\n/** A label over the actions that follow it. */\nexport interface MenuHeading {\n heading: ReactNode;\n}\n\nexport type MenuEntry = MenuAction | MenuSeparator | MenuHeading;\n\nexport function isMenuSeparator(entry: MenuEntry): entry is MenuSeparator {\n return 'separator' in entry;\n}\n\nexport function isMenuHeading(entry: MenuEntry): entry is MenuHeading {\n return 'heading' in entry;\n}\n\nexport function isMenuAction(entry: MenuEntry): entry is MenuAction {\n return !isMenuSeparator(entry) && !isMenuHeading(entry);\n}\n\nexport type MenuSlots =\n | 'root'\n | 'trigger'\n | 'menu'\n | 'item'\n | 'itemIcon'\n | 'itemLabel'\n | 'itemShortcut'\n | 'separator'\n | 'heading';\n\nexport interface MenuProps extends Omit<HtmlProps, 'onSelect'> {\n /** What the menu offers, in the order it is read. */\n items: MenuEntry[];\n /** The element that opens the menu. */\n children?: ReactNode;\n /** Called with the id of the action that was picked, after its own `onClick`. */\n onAction?: (id: string) => void;\n /** Controlled open state. Leave it out to let the menu manage its own. */\n isOpen?: boolean;\n onOpenChange?: (isOpen: boolean) => void;\n /** Which side of the trigger the menu opens on (default `Bottom`). */\n placement?: PopoverPlacement;\n /** Which edge it lines up with on the cross axis (default `Start`). */\n align?: PopoverAlign;\n /** Anchored to the trigger, pinned to the bottom of the screen, or the latter only on touch. */\n presentation?: Presentation;\n className?: string;\n style?: CSSProperties;\n classNames?: SlotClassNames<MenuSlots>;\n styles?: SlotStyles<MenuSlots>;\n}\n"],"mappings":"aA2CA,SAAgB,EAAgB,EAA0C,CACxE,MAAO,cAAe,CACxB,CAEA,SAAgB,EAAc,EAAwC,CACpE,MAAO,YAAa,CACtB,CAEA,SAAgB,EAAa,EAAuC,CAClE,MAAO,CAAC,EAAgB,CAAK,GAAK,CAAC,EAAc,CAAK,CACxD"}
@@ -0,0 +1,2 @@
1
+ "use client";import{isMenuAction as e}from"./menu.types.mjs";function t(t,n){let r={};for(let i of t)e(i)&&!i.disabled&&i.shortcut&&(r[i.shortcut]=()=>{i.onClick?.(),n?.(i.id)});return r}export{t as menuHotkeys};
2
+ //# sourceMappingURL=menuHotkeys.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"menuHotkeys.mjs","names":[],"sources":["../../../../package/common/menu/menuHotkeys.ts"],"sourcesContent":["import type { HotkeyMap } from '../hooks/useHotkeys';\nimport { isMenuAction, type MenuEntry } from './menu.types';\n\n/**\n * The bindings for a menu's `shortcut` entries, to hand to `useHotkeys` yourself.\n *\n * The menu draws a shortcut but never listens for one, because only the app knows what else is\n * listening: binding it here as well as in an app's own keymap would run the action twice, and\n * whether a shortcut should work at all times or only while the menu is open is the app's\n * question, not the menu's. Calling this keeps the answer in one spelling, so the hint the menu\n * draws cannot drift from what actually fires.\n *\n * ```tsx\n * useHotkeys(menuHotkeys(items, run)); // whenever the page is up\n * useHotkeys(menuHotkeys(items, run), { enabled: isOpen }); // only while the menu is open\n * ```\n *\n * Runs each entry the way clicking it does: its own `onClick`, then `onAction`. A `disabled`\n * entry, and an entry with no `shortcut`, is left out.\n */\nexport function menuHotkeys(items: MenuEntry[], onAction?: (id: string) => void): HotkeyMap {\n const bindings: HotkeyMap = {};\n for (const entry of items) {\n if (!isMenuAction(entry) || entry.disabled || !entry.shortcut) continue;\n bindings[entry.shortcut] = () => {\n entry.onClick?.();\n onAction?.(entry.id);\n };\n }\n return bindings;\n}\n"],"mappings":"6DAoBA,SAAgB,EAAY,EAAoB,EAA4C,CAC1F,IAAM,EAAsB,CAAC,EAC7B,IAAK,IAAM,KAAS,EACb,EAAa,CAAK,GAAK,GAAM,UAAa,EAAM,WACrD,EAAS,EAAM,cAAkB,CAC/B,EAAM,UAAU,EAChB,IAAW,EAAM,EAAE,CACrB,GAEF,OAAO,CACT"}
@@ -1,2 +1,2 @@
1
- "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{PopoverAlign as n,PopoverPlacement as r,PopoverTrigger as ee}from"../popover/popover.types.mjs";import te from"../popover/popover.mjs";import ne from"../chip/chip.mjs";import re from"../tooltip/tooltip.mjs";import{edgeIndex as ie,nextIndex as ae,scrollOptionIntoView as oe}from"../utils/listNavigation.mjs";import se from"../searchInput/searchInput.mjs";import i from"./multiSelect.module.mjs";import{useEffect as a,useId as ce,useRef as o,useState as s}from"react";import c from"classnames";import{FontAwesomeIcon as l}from"@fortawesome/react-fontawesome";import{faCheck as le,faChevronDown as ue,faXmark as de}from"@fortawesome/free-solid-svg-icons";import{jsx as u,jsxs as d}from"react/jsx-runtime";var fe=[],pe={clearSelection:`Clear selection`,create:e=>`Add "${e}"`,searchPlaceholder:`Search…`,empty:`No matches`};function f(f){let{items:p,value:m,defaultValue:me=[],onChange:he=()=>{},label:h=``,alwaysFloatLabel:ge=!1,placeholder:g=``,searchable:_e=!1,allowCustomValues:_=!1,maxSelected:v,clearable:ve=!0,disabled:y=!1,readOnly:b=!1,presentation:ye,formValidator:x=null,errorMessage:S=``,isValid:be=!0,className:xe,style:Se,classNames:C,styles:w,onMouseEnter:Ce,onMouseLeave:we,onFocus:Te,onBlur:Ee,labels:De,...Oe}=e(`MultiSelect`,f),T=t(`MultiSelect`,De,pe),E=o(null),D=o(null),O=o(null),k=o(null),A=ce(),[ke,Ae]=s(me),[j,je]=s(!1),[M,N]=s(``),[P,F]=s(0),[I,L]=s(!1),[Me,Ne]=s(!1),[Pe,Fe]=s(),R=o(x),[,Ie]=s(0);a(()=>{R.current=x;let e=R.current;if(!e)return;e.label=h,e.validate();let t=()=>Ie(e=>e+1);return e.registerOnUpdateListener(t),()=>{e.removeOnUpdateListener(t)}},[x,h]);let z=m!==void 0&&!x,B=x?x.value:z?m:ke,V=Array.isArray(B)?B:fe,H=x?x.touched&&x.hasError():!be||!!S,Le=x?x.getCurrentErrorMessage():S||``,U=!y&&!b,W=v!==void 0&&V.length>=v,G=ge||j||I||V.length>0,K=_e||_,q=M.trim(),J=K?q.toLowerCase():``,Y=J?p.filter(e=>e.label.toLowerCase().includes(J)):p,X=_&&q.length>0&&!W&&!p.some(e=>e.label.toLowerCase()===J||String(e.key).toLowerCase()===J)&&!V.some(e=>String(e).toLowerCase()===J),Z=X?[...Y,{key:q,label:T.create(q)}]:Y,Re=V.map(e=>p.find(t=>t.key===e)??(_?{key:e,label:String(e)}:void 0)).filter(e=>e!==void 0);function Q(e){x?x.set(e):z||Ae(e),he(e)}function $(e){U&&!e.disabled&&(V.includes(e.key)?Q(V.filter(t=>t!==e.key)):W||Q([...V,e.key]),X&&e.key===q&&N(``))}function ze(){E.current?.contains(document.activeElement)&&D.current?.focus()}function Be(e){(!e||U)&&(je(e),e?(Fe(D.current?.offsetWidth),F(0)):(N(``),R.current&&(R.current.touched=!0)))}a(()=>{j&&K&&O.current?.querySelector(`input`)?.focus()},[j,K]),a(()=>{j&&oe(k.current,document.getElementById(`${A}-${P}`))},[j,P,A]);let Ve={isDisabled:e=>{let t=Z[e];return!!t.disabled||W&&!V.includes(t.key)}};function He(e){if(U){if(!j){e.key===`ArrowDown`||e.key===`Enter`||e.key===` `?(e.preventDefault(),Be(!0)):e.key===`Backspace`&&V.length>0&&Q(V.slice(0,-1));return}if(e.key===`ArrowDown`||e.key===`ArrowUp`)e.preventDefault(),F(ae(P,e.key===`ArrowDown`?1:-1,Z.length,Ve)??0);else if(e.key===`Home`||e.key===`End`)e.preventDefault(),F(ie(e.key===`Home`?`first`:`last`,Z.length,Ve)??0);else if(e.key===`Enter`){e.preventDefault();let t=Z[P];t&&$(t)}else e.key===`Backspace`&&!M&&V.length>0&&Q(V.slice(0,-1))}}let Ue=d(`div`,{className:c(i.multiSelectPanel,C?.panel),style:w?.panel,children:[K&&u(`div`,{ref:O,className:c(i.multiSelectSearch,C?.search),style:w?.search,children:u(se,{value:M,onChange:e=>{N(e),F(0)},placeholder:T.searchPlaceholder,onKeyDown:He})}),u(`div`,{ref:k,id:A,role:`listbox`,"aria-multiselectable":`true`,"aria-label":h||void 0,className:c(i.multiSelectOptions,C?.options),style:w?.options,children:Z.length===0?u(`div`,{className:c(i.multiSelectEmpty,C?.empty),style:w?.empty,children:T.empty}):Z.map((e,t)=>{let n=V.includes(e.key),r=e.disabled||W&&!n;return d(`div`,{id:`${A}-${t}`,role:`option`,"aria-selected":n,"aria-disabled":r||void 0,className:c(i.multiSelectOption,t===P&&i.multiSelectOptionActive,n&&i.multiSelectOptionSelected,r&&i.multiSelectOptionDisabled,C?.option),style:w?.option,onMouseDown:e=>e.preventDefault(),onMouseEnter:()=>F(t),onClick:()=>$(e),children:[u(`span`,{className:c(i.multiSelectOptionCheck,C?.optionCheck),style:w?.optionCheck,children:n&&u(l,{icon:le})}),u(`span`,{className:c(i.multiSelectOptionLabel,C?.optionLabel),style:w?.optionLabel,children:e.label})]},e.key)})})]});return d(`div`,{...Oe,ref:E,className:c(i.multiSelect,y&&i.multiSelectDisabled,C?.root,xe),style:{...Se,...w?.root},onMouseEnter:e=>{Ne(!0),Ce?.(e)},onMouseLeave:e=>{Ne(!1),we?.(e)},onFocus:e=>{L(!0),Te?.(e)},onBlur:e=>{e.currentTarget.contains(e.relatedTarget)||(L(!1),Ee?.(e))},children:[u(te,{isOpen:j,onOpenChange:Be,trigger:ee.Click,placement:r.Bottom,align:n.Start,offset:4,withArrow:!1,focusOnOpen:K,constrainWidth:!1,width:`trigger`,disabled:!U,presentation:ye,content:Ue,styles:{panel:Pe?{minWidth:Pe}:void 0},children:d(`div`,{ref:D,role:`combobox`,tabIndex:y?-1:0,"aria-expanded":j,"aria-haspopup":`listbox`,"aria-controls":A,"aria-activedescendant":j&&Z.length>0?`${A}-${P}`:void 0,"aria-disabled":y||void 0,"aria-readonly":b||void 0,className:c(i.multiSelectControl,C?.control),style:w?.control,onKeyDown:He,children:[g&&Re.length===0&&u(`span`,{className:c(i.multiSelectPlaceholder,(!h||G)&&i.multiSelectPlaceholderShown,!h&&i.multiSelectPlaceholderInstant,C?.placeholder),style:w?.placeholder,children:g}),u(`div`,{className:c(i.multiSelectValues,C?.values),style:w?.values,children:Re.map(e=>u(ne,{text:e.label,showRemoveIcon:U,labels:{remove:`Remove ${e.label}`},onMouseDown:e=>e.preventDefault(),onRemove:()=>{$(e),ze()},className:c(i.multiSelectChip,C?.chip),style:w?.chip},e.key))}),ve&&U&&V.length>0&&u(`button`,{type:`button`,tabIndex:-1,"aria-label":T.clearSelection,className:c(i.multiSelectClearButton,C?.clearButton),style:w?.clearButton,onMouseDown:e=>e.preventDefault(),onClick:e=>{e.stopPropagation(),Q([]),ze()},children:u(l,{icon:de})}),u(l,{icon:ue,className:c(i.multiSelectIcon,j&&i.multiSelectIconOpen,C?.icon),style:w?.icon}),h&&u(`div`,{className:c(i.multiSelectLabel,G&&i.multiSelectLabelFloating,C?.label),style:w?.label,children:h}),u(`fieldset`,{className:c(i.multiSelectFieldset,!h&&i.multiSelectFieldsetNoNotch,(I||j)&&i.multiSelectFieldsetFocused,H&&i.multiSelectFieldsetError,C?.fieldset),style:w?.fieldset,children:u(`legend`,{className:c(i.multiSelectFieldsetLegend,G&&i.multiSelectFieldsetLegendActive),children:u(`span`,{children:h})})})]})}),u(re,{variant:`error`,message:Le,isVisible:H&&(I||Me)})]})}export{f as default};
1
+ "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{PopoverAlign as n,PopoverPlacement as r,PopoverTrigger as ee}from"../popover/popover.types.mjs";import te from"../popover/popover.mjs";import ne from"../chip/chip.mjs";import re from"../tooltip/tooltip.mjs";import{edgeIndex as ie,nextIndex as ae,scrollOptionIntoView as oe}from"../utils/listNavigation.mjs";import se from"../searchInput/searchInput.mjs";import i from"./multiSelect.module.mjs";import{useEffect as a,useId as ce,useRef as o,useState as s}from"react";import c from"classnames";import{FontAwesomeIcon as l}from"@fortawesome/react-fontawesome";import{faCheck as le,faChevronDown as ue,faXmark as de}from"@fortawesome/free-solid-svg-icons";import{jsx as u,jsxs as d}from"react/jsx-runtime";var fe=[],pe={clearSelection:`Clear selection`,create:e=>`Add "${e}"`,searchPlaceholder:`Search…`,empty:`No matches`};function f(f){let{items:p,value:m,defaultValue:me=[],onChange:he=()=>{},label:h=``,alwaysFloatLabel:ge=!1,placeholder:g=``,searchable:_e=!1,allowCustomValues:_=!1,maxSelected:v,clearable:ve=!0,disabled:y=!1,readOnly:b=!1,presentation:ye,formValidator:x=null,errorMessage:S=``,isValid:be=!0,className:xe,style:Se,classNames:C,styles:w,onMouseEnter:Ce,onMouseLeave:we,onFocus:Te,onBlur:Ee,labels:De,...Oe}=e(`MultiSelect`,f),T=t(`MultiSelect`,De,pe),E=o(null),D=o(null),O=o(null),k=o(null),A=ce(),[ke,Ae]=s(me),[j,je]=s(!1),[M,N]=s(``),[P,F]=s(0),[I,L]=s(!1),[Me,Ne]=s(!1),[Pe,Fe]=s(),R=o(x),[,Ie]=s(0);a(()=>{R.current=x;let e=R.current;if(!e)return;e.label=h,e.validate();let t=()=>Ie(e=>e+1);return e.registerOnUpdateListener(t),()=>{e.removeOnUpdateListener(t)}},[x,h]);let z=m!==void 0&&!x,B=x?x.value:z?m:ke,V=Array.isArray(B)?B:fe,H=x?x.touched&&x.hasError():!be||!!S,Le=x?x.getCurrentErrorMessage():S||``,U=!y&&!b,W=v!==void 0&&V.length>=v,G=ge||j||I||V.length>0,K=_e||_,q=M.trim(),J=K?q.toLowerCase():``,Y=J?p.filter(e=>e.label.toLowerCase().includes(J)):p,X=_&&q.length>0&&!W&&!p.some(e=>e.label.toLowerCase()===J||String(e.key).toLowerCase()===J)&&!V.some(e=>String(e).toLowerCase()===J),Z=X?[...Y,{key:q,label:T.create(q)}]:Y,Re=V.map(e=>p.find(t=>t.key===e)??(_?{key:e,label:String(e)}:void 0)).filter(e=>e!==void 0);function Q(e){x?x.set(e):z||Ae(e),he(e)}function $(e){U&&!e.disabled&&(V.includes(e.key)?Q(V.filter(t=>t!==e.key)):W||Q([...V,e.key]),X&&e.key===q&&N(``))}function ze(){E.current?.contains(document.activeElement)&&D.current?.focus()}function Be(e){(!e||U)&&(je(e),e?(Fe(D.current?.offsetWidth),F(0)):(N(``),R.current&&(R.current.touched=!0)))}a(()=>{j&&K&&O.current?.querySelector(`input`)?.focus()},[j,K]),a(()=>{j&&oe(k.current,document.getElementById(`${A}-${P}`))},[j,P,A]);let Ve={isDisabled:e=>{let t=Z[e];return!!t.disabled||W&&!V.includes(t.key)}};function He(e){if(U){if(!j){e.key===`ArrowDown`||e.key===`Enter`||e.key===` `?(e.preventDefault(),Be(!0)):e.key===`Backspace`&&V.length>0&&Q(V.slice(0,-1));return}if(e.key===`ArrowDown`||e.key===`ArrowUp`)e.preventDefault(),F(ae(P,e.key===`ArrowDown`?1:-1,Z.length,Ve)??0);else if(e.key===`Home`||e.key===`End`)e.preventDefault(),F(ie(e.key===`Home`?`first`:`last`,Z.length,Ve)??0);else if(e.key===`Enter`){e.preventDefault();let t=Z[P];t&&$(t)}else e.key===`Backspace`&&!M&&V.length>0&&Q(V.slice(0,-1))}}let Ue=d(`div`,{className:c(i.multiSelectPanel,C?.panel),style:w?.panel,children:[K&&u(`div`,{ref:O,className:c(i.multiSelectSearch,C?.search),style:w?.search,children:u(se,{value:M,onChange:e=>{N(e),F(0)},placeholder:T.searchPlaceholder,onKeyDown:He})}),u(`div`,{ref:k,id:A,role:`listbox`,"aria-multiselectable":`true`,"aria-label":h||void 0,className:c(i.multiSelectOptions,C?.options),style:w?.options,children:Z.length===0?u(`div`,{className:c(i.multiSelectEmpty,C?.empty),style:w?.empty,children:T.empty}):Z.map((e,t)=>{let n=V.includes(e.key),r=e.disabled||W&&!n;return d(`div`,{id:`${A}-${t}`,role:`option`,"aria-selected":n,"aria-disabled":r||void 0,className:c(i.multiSelectOption,t===P&&i.multiSelectOptionActive,n&&i.multiSelectOptionSelected,r&&i.multiSelectOptionDisabled,C?.option),style:w?.option,onMouseDown:e=>e.preventDefault(),onMouseEnter:()=>F(t),onClick:()=>$(e),children:[u(`span`,{className:c(i.multiSelectOptionCheck,C?.optionCheck),style:w?.optionCheck,children:n&&u(l,{icon:le})}),u(`span`,{className:c(i.multiSelectOptionLabel,C?.optionLabel),style:w?.optionLabel,children:e.label})]},e.key)})})]});return d(`div`,{...Oe,ref:E,className:c(i.multiSelect,y&&i.multiSelectDisabled,C?.root,xe),style:{...Se,...w?.root},onMouseEnter:e=>{Ne(!0),Ce?.(e)},onMouseLeave:e=>{Ne(!1),we?.(e)},onFocus:e=>{L(!0),Te?.(e)},onBlur:e=>{e.currentTarget.contains(e.relatedTarget)||(L(!1),Ee?.(e))},children:[u(te,{isOpen:j,onOpenChange:Be,trigger:ee.Click,placement:r.Bottom,align:n.Start,offset:4,withArrow:!1,focusOnOpen:K,constrainWidth:!1,width:`trigger`,disabled:!U,presentation:ye,content:Ue,styles:{panel:Pe?{minWidth:Pe}:void 0},children:d(`div`,{ref:D,role:`combobox`,tabIndex:y?-1:0,"aria-expanded":j,"aria-haspopup":`listbox`,"aria-controls":A,"aria-activedescendant":j&&Z.length>0?`${A}-${P}`:void 0,"aria-disabled":y||void 0,"aria-readonly":b||void 0,className:c(i.multiSelectControl,C?.control),style:w?.control,onKeyDown:He,children:[g&&Re.length===0&&u(`span`,{className:c(i.multiSelectPlaceholder,(!h||G)&&i.multiSelectPlaceholderShown,!h&&i.multiSelectPlaceholderInstant,C?.placeholder),style:w?.placeholder,children:g}),u(`div`,{className:c(i.multiSelectValues,C?.values),style:w?.values,children:Re.map(e=>u(ne,{text:e.label,showRemoveIcon:U,labels:{remove:`Remove ${e.label}`},onMouseDown:e=>e.preventDefault(),onRemove:()=>{$(e),ze()},className:c(i.multiSelectChip,C?.chip),style:w?.chip},e.key))}),ve&&U&&V.length>0&&u(`button`,{type:`button`,tabIndex:-1,"aria-label":T.clearSelection,className:c(i.multiSelectClearButton,C?.clearButton),style:w?.clearButton,onMouseDown:e=>e.preventDefault(),onClick:e=>{e.stopPropagation(),Q([]),ze()},children:u(l,{icon:de})}),u(l,{icon:ue,className:c(i.multiSelectIcon,j&&i.multiSelectIconOpen,C?.icon),style:w?.icon}),h&&u(`div`,{className:c(i.multiSelectLabel,G&&i.multiSelectLabelFloating,C?.label),style:w?.label,children:h}),u(`fieldset`,{className:c(i.multiSelectFieldset,(I||j)&&i.multiSelectFieldsetFocused,H&&i.multiSelectFieldsetError,C?.fieldset),style:w?.fieldset,children:u(`legend`,{className:c(i.multiSelectFieldsetLegend,G&&i.multiSelectFieldsetLegendActive),children:u(`span`,{children:h})})})]})}),u(re,{variant:`error`,message:Le,isVisible:H&&(I||Me)})]})}export{f as default};
2
2
  //# sourceMappingURL=multiSelect.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"multiSelect.mjs","names":[],"sources":["../../../../package/common/multiSelect/multiSelect.tsx"],"sourcesContent":["import { useEffect, useId, useRef, useState } from 'react';\nimport type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faChevronDown, faCheck, faXmark } from '@fortawesome/free-solid-svg-icons';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\n\nimport Chip from 'components/common/chip';\nimport SearchInput from 'components/common/searchInput';\nimport Tooltip from 'components/common/tooltip';\nimport Popover from 'components/common/popover';\nimport { PopoverAlign, PopoverPlacement, PopoverTrigger } from '../popover/popover.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { edgeIndex, nextIndex, scrollOptionIntoView } from '../utils/listNavigation';\nimport type { MultiSelectItem, MultiSelectLabels, MultiSelectProps, MultiSelectValue } from './multiSelect.types';\n\nimport styles from './multiSelect.module.pcss';\n\nconst EMPTY: MultiSelectValue[] = [];\n\nconst DEFAULT_LABELS: Required<MultiSelectLabels> = {\n clearSelection: 'Clear selection',\n create: (value) => `Add \"${value}\"`,\n searchPlaceholder: 'Search…',\n empty: 'No matches',\n};\n\nfunction MultiSelect(props: MultiSelectProps): ReactElement {\n const {\n items,\n value,\n defaultValue = [],\n onChange = () => {},\n label = '',\n alwaysFloatLabel = false,\n placeholder = '',\n searchable = false,\n allowCustomValues = false,\n maxSelected,\n clearable = true,\n disabled = false,\n readOnly = false,\n presentation,\n formValidator = null,\n errorMessage = '',\n isValid = true,\n className,\n style,\n classNames,\n styles: slotStyles,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n labels,\n ...rest\n } = useComponentDefaults('MultiSelect', props);\n const text = useLabels('MultiSelect', labels, DEFAULT_LABELS);\n\n const rootRef = useRef<HTMLDivElement>(null);\n const controlRef = useRef<HTMLDivElement>(null);\n const searchRef = useRef<HTMLDivElement>(null);\n const optionsRef = useRef<HTMLDivElement>(null);\n const listboxId = useId();\n\n const [internalValue, setInternalValue] = useState<MultiSelectValue[]>(defaultValue);\n const [isOpen, setIsOpen] = useState(false);\n const [search, setSearch] = useState('');\n const [activeIndex, setActiveIndex] = useState(0);\n const [isFocused, setIsFocused] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const [panelWidth, setPanelWidth] = useState<number>();\n\n // A ref as well as the prop: react-hooks/immutability refuses a mutation of the prop itself in\n // an event handler, and marking the validator touched happens in one.\n const fvRef = useRef(formValidator);\n const [, forceUpdate] = useState(0);\n useEffect(() => {\n fvRef.current = formValidator;\n const fv = fvRef.current;\n if (!fv) return;\n fv.label = label;\n fv.validate();\n const listener = () => forceUpdate((n) => n + 1);\n fv.registerOnUpdateListener(listener);\n return () => {\n fv.removeOnUpdateListener(listener);\n };\n }, [formValidator, label]);\n\n const isControlled = value !== undefined && !formValidator;\n const rawValue = formValidator ? formValidator.value : isControlled ? value : internalValue;\n const selected = Array.isArray(rawValue) ? (rawValue as MultiSelectValue[]) : EMPTY;\n\n const hasError = formValidator ? formValidator.touched && formValidator.hasError() : !isValid || !!errorMessage;\n const errorText = formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || '';\n\n const isInteractive = !disabled && !readOnly;\n const atMax = maxSelected !== undefined && selected.length >= maxSelected;\n\n const shouldFloatLabel = alwaysFloatLabel || isOpen || isFocused || selected.length > 0;\n\n const isSearchable = searchable || allowCustomValues;\n const typed = search.trim();\n const needle = isSearchable ? typed.toLowerCase() : '';\n const matches = needle ? items.filter((item) => item.label.toLowerCase().includes(needle)) : items;\n\n const canCreate =\n allowCustomValues &&\n typed.length > 0 &&\n !atMax &&\n !items.some((item) => item.label.toLowerCase() === needle || String(item.key).toLowerCase() === needle) &&\n !selected.some((key) => String(key).toLowerCase() === needle);\n\n const visibleItems: MultiSelectItem[] = canCreate ? [...matches, { key: typed, label: text.create(typed) }] : matches;\n\n // Mapped from `selected`, not filtered from `items`, to keep the chips in pick order. A custom\n // value has no entry in `items`, so it stands in for itself.\n const selectedItems = selected\n .map<MultiSelectItem | undefined>(\n (key) => items.find((item) => item.key === key) ?? (allowCustomValues ? { key, label: String(key) } : undefined),\n )\n .filter((item): item is MultiSelectItem => item !== undefined);\n\n function commit(next: MultiSelectValue[]) {\n if (formValidator) formValidator.set(next);\n else if (!isControlled) setInternalValue(next);\n onChange(next);\n }\n\n function toggle(item: MultiSelectItem) {\n if (!isInteractive || item.disabled) return;\n if (selected.includes(item.key)) commit(selected.filter((key) => key !== item.key));\n else if (!atMax) commit([...selected, item.key]);\n // The typed text has become a chip, so leaving it in the box would offer to add it again.\n if (canCreate && item.key === typed) setSearch('');\n }\n\n /**\n * A focused element that unmounts fires no blur, so removing the chip or clear button that was\n * just clicked would leave `isFocused` stuck true. Hand focus back so there is still something\n * left to blur.\n */\n function keepFocusInControl() {\n if (rootRef.current?.contains(document.activeElement)) controlRef.current?.focus();\n }\n\n function handleOpenChange(next: boolean) {\n if (next && !isInteractive) return;\n setIsOpen(next);\n if (next) {\n setPanelWidth(controlRef.current?.offsetWidth);\n setActiveIndex(0);\n } else {\n setSearch('');\n if (fvRef.current) fvRef.current.touched = true;\n }\n }\n\n useEffect(() => {\n if (!isOpen || !isSearchable) return;\n searchRef.current?.querySelector('input')?.focus();\n }, [isOpen, isSearchable]);\n\n useEffect(() => {\n if (!isOpen) return;\n scrollOptionIntoView(optionsRef.current, document.getElementById(`${listboxId}-${activeIndex}`));\n }, [isOpen, activeIndex, listboxId]);\n\n // Matches what the options render as `aria-disabled`, so the arrows cannot land on one.\n const navigation = {\n isDisabled: (index: number) => {\n const item = visibleItems[index];\n return !!item.disabled || (atMax && !selected.includes(item.key));\n },\n };\n\n function handleKeyDown(event: React.KeyboardEvent) {\n if (!isInteractive) return;\n\n if (!isOpen) {\n if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleOpenChange(true);\n } else if (event.key === 'Backspace' && selected.length > 0) {\n commit(selected.slice(0, -1));\n }\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n setActiveIndex(nextIndex(activeIndex, event.key === 'ArrowDown' ? 1 : -1, visibleItems.length, navigation) ?? 0);\n } else if (event.key === 'Home' || event.key === 'End') {\n event.preventDefault();\n setActiveIndex(edgeIndex(event.key === 'Home' ? 'first' : 'last', visibleItems.length, navigation) ?? 0);\n } else if (event.key === 'Enter') {\n event.preventDefault();\n const item = visibleItems[activeIndex];\n if (item) toggle(item);\n } else if (event.key === 'Backspace' && !search && selected.length > 0) {\n commit(selected.slice(0, -1));\n }\n }\n\n const panel = (\n <div className={cx(styles.multiSelectPanel, classNames?.panel)} style={slotStyles?.panel}>\n {isSearchable && (\n <div ref={searchRef} className={cx(styles.multiSelectSearch, classNames?.search)} style={slotStyles?.search}>\n <SearchInput\n value={search}\n onChange={(value) => {\n setSearch(value);\n setActiveIndex(0);\n }}\n placeholder={text.searchPlaceholder}\n onKeyDown={handleKeyDown}\n />\n </div>\n )}\n <div\n ref={optionsRef}\n id={listboxId}\n role='listbox'\n aria-multiselectable='true'\n aria-label={label || undefined}\n className={cx(styles.multiSelectOptions, classNames?.options)}\n style={slotStyles?.options}\n >\n {visibleItems.length === 0 ? (\n <div className={cx(styles.multiSelectEmpty, classNames?.empty)} style={slotStyles?.empty}>\n {text.empty}\n </div>\n ) : (\n visibleItems.map((item, index) => {\n const isSelected = selected.includes(item.key);\n const isDisabled = item.disabled || (atMax && !isSelected);\n\n return (\n <div\n key={item.key}\n id={`${listboxId}-${index}`}\n role='option'\n aria-selected={isSelected}\n aria-disabled={isDisabled || undefined}\n className={cx(\n styles.multiSelectOption,\n index === activeIndex && styles.multiSelectOptionActive,\n isSelected && styles.multiSelectOptionSelected,\n isDisabled && styles.multiSelectOptionDisabled,\n classNames?.option,\n )}\n style={slotStyles?.option}\n onMouseDown={(event) => event.preventDefault()}\n onMouseEnter={() => setActiveIndex(index)}\n onClick={() => toggle(item)}\n >\n <span\n className={cx(styles.multiSelectOptionCheck, classNames?.optionCheck)}\n style={slotStyles?.optionCheck}\n >\n {isSelected && <FontAwesomeIcon icon={faCheck} />}\n </span>\n <span\n className={cx(styles.multiSelectOptionLabel, classNames?.optionLabel)}\n style={slotStyles?.optionLabel}\n >\n {item.label}\n </span>\n </div>\n );\n })\n )}\n </div>\n </div>\n );\n\n return (\n <div\n {...rest}\n ref={rootRef}\n className={cx(styles.multiSelect, disabled && styles.multiSelectDisabled, classNames?.root, className)}\n style={{ ...style, ...slotStyles?.root }}\n onMouseEnter={(event) => {\n setIsHovered(true);\n onMouseEnter?.(event);\n }}\n onMouseLeave={(event) => {\n setIsHovered(false);\n onMouseLeave?.(event);\n }}\n onFocus={(event) => {\n setIsFocused(true);\n onFocus?.(event);\n }}\n onBlur={(event) => {\n if (event.currentTarget.contains(event.relatedTarget as Node | null)) return;\n setIsFocused(false);\n onBlur?.(event);\n }}\n >\n <Popover\n isOpen={isOpen}\n onOpenChange={handleOpenChange}\n trigger={PopoverTrigger.Click}\n placement={PopoverPlacement.Bottom}\n align={PopoverAlign.Start}\n offset={4}\n withArrow={false}\n // Neither half of this is the keyboard-only default. A search field takes\n // the caret however the panel was opened, and without one the control\n // keeps the focus and points at the active option with\n // `aria-activedescendant`, so nothing may take it away.\n focusOnOpen={isSearchable}\n constrainWidth={false}\n width='trigger'\n disabled={!isInteractive}\n presentation={presentation}\n content={panel}\n styles={{ panel: panelWidth ? { minWidth: panelWidth } : undefined }}\n >\n <div\n ref={controlRef}\n role='combobox'\n tabIndex={disabled ? -1 : 0}\n aria-expanded={isOpen}\n aria-haspopup='listbox'\n aria-controls={listboxId}\n aria-activedescendant={isOpen && visibleItems.length > 0 ? `${listboxId}-${activeIndex}` : undefined}\n aria-disabled={disabled || undefined}\n aria-readonly={readOnly || undefined}\n className={cx(styles.multiSelectControl, classNames?.control)}\n style={slotStyles?.control}\n onKeyDown={handleKeyDown}\n >\n {placeholder && selectedItems.length === 0 && (\n <span\n className={cx(\n styles.multiSelectPlaceholder,\n (!label || shouldFloatLabel) && styles.multiSelectPlaceholderShown,\n !label && styles.multiSelectPlaceholderInstant,\n classNames?.placeholder,\n )}\n style={slotStyles?.placeholder}\n >\n {placeholder}\n </span>\n )}\n\n <div className={cx(styles.multiSelectValues, classNames?.values)} style={slotStyles?.values}>\n {selectedItems.map((item) => (\n <Chip\n key={item.key}\n text={item.label}\n showRemoveIcon={isInteractive}\n labels={{ remove: `Remove ${item.label}` }}\n onMouseDown={(event) => event.preventDefault()}\n onRemove={() => {\n toggle(item);\n keepFocusInControl();\n }}\n className={cx(styles.multiSelectChip, classNames?.chip)}\n style={slotStyles?.chip}\n />\n ))}\n </div>\n\n {clearable && isInteractive && selected.length > 0 && (\n <button\n type='button'\n tabIndex={-1}\n aria-label={text.clearSelection}\n className={cx(styles.multiSelectClearButton, classNames?.clearButton)}\n style={slotStyles?.clearButton}\n onMouseDown={(event) => event.preventDefault()}\n onClick={(event) => {\n event.stopPropagation();\n commit([]);\n keepFocusInControl();\n }}\n >\n <FontAwesomeIcon icon={faXmark} />\n </button>\n )}\n\n <FontAwesomeIcon\n icon={faChevronDown}\n className={cx(styles.multiSelectIcon, isOpen && styles.multiSelectIconOpen, classNames?.icon)}\n style={slotStyles?.icon as React.CSSProperties & Record<`--fa-font-${string}`, string>}\n />\n\n {label && (\n <div\n className={cx(\n styles.multiSelectLabel,\n shouldFloatLabel && styles.multiSelectLabelFloating,\n classNames?.label,\n )}\n style={slotStyles?.label}\n >\n {label}\n </div>\n )}\n\n {/* The visible border lives here rather than on the control, because the legend is what\n cuts the notch the floating label sits in. Same construction as Input. */}\n <fieldset\n className={cx(\n styles.multiSelectFieldset,\n !label && styles.multiSelectFieldsetNoNotch,\n (isFocused || isOpen) && styles.multiSelectFieldsetFocused,\n hasError && styles.multiSelectFieldsetError,\n classNames?.fieldset,\n )}\n style={slotStyles?.fieldset}\n >\n <legend\n className={cx(\n styles.multiSelectFieldsetLegend,\n shouldFloatLabel && styles.multiSelectFieldsetLegendActive,\n )}\n >\n <span>{label}</span>\n </legend>\n </fieldset>\n </div>\n </Popover>\n\n <Tooltip variant='error' message={errorText} isVisible={hasError && (isFocused || isHovered)} />\n </div>\n );\n}\n\nexport default MultiSelect;\n"],"mappings":"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"}
1
+ {"version":3,"file":"multiSelect.mjs","names":[],"sources":["../../../../package/common/multiSelect/multiSelect.tsx"],"sourcesContent":["import { useEffect, useId, useRef, useState } from 'react';\nimport type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { faChevronDown, faCheck, faXmark } from '@fortawesome/free-solid-svg-icons';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\n\nimport Chip from 'components/common/chip';\nimport SearchInput from 'components/common/searchInput';\nimport Tooltip from 'components/common/tooltip';\nimport Popover from 'components/common/popover';\nimport { PopoverAlign, PopoverPlacement, PopoverTrigger } from '../popover/popover.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { edgeIndex, nextIndex, scrollOptionIntoView } from '../utils/listNavigation';\nimport type { MultiSelectItem, MultiSelectLabels, MultiSelectProps, MultiSelectValue } from './multiSelect.types';\n\nimport styles from './multiSelect.module.pcss';\n\nconst EMPTY: MultiSelectValue[] = [];\n\nconst DEFAULT_LABELS: Required<MultiSelectLabels> = {\n clearSelection: 'Clear selection',\n create: (value) => `Add \"${value}\"`,\n searchPlaceholder: 'Search…',\n empty: 'No matches',\n};\n\nfunction MultiSelect(props: MultiSelectProps): ReactElement {\n const {\n items,\n value,\n defaultValue = [],\n onChange = () => {},\n label = '',\n alwaysFloatLabel = false,\n placeholder = '',\n searchable = false,\n allowCustomValues = false,\n maxSelected,\n clearable = true,\n disabled = false,\n readOnly = false,\n presentation,\n formValidator = null,\n errorMessage = '',\n isValid = true,\n className,\n style,\n classNames,\n styles: slotStyles,\n onMouseEnter,\n onMouseLeave,\n onFocus,\n onBlur,\n labels,\n ...rest\n } = useComponentDefaults('MultiSelect', props);\n const text = useLabels('MultiSelect', labels, DEFAULT_LABELS);\n\n const rootRef = useRef<HTMLDivElement>(null);\n const controlRef = useRef<HTMLDivElement>(null);\n const searchRef = useRef<HTMLDivElement>(null);\n const optionsRef = useRef<HTMLDivElement>(null);\n const listboxId = useId();\n\n const [internalValue, setInternalValue] = useState<MultiSelectValue[]>(defaultValue);\n const [isOpen, setIsOpen] = useState(false);\n const [search, setSearch] = useState('');\n const [activeIndex, setActiveIndex] = useState(0);\n const [isFocused, setIsFocused] = useState(false);\n const [isHovered, setIsHovered] = useState(false);\n const [panelWidth, setPanelWidth] = useState<number>();\n\n // A ref as well as the prop: react-hooks/immutability refuses a mutation of the prop itself in\n // an event handler, and marking the validator touched happens in one.\n const fvRef = useRef(formValidator);\n const [, forceUpdate] = useState(0);\n useEffect(() => {\n fvRef.current = formValidator;\n const fv = fvRef.current;\n if (!fv) return;\n fv.label = label;\n fv.validate();\n const listener = () => forceUpdate((n) => n + 1);\n fv.registerOnUpdateListener(listener);\n return () => {\n fv.removeOnUpdateListener(listener);\n };\n }, [formValidator, label]);\n\n const isControlled = value !== undefined && !formValidator;\n const rawValue = formValidator ? formValidator.value : isControlled ? value : internalValue;\n const selected = Array.isArray(rawValue) ? (rawValue as MultiSelectValue[]) : EMPTY;\n\n const hasError = formValidator ? formValidator.touched && formValidator.hasError() : !isValid || !!errorMessage;\n const errorText = formValidator ? formValidator.getCurrentErrorMessage() : errorMessage || '';\n\n const isInteractive = !disabled && !readOnly;\n const atMax = maxSelected !== undefined && selected.length >= maxSelected;\n\n const shouldFloatLabel = alwaysFloatLabel || isOpen || isFocused || selected.length > 0;\n\n const isSearchable = searchable || allowCustomValues;\n const typed = search.trim();\n const needle = isSearchable ? typed.toLowerCase() : '';\n const matches = needle ? items.filter((item) => item.label.toLowerCase().includes(needle)) : items;\n\n const canCreate =\n allowCustomValues &&\n typed.length > 0 &&\n !atMax &&\n !items.some((item) => item.label.toLowerCase() === needle || String(item.key).toLowerCase() === needle) &&\n !selected.some((key) => String(key).toLowerCase() === needle);\n\n const visibleItems: MultiSelectItem[] = canCreate ? [...matches, { key: typed, label: text.create(typed) }] : matches;\n\n // Mapped from `selected`, not filtered from `items`, to keep the chips in pick order. A custom\n // value has no entry in `items`, so it stands in for itself.\n const selectedItems = selected\n .map<MultiSelectItem | undefined>(\n (key) => items.find((item) => item.key === key) ?? (allowCustomValues ? { key, label: String(key) } : undefined),\n )\n .filter((item): item is MultiSelectItem => item !== undefined);\n\n function commit(next: MultiSelectValue[]) {\n if (formValidator) formValidator.set(next);\n else if (!isControlled) setInternalValue(next);\n onChange(next);\n }\n\n function toggle(item: MultiSelectItem) {\n if (!isInteractive || item.disabled) return;\n if (selected.includes(item.key)) commit(selected.filter((key) => key !== item.key));\n else if (!atMax) commit([...selected, item.key]);\n // The typed text has become a chip, so leaving it in the box would offer to add it again.\n if (canCreate && item.key === typed) setSearch('');\n }\n\n /**\n * A focused element that unmounts fires no blur, so removing the chip or clear button that was\n * just clicked would leave `isFocused` stuck true. Hand focus back so there is still something\n * left to blur.\n */\n function keepFocusInControl() {\n if (rootRef.current?.contains(document.activeElement)) controlRef.current?.focus();\n }\n\n function handleOpenChange(next: boolean) {\n if (next && !isInteractive) return;\n setIsOpen(next);\n if (next) {\n setPanelWidth(controlRef.current?.offsetWidth);\n setActiveIndex(0);\n } else {\n setSearch('');\n if (fvRef.current) fvRef.current.touched = true;\n }\n }\n\n useEffect(() => {\n if (!isOpen || !isSearchable) return;\n searchRef.current?.querySelector('input')?.focus();\n }, [isOpen, isSearchable]);\n\n useEffect(() => {\n if (!isOpen) return;\n scrollOptionIntoView(optionsRef.current, document.getElementById(`${listboxId}-${activeIndex}`));\n }, [isOpen, activeIndex, listboxId]);\n\n // Matches what the options render as `aria-disabled`, so the arrows cannot land on one.\n const navigation = {\n isDisabled: (index: number) => {\n const item = visibleItems[index];\n return !!item.disabled || (atMax && !selected.includes(item.key));\n },\n };\n\n function handleKeyDown(event: React.KeyboardEvent) {\n if (!isInteractive) return;\n\n if (!isOpen) {\n if (event.key === 'ArrowDown' || event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n handleOpenChange(true);\n } else if (event.key === 'Backspace' && selected.length > 0) {\n commit(selected.slice(0, -1));\n }\n return;\n }\n\n if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {\n event.preventDefault();\n setActiveIndex(nextIndex(activeIndex, event.key === 'ArrowDown' ? 1 : -1, visibleItems.length, navigation) ?? 0);\n } else if (event.key === 'Home' || event.key === 'End') {\n event.preventDefault();\n setActiveIndex(edgeIndex(event.key === 'Home' ? 'first' : 'last', visibleItems.length, navigation) ?? 0);\n } else if (event.key === 'Enter') {\n event.preventDefault();\n const item = visibleItems[activeIndex];\n if (item) toggle(item);\n } else if (event.key === 'Backspace' && !search && selected.length > 0) {\n commit(selected.slice(0, -1));\n }\n }\n\n const panel = (\n <div className={cx(styles.multiSelectPanel, classNames?.panel)} style={slotStyles?.panel}>\n {isSearchable && (\n <div ref={searchRef} className={cx(styles.multiSelectSearch, classNames?.search)} style={slotStyles?.search}>\n <SearchInput\n value={search}\n onChange={(value) => {\n setSearch(value);\n setActiveIndex(0);\n }}\n placeholder={text.searchPlaceholder}\n onKeyDown={handleKeyDown}\n />\n </div>\n )}\n <div\n ref={optionsRef}\n id={listboxId}\n role='listbox'\n aria-multiselectable='true'\n aria-label={label || undefined}\n className={cx(styles.multiSelectOptions, classNames?.options)}\n style={slotStyles?.options}\n >\n {visibleItems.length === 0 ? (\n <div className={cx(styles.multiSelectEmpty, classNames?.empty)} style={slotStyles?.empty}>\n {text.empty}\n </div>\n ) : (\n visibleItems.map((item, index) => {\n const isSelected = selected.includes(item.key);\n const isDisabled = item.disabled || (atMax && !isSelected);\n\n return (\n <div\n key={item.key}\n id={`${listboxId}-${index}`}\n role='option'\n aria-selected={isSelected}\n aria-disabled={isDisabled || undefined}\n className={cx(\n styles.multiSelectOption,\n index === activeIndex && styles.multiSelectOptionActive,\n isSelected && styles.multiSelectOptionSelected,\n isDisabled && styles.multiSelectOptionDisabled,\n classNames?.option,\n )}\n style={slotStyles?.option}\n onMouseDown={(event) => event.preventDefault()}\n onMouseEnter={() => setActiveIndex(index)}\n onClick={() => toggle(item)}\n >\n <span\n className={cx(styles.multiSelectOptionCheck, classNames?.optionCheck)}\n style={slotStyles?.optionCheck}\n >\n {isSelected && <FontAwesomeIcon icon={faCheck} />}\n </span>\n <span\n className={cx(styles.multiSelectOptionLabel, classNames?.optionLabel)}\n style={slotStyles?.optionLabel}\n >\n {item.label}\n </span>\n </div>\n );\n })\n )}\n </div>\n </div>\n );\n\n return (\n <div\n {...rest}\n ref={rootRef}\n className={cx(styles.multiSelect, disabled && styles.multiSelectDisabled, classNames?.root, className)}\n style={{ ...style, ...slotStyles?.root }}\n onMouseEnter={(event) => {\n setIsHovered(true);\n onMouseEnter?.(event);\n }}\n onMouseLeave={(event) => {\n setIsHovered(false);\n onMouseLeave?.(event);\n }}\n onFocus={(event) => {\n setIsFocused(true);\n onFocus?.(event);\n }}\n onBlur={(event) => {\n if (event.currentTarget.contains(event.relatedTarget as Node | null)) return;\n setIsFocused(false);\n onBlur?.(event);\n }}\n >\n <Popover\n isOpen={isOpen}\n onOpenChange={handleOpenChange}\n trigger={PopoverTrigger.Click}\n placement={PopoverPlacement.Bottom}\n align={PopoverAlign.Start}\n offset={4}\n withArrow={false}\n // Neither half of this is the keyboard-only default. A search field takes\n // the caret however the panel was opened, and without one the control\n // keeps the focus and points at the active option with\n // `aria-activedescendant`, so nothing may take it away.\n focusOnOpen={isSearchable}\n constrainWidth={false}\n width='trigger'\n disabled={!isInteractive}\n presentation={presentation}\n content={panel}\n styles={{ panel: panelWidth ? { minWidth: panelWidth } : undefined }}\n >\n <div\n ref={controlRef}\n role='combobox'\n tabIndex={disabled ? -1 : 0}\n aria-expanded={isOpen}\n aria-haspopup='listbox'\n aria-controls={listboxId}\n aria-activedescendant={isOpen && visibleItems.length > 0 ? `${listboxId}-${activeIndex}` : undefined}\n aria-disabled={disabled || undefined}\n aria-readonly={readOnly || undefined}\n className={cx(styles.multiSelectControl, classNames?.control)}\n style={slotStyles?.control}\n onKeyDown={handleKeyDown}\n >\n {placeholder && selectedItems.length === 0 && (\n <span\n className={cx(\n styles.multiSelectPlaceholder,\n (!label || shouldFloatLabel) && styles.multiSelectPlaceholderShown,\n !label && styles.multiSelectPlaceholderInstant,\n classNames?.placeholder,\n )}\n style={slotStyles?.placeholder}\n >\n {placeholder}\n </span>\n )}\n\n <div className={cx(styles.multiSelectValues, classNames?.values)} style={slotStyles?.values}>\n {selectedItems.map((item) => (\n <Chip\n key={item.key}\n text={item.label}\n showRemoveIcon={isInteractive}\n labels={{ remove: `Remove ${item.label}` }}\n onMouseDown={(event) => event.preventDefault()}\n onRemove={() => {\n toggle(item);\n keepFocusInControl();\n }}\n className={cx(styles.multiSelectChip, classNames?.chip)}\n style={slotStyles?.chip}\n />\n ))}\n </div>\n\n {clearable && isInteractive && selected.length > 0 && (\n <button\n type='button'\n tabIndex={-1}\n aria-label={text.clearSelection}\n className={cx(styles.multiSelectClearButton, classNames?.clearButton)}\n style={slotStyles?.clearButton}\n onMouseDown={(event) => event.preventDefault()}\n onClick={(event) => {\n event.stopPropagation();\n commit([]);\n keepFocusInControl();\n }}\n >\n <FontAwesomeIcon icon={faXmark} />\n </button>\n )}\n\n <FontAwesomeIcon\n icon={faChevronDown}\n className={cx(styles.multiSelectIcon, isOpen && styles.multiSelectIconOpen, classNames?.icon)}\n style={slotStyles?.icon as React.CSSProperties & Record<`--fa-font-${string}`, string>}\n />\n\n {label && (\n <div\n className={cx(\n styles.multiSelectLabel,\n shouldFloatLabel && styles.multiSelectLabelFloating,\n classNames?.label,\n )}\n style={slotStyles?.label}\n >\n {label}\n </div>\n )}\n\n {/* The visible border lives here rather than on the control, because the legend is what\n cuts the notch the floating label sits in. Same construction as Input. */}\n <fieldset\n className={cx(\n styles.multiSelectFieldset,\n (isFocused || isOpen) && styles.multiSelectFieldsetFocused,\n hasError && styles.multiSelectFieldsetError,\n classNames?.fieldset,\n )}\n style={slotStyles?.fieldset}\n >\n <legend\n className={cx(\n styles.multiSelectFieldsetLegend,\n shouldFloatLabel && styles.multiSelectFieldsetLegendActive,\n )}\n >\n <span>{label}</span>\n </legend>\n </fieldset>\n </div>\n </Popover>\n\n <Tooltip variant='error' message={errorText} isVisible={hasError && (isFocused || isHovered)} />\n </div>\n );\n}\n\nexport default MultiSelect;\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";var e=`multiSelect_fmViY`,t=`multiSelect-label_asHZ1`,n=`multiSelect-label-floating_d4-Om`,r=`multiSelect-fieldset_Eo29l`,i=`multiSelect-fieldset-noNotch_8EL3U`,a=`multiSelect-fieldset-focused_ZC2P6`,o=`multiSelect-fieldset-error_CWzEo`,s=`multiSelect-fieldset-legend_p-6nh`,c=`multiSelect-fieldset-legend-active_M0L7g`,l=`multiSelect-control_zLb7t`,u=`multiSelect-values_k1Q8x`,d=`multiSelect-chip_xqe8Q`,f=`multiSelect-placeholder_Tjrmh`,p=`multiSelect-placeholder-shown_8v36t`,m=`multiSelectFadeIn_rOplX`,h=`multiSelect-placeholder-instant_r9-TD`,g=`multiSelect-clearButton_FKs0m`,_=`multiSelect-icon_ovLu-`,v=`multiSelect-icon-open_h8VAu`,y=`multiSelect-disabled_Hm8mv`,b=`multiSelect-panel_VX7gU`,x=`multiSelect-search_g4OHX`,S=`multiSelect-options_MGZCv`,C=`multiSelect-option_hbvPY`,w=`multiSelect-option-active_mSWFG`,T=`multiSelect-option-selected_mjPq-`,E=`multiSelect-option-disabled_qSgYz`,D=`multiSelect-optionCheck_-YKoj`,O=`multiSelect-optionLabel_CFZke`,k=`multiSelect-empty_MjABY`,A={multiSelect:e,"multiSelect-label":`multiSelect-label_asHZ1`,multiSelectLabel:t,"multiSelect-label-floating":`multiSelect-label-floating_d4-Om`,multiSelectLabelFloating:n,"multiSelect-fieldset":`multiSelect-fieldset_Eo29l`,multiSelectFieldset:r,"multiSelect-fieldset-noNotch":`multiSelect-fieldset-noNotch_8EL3U`,multiSelectFieldsetNoNotch:i,"multiSelect-fieldset-focused":`multiSelect-fieldset-focused_ZC2P6`,multiSelectFieldsetFocused:a,"multiSelect-fieldset-error":`multiSelect-fieldset-error_CWzEo`,multiSelectFieldsetError:o,"multiSelect-fieldset-legend":`multiSelect-fieldset-legend_p-6nh`,multiSelectFieldsetLegend:s,"multiSelect-fieldset-legend-active":`multiSelect-fieldset-legend-active_M0L7g`,multiSelectFieldsetLegendActive:c,"multiSelect-control":`multiSelect-control_zLb7t`,multiSelectControl:l,"multiSelect-values":`multiSelect-values_k1Q8x`,multiSelectValues:u,"multiSelect-chip":`multiSelect-chip_xqe8Q`,multiSelectChip:d,"multiSelect-placeholder":`multiSelect-placeholder_Tjrmh`,multiSelectPlaceholder:f,"multiSelect-placeholder-shown":`multiSelect-placeholder-shown_8v36t`,multiSelectPlaceholderShown:p,multiSelectFadeIn:m,"multiSelect-placeholder-instant":`multiSelect-placeholder-instant_r9-TD`,multiSelectPlaceholderInstant:h,"multiSelect-clearButton":`multiSelect-clearButton_FKs0m`,multiSelectClearButton:g,"multiSelect-icon":`multiSelect-icon_ovLu-`,multiSelectIcon:_,"multiSelect-icon-open":`multiSelect-icon-open_h8VAu`,multiSelectIconOpen:v,"multiSelect-disabled":`multiSelect-disabled_Hm8mv`,multiSelectDisabled:y,"multiSelect-panel":`multiSelect-panel_VX7gU`,multiSelectPanel:b,"multiSelect-search":`multiSelect-search_g4OHX`,multiSelectSearch:x,"multiSelect-options":`multiSelect-options_MGZCv`,multiSelectOptions:S,"multiSelect-option":`multiSelect-option_hbvPY`,multiSelectOption:C,"multiSelect-option-active":`multiSelect-option-active_mSWFG`,multiSelectOptionActive:w,"multiSelect-option-selected":`multiSelect-option-selected_mjPq-`,multiSelectOptionSelected:T,"multiSelect-option-disabled":`multiSelect-option-disabled_qSgYz`,multiSelectOptionDisabled:E,"multiSelect-optionCheck":`multiSelect-optionCheck_-YKoj`,multiSelectOptionCheck:D,"multiSelect-optionLabel":`multiSelect-optionLabel_CFZke`,multiSelectOptionLabel:O,"multiSelect-empty":`multiSelect-empty_MjABY`,multiSelectEmpty:k};export{A as default,e as multiSelect,d as multiSelectChip,g as multiSelectClearButton,l as multiSelectControl,y as multiSelectDisabled,k as multiSelectEmpty,m as multiSelectFadeIn,r as multiSelectFieldset,o as multiSelectFieldsetError,a as multiSelectFieldsetFocused,s as multiSelectFieldsetLegend,c as multiSelectFieldsetLegendActive,i as multiSelectFieldsetNoNotch,_ as multiSelectIcon,v as multiSelectIconOpen,t as multiSelectLabel,n as multiSelectLabelFloating,C as multiSelectOption,w as multiSelectOptionActive,D as multiSelectOptionCheck,E as multiSelectOptionDisabled,O as multiSelectOptionLabel,T as multiSelectOptionSelected,S as multiSelectOptions,b as multiSelectPanel,f as multiSelectPlaceholder,h as multiSelectPlaceholderInstant,p as multiSelectPlaceholderShown,x as multiSelectSearch,u as multiSelectValues};
1
+ "use client";var e=`multiSelect_fmViY`,t=`multiSelect-label_asHZ1`,n=`multiSelect-label-floating_d4-Om`,r=`multiSelect-fieldset_Eo29l`,i=`multiSelect-fieldset-focused_ZC2P6`,a=`multiSelect-fieldset-error_CWzEo`,o=`multiSelect-fieldset-legend_p-6nh`,s=`multiSelect-fieldset-legend-active_M0L7g`,c=`multiSelect-control_zLb7t`,l=`multiSelect-values_k1Q8x`,u=`multiSelect-chip_xqe8Q`,d=`multiSelect-placeholder_Tjrmh`,f=`multiSelect-placeholder-shown_8v36t`,p=`multiSelectFadeIn_rOplX`,m=`multiSelect-placeholder-instant_r9-TD`,h=`multiSelect-clearButton_FKs0m`,g=`multiSelect-icon_ovLu-`,_=`multiSelect-icon-open_h8VAu`,v=`multiSelect-disabled_Hm8mv`,y=`multiSelect-panel_VX7gU`,b=`multiSelect-search_g4OHX`,x=`multiSelect-options_MGZCv`,S=`multiSelect-option_hbvPY`,C=`multiSelect-option-active_mSWFG`,w=`multiSelect-option-selected_mjPq-`,T=`multiSelect-option-disabled_qSgYz`,E=`multiSelect-optionCheck_-YKoj`,D=`multiSelect-optionLabel_CFZke`,O=`multiSelect-empty_MjABY`,k={multiSelect:e,"multiSelect-label":`multiSelect-label_asHZ1`,multiSelectLabel:t,"multiSelect-label-floating":`multiSelect-label-floating_d4-Om`,multiSelectLabelFloating:n,"multiSelect-fieldset":`multiSelect-fieldset_Eo29l`,multiSelectFieldset:r,"multiSelect-fieldset-focused":`multiSelect-fieldset-focused_ZC2P6`,multiSelectFieldsetFocused:i,"multiSelect-fieldset-error":`multiSelect-fieldset-error_CWzEo`,multiSelectFieldsetError:a,"multiSelect-fieldset-legend":`multiSelect-fieldset-legend_p-6nh`,multiSelectFieldsetLegend:o,"multiSelect-fieldset-legend-active":`multiSelect-fieldset-legend-active_M0L7g`,multiSelectFieldsetLegendActive:s,"multiSelect-control":`multiSelect-control_zLb7t`,multiSelectControl:c,"multiSelect-values":`multiSelect-values_k1Q8x`,multiSelectValues:l,"multiSelect-chip":`multiSelect-chip_xqe8Q`,multiSelectChip:u,"multiSelect-placeholder":`multiSelect-placeholder_Tjrmh`,multiSelectPlaceholder:d,"multiSelect-placeholder-shown":`multiSelect-placeholder-shown_8v36t`,multiSelectPlaceholderShown:f,multiSelectFadeIn:p,"multiSelect-placeholder-instant":`multiSelect-placeholder-instant_r9-TD`,multiSelectPlaceholderInstant:m,"multiSelect-clearButton":`multiSelect-clearButton_FKs0m`,multiSelectClearButton:h,"multiSelect-icon":`multiSelect-icon_ovLu-`,multiSelectIcon:g,"multiSelect-icon-open":`multiSelect-icon-open_h8VAu`,multiSelectIconOpen:_,"multiSelect-disabled":`multiSelect-disabled_Hm8mv`,multiSelectDisabled:v,"multiSelect-panel":`multiSelect-panel_VX7gU`,multiSelectPanel:y,"multiSelect-search":`multiSelect-search_g4OHX`,multiSelectSearch:b,"multiSelect-options":`multiSelect-options_MGZCv`,multiSelectOptions:x,"multiSelect-option":`multiSelect-option_hbvPY`,multiSelectOption:S,"multiSelect-option-active":`multiSelect-option-active_mSWFG`,multiSelectOptionActive:C,"multiSelect-option-selected":`multiSelect-option-selected_mjPq-`,multiSelectOptionSelected:w,"multiSelect-option-disabled":`multiSelect-option-disabled_qSgYz`,multiSelectOptionDisabled:T,"multiSelect-optionCheck":`multiSelect-optionCheck_-YKoj`,multiSelectOptionCheck:E,"multiSelect-optionLabel":`multiSelect-optionLabel_CFZke`,multiSelectOptionLabel:D,"multiSelect-empty":`multiSelect-empty_MjABY`,multiSelectEmpty:O};export{k as default,e as multiSelect,u as multiSelectChip,h as multiSelectClearButton,c as multiSelectControl,v as multiSelectDisabled,O as multiSelectEmpty,p as multiSelectFadeIn,r as multiSelectFieldset,a as multiSelectFieldsetError,i as multiSelectFieldsetFocused,o as multiSelectFieldsetLegend,s as multiSelectFieldsetLegendActive,g as multiSelectIcon,_ as multiSelectIconOpen,t as multiSelectLabel,n as multiSelectLabelFloating,S as multiSelectOption,C as multiSelectOptionActive,E as multiSelectOptionCheck,T as multiSelectOptionDisabled,D as multiSelectOptionLabel,w as multiSelectOptionSelected,x as multiSelectOptions,y as multiSelectPanel,d as multiSelectPlaceholder,m as multiSelectPlaceholderInstant,f as multiSelectPlaceholderShown,b as multiSelectSearch,l as multiSelectValues};
2
2
  //# sourceMappingURL=multiSelect.module.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"multiSelect.module.mjs","names":[],"sources":["../../../../package/common/multiSelect/multiSelect.module.pcss"],"sourcesContent":[".multiSelect {\n position: relative;\n width: 100%;\n\n /* Input's notch geometry, so both labels float to the same height in a form row. */\n --multi-select-notch-height: 1.75em;\n\n &-label {\n position: absolute;\n left: 7px;\n right: 0;\n top: 50%;\n transform: translateY(-50%);\n font-size: 1em;\n color: var(--input-label-resting-color, var(--text-light));\n pointer-events: none;\n user-select: none;\n transition: all 0.3s;\n\n &-floating {\n top: 0;\n bottom: auto;\n left: 14px;\n font-size: 0.8em;\n color: var(--input-label-floating-color, var(--text-color));\n }\n }\n\n /* The field's border lives here, not on the control: the legend has to cut the notch out of it. */\n &-fieldset {\n position: absolute;\n inset: calc(var(--multi-select-notch-height) / -2) 0 0;\n width: 100%;\n margin: 0;\n padding: 0 0 0 8px;\n min-width: 0;\n box-sizing: border-box;\n border: 1px solid var(--border-color);\n border-radius: var(--default-border-radius);\n font-size: 0.8em;\n pointer-events: none;\n user-select: none;\n transition: border-color 0.15s;\n\n &-noNotch {\n /* Resetting this everywhere would leave a label-less field shorter than its neighbours.\n Only Firefox renders the reserved space as an overlap, so scope it there, as Input does. */\n @supports (-moz-appearance: none) {\n inset: 0;\n }\n }\n\n &-focused {\n border-color: var(--focus-border-color, var(--primary-color));\n }\n\n &-error {\n border-color: var(--error-border-color, var(--error-color));\n }\n\n &-legend {\n display: block;\n max-width: 0;\n padding: 0;\n white-space: nowrap;\n transition: max-width 0.2s;\n\n &-active {\n max-width: 300px;\n }\n\n span {\n line-height: var(--multi-select-notch-height);\n padding: 0 5px;\n opacity: 0;\n }\n\n span:empty {\n padding: 0.000001px;\n }\n }\n }\n\n &-control {\n position: relative;\n display: flex;\n align-items: center;\n gap: 6px;\n box-sizing: border-box;\n width: 100%;\n\n /* Input and Dropdown both resolve to this: a 1.25 line-height plus 14px of padding. Kept as\n the formula rather than a flat 34px so it still matches them at another font size. */\n min-height: var(--multi-select-min-height, calc(1.25em + 14px));\n padding: 2px 8px;\n border-radius: var(--default-border-radius);\n background: var(--input-background, var(--background));\n color: var(--text-color);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n /* An outline here would draw a second, un-notched rectangle over the fieldset's border. */\n &:focus-visible {\n outline: none;\n\n .multiSelect-fieldset {\n border-color: var(--focus-border-color, var(--primary-color));\n }\n }\n }\n\n &-values {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px;\n flex: 1;\n min-width: 0;\n }\n\n /* At full size a Chip is nearly as tall as the field, so one row would set the height instead\n of fitting inside it. Shrinking the font shrinks the text and the 1em remove icon together. */\n &-chip {\n max-width: 100%;\n min-width: 0;\n font-size: 0.8em;\n\n /* FontAwesome's `vertical-align: -0.125em` makes the inline remove icon reserve 1.6px of\n descender space: the difference between a 34px field and a 34.4px one. */\n svg {\n display: block;\n }\n }\n\n /* Faded in on a delay rather than swapped in: the label takes 0.3s to float clear, and showing\n the placeholder as it starts moving puts the two on top of each other. As Input does it. */\n &-placeholder {\n position: absolute;\n left: 8px;\n right: 1.75em;\n top: 50%;\n transform: translateY(-50%);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n opacity: 0;\n color: var(--input-label-resting-color, var(--text-light));\n pointer-events: none;\n user-select: none;\n\n &-shown {\n animation: 0.2s multiSelectFadeIn 0.3s forwards;\n }\n\n /* Nothing to wait for without a label. Must stay after `-shown` to win the specificity tie. */\n &-instant {\n animation-delay: 0s;\n }\n }\n\n @keyframes multiSelectFadeIn {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 1;\n }\n }\n\n &-clearButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 22px;\n height: 22px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--text-dark);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n &:hover {\n background: var(--background-accent);\n color: var(--text-color);\n }\n }\n\n &-icon {\n flex: none;\n color: var(--input-icon-color, var(--text-dark));\n transition: transform 0.2s;\n\n &-open {\n transform: rotate(180deg);\n }\n }\n\n &-disabled {\n opacity: 0.5;\n\n .multiSelect-control {\n cursor: default;\n }\n }\n}\n\n/* Portalled, so a sibling of the field rather than a descendant, and not nestable above. */\n.multiSelect-panel {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 180px;\n}\n\n.multiSelect-search {\n flex: none;\n}\n\n.multiSelect-options {\n display: flex;\n flex-direction: column;\n max-height: var(--multi-select-panel-height, 260px);\n overflow-y: auto;\n}\n\n.multiSelect-option {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-radius: var(--default-border-radius);\n color: var(--text-color);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n &-active {\n background: var(--background-accent);\n }\n\n &-selected {\n color: var(--primary-color);\n font-weight: 600;\n }\n\n &-disabled {\n opacity: 0.45;\n cursor: default;\n pointer-events: none;\n }\n}\n\n.multiSelect-optionCheck {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 1em;\n color: var(--primary-color);\n}\n\n.multiSelect-optionLabel {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.multiSelect-empty {\n padding: 12px 10px;\n color: var(--text-dark);\n text-align: center;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .multiSelect-label,\n .multiSelect-icon {\n transition: none;\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"multiSelect.module.mjs","names":[],"sources":["../../../../package/common/multiSelect/multiSelect.module.pcss"],"sourcesContent":[".multiSelect {\n position: relative;\n width: 100%;\n\n /* Input's notch geometry, so both labels float to the same height in a form row. */\n --multi-select-notch-height: 1.75em;\n\n &-label {\n position: absolute;\n left: 7px;\n right: 0;\n top: 50%;\n transform: translateY(-50%);\n font-size: 1em;\n color: var(--input-label-resting-color, var(--text-light));\n pointer-events: none;\n user-select: none;\n transition: all 0.3s;\n\n &-floating {\n top: 0;\n bottom: auto;\n left: 14px;\n font-size: 0.8em;\n color: var(--input-label-floating-color, var(--text-color));\n }\n }\n\n /* The field's border lives here, not on the control: the legend has to cut the notch out of it. */\n &-fieldset {\n position: absolute;\n inset: calc(var(--multi-select-notch-height) / -2) 0 0;\n width: 100%;\n margin: 0;\n padding: 0 0 0 8px;\n min-width: 0;\n box-sizing: border-box;\n border: 1px solid var(--border-color);\n border-radius: var(--default-border-radius);\n font-size: 0.8em;\n pointer-events: none;\n user-select: none;\n transition: border-color 0.15s;\n\n &-focused {\n border-color: var(--focus-border-color, var(--primary-color));\n }\n\n &-error {\n border-color: var(--error-border-color, var(--error-color));\n }\n\n &-legend {\n display: block;\n\n /* Not taken from the text: an empty span gets no line box, which raised a label-less\n field's border half a notch. */\n height: var(--multi-select-notch-height);\n max-width: 0;\n padding: 0;\n white-space: nowrap;\n transition: max-width 0.2s;\n\n &-active {\n max-width: 300px;\n }\n\n span {\n line-height: var(--multi-select-notch-height);\n padding: 0 5px;\n opacity: 0;\n }\n\n span:empty {\n padding: 0;\n }\n }\n }\n\n &-control {\n position: relative;\n display: flex;\n align-items: center;\n gap: 6px;\n box-sizing: border-box;\n width: 100%;\n\n /* Input and Dropdown both resolve to this: a 1.25 line-height plus 14px of padding. Kept as\n the formula rather than a flat 34px so it still matches them at another font size. */\n min-height: var(--multi-select-min-height, calc(1.25em + 14px));\n padding: 2px 8px;\n border-radius: var(--default-border-radius);\n background: var(--input-background, var(--background));\n color: var(--text-color);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n /* An outline here would draw a second, un-notched rectangle over the fieldset's border. */\n &:focus-visible {\n outline: none;\n\n .multiSelect-fieldset {\n border-color: var(--focus-border-color, var(--primary-color));\n }\n }\n }\n\n &-values {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: 4px;\n flex: 1;\n min-width: 0;\n }\n\n /* At full size a Chip is nearly as tall as the field, so one row would set the height instead\n of fitting inside it. Shrinking the font shrinks the text and the 1em remove icon together. */\n &-chip {\n max-width: 100%;\n min-width: 0;\n font-size: 0.8em;\n\n /* FontAwesome's `vertical-align: -0.125em` makes the inline remove icon reserve 1.6px of\n descender space: the difference between a 34px field and a 34.4px one. */\n svg {\n display: block;\n }\n }\n\n /* Faded in on a delay rather than swapped in: the label takes 0.3s to float clear, and showing\n the placeholder as it starts moving puts the two on top of each other. As Input does it. */\n &-placeholder {\n position: absolute;\n left: 8px;\n right: 1.75em;\n top: 50%;\n transform: translateY(-50%);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n opacity: 0;\n color: var(--input-label-resting-color, var(--text-light));\n pointer-events: none;\n user-select: none;\n\n &-shown {\n animation: 0.2s multiSelectFadeIn 0.3s forwards;\n }\n\n /* Nothing to wait for without a label. Must stay after `-shown` to win the specificity tie. */\n &-instant {\n animation-delay: 0s;\n }\n }\n\n @keyframes multiSelectFadeIn {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 1;\n }\n }\n\n &-clearButton {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 22px;\n height: 22px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--text-dark);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n &:hover {\n background: var(--background-accent);\n color: var(--text-color);\n }\n }\n\n &-icon {\n flex: none;\n color: var(--input-icon-color, var(--text-dark));\n transition: transform 0.2s;\n\n &-open {\n transform: rotate(180deg);\n }\n }\n\n &-disabled {\n opacity: 0.5;\n\n .multiSelect-control {\n cursor: default;\n }\n }\n}\n\n/* Portalled, so a sibling of the field rather than a descendant, and not nestable above. */\n.multiSelect-panel {\n display: flex;\n flex-direction: column;\n gap: 6px;\n min-width: 180px;\n}\n\n.multiSelect-search {\n flex: none;\n}\n\n.multiSelect-options {\n display: flex;\n flex-direction: column;\n max-height: var(--multi-select-panel-height, 260px);\n overflow-y: auto;\n}\n\n.multiSelect-option {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 10px;\n border-radius: var(--default-border-radius);\n color: var(--text-color);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n &-active {\n background: var(--background-accent);\n }\n\n &-selected {\n color: var(--primary-color);\n font-weight: 600;\n }\n\n &-disabled {\n opacity: 0.45;\n cursor: default;\n pointer-events: none;\n }\n}\n\n.multiSelect-optionCheck {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: none;\n width: 1em;\n color: var(--primary-color);\n}\n\n.multiSelect-optionLabel {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.multiSelect-empty {\n padding: 12px 10px;\n color: var(--text-dark);\n text-align: center;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .multiSelect-label,\n .multiSelect-icon {\n transition: none;\n }\n}\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"otpInput.module.mjs","names":[],"sources":["../../../../package/common/otpInput/otpInput.module.pcss"],"sourcesContent":["@keyframes otpInputShake {\n 10%,\n 90% {\n transform: translateX(-1px);\n }\n\n 20%,\n 80% {\n transform: translateX(2px);\n }\n\n 30%,\n 50%,\n 70% {\n transform: translateX(-4px);\n }\n\n 40%,\n 60% {\n transform: translateX(4px);\n }\n}\n\n.otpInput {\n display: inline-flex;\n flex-direction: column;\n row-gap: 0.5em;\n\n &-label {\n font-size: var(--default-font-size);\n color: var(--text-color);\n }\n\n &-requiredMark {\n color: var(--required-mark-color, var(--error-color));\n }\n\n &-boxes {\n display: flex;\n align-items: center;\n column-gap: 0.5em;\n\n &-shake {\n animation: otpInputShake 0.4s ease-in-out;\n\n @media (prefers-reduced-motion: reduce) {\n animation: none;\n }\n }\n }\n\n &-boxWrapper {\n display: flex;\n align-items: center;\n column-gap: 0.5em;\n }\n\n /* These em are against this box's OWN 1.25em font, not the root's: 40px and 48px of 20px. */\n &-box {\n width: 2em;\n height: 2.4em;\n text-align: center;\n font-size: 1.25em;\n font-weight: 600;\n font-family: var(--themed-font, inherit);\n color: var(--text-color);\n background: var(--input-background, var(--background));\n border: 0.075em solid var(--border-color);\n border-radius: var(--default-border-radius);\n outline: none;\n transition:\n border-color 0.15s,\n box-shadow 0.15s;\n\n &:focus {\n border-color: var(--focus-border-color, var(--primary-color));\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus-border-color, var(--primary-color)) 20%, transparent);\n }\n\n &-filled {\n border-color: var(--primary-lighter, var(--border-color));\n }\n\n &-error {\n border-color: var(--error-color);\n\n &:focus {\n border-color: var(--error-color);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--error-color) 20%, transparent);\n }\n }\n\n &:disabled {\n color: var(--disabled-font-color);\n background: var(--disabled-input-color);\n cursor: not-allowed;\n }\n }\n\n &-separator {\n width: 0.625em;\n height: 0.09375em;\n background: var(--border-color);\n flex-shrink: 0;\n }\n\n &-disabled &-label {\n color: var(--disabled-font-color);\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"otpInput.module.mjs","names":[],"sources":["../../../../package/common/otpInput/otpInput.module.pcss"],"sourcesContent":["@keyframes otpInputShake {\n 10%,\n 90% {\n transform: translateX(-1px);\n }\n\n 20%,\n 80% {\n transform: translateX(2px);\n }\n\n 30%,\n 50%,\n 70% {\n transform: translateX(-4px);\n }\n\n 40%,\n 60% {\n transform: translateX(4px);\n }\n}\n\n.otpInput {\n display: inline-flex;\n flex-direction: column;\n row-gap: 0.5em;\n\n &-label {\n font-size: var(--default-font-size);\n color: var(--text-color);\n }\n\n &-requiredMark {\n color: var(--required-mark-color, var(--error-color));\n }\n\n &-boxes {\n display: flex;\n align-items: center;\n column-gap: 0.5em;\n\n &-shake {\n animation: otpInputShake 0.4s ease-in-out;\n\n @media (prefers-reduced-motion: reduce) {\n animation: none;\n }\n }\n }\n\n &-boxWrapper {\n display: flex;\n align-items: center;\n column-gap: 0.5em;\n }\n\n /* These em are against this box's OWN 1.25em font, not the root's: 40px and 48px of 20px. */\n &-box {\n width: 2em;\n height: 2.4em;\n text-align: center;\n font-size: 1.25em;\n font-weight: 600;\n font-family: var(--themed-font, inherit);\n color: var(--text-color);\n background: var(--input-background, var(--background));\n border: 0.075em solid var(--border-color);\n border-radius: var(--default-border-radius);\n outline: none;\n transition:\n border-color 0.15s,\n box-shadow 0.15s;\n\n &:focus {\n border-color: var(--focus-border-color, var(--primary-color));\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--focus-border-color, var(--primary-color)) 20%, transparent);\n }\n\n &-filled {\n border-color: var(--primary-lighter, var(--border-color));\n }\n\n &-error {\n border-color: var(--error-color);\n\n &:focus {\n border-color: var(--error-color);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--error-color) 20%, transparent);\n }\n }\n\n &:disabled {\n color: var(--disabled-font-color);\n background: var(--disabled-input-color);\n cursor: not-allowed;\n }\n }\n\n &-separator {\n width: 0.625em;\n height: calc(1.5em / 16);\n background: var(--border-color);\n flex-shrink: 0;\n }\n\n &-disabled &-label {\n color: var(--disabled-font-color);\n }\n}\n"],"mappings":""}
@@ -1,2 +1,2 @@
1
- "use client";import{isHiddenByAnyAncestor as e}from"../utils/scrollAncestors.mjs";import{getVisualViewportRect as t}from"../utils/visualViewport.mjs";import{useAnchorTracking as n}from"../hooks/useAnchorTracking.mjs";import{PopoverAlign as r,PopoverPlacement as i}from"./popover.types.mjs";var a={[i.Top]:`bottom`,[i.Bottom]:`top`,[i.Left]:`right`,[i.Right]:`left`},o=8,s=160,c=3;function l(e){let t=e;for(let e=0;e<=c;e++){let e=t.getBoundingClientRect();if(e.width>0||e.height>0)return e;let n=t.firstElementChild;if(!n)break;t=n}return e.getBoundingClientRect()}function u({isOpen:c,placement:u,align:d,offset:f,triggerRef:p,floatingRef:m,panelRef:h,arrowRef:g,matchTriggerWidth:_=!1,shrinkToFit:v=!1}){n({isOpen:c,triggerRef:p,elementRefs:[m,h],resizeRefs:[h],deps:[u,d,f,m,h,g,_,v],update:(n,c)=>{let p=m.current,y=h.current;if(!p||!y)return;let b=l(n);_?y.style.minWidth=`${b.width}px`:y.style.removeProperty(`min-width`),v&&y.style.removeProperty(`max-width`);let{width:x,height:S}=p.getBoundingClientRect(),C=t(),w=u===i.Top||u===i.Bottom;if(v&&w){let e=d===r.End?b.right-(C.left+o):d===r.Start?C.left+C.width-o-b.left:C.width-16,t=Math.min(e,C.width-16);x>t&&t>=s&&(y.style.maxWidth=`${t}px`,{width:x,height:S}=p.getBoundingClientRect())}let T={top:b.top-C.top,bottom:C.top+C.height-b.bottom,left:b.left-C.left,right:C.left+C.width-b.right},E=u===i.Top||u===i.Bottom?S:x,D=u,O=a[u];T[D]<E+f&&T[O]>T[D]&&(D=O);let k=D===`top`||D===`bottom`,A,j;D===`bottom`?A=b.bottom+f:D===`top`?A=b.top-f-S:j=D===`right`?b.right+f:b.left-f-x,k?(j=d===r.Start?b.left:d===r.End?b.right-x:b.left+b.width/2-x/2,j=Math.max(C.left+o,Math.min(j,C.left+C.width-x-o))):(A=d===r.Start?b.top:d===r.End?b.bottom-S:b.top+b.height/2-S/2,A=Math.max(C.top+o,Math.min(A,C.top+C.height-S-o))),p.style.top=`${A}px`,p.style.left=`${j}px`,p.style.visibility=e(b,c)?`hidden`:`visible`,p.dataset.placement=D;let M=g.current;if(M){let e=M.offsetWidth/2||5;if(k){let t=b.left+b.width/2-j;M.style.setProperty(`--arrow-x`,`${Math.max(e+2,Math.min(t,x-e-2))}px`),M.style.removeProperty(`--arrow-y`)}else{let t=b.top+b.height/2-A;M.style.setProperty(`--arrow-y`,`${Math.max(e+2,Math.min(t,S-e-2))}px`),M.style.removeProperty(`--arrow-x`)}}},teardown:()=>{let e=m.current;if(!e)return;e.style.top=``,e.style.left=``,e.style.visibility=``;let t=h.current;t?.style.removeProperty(`min-width`),t?.style.removeProperty(`max-width`)}})}export{u as usePopoverPosition};
1
+ "use client";import{isHiddenByAnyAncestor as e}from"../utils/scrollAncestors.mjs";import{getVisualViewportRect as t}from"../utils/visualViewport.mjs";import{useAnchorTracking as n}from"../hooks/useAnchorTracking.mjs";import{PopoverAlign as r,PopoverPlacement as i}from"./popover.types.mjs";import{useId as a,useRef as o}from"react";var s={[i.Top]:`bottom`,[i.Bottom]:`top`,[i.Left]:`right`,[i.Right]:`left`},c=8,l=160,u=3;function d(e){let t=e;for(let e=0;e<=u;e++){let e=t.getBoundingClientRect();if(e.width>0||e.height>0)return{element:t,rect:e};let n=t.firstElementChild;if(!n)break;t=n}return{element:null,rect:e.getBoundingClientRect()}}var f;function p(){if(f!==void 0)return f;if(typeof CSS>`u`||!CSS.supports?.(`top: anchor(top, 0px)`))return f=!1;let e=document.createElement(`div`);e.style.cssText=`position: fixed; top: 0; left: 0; transform: translateY(100px); visibility: hidden`;let t=document.createElement(`div`);t.style.cssText=`anchor-name: --popover-probe; width: 1px; height: 1px`;let n=document.createElement(`div`);return n.style.cssText=`position: fixed; position-anchor: --popover-probe; top: anchor(top, 0px); left: 0; visibility: hidden`,e.append(t),document.body.append(e,n),f=Math.abs(n.getBoundingClientRect().top-t.getBoundingClientRect().top)<1,e.remove(),n.remove(),f}function m(e){return e.style.getPropertyValue(`anchor-name`).split(`,`).map(e=>e.trim()).filter(e=>e&&e!==`none`)}function h(e,t){let n=m(e);n.includes(t)||e.style.setProperty(`anchor-name`,[...n,t].join(`, `))}function g(e,t){let n=m(e).filter(e=>e!==t);n.length>0?e.style.setProperty(`anchor-name`,n.join(`, `)):e.style.removeProperty(`anchor-name`)}function _(e,t,n,r){return r?`calc(anchor(${e}, ${n}px) + ${t-n}px)`:`${t}px`}function v({isOpen:u,placement:f,align:m,offset:v,triggerRef:y,floatingRef:b,panelRef:x,arrowRef:S,matchTriggerWidth:C=!1,shrinkToFit:w=!1}){let T=`--popover-${a().replace(/[^\w-]/g,``)}`,E=o(null);function D(e){E.current!==e&&(E.current&&g(E.current,T),e&&h(e,T),E.current=e)}n({isOpen:u,triggerRef:y,elementRefs:[b,x],resizeRefs:[x],deps:[f,m,v,b,x,S,C,w],update:(n,a)=>{let o=b.current,u=x.current;if(!o||!u)return;let{element:h,rect:g}=d(n),y=h!==null&&p();D(y?h:null),y?o.style.setProperty(`position-anchor`,T):o.style.removeProperty(`position-anchor`),C?u.style.minWidth=`${g.width}px`:u.style.removeProperty(`min-width`),w&&u.style.removeProperty(`max-width`);let{width:E,height:O}=o.getBoundingClientRect(),k=t(),A=f===i.Top||f===i.Bottom;if(w&&A){let e=m===r.End?g.right-(k.left+c):m===r.Start?k.left+k.width-c-g.left:k.width-16,t=Math.min(e,k.width-16);E>t&&t>=l&&(u.style.maxWidth=`${t}px`,{width:E,height:O}=o.getBoundingClientRect())}let j={top:g.top-k.top,bottom:k.top+k.height-g.bottom,left:g.left-k.left,right:k.left+k.width-g.right},M=f===i.Top||f===i.Bottom?O:E,N=f,P=s[f];j[N]<M+v&&j[P]>j[N]&&(N=P);let F=N===`top`||N===`bottom`,I,L;N===`bottom`?I=g.bottom+v:N===`top`?I=g.top-v-O:L=N===`right`?g.right+v:g.left-v-E;let R=!1,z=!1;if(F){L=m===r.Start?g.left:m===r.End?g.right-E:g.left+g.width/2-E/2;let e=Math.max(k.left+c,Math.min(L,k.left+k.width-E-c));R=e!==L,L=e}else{I=m===r.Start?g.top:m===r.End?g.bottom-O:g.top+g.height/2-O/2;let e=Math.max(k.top+c,Math.min(I,k.top+k.height-O-c));z=e!==I,I=e}o.style.top=_(`top`,I,g.top,y&&!z),o.style.left=_(`left`,L,g.left,y&&!R),o.style.visibility=e(g,a)?`hidden`:`visible`,o.dataset.placement=N;let B=S.current;if(B){let e=B.offsetWidth/2||5;if(F){let t=g.left+g.width/2-L;B.style.setProperty(`--arrow-x`,`${Math.max(e+2,Math.min(t,E-e-2))}px`),B.style.removeProperty(`--arrow-y`)}else{let t=g.top+g.height/2-I;B.style.setProperty(`--arrow-y`,`${Math.max(e+2,Math.min(t,O-e-2))}px`),B.style.removeProperty(`--arrow-x`)}}},teardown:()=>{D(null);let e=b.current;if(!e)return;e.style.top=``,e.style.left=``,e.style.visibility=``,e.style.removeProperty(`position-anchor`);let t=x.current;t?.style.removeProperty(`min-width`),t?.style.removeProperty(`max-width`)}})}export{p as supportsAnchoring,v as usePopoverPosition};
2
2
  //# sourceMappingURL=usePopoverPosition.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"usePopoverPosition.mjs","names":[],"sources":["../../../../package/common/popover/usePopoverPosition.ts"],"sourcesContent":["import { type RefObject } from 'react';\nimport { isHiddenByAnyAncestor } from 'components/common/utils/scrollAncestors';\nimport { getVisualViewportRect } from 'components/common/utils/visualViewport';\nimport { useAnchorTracking } from '../hooks/useAnchorTracking';\nimport { PopoverPlacement, PopoverAlign } from './popover.types';\n\ntype Axis = 'top' | 'bottom' | 'left' | 'right';\nconst OPPOSITE: Record<PopoverPlacement, Axis> = {\n [PopoverPlacement.Top]: 'bottom',\n [PopoverPlacement.Bottom]: 'top',\n [PopoverPlacement.Left]: 'right',\n [PopoverPlacement.Right]: 'left',\n};\n\nconst MARGIN = 8; // viewport gutter kept clear when shifting a panel back on-screen\n\n/**\n * Narrower than this and `shrinkToFit` gives up: a panel this size reads worse than\n * one that overhangs. Around four words a line for an error bubble, which is the\n * narrowest caller. A constant rather than an option, because the one caller that\n * shrinks has no reason to want a different floor; give it one the day a second\n * caller disagrees, as `shrinkToFit: boolean | number`.\n */\nconst MIN_SHRINK_WIDTH = 160;\n\n/** How far down to look for a box when the wrappers in between have none. */\nconst MAX_ANCHOR_DEPTH = 3;\n\n/**\n * The rect to anchor to. A trigger with a box of its own is that box, which is the\n * case for a caller that hands over a real element (`Tooltip`).\n *\n * Popover's own trigger wrapper generates no box, deliberately, so that the\n * trigger keeps the place it would have had without Popover around it. Then the\n * real trigger is inside it, and this descends to find it, past any further\n * boxless element a consumer wrapped its trigger in: such an element has a zero\n * rect at the origin, which would otherwise send the panel to the top-left corner\n * of the screen.\n */\nfunction resolveAnchorRect(trigger: HTMLElement): DOMRect {\n let el: HTMLElement = trigger;\n for (let depth = 0; depth <= MAX_ANCHOR_DEPTH; depth++) {\n const rect = el.getBoundingClientRect();\n if (rect.width > 0 || rect.height > 0) return rect;\n const child = el.firstElementChild as HTMLElement | null;\n if (!child) break;\n el = child;\n }\n return trigger.getBoundingClientRect();\n}\n\nexport interface UsePopoverPositionArgs {\n isOpen: boolean;\n placement: PopoverPlacement;\n align: PopoverAlign;\n offset: number;\n triggerRef: RefObject<HTMLElement | null>;\n /** `position: fixed` wrapper that receives top/left/visibility and the resolved-placement data attribute. */\n floatingRef: RefObject<HTMLElement | null>;\n /** Visible panel, measured to decide flip and shift. */\n panelRef: RefObject<HTMLElement | null>;\n /** Arrow element; gets `--arrow-x` / `--arrow-y` written to it. Pass a null ref when `withArrow` is false. */\n arrowRef: RefObject<HTMLElement | null>;\n /**\n * Give the panel at least the trigger's width, for a list that lines up with\n * the field it belongs to. A minimum rather than a fixed width: a menu on a\n * narrow icon button still sizes to its own content.\n */\n matchTriggerWidth?: boolean;\n /**\n * When the panel does not fit on the side its alignment puts it, narrow it\n * rather than sliding it away from the trigger. Sliding keeps the panel\n * on-screen but detaches it: an end-aligned bubble stops ending where its\n * trigger ends, and its arrow stops pointing at anything. Off by default, since\n * a panel whose width is its content, a menu or a list, would rather overhang\n * its trigger than wrap.\n *\n * Only down to `MIN_SHRINK_WIDTH`. Past that the panel is narrower than it is\n * readable, and a trigger that close to the screen edge is better served by a\n * panel that overhangs it.\n */\n shrinkToFit?: boolean;\n}\n\n/**\n * Positions a portaled, `position: fixed` popover on any of the four sides of its trigger with\n * start/center/end cross-axis alignment. Flips to the opposite side when the preferred one has no\n * room, shifts back on-screen along the cross axis, points the arrow at the trigger centre, and\n * hides (not closes) the panel once the trigger scrolls behind a clipping ancestor.\n *\n * `useAnchorTracking` supplies the lifecycle around this: mount wait, ancestor resolution, and\n * re-running on scroll, viewport resize and panel size changes.\n */\nexport function usePopoverPosition({\n isOpen,\n placement,\n align,\n offset,\n triggerRef,\n floatingRef,\n panelRef,\n arrowRef,\n matchTriggerWidth = false,\n shrinkToFit = false,\n}: UsePopoverPositionArgs) {\n useAnchorTracking({\n isOpen,\n triggerRef,\n elementRefs: [floatingRef, panelRef],\n // The panel's own size feeds the flip decision and, on the `top`/`left` sides, the coordinate\n // itself — so content that resizes while open has to be re-measured.\n resizeRefs: [panelRef],\n deps: [placement, align, offset, floatingRef, panelRef, arrowRef, matchTriggerWidth, shrinkToFit],\n\n update: (trigger, ancestors) => {\n const floating = floatingRef.current;\n const panel = panelRef.current;\n if (!floating || !panel) return;\n\n const t = resolveAnchorRect(trigger);\n // Written before the panel is measured, so `pw` below already accounts for it,\n // and taken back off when it is no longer wanted: the panel outlives the\n // switch, and an inline width would outrank whatever it switched to.\n if (matchTriggerWidth) panel.style.minWidth = `${t.width}px`;\n else panel.style.removeProperty('min-width');\n // The wrapper's box, not the panel's: `offsetWidth` rounds to whole pixels, so\n // aligning a panel 165.4px wide against a trigger edge at 406.47 left it 0.4px\n // past that edge. The panel's own rect is fractional but carries the entrance\n // animation's scale; the wrapper is the same layout box without the transform.\n // Cleared before measuring, or a cap left over from a narrower screen would be\n // read back as the panel's own width and never widen again.\n if (shrinkToFit) panel.style.removeProperty('max-width');\n let { width: pw, height: ph } = floating.getBoundingClientRect();\n // The visible area, not the full layout viewport — shrinks on mobile when a keyboard is\n // open, so the panel flips/clamps against what's actually on screen instead of ending up\n // hidden behind the keyboard.\n const vv = getVisualViewportRect();\n\n // Flipping only swaps a side for its opposite, so which axis the alignment runs\n // along is decided by the preferred placement alone, before any of that.\n const alignsHorizontally = placement === PopoverPlacement.Top || placement === PopoverPlacement.Bottom;\n\n if (shrinkToFit && alignsHorizontally) {\n const alignedSide =\n align === PopoverAlign.End\n ? t.right - (vv.left + MARGIN)\n : align === PopoverAlign.Start\n ? vv.left + vv.width - MARGIN - t.left\n : vv.width - 2 * MARGIN;\n const room = Math.min(alignedSide, vv.width - 2 * MARGIN);\n if (pw > room && room >= MIN_SHRINK_WIDTH) {\n panel.style.maxWidth = `${room}px`;\n // Wrapping makes it taller, and the height is what the flip below is decided on.\n ({ width: pw, height: ph } = floating.getBoundingClientRect());\n }\n }\n\n // Resolve the main-axis side, flipping when the preferred side lacks room and the\n // opposite side has more.\n const room: Record<Axis, number> = {\n top: t.top - vv.top,\n bottom: vv.top + vv.height - t.bottom,\n left: t.left - vv.left,\n right: vv.left + vv.width - t.right,\n };\n const need = placement === PopoverPlacement.Top || placement === PopoverPlacement.Bottom ? ph : pw;\n let side: Axis = placement as unknown as Axis;\n const opp = OPPOSITE[placement];\n if (room[side] < need + offset && room[opp] > room[side]) side = opp;\n\n const vertical = side === 'top' || side === 'bottom';\n\n // Main axis: place the panel just outside the trigger on the resolved side.\n let top: number;\n let left: number;\n if (side === 'bottom') {\n top = t.bottom + offset;\n } else if (side === 'top') {\n top = t.top - offset - ph;\n } else if (side === 'right') {\n left = t.right + offset;\n } else {\n left = t.left - offset - pw;\n }\n\n // Cross axis: align start/center/end against the trigger, then shift on-screen.\n if (vertical) {\n if (align === PopoverAlign.Start) left = t.left;\n else if (align === PopoverAlign.End) left = t.right - pw;\n else left = t.left + t.width / 2 - pw / 2;\n left = Math.max(vv.left + MARGIN, Math.min(left!, vv.left + vv.width - pw - MARGIN));\n } else {\n if (align === PopoverAlign.Start) top = t.top;\n else if (align === PopoverAlign.End) top = t.bottom - ph;\n else top = t.top + t.height / 2 - ph / 2;\n top = Math.max(vv.top + MARGIN, Math.min(top!, vv.top + vv.height - ph - MARGIN));\n }\n\n floating.style.top = `${top!}px`;\n floating.style.left = `${left!}px`;\n floating.style.visibility = isHiddenByAnyAncestor(t, ancestors) ? 'hidden' : 'visible';\n floating.dataset.placement = side;\n\n // Arrow: centre it on the trigger along the cross axis, clamped inside the panel edges.\n const arrow = arrowRef.current;\n if (arrow) {\n const half = arrow.offsetWidth / 2 || 5;\n if (vertical) {\n const x = t.left + t.width / 2 - left!;\n arrow.style.setProperty('--arrow-x', `${Math.max(half + 2, Math.min(x, pw - half - 2))}px`);\n arrow.style.removeProperty('--arrow-y');\n } else {\n const y = t.top + t.height / 2 - top!;\n arrow.style.setProperty('--arrow-y', `${Math.max(half + 2, Math.min(y, ph - half - 2))}px`);\n arrow.style.removeProperty('--arrow-x');\n }\n }\n },\n\n teardown: () => {\n // Positioning is written as inline styles, which outrank any class the panel might\n // switch to while it stays mounted — a popover turning into a bottom sheet (`useSheet`)\n // would keep the coordinates computed for its anchored self.\n const floating = floatingRef.current;\n if (!floating) return;\n floating.style.top = '';\n floating.style.left = '';\n floating.style.visibility = '';\n const panel = panelRef.current;\n panel?.style.removeProperty('min-width');\n panel?.style.removeProperty('max-width');\n },\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"usePopoverPosition.mjs","names":[],"sources":["../../../../package/common/popover/usePopoverPosition.ts"],"sourcesContent":["import { useId, useRef, type RefObject } from 'react';\nimport { isHiddenByAnyAncestor } from 'components/common/utils/scrollAncestors';\nimport { getVisualViewportRect } from 'components/common/utils/visualViewport';\nimport { useAnchorTracking } from '../hooks/useAnchorTracking';\nimport { PopoverPlacement, PopoverAlign } from './popover.types';\n\ntype Axis = 'top' | 'bottom' | 'left' | 'right';\nconst OPPOSITE: Record<PopoverPlacement, Axis> = {\n [PopoverPlacement.Top]: 'bottom',\n [PopoverPlacement.Bottom]: 'top',\n [PopoverPlacement.Left]: 'right',\n [PopoverPlacement.Right]: 'left',\n};\n\nconst MARGIN = 8; // viewport gutter kept clear when shifting a panel back on-screen\n\n/**\n * Narrower than this and `shrinkToFit` gives up: a panel this size reads worse than\n * one that overhangs. Around four words a line for an error bubble, which is the\n * narrowest caller. A constant rather than an option, because the one caller that\n * shrinks has no reason to want a different floor; give it one the day a second\n * caller disagrees, as `shrinkToFit: boolean | number`.\n */\nconst MIN_SHRINK_WIDTH = 160;\n\n/** How far down to look for a box when the wrappers in between have none. */\nconst MAX_ANCHOR_DEPTH = 3;\n\n/**\n * The element to anchor to. A trigger with a box of its own is that element, which\n * is the case for a caller that hands over a real element (`Tooltip`).\n *\n * Popover's own trigger wrapper generates no box, deliberately, so that the\n * trigger keeps the place it would have had without Popover around it. Then the\n * real trigger is inside it, and this descends to find it, past any further\n * boxless element a consumer wrapped its trigger in: such an element has a zero\n * rect at the origin, which would otherwise send the panel to the top-left corner\n * of the screen.\n */\nfunction resolveAnchor(trigger: HTMLElement): { element: HTMLElement | null; rect: DOMRect } {\n let el: HTMLElement = trigger;\n for (let depth = 0; depth <= MAX_ANCHOR_DEPTH; depth++) {\n const rect = el.getBoundingClientRect();\n if (rect.width > 0 || rect.height > 0) return { element: el, rect };\n const child = el.firstElementChild as HTMLElement | null;\n if (!child) break;\n el = child;\n }\n return { element: null, rect: trigger.getBoundingClientRect() };\n}\n\nlet anchorSupport: boolean | undefined;\n\n// Probed, not just parsed: Firefox 155 resolves `anchor()` without the anchor's transforms, which\n// put a Dropdown in a VirtualList row, placed by `translateY`, above the list.\nexport function supportsAnchoring(): boolean {\n if (anchorSupport !== undefined) return anchorSupport;\n if (typeof CSS === 'undefined' || !CSS.supports?.('top: anchor(top, 0px)')) return (anchorSupport = false);\n const host = document.createElement('div');\n host.style.cssText = 'position: fixed; top: 0; left: 0; transform: translateY(100px); visibility: hidden';\n const anchor = document.createElement('div');\n anchor.style.cssText = 'anchor-name: --popover-probe; width: 1px; height: 1px';\n const probe = document.createElement('div');\n probe.style.cssText =\n 'position: fixed; position-anchor: --popover-probe; top: anchor(top, 0px); left: 0; visibility: hidden';\n host.append(anchor);\n document.body.append(host, probe);\n anchorSupport = Math.abs(probe.getBoundingClientRect().top - anchor.getBoundingClientRect().top) < 1;\n host.remove();\n probe.remove();\n return anchorSupport;\n}\n\nfunction anchorNames(el: HTMLElement): string[] {\n return el.style\n .getPropertyValue('anchor-name')\n .split(',')\n .map((name) => name.trim())\n .filter((name) => name && name !== 'none');\n}\n\n// Merged, not replaced: one trigger can carry a Tooltip and a Popover at once.\nfunction addAnchorName(el: HTMLElement, name: string) {\n const names = anchorNames(el);\n if (!names.includes(name)) el.style.setProperty('anchor-name', [...names, name].join(', '));\n}\n\nfunction removeAnchorName(el: HTMLElement, name: string) {\n const names = anchorNames(el).filter((n) => n !== name);\n if (names.length > 0) el.style.setProperty('anchor-name', names.join(', '));\n else el.style.removeProperty('anchor-name');\n}\n\n// Anchored, the browser moves the panel with the scroll in the same frame; the scroll listener\n// lands a frame or more later. The pixel fallback covers an anchor the browser refuses.\nfunction inset(side: 'top' | 'left', value: number, anchorEdge: number, anchored: boolean): string {\n if (!anchored) return `${value}px`;\n return `calc(anchor(${side}, ${anchorEdge}px) + ${value - anchorEdge}px)`;\n}\n\nexport interface UsePopoverPositionArgs {\n isOpen: boolean;\n placement: PopoverPlacement;\n align: PopoverAlign;\n offset: number;\n triggerRef: RefObject<HTMLElement | null>;\n /** `position: fixed` wrapper that receives top/left/visibility and the resolved-placement data attribute. */\n floatingRef: RefObject<HTMLElement | null>;\n /** Visible panel, measured to decide flip and shift. */\n panelRef: RefObject<HTMLElement | null>;\n /** Arrow element; gets `--arrow-x` / `--arrow-y` written to it. Pass a null ref when `withArrow` is false. */\n arrowRef: RefObject<HTMLElement | null>;\n /**\n * Give the panel at least the trigger's width, for a list that lines up with\n * the field it belongs to. A minimum rather than a fixed width: a menu on a\n * narrow icon button still sizes to its own content.\n */\n matchTriggerWidth?: boolean;\n /**\n * When the panel does not fit on the side its alignment puts it, narrow it\n * rather than sliding it away from the trigger. Sliding keeps the panel\n * on-screen but detaches it: an end-aligned bubble stops ending where its\n * trigger ends, and its arrow stops pointing at anything. Off by default, since\n * a panel whose width is its content, a menu or a list, would rather overhang\n * its trigger than wrap.\n *\n * Only down to `MIN_SHRINK_WIDTH`. Past that the panel is narrower than it is\n * readable, and a trigger that close to the screen edge is better served by a\n * panel that overhangs it.\n */\n shrinkToFit?: boolean;\n}\n\n/**\n * Positions a portaled, `position: fixed` popover on any of the four sides of its trigger with\n * start/center/end cross-axis alignment. Flips to the opposite side when the preferred one has no\n * room, shifts back on-screen along the cross axis, points the arrow at the trigger centre, and\n * hides (not closes) the panel once the trigger scrolls behind a clipping ancestor.\n *\n * `useAnchorTracking` supplies the lifecycle around this: mount wait, ancestor resolution, and\n * re-running on scroll, viewport resize and panel size changes.\n */\nexport function usePopoverPosition({\n isOpen,\n placement,\n align,\n offset,\n triggerRef,\n floatingRef,\n panelRef,\n arrowRef,\n matchTriggerWidth = false,\n shrinkToFit = false,\n}: UsePopoverPositionArgs) {\n const anchorName = `--popover-${useId().replace(/[^\\w-]/g, '')}`;\n const anchorRef = useRef<HTMLElement | null>(null);\n\n function setAnchor(next: HTMLElement | null) {\n if (anchorRef.current === next) return;\n if (anchorRef.current) removeAnchorName(anchorRef.current, anchorName);\n if (next) addAnchorName(next, anchorName);\n anchorRef.current = next;\n }\n\n useAnchorTracking({\n isOpen,\n triggerRef,\n elementRefs: [floatingRef, panelRef],\n // The panel's own size feeds the flip decision and, on the `top`/`left` sides, the coordinate\n // itself — so content that resizes while open has to be re-measured.\n resizeRefs: [panelRef],\n deps: [placement, align, offset, floatingRef, panelRef, arrowRef, matchTriggerWidth, shrinkToFit],\n\n update: (trigger, ancestors) => {\n const floating = floatingRef.current;\n const panel = panelRef.current;\n if (!floating || !panel) return;\n\n const { element: anchorElement, rect: t } = resolveAnchor(trigger);\n const anchored = anchorElement !== null && supportsAnchoring();\n setAnchor(anchored ? anchorElement : null);\n if (anchored) floating.style.setProperty('position-anchor', anchorName);\n else floating.style.removeProperty('position-anchor');\n // Written before the panel is measured, so `pw` below already accounts for it,\n // and taken back off when it is no longer wanted: the panel outlives the\n // switch, and an inline width would outrank whatever it switched to.\n if (matchTriggerWidth) panel.style.minWidth = `${t.width}px`;\n else panel.style.removeProperty('min-width');\n // The wrapper's box, not the panel's: `offsetWidth` rounds to whole pixels, so\n // aligning a panel 165.4px wide against a trigger edge at 406.47 left it 0.4px\n // past that edge. The panel's own rect is fractional but carries the entrance\n // animation's scale; the wrapper is the same layout box without the transform.\n // Cleared before measuring, or a cap left over from a narrower screen would be\n // read back as the panel's own width and never widen again.\n if (shrinkToFit) panel.style.removeProperty('max-width');\n let { width: pw, height: ph } = floating.getBoundingClientRect();\n // The visible area, not the full layout viewport — shrinks on mobile when a keyboard is\n // open, so the panel flips/clamps against what's actually on screen instead of ending up\n // hidden behind the keyboard.\n const vv = getVisualViewportRect();\n\n // Flipping only swaps a side for its opposite, so which axis the alignment runs\n // along is decided by the preferred placement alone, before any of that.\n const alignsHorizontally = placement === PopoverPlacement.Top || placement === PopoverPlacement.Bottom;\n\n if (shrinkToFit && alignsHorizontally) {\n const alignedSide =\n align === PopoverAlign.End\n ? t.right - (vv.left + MARGIN)\n : align === PopoverAlign.Start\n ? vv.left + vv.width - MARGIN - t.left\n : vv.width - 2 * MARGIN;\n const room = Math.min(alignedSide, vv.width - 2 * MARGIN);\n if (pw > room && room >= MIN_SHRINK_WIDTH) {\n panel.style.maxWidth = `${room}px`;\n // Wrapping makes it taller, and the height is what the flip below is decided on.\n ({ width: pw, height: ph } = floating.getBoundingClientRect());\n }\n }\n\n // Resolve the main-axis side, flipping when the preferred side lacks room and the\n // opposite side has more.\n const room: Record<Axis, number> = {\n top: t.top - vv.top,\n bottom: vv.top + vv.height - t.bottom,\n left: t.left - vv.left,\n right: vv.left + vv.width - t.right,\n };\n const need = placement === PopoverPlacement.Top || placement === PopoverPlacement.Bottom ? ph : pw;\n let side: Axis = placement as unknown as Axis;\n const opp = OPPOSITE[placement];\n if (room[side] < need + offset && room[opp] > room[side]) side = opp;\n\n const vertical = side === 'top' || side === 'bottom';\n\n // Main axis: place the panel just outside the trigger on the resolved side.\n let top: number;\n let left: number;\n if (side === 'bottom') {\n top = t.bottom + offset;\n } else if (side === 'top') {\n top = t.top - offset - ph;\n } else if (side === 'right') {\n left = t.right + offset;\n } else {\n left = t.left - offset - pw;\n }\n\n // Cross axis: align start/center/end against the trigger, then shift on-screen.\n let shiftedX = false;\n let shiftedY = false;\n if (vertical) {\n if (align === PopoverAlign.Start) left = t.left;\n else if (align === PopoverAlign.End) left = t.right - pw;\n else left = t.left + t.width / 2 - pw / 2;\n const clamped = Math.max(vv.left + MARGIN, Math.min(left!, vv.left + vv.width - pw - MARGIN));\n shiftedX = clamped !== left!;\n left = clamped;\n } else {\n if (align === PopoverAlign.Start) top = t.top;\n else if (align === PopoverAlign.End) top = t.bottom - ph;\n else top = t.top + t.height / 2 - ph / 2;\n const clamped = Math.max(vv.top + MARGIN, Math.min(top!, vv.top + vv.height - ph - MARGIN));\n shiftedY = clamped !== top!;\n top = clamped;\n }\n\n // A shifted axis is pinned to the viewport edge, so it stays in pixels: anchored, the scroll\n // would carry it off the edge until the next update pulled it back, which reads as shaking.\n floating.style.top = inset('top', top!, t.top, anchored && !shiftedY);\n floating.style.left = inset('left', left!, t.left, anchored && !shiftedX);\n floating.style.visibility = isHiddenByAnyAncestor(t, ancestors) ? 'hidden' : 'visible';\n floating.dataset.placement = side;\n\n // Arrow: centre it on the trigger along the cross axis, clamped inside the panel edges.\n const arrow = arrowRef.current;\n if (arrow) {\n const half = arrow.offsetWidth / 2 || 5;\n if (vertical) {\n const x = t.left + t.width / 2 - left!;\n arrow.style.setProperty('--arrow-x', `${Math.max(half + 2, Math.min(x, pw - half - 2))}px`);\n arrow.style.removeProperty('--arrow-y');\n } else {\n const y = t.top + t.height / 2 - top!;\n arrow.style.setProperty('--arrow-y', `${Math.max(half + 2, Math.min(y, ph - half - 2))}px`);\n arrow.style.removeProperty('--arrow-x');\n }\n }\n },\n\n teardown: () => {\n // Positioning is written as inline styles, which outrank any class the panel might\n // switch to while it stays mounted — a popover turning into a bottom sheet (`useSheet`)\n // would keep the coordinates computed for its anchored self.\n setAnchor(null);\n const floating = floatingRef.current;\n if (!floating) return;\n floating.style.top = '';\n floating.style.left = '';\n floating.style.visibility = '';\n floating.style.removeProperty('position-anchor');\n const panel = panelRef.current;\n panel?.style.removeProperty('min-width');\n panel?.style.removeProperty('max-width');\n },\n });\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";import{stickyStack as e}from"./stickyStack.mjs";import t from"./sticky.module.mjs";import{useCallback as n,useEffect as r,useLayoutEffect as i,useRef as a,useState as o}from"react";import s from"classnames";import{jsx as c,jsxs as l}from"react/jsx-runtime";function u(e){let t=e?.parentElement??null;for(;t&&t!==document.body;){let{overflowY:e}=getComputedStyle(t);if(e===`auto`||e===`scroll`||e===`overlay`)return t;t=t.parentElement}return null}function d({children:d,offsetTop:f=0,offsetElement:p,stickToScrollParent:m=!1,zIndex:h=20,disabled:g=!1,onStuckChange:_,className:v,style:y,classNames:b,styles:x,...S}){let C=a(null),w=a(null),T=a(null),E=a(null),[D,O]=o({width:0,left:0,height:0}),[k,A]=o(null),[j,M]=o(0),N=p?.current?k??p.current.offsetHeight:f,[P,F]=o({isStuck:!1,top:N}),I=a(_);r(()=>{I.current=_},[_]);let L=n(()=>{let t=C.current,n=w.current;if(!t||!n)return;let r=t.getBoundingClientRect();O({width:t.clientWidth,left:r.left,height:n.offsetHeight}),e.schedule()},[]);r(()=>{let e=p?.current;if(!e)return;let t=()=>A(e.offsetHeight);t();let n=new ResizeObserver(t);return n.observe(e),()=>n.disconnect()},[p]),i(()=>{E.current=m?u(C.current):null},[m]),i(()=>{let t=e.register({node:C.current,offsetTop:N,disabled:g,getMetrics:()=>{let e=C.current,t=w.current;if(!e||!t)return null;let n=e.getBoundingClientRect(),r=E.current?.getBoundingClientRect(),i=r?.top??0;return M(e=>e===i?e:i),{top:r?n.top-r.top:n.top,left:r?n.left-r.left:n.left,width:e.clientWidth,contentHeight:t.offsetHeight,container:E.current}}},F);return T.current=t,()=>{e.unregister(t),T.current=null}},[]),r(()=>{T.current&&e.updateEntry(T.current,{offsetTop:N,disabled:g}),e.schedule()},[N,g]),i(()=>{L()},[L,d]),r(()=>{let e=new ResizeObserver(L);return C.current&&e.observe(C.current),w.current&&e.observe(w.current),E.current&&e.observe(E.current),()=>e.disconnect()},[L,m]);let R=a(!0);r(()=>{if(R.current){R.current=!1;return}I.current?.(P.isStuck)},[P.isStuck]);let z=P.isStuck?{position:`fixed`,top:j+P.top,left:D.left,width:D.width,zIndex:h,...x?.content}:x?.content;return l(`div`,{ref:C,className:s(t.sticky,v,b?.root),style:{...x?.root,...y},...S,children:[P.isStuck&&c(`div`,{"aria-hidden":!0,className:s(t.stickyPlaceholder,b?.placeholder),style:{height:D.height,...x?.placeholder}}),c(`div`,{ref:w,className:s(t.stickyContent,P.isStuck&&t.stickyContentStuck,b?.content),style:z,children:d})]})}export{d as default};
1
+ "use client";import{getScrollParent as e}from"../utils/scrollAncestors.mjs";import{stickyStack as t}from"./stickyStack.mjs";import n from"./sticky.module.mjs";import{useCallback as r,useEffect as i,useLayoutEffect as a,useRef as o,useState as s}from"react";import c from"classnames";import{jsx as l,jsxs as u}from"react/jsx-runtime";function d({children:d,offsetTop:f=0,offsetElement:p,stickToScrollParent:m=!1,boundary:h,boundaryInset:g=0,zIndex:_=20,disabled:v=!1,onStuckChange:y,className:b,style:x,classNames:S,styles:C,...w}){let T=o(null),E=o(null),D=o(null),O=o(null),[k,A]=s({width:0,left:0,height:0}),[j,M]=s(null),[N,P]=s(0),F=p?.current?j??p.current.offsetHeight:f,[I,L]=s({isStuck:!1,top:F}),R=o(y);i(()=>{R.current=y},[y]);let z=o(h),B=o(g);a(()=>{z.current=h,B.current=g});let V=r(()=>{let e=T.current,n=E.current;if(!e||!n)return;let r=e.getBoundingClientRect();A({width:e.clientWidth,left:r.left,height:n.offsetHeight}),t.schedule()},[]);i(()=>{let e=p?.current;if(!e)return;let t=()=>M(e.offsetHeight);t();let n=new ResizeObserver(t);return n.observe(e),()=>n.disconnect()},[p]),a(()=>{O.current=m?e(T.current):null},[m]),a(()=>{let e=t.register({node:T.current,offsetTop:F,disabled:v,getMetrics:()=>{let e=T.current,t=E.current;if(!e||!t)return null;let n=e.getBoundingClientRect(),r=O.current?.getBoundingClientRect(),i=r?.top??0;P(e=>e===i?e:i);let a=r?n.top-r.top:n.top,o=r?n.left-r.left:n.left,s=z.current?.current??null,c=s?.getBoundingClientRect();return{top:a,left:o,width:e.clientWidth,contentHeight:t.offsetHeight,container:O.current,boundary:s,boundaryBottom:c?c.bottom-i-B.current:null}}},L);return D.current=e,()=>{t.unregister(e),D.current=null}},[]),i(()=>{D.current&&t.updateEntry(D.current,{offsetTop:F,disabled:v}),t.schedule()},[F,v]),a(()=>{V()},[V,d]),i(()=>{let e=new ResizeObserver(V);return T.current&&e.observe(T.current),E.current&&e.observe(E.current),O.current&&e.observe(O.current),()=>e.disconnect()},[V,m]);let H=o(!0);i(()=>{if(H.current){H.current=!1;return}R.current?.(I.isStuck)},[I.isStuck]);let U=I.isStuck?{position:`fixed`,top:N+I.top,left:k.left,width:k.width,zIndex:_,...C?.content}:C?.content;return u(`div`,{ref:T,className:c(n.sticky,b,S?.root),style:{...C?.root,...x},...w,children:[I.isStuck&&l(`div`,{"aria-hidden":!0,className:c(n.stickyPlaceholder,S?.placeholder),style:{height:k.height,...C?.placeholder}}),l(`div`,{ref:E,className:c(n.stickyContent,I.isStuck&&n.stickyContentStuck,S?.content),style:U,children:d})]})}export{d as default};
2
2
  //# sourceMappingURL=sticky.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"sticky.mjs","names":[],"sources":["../../../../package/common/sticky/sticky.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport type { StickyProps } from './sticky.types';\nimport { stickyStack } from './stickyStack';\nimport type { StickyStackResult } from './stickyStack';\n\nimport styles from './sticky.module.pcss';\n\ninterface Metrics {\n width: number;\n left: number;\n height: number;\n}\n\n// Nearest ancestor whose own scrollbar would carry the sticky's scroll — used so\n// `stickToScrollParent` can pin relative to that container's box instead of the viewport.\nfunction getScrollParent(el: HTMLElement | null): HTMLElement | null {\n let node = el?.parentElement ?? null;\n while (node && node !== document.body) {\n const { overflowY } = getComputedStyle(node);\n if (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay') return node;\n node = node.parentElement;\n }\n return null;\n}\n\nexport default function Sticky({\n children,\n offsetTop = 0,\n offsetElement,\n stickToScrollParent = false,\n zIndex = 20,\n disabled = false,\n onStuckChange,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n}: StickyProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const stackIdRef = useRef<symbol | null>(null);\n const scrollParentRef = useRef<HTMLElement | null>(null);\n const [metrics, setMetrics] = useState<Metrics>({ width: 0, left: 0, height: 0 });\n const [measuredOffset, setMeasuredOffset] = useState<number | null>(null);\n const [scrollParentTop, setScrollParentTop] = useState(0);\n const effectiveOffsetTop = offsetElement?.current\n ? (measuredOffset ?? offsetElement.current.offsetHeight)\n : offsetTop;\n const [stackResult, setStackResult] = useState<StickyStackResult>({ isStuck: false, top: effectiveOffsetTop });\n\n const onStuckChangeRef = useRef(onStuckChange);\n useEffect(() => {\n onStuckChangeRef.current = onStuckChange;\n }, [onStuckChange]);\n\n // Local geometry (width/left/height) doesn't depend on other Stickies — only the\n // effective `top` and `isStuck` (from the stack coordinator) do.\n const measure = useCallback(() => {\n const root = rootRef.current;\n const content = contentRef.current;\n if (!root || !content) return;\n const rootRect = root.getBoundingClientRect();\n setMetrics({ width: root.clientWidth, left: rootRect.left, height: content.offsetHeight });\n stickyStack.schedule();\n }, []);\n\n useEffect(() => {\n const el = offsetElement?.current;\n if (!el) return;\n const updateFromElement = () => setMeasuredOffset(el.offsetHeight);\n updateFromElement();\n const ro = new ResizeObserver(updateFromElement);\n ro.observe(el);\n return () => ro.disconnect();\n }, [offsetElement]);\n\n useLayoutEffect(() => {\n scrollParentRef.current = stickToScrollParent ? getScrollParent(rootRef.current) : null;\n }, [stickToScrollParent]);\n\n useLayoutEffect(() => {\n const id = stickyStack.register(\n {\n node: rootRef.current as HTMLElement,\n offsetTop: effectiveOffsetTop,\n disabled,\n getMetrics: () => {\n const root = rootRef.current;\n const content = contentRef.current;\n if (!root || !content) return null;\n const rect = root.getBoundingClientRect();\n const parentRect = scrollParentRef.current?.getBoundingClientRect();\n const newParentTop = parentRect?.top ?? 0;\n setScrollParentTop((prev) => (prev === newParentTop ? prev : newParentTop));\n // Report top/left relative to the scroll parent's box so the stack's stuck/stacking\n // math stays in the same coordinate space regardless of what it's pinned relative to.\n const top = parentRect ? rect.top - parentRect.top : rect.top;\n const left = parentRect ? rect.left - parentRect.left : rect.left;\n return {\n top,\n left,\n width: root.clientWidth,\n contentHeight: content.offsetHeight,\n container: scrollParentRef.current,\n };\n },\n },\n setStackResult,\n );\n stackIdRef.current = id;\n return () => {\n stickyStack.unregister(id);\n stackIdRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n if (stackIdRef.current) {\n stickyStack.updateEntry(stackIdRef.current, { offsetTop: effectiveOffsetTop, disabled });\n }\n stickyStack.schedule();\n }, [effectiveOffsetTop, disabled]);\n\n useLayoutEffect(() => {\n measure();\n }, [measure, children]);\n\n useEffect(() => {\n const ro = new ResizeObserver(measure);\n if (rootRef.current) ro.observe(rootRef.current);\n if (contentRef.current) ro.observe(contentRef.current);\n if (scrollParentRef.current) ro.observe(scrollParentRef.current);\n return () => ro.disconnect();\n }, [measure, stickToScrollParent]);\n\n const firstRun = useRef(true);\n useEffect(() => {\n if (firstRun.current) {\n firstRun.current = false;\n return;\n }\n onStuckChangeRef.current?.(stackResult.isStuck);\n }, [stackResult.isStuck]);\n\n // stackResult.top is relative to the scroll parent's box (or the viewport if there isn't\n // one) — convert back to a viewport-absolute value for the fixed `top`.\n const contentStyle: React.CSSProperties | undefined = stackResult.isStuck\n ? {\n position: 'fixed',\n top: scrollParentTop + stackResult.top,\n left: metrics.left,\n width: metrics.width,\n zIndex,\n ...slotStyles?.content,\n }\n : slotStyles?.content;\n\n return (\n <div\n ref={rootRef}\n className={cx(styles.sticky, className, classNames?.root)}\n style={{ ...slotStyles?.root, ...style }}\n {...rest}\n >\n {stackResult.isStuck && (\n <div\n aria-hidden\n className={cx(styles.stickyPlaceholder, classNames?.placeholder)}\n style={{ height: metrics.height, ...slotStyles?.placeholder }}\n />\n )}\n <div\n ref={contentRef}\n className={cx(styles.stickyContent, stackResult.isStuck && styles.stickyContentStuck, classNames?.content)}\n style={contentStyle}\n >\n {children}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sticky.mjs","names":[],"sources":["../../../../package/common/sticky/sticky.tsx"],"sourcesContent":["import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';\nimport cx from 'classnames';\nimport type { StickyProps } from './sticky.types';\nimport { stickyStack } from './stickyStack';\nimport type { StickyStackResult } from './stickyStack';\nimport { getScrollParent } from '../utils/scrollAncestors';\n\nimport styles from './sticky.module.pcss';\n\ninterface Metrics {\n width: number;\n left: number;\n height: number;\n}\n\nexport default function Sticky({\n children,\n offsetTop = 0,\n offsetElement,\n stickToScrollParent = false,\n boundary,\n boundaryInset = 0,\n zIndex = 20,\n disabled = false,\n onStuckChange,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n}: StickyProps) {\n const rootRef = useRef<HTMLDivElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const stackIdRef = useRef<symbol | null>(null);\n const scrollParentRef = useRef<HTMLElement | null>(null);\n const [metrics, setMetrics] = useState<Metrics>({ width: 0, left: 0, height: 0 });\n const [measuredOffset, setMeasuredOffset] = useState<number | null>(null);\n const [scrollParentTop, setScrollParentTop] = useState(0);\n const effectiveOffsetTop = offsetElement?.current\n ? (measuredOffset ?? offsetElement.current.offsetHeight)\n : offsetTop;\n const [stackResult, setStackResult] = useState<StickyStackResult>({ isStuck: false, top: effectiveOffsetTop });\n\n const onStuckChangeRef = useRef(onStuckChange);\n useEffect(() => {\n onStuckChangeRef.current = onStuckChange;\n }, [onStuckChange]);\n\n // The stack entry is registered once, so the boundary reaches it through a ref\n // rather than the closure: the one handed in may still be empty on first render.\n const boundaryRef = useRef(boundary);\n const boundaryInsetRef = useRef(boundaryInset);\n useLayoutEffect(() => {\n boundaryRef.current = boundary;\n boundaryInsetRef.current = boundaryInset;\n });\n\n // Local geometry (width/left/height) doesn't depend on other Stickies — only the\n // effective `top` and `isStuck` (from the stack coordinator) do.\n const measure = useCallback(() => {\n const root = rootRef.current;\n const content = contentRef.current;\n if (!root || !content) return;\n const rootRect = root.getBoundingClientRect();\n setMetrics({ width: root.clientWidth, left: rootRect.left, height: content.offsetHeight });\n stickyStack.schedule();\n }, []);\n\n useEffect(() => {\n const el = offsetElement?.current;\n if (!el) return;\n const updateFromElement = () => setMeasuredOffset(el.offsetHeight);\n updateFromElement();\n const ro = new ResizeObserver(updateFromElement);\n ro.observe(el);\n return () => ro.disconnect();\n }, [offsetElement]);\n\n useLayoutEffect(() => {\n scrollParentRef.current = stickToScrollParent ? getScrollParent(rootRef.current) : null;\n }, [stickToScrollParent]);\n\n useLayoutEffect(() => {\n const id = stickyStack.register(\n {\n node: rootRef.current as HTMLElement,\n offsetTop: effectiveOffsetTop,\n disabled,\n getMetrics: () => {\n const root = rootRef.current;\n const content = contentRef.current;\n if (!root || !content) return null;\n const rect = root.getBoundingClientRect();\n const parentRect = scrollParentRef.current?.getBoundingClientRect();\n const newParentTop = parentRect?.top ?? 0;\n setScrollParentTop((prev) => (prev === newParentTop ? prev : newParentTop));\n // Report top/left relative to the scroll parent's box so the stack's stuck/stacking\n // math stays in the same coordinate space regardless of what it's pinned relative to.\n const top = parentRect ? rect.top - parentRect.top : rect.top;\n const left = parentRect ? rect.left - parentRect.left : rect.left;\n const boundaryEl = boundaryRef.current?.current ?? null;\n const boundaryRect = boundaryEl?.getBoundingClientRect();\n return {\n top,\n left,\n width: root.clientWidth,\n contentHeight: content.offsetHeight,\n container: scrollParentRef.current,\n boundary: boundaryEl,\n boundaryBottom: boundaryRect ? boundaryRect.bottom - newParentTop - boundaryInsetRef.current : null,\n };\n },\n },\n setStackResult,\n );\n stackIdRef.current = id;\n return () => {\n stickyStack.unregister(id);\n stackIdRef.current = null;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n useEffect(() => {\n if (stackIdRef.current) {\n stickyStack.updateEntry(stackIdRef.current, { offsetTop: effectiveOffsetTop, disabled });\n }\n stickyStack.schedule();\n }, [effectiveOffsetTop, disabled]);\n\n useLayoutEffect(() => {\n measure();\n }, [measure, children]);\n\n useEffect(() => {\n const ro = new ResizeObserver(measure);\n if (rootRef.current) ro.observe(rootRef.current);\n if (contentRef.current) ro.observe(contentRef.current);\n if (scrollParentRef.current) ro.observe(scrollParentRef.current);\n return () => ro.disconnect();\n }, [measure, stickToScrollParent]);\n\n const firstRun = useRef(true);\n useEffect(() => {\n if (firstRun.current) {\n firstRun.current = false;\n return;\n }\n onStuckChangeRef.current?.(stackResult.isStuck);\n }, [stackResult.isStuck]);\n\n // stackResult.top is relative to the scroll parent's box (or the viewport if there isn't\n // one) — convert back to a viewport-absolute value for the fixed `top`.\n const contentStyle: React.CSSProperties | undefined = stackResult.isStuck\n ? {\n position: 'fixed',\n top: scrollParentTop + stackResult.top,\n left: metrics.left,\n width: metrics.width,\n zIndex,\n ...slotStyles?.content,\n }\n : slotStyles?.content;\n\n return (\n <div\n ref={rootRef}\n className={cx(styles.sticky, className, classNames?.root)}\n style={{ ...slotStyles?.root, ...style }}\n {...rest}\n >\n {stackResult.isStuck && (\n <div\n aria-hidden\n className={cx(styles.stickyPlaceholder, classNames?.placeholder)}\n style={{ height: metrics.height, ...slotStyles?.placeholder }}\n />\n )}\n <div\n ref={contentRef}\n className={cx(styles.stickyContent, stackResult.isStuck && styles.stickyContentStuck, classNames?.content)}\n style={contentStyle}\n >\n {children}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";function e(e,t,n,r){return e<r&&n<t}function t(e,t){if(e===t)return 0;let n=e.compareDocumentPosition(t);return n&Node.DOCUMENT_POSITION_FOLLOWING?-1:n&Node.DOCUMENT_POSITION_PRECEDING?1:0}var n=new class{constructor(){this.entries=new Map,this.listeners=new Map,this.results=new Map,this.rafId=0,this.listenerCount=0,this.schedule=()=>{cancelAnimationFrame(this.rafId),this.rafId=requestAnimationFrame(this.recompute)},this.recompute=()=>{let n=[...this.entries.entries()].sort(([,e],[,n])=>t(e.node,n.node)),r=new Map;for(let[t,i]of n){let n=i.getMetrics();if(!n)continue;let a=r.get(n.container);a||(a=[],r.set(n.container,a));let o=a.filter(t=>e(t.left,t.right,n.left,n.left+n.width)).reduce((e,t)=>Math.max(e,t.bottom),i.offsetTop),s=!i.disabled&&n.top<=o,c={isStuck:s,top:s?o:i.offsetTop};s&&a.push({left:n.left,right:n.left+n.width,bottom:o+n.contentHeight});let l=this.results.get(t);(!l||l.isStuck!==c.isStuck||l.top!==c.top)&&(this.results.set(t,c),this.listeners.get(t)?.(c))}}}register(e,t){let n=Symbol(`sticky`);return this.entries.set(n,e),this.listeners.set(n,t),this.startListening(),n}updateEntry(e,t){let n=this.entries.get(e);n&&Object.assign(n,t)}unregister(e){this.entries.has(e)&&(this.entries.delete(e),this.listeners.delete(e),this.results.delete(e),this.stopListening())}startListening(){this.listenerCount===0&&(window.addEventListener(`scroll`,this.schedule,{passive:!0,capture:!0}),window.addEventListener(`resize`,this.schedule)),this.listenerCount++}stopListening(){this.listenerCount--,this.listenerCount===0&&(window.removeEventListener(`scroll`,this.schedule,{capture:!0}),window.removeEventListener(`resize`,this.schedule))}};export{n as stickyStack};
1
+ "use client";function e(e,t,n,r){return e<r&&n<t}function t(e,t){if(e===t)return 0;let n=e.compareDocumentPosition(t);return n&Node.DOCUMENT_POSITION_FOLLOWING?-1:n&Node.DOCUMENT_POSITION_PRECEDING?1:0}var n=new class{constructor(){this.entries=new Map,this.listeners=new Map,this.results=new Map,this.rafId=0,this.listenerCount=0,this.schedule=()=>{cancelAnimationFrame(this.rafId),this.rafId=requestAnimationFrame(this.recompute)},this.recompute=()=>{let n=[...this.entries.entries()].sort(([,e],[,n])=>t(e.node,n.node)),r=new Map,i=[];for(let[t,a]of n){let n=a.getMetrics();if(!n)continue;let o=r.get(n.container);o||(o=[],r.set(n.container,o));let s=o.filter(t=>e(t.left,t.right,n.left,n.left+n.width)).reduce((e,t)=>Math.max(e,t.bottom),a.offsetTop),c=!a.disabled&&n.top<=s;if(c){let e=s+n.contentHeight;o.push({left:n.left,right:n.left+n.width,bottom:n.boundaryBottom==null?e:Math.min(e,n.boundaryBottom)})}i.push({id:t,metrics:n,baseTop:c?s:a.offsetTop,isStuck:c})}let a=new Map;for(let{metrics:e,baseTop:t,isStuck:n}of i){let{boundary:r,boundaryBottom:i,contentHeight:o}=e;if(!n||r==null||i==null)continue;let s=Math.min(0,i-(t+o)),c=a.get(r);(c==null||s<c)&&a.set(r,s)}for(let{id:e,metrics:t,baseTop:n,isStuck:r}of i){let i={isStuck:r,top:n+(r&&t.boundary?a.get(t.boundary)??0:0)},o=this.results.get(e);o&&o.isStuck===i.isStuck&&o.top===i.top||(this.results.set(e,i),this.listeners.get(e)?.(i))}}}register(e,t){let n=Symbol(`sticky`);return this.entries.set(n,e),this.listeners.set(n,t),this.startListening(),n}updateEntry(e,t){let n=this.entries.get(e);n&&Object.assign(n,t)}unregister(e){this.entries.has(e)&&(this.entries.delete(e),this.listeners.delete(e),this.results.delete(e),this.stopListening())}startListening(){this.listenerCount===0&&(window.addEventListener(`scroll`,this.schedule,{passive:!0,capture:!0}),window.addEventListener(`resize`,this.schedule)),this.listenerCount++}stopListening(){this.listenerCount--,this.listenerCount===0&&(window.removeEventListener(`scroll`,this.schedule,{capture:!0}),window.removeEventListener(`resize`,this.schedule))}};export{n as stickyStack};
2
2
  //# sourceMappingURL=stickyStack.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"stickyStack.mjs","names":[],"sources":["../../../../package/common/sticky/stickyStack.ts"],"sourcesContent":["/**\n * Coordinates every mounted `Sticky` so ones that overlap horizontally stack\n * underneath each other (in DOM order) instead of pinning to the same spot.\n * Non-overlapping ones (e.g. side-by-side halves under a full-width one) land\n * at the same top instead of being pushed down by each other.\n */\n\nexport interface StickyStackMetrics {\n /** Current (unstuck-flow) top of the root element, from getBoundingClientRect. */\n top: number;\n left: number;\n width: number;\n contentHeight: number;\n /** The element this entry sticks relative to (its scroll parent), or null for the viewport.\n * Stacking only considers overlap between entries sharing the same container — two Stickies\n * in unrelated scroll panels should never push each other down just because they happen to\n * share horizontal DOM coordinates. */\n container: HTMLElement | null;\n}\n\nexport interface StickyStackEntry {\n node: HTMLElement;\n offsetTop: number;\n disabled: boolean;\n getMetrics: () => StickyStackMetrics | null;\n}\n\nexport interface StickyStackResult {\n isStuck: boolean;\n /** Effective `top` to pin at while stuck — offsetTop plus the height of any stuck, overlapping entries above it. */\n top: number;\n}\n\ntype ResultListener = (result: StickyStackResult) => void;\n\nfunction rangesOverlap(aLeft: number, aRight: number, bLeft: number, bRight: number): boolean {\n return aLeft < bRight && bLeft < aRight;\n}\n\nfunction compareDomOrder(a: HTMLElement, b: HTMLElement): number {\n if (a === b) return 0;\n const position = a.compareDocumentPosition(b);\n if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1;\n if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;\n return 0;\n}\n\nclass StickyStack {\n private entries = new Map<symbol, StickyStackEntry>();\n private listeners = new Map<symbol, ResultListener>();\n private results = new Map<symbol, StickyStackResult>();\n private rafId = 0;\n private listenerCount = 0;\n\n schedule = (): void => {\n cancelAnimationFrame(this.rafId);\n this.rafId = requestAnimationFrame(this.recompute);\n };\n\n register(entry: StickyStackEntry, onChange: ResultListener): symbol {\n const id = Symbol('sticky');\n this.entries.set(id, entry);\n this.listeners.set(id, onChange);\n this.startListening();\n return id;\n }\n\n updateEntry(id: symbol, patch: Partial<Pick<StickyStackEntry, 'offsetTop' | 'disabled'>>): void {\n const entry = this.entries.get(id);\n if (entry) Object.assign(entry, patch);\n }\n\n unregister(id: symbol): void {\n if (!this.entries.has(id)) return;\n this.entries.delete(id);\n this.listeners.delete(id);\n this.results.delete(id);\n this.stopListening();\n }\n\n recompute = (): void => {\n const ordered = [...this.entries.entries()].sort(([, a], [, b]) => compareDomOrder(a.node, b.node));\n const placedByContainer = new Map<HTMLElement | null, { left: number; right: number; bottom: number }[]>();\n\n for (const [id, entry] of ordered) {\n const metrics = entry.getMetrics();\n if (!metrics) continue;\n\n let placed = placedByContainer.get(metrics.container);\n if (!placed) {\n placed = [];\n placedByContainer.set(metrics.container, placed);\n }\n\n const overlapping = placed.filter((p) =>\n rangesOverlap(p.left, p.right, metrics.left, metrics.left + metrics.width),\n );\n const baseTop = overlapping.reduce((max, p) => Math.max(max, p.bottom), entry.offsetTop);\n const isStuck = !entry.disabled && metrics.top <= baseTop;\n const result: StickyStackResult = { isStuck, top: isStuck ? baseTop : entry.offsetTop };\n\n if (isStuck) {\n placed.push({\n left: metrics.left,\n right: metrics.left + metrics.width,\n bottom: baseTop + metrics.contentHeight,\n });\n }\n\n const prev = this.results.get(id);\n if (!prev || prev.isStuck !== result.isStuck || prev.top !== result.top) {\n this.results.set(id, result);\n this.listeners.get(id)?.(result);\n }\n }\n };\n\n private startListening(): void {\n if (this.listenerCount === 0) {\n window.addEventListener('scroll', this.schedule, { passive: true, capture: true });\n window.addEventListener('resize', this.schedule);\n }\n this.listenerCount++;\n }\n\n private stopListening(): void {\n this.listenerCount--;\n if (this.listenerCount === 0) {\n window.removeEventListener('scroll', this.schedule, { capture: true } as EventListenerOptions);\n window.removeEventListener('resize', this.schedule);\n }\n }\n}\n\nexport const stickyStack = new StickyStack();\n"],"mappings":"aAmCA,SAAS,EAAc,EAAe,EAAgB,EAAe,EAAyB,CAC5F,OAAO,EAAQ,GAAU,EAAQ,CACnC,CAEA,SAAS,EAAgB,EAAgB,EAAwB,CAC/D,GAAI,IAAM,EAAG,MAAO,GACpB,IAAM,EAAW,EAAE,wBAAwB,CAAC,EAG5C,OAFI,EAAW,KAAK,4BAAoC,GACpD,EAAW,KAAK,4BAAoC,EACjD,CACT,CAyFA,IAAa,EAAc,IAAI,KAvFb,eACE,KAAA,QAAA,IAAI,IACF,KAAA,UAAA,IAAI,IACN,KAAA,QAAA,IAAI,IACN,KAAA,MAAA,EACQ,KAAA,cAAA,EAED,KAAA,aAAA,CACrB,qBAAqB,KAAK,KAAK,EAC/B,KAAK,MAAQ,sBAAsB,KAAK,SAAS,CACnD,EAuBwB,KAAA,cAAA,CACtB,IAAM,EAAU,CAAC,GAAG,KAAK,QAAQ,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAG,GAAI,EAAG,KAAO,EAAgB,EAAE,KAAM,EAAE,IAAI,CAAC,EAC5F,EAAoB,IAAI,IAE9B,IAAK,GAAM,CAAC,EAAI,KAAU,EAAS,CACjC,IAAM,EAAU,EAAM,WAAW,EACjC,GAAI,CAAC,EAAS,SAEd,IAAI,EAAS,EAAkB,IAAI,EAAQ,SAAS,EAC/C,IACH,EAAS,CAAC,EACV,EAAkB,IAAI,EAAQ,UAAW,CAAM,GAMjD,IAAM,EAHc,EAAO,OAAQ,GACjC,EAAc,EAAE,KAAM,EAAE,MAAO,EAAQ,KAAM,EAAQ,KAAO,EAAQ,KAAK,CAE3D,CAAA,CAAY,QAAQ,EAAK,IAAM,KAAK,IAAI,EAAK,EAAE,MAAM,EAAG,EAAM,SAAS,EACjF,EAAU,CAAC,EAAM,UAAY,EAAQ,KAAO,EAC5C,EAA4B,CAAE,UAAS,IAAK,EAAU,EAAU,EAAM,SAAU,EAElF,GACF,EAAO,KAAK,CACV,KAAM,EAAQ,KACd,MAAO,EAAQ,KAAO,EAAQ,MAC9B,OAAQ,EAAU,EAAQ,aAC5B,CAAC,EAGH,IAAM,EAAO,KAAK,QAAQ,IAAI,CAAE,GAC5B,CAAC,GAAQ,EAAK,UAAY,EAAO,SAAW,EAAK,MAAQ,EAAO,OAClE,KAAK,QAAQ,IAAI,EAAI,CAAM,EAC3B,KAAK,UAAU,IAAI,CAAE,CAAC,GAAG,CAAM,EAEnC,CACF,EAxDA,SAAS,EAAyB,EAAkC,CAClE,IAAM,EAAK,OAAO,QAAQ,EAI1B,OAHA,KAAK,QAAQ,IAAI,EAAI,CAAK,EAC1B,KAAK,UAAU,IAAI,EAAI,CAAQ,EAC/B,KAAK,eAAe,EACb,CACT,CAEA,YAAY,EAAY,EAAwE,CAC9F,IAAM,EAAQ,KAAK,QAAQ,IAAI,CAAE,EAC7B,GAAO,OAAO,OAAO,EAAO,CAAK,CACvC,CAEA,WAAW,EAAkB,CACtB,KAAK,QAAQ,IAAI,CAAE,IACxB,KAAK,QAAQ,OAAO,CAAE,EACtB,KAAK,UAAU,OAAO,CAAE,EACxB,KAAK,QAAQ,OAAO,CAAE,EACtB,KAAK,cAAc,EACrB,CAuCA,gBAA+B,CACzB,KAAK,gBAAkB,IACzB,OAAO,iBAAiB,SAAU,KAAK,SAAU,CAAE,QAAS,GAAM,QAAS,EAAK,CAAC,EACjF,OAAO,iBAAiB,SAAU,KAAK,QAAQ,GAEjD,KAAK,eACP,CAEA,eAA8B,CAC5B,KAAK,gBACD,KAAK,gBAAkB,IACzB,OAAO,oBAAoB,SAAU,KAAK,SAAU,CAAE,QAAS,EAAK,CAAyB,EAC7F,OAAO,oBAAoB,SAAU,KAAK,QAAQ,EAEtD,CACF"}
1
+ {"version":3,"file":"stickyStack.mjs","names":[],"sources":["../../../../package/common/sticky/stickyStack.ts"],"sourcesContent":["/**\n * Coordinates every mounted `Sticky` so ones that overlap horizontally stack\n * underneath each other (in DOM order) instead of pinning to the same spot.\n * Non-overlapping ones (e.g. side-by-side halves under a full-width one) land\n * at the same top instead of being pushed down by each other.\n */\n\nexport interface StickyStackMetrics {\n /** Current (unstuck-flow) top of the root element, from getBoundingClientRect. */\n top: number;\n left: number;\n width: number;\n contentHeight: number;\n /** The element this entry sticks relative to (its scroll parent), or null for the viewport.\n * Stacking only considers overlap between entries sharing the same container — two Stickies\n * in unrelated scroll panels should never push each other down just because they happen to\n * share horizontal DOM coordinates. */\n container: HTMLElement | null;\n /** The element the entry is confined to, or null when nothing confines it.\n * Entries sharing one leave together. */\n boundary: HTMLElement | null;\n /** Bottom edge of that element (less its inset), in the same coordinate space as `top`. */\n boundaryBottom: number | null;\n}\n\nexport interface StickyStackEntry {\n node: HTMLElement;\n offsetTop: number;\n disabled: boolean;\n getMetrics: () => StickyStackMetrics | null;\n}\n\nexport interface StickyStackResult {\n isStuck: boolean;\n /** Effective `top` to pin at while stuck — offsetTop plus the height of any stuck, overlapping entries above it. */\n top: number;\n}\n\ntype ResultListener = (result: StickyStackResult) => void;\n\nfunction rangesOverlap(aLeft: number, aRight: number, bLeft: number, bRight: number): boolean {\n return aLeft < bRight && bLeft < aRight;\n}\n\nfunction compareDomOrder(a: HTMLElement, b: HTMLElement): number {\n if (a === b) return 0;\n const position = a.compareDocumentPosition(b);\n if (position & Node.DOCUMENT_POSITION_FOLLOWING) return -1;\n if (position & Node.DOCUMENT_POSITION_PRECEDING) return 1;\n return 0;\n}\n\nclass StickyStack {\n private entries = new Map<symbol, StickyStackEntry>();\n private listeners = new Map<symbol, ResultListener>();\n private results = new Map<symbol, StickyStackResult>();\n private rafId = 0;\n private listenerCount = 0;\n\n schedule = (): void => {\n cancelAnimationFrame(this.rafId);\n this.rafId = requestAnimationFrame(this.recompute);\n };\n\n register(entry: StickyStackEntry, onChange: ResultListener): symbol {\n const id = Symbol('sticky');\n this.entries.set(id, entry);\n this.listeners.set(id, onChange);\n this.startListening();\n return id;\n }\n\n updateEntry(id: symbol, patch: Partial<Pick<StickyStackEntry, 'offsetTop' | 'disabled'>>): void {\n const entry = this.entries.get(id);\n if (entry) Object.assign(entry, patch);\n }\n\n unregister(id: symbol): void {\n if (!this.entries.has(id)) return;\n this.entries.delete(id);\n this.listeners.delete(id);\n this.results.delete(id);\n this.stopListening();\n }\n\n recompute = (): void => {\n const ordered = [...this.entries.entries()].sort(([, a], [, b]) => compareDomOrder(a.node, b.node));\n const placedByContainer = new Map<HTMLElement | null, { left: number; right: number; bottom: number }[]>();\n const rows: { id: symbol; metrics: StickyStackMetrics; baseTop: number; isStuck: boolean }[] = [];\n\n for (const [id, entry] of ordered) {\n const metrics = entry.getMetrics();\n if (!metrics) continue;\n\n let placed = placedByContainer.get(metrics.container);\n if (!placed) {\n placed = [];\n placedByContainer.set(metrics.container, placed);\n }\n\n const overlapping = placed.filter((p) =>\n rangesOverlap(p.left, p.right, metrics.left, metrics.left + metrics.width),\n );\n const baseTop = overlapping.reduce((max, p) => Math.max(max, p.bottom), entry.offsetTop);\n const isStuck = !entry.disabled && metrics.top <= baseTop;\n\n if (isStuck) {\n // Never hold space below the boundary this entry is confined to: the\n // shared shift below has pulled it up to that edge, and an entry under it\n // must stack on where it sits, not on where it would sit unconfined.\n const bottom = baseTop + metrics.contentHeight;\n placed.push({\n left: metrics.left,\n right: metrics.left + metrics.width,\n bottom: metrics.boundaryBottom == null ? bottom : Math.min(bottom, metrics.boundaryBottom),\n });\n }\n rows.push({ id, metrics, baseTop: isStuck ? baseTop : entry.offsetTop, isStuck });\n }\n\n // A boundary pulls everything confined to it up by one shared amount, the\n // largest any of them needs. Letting each clamp its own bottom edge instead\n // would collapse a stacked group onto that edge, sliding the lower ones up\n // through the ones above.\n const shiftByBoundary = new Map<HTMLElement, number>();\n for (const { metrics, baseTop, isStuck } of rows) {\n const { boundary, boundaryBottom, contentHeight } = metrics;\n if (!isStuck || boundary == null || boundaryBottom == null) continue;\n const needed = Math.min(0, boundaryBottom - (baseTop + contentHeight));\n const current = shiftByBoundary.get(boundary);\n if (current == null || needed < current) shiftByBoundary.set(boundary, needed);\n }\n\n // An entry on its way out stays `isStuck`, so its placeholder keeps holding\n // the flow slot and the exit isn't a jump.\n for (const { id, metrics, baseTop, isStuck } of rows) {\n const shift = isStuck && metrics.boundary ? (shiftByBoundary.get(metrics.boundary) ?? 0) : 0;\n const result: StickyStackResult = { isStuck, top: baseTop + shift };\n const prev = this.results.get(id);\n if (prev && prev.isStuck === result.isStuck && prev.top === result.top) continue;\n this.results.set(id, result);\n this.listeners.get(id)?.(result);\n }\n };\n\n private startListening(): void {\n if (this.listenerCount === 0) {\n window.addEventListener('scroll', this.schedule, { passive: true, capture: true });\n window.addEventListener('resize', this.schedule);\n }\n this.listenerCount++;\n }\n\n private stopListening(): void {\n this.listenerCount--;\n if (this.listenerCount === 0) {\n window.removeEventListener('scroll', this.schedule, { capture: true } as EventListenerOptions);\n window.removeEventListener('resize', this.schedule);\n }\n }\n}\n\nexport const stickyStack = new StickyStack();\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";import{ThemeVariablesContext as e}from"./themeVariablesContext.mjs";import{defaultTheme as t}from"./defaultTheme.mjs";import n from"./theme.module.mjs";import r,{Component as i}from"react";import a from"classnames";import{jsx as o}from"react/jsx-runtime";var s=class extends i{constructor(...e){super(...e),this.state={resolvedVariables:{},colorFadeActive:!1,currentThemeColor:null},this.colorFadeTimeout=null,this.wrapperRef=r.createRef(),this.themeColorMeta=null,this.createdThemeColorMeta=!1,this.applyTheme=e=>{let n=this.props.themes??[t],r=n.find(e=>!e.baseThemeId)??n[0],i=r?r.variables:{},a=n.find(t=>t.id===e),o=a?this.resolveChain(a.id):[],s=Object.assign({},i,...o);this.setState({resolvedVariables:s,colorFadeActive:!0}),this.colorFadeTimeout&&clearTimeout(this.colorFadeTimeout),this.colorFadeTimeout=setTimeout(()=>{this.setState({colorFadeActive:!1}),this.colorFadeTimeout=null},500),setTimeout(()=>{this.wrapperRef.current&&this.setState({currentThemeColor:getComputedStyle(this.wrapperRef.current).getPropertyValue(`--primary-color`).trim()})},1)}}componentDidMount(){this.applyTheme(this.props.currentThemeId),this.syncThemeColorMeta(this.state.currentThemeColor??``)}componentDidUpdate(e,t){t.currentThemeColor!==this.state.currentThemeColor&&this.syncThemeColorMeta(this.state.currentThemeColor??``),(e.currentThemeId!==this.props.currentThemeId||e.themes!==this.props.themes)&&this.applyTheme(this.props.currentThemeId)}componentWillUnmount(){this.colorFadeTimeout&&clearTimeout(this.colorFadeTimeout),this.createdThemeColorMeta&&=(this.themeColorMeta?.remove(),this.themeColorMeta=null,!1)}syncThemeColorMeta(e){if(typeof document<`u`){if(!this.themeColorMeta){let e=document.head.querySelector(`meta[name="theme-color"]`);if(e)this.themeColorMeta=e;else{let e=document.createElement(`meta`);e.setAttribute(`name`,`theme-color`),document.head.appendChild(e),this.themeColorMeta=e,this.createdThemeColorMeta=!0}}this.themeColorMeta.setAttribute(`content`,e)}}resolveChain(e){let n=(this.props.themes??[t]).find(t=>t.id===e);return n?[...n.baseThemeId?this.resolveChain(n.baseThemeId):[],n.variables]:[]}render(){let{children:t}=this.props,{resolvedVariables:r,colorFadeActive:i}=this.state;return o(`div`,{ref:this.wrapperRef,id:`themeProvider`,className:a(n.themeRoot,i&&n.colorFade),style:r,children:o(e.Provider,{value:r,children:t})})}};export{s as default};
1
+ "use client";import{ThemeVariablesContext as e}from"./themeVariablesContext.mjs";import{defaultTheme as t}from"./defaultTheme.mjs";import n from"./theme.module.mjs";import r,{Component as i}from"react";import a from"classnames";import{jsx as o}from"react/jsx-runtime";function s(e,t){let n=e.find(e=>e.id===t);return n?[...n.baseThemeId?s(e,n.baseThemeId):[],n.variables]:[]}function c({themes:e=[t],currentThemeId:n}){let r=e.find(e=>!e.baseThemeId)??e[0],i=r?r.variables:{},a=n?s(e,n):[];return Object.assign({},i,...a)}var l=class extends i{constructor(...e){super(...e),this.state={resolvedVariables:c(this.props),colorFadeActive:!1,currentThemeColor:null,resolvedFrom:{themes:this.props.themes,currentThemeId:this.props.currentThemeId}},this.colorFadeTimeout=null,this.wrapperRef=r.createRef(),this.themeColorMeta=null,this.createdThemeColorMeta=!1}static getDerivedStateFromProps(e,t){let{themes:n,currentThemeId:r}=t.resolvedFrom;return e.themes===n&&e.currentThemeId===r?null:{resolvedVariables:c(e),colorFadeActive:!0,resolvedFrom:{themes:e.themes,currentThemeId:e.currentThemeId}}}componentDidMount(){this.readThemeColor()}componentDidUpdate(e,t){t.resolvedVariables!==this.state.resolvedVariables&&(this.readThemeColor(),this.colorFadeTimeout&&clearTimeout(this.colorFadeTimeout),this.colorFadeTimeout=setTimeout(()=>{this.setState({colorFadeActive:!1}),this.colorFadeTimeout=null},500)),t.currentThemeColor!==this.state.currentThemeColor&&this.syncThemeColorMeta(this.state.currentThemeColor??``)}componentWillUnmount(){this.colorFadeTimeout&&clearTimeout(this.colorFadeTimeout),this.createdThemeColorMeta&&=(this.themeColorMeta?.remove(),this.themeColorMeta=null,!1)}syncThemeColorMeta(e){if(typeof document<`u`){if(!this.themeColorMeta){let e=document.head.querySelector(`meta[name="theme-color"]`);if(e)this.themeColorMeta=e;else{let e=document.createElement(`meta`);e.setAttribute(`name`,`theme-color`),document.head.appendChild(e),this.themeColorMeta=e,this.createdThemeColorMeta=!0}}this.themeColorMeta.setAttribute(`content`,e)}}readThemeColor(){if(!this.wrapperRef.current)return;let e=getComputedStyle(this.wrapperRef.current).getPropertyValue(`--primary-color`).trim();e!==this.state.currentThemeColor&&this.setState({currentThemeColor:e})}render(){let{children:t}=this.props,{resolvedVariables:r,colorFadeActive:i}=this.state;return o(`div`,{ref:this.wrapperRef,id:`themeProvider`,className:a(n.themeRoot,i&&n.colorFade),style:r,children:o(e.Provider,{value:r,children:t})})}};export{l as default};
2
2
  //# sourceMappingURL=themeProvider.mjs.map