@seekalfred/primitives 0.6.0 → 0.7.3

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 (346) hide show
  1. package/README.md +214 -15
  2. package/dist/activity-heatmap/activity-heatmap.d.ts +92 -0
  3. package/dist/activity-heatmap/activity-heatmap.js +699 -0
  4. package/dist/activity-heatmap/activity-heatmap.js.map +1 -0
  5. package/dist/activity-heatmap/index.d.ts +2 -0
  6. package/dist/activity-heatmap/index.js +2 -0
  7. package/dist/alert/alert.d.ts +53 -4
  8. package/dist/alert/alert.js +100 -11
  9. package/dist/alert/alert.js.map +1 -1
  10. package/dist/alert/index.d.ts +2 -2
  11. package/dist/alert/index.js +2 -2
  12. package/dist/alert-dialog/alert-dialog.d.ts +58 -18
  13. package/dist/alert-dialog/alert-dialog.js +152 -9
  14. package/dist/alert-dialog/alert-dialog.js.map +1 -1
  15. package/dist/alert-dialog/index.d.ts +2 -2
  16. package/dist/alert-dialog/index.js +2 -2
  17. package/dist/animated-counter/animated-counter.d.ts +38 -0
  18. package/dist/animated-counter/animated-counter.js +230 -0
  19. package/dist/animated-counter/animated-counter.js.map +1 -0
  20. package/dist/animated-counter/index.d.ts +2 -0
  21. package/dist/animated-counter/index.js +2 -0
  22. package/dist/announcement-bar/announcement-bar.d.ts +67 -0
  23. package/dist/announcement-bar/announcement-bar.js +364 -0
  24. package/dist/announcement-bar/announcement-bar.js.map +1 -0
  25. package/dist/announcement-bar/index.d.ts +2 -0
  26. package/dist/announcement-bar/index.js +2 -0
  27. package/dist/badge/badge.d.ts +1 -1
  28. package/dist/bar-chart/bar-chart.d.ts +51 -0
  29. package/dist/bar-chart/bar-chart.js +565 -0
  30. package/dist/bar-chart/bar-chart.js.map +1 -0
  31. package/dist/bar-chart/index.d.ts +2 -0
  32. package/dist/bar-chart/index.js +2 -0
  33. package/dist/breadcrumb/breadcrumb.d.ts +2 -2
  34. package/dist/breadcrumb/breadcrumb.js +12 -3
  35. package/dist/breadcrumb/breadcrumb.js.map +1 -1
  36. package/dist/brush-chart/brush-chart.d.ts +64 -0
  37. package/dist/brush-chart/brush-chart.js +1056 -0
  38. package/dist/brush-chart/brush-chart.js.map +1 -0
  39. package/dist/brush-chart/index.d.ts +2 -0
  40. package/dist/brush-chart/index.js +2 -0
  41. package/dist/button/button.d.ts +1 -1
  42. package/dist/button/button.js +31 -21
  43. package/dist/button/button.js.map +1 -1
  44. package/dist/button-group/button-group.js +3 -4
  45. package/dist/button-group/button-group.js.map +1 -1
  46. package/dist/calendar/calendar.d.ts +1 -1
  47. package/dist/card/card.d.ts +22 -1
  48. package/dist/card/card.js +60 -7
  49. package/dist/card/card.js.map +1 -1
  50. package/dist/card-stack/card-stack.d.ts +54 -0
  51. package/dist/card-stack/card-stack.js +492 -0
  52. package/dist/card-stack/card-stack.js.map +1 -0
  53. package/dist/card-stack/index.d.ts +2 -0
  54. package/dist/card-stack/index.js +2 -0
  55. package/dist/carousel/carousel.d.ts +77 -0
  56. package/dist/carousel/carousel.js +415 -0
  57. package/dist/carousel/carousel.js.map +1 -0
  58. package/dist/carousel/index.d.ts +2 -0
  59. package/dist/carousel/index.js +2 -0
  60. package/dist/checkbox/checkbox.js +4 -4
  61. package/dist/checkbox/checkbox.js.map +1 -1
  62. package/dist/chip-group/chip-group.d.ts +37 -0
  63. package/dist/chip-group/chip-group.js +148 -0
  64. package/dist/chip-group/chip-group.js.map +1 -0
  65. package/dist/chip-group/index.d.ts +2 -0
  66. package/dist/chip-group/index.js +2 -0
  67. package/dist/copy-button/copy-button.d.ts +18 -2
  68. package/dist/copy-button/copy-button.js +29 -8
  69. package/dist/copy-button/copy-button.js.map +1 -1
  70. package/dist/countdown/countdown.d.ts +45 -0
  71. package/dist/countdown/countdown.js +298 -0
  72. package/dist/countdown/countdown.js.map +1 -0
  73. package/dist/countdown/index.d.ts +2 -0
  74. package/dist/countdown/index.js +2 -0
  75. package/dist/date-picker/picker-parts.js +1 -1
  76. package/dist/date-picker/picker-parts.js.map +1 -1
  77. package/dist/dialog/dialog.js +1 -1
  78. package/dist/dialog/dialog.js.map +1 -1
  79. package/dist/donut-chart/donut-chart.d.ts +86 -0
  80. package/dist/donut-chart/donut-chart.js +593 -0
  81. package/dist/donut-chart/donut-chart.js.map +1 -0
  82. package/dist/donut-chart/index.d.ts +2 -0
  83. package/dist/donut-chart/index.js +2 -0
  84. package/dist/empty-state/empty-state.d.ts +42 -10
  85. package/dist/empty-state/empty-state.js +44 -26
  86. package/dist/empty-state/empty-state.js.map +1 -1
  87. package/dist/empty-state/index.d.ts +2 -2
  88. package/dist/empty-state/index.js +2 -2
  89. package/dist/file-drop-zone/file-drop-zone.js +1 -1
  90. package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
  91. package/dist/filter-bar/filter-bar.d.ts +50 -26
  92. package/dist/filter-bar/filter-bar.js +317 -157
  93. package/dist/filter-bar/filter-bar.js.map +1 -1
  94. package/dist/gauge/gauge.d.ts +45 -0
  95. package/dist/gauge/gauge.js +219 -0
  96. package/dist/gauge/gauge.js.map +1 -0
  97. package/dist/gauge/index.d.ts +2 -0
  98. package/dist/gauge/index.js +2 -0
  99. package/dist/in-view-title/in-view-title.d.ts +33 -0
  100. package/dist/in-view-title/in-view-title.js +119 -0
  101. package/dist/in-view-title/in-view-title.js.map +1 -0
  102. package/dist/in-view-title/index.d.ts +2 -0
  103. package/dist/in-view-title/index.js +2 -0
  104. package/dist/index.d.ts +53 -10
  105. package/dist/index.js +52 -12
  106. package/dist/input/input.js +4 -4
  107. package/dist/input/input.js.map +1 -1
  108. package/dist/lib/cn.js +11 -1
  109. package/dist/lib/cn.js.map +1 -1
  110. package/dist/lib/field-message.js +28 -10
  111. package/dist/lib/field-message.js.map +1 -1
  112. package/dist/lib/field-shell.js +10 -3
  113. package/dist/lib/field-shell.js.map +1 -1
  114. package/dist/lib/field-surface.js +31 -9
  115. package/dist/lib/field-surface.js.map +1 -1
  116. package/dist/lib/flag-emoji.js +50 -0
  117. package/dist/lib/flag-emoji.js.map +1 -0
  118. package/dist/lib/flip.js +11 -3
  119. package/dist/lib/flip.js.map +1 -1
  120. package/dist/lib/menu-highlight.js +64 -0
  121. package/dist/lib/menu-highlight.js.map +1 -0
  122. package/dist/lib/motion.js +9 -2
  123. package/dist/lib/motion.js.map +1 -1
  124. package/dist/lib/rolling-value.js +1 -1
  125. package/dist/lib/rolling-value.js.map +1 -1
  126. package/dist/lib/spring-tween.js +500 -0
  127. package/dist/lib/spring-tween.js.map +1 -0
  128. package/dist/lib/swap-text.js +3 -1
  129. package/dist/lib/swap-text.js.map +1 -1
  130. package/dist/lib/use-copy-feedback.js +17 -3
  131. package/dist/lib/use-copy-feedback.js.map +1 -1
  132. package/dist/line-chart/index.d.ts +2 -0
  133. package/dist/line-chart/index.js +2 -0
  134. package/dist/line-chart/line-chart.d.ts +116 -0
  135. package/dist/line-chart/line-chart.js +740 -0
  136. package/dist/line-chart/line-chart.js.map +1 -0
  137. package/dist/mention-input/index.d.ts +2 -0
  138. package/dist/mention-input/index.js +2 -0
  139. package/dist/mention-input/mention-input.d.ts +98 -0
  140. package/dist/mention-input/mention-input.js +576 -0
  141. package/dist/mention-input/mention-input.js.map +1 -0
  142. package/dist/menu/menu.d.ts +2 -0
  143. package/dist/menu/menu.js +23 -12
  144. package/dist/menu/menu.js.map +1 -1
  145. package/dist/money-input/index.d.ts +2 -0
  146. package/dist/money-input/index.js +2 -0
  147. package/dist/money-input/money-input.d.ts +86 -0
  148. package/dist/money-input/money-input.js +613 -0
  149. package/dist/money-input/money-input.js.map +1 -0
  150. package/dist/morph-select/index.d.ts +2 -0
  151. package/dist/morph-select/index.js +2 -0
  152. package/dist/morph-select/morph-select.d.ts +89 -0
  153. package/dist/morph-select/morph-select.js +712 -0
  154. package/dist/morph-select/morph-select.js.map +1 -0
  155. package/dist/multi-select/index.d.ts +2 -0
  156. package/dist/multi-select/index.js +2 -0
  157. package/dist/multi-select/multi-select.d.ts +77 -0
  158. package/dist/multi-select/multi-select.js +209 -0
  159. package/dist/multi-select/multi-select.js.map +1 -0
  160. package/dist/number-field/index.d.ts +2 -0
  161. package/dist/number-field/index.js +2 -0
  162. package/dist/number-field/number-field.d.ts +42 -0
  163. package/dist/number-field/number-field.js +199 -0
  164. package/dist/number-field/number-field.js.map +1 -0
  165. package/dist/onboarding-checklist/index.d.ts +2 -0
  166. package/dist/onboarding-checklist/index.js +2 -0
  167. package/dist/onboarding-checklist/onboarding-checklist.d.ts +55 -0
  168. package/dist/onboarding-checklist/onboarding-checklist.js +577 -0
  169. package/dist/onboarding-checklist/onboarding-checklist.js.map +1 -0
  170. package/dist/password-field/password-field.d.ts +16 -0
  171. package/dist/password-field/password-field.js +39 -10
  172. package/dist/password-field/password-field.js.map +1 -1
  173. package/dist/phone-input/index.d.ts +3 -0
  174. package/dist/phone-input/index.js +3 -0
  175. package/dist/phone-input/phone-countries.d.ts +29 -0
  176. package/dist/phone-input/phone-countries.js +170 -0
  177. package/dist/phone-input/phone-countries.js.map +1 -0
  178. package/dist/phone-input/phone-input.d.ts +66 -0
  179. package/dist/phone-input/phone-input.js +440 -0
  180. package/dist/phone-input/phone-input.js.map +1 -0
  181. package/dist/popover/index.d.ts +2 -2
  182. package/dist/popover/index.js +2 -2
  183. package/dist/popover/popover.d.ts +51 -2
  184. package/dist/popover/popover.js +59 -8
  185. package/dist/popover/popover.js.map +1 -1
  186. package/dist/progress/progress.d.ts +16 -2
  187. package/dist/progress/progress.js +16 -9
  188. package/dist/progress/progress.js.map +1 -1
  189. package/dist/radio-group/index.d.ts +2 -2
  190. package/dist/radio-group/index.js +2 -2
  191. package/dist/radio-group/radio-group.d.ts +33 -3
  192. package/dist/radio-group/radio-group.js +110 -10
  193. package/dist/radio-group/radio-group.js.map +1 -1
  194. package/dist/reactions/index.d.ts +2 -0
  195. package/dist/reactions/index.js +2 -0
  196. package/dist/reactions/reactions.d.ts +50 -0
  197. package/dist/reactions/reactions.js +315 -0
  198. package/dist/reactions/reactions.js.map +1 -0
  199. package/dist/rich-text-editor/index.d.ts +3 -0
  200. package/dist/rich-text-editor/index.js +2 -0
  201. package/dist/rich-text-editor/rich-text-editor.d.ts +70 -0
  202. package/dist/rich-text-editor/rich-text-editor.js +909 -0
  203. package/dist/rich-text-editor/rich-text-editor.js.map +1 -0
  204. package/dist/rich-text-editor/rich-text-engine.d.ts +16 -0
  205. package/dist/rich-text-editor/rich-text-engine.js +366 -0
  206. package/dist/rich-text-editor/rich-text-engine.js.map +1 -0
  207. package/dist/rich-text-editor/rich-text-html.js +174 -0
  208. package/dist/rich-text-editor/rich-text-html.js.map +1 -0
  209. package/dist/rich-text-editor/rich-text-icons.js +43 -0
  210. package/dist/rich-text-editor/rich-text-icons.js.map +1 -0
  211. package/dist/ridgeline/index.d.ts +2 -0
  212. package/dist/ridgeline/index.js +2 -0
  213. package/dist/ridgeline/ridgeline.d.ts +55 -0
  214. package/dist/ridgeline/ridgeline.js +701 -0
  215. package/dist/ridgeline/ridgeline.js.map +1 -0
  216. package/dist/scroll-area/index.d.ts +2 -2
  217. package/dist/scroll-area/index.js +2 -2
  218. package/dist/scroll-area/scroll-area.d.ts +68 -31
  219. package/dist/scroll-area/scroll-area.js +212 -62
  220. package/dist/scroll-area/scroll-area.js.map +1 -1
  221. package/dist/scrub-input/evaluate.d.ts +14 -0
  222. package/dist/scrub-input/evaluate.js +72 -0
  223. package/dist/scrub-input/evaluate.js.map +1 -0
  224. package/dist/scrub-input/index.d.ts +3 -0
  225. package/dist/scrub-input/index.js +3 -0
  226. package/dist/scrub-input/scrub-input.d.ts +74 -0
  227. package/dist/scrub-input/scrub-input.js +486 -0
  228. package/dist/scrub-input/scrub-input.js.map +1 -0
  229. package/dist/search-field/search-field.d.ts +24 -2
  230. package/dist/search-field/search-field.js +24 -9
  231. package/dist/search-field/search-field.js.map +1 -1
  232. package/dist/select/select.d.ts +3 -1
  233. package/dist/select/select.js +28 -14
  234. package/dist/select/select.js.map +1 -1
  235. package/dist/separator/separator.d.ts +22 -10
  236. package/dist/separator/separator.js +33 -11
  237. package/dist/separator/separator.js.map +1 -1
  238. package/dist/sheet/sheet.js +2 -1
  239. package/dist/sheet/sheet.js.map +1 -1
  240. package/dist/shortcut-recorder/index.d.ts +3 -0
  241. package/dist/shortcut-recorder/index.js +3 -0
  242. package/dist/shortcut-recorder/shortcut-model.d.ts +27 -0
  243. package/dist/shortcut-recorder/shortcut-model.js +206 -0
  244. package/dist/shortcut-recorder/shortcut-model.js.map +1 -0
  245. package/dist/shortcut-recorder/shortcut-recorder.d.ts +93 -0
  246. package/dist/shortcut-recorder/shortcut-recorder.js +489 -0
  247. package/dist/shortcut-recorder/shortcut-recorder.js.map +1 -0
  248. package/dist/slider/index.d.ts +2 -2
  249. package/dist/slider/slider.d.ts +46 -14
  250. package/dist/slider/slider.js +151 -67
  251. package/dist/slider/slider.js.map +1 -1
  252. package/dist/slope-chart/index.d.ts +2 -0
  253. package/dist/slope-chart/index.js +2 -0
  254. package/dist/slope-chart/slope-chart.d.ts +54 -0
  255. package/dist/slope-chart/slope-chart.js +568 -0
  256. package/dist/slope-chart/slope-chart.js.map +1 -0
  257. package/dist/sparkline/index.d.ts +2 -0
  258. package/dist/sparkline/index.js +2 -0
  259. package/dist/sparkline/sparkline.d.ts +46 -0
  260. package/dist/sparkline/sparkline.js +390 -0
  261. package/dist/sparkline/sparkline.js.map +1 -0
  262. package/dist/split-bar/index.d.ts +2 -0
  263. package/dist/split-bar/index.js +2 -0
  264. package/dist/split-bar/split-bar.d.ts +62 -0
  265. package/dist/split-bar/split-bar.js +513 -0
  266. package/dist/split-bar/split-bar.js.map +1 -0
  267. package/dist/split-button/split-button.d.ts +1 -1
  268. package/dist/split-button/split-button.js +12 -11
  269. package/dist/split-button/split-button.js.map +1 -1
  270. package/dist/stat-card/index.d.ts +2 -2
  271. package/dist/stat-card/index.js +2 -2
  272. package/dist/stat-card/stat-card.d.ts +58 -5
  273. package/dist/stat-card/stat-card.js +144 -17
  274. package/dist/stat-card/stat-card.js.map +1 -1
  275. package/dist/stepper/index.d.ts +2 -0
  276. package/dist/stepper/index.js +2 -0
  277. package/dist/stepper/stepper.d.ts +51 -0
  278. package/dist/stepper/stepper.js +215 -0
  279. package/dist/stepper/stepper.js.map +1 -0
  280. package/dist/streamgraph/index.d.ts +2 -0
  281. package/dist/streamgraph/index.js +2 -0
  282. package/dist/streamgraph/streamgraph.d.ts +70 -0
  283. package/dist/streamgraph/streamgraph.js +613 -0
  284. package/dist/streamgraph/streamgraph.js.map +1 -0
  285. package/dist/styles.css +1 -1
  286. package/dist/swipe-actions/index.d.ts +2 -0
  287. package/dist/swipe-actions/index.js +2 -0
  288. package/dist/swipe-actions/swipe-actions.d.ts +67 -0
  289. package/dist/swipe-actions/swipe-actions.js +552 -0
  290. package/dist/swipe-actions/swipe-actions.js.map +1 -0
  291. package/dist/switch/switch.js +2 -2
  292. package/dist/switch/switch.js.map +1 -1
  293. package/dist/table/index.d.ts +2 -2
  294. package/dist/table/index.js +2 -2
  295. package/dist/table/table.d.ts +55 -3
  296. package/dist/table/table.js +340 -165
  297. package/dist/table/table.js.map +1 -1
  298. package/dist/tabs/tabs.js +2 -2
  299. package/dist/tabs/tabs.js.map +1 -1
  300. package/dist/tag-input/index.d.ts +2 -0
  301. package/dist/tag-input/index.js +2 -0
  302. package/dist/tag-input/tag-input.d.ts +40 -0
  303. package/dist/tag-input/tag-input.js +269 -0
  304. package/dist/tag-input/tag-input.js.map +1 -0
  305. package/dist/text-morph/index.d.ts +2 -0
  306. package/dist/text-morph/index.js +2 -0
  307. package/dist/text-morph/text-morph.d.ts +23 -0
  308. package/dist/text-morph/text-morph.js +21 -0
  309. package/dist/text-morph/text-morph.js.map +1 -0
  310. package/dist/text-reveal/index.d.ts +2 -0
  311. package/dist/text-reveal/index.js +2 -0
  312. package/dist/text-reveal/text-reveal.d.ts +27 -0
  313. package/dist/text-reveal/text-reveal.js +51 -0
  314. package/dist/text-reveal/text-reveal.js.map +1 -0
  315. package/dist/text-shimmer/index.d.ts +2 -0
  316. package/dist/text-shimmer/index.js +2 -0
  317. package/dist/text-shimmer/text-shimmer.d.ts +29 -0
  318. package/dist/text-shimmer/text-shimmer.js +29 -0
  319. package/dist/text-shimmer/text-shimmer.js.map +1 -0
  320. package/dist/time-picker/time-picker.d.ts +36 -1
  321. package/dist/time-picker/time-picker.js +35 -7
  322. package/dist/time-picker/time-picker.js.map +1 -1
  323. package/dist/toast/index.d.ts +2 -2
  324. package/dist/toast/index.js +2 -2
  325. package/dist/toast/toast.d.ts +7 -1
  326. package/dist/toast/toast.js +82 -40
  327. package/dist/toast/toast.js.map +1 -1
  328. package/dist/tooltip/tooltip.d.ts +9 -2
  329. package/dist/tooltip/tooltip.js +7 -7
  330. package/dist/tooltip/tooltip.js.map +1 -1
  331. package/dist/treemap/index.d.ts +2 -0
  332. package/dist/treemap/index.js +2 -0
  333. package/dist/treemap/treemap.d.ts +53 -0
  334. package/dist/treemap/treemap.js +699 -0
  335. package/dist/treemap/treemap.js.map +1 -0
  336. package/dist/usage-meter/index.d.ts +2 -0
  337. package/dist/usage-meter/index.js +2 -0
  338. package/dist/usage-meter/usage-meter.d.ts +80 -0
  339. package/dist/usage-meter/usage-meter.js +458 -0
  340. package/dist/usage-meter/usage-meter.js.map +1 -0
  341. package/dist/waffle-chart/index.d.ts +2 -0
  342. package/dist/waffle-chart/index.js +2 -0
  343. package/dist/waffle-chart/waffle-chart.d.ts +57 -0
  344. package/dist/waffle-chart/waffle-chart.js +426 -0
  345. package/dist/waffle-chart/waffle-chart.js.map +1 -0
  346. package/package.json +165 -3
@@ -1,18 +1,161 @@
1
1
  "use client";
2
- import { Dialog } from "../dialog/dialog.js";
2
+ import { cnAll } from "../lib/cn.js";
3
+ import { Button as Button$1 } from "../button/button.js";
4
+ import { SwapText } from "../lib/swap-text.js";
5
+ import { dialogOverlayVariants, dialogPanelVariants } from "../dialog/dialog.js";
6
+ import { cva } from "class-variance-authority";
3
7
  import { forwardRef } from "react";
4
- import { jsx } from "react/jsx-runtime";
8
+ import { Button, Dialog, Heading, Modal, ModalOverlay, Text } from "react-aria-components";
9
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
5
10
  //#region src/alert-dialog/alert-dialog.tsx
6
- const AlertDialog = forwardRef(function AlertDialog(props, ref) {
7
- return /* @__PURE__ */ jsx(Dialog, {
8
- ...props,
11
+ const alertDialogActionsVariants = cva("flex flex-wrap items-center justify-end gap-2 px-8 pb-8");
12
+ /**
13
+ * The body slot: the description's side padding, so the two share an edge,
14
+ * and the description's own bottom space repeated below it before the actions.
15
+ */
16
+ const alertDialogBodyVariants = cva("min-w-0 px-8 pb-6 text-sm leading-(--leading-normal) text-foreground");
17
+ /**
18
+ * The cancel action: the secondary button, wired to dismiss the dialog.
19
+ *
20
+ * @example
21
+ * <AlertDialogCancel>Cancel</AlertDialogCancel>
22
+ */
23
+ const AlertDialogCancel = forwardRef(function AlertDialogCancel(props, ref) {
24
+ return /* @__PURE__ */ jsx(Button$1, {
25
+ ref,
26
+ slot: "close",
27
+ variant: "secondary",
28
+ size: "sm",
29
+ ...props
30
+ });
31
+ });
32
+ /**
33
+ * The confirm action: the primary button in the destructive red.
34
+ *
35
+ * It is the primary button with its fill, hover, pressed and label roles
36
+ * pointed at the destructive tokens, so it keeps the primary's press motion
37
+ * and shadow. Variables on the element, rather than a competing background
38
+ * class, so no stylesheet order decides which colour wins.
39
+ *
40
+ * @example
41
+ * <AlertDialogAction onPress={remove}>Delete</AlertDialogAction>
42
+ */
43
+ const AlertDialogAction = forwardRef(function AlertDialogAction({ className, ...props }, ref) {
44
+ return /* @__PURE__ */ jsx(Button$1, {
9
45
  ref,
10
- role: "alertdialog",
11
- closeButton: false,
12
- isDismissable: false
46
+ variant: "primary",
47
+ size: "sm",
48
+ className: cnAll("[--color-primary:var(--color-destructive)] [--color-primary-hover:var(--color-destructive-hover)] [--color-primary-pressed:var(--color-destructive-pressed)] [--color-primary-foreground:var(--color-destructive-foreground)]", className),
49
+ ...props
50
+ });
51
+ });
52
+ const alertDialogHeaderVariants = cva("flex items-start gap-4 border-b-(length:--border-width) border-border px-8 pt-8 pb-6");
53
+ /**
54
+ * The title column. With no subtitle it is at least the close button's height
55
+ * and centres the title in it, so a one-line title sits level with the close;
56
+ * a longer title grows the column from the top as before. With a subtitle the
57
+ * two lines start at the top of the button, as they always have.
58
+ */
59
+ const alertDialogTitleBlockVariants = cva("min-w-0 flex-1", { variants: { subtitle: {
60
+ true: "",
61
+ false: "flex min-h-9 flex-col justify-center"
62
+ } } });
63
+ const alertDialogTitleVariants = cva("m-0 text-lg leading-(--leading-tight) font-semibold text-foreground");
64
+ const alertDialogCloseVariants = cva([
65
+ "inline-flex size-9 shrink-0 items-center justify-center",
66
+ "rounded-circle box-border cursor-pointer",
67
+ "border-(length:--border-width) border-border bg-surface p-0 text-text-secondary",
68
+ "calendar-nav-motion",
69
+ "data-[hovered]:bg-muted data-[hovered]:text-foreground",
70
+ "outline-none",
71
+ "data-[focus-visible]:outline-solid",
72
+ "data-[focus-visible]:outline-(length:--focus-ring-width)",
73
+ "data-[focus-visible]:outline-offset-(--focus-ring-offset)",
74
+ "data-[focus-visible]:outline-(--color-ring)",
75
+ "[&_svg]:pointer-events-none [&_svg]:size-4"
76
+ ]);
77
+ /** A string swaps as a whole when its text changes while the dialog is open. */
78
+ function Swappable({ value }) {
79
+ if (typeof value !== "string") return /* @__PURE__ */ jsx(Fragment$1, { children: value });
80
+ return /* @__PURE__ */ jsx(SwapText, {
81
+ swapKey: value,
82
+ className: "block",
83
+ children: value
84
+ });
85
+ }
86
+ /** `cond && <X />` hands over `false`; React renders nothing for it, so neither does the slot. */
87
+ function isEmpty(node) {
88
+ return node == null || typeof node === "boolean" || node === "";
89
+ }
90
+ const AlertDialog = forwardRef(function AlertDialog({ title, subtitle, beforeTitle, description, body, children, className, closeLabel = "Close", overlayClassName, entrance = "rise", ...props }, ref) {
91
+ return /* @__PURE__ */ jsx(ModalOverlay, {
92
+ ...props,
93
+ isDismissable: false,
94
+ "data-slot": "dialog-overlay",
95
+ className: cnAll(dialogOverlayVariants(), entrance === "rise" && "dialog-overlay-motion", overlayClassName),
96
+ children: /* @__PURE__ */ jsx(Modal, {
97
+ className: "contents",
98
+ children: /* @__PURE__ */ jsxs(Dialog, {
99
+ ref,
100
+ role: "alertdialog",
101
+ "data-slot": "dialog",
102
+ className: cnAll(dialogPanelVariants(), "gap-0 p-0", entrance === "rise" && "dialog-panel-motion", className),
103
+ children: [
104
+ beforeTitle,
105
+ /* @__PURE__ */ jsxs("div", {
106
+ "data-slot": "alert-dialog-header",
107
+ className: alertDialogHeaderVariants(),
108
+ children: [/* @__PURE__ */ jsxs("div", {
109
+ className: alertDialogTitleBlockVariants({ subtitle: subtitle != null }),
110
+ children: [/* @__PURE__ */ jsx(Heading, {
111
+ slot: "title",
112
+ "data-slot": "dialog-title",
113
+ className: alertDialogTitleVariants(),
114
+ children: /* @__PURE__ */ jsx(Swappable, { value: title })
115
+ }), subtitle == null ? null : /* @__PURE__ */ jsx("p", {
116
+ "data-slot": "alert-dialog-subtitle",
117
+ className: "m-0 mt-1 text-sm leading-(--leading-normal) text-text-secondary",
118
+ children: /* @__PURE__ */ jsx(Swappable, { value: subtitle })
119
+ })]
120
+ }), /* @__PURE__ */ jsx(Button, {
121
+ slot: "close",
122
+ "aria-label": closeLabel,
123
+ "data-slot": "alert-dialog-close",
124
+ className: alertDialogCloseVariants(),
125
+ children: /* @__PURE__ */ jsx("svg", {
126
+ "aria-hidden": "true",
127
+ viewBox: "0 0 16 16",
128
+ fill: "none",
129
+ stroke: "currentColor",
130
+ strokeWidth: "1.75",
131
+ strokeLinecap: "round",
132
+ children: /* @__PURE__ */ jsx("path", { d: "m4 4 8 8M12 4l-8 8" })
133
+ })
134
+ })]
135
+ }),
136
+ /* @__PURE__ */ jsx(Text, {
137
+ slot: "description",
138
+ elementType: "p",
139
+ "data-slot": "dialog-description",
140
+ className: "m-0 block px-8 py-6 text-sm leading-(--leading-normal) text-foreground",
141
+ children: /* @__PURE__ */ jsx(Swappable, { value: description })
142
+ }),
143
+ isEmpty(body) ? null : /* @__PURE__ */ jsx("div", {
144
+ "data-slot": "alert-dialog-body",
145
+ className: alertDialogBodyVariants(),
146
+ children: body
147
+ }),
148
+ children == null ? null : /* @__PURE__ */ jsx("div", {
149
+ "data-slot": "alert-dialog-actions",
150
+ className: alertDialogActionsVariants(),
151
+ children
152
+ })
153
+ ]
154
+ })
155
+ })
13
156
  });
14
157
  });
15
158
  //#endregion
16
- export { AlertDialog };
159
+ export { AlertDialog, AlertDialogAction, AlertDialogCancel };
17
160
 
18
161
  //# sourceMappingURL=alert-dialog.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"alert-dialog.js","names":[],"sources":["../../src/alert-dialog/alert-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { forwardRef, type ReactNode } from \"react\";\n\nimport { Dialog, type DialogProps } from \"../dialog/dialog.js\";\n\n/**\n * AlertDialog: a confirmation that must be answered.\n *\n * A PRESET over `Dialog`, not a second implementation. The legacy\n * `alert-dialog.tsx` is a 157-line clone of `dialog.tsx` with three\n * differences, and keeping two copies is how they drift -- the legacy pair\n * already disagree about `leading-none` on their titles for no stated\n * reason.\n *\n * Census: 2 files, all eight parts used twice each, `AlertDialogPortal`,\n * `AlertDialogOverlay` and `AlertDialogTrigger` never rendered externally.\n *\n * What the preset forces, and why each one is a defect if forgotten:\n *\n * - `role=\"alertdialog\"`. React Aria associates the description with\n * `aria-describedby` ONLY for this role (useDialog.mjs:29), so a\n * confirmation built from a plain Dialog loses the sentence explaining\n * what is about to happen.\n * - `closeButton={false}`. An X in the corner is a third answer to a\n * two-answer question, and it is ambiguous: it reads as \"cancel\" but\n * looks like \"dismiss\".\n * - `isDismissable={false}`. Clicking the page must not resolve a\n * destructive confirmation. Escape still cancels, which is the APG\n * behaviour and what Radix does.\n * - `description` is REQUIRED. A confirmation with no explanation is the\n * thing this component exists to prevent, and the role only associates it\n * when it is there.\n *\n * NO `AlertDialogAction` OR `AlertDialogCancel`. The legacy shipped both,\n * with `AlertDialogAction` defaulting to the gradient primary button -- and\n * BOTH call sites override it back to destructive by hand\n * (`data-table.tsx:146`, `calendar-view.tsx:638`). A default that every\n * caller fights is not a default. Use a `Button` and say which one it is:\n * `slot=\"close\"` cancels, and the confirm button picks its own variant.\n */\nexport interface AlertDialogProps\n extends Omit<DialogProps, \"role\" | \"closeButton\" | \"closeLabel\" | \"isDismissable\"> {\n /**\n * What is about to happen, and whether it can be undone.\n *\n * **Required**, unlike on `Dialog`. `role=\"alertdialog\"` is what makes\n * React Aria wire this to `aria-describedby`, so it is both the sentence\n * the user needs and the one the screen reader announces with the name.\n */\n description: ReactNode;\n /** The actions. Give the cancel button `slot=\"close\"`. */\n children?: ReactNode;\n}\n\nexport const AlertDialog = forwardRef<HTMLElement, AlertDialogProps>(function AlertDialog(\n props,\n ref,\n) {\n return (\n <Dialog {...props} ref={ref} role=\"alertdialog\" closeButton={false} isDismissable={false} />\n );\n});\n"],"mappings":";;;;;AAuDA,MAAa,cAAc,WAA0C,SAAS,YAC5E,OACA,KACA;CACA,OACE,oBAAC,QAAD;EAAQ,GAAI;EAAY;EAAK,MAAK;EAAc,aAAa;EAAO,eAAe;CAAQ,CAAA;AAE/F,CAAC"}
1
+ {"version":3,"file":"alert-dialog.js","names":["Button","AriaModalOverlay","AriaModal","AriaDialog","AriaHeading","AriaButton","AriaText"],"sources":["../../src/alert-dialog/alert-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, type ReactNode } from \"react\";\nimport {\n Button as AriaButton,\n Dialog as AriaDialog,\n Heading as AriaHeading,\n Modal as AriaModal,\n ModalOverlay as AriaModalOverlay,\n Text as AriaText,\n} from \"react-aria-components\";\n\nimport { Button, type ButtonProps } from \"../button/button.js\";\nimport { cnAll } from \"../lib/cn.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\nimport { dialogOverlayVariants, dialogPanelVariants, type DialogProps } from \"../dialog/dialog.js\";\n\n/**\n * AlertDialog: a confirmation that must be answered.\n *\n * Built from `Dialog`'s own overlay and panel styles and the same React Aria\n * parts, but with its own layout: a header (title, a grey subtitle and a\n * circular close button), a thin divider, the description sentence, an\n * optional `body` of rich content, then the actions.\n * The legacy `alert-dialog.tsx` was a 157-line clone of `dialog.tsx`; this\n * shares the tokens and the motion classes instead, so the two cannot drift\n * on radius, scrim or entrance.\n *\n * Census: 2 files, all eight parts used twice each, `AlertDialogPortal`,\n * `AlertDialogOverlay` and `AlertDialogTrigger` never rendered externally.\n *\n * What the preset forces, and why each one is a defect if forgotten:\n *\n * - `role=\"alertdialog\"`. React Aria associates the description with\n * `aria-describedby` ONLY for this role (useDialog.mjs:29), so a\n * confirmation built from a plain Dialog loses the sentence explaining\n * what is about to happen.\n * - `isDismissable={false}`. Clicking the page must not resolve a\n * destructive confirmation. Escape still cancels, which is the APG\n * behaviour and what Radix does.\n * - THE CLOSE (X) IS CANCEL. It is `slot=\"close\"`, the same dismiss wiring as\n * a Cancel button, so it goes through the same `onOpenChange(false)` and\n * focus returns to the trigger. It is not a third answer: it is the\n * header's copy of the second one.\n * - `description` is REQUIRED. A confirmation with no explanation is the\n * thing this component exists to prevent, and the role only associates it\n * when it is there. It is the sentence under the divider.\n *\n * THE TWO ACTIONS MUST NEVER LOOK ALIKE. `AlertDialogCancel` is the secondary\n * button (white, light border) and cancels through `slot=\"close\"`;\n * `AlertDialogAction` is the primary button painted destructive red, because a\n * confirmation here is a destructive one. The legacy `AlertDialogAction`\n * defaulted to the gradient primary and BOTH call sites overrode it back to\n * destructive by hand (`data-table.tsx:146`, `calendar-view.tsx:638`); the red\n * is the default now, so nobody fights it. Plain `Button`s still work in the\n * actions slot for anything else.\n */\nexport interface AlertDialogProps extends Omit<\n DialogProps,\n \"role\" | \"closeButton\" | \"isDismissable\"\n> {\n /**\n * What is about to happen, and whether it can be undone. Rendered as the\n * sentence under the divider, above the `body` slot (if any) and the actions.\n *\n * **Required**, unlike on `Dialog`. `role=\"alertdialog\"` is what makes\n * React Aria wire this to `aria-describedby`, so it is both the sentence\n * the user needs and the one the screen reader announces with the name.\n */\n description: ReactNode;\n /** A short grey line under the title, in the header. */\n subtitle?: ReactNode;\n /**\n * Rich content between the description and the actions: a plan comparison,\n * a before/after diff, a quoted reply, a form field or a choice to make.\n *\n * It is NOT part of the accessible description: the screen reader announces\n * the one `description` sentence with the name, and the body is read in\n * place or as focus reaches its controls (each with its own label). So the\n * `description` must say everything needed to answer, including that a\n * choice or field below is required (\"Pick what happens to your data.\").\n * `false`, `true` and `\"\"` (as from `cond && <X />`) render no body.\n */\n body?: ReactNode;\n /** The actions. Give the cancel button `slot=\"close\"`. */\n children?: ReactNode;\n}\n\nconst alertDialogActionsVariants = cva(\"flex flex-wrap items-center justify-end gap-2 px-8 pb-8\");\n\n/**\n * The body slot: the description's side padding, so the two share an edge,\n * and the description's own bottom space repeated below it before the actions.\n */\nconst alertDialogBodyVariants = cva(\n \"min-w-0 px-8 pb-6 text-sm leading-(--leading-normal) text-foreground\",\n);\n\n/**\n * The cancel action: the secondary button, wired to dismiss the dialog.\n *\n * @example\n * <AlertDialogCancel>Cancel</AlertDialogCancel>\n */\nexport const AlertDialogCancel = forwardRef<HTMLButtonElement, ButtonProps>(\n function AlertDialogCancel(props, ref) {\n return <Button ref={ref} slot=\"close\" variant=\"secondary\" size=\"sm\" {...props} />;\n },\n);\n\n/**\n * The confirm action: the primary button in the destructive red.\n *\n * It is the primary button with its fill, hover, pressed and label roles\n * pointed at the destructive tokens, so it keeps the primary's press motion\n * and shadow. Variables on the element, rather than a competing background\n * class, so no stylesheet order decides which colour wins.\n *\n * @example\n * <AlertDialogAction onPress={remove}>Delete</AlertDialogAction>\n */\nexport const AlertDialogAction = forwardRef<HTMLButtonElement, ButtonProps>(\n function AlertDialogAction({ className, ...props }, ref) {\n return (\n <Button\n ref={ref}\n variant=\"primary\"\n size=\"sm\"\n className={cnAll(\n \"[--color-primary:var(--color-destructive)] [--color-primary-hover:var(--color-destructive-hover)] [--color-primary-pressed:var(--color-destructive-pressed)] [--color-primary-foreground:var(--color-destructive-foreground)]\",\n className,\n )}\n {...props}\n />\n );\n },\n);\n\nconst alertDialogHeaderVariants = cva(\n \"flex items-start gap-4 border-b-(length:--border-width) border-border px-8 pt-8 pb-6\",\n);\n\n/**\n * The title column. With no subtitle it is at least the close button's height\n * and centres the title in it, so a one-line title sits level with the close;\n * a longer title grows the column from the top as before. With a subtitle the\n * two lines start at the top of the button, as they always have.\n */\nconst alertDialogTitleBlockVariants = cva(\"min-w-0 flex-1\", {\n variants: {\n subtitle: {\n true: \"\",\n false: \"flex min-h-9 flex-col justify-center\",\n },\n },\n});\n\nconst alertDialogTitleVariants = cva(\n \"m-0 text-lg leading-(--leading-tight) font-semibold text-foreground\",\n);\n\nconst alertDialogCloseVariants = cva([\n \"inline-flex size-9 shrink-0 items-center justify-center\",\n \"rounded-circle box-border cursor-pointer\",\n \"border-(length:--border-width) border-border bg-surface p-0 text-text-secondary\",\n // Fill on hover, a quick press to .96 and a spring release (see calendar-nav-motion).\n \"calendar-nav-motion\",\n \"data-[hovered]:bg-muted data-[hovered]:text-foreground\",\n \"outline-none\",\n \"data-[focus-visible]:outline-solid\",\n \"data-[focus-visible]:outline-(length:--focus-ring-width)\",\n \"data-[focus-visible]:outline-offset-(--focus-ring-offset)\",\n \"data-[focus-visible]:outline-(--color-ring)\",\n \"[&_svg]:pointer-events-none [&_svg]:size-4\",\n]);\n\n/** A string swaps as a whole when its text changes while the dialog is open. */\nfunction Swappable({ value }: { value: ReactNode }) {\n if (typeof value !== \"string\") return <>{value}</>;\n return (\n <SwapText swapKey={value} className=\"block\">\n {value}\n </SwapText>\n );\n}\n\n/** `cond && <X />` hands over `false`; React renders nothing for it, so neither does the slot. */\nfunction isEmpty(node: ReactNode) {\n return node == null || typeof node === \"boolean\" || node === \"\";\n}\n\nexport const AlertDialog = forwardRef<HTMLElement, AlertDialogProps>(function AlertDialog(\n {\n title,\n subtitle,\n beforeTitle,\n description,\n body,\n children,\n className,\n closeLabel = \"Close\",\n overlayClassName,\n entrance = \"rise\",\n ...props\n },\n ref,\n) {\n return (\n <AriaModalOverlay\n {...props}\n isDismissable={false}\n data-slot=\"dialog-overlay\"\n className={cnAll(\n dialogOverlayVariants(),\n entrance === \"rise\" && \"dialog-overlay-motion\",\n overlayClassName,\n )}\n >\n <AriaModal className=\"contents\">\n <AriaDialog\n ref={ref}\n role=\"alertdialog\"\n data-slot=\"dialog\"\n className={cnAll(\n dialogPanelVariants(),\n // The panel has no padding of its own: the header, body and actions pad themselves,\n // so the divider can run edge to edge.\n \"gap-0 p-0\",\n entrance === \"rise\" && \"dialog-panel-motion\",\n className,\n )}\n >\n {beforeTitle}\n <div data-slot=\"alert-dialog-header\" className={alertDialogHeaderVariants()}>\n <div className={alertDialogTitleBlockVariants({ subtitle: subtitle != null })}>\n {/* slot=\"title\" is what React Aria wires aria-labelledby to. */}\n <AriaHeading\n slot=\"title\"\n data-slot=\"dialog-title\"\n className={alertDialogTitleVariants()}\n >\n <Swappable value={title} />\n </AriaHeading>\n {subtitle == null ? null : (\n <p\n data-slot=\"alert-dialog-subtitle\"\n className=\"m-0 mt-1 text-sm leading-(--leading-normal) text-text-secondary\"\n >\n <Swappable value={subtitle} />\n </p>\n )}\n </div>\n {/* slot=\"close\" is the same dismiss wiring as a Cancel button: one handler, focus back to the trigger. */}\n <AriaButton\n slot=\"close\"\n aria-label={closeLabel}\n data-slot=\"alert-dialog-close\"\n className={alertDialogCloseVariants()}\n >\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.75\"\n strokeLinecap=\"round\"\n >\n <path d=\"m4 4 8 8M12 4l-8 8\" />\n </svg>\n </AriaButton>\n </div>\n <AriaText\n slot=\"description\"\n elementType=\"p\"\n data-slot=\"dialog-description\"\n className=\"m-0 block px-8 py-6 text-sm leading-(--leading-normal) text-foreground\"\n >\n <Swappable value={description} />\n </AriaText>\n {isEmpty(body) ? null : (\n <div data-slot=\"alert-dialog-body\" className={alertDialogBodyVariants()}>\n {body}\n </div>\n )}\n {children == null ? null : (\n <div data-slot=\"alert-dialog-actions\" className={alertDialogActionsVariants()}>\n {children}\n </div>\n )}\n </AriaDialog>\n </AriaModal>\n </AriaModalOverlay>\n );\n});\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- import { AlertDialog, AlertDialogProps } from "./alert-dialog.js";
2
- export { AlertDialog, type AlertDialogProps };
1
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogProps } from "./alert-dialog.js";
2
+ export { AlertDialog, AlertDialogAction, AlertDialogCancel, type AlertDialogProps };
@@ -1,2 +1,2 @@
1
- import { AlertDialog } from "./alert-dialog.js";
2
- export { AlertDialog };
1
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel } from "./alert-dialog.js";
2
+ export { AlertDialog, AlertDialogAction, AlertDialogCancel };
@@ -0,0 +1,38 @@
1
+ //#region src/animated-counter/animated-counter.d.ts
2
+ /**
3
+ * AnimatedCounter: a number whose digits are wheels. When `value` changes each
4
+ * digit that changed rolls to its new face, turning the way the whole number
5
+ * moved (up when it grew, down when it shrank, wrapping 9 to 0 like an
6
+ * odometer) while the digits that did not change stay still. A gained or lost
7
+ * digit, or separator, opens or closes its width instead of snapping, so 999
8
+ * becoming 1,000 keeps the ones column the ones column. A changed `label` rises
9
+ * in while the old one lifts away.
10
+ *
11
+ * With `animateOnView` every digit starts at 0 and rolls up to its value, one
12
+ * after another, the first time the counter scrolls into view.
13
+ *
14
+ * The digits are a picture: assistive technology reads the formatted number
15
+ * once, in a polite live region, so a change is announced as text and never as
16
+ * a spinning wheel. Nothing animates on first render, and reduced motion jumps.
17
+ */
18
+ export interface AnimatedCounterProps {
19
+ /** The number to show. Changing it rolls the digits that changed. */
20
+ value: number;
21
+ /** A caption above the number. */
22
+ label?: string;
23
+ /** Text before the number, such as a currency symbol. */
24
+ prefix?: string;
25
+ /** Text after the number, such as a unit. */
26
+ suffix?: string;
27
+ /** Digits after the decimal point. @default 0 */
28
+ decimals?: number;
29
+ /** Roll every digit up from zero the first time the counter scrolls into view. */
30
+ animateOnView?: boolean;
31
+ /** Formatting locale. Fixed by default so server and client render the same digits. @default "en-US" */
32
+ locale?: string;
33
+ /** Extra classes for the counter. Merged last. */
34
+ className?: string;
35
+ }
36
+ export declare const AnimatedCounter: import("react").ForwardRefExoticComponent<AnimatedCounterProps & import("react").RefAttributes<HTMLSpanElement>>;
37
+ //#endregion
38
+ //# sourceMappingURL=animated-counter.d.ts.map
@@ -0,0 +1,230 @@
1
+ "use client";
2
+ import { cn } from "../lib/cn.js";
3
+ import { mergeRefs } from "../lib/merge-refs.js";
4
+ import { prefersReducedMotion, useExitBackstop, useIsomorphicLayoutEffect } from "../lib/motion.js";
5
+ import { SwapText } from "../lib/swap-text.js";
6
+ import { forwardRef, useCallback, useEffect, useMemo, useRef, useState } from "react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ //#region src/animated-counter/animated-counter.tsx
9
+ const FACES = [
10
+ 0,
11
+ 1,
12
+ 2,
13
+ 3,
14
+ 4,
15
+ 5,
16
+ 6,
17
+ 7,
18
+ 8,
19
+ 9
20
+ ];
21
+ /** Stagger between digits on the first reveal (ms), at most 250 in all. */
22
+ const REVEAL_STAGGER_MS = 35;
23
+ const REVEAL_MAX_DELAY_MS = 250;
24
+ /** How long the reveal takes at most: the longest delay, the wheel at 1.2 times the smooth spring, and a margin. */
25
+ const REVEAL_SETTLE_MS = 1200;
26
+ /** The longest a width exit takes (the morph spring, 736ms) plus a margin, for the backstop that removes a node whose animation never ends. */
27
+ const EXIT_BACKSTOP_MS = 900;
28
+ /** Split a formatted number into parts keyed by place value, so 999 to 1,000 keeps the ones column the ones column. */
29
+ function partsFor(value, decimals, locale) {
30
+ const parts = new Intl.NumberFormat(locale, {
31
+ minimumFractionDigits: decimals,
32
+ maximumFractionDigits: decimals,
33
+ numberingSystem: "latn"
34
+ }).formatToParts(value);
35
+ let place = parts.reduce((count, part) => count + (part.type === "integer" ? part.value.length : 0), 0);
36
+ let fraction = 0;
37
+ let order = 0;
38
+ return parts.flatMap((part, index) => {
39
+ if (part.type === "integer") return [...part.value].map((char) => ({
40
+ key: `i${--place}`,
41
+ digit: Number(char),
42
+ order: order++
43
+ }));
44
+ if (part.type === "fraction") return [...part.value].map((char) => ({
45
+ key: `f${fraction++}`,
46
+ digit: Number(char),
47
+ order: order++
48
+ }));
49
+ return [{
50
+ key: part.type === "group" ? `g${place}` : part.type === "decimal" ? "d" : `${part.type}${index}`,
51
+ text: part.value
52
+ }];
53
+ });
54
+ }
55
+ const same = (a, b) => a.key === b.key && ("digit" in a ? "digit" in b && a.digit === b.digit && a.order === b.order : "text" in b && a.text === b.text);
56
+ /** The entries after `parts` arrive: gone ones stay (leaving) where they stood, new ones are fresh. */
57
+ function merge(previous, parts) {
58
+ const before = new Map(previous.map((entry) => [entry.part.key, entry]));
59
+ const result = parts.map((part) => {
60
+ const old = before.get(part.key);
61
+ return {
62
+ part,
63
+ leaving: false,
64
+ fresh: old ? old.fresh && !old.leaving : true
65
+ };
66
+ });
67
+ const live = new Set(parts.map((part) => part.key));
68
+ previous.forEach((entry, index) => {
69
+ if (live.has(entry.part.key)) return;
70
+ let anchor = -1;
71
+ for (let back = index - 1; back >= 0 && anchor < 0; back--) anchor = result.findIndex((item) => item.part.key === previous[back].part.key);
72
+ result.splice(anchor + 1, 0, {
73
+ ...entry,
74
+ leaving: true
75
+ });
76
+ });
77
+ return result;
78
+ }
79
+ /** True once the node has been at least 60% on screen (or at once where IntersectionObserver is missing). */
80
+ function useSeen(node, enabled) {
81
+ const [seen, setSeen] = useState(false);
82
+ useEffect(() => {
83
+ const target = node.current;
84
+ if (!enabled || seen || !target || typeof IntersectionObserver === "undefined") return;
85
+ const observer = new IntersectionObserver(([entry]) => {
86
+ if (entry?.isIntersecting) setSeen(true);
87
+ }, { threshold: .6 });
88
+ observer.observe(target);
89
+ return () => observer.disconnect();
90
+ }, [
91
+ node,
92
+ enabled,
93
+ seen
94
+ ]);
95
+ return seen || enabled && typeof IntersectionObserver === "undefined";
96
+ }
97
+ /** Width presence: opens when it arrives after the first render, closes while it leaves, and is removed once closed. */
98
+ function Slot({ entry, onGone, children }) {
99
+ const { key } = entry.part;
100
+ const gone = useCallback(() => onGone(key), [onGone, key]);
101
+ useExitBackstop(entry.leaving, gone, EXIT_BACKSTOP_MS);
102
+ useIsomorphicLayoutEffect(() => {
103
+ if (entry.leaving && prefersReducedMotion()) gone();
104
+ }, [entry.leaving, gone]);
105
+ return /* @__PURE__ */ jsx("span", {
106
+ "data-state": entry.leaving ? "leaving" : entry.fresh ? "entering" : void 0,
107
+ onAnimationEnd: (event) => {
108
+ if (entry.leaving && event.target === event.currentTarget) gone();
109
+ },
110
+ className: "counter-slot",
111
+ children
112
+ });
113
+ }
114
+ /** One digit wheel. It remembers how far it has turned, so it always keeps going the way the whole number moved. */
115
+ function Column({ digit, direction, phase, delay }) {
116
+ const armed = phase === "armed";
117
+ const [wheel, setWheel] = useState(() => {
118
+ const start = armed ? 0 : digit;
119
+ return {
120
+ digit: start,
121
+ turns: start
122
+ };
123
+ });
124
+ let { turns } = wheel;
125
+ if (!armed && wheel.digit !== digit) {
126
+ const back = direction < 0 && phase === "settled";
127
+ turns = wheel.turns + (back ? -((wheel.digit - digit + 10) % 10) : (digit - wheel.digit + 10) % 10);
128
+ setWheel({
129
+ digit,
130
+ turns
131
+ });
132
+ }
133
+ const style = { "--wheel": turns };
134
+ if (phase === "revealing") {
135
+ style.transitionDelay = `${delay}ms`;
136
+ style.transitionDuration = "calc(var(--duration-smooth) * 1.2)";
137
+ }
138
+ return /* @__PURE__ */ jsxs("span", {
139
+ style,
140
+ className: "counter-column",
141
+ children: [/* @__PURE__ */ jsx("span", {
142
+ className: "counter-sizer",
143
+ children: "0"
144
+ }), FACES.map((face) => /* @__PURE__ */ jsx("span", {
145
+ style: { "--face": face },
146
+ className: "counter-glyph",
147
+ children: face
148
+ }, face))]
149
+ });
150
+ }
151
+ const AnimatedCounter = forwardRef(function AnimatedCounter({ value, label, prefix = "", suffix = "", decimals = 0, animateOnView = false, locale = "en-US", className }, ref) {
152
+ const root = useRef(null);
153
+ const refs = useMemo(() => mergeRefs(ref, root), [ref]);
154
+ const seen = useSeen(root, animateOnView);
155
+ const [phase, setPhase] = useState(animateOnView ? "armed" : "settled");
156
+ if (phase === "armed" && seen) setPhase("revealing");
157
+ useEffect(() => {
158
+ if (phase !== "revealing") return;
159
+ const timer = setTimeout(() => setPhase("settled"), prefersReducedMotion() ? 0 : REVEAL_SETTLE_MS);
160
+ return () => clearTimeout(timer);
161
+ }, [phase]);
162
+ const [previous, setPrevious] = useState(value);
163
+ const [direction, setDirection] = useState(1);
164
+ if (value !== previous) {
165
+ setPrevious(value);
166
+ setDirection(value > previous ? 1 : -1);
167
+ }
168
+ const parts = partsFor(value, decimals, locale);
169
+ const [entries, setEntries] = useState(() => parts.map((part) => ({
170
+ part,
171
+ leaving: false,
172
+ fresh: false
173
+ })));
174
+ const live = entries.filter((entry) => !entry.leaving);
175
+ if (live.length !== parts.length || live.some((entry, index) => !same(entry.part, parts[index]))) setEntries(merge(entries, parts));
176
+ const finish = useCallback((key) => setEntries((list) => list.filter((entry) => !(entry.leaving && entry.part.key === key))), []);
177
+ const text = `${prefix}${parts.map((part) => "text" in part ? part.text : part.digit).join("")}${suffix}`;
178
+ return /* @__PURE__ */ jsxs("span", {
179
+ ref: refs,
180
+ "data-slot": "animated-counter",
181
+ className: cn("animated-counter box-border font-display text-h2 leading-none font-semibold tabular-nums text-foreground", className),
182
+ children: [
183
+ label ? /* @__PURE__ */ jsx("span", {
184
+ className: "counter-label",
185
+ children: /* @__PURE__ */ jsx(SwapText, {
186
+ swapKey: label,
187
+ motion: "rise",
188
+ className: "block",
189
+ children: label
190
+ })
191
+ }) : null,
192
+ /* @__PURE__ */ jsx("span", {
193
+ className: "sr-only",
194
+ "aria-live": "polite",
195
+ children: text
196
+ }),
197
+ /* @__PURE__ */ jsxs("span", {
198
+ className: "counter-value",
199
+ "aria-hidden": "true",
200
+ children: [
201
+ prefix ? /* @__PURE__ */ jsx("span", {
202
+ className: "counter-symbol",
203
+ children: prefix
204
+ }) : null,
205
+ entries.map((entry) => /* @__PURE__ */ jsx(Slot, {
206
+ entry,
207
+ onGone: finish,
208
+ children: "digit" in entry.part ? /* @__PURE__ */ jsx(Column, {
209
+ digit: entry.part.digit,
210
+ direction,
211
+ phase,
212
+ delay: Math.min(entry.part.order * REVEAL_STAGGER_MS, REVEAL_MAX_DELAY_MS)
213
+ }) : /* @__PURE__ */ jsx("span", {
214
+ className: "counter-symbol",
215
+ children: entry.part.text
216
+ })
217
+ }, entry.part.key)),
218
+ suffix ? /* @__PURE__ */ jsx("span", {
219
+ className: "counter-symbol",
220
+ children: suffix
221
+ }) : null
222
+ ]
223
+ })
224
+ ]
225
+ });
226
+ });
227
+ //#endregion
228
+ export { AnimatedCounter };
229
+
230
+ //# sourceMappingURL=animated-counter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"animated-counter.js","names":[],"sources":["../../src/animated-counter/animated-counter.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n} from \"react\";\n\nimport { cn } from \"../lib/cn.js\";\nimport { mergeRefs } from \"../lib/merge-refs.js\";\nimport { prefersReducedMotion, useExitBackstop, useIsomorphicLayoutEffect } from \"../lib/motion.js\";\nimport { SwapText } from \"../lib/swap-text.js\";\n\n/**\n * AnimatedCounter: a number whose digits are wheels. When `value` changes each\n * digit that changed rolls to its new face, turning the way the whole number\n * moved (up when it grew, down when it shrank, wrapping 9 to 0 like an\n * odometer) while the digits that did not change stay still. A gained or lost\n * digit, or separator, opens or closes its width instead of snapping, so 999\n * becoming 1,000 keeps the ones column the ones column. A changed `label` rises\n * in while the old one lifts away.\n *\n * With `animateOnView` every digit starts at 0 and rolls up to its value, one\n * after another, the first time the counter scrolls into view.\n *\n * The digits are a picture: assistive technology reads the formatted number\n * once, in a polite live region, so a change is announced as text and never as\n * a spinning wheel. Nothing animates on first render, and reduced motion jumps.\n */\nexport interface AnimatedCounterProps {\n /** The number to show. Changing it rolls the digits that changed. */\n value: number;\n /** A caption above the number. */\n label?: string;\n /** Text before the number, such as a currency symbol. */\n prefix?: string;\n /** Text after the number, such as a unit. */\n suffix?: string;\n /** Digits after the decimal point. @default 0 */\n decimals?: number;\n /** Roll every digit up from zero the first time the counter scrolls into view. */\n animateOnView?: boolean;\n /** Formatting locale. Fixed by default so server and client render the same digits. @default \"en-US\" */\n locale?: string;\n /** Extra classes for the counter. Merged last. */\n className?: string;\n}\n\ntype Part = { key: string; digit: number; order: number } | { key: string; text: string };\n\ninterface Entry {\n part: Part;\n /** Kept on screen while its width closes. */\n leaving: boolean;\n /** Appeared after the first render, so its width opens. */\n fresh: boolean;\n}\n\nconst FACES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] as const;\n/** Stagger between digits on the first reveal (ms), at most 250 in all. */\nconst REVEAL_STAGGER_MS = 35;\nconst REVEAL_MAX_DELAY_MS = 250;\n/** How long the reveal takes at most: the longest delay, the wheel at 1.2 times the smooth spring, and a margin. */\nconst REVEAL_SETTLE_MS = 1200;\n/** The longest a width exit takes (the morph spring, 736ms) plus a margin, for the backstop that removes a node whose animation never ends. */\nconst EXIT_BACKSTOP_MS = 900;\n\n/** Split a formatted number into parts keyed by place value, so 999 to 1,000 keeps the ones column the ones column. */\nfunction partsFor(value: number, decimals: number, locale: string): Part[] {\n const parts = new Intl.NumberFormat(locale, {\n minimumFractionDigits: decimals,\n maximumFractionDigits: decimals,\n numberingSystem: \"latn\",\n }).formatToParts(value);\n let place = parts.reduce((count, part) => count + (part.type === \"integer\" ? part.value.length : 0), 0);\n let fraction = 0;\n let order = 0;\n return parts.flatMap((part, index): Part[] => {\n if (part.type === \"integer\")\n return [...part.value].map((char) => ({ key: `i${--place}`, digit: Number(char), order: order++ }));\n if (part.type === \"fraction\")\n return [...part.value].map((char) => ({ key: `f${fraction++}`, digit: Number(char), order: order++ }));\n const key = part.type === \"group\" ? `g${place}` : part.type === \"decimal\" ? \"d\" : `${part.type}${index}`;\n return [{ key, text: part.value }];\n });\n}\n\nconst same = (a: Part, b: Part) =>\n a.key === b.key && (\"digit\" in a ? \"digit\" in b && a.digit === b.digit && a.order === b.order : \"text\" in b && a.text === b.text);\n\n/** The entries after `parts` arrive: gone ones stay (leaving) where they stood, new ones are fresh. */\nfunction merge(previous: Entry[], parts: Part[]): Entry[] {\n const before = new Map(previous.map((entry) => [entry.part.key, entry]));\n const result: Entry[] = parts.map((part) => {\n const old = before.get(part.key);\n return { part, leaving: false, fresh: old ? old.fresh && !old.leaving : true };\n });\n const live = new Set(parts.map((part) => part.key));\n previous.forEach((entry, index) => {\n if (live.has(entry.part.key)) return;\n let anchor = -1;\n for (let back = index - 1; back >= 0 && anchor < 0; back--) {\n anchor = result.findIndex((item) => item.part.key === previous[back]!.part.key);\n }\n result.splice(anchor + 1, 0, { ...entry, leaving: true });\n });\n return result;\n}\n\n/** True once the node has been at least 60% on screen (or at once where IntersectionObserver is missing). */\nfunction useSeen(node: { current: HTMLElement | null }, enabled: boolean) {\n const [seen, setSeen] = useState(false);\n useEffect(() => {\n const target = node.current;\n if (!enabled || seen || !target || typeof IntersectionObserver === \"undefined\") return;\n const observer = new IntersectionObserver(\n ([entry]) => {\n if (entry?.isIntersecting) setSeen(true);\n },\n { threshold: 0.6 },\n );\n observer.observe(target);\n return () => observer.disconnect();\n }, [node, enabled, seen]);\n return seen || (enabled && typeof IntersectionObserver === \"undefined\");\n}\n\n/** Width presence: opens when it arrives after the first render, closes while it leaves, and is removed once closed. */\nfunction Slot({\n entry,\n onGone,\n children,\n}: {\n entry: Entry;\n onGone: (key: string) => void;\n children: ReactNode;\n}) {\n const { key } = entry.part;\n const gone = useCallback(() => onGone(key), [onGone, key]);\n useExitBackstop(entry.leaving, gone, EXIT_BACKSTOP_MS);\n // Reduced motion removes it at once, and an animation that is switched off never ends.\n useIsomorphicLayoutEffect(() => {\n if (entry.leaving && prefersReducedMotion()) gone();\n }, [entry.leaving, gone]);\n return (\n <span\n data-state={entry.leaving ? \"leaving\" : entry.fresh ? \"entering\" : undefined}\n onAnimationEnd={(event) => {\n if (entry.leaving && event.target === event.currentTarget) gone();\n }}\n className=\"counter-slot\"\n >\n {children}\n </span>\n );\n}\n\n/** One digit wheel. It remembers how far it has turned, so it always keeps going the way the whole number moved. */\nfunction Column({\n digit,\n direction,\n phase,\n delay,\n}: {\n digit: number;\n direction: number;\n phase: \"armed\" | \"revealing\" | \"settled\";\n delay: number;\n}) {\n const armed = phase === \"armed\";\n const [wheel, setWheel] = useState(() => {\n const start = armed ? 0 : digit;\n return { digit: start, turns: start };\n });\n let { turns } = wheel;\n if (!armed && wheel.digit !== digit) {\n // The first reveal always counts up from 0; afterwards a smaller number turns the wheel back.\n const back = direction < 0 && phase === \"settled\";\n turns = wheel.turns + (back ? -((wheel.digit - digit + 10) % 10) : (digit - wheel.digit + 10) % 10);\n setWheel({ digit, turns });\n }\n const style: CSSProperties & Record<string, string | number> = { \"--wheel\": turns };\n // The first reveal rolls on a slower spring (1.2 times the smooth one), each digit a beat after the last.\n if (phase === \"revealing\") {\n style.transitionDelay = `${delay}ms`;\n style.transitionDuration = \"calc(var(--duration-smooth) * 1.2)\";\n }\n return (\n <span style={style} className=\"counter-column\">\n <span className=\"counter-sizer\">0</span>\n {FACES.map((face) => (\n <span key={face} style={{ \"--face\": face } as CSSProperties} className=\"counter-glyph\">\n {face}\n </span>\n ))}\n </span>\n );\n}\n\nexport const AnimatedCounter = forwardRef<HTMLSpanElement, AnimatedCounterProps>(function AnimatedCounter(\n { value, label, prefix = \"\", suffix = \"\", decimals = 0, animateOnView = false, locale = \"en-US\", className },\n ref,\n) {\n const root = useRef<HTMLSpanElement | null>(null);\n const refs = useMemo(() => mergeRefs(ref, root), [ref]);\n const seen = useSeen(root, animateOnView);\n const [phase, setPhase] = useState<\"armed\" | \"revealing\" | \"settled\">(animateOnView ? \"armed\" : \"settled\");\n if (phase === \"armed\" && seen) setPhase(\"revealing\");\n useEffect(() => {\n if (phase !== \"revealing\") return;\n const timer = setTimeout(() => setPhase(\"settled\"), prefersReducedMotion() ? 0 : REVEAL_SETTLE_MS);\n return () => clearTimeout(timer);\n }, [phase]);\n\n const [previous, setPrevious] = useState(value);\n const [direction, setDirection] = useState(1);\n if (value !== previous) {\n setPrevious(value);\n setDirection(value > previous ? 1 : -1);\n }\n\n const parts = partsFor(value, decimals, locale);\n const [entries, setEntries] = useState<Entry[]>(() =>\n parts.map((part) => ({ part, leaving: false, fresh: false })),\n );\n const live = entries.filter((entry) => !entry.leaving);\n if (live.length !== parts.length || live.some((entry, index) => !same(entry.part, parts[index]!))) {\n setEntries(merge(entries, parts));\n }\n const finish = useCallback(\n (key: string) =>\n setEntries((list) => list.filter((entry) => !(entry.leaving && entry.part.key === key))),\n [],\n );\n\n const text = `${prefix}${parts.map((part) => (\"text\" in part ? part.text : part.digit)).join(\"\")}${suffix}`;\n return (\n <span\n ref={refs}\n data-slot=\"animated-counter\"\n className={cn(\n \"animated-counter box-border font-display text-h2 leading-none font-semibold tabular-nums text-foreground\",\n className,\n )}\n >\n {label ? (\n <span className=\"counter-label\">\n <SwapText swapKey={label} motion=\"rise\" className=\"block\">\n {label}\n </SwapText>\n </span>\n ) : null}\n <span className=\"sr-only\" aria-live=\"polite\">\n {text}\n </span>\n <span className=\"counter-value\" aria-hidden=\"true\">\n {prefix ? <span className=\"counter-symbol\">{prefix}</span> : null}\n {entries.map((entry) => (\n <Slot key={entry.part.key} entry={entry} onGone={finish}>\n {\"digit\" in entry.part ? (\n <Column\n digit={entry.part.digit}\n direction={direction}\n phase={phase}\n delay={Math.min(entry.part.order * REVEAL_STAGGER_MS, REVEAL_MAX_DELAY_MS)}\n />\n ) : (\n <span className=\"counter-symbol\">{entry.part.text}</span>\n )}\n </Slot>\n ))}\n {suffix ? <span className=\"counter-symbol\">{suffix}</span> : null}\n </span>\n </span>\n );\n});\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { AnimatedCounter, AnimatedCounterProps } from "./animated-counter.js";
2
+ export { AnimatedCounter, type AnimatedCounterProps };
@@ -0,0 +1,2 @@
1
+ import { AnimatedCounter } from "./animated-counter.js";
2
+ export { AnimatedCounter };
@@ -0,0 +1,67 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/announcement-bar/announcement-bar.d.ts
3
+ export interface AnnouncementAction {
4
+ label: string;
5
+ /** Renders a link. Without it the action is a button. */
6
+ href?: string;
7
+ onClick?: () => void;
8
+ }
9
+ export interface Announcement {
10
+ /** Stable id, reported to callbacks. */
11
+ id: string;
12
+ message: ReactNode;
13
+ action?: AnnouncementAction;
14
+ /** Counts down to a moment, for example the end of a sale. */
15
+ countdown?: {
16
+ to: Date | string | number;
17
+ label?: string;
18
+ };
19
+ }
20
+ /**
21
+ * AnnouncementBar: a slim bar for the top of a page.
22
+ *
23
+ * It can rotate several messages: the next one rises in from below (the
24
+ * previous one from above) while the bar follows the new height. Rotation pauses
25
+ * on hover, keyboard focus, a hidden tab and, with the `controls`, a Pause
26
+ * button whose ring is the clock; it is off under reduced motion. Messages can
27
+ * carry a call to action and a live countdown whose digits roll. Dismissing
28
+ * collapses the bar's height so the page below eases up, and with an `id` the
29
+ * dismissal is remembered in `localStorage`. While rotation runs the message is
30
+ * not announced on each change; when it holds, changes are announced politely.
31
+ */
32
+ export interface AnnouncementBarProps {
33
+ messages: Announcement[];
34
+ /** Remembers dismissal under this id. Change the id to show a new campaign to everyone again. */
35
+ id?: string;
36
+ /** Whether the bar is shown. Leave it out to let the component manage it. */
37
+ open?: boolean;
38
+ /** @default true */
39
+ defaultOpen?: boolean;
40
+ onOpenChange?: (open: boolean) => void;
41
+ /** Index of the visible message. */
42
+ index?: number;
43
+ /** @default 0 */
44
+ defaultIndex?: number;
45
+ onIndexChange?: (index: number) => void;
46
+ /** Milliseconds each message stays before the next one. @default 6000 */
47
+ interval?: number;
48
+ /** Rotate automatically. Off under reduced motion. @default true */
49
+ autoPlay?: boolean;
50
+ /** Show previous, next and pause controls when there are several messages. By default only the close button shows. @default false */
51
+ controls?: boolean;
52
+ /** @default true */
53
+ dismissible?: boolean;
54
+ /** `inverted` is the foreground colour as the fill. @default "neutral" */
55
+ tone?: "neutral" | "inverted";
56
+ onAction?: (announcement: Announcement) => void;
57
+ onCountdownEnd?: (announcement: Announcement) => void;
58
+ /** Accessible name of the region. @default "Announcements" */
59
+ label?: string;
60
+ /** Extra classes for the bar. Merged last. */
61
+ className?: string;
62
+ }
63
+ /** Forgets a remembered dismissal so the bar with this id shows again. */
64
+ export declare function clearAnnouncementDismissal(id: string): void;
65
+ export declare const AnnouncementBar: import("react").ForwardRefExoticComponent<AnnouncementBarProps & import("react").RefAttributes<HTMLElement>>;
66
+ //#endregion
67
+ //# sourceMappingURL=announcement-bar.d.ts.map