@ahrowe/ui 0.42.0 → 0.43.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 (273) hide show
  1. package/CHANGELOG.md +934 -0
  2. package/README.md +11 -10
  3. package/dist/cjs-types/common/accordion/accordion.types.d.ts +17 -2
  4. package/dist/cjs-types/common/actionButtons/actionButtons.types.d.ts +7 -1
  5. package/dist/cjs-types/common/actionIcon/actionIcon.d.ts +1 -1
  6. package/dist/cjs-types/common/actionIcon/actionIcon.types.d.ts +7 -9
  7. package/dist/cjs-types/common/alert/alert.types.d.ts +6 -8
  8. package/dist/cjs-types/common/animatedLogo/animatedLogo.types.d.ts +3 -2
  9. package/dist/cjs-types/common/badge/badge.types.d.ts +10 -6
  10. package/dist/cjs-types/common/button/button.types.d.ts +14 -4
  11. package/dist/cjs-types/common/calendar/calendar.types.d.ts +25 -10
  12. package/dist/cjs-types/common/calendar/components/agendaList/agendaList.d.ts +1 -1
  13. package/dist/cjs-types/common/calendar/components/agendaList/agendaList.types.d.ts +3 -0
  14. package/dist/cjs-types/common/calendar/components/calendarHeader/calendarHeader.d.ts +1 -1
  15. package/dist/cjs-types/common/calendar/components/calendarHeader/calendarHeader.types.d.ts +2 -0
  16. package/dist/cjs-types/common/calendar/components/compactMonth/compactMonth.d.ts +1 -1
  17. package/dist/cjs-types/common/calendar/components/monthView/monthView.d.ts +1 -1
  18. package/dist/cjs-types/common/calendar/components/monthView/monthView.types.d.ts +3 -2
  19. package/dist/cjs-types/common/calendar/components/timeGrid/timeGrid.d.ts +1 -1
  20. package/dist/cjs-types/common/calendar/components/timeGrid/timeGrid.types.d.ts +9 -1
  21. package/dist/cjs-types/common/calendar/dates.d.ts +21 -5
  22. package/dist/cjs-types/common/calendar/format.d.ts +3 -2
  23. package/dist/cjs-types/common/calendar/layout.d.ts +5 -0
  24. package/dist/cjs-types/common/calendar/useNow.d.ts +4 -0
  25. package/dist/cjs-types/common/calendar/view.types.d.ts +9 -2
  26. package/dist/cjs-types/common/card/card.types.d.ts +16 -2
  27. package/dist/cjs-types/common/chip/chip.types.d.ts +10 -5
  28. package/dist/cjs-types/common/copyButton/copyButton.types.d.ts +5 -4
  29. package/dist/cjs-types/common/fab/components/fabBase/fabBase.d.ts +1 -1
  30. package/dist/cjs-types/common/fab/components/fabBase/fabBase.types.d.ts +2 -1
  31. package/dist/cjs-types/common/progressBar/progressBar.d.ts +0 -1
  32. package/dist/cjs-types/common/progressBar/progressBar.types.d.ts +3 -1
  33. package/dist/cjs-types/common/splitButton/splitButton.types.d.ts +4 -1
  34. package/dist/cjs-types/common/styles/tone.d.ts +11 -0
  35. package/dist/cjs-types/common/themeProvider/color.types.d.ts +20 -0
  36. package/dist/cjs-types/common/themeProvider/index.d.ts +2 -0
  37. package/dist/cjs-types/common/themeProvider/theme.types.d.ts +3 -6
  38. package/dist/cjs-types/common/timeline/timeline.types.d.ts +3 -10
  39. package/dist/cjs-types/common/toast/index.d.ts +1 -1
  40. package/dist/cjs-types/common/toast/toast.types.d.ts +7 -3
  41. package/dist/cjs-types/common/types/actions.types.d.ts +1 -1
  42. package/dist/cjs-types/common/utils/dismissClick.d.ts +4 -0
  43. package/dist/cjs-types/common/utils/isDev.d.ts +1 -0
  44. package/dist/esm/common/accordion/accordion.mjs +1 -1
  45. package/dist/esm/common/accordion/accordion.mjs.map +1 -1
  46. package/dist/esm/common/accordion/accordion.module.mjs +1 -1
  47. package/dist/esm/common/accordion/accordion.module.mjs.map +1 -1
  48. package/dist/esm/common/accordion/accordion.types.mjs +1 -1
  49. package/dist/esm/common/accordion/accordion.types.mjs.map +1 -1
  50. package/dist/esm/common/actionButtons/actionButtons.mjs +1 -1
  51. package/dist/esm/common/actionButtons/actionButtons.mjs.map +1 -1
  52. package/dist/esm/common/actionIcon/actionIcon.mjs +1 -1
  53. package/dist/esm/common/actionIcon/actionIcon.mjs.map +1 -1
  54. package/dist/esm/common/actionIcon/actionIcon.module.mjs +1 -1
  55. package/dist/esm/common/actionIcon/actionIcon.module.mjs.map +1 -1
  56. package/dist/esm/common/alert/alert.mjs +1 -1
  57. package/dist/esm/common/alert/alert.mjs.map +1 -1
  58. package/dist/esm/common/alert/alert.module.mjs +1 -1
  59. package/dist/esm/common/alert/alert.module.mjs.map +1 -1
  60. package/dist/esm/common/animatedLogo/animatedLogo.mjs +1 -1
  61. package/dist/esm/common/animatedLogo/animatedLogo.mjs.map +1 -1
  62. package/dist/esm/common/avatar/avatar.mjs +1 -1
  63. package/dist/esm/common/avatar/avatar.mjs.map +1 -1
  64. package/dist/esm/common/badge/badge.mjs +1 -1
  65. package/dist/esm/common/badge/badge.mjs.map +1 -1
  66. package/dist/esm/common/badge/badge.module.mjs +1 -1
  67. package/dist/esm/common/badge/badge.module.mjs.map +1 -1
  68. package/dist/esm/common/badge/badge.types.mjs +1 -1
  69. package/dist/esm/common/badge/badge.types.mjs.map +1 -1
  70. package/dist/esm/common/bodyEnd/bodyEnd.mjs +1 -1
  71. package/dist/esm/common/bodyEnd/bodyEnd.mjs.map +1 -1
  72. package/dist/esm/common/breadcrumb/breadcrumb.mjs +1 -1
  73. package/dist/esm/common/breadcrumb/breadcrumb.mjs.map +1 -1
  74. package/dist/esm/common/breadcrumb/breadcrumb.module.mjs.map +1 -1
  75. package/dist/esm/common/button/button.mjs +1 -1
  76. package/dist/esm/common/button/button.mjs.map +1 -1
  77. package/dist/esm/common/button/button.module.mjs +1 -1
  78. package/dist/esm/common/button/button.module.mjs.map +1 -1
  79. package/dist/esm/common/button/button.types.mjs +1 -1
  80. package/dist/esm/common/button/button.types.mjs.map +1 -1
  81. package/dist/esm/common/calendar/calendar.mjs +1 -1
  82. package/dist/esm/common/calendar/calendar.mjs.map +1 -1
  83. package/dist/esm/common/calendar/calendar.module.mjs.map +1 -1
  84. package/dist/esm/common/calendar/calendar.types.mjs.map +1 -1
  85. package/dist/esm/common/calendar/components/agendaList/agendaList.mjs +1 -1
  86. package/dist/esm/common/calendar/components/agendaList/agendaList.mjs.map +1 -1
  87. package/dist/esm/common/calendar/components/agendaList/agendaList.module.mjs.map +1 -1
  88. package/dist/esm/common/calendar/components/calendarHeader/calendarHeader.mjs +1 -1
  89. package/dist/esm/common/calendar/components/calendarHeader/calendarHeader.mjs.map +1 -1
  90. package/dist/esm/common/calendar/components/calendarHeader/calendarHeader.module.mjs +1 -1
  91. package/dist/esm/common/calendar/components/calendarHeader/calendarHeader.module.mjs.map +1 -1
  92. package/dist/esm/common/calendar/components/compactMonth/compactMonth.mjs +1 -1
  93. package/dist/esm/common/calendar/components/compactMonth/compactMonth.mjs.map +1 -1
  94. package/dist/esm/common/calendar/components/compactMonth/compactMonth.module.mjs +1 -1
  95. package/dist/esm/common/calendar/components/compactMonth/compactMonth.module.mjs.map +1 -1
  96. package/dist/esm/common/calendar/components/monthView/monthView.mjs +1 -1
  97. package/dist/esm/common/calendar/components/monthView/monthView.mjs.map +1 -1
  98. package/dist/esm/common/calendar/components/monthView/monthView.module.mjs +1 -1
  99. package/dist/esm/common/calendar/components/monthView/monthView.module.mjs.map +1 -1
  100. package/dist/esm/common/calendar/components/timeGrid/timeGrid.mjs +1 -1
  101. package/dist/esm/common/calendar/components/timeGrid/timeGrid.mjs.map +1 -1
  102. package/dist/esm/common/calendar/components/timeGrid/timeGrid.module.mjs +1 -1
  103. package/dist/esm/common/calendar/components/timeGrid/timeGrid.module.mjs.map +1 -1
  104. package/dist/esm/common/calendar/dates.mjs +1 -1
  105. package/dist/esm/common/calendar/dates.mjs.map +1 -1
  106. package/dist/esm/common/calendar/format.mjs +1 -1
  107. package/dist/esm/common/calendar/format.mjs.map +1 -1
  108. package/dist/esm/common/calendar/layout.mjs +1 -1
  109. package/dist/esm/common/calendar/layout.mjs.map +1 -1
  110. package/dist/esm/common/calendar/useNow.mjs +2 -0
  111. package/dist/esm/common/calendar/useNow.mjs.map +1 -0
  112. package/dist/esm/common/card/Card.mjs +1 -1
  113. package/dist/esm/common/card/Card.mjs.map +1 -1
  114. package/dist/esm/common/card/CardActions.mjs +1 -1
  115. package/dist/esm/common/card/CardActions.mjs.map +1 -1
  116. package/dist/esm/common/card/CardHeader.mjs.map +1 -1
  117. package/dist/esm/common/card/card.module.mjs +1 -1
  118. package/dist/esm/common/card/card.module.mjs.map +1 -1
  119. package/dist/esm/common/card/card.types.mjs +1 -1
  120. package/dist/esm/common/card/card.types.mjs.map +1 -1
  121. package/dist/esm/common/carousel/carousel.mjs +1 -1
  122. package/dist/esm/common/carousel/carousel.mjs.map +1 -1
  123. package/dist/esm/common/carousel/carousel.module.mjs.map +1 -1
  124. package/dist/esm/common/chip/chip.mjs +1 -1
  125. package/dist/esm/common/chip/chip.mjs.map +1 -1
  126. package/dist/esm/common/chip/chip.module.mjs +1 -1
  127. package/dist/esm/common/chip/chip.module.mjs.map +1 -1
  128. package/dist/esm/common/chip/chip.types.mjs +1 -1
  129. package/dist/esm/common/chip/chip.types.mjs.map +1 -1
  130. package/dist/esm/common/colorPicker/colorPicker.mjs +1 -1
  131. package/dist/esm/common/colorPicker/colorPicker.mjs.map +1 -1
  132. package/dist/esm/common/colorPicker/colorPicker.module.mjs.map +1 -1
  133. package/dist/esm/common/confirmModal/confirmModal.mjs +1 -1
  134. package/dist/esm/common/confirmModal/confirmModal.mjs.map +1 -1
  135. package/dist/esm/common/copyButton/copyButton.mjs +1 -1
  136. package/dist/esm/common/copyButton/copyButton.mjs.map +1 -1
  137. package/dist/esm/common/copyButton/copyButton.module.mjs +1 -1
  138. package/dist/esm/common/copyButton/copyButton.module.mjs.map +1 -1
  139. package/dist/esm/common/errorBoundary/errorBoundary.mjs +2 -2
  140. package/dist/esm/common/errorBoundary/errorBoundary.mjs.map +1 -1
  141. package/dist/esm/common/fab/components/fabBase/fabBase.mjs +1 -1
  142. package/dist/esm/common/fab/components/fabBase/fabBase.mjs.map +1 -1
  143. package/dist/esm/common/fab/fab.mjs +1 -1
  144. package/dist/esm/common/fab/fab.mjs.map +1 -1
  145. package/dist/esm/common/flip/flip.mjs +1 -1
  146. package/dist/esm/common/flip/flip.mjs.map +1 -1
  147. package/dist/esm/common/klipyPicker/klipyPicker.mjs +1 -1
  148. package/dist/esm/common/klipyPicker/klipyPicker.mjs.map +1 -1
  149. package/dist/esm/common/modal/modal.mjs +1 -1
  150. package/dist/esm/common/modal/modal.mjs.map +1 -1
  151. package/dist/esm/common/modal/modal.module.mjs.map +1 -1
  152. package/dist/esm/common/numberInput/numberInput.mjs +1 -1
  153. package/dist/esm/common/numberInput/numberInput.mjs.map +1 -1
  154. package/dist/esm/common/numberInput/numberInput.module.mjs.map +1 -1
  155. package/dist/esm/common/popover/popover.mjs +1 -1
  156. package/dist/esm/common/popover/popover.mjs.map +1 -1
  157. package/dist/esm/common/progressBar/progressBar.mjs +1 -1
  158. package/dist/esm/common/progressBar/progressBar.mjs.map +1 -1
  159. package/dist/esm/common/progressBar/progressBar.module.mjs +1 -1
  160. package/dist/esm/common/progressBar/progressBar.module.mjs.map +1 -1
  161. package/dist/esm/common/roomDrawer/roomDrawer.mjs +1 -1
  162. package/dist/esm/common/roomDrawer/roomDrawer.mjs.map +1 -1
  163. package/dist/esm/common/roomDrawer/roomDrawer.module.mjs.map +1 -1
  164. package/dist/esm/common/sectionHeader/sectionHeader.mjs.map +1 -1
  165. package/dist/esm/common/splitButton/splitButton.mjs +1 -1
  166. package/dist/esm/common/splitButton/splitButton.mjs.map +1 -1
  167. package/dist/esm/common/styles/tone.mjs +4 -0
  168. package/dist/esm/common/styles/tone.mjs.map +1 -0
  169. package/dist/esm/common/styles/tone.module.mjs +2 -0
  170. package/dist/esm/common/styles/tone.module.mjs.map +1 -0
  171. package/dist/esm/common/themeProvider/color.types.mjs +2 -0
  172. package/dist/esm/common/themeProvider/color.types.mjs.map +1 -0
  173. package/dist/esm/common/themeProvider/defaultTheme.mjs +1 -1
  174. package/dist/esm/common/themeProvider/defaultTheme.mjs.map +1 -1
  175. package/dist/esm/common/tilt/tilt.mjs +1 -1
  176. package/dist/esm/common/tilt/tilt.mjs.map +1 -1
  177. package/dist/esm/common/timeInput/timeInput.mjs +1 -1
  178. package/dist/esm/common/timeInput/timeInput.mjs.map +1 -1
  179. package/dist/esm/common/timeline/timeline.mjs +1 -1
  180. package/dist/esm/common/timeline/timeline.mjs.map +1 -1
  181. package/dist/esm/common/timeline/timeline.module.mjs +1 -1
  182. package/dist/esm/common/timeline/timeline.module.mjs.map +1 -1
  183. package/dist/esm/common/timeline/timeline.types.mjs +1 -1
  184. package/dist/esm/common/timeline/timeline.types.mjs.map +1 -1
  185. package/dist/esm/common/timer/timer.mjs +1 -1
  186. package/dist/esm/common/timer/timer.mjs.map +1 -1
  187. package/dist/esm/common/timer/timer.module.mjs +1 -1
  188. package/dist/esm/common/timer/timer.module.mjs.map +1 -1
  189. package/dist/esm/common/toast/toast.mjs +1 -1
  190. package/dist/esm/common/toast/toast.mjs.map +1 -1
  191. package/dist/esm/common/toast/toast.module.mjs +1 -1
  192. package/dist/esm/common/toast/toast.module.mjs.map +1 -1
  193. package/dist/esm/common/toast/toastHost.mjs +1 -1
  194. package/dist/esm/common/toast/toastHost.mjs.map +1 -1
  195. package/dist/esm/common/utils/dismissClick.mjs +2 -0
  196. package/dist/esm/common/utils/dismissClick.mjs.map +1 -0
  197. package/dist/esm/common/utils/isDev.mjs +2 -0
  198. package/dist/esm/common/utils/isDev.mjs.map +1 -0
  199. package/dist/esm/index.mjs +1 -1
  200. package/dist/esm/services/formValidation/FormValidatorGroupWithForm.mjs +1 -1
  201. package/dist/esm/services/formValidation/FormValidatorGroupWithForm.mjs.map +1 -1
  202. package/dist/index.cjs +5 -3
  203. package/dist/index.cjs.map +1 -1
  204. package/dist/style.css +1 -1
  205. package/dist/types/common/accordion/accordion.types.d.ts +17 -2
  206. package/dist/types/common/actionButtons/actionButtons.types.d.ts +7 -1
  207. package/dist/types/common/actionIcon/actionIcon.d.ts +1 -1
  208. package/dist/types/common/actionIcon/actionIcon.types.d.ts +7 -9
  209. package/dist/types/common/alert/alert.types.d.ts +6 -8
  210. package/dist/types/common/animatedLogo/animatedLogo.types.d.ts +3 -2
  211. package/dist/types/common/badge/badge.types.d.ts +10 -6
  212. package/dist/types/common/button/button.types.d.ts +14 -4
  213. package/dist/types/common/calendar/calendar.types.d.ts +25 -10
  214. package/dist/types/common/calendar/components/agendaList/agendaList.d.ts +1 -1
  215. package/dist/types/common/calendar/components/agendaList/agendaList.types.d.ts +3 -0
  216. package/dist/types/common/calendar/components/calendarHeader/calendarHeader.d.ts +1 -1
  217. package/dist/types/common/calendar/components/calendarHeader/calendarHeader.types.d.ts +2 -0
  218. package/dist/types/common/calendar/components/compactMonth/compactMonth.d.ts +1 -1
  219. package/dist/types/common/calendar/components/monthView/monthView.d.ts +1 -1
  220. package/dist/types/common/calendar/components/monthView/monthView.types.d.ts +3 -2
  221. package/dist/types/common/calendar/components/timeGrid/timeGrid.d.ts +1 -1
  222. package/dist/types/common/calendar/components/timeGrid/timeGrid.types.d.ts +9 -1
  223. package/dist/types/common/calendar/dates.d.ts +21 -5
  224. package/dist/types/common/calendar/format.d.ts +3 -2
  225. package/dist/types/common/calendar/layout.d.ts +5 -0
  226. package/dist/types/common/calendar/useNow.d.ts +4 -0
  227. package/dist/types/common/calendar/view.types.d.ts +9 -2
  228. package/dist/types/common/card/card.types.d.ts +16 -2
  229. package/dist/types/common/chip/chip.types.d.ts +10 -5
  230. package/dist/types/common/copyButton/copyButton.types.d.ts +5 -4
  231. package/dist/types/common/fab/components/fabBase/fabBase.d.ts +1 -1
  232. package/dist/types/common/fab/components/fabBase/fabBase.types.d.ts +2 -1
  233. package/dist/types/common/progressBar/progressBar.d.ts +0 -1
  234. package/dist/types/common/progressBar/progressBar.types.d.ts +3 -1
  235. package/dist/types/common/splitButton/splitButton.types.d.ts +4 -1
  236. package/dist/types/common/styles/tone.d.ts +11 -0
  237. package/dist/types/common/themeProvider/color.types.d.ts +20 -0
  238. package/dist/types/common/themeProvider/index.d.ts +2 -0
  239. package/dist/types/common/themeProvider/theme.types.d.ts +3 -6
  240. package/dist/types/common/timeline/timeline.types.d.ts +3 -10
  241. package/dist/types/common/toast/index.d.ts +1 -1
  242. package/dist/types/common/toast/toast.types.d.ts +7 -3
  243. package/dist/types/common/types/actions.types.d.ts +1 -1
  244. package/dist/types/common/utils/dismissClick.d.ts +4 -0
  245. package/dist/types/common/utils/isDev.d.ts +1 -0
  246. package/docs/Accordion.md +74 -5
  247. package/docs/ActionButtons.md +6 -3
  248. package/docs/ActionIcon.md +39 -14
  249. package/docs/Alert.md +27 -17
  250. package/docs/AnimatedLogo.md +1 -1
  251. package/docs/Badge.md +24 -7
  252. package/docs/Button.md +98 -40
  253. package/docs/ButtonGroup.md +18 -18
  254. package/docs/CLAUDE.md +21 -11
  255. package/docs/Calendar.md +35 -17
  256. package/docs/Card.md +53 -14
  257. package/docs/Chip.md +24 -9
  258. package/docs/ConfigProvider.md +3 -3
  259. package/docs/CopyButton.md +6 -5
  260. package/docs/ProgressBar.md +7 -4
  261. package/docs/SectionHeader.md +1 -1
  262. package/docs/SplitButton.md +6 -5
  263. package/docs/ThemeColor.md +103 -0
  264. package/docs/ThemeProvider.md +5 -5
  265. package/docs/Timeline.md +18 -9
  266. package/docs/Toast.md +25 -11
  267. package/package.json +3 -2
  268. package/dist/esm/common/accordion/accordion.plain.module.mjs +0 -2
  269. package/dist/esm/common/accordion/accordion.plain.module.mjs.map +0 -1
  270. package/dist/esm/common/accordion/accordion.primary.module.mjs +0 -2
  271. package/dist/esm/common/accordion/accordion.primary.module.mjs.map +0 -1
  272. package/dist/esm/common/alert/alert.types.mjs +0 -2
  273. package/dist/esm/common/alert/alert.types.mjs.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"modal.mjs","names":[],"sources":["../../../../package/common/modal/modal.tsx"],"sourcesContent":["import cx from 'classnames';\nimport BodyEnd from 'components/common/bodyEnd';\n\nimport styles from './modal.module.pcss';\nimport sheetStyles from 'components/common/styles/sheet.module.pcss';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faXmark as closeIcon } from '@fortawesome/free-solid-svg-icons';\nimport { ModalProps } from './modal.types';\nimport type { ModalLabels } from './modal.types';\nimport { useEffect, useId, useRef, useState } from 'react';\nimport { useVisualViewportSize } from 'components/common/hooks/useVisualViewportSize';\nimport { useOverlay } from 'components/common/hooks/useOverlay';\nimport { useSheet } from 'components/common/hooks/useSheet';\nimport { useScrollEdges } from 'components/common/hooks/useScrollEdges';\nimport fadeStyles from 'components/common/styles/scrollFade.module.pcss';\nimport { useComponentDefaults, useLabels, usePresentationDefault } from 'components/common/configProvider';\n\nconst DEFAULT_LABELS: Required<ModalLabels> = {\n close: 'Close',\n};\n\nfunction Modal(props: ModalProps) {\n const {\n children = null,\n title,\n isOpen = false,\n onClose = () => null,\n footer = null,\n className = '',\n style,\n hideHeader = false,\n closeOnEscape = true,\n presentation: presentationProp,\n classNames,\n styles: slotStyles,\n labels,\n ...rest\n } = useComponentDefaults('Modal', props);\n const text = useLabels('Modal', labels, DEFAULT_LABELS);\n const [isRendered, setIsRendered] = useState(isOpen);\n const [animateIn, setAnimateIn] = useState(isOpen);\n const modalRef = useRef<HTMLDivElement>(null);\n const bodyRef = useRef<HTMLDivElement>(null);\n const titleId = useId();\n // Centering/sizing against the actually-visible viewport (not the full layout viewport) keeps\n // the modal — and whatever's focused inside it — from ending up under a mobile keyboard.\n const { height: viewportHeight, offsetTop: viewportOffsetTop } = useVisualViewportSize();\n // Explicit prop → this component's global default → the app-wide one. There's no built-in\n // fallback to add: `Default` and no presentation at all both mean the ordinary centred dialog.\n const appPresentation = usePresentationDefault();\n const presentation = presentationProp ?? appPresentation;\n const { isSheet, sheetStyle } = useSheet(presentation);\n\n // Cancelled on a flip back, or a reopen during the close animation is hidden by its late unmount.\n useEffect(() => {\n if (isOpen) {\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setIsRendered(true);\n const id = setTimeout(() => setAnimateIn(true), 1);\n return () => clearTimeout(id);\n }\n setAnimateIn(false);\n const id = setTimeout(() => setIsRendered(false), 300);\n return () => clearTimeout(id);\n }, [isOpen]);\n\n // Keyed on `animateIn` rather than `isOpen`, because the panel this needs to focus and trap\n // focus inside of only mounts once that flips.\n useOverlay({\n isActive: isRendered && animateIn,\n containerRef: modalRef,\n onEscape: closeOnEscape ? onClose : undefined,\n });\n\n const { hasStart, hasEnd } = useScrollEdges(bodyRef, { isEnabled: isRendered && animateIn });\n\n if (!isRendered) return null;\n\n const hasHeaderTitle = !hideHeader && !!title;\n\n return (\n <BodyEnd>\n <div\n className={cx(styles.modalContainer, classNames?.container)}\n style={{ height: viewportHeight, top: viewportOffsetTop, ...slotStyles?.container }}\n >\n <div\n className={cx(styles.modalWrapper, animateIn && styles.modalWrapperShown, classNames?.backdrop)}\n style={slotStyles?.backdrop}\n onClick={onClose}\n />\n {animateIn && (\n <div\n {...rest}\n ref={modalRef}\n role='dialog'\n aria-modal='true'\n // aria-labelledby would outrank aria-label, which is the only way to name a title with no text.\n aria-labelledby={hasHeaderTitle && !rest['aria-label'] ? titleId : undefined}\n // Focusable so the trap has somewhere to put focus when the content has no tabbables.\n tabIndex={-1}\n className={cx(\n styles.modal,\n animateIn && styles.modalShown,\n // The panel is its own sheet surface here: there's no inner panel to fill, since\n // Modal already splits itself into a fixed header and a scrolling body.\n isSheet && [sheetStyles.sheet, sheetStyles.sheetSurface, styles.modalSheet],\n className,\n classNames?.modal,\n )}\n style={{ ...(isSheet ? sheetStyle : undefined), ...style, ...slotStyles?.modal }}\n onClick={(e) => e.stopPropagation()}\n >\n {!hideHeader && (\n <div className={cx(styles.modalHeader, classNames?.header)} style={slotStyles?.header}>\n <div id={titleId} className={classNames?.title} style={slotStyles?.title}>\n {title}\n </div>\n <button\n type='button'\n onClick={onClose}\n aria-label={text.close}\n className={cx(styles.modalCloseButton, classNames?.closeButton)}\n style={slotStyles?.closeButton}\n >\n <FontAwesomeIcon icon={closeIcon} />\n </button>\n </div>\n )}\n <div\n ref={bodyRef}\n className={cx(\n styles.modalBody,\n fadeStyles.scrollFade,\n fadeStyles.scrollFadeY,\n hasStart && fadeStyles.scrollFadeFadedStart,\n hasEnd && fadeStyles.scrollFadeFadedEnd,\n classNames?.body,\n )}\n style={slotStyles?.body}\n >\n {children}\n </div>\n {footer && (\n <div className={cx(styles.modalFooter, classNames?.footer)} style={slotStyles?.footer}>\n {footer}\n </div>\n )}\n </div>\n )}\n </div>\n </BodyEnd>\n );\n}\n\nexport default Modal;\n"],"mappings":"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"}
1
+ {"version":3,"file":"modal.mjs","names":[],"sources":["../../../../package/common/modal/modal.tsx"],"sourcesContent":["import cx from 'classnames';\nimport BodyEnd from 'components/common/bodyEnd';\n\nimport styles from './modal.module.pcss';\nimport sheetStyles from 'components/common/styles/sheet.module.pcss';\nimport { faXmark as closeIcon } from '@fortawesome/free-solid-svg-icons';\nimport { ModalProps } from './modal.types';\nimport type { ModalLabels } from './modal.types';\nimport { useEffect, useId, useRef, useState } from 'react';\nimport { useVisualViewportSize } from 'components/common/hooks/useVisualViewportSize';\nimport { useOverlay } from 'components/common/hooks/useOverlay';\nimport { useSheet } from 'components/common/hooks/useSheet';\nimport { useScrollEdges } from 'components/common/hooks/useScrollEdges';\nimport fadeStyles from 'components/common/styles/scrollFade.module.pcss';\nimport { useComponentDefaults, useLabels, usePresentationDefault } from 'components/common/configProvider';\nimport ActionIcon from 'components/common/actionIcon';\n\nconst DEFAULT_LABELS: Required<ModalLabels> = {\n close: 'Close',\n};\n\nfunction Modal(props: ModalProps) {\n const {\n children = null,\n title,\n isOpen = false,\n onClose = () => null,\n footer = null,\n className = '',\n style,\n hideHeader = false,\n closeOnEscape = true,\n presentation: presentationProp,\n classNames,\n styles: slotStyles,\n labels,\n ...rest\n } = useComponentDefaults('Modal', props);\n const text = useLabels('Modal', labels, DEFAULT_LABELS);\n const [isRendered, setIsRendered] = useState(isOpen);\n const [animateIn, setAnimateIn] = useState(isOpen);\n const modalRef = useRef<HTMLDivElement>(null);\n const bodyRef = useRef<HTMLDivElement>(null);\n const titleId = useId();\n // Centering/sizing against the actually-visible viewport (not the full layout viewport) keeps\n // the modal — and whatever's focused inside it — from ending up under a mobile keyboard.\n const { height: viewportHeight, offsetTop: viewportOffsetTop } = useVisualViewportSize();\n // Explicit prop → this component's global default → the app-wide one. There's no built-in\n // fallback to add: `Default` and no presentation at all both mean the ordinary centred dialog.\n const appPresentation = usePresentationDefault();\n const presentation = presentationProp ?? appPresentation;\n const { isSheet, sheetStyle } = useSheet(presentation);\n\n // Cancelled on a flip back, or a reopen during the close animation is hidden by its late unmount.\n useEffect(() => {\n if (isOpen) {\n // eslint-disable-next-line react-hooks/set-state-in-effect\n setIsRendered(true);\n const id = setTimeout(() => setAnimateIn(true), 1);\n return () => clearTimeout(id);\n }\n setAnimateIn(false);\n const id = setTimeout(() => setIsRendered(false), 300);\n return () => clearTimeout(id);\n }, [isOpen]);\n\n // Keyed on `animateIn` rather than `isOpen`, because the panel this needs to focus and trap\n // focus inside of only mounts once that flips.\n useOverlay({\n isActive: isRendered && animateIn,\n containerRef: modalRef,\n onEscape: closeOnEscape ? onClose : undefined,\n });\n\n const { hasStart, hasEnd } = useScrollEdges(bodyRef, { isEnabled: isRendered && animateIn });\n\n if (!isRendered) return null;\n\n const hasHeaderTitle = !hideHeader && !!title;\n\n return (\n <BodyEnd>\n <div\n className={cx(styles.modalContainer, classNames?.container)}\n style={{ height: viewportHeight, top: viewportOffsetTop, ...slotStyles?.container }}\n >\n <div\n className={cx(styles.modalWrapper, animateIn && styles.modalWrapperShown, classNames?.backdrop)}\n style={slotStyles?.backdrop}\n onClick={onClose}\n />\n {animateIn && (\n <div\n {...rest}\n ref={modalRef}\n role='dialog'\n aria-modal='true'\n // aria-labelledby would outrank aria-label, which is the only way to name a title with no text.\n aria-labelledby={hasHeaderTitle && !rest['aria-label'] ? titleId : undefined}\n // Focusable so the trap has somewhere to put focus when the content has no tabbables.\n tabIndex={-1}\n className={cx(\n styles.modal,\n animateIn && styles.modalShown,\n // The panel is its own sheet surface here: there's no inner panel to fill, since\n // Modal already splits itself into a fixed header and a scrolling body.\n isSheet && [sheetStyles.sheet, sheetStyles.sheetSurface, styles.modalSheet],\n className,\n classNames?.modal,\n )}\n style={{ ...(isSheet ? sheetStyle : undefined), ...style, ...slotStyles?.modal }}\n onClick={(e) => e.stopPropagation()}\n >\n {!hideHeader && (\n <div className={cx(styles.modalHeader, classNames?.header)} style={slotStyles?.header}>\n <div id={titleId} className={classNames?.title} style={slotStyles?.title}>\n {title}\n </div>\n <ActionIcon\n icon={closeIcon}\n onClick={onClose}\n aria-label={text.close}\n className={cx(styles.modalCloseButton, classNames?.closeButton)}\n style={slotStyles?.closeButton}\n />\n </div>\n )}\n <div\n ref={bodyRef}\n className={cx(\n styles.modalBody,\n fadeStyles.scrollFade,\n fadeStyles.scrollFadeY,\n hasStart && fadeStyles.scrollFadeFadedStart,\n hasEnd && fadeStyles.scrollFadeFadedEnd,\n classNames?.body,\n )}\n style={slotStyles?.body}\n >\n {children}\n </div>\n {footer && (\n <div className={cx(styles.modalFooter, classNames?.footer)} style={slotStyles?.footer}>\n {footer}\n </div>\n )}\n </div>\n )}\n </div>\n </BodyEnd>\n );\n}\n\nexport default Modal;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"modal.module.mjs","names":[],"sources":["../../../../package/common/modal/modal.module.pcss"],"sourcesContent":[".modal {\n /* Theme tokens resolved once, each with a built-in fallback. Set any of the public\n `--modal-*` variables via ThemeProvider (theme-wide) or `style` (per instance) to\n override; unset ones fall back to the values below. */\n --custom-modal-padding: var(--modal-padding, var(--spacing-l));\n --custom-modal-gap: var(--modal-gap, var(--spacing-l));\n\n /* Half the gap, so the scroll port's clipping edge — where the divider sits — lands midway\n between the chrome and where the content rests, with the same room either side. Flush\n against the chrome (bleed = gap) reads as a sliced line of text colliding with the title;\n an unrelated value (this was `0.6em`) puts it at a distance that drifts with the font size. */\n --custom-modal-body-bleed: var(--modal-body-bleed, calc(var(--custom-modal-gap) / 2));\n\n /* The panel never comes closer to the screen edge than its own padding, at every size. A\n percentage instead reads as a different inset on every screen, and pairing one with a\n breakpoint made the two axes disagree: 5% of a 390px width is 19.5px but 5% of a 780px\n height is 39px, so the same rule came out narrower and taller than the default on one phone. */\n --custom-modal-max-width: var(--modal-max-width, calc(100% - 2 * var(--custom-modal-padding)));\n --custom-modal-max-height: var(--modal-max-height, calc(100% - 2 * var(--custom-modal-padding)));\n\n padding: var(--custom-modal-padding);\n border-radius: var(--default-border-radius);\n background: var(--background-modal);\n color: var(--text-color);\n font-family: var(--default-font);\n box-sizing: border-box;\n position: relative;\n display: flex;\n flex-direction: column;\n transform-origin: bottom center;\n animation:\n slideIn 0.2s,\n fadeIn 0.3s forwards;\n max-width: var(--custom-modal-max-width);\n max-height: var(--custom-modal-max-height);\n\n /* The panel itself never scrolls, so the header stays put; `&-body` is what scrolls. */\n overflow: hidden;\n\n /* A sheet-presented modal carries forms and detail views, not a short menu, so it takes more\n of the screen than the shared default — and keeps the panel's own padding at the bottom edge. */\n &-sheet {\n --sheet-max-height: 90dvh;\n --sheet-safe-padding: var(--custom-modal-padding);\n }\n\n &-container {\n position: fixed;\n width: 100%;\n height: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n top: 0;\n left: 0;\n\n /* Shared by every overlay, so the order they are portaled in, which is the order they opened in, decides. */\n z-index: 1;\n }\n\n &-header {\n display: flex;\n gap: var(--spacing-m);\n justify-content: space-between;\n align-items: center;\n margin-bottom: var(--custom-modal-gap);\n flex: 0 0 auto;\n\n div {\n overflow: hidden;\n text-overflow: ellipsis;\n }\n }\n\n &-body {\n /* `min-height: 0` lets this shrink below its content's height so it, not the panel, scrolls. */\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n\n /* A negative margin cancelled by an equal padding, so a focus ring or a floating label at\n the body's edge isn't clipped by the scroll container. Override the pair with\n `--modal-body-bleed`, never with a plain `padding`, which drops the negative half and\n brings the clipping back. See `docs/Modal.md` for what the two do together. */\n margin: calc(-1 * var(--custom-modal-body-bleed));\n padding: var(--custom-modal-body-bleed);\n\n /* The mask lives in `styles/scrollFade.module.pcss`; the component applies it along with the\n modifiers saying which end genuinely hides content. The run has to clear the port's own\n padding before any content fades at all, hence gap + bleed. Set here as the shared sheet's\n private length rather than as `--scroll-fade-length`, which would inherit down into a\n `ScrollFade` nested in the body. */\n --custom-scroll-fade-length: var(\n --modal-scroll-fade,\n calc(var(--custom-modal-gap) + var(--custom-modal-body-bleed))\n );\n }\n\n &-footer {\n margin-top: var(--custom-modal-gap);\n flex: 0 0 auto;\n }\n\n &-close-button {\n flex-shrink: 0;\n background: none;\n border: none;\n padding: 0;\n color: inherit;\n font-size: inherit;\n line-height: 1;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n }\n\n &-wrapper {\n position: fixed;\n width: 100%;\n height: 100%;\n background: #00000052;\n top: 0;\n left: 0;\n transition: all 0.3s ease-in-out;\n opacity: 0;\n\n &-shown {\n opacity: 1;\n }\n }\n}\n\n@keyframes slideIn {\n from {\n transform: scale(0);\n }\n\n to {\n transform: scale(0.99);\n }\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 1;\n }\n}\n\n/* Only the scale-in goes; the backdrop and the panel still fade, which is not motion.\n `slideIn` ends at scale(0.99) and the panel has no transform of its own, so dropping the\n animation lands it at scale(1) rather than leaving it collapsed. */\n@media (prefers-reduced-motion: reduce) {\n .modal {\n animation: fadeIn 0.3s forwards;\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"modal.module.mjs","names":[],"sources":["../../../../package/common/modal/modal.module.pcss"],"sourcesContent":[".modal {\n /* Theme tokens resolved once, each with a built-in fallback. Set any of the public\n `--modal-*` variables via ThemeProvider (theme-wide) or `style` (per instance) to\n override; unset ones fall back to the values below. */\n --custom-modal-padding: var(--modal-padding, var(--spacing-l));\n --custom-modal-gap: var(--modal-gap, var(--spacing-l));\n\n /* Half the gap, so the scroll port's clipping edge — where the divider sits — lands midway\n between the chrome and where the content rests, with the same room either side. Flush\n against the chrome (bleed = gap) reads as a sliced line of text colliding with the title;\n an unrelated value (this was `0.6em`) puts it at a distance that drifts with the font size. */\n --custom-modal-body-bleed: var(--modal-body-bleed, calc(var(--custom-modal-gap) / 2));\n\n /* The panel never comes closer to the screen edge than its own padding, at every size. A\n percentage instead reads as a different inset on every screen, and pairing one with a\n breakpoint made the two axes disagree: 5% of a 390px width is 19.5px but 5% of a 780px\n height is 39px, so the same rule came out narrower and taller than the default on one phone. */\n --custom-modal-max-width: var(--modal-max-width, calc(100% - 2 * var(--custom-modal-padding)));\n --custom-modal-max-height: var(--modal-max-height, calc(100% - 2 * var(--custom-modal-padding)));\n\n padding: var(--custom-modal-padding);\n border-radius: var(--default-border-radius);\n background: var(--background-modal);\n color: var(--text-color);\n font-family: var(--default-font);\n box-sizing: border-box;\n position: relative;\n display: flex;\n flex-direction: column;\n transform-origin: bottom center;\n animation:\n slideIn 0.2s,\n fadeIn 0.3s forwards;\n max-width: var(--custom-modal-max-width);\n max-height: var(--custom-modal-max-height);\n\n /* The panel itself never scrolls, so the header stays put; `&-body` is what scrolls. */\n overflow: hidden;\n\n /* A sheet-presented modal carries forms and detail views, not a short menu, so it takes more\n of the screen than the shared default — and keeps the panel's own padding at the bottom edge. */\n &-sheet {\n --sheet-max-height: 90dvh;\n --sheet-safe-padding: var(--custom-modal-padding);\n }\n\n &-container {\n position: fixed;\n width: 100%;\n height: 100%;\n display: flex;\n justify-content: center;\n align-items: center;\n top: 0;\n left: 0;\n\n /* Shared by every overlay, so the order they are portaled in, which is the order they opened in, decides. */\n z-index: 1;\n }\n\n &-header {\n display: flex;\n gap: var(--spacing-m);\n justify-content: space-between;\n align-items: center;\n margin-bottom: var(--custom-modal-gap);\n flex: 0 0 auto;\n\n div {\n overflow: hidden;\n text-overflow: ellipsis;\n }\n }\n\n &-body {\n /* `min-height: 0` lets this shrink below its content's height so it, not the panel, scrolls. */\n flex: 1 1 auto;\n min-height: 0;\n overflow: auto;\n\n /* A negative margin cancelled by an equal padding, so a focus ring or a floating label at\n the body's edge isn't clipped by the scroll container. Override the pair with\n `--modal-body-bleed`, never with a plain `padding`, which drops the negative half and\n brings the clipping back. See `docs/Modal.md` for what the two do together. */\n margin: calc(-1 * var(--custom-modal-body-bleed));\n padding: var(--custom-modal-body-bleed);\n\n /* The mask lives in `styles/scrollFade.module.pcss`; the component applies it along with the\n modifiers saying which end genuinely hides content. The run has to clear the port's own\n padding before any content fades at all, hence gap + bleed. Set here as the shared sheet's\n private length rather than as `--scroll-fade-length`, which would inherit down into a\n `ScrollFade` nested in the body. */\n --custom-scroll-fade-length: var(\n --modal-scroll-fade,\n calc(var(--custom-modal-gap) + var(--custom-modal-body-bleed))\n );\n }\n\n &-footer {\n margin-top: var(--custom-modal-gap);\n flex: 0 0 auto;\n }\n\n &-close-button {\n flex-shrink: 0;\n }\n\n &-wrapper {\n position: fixed;\n width: 100%;\n height: 100%;\n background: #00000052;\n top: 0;\n left: 0;\n transition: all 0.3s ease-in-out;\n opacity: 0;\n\n &-shown {\n opacity: 1;\n }\n }\n}\n\n@keyframes slideIn {\n from {\n transform: scale(0);\n }\n\n to {\n transform: scale(0.99);\n }\n}\n\n@keyframes fadeIn {\n from {\n opacity: 0;\n }\n\n to {\n opacity: 1;\n }\n}\n\n/* Only the scale-in goes; the backdrop and the panel still fade, which is not motion.\n `slideIn` ends at scale(0.99) and the panel has no transform of its own, so dropping the\n animation lands it at scale(1) rather than leaving it collapsed. */\n@media (prefers-reduced-motion: reduce) {\n .modal {\n animation: fadeIn 0.3s forwards;\n }\n}\n"],"mappings":""}
@@ -1,2 +1,2 @@
1
- "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{ButtonSize as n}from"../button/button.types.mjs";import r from"../button/button.mjs";import i from"../hooks/useCoarsePointer.mjs";import ee from"../buttonGroup/buttonGroup.mjs";import a from"../input/input.mjs";import o from"./numberInput.module.mjs";import te from"./useStepRepeat.mjs";import s from"react";import c from"classnames";import{FontAwesomeIcon as l}from"@fortawesome/react-fontawesome";import{faChevronDown as ne,faChevronUp as re,faMinus as ie,faPlus as ae}from"@fortawesome/free-solid-svg-icons";import{jsx as u,jsxs as d}from"react/jsx-runtime";import{NumericFormat as oe}from"react-number-format";var se={increase:`Increase`,decrease:`Decrease`};function f({label:e,disabled:t,direction:n,onStart:r,onStop:i}){return{tabIndex:-1,disabled:t,"aria-label":e,onPointerDown:e=>{(e.button===0||e.pointerType!==`mouse`)&&(e.preventDefault(),r(n,e.shiftKey?10:1))},onPointerUp:i,onPointerCancel:i,onContextMenu:e=>e.preventDefault()}}function p(e){return u(`button`,{type:`button`,...f(e),className:c(o.arrowBtn,e.className),style:e.style,children:u(l,{icon:e.icon,"aria-hidden":`true`})})}function m(e){let{disabled:t,...i}=f(e);return u(r,{...i,className:c(o.splitBtn,e.className),style:e.style,size:n.Icon,disabled:t,icon:e.icon})}function h(n){let r=s.useRef(void 0),{value:l,onChange:f,formValidator:h,label:ce,error:g,className:_,style:v,classNames:y,styles:b,allowNegative:x=!1,decimalSeparator:S,thousandSeparator:le,prefix:ue,suffix:de,decimalScale:C,fixedDecimalScale:fe=!0,placeholder:pe,sign:w,showArrows:me=!1,step:T=1,min:E,max:D,arrowsLayout:O=`auto`,labels:he,readOnly:k=!1,disabled:A=!1,isRequired:ge=!1,onKeyDown:j,ref:_e,...M}=e(`NumberInput`,n),N=t(`NumberInput`,he,se),[,P]=s.useState(0);s.useEffect(()=>{if(r.current=h,!h)return;let e=()=>P(e=>e+1);return h.registerOnUpdateListener(e),()=>{h.removeOnUpdateListener(e)}},[h]);let F=ge||!!h?.validators.find(e=>e.validatorId===`required`),I=h==null?l:h.value,L=w!=null&&I!=null?Math.abs(Number(I)):I,R=L==null?``:String(L);function z(e){let t=e;t!==void 0&&(w===`-`&&(t=-Math.abs(t)),w===`+`&&(t=Math.abs(t))),h?.set(t??null),f?.(t)}function B(e){let t=e;return!x&&w!==`-`&&(t=Math.max(0,t)),w===`-`&&(t=Math.min(0,t)),E!==void 0&&(t=Math.max(E,t)),D!==void 0&&(t=Math.min(D,t)),t}let V=I!=null&&I!==``?Number(I):0;function H(){if(k||A||I==null||I===``)return;let e=Number(I);if(!Number.isFinite(e))return;let t=B(e);t!==e&&z(t)}function U(e){if(k||A)return!1;let t=B(V+e);return t!==V&&(z(t),!0)}let{start:ve,stop:ye}=te(e=>U(e*T));function be(e){if(e.key===`Enter`){H(),j?.(e);return}if(!G){j?.(e);return}if(e.key===`ArrowUp`){e.preventDefault(),U(e.shiftKey?T*10:T);return}if(e.key===`ArrowDown`){e.preventDefault(),U(e.shiftKey?-T*10:-T);return}j?.(e)}let W=w===`-`?`-`:w===`+`?`+`:ue,xe=i(),G=me&&!k&&!A,K=O===`split`||O===`auto`&&xe,q=B(V+T)!==V,J=B(V-T)!==V,Y={onStart:ve,onStop:ye},X=G&&!K?d(`div`,{className:c(o.arrows,y?.arrows),style:b?.arrows,children:[u(p,{className:y?.increaseButton,style:b?.increaseButton,label:N.increase,icon:re,disabled:!q,direction:1,...Y}),u(p,{className:y?.decreaseButton,style:b?.decreaseButton,label:N.decrease,icon:ne,disabled:!J,direction:-1,...Y})]}):null,Se=G?{role:`spinbutton`,"aria-valuenow":Number.isFinite(V)?V:void 0,"aria-valuemin":E,"aria-valuemax":D,"aria-valuetext":R||void 0}:{},Z=h?!h.touched||!h.hasError():!g,Q=G&&K,$=u(a,{label:ce,className:c(o.numberInput,{[o.numberInputSplit]:Q},Q?void 0:_,Q?void 0:y?.root,y?.input),style:{...Q?void 0:v,...Q?void 0:b?.root,...b?.input},value:R,onChange:()=>{},formValidator:null,isRequired:F,onBlur:()=>{H(),r.current&&(r.current.touched=!0)},isValid:Z,errorMessage:h?h.touched?h.getCurrentErrorMessage()??``:``:g??``,placeholder:pe,readOnly:k,disabled:A,suffix:X,classNames:X?{suffix:o.arrowSuffix}:void 0,inputMode:C===0?`numeric`:`decimal`,customInput:u(oe,{onValueChange:e=>z(e.floatValue),allowNegative:w==null&&x,decimalSeparator:S,thousandSeparator:le,decimalScale:C,fixedDecimalScale:fe,prefix:W,suffix:de,onKeyDown:be,getInputRef:_e,...Se,...M})});return Q?d(ee,{className:c(o.split,{[o.splitInvalid]:!Z},_,y?.root),style:{...v,...b?.root},children:[u(m,{className:y?.decreaseButton,style:b?.decreaseButton,label:N.decrease,icon:ie,disabled:!J,direction:-1,...Y}),$,u(m,{className:y?.increaseButton,style:b?.increaseButton,label:N.increase,icon:ae,disabled:!q,direction:1,...Y})]}):$}export{h as default};
1
+ "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{useLabels as t}from"../configProvider/useLabels.mjs";import{ThemeColor as n}from"../themeProvider/color.types.mjs";import{ButtonSize as r}from"../button/button.types.mjs";import i from"../button/button.mjs";import a from"../hooks/useCoarsePointer.mjs";import ee from"../buttonGroup/buttonGroup.mjs";import o from"../input/input.mjs";import s from"./numberInput.module.mjs";import te from"./useStepRepeat.mjs";import c from"react";import l from"classnames";import{FontAwesomeIcon as u}from"@fortawesome/react-fontawesome";import{faChevronDown as ne,faChevronUp as re,faMinus as ie,faPlus as ae}from"@fortawesome/free-solid-svg-icons";import{jsx as d,jsxs as f}from"react/jsx-runtime";import{NumericFormat as p}from"react-number-format";var oe={increase:`Increase`,decrease:`Decrease`};function m({label:e,disabled:t,direction:n,onStart:r,onStop:i}){return{tabIndex:-1,disabled:t,"aria-label":e,onPointerDown:e=>{(e.button===0||e.pointerType!==`mouse`)&&(e.preventDefault(),r(n,e.shiftKey?10:1))},onPointerUp:i,onPointerCancel:i,onContextMenu:e=>e.preventDefault()}}function h(e){return d(`button`,{type:`button`,...m(e),className:l(s.arrowBtn,e.className),style:e.style,children:d(u,{icon:e.icon,"aria-hidden":`true`})})}function g(e){let{disabled:t,...a}=m(e);return d(i,{...a,className:l(s.splitBtn,e.className),style:e.style,color:n.Neutral,size:r.Icon,disabled:t,icon:e.icon})}function _(n){let r=c.useRef(void 0),{value:i,onChange:u,formValidator:m,label:_,error:v,className:y,style:b,classNames:x,styles:S,allowNegative:C=!1,decimalSeparator:se,thousandSeparator:ce,prefix:le,suffix:ue,decimalScale:w,fixedDecimalScale:de=!0,placeholder:fe,sign:T,showArrows:pe=!1,step:E=1,min:D,max:O,arrowsLayout:k=`auto`,labels:me,readOnly:A=!1,disabled:j=!1,isRequired:he=!1,onKeyDown:M,ref:ge,...N}=e(`NumberInput`,n),P=t(`NumberInput`,me,oe),[,F]=c.useState(0);c.useEffect(()=>{if(r.current=m,!m)return;let e=()=>F(e=>e+1);return m.registerOnUpdateListener(e),()=>{m.removeOnUpdateListener(e)}},[m]);let I=he||!!m?.validators.find(e=>e.validatorId===`required`),L=m==null?i:m.value,R=T!=null&&L!=null?Math.abs(Number(L)):L,z=R==null?``:String(R);function B(e){let t=e;t!==void 0&&(T===`-`&&(t=-Math.abs(t)),T===`+`&&(t=Math.abs(t))),m?.set(t??null),u?.(t)}function V(e){let t=e;return!C&&T!==`-`&&(t=Math.max(0,t)),T===`-`&&(t=Math.min(0,t)),D!==void 0&&(t=Math.max(D,t)),O!==void 0&&(t=Math.min(O,t)),t}let H=L!=null&&L!==``?Number(L):0;function U(){if(A||j||L==null||L===``)return;let e=Number(L);if(!Number.isFinite(e))return;let t=V(e);t!==e&&B(t)}function W(e){if(A||j)return!1;let t=V(H+e);return t!==H&&(B(t),!0)}let{start:_e,stop:ve}=te(e=>W(e*E));function ye(e){if(e.key===`Enter`){U(),M?.(e);return}if(!G){M?.(e);return}if(e.key===`ArrowUp`){e.preventDefault(),W(e.shiftKey?E*10:E);return}if(e.key===`ArrowDown`){e.preventDefault(),W(e.shiftKey?-E*10:-E);return}M?.(e)}let be=T===`-`?`-`:T===`+`?`+`:le,xe=a(),G=pe&&!A&&!j,K=k===`split`||k===`auto`&&xe,q=V(H+E)!==H,J=V(H-E)!==H,Y={onStart:_e,onStop:ve},X=G&&!K?f(`div`,{className:l(s.arrows,x?.arrows),style:S?.arrows,children:[d(h,{className:x?.increaseButton,style:S?.increaseButton,label:P.increase,icon:re,disabled:!q,direction:1,...Y}),d(h,{className:x?.decreaseButton,style:S?.decreaseButton,label:P.decrease,icon:ne,disabled:!J,direction:-1,...Y})]}):null,Se=G?{role:`spinbutton`,"aria-valuenow":Number.isFinite(H)?H:void 0,"aria-valuemin":D,"aria-valuemax":O,"aria-valuetext":z||void 0}:{},Z=m?!m.touched||!m.hasError():!v,Q=G&&K,$=d(o,{label:_,className:l(s.numberInput,{[s.numberInputSplit]:Q},Q?void 0:y,Q?void 0:x?.root,x?.input),style:{...Q?void 0:b,...Q?void 0:S?.root,...S?.input},value:z,onChange:()=>{},formValidator:null,isRequired:I,onBlur:()=>{U(),r.current&&(r.current.touched=!0)},isValid:Z,errorMessage:m?m.touched?m.getCurrentErrorMessage()??``:``:v??``,placeholder:fe,readOnly:A,disabled:j,suffix:X,classNames:X?{suffix:s.arrowSuffix}:void 0,inputMode:w===0?`numeric`:`decimal`,customInput:d(p,{onValueChange:e=>B(e.floatValue),allowNegative:T==null&&C,decimalSeparator:se,thousandSeparator:ce,decimalScale:w,fixedDecimalScale:de,prefix:be,suffix:ue,onKeyDown:ye,getInputRef:ge,...Se,...N})});return Q?f(ee,{className:l(s.split,{[s.splitInvalid]:!Z},y,x?.root),style:{...b,...S?.root},children:[d(g,{className:x?.decreaseButton,style:S?.decreaseButton,label:P.decrease,icon:ie,disabled:!J,direction:-1,...Y}),$,d(g,{className:x?.increaseButton,style:S?.increaseButton,label:P.increase,icon:ae,disabled:!q,direction:1,...Y})]}):$}export{_ as default};
2
2
  //# sourceMappingURL=numberInput.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"numberInput.mjs","names":[],"sources":["../../../../package/common/numberInput/numberInput.tsx"],"sourcesContent":["import type { CSSProperties, ReactElement } from 'react';\nimport React from 'react';\nimport cx from 'classnames';\nimport { NumericFormat } from 'react-number-format';\nimport type { FormValidator } from 'services/formValidation';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronUp, faChevronDown, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons';\n\nimport styles from './numberInput.module.pcss';\nimport Input from '../input';\nimport Button from '../button';\nimport ButtonGroup from '../buttonGroup';\nimport { ButtonSize } from '../button/button.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { useCoarsePointer } from '../hooks/useCoarsePointer';\nimport { useStepRepeat } from './useStepRepeat';\nimport type { NumberInputLabels, NumberInputProps } from './numberInput.types';\n\nexport type { NumberInputProps, NumberInputArrowsLayout } from './numberInput.types';\n\ninterface StepButtonProps {\n label: string;\n className?: string;\n style?: CSSProperties;\n icon: typeof faPlus;\n disabled: boolean;\n direction: 1 | -1;\n onStart: (direction: 1 | -1, multiplier: number) => void;\n onStop: () => void;\n}\n\n/** Press-and-hold behaviour shared by both stepper layouts. */\nconst DEFAULT_LABELS: Required<NumberInputLabels> = {\n increase: 'Increase',\n decrease: 'Decrease',\n};\n\nfunction stepButtonProps({ label, disabled, direction, onStart, onStop }: StepButtonProps) {\n return {\n // The input keeps the tab stop — it carries role=\"spinbutton\", which announces the value\n // and the arrow-key affordance, so two more tab stops per field would be noise.\n tabIndex: -1,\n disabled,\n 'aria-label': label,\n // pointerdown (not click) keeps focus in the field and responds immediately on touch.\n // Deliberately no setPointerCapture: capturing retargets the following click to the captured\n // element, past the node Button's ripple listens on. useStepRepeat watches the window for\n // the release instead.\n onPointerDown: (e: React.PointerEvent<HTMLElement>) => {\n if (e.button !== 0 && e.pointerType === 'mouse') return;\n e.preventDefault();\n onStart(direction, e.shiftKey ? 10 : 1);\n },\n onPointerUp: onStop,\n onPointerCancel: onStop,\n onContextMenu: (e: React.SyntheticEvent) => e.preventDefault(),\n };\n}\n\n/** Stacked-layout chevron: borderless, sits inside the field's border. */\nfunction ChevronStepButton(props: StepButtonProps): ReactElement {\n return (\n <button\n type='button'\n {...stepButtonProps(props)}\n className={cx(styles.arrowBtn, props.className)}\n style={props.style}\n >\n <FontAwesomeIcon icon={props.icon} aria-hidden='true' />\n </button>\n );\n}\n\n/** Split-layout stepper: a standalone control, so a real Button. */\nfunction SplitStepButton(props: StepButtonProps): ReactElement {\n const { disabled, ...handlers } = stepButtonProps(props);\n return (\n <Button\n {...handlers}\n className={cx(styles.splitBtn, props.className)}\n style={props.style}\n size={ButtonSize.Icon}\n disabled={disabled}\n icon={props.icon}\n />\n );\n}\n\nfunction NumberInput(props: NumberInputProps): ReactElement {\n const fvRef = React.useRef<FormValidator | null | undefined>(undefined);\n const {\n value,\n onChange,\n formValidator,\n label,\n error,\n className,\n style,\n classNames,\n styles: slotStyles,\n allowNegative = false,\n decimalSeparator,\n thousandSeparator,\n prefix,\n suffix,\n decimalScale,\n fixedDecimalScale = true,\n placeholder,\n sign,\n showArrows = false,\n step = 1,\n min,\n max,\n arrowsLayout = 'auto',\n labels,\n readOnly = false,\n disabled = false,\n isRequired = false,\n onKeyDown: onKeyDownProp,\n ref,\n ...rest\n } = useComponentDefaults('NumberInput', props);\n const text = useLabels('NumberInput', labels, DEFAULT_LABELS);\n const [, forceUpdate] = React.useState(0);\n React.useEffect(() => {\n fvRef.current = formValidator;\n if (!formValidator) return;\n const listener = () => forceUpdate((n) => n + 1);\n formValidator.registerOnUpdateListener(listener);\n return () => {\n formValidator.removeOnUpdateListener(listener);\n };\n }, [formValidator]);\n const computedIsRequired =\n isRequired || !!formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required');\n const rawValue = formValidator != null ? (formValidator.value as number | string) : value;\n // When a sign is forced, display the absolute value — the prefix character carries the sign visually.\n const displayValue = sign != null && rawValue != null ? Math.abs(Number(rawValue)) : rawValue;\n const controlledValue = displayValue != null ? String(displayValue) : '';\n\n function handleValueChange(floatValue: number | undefined) {\n let out = floatValue;\n if (out !== undefined) {\n if (sign === '-') out = -Math.abs(out);\n if (sign === '+') out = Math.abs(out);\n }\n if (formValidator != null) formValidator.set(out ?? null);\n onChange?.(out);\n }\n\n function clamp(next: number): number {\n let out = next;\n if (!allowNegative && sign !== '-') out = Math.max(0, out);\n if (sign === '-') out = Math.min(0, out);\n if (min !== undefined) out = Math.max(min, out);\n if (max !== undefined) out = Math.min(max, out);\n return out;\n }\n\n const currentNumber = rawValue != null && rawValue !== '' ? Number(rawValue) : 0;\n\n /**\n * Called on blur and Enter, never per keystroke: rejecting an out-of-bounds edit as it is\n * typed swallows keystrokes on their way somewhere valid (with `max: 10`, typing `1` in front\n * of `8` to reach `18`). An empty field is skipped so a `min` is never typed in for the user.\n */\n function clampToBounds() {\n if (readOnly || disabled) return;\n if (rawValue == null || rawValue === '') return;\n const n = Number(rawValue);\n if (!Number.isFinite(n)) return;\n const next = clamp(n);\n if (next !== n) handleValueChange(next);\n }\n\n /** Returns whether the value moved, which is how useStepRepeat knows to end a hold at a bound. */\n function handleStep(delta: number): boolean {\n if (readOnly || disabled) return false;\n const next = clamp(currentNumber + delta);\n if (next === currentNumber) return false;\n handleValueChange(next);\n return true;\n }\n\n const { start: startRepeat, stop: stopRepeat } = useStepRepeat((delta) => handleStep(delta * step));\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n // Enter submits the surrounding form without ever blurring, so the blur clamp would not run.\n // Before the stepper guard, since the bounds apply with or without steppers.\n if (e.key === 'Enter') {\n clampToBounds();\n onKeyDownProp?.(e);\n return;\n }\n if (!showStepper) {\n onKeyDownProp?.(e);\n return;\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n handleStep(e.shiftKey ? step * 10 : step);\n return;\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n handleStep(e.shiftKey ? -step * 10 : -step);\n return;\n }\n onKeyDownProp?.(e);\n }\n\n const resolvedPrefix = sign === '-' ? '-' : sign === '+' ? '+' : prefix;\n\n const isCoarsePointer = useCoarsePointer();\n const showStepper = showArrows && !readOnly && !disabled;\n const isSplit = arrowsLayout === 'split' || (arrowsLayout === 'auto' && isCoarsePointer);\n\n const canIncrease = clamp(currentNumber + step) !== currentNumber;\n const canDecrease = clamp(currentNumber - step) !== currentNumber;\n\n const stepperProps = { onStart: startRepeat, onStop: stopRepeat };\n\n const stackedArrows =\n showStepper && !isSplit ? (\n <div className={cx(styles.arrows, classNames?.arrows)} style={slotStyles?.arrows}>\n <ChevronStepButton\n className={classNames?.increaseButton}\n style={slotStyles?.increaseButton}\n label={text.increase}\n icon={faChevronUp}\n disabled={!canIncrease}\n direction={1}\n {...stepperProps}\n />\n <ChevronStepButton\n className={classNames?.decreaseButton}\n style={slotStyles?.decreaseButton}\n label={text.decrease}\n icon={faChevronDown}\n disabled={!canDecrease}\n direction={-1}\n {...stepperProps}\n />\n </div>\n ) : null;\n\n // aria-* is spread onto NumericFormat, which forwards unknown props to the DOM input.\n // Input's own cloneElement doesn't set these keys, so they survive the clone.\n const spinButtonProps = showStepper\n ? {\n role: 'spinbutton',\n 'aria-valuenow': Number.isFinite(currentNumber) ? currentNumber : undefined,\n 'aria-valuemin': min,\n 'aria-valuemax': max,\n 'aria-valuetext': controlledValue || undefined,\n }\n : {};\n\n // Shared with the split group, whose buttons have to mirror the field's invalid state — they\n // form one continuous border.\n const isValid = formValidator ? (formValidator.touched ? !formValidator.hasError() : true) : !error;\n\n // In the split layout the ButtonGroup is the root, so the consumer's className and style\n // belong there rather than on the Input.\n const isSplitGroup = showStepper && isSplit;\n\n const input = (\n <Input\n label={label}\n className={cx(\n styles.numberInput,\n { [styles.numberInputSplit]: isSplitGroup },\n // In split mode the ButtonGroup around all three is the root, so the\n // consumer's root class belongs there rather than on the field.\n isSplitGroup ? undefined : className,\n isSplitGroup ? undefined : classNames?.root,\n classNames?.input,\n )}\n style={{\n ...(isSplitGroup ? undefined : style),\n ...(isSplitGroup ? undefined : slotStyles?.root),\n ...slotStyles?.input,\n }}\n value={controlledValue}\n onChange={() => {}}\n formValidator={null}\n isRequired={computedIsRequired}\n onBlur={() => {\n clampToBounds();\n if (fvRef.current) fvRef.current.touched = true;\n }}\n isValid={isValid}\n errorMessage={\n formValidator ? (formValidator.touched ? (formValidator.getCurrentErrorMessage() ?? '') : '') : (error ?? '')\n }\n placeholder={placeholder}\n readOnly={readOnly}\n disabled={disabled}\n suffix={stackedArrows}\n // Input centres its suffix wrapper; this lets the chevrons claim the field's full height.\n classNames={stackedArrows ? { suffix: styles.arrowSuffix } : undefined}\n inputMode={decimalScale === 0 ? 'numeric' : 'decimal'}\n customInput={\n <NumericFormat\n onValueChange={(vals) => handleValueChange(vals.floatValue)}\n allowNegative={sign != null ? false : allowNegative}\n decimalSeparator={decimalSeparator}\n thousandSeparator={thousandSeparator}\n decimalScale={decimalScale}\n fixedDecimalScale={fixedDecimalScale}\n prefix={resolvedPrefix}\n suffix={suffix}\n onKeyDown={handleKeyDown}\n // react-number-format owns the <input>, so its ref is reachable only through this prop.\n getInputRef={ref}\n {...spinButtonProps}\n {...rest}\n />\n }\n />\n );\n\n if (!isSplitGroup) return input;\n\n return (\n // ButtonGroup collapses the doubled borders and flattens the inner corners, so the three\n // read as one control.\n <ButtonGroup\n className={cx(styles.split, { [styles.splitInvalid]: !isValid }, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <SplitStepButton\n className={classNames?.decreaseButton}\n style={slotStyles?.decreaseButton}\n label={text.decrease}\n icon={faMinus}\n disabled={!canDecrease}\n direction={-1}\n {...stepperProps}\n />\n {input}\n <SplitStepButton\n className={classNames?.increaseButton}\n style={slotStyles?.increaseButton}\n label={text.increase}\n icon={faPlus}\n disabled={!canIncrease}\n direction={1}\n {...stepperProps}\n />\n </ButtonGroup>\n );\n}\n\nexport default NumberInput;\n"],"mappings":"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"}
1
+ {"version":3,"file":"numberInput.mjs","names":[],"sources":["../../../../package/common/numberInput/numberInput.tsx"],"sourcesContent":["import type { CSSProperties, ReactElement } from 'react';\nimport React from 'react';\nimport cx from 'classnames';\nimport { NumericFormat } from 'react-number-format';\nimport type { FormValidator } from 'services/formValidation';\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome';\nimport { faChevronUp, faChevronDown, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons';\n\nimport styles from './numberInput.module.pcss';\nimport Input from '../input';\nimport Button from '../button';\nimport ButtonGroup from '../buttonGroup';\nimport { ButtonSize } from '../button/button.types';\nimport { useComponentDefaults, useLabels } from '../configProvider';\nimport { useCoarsePointer } from '../hooks/useCoarsePointer';\nimport { useStepRepeat } from './useStepRepeat';\nimport type { NumberInputLabels, NumberInputProps } from './numberInput.types';\nimport { ThemeColor } from '../themeProvider/color.types';\n\nexport type { NumberInputProps, NumberInputArrowsLayout } from './numberInput.types';\n\ninterface StepButtonProps {\n label: string;\n className?: string;\n style?: CSSProperties;\n icon: typeof faPlus;\n disabled: boolean;\n direction: 1 | -1;\n onStart: (direction: 1 | -1, multiplier: number) => void;\n onStop: () => void;\n}\n\n/** Press-and-hold behaviour shared by both stepper layouts. */\nconst DEFAULT_LABELS: Required<NumberInputLabels> = {\n increase: 'Increase',\n decrease: 'Decrease',\n};\n\nfunction stepButtonProps({ label, disabled, direction, onStart, onStop }: StepButtonProps) {\n return {\n // The input keeps the tab stop — it carries role=\"spinbutton\", which announces the value\n // and the arrow-key affordance, so two more tab stops per field would be noise.\n tabIndex: -1,\n disabled,\n 'aria-label': label,\n // pointerdown (not click) keeps focus in the field and responds immediately on touch.\n // Deliberately no setPointerCapture: capturing retargets the following click to the captured\n // element, past the node Button's ripple listens on. useStepRepeat watches the window for\n // the release instead.\n onPointerDown: (e: React.PointerEvent<HTMLElement>) => {\n if (e.button !== 0 && e.pointerType === 'mouse') return;\n e.preventDefault();\n onStart(direction, e.shiftKey ? 10 : 1);\n },\n onPointerUp: onStop,\n onPointerCancel: onStop,\n onContextMenu: (e: React.SyntheticEvent) => e.preventDefault(),\n };\n}\n\n/** Stacked-layout chevron: borderless, sits inside the field's border. */\nfunction ChevronStepButton(props: StepButtonProps): ReactElement {\n return (\n <button\n type='button'\n {...stepButtonProps(props)}\n className={cx(styles.arrowBtn, props.className)}\n style={props.style}\n >\n <FontAwesomeIcon icon={props.icon} aria-hidden='true' />\n </button>\n );\n}\n\n/** Split-layout stepper: a standalone control, so a real Button. */\nfunction SplitStepButton(props: StepButtonProps): ReactElement {\n const { disabled, ...handlers } = stepButtonProps(props);\n return (\n <Button\n {...handlers}\n className={cx(styles.splitBtn, props.className)}\n style={props.style}\n color={ThemeColor.Neutral}\n size={ButtonSize.Icon}\n disabled={disabled}\n icon={props.icon}\n />\n );\n}\n\nfunction NumberInput(props: NumberInputProps): ReactElement {\n const fvRef = React.useRef<FormValidator | null | undefined>(undefined);\n const {\n value,\n onChange,\n formValidator,\n label,\n error,\n className,\n style,\n classNames,\n styles: slotStyles,\n allowNegative = false,\n decimalSeparator,\n thousandSeparator,\n prefix,\n suffix,\n decimalScale,\n fixedDecimalScale = true,\n placeholder,\n sign,\n showArrows = false,\n step = 1,\n min,\n max,\n arrowsLayout = 'auto',\n labels,\n readOnly = false,\n disabled = false,\n isRequired = false,\n onKeyDown: onKeyDownProp,\n ref,\n ...rest\n } = useComponentDefaults('NumberInput', props);\n const text = useLabels('NumberInput', labels, DEFAULT_LABELS);\n const [, forceUpdate] = React.useState(0);\n React.useEffect(() => {\n fvRef.current = formValidator;\n if (!formValidator) return;\n const listener = () => forceUpdate((n) => n + 1);\n formValidator.registerOnUpdateListener(listener);\n return () => {\n formValidator.removeOnUpdateListener(listener);\n };\n }, [formValidator]);\n const computedIsRequired =\n isRequired || !!formValidator?.validators.find((v) => (v as { validatorId?: string }).validatorId === 'required');\n const rawValue = formValidator != null ? (formValidator.value as number | string) : value;\n // When a sign is forced, display the absolute value — the prefix character carries the sign visually.\n const displayValue = sign != null && rawValue != null ? Math.abs(Number(rawValue)) : rawValue;\n const controlledValue = displayValue != null ? String(displayValue) : '';\n\n function handleValueChange(floatValue: number | undefined) {\n let out = floatValue;\n if (out !== undefined) {\n if (sign === '-') out = -Math.abs(out);\n if (sign === '+') out = Math.abs(out);\n }\n if (formValidator != null) formValidator.set(out ?? null);\n onChange?.(out);\n }\n\n function clamp(next: number): number {\n let out = next;\n if (!allowNegative && sign !== '-') out = Math.max(0, out);\n if (sign === '-') out = Math.min(0, out);\n if (min !== undefined) out = Math.max(min, out);\n if (max !== undefined) out = Math.min(max, out);\n return out;\n }\n\n const currentNumber = rawValue != null && rawValue !== '' ? Number(rawValue) : 0;\n\n /**\n * Called on blur and Enter, never per keystroke: rejecting an out-of-bounds edit as it is\n * typed swallows keystrokes on their way somewhere valid (with `max: 10`, typing `1` in front\n * of `8` to reach `18`). An empty field is skipped so a `min` is never typed in for the user.\n */\n function clampToBounds() {\n if (readOnly || disabled) return;\n if (rawValue == null || rawValue === '') return;\n const n = Number(rawValue);\n if (!Number.isFinite(n)) return;\n const next = clamp(n);\n if (next !== n) handleValueChange(next);\n }\n\n /** Returns whether the value moved, which is how useStepRepeat knows to end a hold at a bound. */\n function handleStep(delta: number): boolean {\n if (readOnly || disabled) return false;\n const next = clamp(currentNumber + delta);\n if (next === currentNumber) return false;\n handleValueChange(next);\n return true;\n }\n\n const { start: startRepeat, stop: stopRepeat } = useStepRepeat((delta) => handleStep(delta * step));\n\n function handleKeyDown(e: React.KeyboardEvent<HTMLInputElement>) {\n // Enter submits the surrounding form without ever blurring, so the blur clamp would not run.\n // Before the stepper guard, since the bounds apply with or without steppers.\n if (e.key === 'Enter') {\n clampToBounds();\n onKeyDownProp?.(e);\n return;\n }\n if (!showStepper) {\n onKeyDownProp?.(e);\n return;\n }\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n handleStep(e.shiftKey ? step * 10 : step);\n return;\n }\n if (e.key === 'ArrowDown') {\n e.preventDefault();\n handleStep(e.shiftKey ? -step * 10 : -step);\n return;\n }\n onKeyDownProp?.(e);\n }\n\n const resolvedPrefix = sign === '-' ? '-' : sign === '+' ? '+' : prefix;\n\n const isCoarsePointer = useCoarsePointer();\n const showStepper = showArrows && !readOnly && !disabled;\n const isSplit = arrowsLayout === 'split' || (arrowsLayout === 'auto' && isCoarsePointer);\n\n const canIncrease = clamp(currentNumber + step) !== currentNumber;\n const canDecrease = clamp(currentNumber - step) !== currentNumber;\n\n const stepperProps = { onStart: startRepeat, onStop: stopRepeat };\n\n const stackedArrows =\n showStepper && !isSplit ? (\n <div className={cx(styles.arrows, classNames?.arrows)} style={slotStyles?.arrows}>\n <ChevronStepButton\n className={classNames?.increaseButton}\n style={slotStyles?.increaseButton}\n label={text.increase}\n icon={faChevronUp}\n disabled={!canIncrease}\n direction={1}\n {...stepperProps}\n />\n <ChevronStepButton\n className={classNames?.decreaseButton}\n style={slotStyles?.decreaseButton}\n label={text.decrease}\n icon={faChevronDown}\n disabled={!canDecrease}\n direction={-1}\n {...stepperProps}\n />\n </div>\n ) : null;\n\n // aria-* is spread onto NumericFormat, which forwards unknown props to the DOM input.\n // Input's own cloneElement doesn't set these keys, so they survive the clone.\n const spinButtonProps = showStepper\n ? {\n role: 'spinbutton',\n 'aria-valuenow': Number.isFinite(currentNumber) ? currentNumber : undefined,\n 'aria-valuemin': min,\n 'aria-valuemax': max,\n 'aria-valuetext': controlledValue || undefined,\n }\n : {};\n\n // Shared with the split group, whose buttons have to mirror the field's invalid state — they\n // form one continuous border.\n const isValid = formValidator ? (formValidator.touched ? !formValidator.hasError() : true) : !error;\n\n // In the split layout the ButtonGroup is the root, so the consumer's className and style\n // belong there rather than on the Input.\n const isSplitGroup = showStepper && isSplit;\n\n const input = (\n <Input\n label={label}\n className={cx(\n styles.numberInput,\n { [styles.numberInputSplit]: isSplitGroup },\n // In split mode the ButtonGroup around all three is the root, so the\n // consumer's root class belongs there rather than on the field.\n isSplitGroup ? undefined : className,\n isSplitGroup ? undefined : classNames?.root,\n classNames?.input,\n )}\n style={{\n ...(isSplitGroup ? undefined : style),\n ...(isSplitGroup ? undefined : slotStyles?.root),\n ...slotStyles?.input,\n }}\n value={controlledValue}\n onChange={() => {}}\n formValidator={null}\n isRequired={computedIsRequired}\n onBlur={() => {\n clampToBounds();\n if (fvRef.current) fvRef.current.touched = true;\n }}\n isValid={isValid}\n errorMessage={\n formValidator ? (formValidator.touched ? (formValidator.getCurrentErrorMessage() ?? '') : '') : (error ?? '')\n }\n placeholder={placeholder}\n readOnly={readOnly}\n disabled={disabled}\n suffix={stackedArrows}\n // Input centres its suffix wrapper; this lets the chevrons claim the field's full height.\n classNames={stackedArrows ? { suffix: styles.arrowSuffix } : undefined}\n inputMode={decimalScale === 0 ? 'numeric' : 'decimal'}\n customInput={\n <NumericFormat\n onValueChange={(vals) => handleValueChange(vals.floatValue)}\n allowNegative={sign != null ? false : allowNegative}\n decimalSeparator={decimalSeparator}\n thousandSeparator={thousandSeparator}\n decimalScale={decimalScale}\n fixedDecimalScale={fixedDecimalScale}\n prefix={resolvedPrefix}\n suffix={suffix}\n onKeyDown={handleKeyDown}\n // react-number-format owns the <input>, so its ref is reachable only through this prop.\n getInputRef={ref}\n {...spinButtonProps}\n {...rest}\n />\n }\n />\n );\n\n if (!isSplitGroup) return input;\n\n return (\n // ButtonGroup collapses the doubled borders and flattens the inner corners, so the three\n // read as one control.\n <ButtonGroup\n className={cx(styles.split, { [styles.splitInvalid]: !isValid }, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <SplitStepButton\n className={classNames?.decreaseButton}\n style={slotStyles?.decreaseButton}\n label={text.decrease}\n icon={faMinus}\n disabled={!canDecrease}\n direction={-1}\n {...stepperProps}\n />\n {input}\n <SplitStepButton\n className={classNames?.increaseButton}\n style={slotStyles?.increaseButton}\n label={text.increase}\n icon={faPlus}\n disabled={!canIncrease}\n direction={1}\n {...stepperProps}\n />\n </ButtonGroup>\n );\n}\n\nexport default NumberInput;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"numberInput.module.mjs","names":[],"sources":["../../../../package/common/numberInput/numberInput.module.pcss"],"sourcesContent":[".numberInput {\n /* Only set when the split layout wraps this input in a flex row. */\n &-split {\n flex: 1;\n min-width: 0;\n }\n}\n\n/* ── Stacked layout: chevrons inside the field's right edge (mouse-oriented) ───────── */\n\n/* Input's `suffix` slot: stretch to the field so the chevrons get its full height. */\n.arrowSuffix {\n align-self: stretch;\n display: flex;\n padding-right: 0;\n}\n\n.arrows {\n display: flex;\n flex-direction: column;\n align-self: stretch;\n border-left: 1px solid var(--border-color);\n\n /* Sitting flush against the field's edge, the chevrons' background would square off its\n rounded corner, so it is clipped. Read from --group-radius-* like Input's own border, so it\n still flattens inside a ButtonGroup. */\n overflow: hidden;\n border-top-right-radius: var(--group-radius-tr, var(--default-border-radius));\n border-bottom-right-radius: var(--group-radius-br, var(--default-border-radius));\n}\n\n.arrowBtn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n padding: 0 0.5em;\n\n /* No min-height: the two halves divide the field's own height, so a stepper never makes a\n NumberInput taller than a plain Input. In em, so it tracks the field's text size. */\n min-width: 1.75em;\n font-size: 0.75em;\n line-height: 1;\n color: var(--text-dark);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n /* So a long press reads as a hold, not a text gesture. */\n touch-action: manipulation;\n user-select: none;\n -webkit-user-select: none;\n -webkit-touch-callout: none;\n transition:\n color 0.15s,\n opacity 0.15s;\n\n &:disabled {\n cursor: default;\n opacity: 0.35;\n }\n\n &:focus-visible {\n outline: 2px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -2px;\n }\n\n /* (hover: hover) only — on touch a :hover state latches after a tap until you tap elsewhere.\n\n Colour-only, unlike the --background-accent-light hover surface used elsewhere in the\n library: this button sits flush against .arrows' 1px --border-color divider, and in dark\n palettes those two tokens are close enough in luminance that painting the surface right up\n to the line eats most of the divider's already-low contrast, under the pointer. Tuning the\n hover colour per theme would not hold — the collision is between three tokens (divider,\n hover surface, field background), so any new theme can reintroduce it. The chevron turning\n --primary-color is affordance enough on a target this small. */\n @media (hover: hover) {\n &:hover:not(:disabled) {\n color: var(--primary-color);\n }\n }\n\n &:active:not(:disabled) {\n color: var(--primary-color);\n }\n}\n\n/* ── Split layout: full-height − / + flanking the field (touch-oriented) ───────────── */\n\n/* ButtonGroup is inline-flex; a form field fills its row instead, with the Input taking the\n slack (see .numberInput-split). The class is repeated to beat `.buttonGroup`'s own `display`\n without depending on which CSS module the bundler emits first. */\n.split.split {\n display: flex;\n width: 100%;\n}\n\n.splitBtn {\n /* Set through Button's own theme hooks: custom properties cascade, so no specificity fight.\n Same surface and border as the field, so the three read as one control.\n\n Resting state only, deliberately — Button has no :hover or :active anywhere, and press\n feedback across the library is the Ripple. */\n --button-default-background: var(--input-background);\n --button-default-color: var(--text-dark);\n --button-default-border: 1px solid var(--border-color);\n\n flex: 0 0 auto;\n\n /* No fixed height — ButtonGroup stretches these to the field.\n\n The width repeats Input's own height formula (`line-height: 1.25` plus the 8px/6px padding\n of `.input-field-default`) to come out square at any text size, and must follow it if that\n padding changes. `aspect-ratio: 1` cannot do this: a flex item's main size is resolved from\n its content before the stretched cross size exists. */\n align-self: stretch;\n width: calc(1.25em + 14px);\n touch-action: manipulation;\n user-select: none;\n -webkit-user-select: none;\n -webkit-touch-callout: none;\n transition: opacity 0.15s;\n\n &:disabled {\n opacity: 0.35;\n }\n\n &:focus-visible {\n outline: 2px solid var(--focus-border-color, var(--primary-color));\n outline-offset: 2px;\n }\n}\n\n/* One continuous border, so the buttons follow the field's focus and error colours — left grey,\n the group reads as a box inside a box. */\n.split:focus-within .splitBtn {\n --button-default-border: 1px solid var(--focus-border-color, var(--primary-color));\n}\n\n.split-invalid .splitBtn {\n --button-default-border: 1px solid var(--error-border-color);\n}\n"],"mappings":""}
1
+ {"version":3,"file":"numberInput.module.mjs","names":[],"sources":["../../../../package/common/numberInput/numberInput.module.pcss"],"sourcesContent":[".numberInput {\n /* Only set when the split layout wraps this input in a flex row. */\n &-split {\n flex: 1;\n min-width: 0;\n }\n}\n\n/* ── Stacked layout: chevrons inside the field's right edge (mouse-oriented) ───────── */\n\n/* Input's `suffix` slot: stretch to the field so the chevrons get its full height. */\n.arrowSuffix {\n align-self: stretch;\n display: flex;\n padding-right: 0;\n}\n\n.arrows {\n display: flex;\n flex-direction: column;\n align-self: stretch;\n border-left: 1px solid var(--border-color);\n\n /* Sitting flush against the field's edge, the chevrons' background would square off its\n rounded corner, so it is clipped. Read from --group-radius-* like Input's own border, so it\n still flattens inside a ButtonGroup. */\n overflow: hidden;\n border-top-right-radius: var(--group-radius-tr, var(--default-border-radius));\n border-bottom-right-radius: var(--group-radius-br, var(--default-border-radius));\n}\n\n.arrowBtn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n background: none;\n border: none;\n padding: 0 0.5em;\n\n /* No min-height: the two halves divide the field's own height, so a stepper never makes a\n NumberInput taller than a plain Input. In em, so it tracks the field's text size. */\n min-width: 1.75em;\n font-size: 0.75em;\n line-height: 1;\n color: var(--text-dark);\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n /* So a long press reads as a hold, not a text gesture. */\n touch-action: manipulation;\n user-select: none;\n -webkit-user-select: none;\n -webkit-touch-callout: none;\n transition:\n color 0.15s,\n opacity 0.15s;\n\n &:disabled {\n cursor: default;\n opacity: 0.35;\n }\n\n &:focus-visible {\n outline: 2px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -2px;\n }\n\n /* (hover: hover) only — on touch a :hover state latches after a tap until you tap elsewhere.\n\n Colour-only, unlike the --background-accent-light hover surface used elsewhere in the\n library: this button sits flush against .arrows' 1px --border-color divider, and in dark\n palettes those two tokens are close enough in luminance that painting the surface right up\n to the line eats most of the divider's already-low contrast, under the pointer. Tuning the\n hover colour per theme would not hold — the collision is between three tokens (divider,\n hover surface, field background), so any new theme can reintroduce it. The chevron turning\n --primary-color is affordance enough on a target this small. */\n @media (hover: hover) {\n &:hover:not(:disabled) {\n color: var(--primary-color);\n }\n }\n\n &:active:not(:disabled) {\n color: var(--primary-color);\n }\n}\n\n/* ── Split layout: full-height − / + flanking the field (touch-oriented) ───────────── */\n\n/* ButtonGroup is inline-flex; a form field fills its row instead, with the Input taking the\n slack (see .numberInput-split). The class is repeated to beat `.buttonGroup`'s own `display`\n without depending on which CSS module the bundler emits first. */\n.split.split {\n display: flex;\n width: 100%;\n}\n\n/* Two classes, to outrank Button's one-class rules whichever stylesheet loads first. Resting state\n only: Button's hover and press layer tints over it. */\n.split .splitBtn {\n background: var(--input-background);\n color: var(--text-dark);\n border-color: var(--border-color);\n}\n\n.splitBtn {\n flex: 0 0 auto;\n\n /* No fixed height — ButtonGroup stretches these to the field.\n\n The width repeats Input's own height formula (`line-height: 1.25` plus the 8px/6px padding\n of `.input-field-default`) to come out square at any text size, and must follow it if that\n padding changes. `aspect-ratio: 1` cannot do this: a flex item's main size is resolved from\n its content before the stretched cross size exists. */\n align-self: stretch;\n width: calc(1.25em + 14px);\n touch-action: manipulation;\n user-select: none;\n -webkit-user-select: none;\n -webkit-touch-callout: none;\n transition: opacity 0.15s;\n\n &:disabled {\n opacity: 0.35;\n }\n\n &:focus-visible {\n outline: 2px solid var(--focus-border-color, var(--primary-color));\n outline-offset: 2px;\n }\n}\n\n/* One continuous border, so the buttons follow the field's focus and error colours — left grey,\n the group reads as a box inside a box. */\n.split:focus-within .splitBtn {\n border-color: var(--focus-border-color, var(--primary-color));\n}\n\n.split-invalid .splitBtn {\n border-color: var(--error-border-color);\n}\n"],"mappings":""}
@@ -1,2 +1,2 @@
1
- "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{usePresentationDefault as t}from"../configProvider/usePresentationDefault.mjs";import{Presentation as n}from"../configProvider/presentation.types.mjs";import r from"../bodyEnd/bodyEnd.mjs";import i from"./popover.module.mjs";import a from"../styles/sheet.module.mjs";import ee from"../sheetBackdrop/sheetBackdrop.mjs";import{PopoverAlign as te,PopoverPlacement as ne,PopoverTrigger as o}from"./popover.types.mjs";import{usePopoverPosition as re}from"./usePopoverPosition.mjs";import{tabbablesIn as s,useFocusBoundary as ie}from"../hooks/useFocusBoundary.mjs";import ae from"../hooks/useSheet.mjs";import{useOverlay as oe}from"../hooks/useOverlay.mjs";import{useCallback as c,useEffect as l,useRef as u,useState as d}from"react";import f from"classnames";import{jsx as p,jsxs as m}from"react/jsx-runtime";var se=1e3;function h(e,t){return e?{current:e}:t}function g(g){let{children:ce=null,anchor:_,content:le=null,placement:v=ne.Bottom,align:ue=te.Center,trigger:y=o.Click,isOpen:b,defaultOpen:de=!1,onOpenChange:x=()=>{},offset:fe=10,withArrow:S=!0,closeOnOutsideClick:C=!0,closeOnEscape:pe=!0,openDelay:me=100,closeDelay:he=120,disabled:w=!1,focusOnOpen:T=`keyboard`,closeOnTriggerClick:ge=!0,width:_e=`auto`,constrainWidth:ve=!0,presentation:ye,className:be,style:xe,classNames:E,styles:D,...Se}=e(`Popover`,g),O=u(null),k=u(null),A=u(null),j=u(null),M=u(void 0),N=u(!1),P=u(!1),F=u(!1),I=u(null);function L(){N.current=!0}function Ce(e){L(),I.current=e.target}let[R,we]=d(()=>({anchor:_,ref:h(_,O)}));R.anchor!==_&&we({anchor:_,ref:h(_,O)});let z=R.ref,B=u(null),V=_!==void 0;l(()=>{if(!V)return;function e(e){B.current={target:e.target,keyboard:e.detail===0,time:performance.now()}}return document.addEventListener(`click`,e,!0),()=>document.removeEventListener(`click`,e,!0)},[V]);let H=b!==void 0,[Te,Ee]=d(de),U=H?b:Te,W=u(x);l(()=>{W.current=x});let G=c((e,t=!1)=>{w&&e||(e&&(F.current=t),H||Ee(e),W.current(e,{fromKeyboard:t}))},[w,H]),De=t(),Oe=ye??De??n.Default,{isSheet:ke,sheetStyle:Ae}=ae(Oe),K=ke&&y===o.Click,q=u(!1),J=u(!1);function Y(){J.current=!1;let e=s(A.current);e.length!==0&&(k.current?.setAttribute(`role`,`dialog`),e[0].focus())}re({isOpen:!!U&&!K,placement:v,align:ue,offset:fe,triggerRef:z,floatingRef:k,panelRef:A,arrowRef:S?j:{current:null},matchTriggerWidth:_e===`trigger`,onShown:()=>{q.current=!0,J.current&&Y()}});let{restoreFocusToTrigger:X,focusAfterTrigger:je}=ie({triggerRef:z,contentRef:k}),Z=u(X);l(()=>{Z.current=X});let Q=u(!1),Me=c(e=>{if(k.current=e,Q.current=!1,e)return()=>{k.current=null;let t=document.activeElement;Q.current=!!t&&(e.contains(t)||t===I.current)}},[]);l(()=>{if(U||!Q.current)return;Q.current=!1;let e=document.activeElement;(!e||e===document.body)&&Z.current()},[U]),oe({isActive:!!U,containerRef:A,onEscape:pe?()=>{k.current?.contains(document.activeElement)&&X(),G(!1,!0)}:void 0,trapFocus:!1,lockScroll:K}),l(()=>{if(!U){F.current=!1,q.current=!1,J.current=!1;return}let e=B.current;B.current=null;let t=!!_&&!!e?.keyboard&&_.contains(e.target)&&performance.now()-e.time<se;(T===!0||T===`keyboard`&&(F.current||t))&&y===o.Click&&(q.current?Y():J.current=!0)},[U,y,T,_]);function Ne(e){if(e.key!==`Tab`||y!==o.Click)return;let t=s(A.current);if(t.length===0)return;let n=document.activeElement;!e.shiftKey&&n===t[t.length-1]?(e.preventDefault(),je(),G(!1,!0)):e.shiftKey&&n===t[0]&&(e.preventDefault(),X(),G(!1,!0))}l(()=>{if(!U)return;N.current=!1,P.current=!1;function e(e){return!!(k.current?.contains(e)||O.current?.contains(e)||z.current?.contains(e))}function t(t){let n=N.current;return N.current=!1,n||e(t.target)}function n(e){P.current=!t(e)}function r(e){let n=P.current;P.current=!1,!t(e)&&n&&G(!1)}function i(n){if(t(n))return;let r=n.relatedTarget;r&&(r===I.current||e(r))&&G(!1,!P.current)}return C&&y!==o.Hover&&(window.addEventListener(`mousedown`,n),window.addEventListener(`mouseup`,r),document.addEventListener(`focusin`,i)),()=>{window.removeEventListener(`mousedown`,n),window.removeEventListener(`mouseup`,r),document.removeEventListener(`focusin`,i)}},[U,C,y,G,z]),l(()=>()=>clearTimeout(M.current),[]);function $(e){clearTimeout(M.current),M.current=setTimeout(()=>G(e),e?me:he)}function Pe(e){U||e.key!==`ArrowDown`&&e.key!==`ArrowUp`||(e.preventDefault(),G(!0,!0))}let Fe=y===o.Click?{onClick:e=>{(!U||ge)&&G(!U,e.detail===0)},onKeyDown:Pe}:y===o.Hover?{onMouseEnter:()=>$(!0),onMouseLeave:()=>$(!1)}:{onFocus:()=>G(!0),onBlur:()=>G(!1)},Ie=y===o.Hover?{onMouseEnter:()=>$(!0),onMouseLeave:()=>$(!1)}:{};return m(`div`,{...Se,className:f(i.popover,be,E?.root),style:{...xe,...D?.root},children:[p(`div`,{ref:O,className:f(i.popoverTrigger,E?.trigger),style:D?.trigger,...Fe,children:ce}),U&&m(r,{children:[K&&p(ee,{onClose:()=>{C&&G(!1)},className:E?.backdrop,style:D?.backdrop}),m(`div`,{ref:Me,className:f(K?a.sheet:i.popoverFloating,E?.floating),style:{...K?Ae:void 0,...D?.floating},"data-placement":v,onKeyDown:Ne,onMouseDownCapture:L,onMouseUpCapture:L,onFocusCapture:Ce,...Ie,children:[p(`div`,{ref:A,className:f(i.popoverPanel,ve&&i.popoverPanelConstrained,K&&a.sheetPanel,E?.panel),style:D?.panel,children:le}),S&&!K&&p(`div`,{ref:j,className:f(i.popoverArrow,E?.arrow),style:D?.arrow})]})]})]})}export{g as default};
1
+ "use client";import{useComponentDefaults as e}from"../configProvider/useComponentDefaults.mjs";import{usePresentationDefault as t}from"../configProvider/usePresentationDefault.mjs";import{Presentation as n}from"../configProvider/presentation.types.mjs";import r from"../bodyEnd/bodyEnd.mjs";import i from"./popover.module.mjs";import a from"../styles/sheet.module.mjs";import ee from"../sheetBackdrop/sheetBackdrop.mjs";import{PopoverAlign as te,PopoverPlacement as ne,PopoverTrigger as o}from"./popover.types.mjs";import{usePopoverPosition as re}from"./usePopoverPosition.mjs";import{tabbablesIn as s,useFocusBoundary as ie}from"../hooks/useFocusBoundary.mjs";import ae from"../hooks/useSheet.mjs";import{useOverlay as oe}from"../hooks/useOverlay.mjs";import{markDismiss as c}from"../utils/dismissClick.mjs";import{useCallback as l,useEffect as u,useRef as d,useState as f}from"react";import p from"classnames";import{jsx as m,jsxs as h}from"react/jsx-runtime";var se=1e3;function g(e,t){return e?{current:e}:t}function _(_){let{children:ce=null,anchor:v,content:le=null,placement:y=ne.Bottom,align:ue=te.Center,trigger:b=o.Click,isOpen:x,defaultOpen:de=!1,onOpenChange:S=()=>{},offset:fe=10,withArrow:C=!0,closeOnOutsideClick:w=!0,closeOnEscape:pe=!0,openDelay:me=100,closeDelay:he=120,disabled:T=!1,focusOnOpen:E=`keyboard`,closeOnTriggerClick:ge=!0,width:_e=`auto`,constrainWidth:ve=!0,presentation:ye,className:be,style:xe,classNames:D,styles:O,...Se}=e(`Popover`,_),k=d(null),A=d(null),j=d(null),M=d(null),N=d(void 0),P=d(!1),F=d(!1),I=d(!1),L=d(null);function R(){P.current=!0}function Ce(e){R(),L.current=e.target}let[z,we]=f(()=>({anchor:v,ref:g(v,k)}));z.anchor!==v&&we({anchor:v,ref:g(v,k)});let B=z.ref,V=d(null),H=v!==void 0;u(()=>{if(!H)return;function e(e){V.current={target:e.target,keyboard:e.detail===0,time:performance.now()}}return document.addEventListener(`click`,e,!0),()=>document.removeEventListener(`click`,e,!0)},[H]);let U=x!==void 0,[Te,Ee]=f(de),W=U?x:Te,G=d(S);u(()=>{G.current=S});let K=l((e,t=!1)=>{T&&e||(e&&(I.current=t),U||Ee(e),G.current(e,{fromKeyboard:t}))},[T,U]),De=t(),Oe=ye??De??n.Default,{isSheet:ke,sheetStyle:Ae}=ae(Oe),q=ke&&b===o.Click,J=d(!1),Y=d(!1);function X(){Y.current=!1;let e=s(j.current);e.length!==0&&(A.current?.setAttribute(`role`,`dialog`),e[0].focus())}re({isOpen:!!W&&!q,placement:y,align:ue,offset:fe,triggerRef:B,floatingRef:A,panelRef:j,arrowRef:C?M:{current:null},matchTriggerWidth:_e===`trigger`,onShown:()=>{J.current=!0,Y.current&&X()}});let{restoreFocusToTrigger:Z,focusAfterTrigger:je}=ie({triggerRef:B,contentRef:A}),Me=d(Z);u(()=>{Me.current=Z});let Q=d(!1),Ne=l(e=>{if(A.current=e,Q.current=!1,e)return()=>{A.current=null;let t=document.activeElement;Q.current=!!t&&(e.contains(t)||t===L.current)}},[]);u(()=>{if(W||!Q.current)return;Q.current=!1;let e=document.activeElement;(!e||e===document.body)&&Me.current()},[W]),oe({isActive:!!W,containerRef:j,onEscape:pe?()=>{A.current?.contains(document.activeElement)&&Z(),K(!1,!0)}:void 0,trapFocus:!1,lockScroll:q}),u(()=>{if(!W){I.current=!1,J.current=!1,Y.current=!1;return}let e=V.current;V.current=null;let t=!!v&&!!e?.keyboard&&v.contains(e.target)&&performance.now()-e.time<se;(E===!0||E===`keyboard`&&(I.current||t))&&b===o.Click&&(J.current?X():Y.current=!0)},[W,b,E,v]);function Pe(e){if(e.key!==`Tab`||b!==o.Click)return;let t=s(j.current);if(t.length===0)return;let n=document.activeElement;!e.shiftKey&&n===t[t.length-1]?(e.preventDefault(),je(),K(!1,!0)):e.shiftKey&&n===t[0]&&(e.preventDefault(),Z(),K(!1,!0))}u(()=>{if(!W)return;P.current=!1,F.current=!1;function e(e){return!!(A.current?.contains(e)||k.current?.contains(e)||B.current?.contains(e))}function t(t){let n=P.current;return P.current=!1,n||e(t.target)}function n(e){F.current=!t(e)}function r(e){let n=F.current;F.current=!1,!t(e)&&n&&(c(),K(!1))}function i(n){if(t(n))return;let r=n.relatedTarget;if(!r||r!==L.current&&!e(r))return;let i=F.current;i&&c(),K(!1,!i)}return w&&b!==o.Hover&&(window.addEventListener(`mousedown`,n),window.addEventListener(`mouseup`,r),document.addEventListener(`focusin`,i)),()=>{window.removeEventListener(`mousedown`,n),window.removeEventListener(`mouseup`,r),document.removeEventListener(`focusin`,i)}},[W,w,b,K,B]),u(()=>()=>clearTimeout(N.current),[]);function $(e){clearTimeout(N.current),N.current=setTimeout(()=>K(e),e?me:he)}function Fe(e){W||e.key!==`ArrowDown`&&e.key!==`ArrowUp`||(e.preventDefault(),K(!0,!0))}let Ie=b===o.Click?{onClick:e=>{(!W||ge)&&K(!W,e.detail===0)},onKeyDown:Fe}:b===o.Hover?{onMouseEnter:()=>$(!0),onMouseLeave:()=>$(!1)}:{onFocus:()=>K(!0),onBlur:()=>K(!1)},Le=b===o.Hover?{onMouseEnter:()=>$(!0),onMouseLeave:()=>$(!1)}:{};return h(`div`,{...Se,className:p(i.popover,be,D?.root),style:{...xe,...O?.root},children:[m(`div`,{ref:k,className:p(i.popoverTrigger,D?.trigger),style:O?.trigger,...Ie,children:ce}),W&&h(r,{children:[q&&m(ee,{onClose:()=>{w&&K(!1)},className:D?.backdrop,style:O?.backdrop}),h(`div`,{ref:Ne,className:p(q?a.sheet:i.popoverFloating,D?.floating),style:{...q?Ae:void 0,...O?.floating},"data-placement":y,onKeyDown:Pe,onMouseDownCapture:R,onMouseUpCapture:R,onFocusCapture:Ce,...Le,children:[m(`div`,{ref:j,className:p(i.popoverPanel,ve&&i.popoverPanelConstrained,q&&a.sheetPanel,D?.panel),style:O?.panel,children:le}),C&&!q&&m(`div`,{ref:M,className:p(i.popoverArrow,D?.arrow),style:O?.arrow})]})]})]})}export{_ as default};
2
2
  //# sourceMappingURL=popover.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"popover.mjs","names":[],"sources":["../../../../package/common/popover/popover.tsx"],"sourcesContent":["import { useRef, useEffect, useState, useCallback, type RefObject } from 'react';\nimport cx from 'classnames';\nimport BodyEnd from 'components/common/bodyEnd';\n\nimport styles from './popover.module.pcss';\nimport sheetStyles from '../styles/sheet.module.pcss';\nimport SheetBackdrop from '../sheetBackdrop';\nimport { usePopoverPosition } from './usePopoverPosition';\nimport { useFocusBoundary, tabbablesIn } from '../hooks/useFocusBoundary';\nimport { useSheet } from '../hooks/useSheet';\nimport { useOverlay } from '../hooks/useOverlay';\nimport { useComponentDefaults, usePresentationDefault } from '../configProvider';\nimport { Presentation } from '../configProvider/presentation.types';\nimport { PopoverPlacement, PopoverAlign, PopoverTrigger, type PopoverProps } from './popover.types';\n\n/**\n * How long after its click an anchored popover still counts as opened by it: long enough for the\n * render the click causes, short enough that an old keypress does not decide a later open.\n */\nconst ACTIVATION_WINDOW_MS = 1000;\n\nfunction toReference(\n anchor: HTMLElement | null | undefined,\n triggerRef: RefObject<HTMLElement | null>,\n): RefObject<HTMLElement | null> {\n return anchor ? { current: anchor } : triggerRef;\n}\n\nfunction Popover(props: PopoverProps) {\n const {\n children = null,\n anchor,\n content = null,\n placement = PopoverPlacement.Bottom,\n align = PopoverAlign.Center,\n trigger = PopoverTrigger.Click,\n isOpen,\n defaultOpen = false,\n onOpenChange = () => {},\n offset = 10,\n withArrow = true,\n closeOnOutsideClick = true,\n closeOnEscape = true,\n openDelay = 100,\n closeDelay = 120,\n disabled = false,\n focusOnOpen = 'keyboard',\n closeOnTriggerClick = true,\n width = 'auto',\n constrainWidth = true,\n presentation: presentationProp,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('Popover', props);\n const triggerRef = useRef<HTMLDivElement>(null);\n const floatingRef = useRef<HTMLDivElement>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n const arrowRef = useRef<HTMLDivElement>(null);\n const hoverTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n // Set from a capture handler on the panel, which React fires for its whole\n // React subtree — including children that portal elsewhere in the DOM, such as\n // a Dropdown's list inside a DatePicker's calendar. A DOM `contains` check\n // cannot see those, and without this the panel closes when one is used.\n const panelInteractedRef = useRef(false);\n // An outside click is a press that starts and ends outside while the panel is open. One that\n // starts inside is a drag, such as selecting a field's text, and Chromium and WebKit send its\n // `mouseup` to wherever it ends. One that started before the panel opened is the gesture that\n // opened it, such as Calendar's drag to create, which opens an editor on `pointerup`.\n const pressStartedOutsideRef = useRef(false);\n // Whether the open that is running came from the keyboard, which is what the\n // default `focusOnOpen` follows and what `onOpenChange` reports. A click raised\n // by Enter or Space reports `detail === 0`, where a pointer press counts the\n // clicks; an open with no trigger event at all, such as a panel that appears\n // while the user types, leaves this false.\n const openedByKeyboardRef = useRef(false);\n // A nested portal sits outside the panel's DOM; this is how focus leaving one counts as leaving.\n const lastPanelFocusRef = useRef<EventTarget | null>(null);\n function markPanelInteraction() {\n panelInteractedRef.current = true;\n }\n function markPanelFocus(event: React.FocusEvent) {\n markPanelInteraction();\n lastPanelFocusRef.current = event.target;\n }\n\n // What the panel is placed against, and where the focus goes back to. A new object exactly when\n // `anchor` changes, which is what restarts the position tracking for the new element: state, not\n // a memo, because a memo React chose to drop would restart it for no reason.\n const [reference, setReference] = useState(() => ({ anchor, ref: toReference(anchor, triggerRef) }));\n if (reference.anchor !== anchor) setReference({ anchor, ref: toReference(anchor, triggerRef) });\n const referenceRef = reference.ref;\n\n // An anchored popover is opened by its consumer through `isOpen`, so none of the key handling\n // below sees how. The click that caused it does: Enter and Space raise one with `detail === 0`,\n // the same test the trigger uses. Not `:focus-visible`, which a text field matches after a\n // mouse click too, and which stays on a keyboard-focused button that is then clicked.\n const activationRef = useRef<{ target: Node; keyboard: boolean; time: number } | null>(null);\n const anchored = anchor !== undefined;\n useEffect(() => {\n if (!anchored) return;\n function onClick(event: MouseEvent) {\n activationRef.current = { target: event.target as Node, keyboard: event.detail === 0, time: performance.now() };\n }\n // Capturing, so it is recorded before the consumer's own handler opens the panel.\n document.addEventListener('click', onClick, true);\n return () => document.removeEventListener('click', onClick, true);\n }, [anchored]);\n\n const isControlled = isOpen !== undefined;\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const open = isControlled ? isOpen : internalOpen;\n\n // Keep the outside-click/Escape listeners pointed at the latest setter without re-subscribing.\n const onOpenChangeRef = useRef(onOpenChange);\n useEffect(() => {\n onOpenChangeRef.current = onOpenChange;\n });\n\n const setOpen = useCallback(\n (next: boolean, fromKeyboard = false) => {\n if (disabled && next) return;\n if (next) openedByKeyboardRef.current = fromKeyboard;\n if (!isControlled) setInternalOpen(next);\n onOpenChangeRef.current(next, { fromKeyboard });\n },\n [disabled, isControlled],\n );\n\n // Explicit prop → this component's global default → the app-wide one → anchored.\n const appPresentation = usePresentationDefault();\n const presentation = presentationProp ?? appPresentation ?? Presentation.Default;\n const { isSheet: sheetPresentation, sheetStyle } = useSheet(presentation);\n // Hover and focus are pointer affordances that open something small next to what caused them;\n // a sheet is a deliberate, screen-owning open. Only a click asks for one.\n const isSheet = sheetPresentation && trigger === PopoverTrigger.Click;\n\n // Focus into the panel waits until it shows, since one that waits out a moving ancestor hidden\n // takes none.\n const shownRef = useRef(false);\n const focusPendingRef = useRef(false);\n function focusPanel() {\n focusPendingRef.current = false;\n const focusables = tabbablesIn(panelRef.current);\n if (focusables.length === 0) return;\n // Only claim the dialog role when we're actually managing focus for real content.\n floatingRef.current?.setAttribute('role', 'dialog');\n focusables[0].focus();\n }\n\n // A sheet isn't anchored to anything, so there's nothing to track, flip or clamp.\n usePopoverPosition({\n isOpen: !!open && !isSheet,\n placement,\n align,\n offset,\n triggerRef: referenceRef,\n floatingRef,\n panelRef,\n arrowRef: withArrow ? arrowRef : { current: null },\n matchTriggerWidth: width === 'trigger',\n onShown: () => {\n shownRef.current = true;\n if (focusPendingRef.current) focusPanel();\n },\n });\n\n // Registered for as long as it's open, not just as a sheet: this is what puts the popover on\n // the overlay stack, so an enclosing Modal knows something sits above it and neither drags\n // focus out of this (portaled, so outside the modal's panel) panel nor closes itself on the\n // Escape meant for this. Focus isn't trapped: a Popover is deliberately non-modal, and it\n // already moves focus into the panel on open by itself.\n const { restoreFocusToTrigger, focusAfterTrigger } = useFocusBoundary({\n triggerRef: referenceRef,\n contentRef: floatingRef,\n });\n const restoreFocusRef = useRef(restoreFocusToTrigger);\n useEffect(() => {\n restoreFocusRef.current = restoreFocusToTrigger;\n });\n\n // Focus inside the panel returns to the trigger however it closes, as with `hidePopover()`. Read\n // in a ref cleanup, while the panel is still in the DOM; moved in an effect, because StrictMode\n // detaches a new panel's refs once more right after it took the focus.\n const focusLeftWithPanelRef = useRef(false);\n const floatingCallbackRef = useCallback((node: HTMLDivElement | null) => {\n floatingRef.current = node;\n focusLeftWithPanelRef.current = false;\n if (!node) return;\n return () => {\n floatingRef.current = null;\n const active = document.activeElement;\n focusLeftWithPanelRef.current = !!active && (node.contains(active) || active === lastPanelFocusRef.current);\n };\n }, []);\n useEffect(() => {\n if (open || !focusLeftWithPanelRef.current) return;\n focusLeftWithPanelRef.current = false;\n const active = document.activeElement;\n if (!active || active === document.body) restoreFocusRef.current();\n }, [open]);\n\n useOverlay({\n isActive: !!open,\n containerRef: panelRef,\n onEscape: closeOnEscape\n ? () => {\n // Hand focus back to the trigger when it currently sits inside the panel.\n if (floatingRef.current?.contains(document.activeElement)) restoreFocusToTrigger();\n setOpen(false, true);\n }\n : undefined,\n trapFocus: false,\n lockScroll: isSheet,\n });\n\n // The panel is portaled to the end of the DOM, so Tab never flows into it naturally. When we\n // manage focus (click trigger + focusable content), move focus in on open and, on Tab out of\n // either end, close and continue focus as if the panel sat right after the trigger.\n useEffect(() => {\n if (!open) {\n openedByKeyboardRef.current = false;\n shownRef.current = false;\n focusPendingRef.current = false;\n return;\n }\n const activation = activationRef.current;\n activationRef.current = null;\n const anchorKeyed =\n !!anchor &&\n !!activation?.keyboard &&\n anchor.contains(activation.target) &&\n performance.now() - activation.time < ACTIVATION_WINDOW_MS;\n const takeFocus =\n focusOnOpen === true || (focusOnOpen === 'keyboard' && (openedByKeyboardRef.current || anchorKeyed));\n if (!takeFocus || trigger !== PopoverTrigger.Click) return;\n if (shownRef.current) focusPanel();\n else focusPendingRef.current = true;\n }, [open, trigger, focusOnOpen, anchor]);\n\n function handleFloatingKeyDown(event: React.KeyboardEvent) {\n if (event.key !== 'Tab' || trigger !== PopoverTrigger.Click) return;\n const focusables = tabbablesIn(panelRef.current);\n if (focusables.length === 0) return;\n const active = document.activeElement;\n if (!event.shiftKey && active === focusables[focusables.length - 1]) {\n event.preventDefault();\n focusAfterTrigger();\n setOpen(false, true);\n } else if (event.shiftKey && active === focusables[0]) {\n event.preventDefault();\n restoreFocusToTrigger();\n setOpen(false, true);\n }\n }\n\n // Outside click. Registered only while open and only for the relevant trigger mode; Escape is\n // handled by `useOverlay` above, which scopes it to the innermost open layer.\n useEffect(() => {\n if (!open) return;\n // Focus that landed in the panel before these listeners existed (ours on open, or content\n // that focuses itself on mount) set the flag with nothing to clear it, and the first\n // click outside was swallowed.\n panelInteractedRef.current = false;\n pressStartedOutsideRef.current = false;\n\n function containsNode(node: Node) {\n // Both trigger refs: with an anchor, `children` still toggle the panel, and a press on them is theirs.\n return Boolean(\n floatingRef.current?.contains(node) ||\n triggerRef.current?.contains(node) ||\n referenceRef.current?.contains(node),\n );\n }\n function isInside(event: Event) {\n // Read and clear: the flag describes this one event, and the panel's React\n // handler has already run by the time this does.\n const inPanelSubtree = panelInteractedRef.current;\n panelInteractedRef.current = false;\n return inPanelSubtree || containsNode(event.target as Node);\n }\n function onMouseDown(event: MouseEvent) {\n pressStartedOutsideRef.current = !isInside(event);\n }\n function onMouseUp(event: MouseEvent) {\n const pressStartedOutside = pressStartedOutsideRef.current;\n pressStartedOutsideRef.current = false;\n if (!isInside(event) && pressStartedOutside) setOpen(false);\n }\n // Keyboard counterpart to the outside click. Only focus that comes from inside closes it: code\n // moving focus elsewhere (a Modal restoring it) must not, nor `null`, focus back from another tab.\n function onFocusIn(event: FocusEvent) {\n if (isInside(event)) return;\n const from = event.relatedTarget as Node | null;\n if (!from || (from !== lastPanelFocusRef.current && !containsNode(from))) return;\n // A press on an outside field focuses it before its `mouseup`, and that is not the keyboard.\n setOpen(false, !pressStartedOutsideRef.current);\n }\n\n if (closeOnOutsideClick && trigger !== PopoverTrigger.Hover) {\n window.addEventListener('mousedown', onMouseDown);\n window.addEventListener('mouseup', onMouseUp);\n document.addEventListener('focusin', onFocusIn);\n }\n return () => {\n window.removeEventListener('mousedown', onMouseDown);\n window.removeEventListener('mouseup', onMouseUp);\n document.removeEventListener('focusin', onFocusIn);\n };\n }, [open, closeOnOutsideClick, trigger, setOpen, referenceRef]);\n\n useEffect(() => () => clearTimeout(hoverTimer.current), []);\n\n function scheduleHover(next: boolean) {\n clearTimeout(hoverTimer.current);\n hoverTimer.current = setTimeout(() => setOpen(next), next ? openDelay : closeDelay);\n }\n\n // Opening from the keyboard without the trigger having to be a button that\n // happens to toggle on Enter: the arrow keys are what the menu-button pattern\n // asks for, and what a consumer reaches for first.\n function handleTriggerKeyDown(event: React.KeyboardEvent) {\n if (open || (event.key !== 'ArrowDown' && event.key !== 'ArrowUp')) return;\n event.preventDefault();\n setOpen(true, true);\n }\n\n const triggerHandlers =\n trigger === PopoverTrigger.Click\n ? {\n onClick: (event: React.MouseEvent) => {\n // A field that opens a picker keeps it open when clicked again, so\n // that placing the caret in the field does not close the panel.\n if (open && !closeOnTriggerClick) return;\n setOpen(!open, event.detail === 0);\n },\n onKeyDown: handleTriggerKeyDown,\n }\n : trigger === PopoverTrigger.Hover\n ? { onMouseEnter: () => scheduleHover(true), onMouseLeave: () => scheduleHover(false) }\n : { onFocus: () => setOpen(true), onBlur: () => setOpen(false) };\n\n const floatingHoverHandlers =\n trigger === PopoverTrigger.Hover\n ? { onMouseEnter: () => scheduleHover(true), onMouseLeave: () => scheduleHover(false) }\n : {};\n\n return (\n <div\n {...rest}\n className={cx(styles.popover, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <div\n ref={triggerRef}\n className={cx(styles.popoverTrigger, classNames?.trigger)}\n style={slotStyles?.trigger}\n {...triggerHandlers}\n >\n {children}\n </div>\n {open && (\n <BodyEnd>\n {isSheet && (\n <SheetBackdrop\n onClose={() => {\n if (closeOnOutsideClick) setOpen(false);\n }}\n className={classNames?.backdrop}\n style={slotStyles?.backdrop}\n />\n )}\n <div\n ref={floatingCallbackRef}\n className={cx(isSheet ? sheetStyles.sheet : styles.popoverFloating, classNames?.floating)}\n style={{ ...(isSheet ? sheetStyle : undefined), ...slotStyles?.floating }}\n data-placement={placement}\n onKeyDown={handleFloatingKeyDown}\n onMouseDownCapture={markPanelInteraction}\n onMouseUpCapture={markPanelInteraction}\n onFocusCapture={markPanelFocus}\n {...floatingHoverHandlers}\n >\n <div\n ref={panelRef}\n className={cx(\n styles.popoverPanel,\n constrainWidth && styles.popoverPanelConstrained,\n isSheet && sheetStyles.sheetPanel,\n classNames?.panel,\n )}\n style={slotStyles?.panel}\n >\n {content}\n </div>\n {withArrow && !isSheet && (\n <div ref={arrowRef} className={cx(styles.popoverArrow, classNames?.arrow)} style={slotStyles?.arrow} />\n )}\n </div>\n </BodyEnd>\n )}\n </div>\n );\n}\n\nexport default Popover;\n"],"mappings":"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"}
1
+ {"version":3,"file":"popover.mjs","names":[],"sources":["../../../../package/common/popover/popover.tsx"],"sourcesContent":["import { useRef, useEffect, useState, useCallback, type RefObject } from 'react';\nimport cx from 'classnames';\nimport BodyEnd from 'components/common/bodyEnd';\n\nimport styles from './popover.module.pcss';\nimport sheetStyles from '../styles/sheet.module.pcss';\nimport SheetBackdrop from '../sheetBackdrop';\nimport { usePopoverPosition } from './usePopoverPosition';\nimport { useFocusBoundary, tabbablesIn } from '../hooks/useFocusBoundary';\nimport { useSheet } from '../hooks/useSheet';\nimport { useOverlay } from '../hooks/useOverlay';\nimport { useComponentDefaults, usePresentationDefault } from '../configProvider';\nimport { markDismiss } from '../utils/dismissClick';\nimport { Presentation } from '../configProvider/presentation.types';\nimport { PopoverPlacement, PopoverAlign, PopoverTrigger, type PopoverProps } from './popover.types';\n\n/**\n * How long after its click an anchored popover still counts as opened by it: long enough for the\n * render the click causes, short enough that an old keypress does not decide a later open.\n */\nconst ACTIVATION_WINDOW_MS = 1000;\n\nfunction toReference(\n anchor: HTMLElement | null | undefined,\n triggerRef: RefObject<HTMLElement | null>,\n): RefObject<HTMLElement | null> {\n return anchor ? { current: anchor } : triggerRef;\n}\n\nfunction Popover(props: PopoverProps) {\n const {\n children = null,\n anchor,\n content = null,\n placement = PopoverPlacement.Bottom,\n align = PopoverAlign.Center,\n trigger = PopoverTrigger.Click,\n isOpen,\n defaultOpen = false,\n onOpenChange = () => {},\n offset = 10,\n withArrow = true,\n closeOnOutsideClick = true,\n closeOnEscape = true,\n openDelay = 100,\n closeDelay = 120,\n disabled = false,\n focusOnOpen = 'keyboard',\n closeOnTriggerClick = true,\n width = 'auto',\n constrainWidth = true,\n presentation: presentationProp,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = useComponentDefaults('Popover', props);\n const triggerRef = useRef<HTMLDivElement>(null);\n const floatingRef = useRef<HTMLDivElement>(null);\n const panelRef = useRef<HTMLDivElement>(null);\n const arrowRef = useRef<HTMLDivElement>(null);\n const hoverTimer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n // Set from a capture handler on the panel, which React fires for its whole\n // React subtree — including children that portal elsewhere in the DOM, such as\n // a Dropdown's list inside a DatePicker's calendar. A DOM `contains` check\n // cannot see those, and without this the panel closes when one is used.\n const panelInteractedRef = useRef(false);\n // An outside click is a press that starts and ends outside while the panel is open. One that\n // starts inside is a drag, such as selecting a field's text, and Chromium and WebKit send its\n // `mouseup` to wherever it ends. One that started before the panel opened is the gesture that\n // opened it, such as Calendar's drag to create, which opens an editor on `pointerup`.\n const pressStartedOutsideRef = useRef(false);\n // Whether the open that is running came from the keyboard, which is what the\n // default `focusOnOpen` follows and what `onOpenChange` reports. A click raised\n // by Enter or Space reports `detail === 0`, where a pointer press counts the\n // clicks; an open with no trigger event at all, such as a panel that appears\n // while the user types, leaves this false.\n const openedByKeyboardRef = useRef(false);\n // A nested portal sits outside the panel's DOM; this is how focus leaving one counts as leaving.\n const lastPanelFocusRef = useRef<EventTarget | null>(null);\n function markPanelInteraction() {\n panelInteractedRef.current = true;\n }\n function markPanelFocus(event: React.FocusEvent) {\n markPanelInteraction();\n lastPanelFocusRef.current = event.target;\n }\n\n // What the panel is placed against, and where the focus goes back to. A new object exactly when\n // `anchor` changes, which is what restarts the position tracking for the new element: state, not\n // a memo, because a memo React chose to drop would restart it for no reason.\n const [reference, setReference] = useState(() => ({ anchor, ref: toReference(anchor, triggerRef) }));\n if (reference.anchor !== anchor) setReference({ anchor, ref: toReference(anchor, triggerRef) });\n const referenceRef = reference.ref;\n\n // An anchored popover is opened by its consumer through `isOpen`, so none of the key handling\n // below sees how. The click that caused it does: Enter and Space raise one with `detail === 0`,\n // the same test the trigger uses. Not `:focus-visible`, which a text field matches after a\n // mouse click too, and which stays on a keyboard-focused button that is then clicked.\n const activationRef = useRef<{ target: Node; keyboard: boolean; time: number } | null>(null);\n const anchored = anchor !== undefined;\n useEffect(() => {\n if (!anchored) return;\n function onClick(event: MouseEvent) {\n activationRef.current = { target: event.target as Node, keyboard: event.detail === 0, time: performance.now() };\n }\n // Capturing, so it is recorded before the consumer's own handler opens the panel.\n document.addEventListener('click', onClick, true);\n return () => document.removeEventListener('click', onClick, true);\n }, [anchored]);\n\n const isControlled = isOpen !== undefined;\n const [internalOpen, setInternalOpen] = useState(defaultOpen);\n const open = isControlled ? isOpen : internalOpen;\n\n // Keep the outside-click/Escape listeners pointed at the latest setter without re-subscribing.\n const onOpenChangeRef = useRef(onOpenChange);\n useEffect(() => {\n onOpenChangeRef.current = onOpenChange;\n });\n\n const setOpen = useCallback(\n (next: boolean, fromKeyboard = false) => {\n if (disabled && next) return;\n if (next) openedByKeyboardRef.current = fromKeyboard;\n if (!isControlled) setInternalOpen(next);\n onOpenChangeRef.current(next, { fromKeyboard });\n },\n [disabled, isControlled],\n );\n\n // Explicit prop → this component's global default → the app-wide one → anchored.\n const appPresentation = usePresentationDefault();\n const presentation = presentationProp ?? appPresentation ?? Presentation.Default;\n const { isSheet: sheetPresentation, sheetStyle } = useSheet(presentation);\n // Hover and focus are pointer affordances that open something small next to what caused them;\n // a sheet is a deliberate, screen-owning open. Only a click asks for one.\n const isSheet = sheetPresentation && trigger === PopoverTrigger.Click;\n\n // Focus into the panel waits until it shows, since one that waits out a moving ancestor hidden\n // takes none.\n const shownRef = useRef(false);\n const focusPendingRef = useRef(false);\n function focusPanel() {\n focusPendingRef.current = false;\n const focusables = tabbablesIn(panelRef.current);\n if (focusables.length === 0) return;\n // Only claim the dialog role when we're actually managing focus for real content.\n floatingRef.current?.setAttribute('role', 'dialog');\n focusables[0].focus();\n }\n\n // A sheet isn't anchored to anything, so there's nothing to track, flip or clamp.\n usePopoverPosition({\n isOpen: !!open && !isSheet,\n placement,\n align,\n offset,\n triggerRef: referenceRef,\n floatingRef,\n panelRef,\n arrowRef: withArrow ? arrowRef : { current: null },\n matchTriggerWidth: width === 'trigger',\n onShown: () => {\n shownRef.current = true;\n if (focusPendingRef.current) focusPanel();\n },\n });\n\n // Registered for as long as it's open, not just as a sheet: this is what puts the popover on\n // the overlay stack, so an enclosing Modal knows something sits above it and neither drags\n // focus out of this (portaled, so outside the modal's panel) panel nor closes itself on the\n // Escape meant for this. Focus isn't trapped: a Popover is deliberately non-modal, and it\n // already moves focus into the panel on open by itself.\n const { restoreFocusToTrigger, focusAfterTrigger } = useFocusBoundary({\n triggerRef: referenceRef,\n contentRef: floatingRef,\n });\n const restoreFocusRef = useRef(restoreFocusToTrigger);\n useEffect(() => {\n restoreFocusRef.current = restoreFocusToTrigger;\n });\n\n // Focus inside the panel returns to the trigger however it closes, as with `hidePopover()`. Read\n // in a ref cleanup, while the panel is still in the DOM; moved in an effect, because StrictMode\n // detaches a new panel's refs once more right after it took the focus.\n const focusLeftWithPanelRef = useRef(false);\n const floatingCallbackRef = useCallback((node: HTMLDivElement | null) => {\n floatingRef.current = node;\n focusLeftWithPanelRef.current = false;\n if (!node) return;\n return () => {\n floatingRef.current = null;\n const active = document.activeElement;\n focusLeftWithPanelRef.current = !!active && (node.contains(active) || active === lastPanelFocusRef.current);\n };\n }, []);\n useEffect(() => {\n if (open || !focusLeftWithPanelRef.current) return;\n focusLeftWithPanelRef.current = false;\n const active = document.activeElement;\n if (!active || active === document.body) restoreFocusRef.current();\n }, [open]);\n\n useOverlay({\n isActive: !!open,\n containerRef: panelRef,\n onEscape: closeOnEscape\n ? () => {\n // Hand focus back to the trigger when it currently sits inside the panel.\n if (floatingRef.current?.contains(document.activeElement)) restoreFocusToTrigger();\n setOpen(false, true);\n }\n : undefined,\n trapFocus: false,\n lockScroll: isSheet,\n });\n\n // The panel is portaled to the end of the DOM, so Tab never flows into it naturally. When we\n // manage focus (click trigger + focusable content), move focus in on open and, on Tab out of\n // either end, close and continue focus as if the panel sat right after the trigger.\n useEffect(() => {\n if (!open) {\n openedByKeyboardRef.current = false;\n shownRef.current = false;\n focusPendingRef.current = false;\n return;\n }\n const activation = activationRef.current;\n activationRef.current = null;\n const anchorKeyed =\n !!anchor &&\n !!activation?.keyboard &&\n anchor.contains(activation.target) &&\n performance.now() - activation.time < ACTIVATION_WINDOW_MS;\n const takeFocus =\n focusOnOpen === true || (focusOnOpen === 'keyboard' && (openedByKeyboardRef.current || anchorKeyed));\n if (!takeFocus || trigger !== PopoverTrigger.Click) return;\n if (shownRef.current) focusPanel();\n else focusPendingRef.current = true;\n }, [open, trigger, focusOnOpen, anchor]);\n\n function handleFloatingKeyDown(event: React.KeyboardEvent) {\n if (event.key !== 'Tab' || trigger !== PopoverTrigger.Click) return;\n const focusables = tabbablesIn(panelRef.current);\n if (focusables.length === 0) return;\n const active = document.activeElement;\n if (!event.shiftKey && active === focusables[focusables.length - 1]) {\n event.preventDefault();\n focusAfterTrigger();\n setOpen(false, true);\n } else if (event.shiftKey && active === focusables[0]) {\n event.preventDefault();\n restoreFocusToTrigger();\n setOpen(false, true);\n }\n }\n\n // Outside click. Registered only while open and only for the relevant trigger mode; Escape is\n // handled by `useOverlay` above, which scopes it to the innermost open layer.\n useEffect(() => {\n if (!open) return;\n // Focus that landed in the panel before these listeners existed (ours on open, or content\n // that focuses itself on mount) set the flag with nothing to clear it, and the first\n // click outside was swallowed.\n panelInteractedRef.current = false;\n pressStartedOutsideRef.current = false;\n\n function containsNode(node: Node) {\n // Both trigger refs: with an anchor, `children` still toggle the panel, and a press on them is theirs.\n return Boolean(\n floatingRef.current?.contains(node) ||\n triggerRef.current?.contains(node) ||\n referenceRef.current?.contains(node),\n );\n }\n function isInside(event: Event) {\n // Read and clear: the flag describes this one event, and the panel's React\n // handler has already run by the time this does.\n const inPanelSubtree = panelInteractedRef.current;\n panelInteractedRef.current = false;\n return inPanelSubtree || containsNode(event.target as Node);\n }\n function onMouseDown(event: MouseEvent) {\n pressStartedOutsideRef.current = !isInside(event);\n }\n function onMouseUp(event: MouseEvent) {\n const pressStartedOutside = pressStartedOutsideRef.current;\n pressStartedOutsideRef.current = false;\n if (isInside(event) || !pressStartedOutside) return;\n markDismiss();\n setOpen(false);\n }\n // Keyboard counterpart to the outside click. Only focus that comes from inside closes it: code\n // moving focus elsewhere (a Modal restoring it) must not, nor `null`, focus back from another tab.\n function onFocusIn(event: FocusEvent) {\n if (isInside(event)) return;\n const from = event.relatedTarget as Node | null;\n if (!from || (from !== lastPanelFocusRef.current && !containsNode(from))) return;\n // A press on an outside field focuses it before its `mouseup`, and that is not the keyboard.\n // It closes here, before the `mouseup` that would mark its click, so it marks it now.\n const pressed = pressStartedOutsideRef.current;\n if (pressed) markDismiss();\n setOpen(false, !pressed);\n }\n\n if (closeOnOutsideClick && trigger !== PopoverTrigger.Hover) {\n window.addEventListener('mousedown', onMouseDown);\n window.addEventListener('mouseup', onMouseUp);\n document.addEventListener('focusin', onFocusIn);\n }\n return () => {\n window.removeEventListener('mousedown', onMouseDown);\n window.removeEventListener('mouseup', onMouseUp);\n document.removeEventListener('focusin', onFocusIn);\n };\n }, [open, closeOnOutsideClick, trigger, setOpen, referenceRef]);\n\n useEffect(() => () => clearTimeout(hoverTimer.current), []);\n\n function scheduleHover(next: boolean) {\n clearTimeout(hoverTimer.current);\n hoverTimer.current = setTimeout(() => setOpen(next), next ? openDelay : closeDelay);\n }\n\n // Opening from the keyboard without the trigger having to be a button that\n // happens to toggle on Enter: the arrow keys are what the menu-button pattern\n // asks for, and what a consumer reaches for first.\n function handleTriggerKeyDown(event: React.KeyboardEvent) {\n if (open || (event.key !== 'ArrowDown' && event.key !== 'ArrowUp')) return;\n event.preventDefault();\n setOpen(true, true);\n }\n\n const triggerHandlers =\n trigger === PopoverTrigger.Click\n ? {\n onClick: (event: React.MouseEvent) => {\n // A field that opens a picker keeps it open when clicked again, so\n // that placing the caret in the field does not close the panel.\n if (open && !closeOnTriggerClick) return;\n setOpen(!open, event.detail === 0);\n },\n onKeyDown: handleTriggerKeyDown,\n }\n : trigger === PopoverTrigger.Hover\n ? { onMouseEnter: () => scheduleHover(true), onMouseLeave: () => scheduleHover(false) }\n : { onFocus: () => setOpen(true), onBlur: () => setOpen(false) };\n\n const floatingHoverHandlers =\n trigger === PopoverTrigger.Hover\n ? { onMouseEnter: () => scheduleHover(true), onMouseLeave: () => scheduleHover(false) }\n : {};\n\n return (\n <div\n {...rest}\n className={cx(styles.popover, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n <div\n ref={triggerRef}\n className={cx(styles.popoverTrigger, classNames?.trigger)}\n style={slotStyles?.trigger}\n {...triggerHandlers}\n >\n {children}\n </div>\n {open && (\n <BodyEnd>\n {isSheet && (\n <SheetBackdrop\n onClose={() => {\n if (closeOnOutsideClick) setOpen(false);\n }}\n className={classNames?.backdrop}\n style={slotStyles?.backdrop}\n />\n )}\n <div\n ref={floatingCallbackRef}\n className={cx(isSheet ? sheetStyles.sheet : styles.popoverFloating, classNames?.floating)}\n style={{ ...(isSheet ? sheetStyle : undefined), ...slotStyles?.floating }}\n data-placement={placement}\n onKeyDown={handleFloatingKeyDown}\n onMouseDownCapture={markPanelInteraction}\n onMouseUpCapture={markPanelInteraction}\n onFocusCapture={markPanelFocus}\n {...floatingHoverHandlers}\n >\n <div\n ref={panelRef}\n className={cx(\n styles.popoverPanel,\n constrainWidth && styles.popoverPanelConstrained,\n isSheet && sheetStyles.sheetPanel,\n classNames?.panel,\n )}\n style={slotStyles?.panel}\n >\n {content}\n </div>\n {withArrow && !isSheet && (\n <div ref={arrowRef} className={cx(styles.popoverArrow, classNames?.arrow)} style={slotStyles?.arrow} />\n )}\n </div>\n </BodyEnd>\n )}\n </div>\n );\n}\n\nexport default Popover;\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";import e from"./progressBar.module.mjs";import t from"classnames";import{jsx as n}from"react/jsx-runtime";function r(e){let t=e.replace(`#`,``),n=parseInt(t.substring(0,2),16),r=parseInt(t.substring(2,4),16),i=parseInt(t.substring(4,6),16);return(.299*n+.587*r+.114*i)/255>.5?`#000000`:`#ffffff`}function i({className:i=void 0,total:a=100,progress:o=0,style:s=void 0,classNames:c,styles:l,...u}){function d(){let i=[];return Array.isArray(o)?i.push(...o):typeof o==`object`?i.push(o):i.push({amount:o}),i.map((o,s)=>{let u=a?o.amount/a*100:0;return n(`div`,{className:t(e.progressBarFill,s===i.length-1&&e.progressBarFillEnd,o.class,c?.fill),style:{width:`${u}%`,background:o.color||void 0,...l?.fill},title:o.label?`${o.label}: ${o.amount}`:void 0,children:o.label&&n(`div`,{className:t(e.progressBarLabel,c?.label),style:{...o.color?{color:r(o.color)}:{},...l?.label},children:o.label})},s)})}return n(`div`,{...u,className:t(e.progressBar,i,c?.root),style:{...s,...l?.root},children:d()})}export{i as default,r as getContrastColor};
1
+ "use client";import{toneProps as e,useToneCheck as t}from"../styles/tone.mjs";import n from"./progressBar.module.mjs";import r from"classnames";import{jsx as i}from"react/jsx-runtime";function a({className:a=void 0,total:o=100,progress:s=0,style:c=void 0,classNames:l,styles:u,...d}){let f=Array.isArray(s)?s:typeof s==`object`?[s]:[{amount:s}];t(`ProgressBar`,f.map(e=>e.color));function p(){return f.map((t,a)=>{let s=o?t.amount/o*100:0,c=t.color?e(t.color):void 0;return i(`div`,{className:r(n.progressBarFill,c&&[c.className,n.progressBarFillToned],a===f.length-1&&n.progressBarFillEnd,t.class,l?.fill),style:{width:`${s}%`,...c?.style,...u?.fill},title:t.label?`${t.label}: ${t.amount}`:void 0,children:t.label&&i(`div`,{className:r(n.progressBarLabel,l?.label),style:u?.label,children:t.label})},a)})}return i(`div`,{...d,className:r(n.progressBar,a,l?.root),style:{...c,...u?.root},children:p()})}export{a as default};
2
2
  //# sourceMappingURL=progressBar.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"progressBar.mjs","names":[],"sources":["../../../../package/common/progressBar/progressBar.tsx"],"sourcesContent":["import type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { Progress, ProgressBarProps } from './progressBar.types';\n\nimport styles from './progressBar.module.pcss';\n\nexport function getContrastColor(hexColor: string): string {\n const hex = hexColor.replace('#', '');\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n\n const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n return luminance > 0.5 ? '#000000' : '#ffffff';\n}\n\nfunction ProgressBar({\n className = undefined,\n total = 100,\n progress = 0,\n style = undefined,\n classNames,\n styles: slotStyles,\n ...rest\n}: ProgressBarProps): ReactElement {\n function renderProgressBars() {\n const progressArray: Progress[] = [];\n\n if (Array.isArray(progress)) progressArray.push(...progress);\n else if (typeof progress === 'object') progressArray.push(progress);\n else progressArray.push({ amount: progress });\n\n return progressArray.map((prog, index) => {\n const widthPercentage = !total ? 0 : (prog.amount / total) * 100;\n\n return (\n <div\n key={index}\n className={cx(\n styles.progressBarFill,\n index === progressArray.length - 1 && styles.progressBarFillEnd,\n prog.class,\n classNames?.fill,\n )}\n style={{\n width: `${widthPercentage}%`,\n background: prog.color || undefined,\n ...slotStyles?.fill,\n }}\n title={prog.label ? `${prog.label}: ${prog.amount}` : undefined}\n >\n {prog.label && (\n <div\n className={cx(styles.progressBarLabel, classNames?.label)}\n style={{ ...(prog.color ? { color: getContrastColor(prog.color) } : {}), ...slotStyles?.label }}\n >\n {prog.label}\n </div>\n )}\n </div>\n );\n });\n }\n\n return (\n <div\n {...rest}\n className={cx(styles.progressBar, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n {renderProgressBars()}\n </div>\n );\n}\n\nexport default ProgressBar;\n"],"mappings":"uHAMA,SAAgB,EAAiB,EAA0B,CACzD,IAAM,EAAM,EAAS,QAAQ,IAAK,EAAE,EAC9B,EAAI,SAAS,EAAI,UAAU,EAAG,CAAC,EAAG,EAAE,EACpC,EAAI,SAAS,EAAI,UAAU,EAAG,CAAC,EAAG,EAAE,EACpC,EAAI,SAAS,EAAI,UAAU,EAAG,CAAC,EAAG,EAAE,EAG1C,OADmB,KAAQ,EAAI,KAAQ,EAAI,KAAQ,GAAK,IACrC,GAAM,UAAY,SACvC,CAEA,SAAS,EAAY,CACnB,YAAY,IAAA,GACZ,QAAQ,IACR,WAAW,EACX,QAAQ,IAAA,GACR,aACA,OAAQ,EACR,GAAG,GAC8B,CACjC,SAAS,GAAqB,CAC5B,IAAM,EAA4B,CAAC,EAMnC,OAJI,MAAM,QAAQ,CAAQ,EAAG,EAAc,KAAK,GAAG,CAAQ,EAClD,OAAO,GAAa,SAAU,EAAc,KAAK,CAAQ,EAC7D,EAAc,KAAK,CAAE,OAAQ,CAAS,CAAC,EAErC,EAAc,KAAK,EAAM,IAAU,CACxC,IAAM,EAAmB,EAAa,EAAK,OAAS,EAAS,IAA5B,EAEjC,OACE,EAAC,MAAD,CAEE,UAAW,EACT,EAAO,gBACP,IAAU,EAAc,OAAS,GAAK,EAAO,mBAC7C,EAAK,MACL,GAAY,IACd,EACA,MAAO,CACL,MAAO,GAAG,EAAgB,GAC1B,WAAY,EAAK,OAAS,IAAA,GAC1B,GAAG,GAAY,IACjB,EACA,MAAO,EAAK,MAAQ,GAAG,EAAK,MAAM,IAAI,EAAK,SAAW,IAAA,GAErD,SAAA,EAAK,OACJ,EAAC,MAAD,CACE,UAAW,EAAG,EAAO,iBAAkB,GAAY,KAAK,EACxD,MAAO,CAAE,GAAI,EAAK,MAAQ,CAAE,MAAO,EAAiB,EAAK,KAAK,CAAE,EAAI,CAAC,EAAI,GAAG,GAAY,KAAM,EAE7F,SAAA,EAAK,KACH,CAAA,CAEJ,EAtBE,CAsBF,CAET,CAAC,CACH,CAEA,OACE,EAAC,MAAD,CACE,GAAI,EACJ,UAAW,EAAG,EAAO,YAAa,EAAW,GAAY,IAAI,EAC7D,MAAO,CAAE,GAAG,EAAO,GAAG,GAAY,IAAK,EAEtC,SAAA,EAAmB,CACjB,CAAA,CAET"}
1
+ {"version":3,"file":"progressBar.mjs","names":[],"sources":["../../../../package/common/progressBar/progressBar.tsx"],"sourcesContent":["import type { ReactElement } from 'react';\nimport cx from 'classnames';\nimport { Progress, ProgressBarProps } from './progressBar.types';\n\nimport styles from './progressBar.module.pcss';\nimport { toneProps, useToneCheck } from '../styles/tone';\n\nfunction ProgressBar({\n className = undefined,\n total = 100,\n progress = 0,\n style = undefined,\n classNames,\n styles: slotStyles,\n ...rest\n}: ProgressBarProps): ReactElement {\n const progressArray: Progress[] = Array.isArray(progress)\n ? progress\n : typeof progress === 'object'\n ? [progress]\n : [{ amount: progress }];\n useToneCheck(\n 'ProgressBar',\n progressArray.map((prog) => prog.color),\n );\n\n function renderProgressBars() {\n return progressArray.map((prog, index) => {\n const widthPercentage = !total ? 0 : (prog.amount / total) * 100;\n const tone = prog.color ? toneProps(prog.color) : undefined;\n\n return (\n <div\n key={index}\n className={cx(\n styles.progressBarFill,\n tone && [tone.className, styles.progressBarFillToned],\n index === progressArray.length - 1 && styles.progressBarFillEnd,\n prog.class,\n classNames?.fill,\n )}\n style={{\n width: `${widthPercentage}%`,\n ...tone?.style,\n ...slotStyles?.fill,\n }}\n title={prog.label ? `${prog.label}: ${prog.amount}` : undefined}\n >\n {prog.label && (\n <div className={cx(styles.progressBarLabel, classNames?.label)} style={slotStyles?.label}>\n {prog.label}\n </div>\n )}\n </div>\n );\n });\n }\n\n return (\n <div\n {...rest}\n className={cx(styles.progressBar, className, classNames?.root)}\n style={{ ...style, ...slotStyles?.root }}\n >\n {renderProgressBars()}\n </div>\n );\n}\n\nexport default ProgressBar;\n"],"mappings":"wLAOA,SAAS,EAAY,CACnB,YAAY,IAAA,GACZ,QAAQ,IACR,WAAW,EACX,QAAQ,IAAA,GACR,aACA,OAAQ,EACR,GAAG,GAC8B,CACjC,IAAM,EAA4B,MAAM,QAAQ,CAAQ,EACpD,EACA,OAAO,GAAa,SAClB,CAAC,CAAQ,EACT,CAAC,CAAE,OAAQ,CAAS,CAAC,EAC3B,EACE,cACA,EAAc,IAAK,GAAS,EAAK,KAAK,CACxC,EAEA,SAAS,GAAqB,CAC5B,OAAO,EAAc,KAAK,EAAM,IAAU,CACxC,IAAM,EAAmB,EAAa,EAAK,OAAS,EAAS,IAA5B,EAC3B,EAAO,EAAK,MAAQ,EAAU,EAAK,KAAK,EAAI,IAAA,GAElD,OACE,EAAC,MAAD,CAEE,UAAW,EACT,EAAO,gBACP,GAAQ,CAAC,EAAK,UAAW,EAAO,oBAAoB,EACpD,IAAU,EAAc,OAAS,GAAK,EAAO,mBAC7C,EAAK,MACL,GAAY,IACd,EACA,MAAO,CACL,MAAO,GAAG,EAAgB,GAC1B,GAAG,GAAM,MACT,GAAG,GAAY,IACjB,EACA,MAAO,EAAK,MAAQ,GAAG,EAAK,MAAM,IAAI,EAAK,SAAW,IAAA,GAErD,SAAA,EAAK,OACJ,EAAC,MAAD,CAAK,UAAW,EAAG,EAAO,iBAAkB,GAAY,KAAK,EAAG,MAAO,GAAY,MAChF,SAAA,EAAK,KACH,CAAA,CAEJ,EApBE,CAoBF,CAET,CAAC,CACH,CAEA,OACE,EAAC,MAAD,CACE,GAAI,EACJ,UAAW,EAAG,EAAO,YAAa,EAAW,GAAY,IAAI,EAC7D,MAAO,CAAE,GAAG,EAAO,GAAG,GAAY,IAAK,EAEtC,SAAA,EAAmB,CACjB,CAAA,CAET"}
@@ -1,2 +1,2 @@
1
- "use client";var e=`progressBar_LO0sX`,t=`progressBar-fill_rt32j`,n=`progressBar-fill-end_71wL3`,r=`progressBar-label_jWud2`,i={progressBar:e,"progressBar-fill":`progressBar-fill_rt32j`,progressBarFill:t,"progressBar-fill-end":`progressBar-fill-end_71wL3`,progressBarFillEnd:n,"progressBar-label":`progressBar-label_jWud2`,progressBarLabel:r};export{i as default,e as progressBar,t as progressBarFill,n as progressBarFillEnd,r as progressBarLabel};
1
+ "use client";var e=`progressBar_LO0sX`,t=`progressBar-fill_rt32j`,n=`progressBar-fill-end_71wL3`,r=`progressBar-fill-toned_cM4yE`,i=`progressBar-label_jWud2`,a={progressBar:e,"progressBar-fill":`progressBar-fill_rt32j`,progressBarFill:t,"progressBar-fill-end":`progressBar-fill-end_71wL3`,progressBarFillEnd:n,"progressBar-fill-toned":`progressBar-fill-toned_cM4yE`,progressBarFillToned:r,"progressBar-label":`progressBar-label_jWud2`,progressBarLabel:i};export{a as default,e as progressBar,t as progressBarFill,n as progressBarFillEnd,r as progressBarFillToned,i as progressBarLabel};
2
2
  //# sourceMappingURL=progressBar.module.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"progressBar.module.mjs","names":[],"sources":["../../../../package/common/progressBar/progressBar.module.pcss"],"sourcesContent":[".progressBar {\n min-width: 100px;\n min-height: 10px;\n background-color: var(--progress-bar-track-color, var(--background-accent-light));\n border-radius: var(--default-border-radius);\n overflow: hidden;\n display: flex;\n align-items: stretch;\n\n &-fill {\n background-color: var(--primary-color);\n background-image: var(--primary-gradient, none);\n width: 0;\n display: flex;\n align-items: center;\n overflow: hidden;\n transition: width 0.3s ease;\n transform: translateZ(0);\n container-type: inline-size;\n\n &-end {\n border-radius: 0 var(--default-border-radius) var(--default-border-radius) 0;\n }\n }\n\n &-label {\n color: var(--text-on-primary);\n font-size: 12px;\n padding: 0 min(5px, 4cqw);\n box-sizing: border-box;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n}\n\n/* The bar still shows its value, it just stops sliding to it. */\n@media (prefers-reduced-motion: reduce) {\n .progressBar-fill {\n transition: none;\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"progressBar.module.mjs","names":[],"sources":["../../../../package/common/progressBar/progressBar.module.pcss"],"sourcesContent":[".progressBar {\n min-width: 100px;\n min-height: 10px;\n background-color: var(--progress-bar-track-color, var(--background-accent-light));\n border-radius: var(--default-border-radius);\n overflow: hidden;\n display: flex;\n align-items: stretch;\n\n &-fill {\n background-color: var(--primary-color);\n background-image: var(--primary-gradient, none);\n width: 0;\n display: flex;\n align-items: center;\n overflow: hidden;\n transition: width 0.3s ease;\n transform: translateZ(0);\n container-type: inline-size;\n\n &-end {\n border-radius: 0 var(--default-border-radius) var(--default-border-radius) 0;\n }\n\n /* After the rule above, which it ties on specificity. */\n &-toned {\n background-color: var(--tone-fill);\n background-image: var(--tone-gradient);\n }\n }\n\n &-label {\n color: var(--text-on-primary);\n font-size: 12px;\n padding: 0 min(5px, 4cqw);\n box-sizing: border-box;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &-fill-toned &-label {\n color: var(--tone-contrast);\n }\n}\n\n/* The bar still shows its value, it just stops sliding to it. */\n@media (prefers-reduced-motion: reduce) {\n .progressBar-fill {\n transition: none;\n }\n}\n"],"mappings":""}