@seekalfred/primitives 0.6.0 → 0.7.1

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 (322) hide show
  1. package/README.md +19 -11
  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 +5 -1
  8. package/dist/alert/alert.js +68 -7
  9. package/dist/alert/alert.js.map +1 -1
  10. package/dist/alert-dialog/alert-dialog.d.ts +45 -18
  11. package/dist/alert-dialog/alert-dialog.js +128 -9
  12. package/dist/alert-dialog/alert-dialog.js.map +1 -1
  13. package/dist/alert-dialog/index.d.ts +2 -2
  14. package/dist/alert-dialog/index.js +2 -2
  15. package/dist/animated-counter/animated-counter.d.ts +38 -0
  16. package/dist/animated-counter/animated-counter.js +230 -0
  17. package/dist/animated-counter/animated-counter.js.map +1 -0
  18. package/dist/animated-counter/index.d.ts +2 -0
  19. package/dist/animated-counter/index.js +2 -0
  20. package/dist/announcement-bar/announcement-bar.d.ts +67 -0
  21. package/dist/announcement-bar/announcement-bar.js +364 -0
  22. package/dist/announcement-bar/announcement-bar.js.map +1 -0
  23. package/dist/announcement-bar/index.d.ts +2 -0
  24. package/dist/announcement-bar/index.js +2 -0
  25. package/dist/badge/badge.d.ts +1 -1
  26. package/dist/bar-chart/bar-chart.d.ts +51 -0
  27. package/dist/bar-chart/bar-chart.js +565 -0
  28. package/dist/bar-chart/bar-chart.js.map +1 -0
  29. package/dist/bar-chart/index.d.ts +2 -0
  30. package/dist/bar-chart/index.js +2 -0
  31. package/dist/breadcrumb/breadcrumb.d.ts +2 -2
  32. package/dist/breadcrumb/breadcrumb.js +12 -3
  33. package/dist/breadcrumb/breadcrumb.js.map +1 -1
  34. package/dist/brush-chart/brush-chart.d.ts +64 -0
  35. package/dist/brush-chart/brush-chart.js +1056 -0
  36. package/dist/brush-chart/brush-chart.js.map +1 -0
  37. package/dist/brush-chart/index.d.ts +2 -0
  38. package/dist/brush-chart/index.js +2 -0
  39. package/dist/button/button.d.ts +2 -2
  40. package/dist/button/button.js +31 -21
  41. package/dist/button/button.js.map +1 -1
  42. package/dist/button-group/button-group.js +3 -4
  43. package/dist/button-group/button-group.js.map +1 -1
  44. package/dist/card/card.js +1 -1
  45. package/dist/card/card.js.map +1 -1
  46. package/dist/card-stack/card-stack.d.ts +54 -0
  47. package/dist/card-stack/card-stack.js +492 -0
  48. package/dist/card-stack/card-stack.js.map +1 -0
  49. package/dist/card-stack/index.d.ts +2 -0
  50. package/dist/card-stack/index.js +2 -0
  51. package/dist/carousel/carousel.d.ts +56 -0
  52. package/dist/carousel/carousel.js +389 -0
  53. package/dist/carousel/carousel.js.map +1 -0
  54. package/dist/carousel/index.d.ts +2 -0
  55. package/dist/carousel/index.js +2 -0
  56. package/dist/checkbox/checkbox.js +4 -4
  57. package/dist/checkbox/checkbox.js.map +1 -1
  58. package/dist/chip-group/chip-group.d.ts +37 -0
  59. package/dist/chip-group/chip-group.js +148 -0
  60. package/dist/chip-group/chip-group.js.map +1 -0
  61. package/dist/chip-group/index.d.ts +2 -0
  62. package/dist/chip-group/index.js +2 -0
  63. package/dist/countdown/countdown.d.ts +45 -0
  64. package/dist/countdown/countdown.js +298 -0
  65. package/dist/countdown/countdown.js.map +1 -0
  66. package/dist/countdown/index.d.ts +2 -0
  67. package/dist/countdown/index.js +2 -0
  68. package/dist/date-picker/picker-parts.js +1 -1
  69. package/dist/date-picker/picker-parts.js.map +1 -1
  70. package/dist/dialog/dialog.js +1 -1
  71. package/dist/dialog/dialog.js.map +1 -1
  72. package/dist/donut-chart/donut-chart.d.ts +64 -0
  73. package/dist/donut-chart/donut-chart.js +576 -0
  74. package/dist/donut-chart/donut-chart.js.map +1 -0
  75. package/dist/donut-chart/index.d.ts +2 -0
  76. package/dist/donut-chart/index.js +2 -0
  77. package/dist/empty-state/empty-state.d.ts +21 -10
  78. package/dist/empty-state/empty-state.js +32 -26
  79. package/dist/empty-state/empty-state.js.map +1 -1
  80. package/dist/empty-state/index.d.ts +2 -2
  81. package/dist/empty-state/index.js +2 -2
  82. package/dist/file-drop-zone/file-drop-zone.js +1 -1
  83. package/dist/file-drop-zone/file-drop-zone.js.map +1 -1
  84. package/dist/filter-bar/filter-bar.d.ts +47 -26
  85. package/dist/filter-bar/filter-bar.js +272 -156
  86. package/dist/filter-bar/filter-bar.js.map +1 -1
  87. package/dist/gauge/gauge.d.ts +45 -0
  88. package/dist/gauge/gauge.js +219 -0
  89. package/dist/gauge/gauge.js.map +1 -0
  90. package/dist/gauge/index.d.ts +2 -0
  91. package/dist/gauge/index.js +2 -0
  92. package/dist/in-view-title/in-view-title.d.ts +33 -0
  93. package/dist/in-view-title/in-view-title.js +119 -0
  94. package/dist/in-view-title/in-view-title.js.map +1 -0
  95. package/dist/in-view-title/index.d.ts +2 -0
  96. package/dist/in-view-title/index.js +2 -0
  97. package/dist/index.d.ts +49 -6
  98. package/dist/index.js +48 -8
  99. package/dist/lib/cn.js +11 -1
  100. package/dist/lib/cn.js.map +1 -1
  101. package/dist/lib/field-message.js +28 -10
  102. package/dist/lib/field-message.js.map +1 -1
  103. package/dist/lib/field-surface.js +9 -8
  104. package/dist/lib/field-surface.js.map +1 -1
  105. package/dist/lib/flag-emoji.js +50 -0
  106. package/dist/lib/flag-emoji.js.map +1 -0
  107. package/dist/lib/flip.js +11 -3
  108. package/dist/lib/flip.js.map +1 -1
  109. package/dist/lib/menu-highlight.js +64 -0
  110. package/dist/lib/menu-highlight.js.map +1 -0
  111. package/dist/lib/motion.js +9 -2
  112. package/dist/lib/motion.js.map +1 -1
  113. package/dist/lib/rolling-value.js +1 -1
  114. package/dist/lib/rolling-value.js.map +1 -1
  115. package/dist/lib/spring-tween.js +500 -0
  116. package/dist/lib/spring-tween.js.map +1 -0
  117. package/dist/lib/swap-text.js +3 -1
  118. package/dist/lib/swap-text.js.map +1 -1
  119. package/dist/line-chart/index.d.ts +2 -0
  120. package/dist/line-chart/index.js +2 -0
  121. package/dist/line-chart/line-chart.d.ts +76 -0
  122. package/dist/line-chart/line-chart.js +611 -0
  123. package/dist/line-chart/line-chart.js.map +1 -0
  124. package/dist/mention-input/index.d.ts +2 -0
  125. package/dist/mention-input/index.js +2 -0
  126. package/dist/mention-input/mention-input.d.ts +98 -0
  127. package/dist/mention-input/mention-input.js +576 -0
  128. package/dist/mention-input/mention-input.js.map +1 -0
  129. package/dist/menu/menu.d.ts +2 -0
  130. package/dist/menu/menu.js +23 -12
  131. package/dist/menu/menu.js.map +1 -1
  132. package/dist/money-input/index.d.ts +2 -0
  133. package/dist/money-input/index.js +2 -0
  134. package/dist/money-input/money-input.d.ts +65 -0
  135. package/dist/money-input/money-input.js +607 -0
  136. package/dist/money-input/money-input.js.map +1 -0
  137. package/dist/morph-select/index.d.ts +2 -0
  138. package/dist/morph-select/index.js +2 -0
  139. package/dist/morph-select/morph-select.d.ts +71 -0
  140. package/dist/morph-select/morph-select.js +693 -0
  141. package/dist/morph-select/morph-select.js.map +1 -0
  142. package/dist/multi-select/index.d.ts +2 -0
  143. package/dist/multi-select/index.js +2 -0
  144. package/dist/multi-select/multi-select.d.ts +36 -0
  145. package/dist/multi-select/multi-select.js +152 -0
  146. package/dist/multi-select/multi-select.js.map +1 -0
  147. package/dist/number-field/index.d.ts +2 -0
  148. package/dist/number-field/index.js +2 -0
  149. package/dist/number-field/number-field.d.ts +42 -0
  150. package/dist/number-field/number-field.js +199 -0
  151. package/dist/number-field/number-field.js.map +1 -0
  152. package/dist/onboarding-checklist/index.d.ts +2 -0
  153. package/dist/onboarding-checklist/index.js +2 -0
  154. package/dist/onboarding-checklist/onboarding-checklist.d.ts +55 -0
  155. package/dist/onboarding-checklist/onboarding-checklist.js +577 -0
  156. package/dist/onboarding-checklist/onboarding-checklist.js.map +1 -0
  157. package/dist/password-field/password-field.js +2 -2
  158. package/dist/password-field/password-field.js.map +1 -1
  159. package/dist/phone-input/index.d.ts +3 -0
  160. package/dist/phone-input/index.js +3 -0
  161. package/dist/phone-input/phone-countries.d.ts +29 -0
  162. package/dist/phone-input/phone-countries.js +170 -0
  163. package/dist/phone-input/phone-countries.js.map +1 -0
  164. package/dist/phone-input/phone-input.d.ts +66 -0
  165. package/dist/phone-input/phone-input.js +440 -0
  166. package/dist/phone-input/phone-input.js.map +1 -0
  167. package/dist/popover/popover.d.ts +12 -0
  168. package/dist/popover/popover.js +31 -7
  169. package/dist/popover/popover.js.map +1 -1
  170. package/dist/progress/progress.js +1 -1
  171. package/dist/radio-group/radio-group.js +1 -1
  172. package/dist/radio-group/radio-group.js.map +1 -1
  173. package/dist/reactions/index.d.ts +2 -0
  174. package/dist/reactions/index.js +2 -0
  175. package/dist/reactions/reactions.d.ts +50 -0
  176. package/dist/reactions/reactions.js +315 -0
  177. package/dist/reactions/reactions.js.map +1 -0
  178. package/dist/rich-text-editor/index.d.ts +3 -0
  179. package/dist/rich-text-editor/index.js +2 -0
  180. package/dist/rich-text-editor/rich-text-editor.d.ts +70 -0
  181. package/dist/rich-text-editor/rich-text-editor.js +909 -0
  182. package/dist/rich-text-editor/rich-text-editor.js.map +1 -0
  183. package/dist/rich-text-editor/rich-text-engine.d.ts +16 -0
  184. package/dist/rich-text-editor/rich-text-engine.js +366 -0
  185. package/dist/rich-text-editor/rich-text-engine.js.map +1 -0
  186. package/dist/rich-text-editor/rich-text-html.js +174 -0
  187. package/dist/rich-text-editor/rich-text-html.js.map +1 -0
  188. package/dist/rich-text-editor/rich-text-icons.js +43 -0
  189. package/dist/rich-text-editor/rich-text-icons.js.map +1 -0
  190. package/dist/ridgeline/index.d.ts +2 -0
  191. package/dist/ridgeline/index.js +2 -0
  192. package/dist/ridgeline/ridgeline.d.ts +55 -0
  193. package/dist/ridgeline/ridgeline.js +701 -0
  194. package/dist/ridgeline/ridgeline.js.map +1 -0
  195. package/dist/scroll-area/index.d.ts +2 -2
  196. package/dist/scroll-area/index.js +2 -2
  197. package/dist/scroll-area/scroll-area.d.ts +68 -31
  198. package/dist/scroll-area/scroll-area.js +208 -59
  199. package/dist/scroll-area/scroll-area.js.map +1 -1
  200. package/dist/scrub-input/evaluate.d.ts +14 -0
  201. package/dist/scrub-input/evaluate.js +72 -0
  202. package/dist/scrub-input/evaluate.js.map +1 -0
  203. package/dist/scrub-input/index.d.ts +3 -0
  204. package/dist/scrub-input/index.js +3 -0
  205. package/dist/scrub-input/scrub-input.d.ts +74 -0
  206. package/dist/scrub-input/scrub-input.js +486 -0
  207. package/dist/scrub-input/scrub-input.js.map +1 -0
  208. package/dist/search-field/search-field.d.ts +2 -1
  209. package/dist/search-field/search-field.js +4 -4
  210. package/dist/search-field/search-field.js.map +1 -1
  211. package/dist/select/select.d.ts +3 -1
  212. package/dist/select/select.js +20 -8
  213. package/dist/select/select.js.map +1 -1
  214. package/dist/separator/separator.d.ts +1 -1
  215. package/dist/sheet/sheet.js +2 -1
  216. package/dist/sheet/sheet.js.map +1 -1
  217. package/dist/shortcut-recorder/index.d.ts +3 -0
  218. package/dist/shortcut-recorder/index.js +3 -0
  219. package/dist/shortcut-recorder/shortcut-model.d.ts +27 -0
  220. package/dist/shortcut-recorder/shortcut-model.js +206 -0
  221. package/dist/shortcut-recorder/shortcut-model.js.map +1 -0
  222. package/dist/shortcut-recorder/shortcut-recorder.d.ts +93 -0
  223. package/dist/shortcut-recorder/shortcut-recorder.js +489 -0
  224. package/dist/shortcut-recorder/shortcut-recorder.js.map +1 -0
  225. package/dist/slider/index.d.ts +2 -2
  226. package/dist/slider/slider.d.ts +46 -14
  227. package/dist/slider/slider.js +151 -67
  228. package/dist/slider/slider.js.map +1 -1
  229. package/dist/slope-chart/index.d.ts +2 -0
  230. package/dist/slope-chart/index.js +2 -0
  231. package/dist/slope-chart/slope-chart.d.ts +54 -0
  232. package/dist/slope-chart/slope-chart.js +568 -0
  233. package/dist/slope-chart/slope-chart.js.map +1 -0
  234. package/dist/sparkline/index.d.ts +2 -0
  235. package/dist/sparkline/index.js +2 -0
  236. package/dist/sparkline/sparkline.d.ts +46 -0
  237. package/dist/sparkline/sparkline.js +390 -0
  238. package/dist/sparkline/sparkline.js.map +1 -0
  239. package/dist/split-bar/index.d.ts +2 -0
  240. package/dist/split-bar/index.js +2 -0
  241. package/dist/split-bar/split-bar.d.ts +62 -0
  242. package/dist/split-bar/split-bar.js +513 -0
  243. package/dist/split-bar/split-bar.js.map +1 -0
  244. package/dist/split-button/split-button.d.ts +1 -1
  245. package/dist/split-button/split-button.js +12 -11
  246. package/dist/split-button/split-button.js.map +1 -1
  247. package/dist/stat-card/index.d.ts +2 -2
  248. package/dist/stat-card/index.js +2 -2
  249. package/dist/stat-card/stat-card.d.ts +24 -4
  250. package/dist/stat-card/stat-card.js +114 -17
  251. package/dist/stat-card/stat-card.js.map +1 -1
  252. package/dist/stepper/index.d.ts +2 -0
  253. package/dist/stepper/index.js +2 -0
  254. package/dist/stepper/stepper.d.ts +51 -0
  255. package/dist/stepper/stepper.js +215 -0
  256. package/dist/stepper/stepper.js.map +1 -0
  257. package/dist/streamgraph/index.d.ts +2 -0
  258. package/dist/streamgraph/index.js +2 -0
  259. package/dist/streamgraph/streamgraph.d.ts +70 -0
  260. package/dist/streamgraph/streamgraph.js +613 -0
  261. package/dist/streamgraph/streamgraph.js.map +1 -0
  262. package/dist/styles.css +1 -1
  263. package/dist/swipe-actions/index.d.ts +2 -0
  264. package/dist/swipe-actions/index.js +2 -0
  265. package/dist/swipe-actions/swipe-actions.d.ts +67 -0
  266. package/dist/swipe-actions/swipe-actions.js +552 -0
  267. package/dist/swipe-actions/swipe-actions.js.map +1 -0
  268. package/dist/switch/switch.js +2 -2
  269. package/dist/switch/switch.js.map +1 -1
  270. package/dist/table/index.d.ts +2 -2
  271. package/dist/table/index.js +2 -2
  272. package/dist/table/table.d.ts +56 -4
  273. package/dist/table/table.js +340 -165
  274. package/dist/table/table.js.map +1 -1
  275. package/dist/tabs/tabs.js +2 -2
  276. package/dist/tabs/tabs.js.map +1 -1
  277. package/dist/tag-input/index.d.ts +2 -0
  278. package/dist/tag-input/index.js +2 -0
  279. package/dist/tag-input/tag-input.d.ts +40 -0
  280. package/dist/tag-input/tag-input.js +269 -0
  281. package/dist/tag-input/tag-input.js.map +1 -0
  282. package/dist/text-morph/index.d.ts +2 -0
  283. package/dist/text-morph/index.js +2 -0
  284. package/dist/text-morph/text-morph.d.ts +23 -0
  285. package/dist/text-morph/text-morph.js +21 -0
  286. package/dist/text-morph/text-morph.js.map +1 -0
  287. package/dist/text-reveal/index.d.ts +2 -0
  288. package/dist/text-reveal/index.js +2 -0
  289. package/dist/text-reveal/text-reveal.d.ts +27 -0
  290. package/dist/text-reveal/text-reveal.js +51 -0
  291. package/dist/text-reveal/text-reveal.js.map +1 -0
  292. package/dist/text-shimmer/index.d.ts +2 -0
  293. package/dist/text-shimmer/index.js +2 -0
  294. package/dist/text-shimmer/text-shimmer.d.ts +29 -0
  295. package/dist/text-shimmer/text-shimmer.js +29 -0
  296. package/dist/text-shimmer/text-shimmer.js.map +1 -0
  297. package/dist/time-picker/time-picker.js +2 -2
  298. package/dist/time-picker/time-picker.js.map +1 -1
  299. package/dist/toast/index.d.ts +2 -2
  300. package/dist/toast/index.js +2 -2
  301. package/dist/toast/toast.d.ts +6 -0
  302. package/dist/toast/toast.js +82 -40
  303. package/dist/toast/toast.js.map +1 -1
  304. package/dist/tooltip/tooltip.d.ts +9 -2
  305. package/dist/tooltip/tooltip.js +7 -7
  306. package/dist/tooltip/tooltip.js.map +1 -1
  307. package/dist/treemap/index.d.ts +2 -0
  308. package/dist/treemap/index.js +2 -0
  309. package/dist/treemap/treemap.d.ts +53 -0
  310. package/dist/treemap/treemap.js +699 -0
  311. package/dist/treemap/treemap.js.map +1 -0
  312. package/dist/usage-meter/index.d.ts +2 -0
  313. package/dist/usage-meter/index.js +2 -0
  314. package/dist/usage-meter/usage-meter.d.ts +50 -0
  315. package/dist/usage-meter/usage-meter.js +440 -0
  316. package/dist/usage-meter/usage-meter.js.map +1 -0
  317. package/dist/waffle-chart/index.d.ts +2 -0
  318. package/dist/waffle-chart/index.js +2 -0
  319. package/dist/waffle-chart/waffle-chart.d.ts +57 -0
  320. package/dist/waffle-chart/waffle-chart.js +426 -0
  321. package/dist/waffle-chart/waffle-chart.js.map +1 -0
  322. package/package.json +165 -3
@@ -1 +1 @@
1
- {"version":3,"file":"button-group.js","names":["AriaButton","Menu","MenuItem"],"sources":["../../src/button-group/button-group.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n isValidElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n type FocusEvent,\n type KeyboardEvent,\n type PointerEvent,\n type ReactNode,\n} from \"react\";\nimport { Button as AriaButton } from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport {\n useExitBackstop,\n useIsomorphicLayoutEffect,\n useLeavingItems,\n useMorphWidth,\n prefersReducedMotion,\n} from \"../lib/motion.js\";\nimport { Menu, MenuItem, MenuTrigger } from \"../menu/menu.js\";\n\nexport type ButtonGroupVariant = \"outline\" | \"solid\";\nexport type ButtonGroupSize = \"sm\" | \"md\";\nexport type ButtonGroupOrientation = \"horizontal\" | \"vertical\";\n\nexport interface ButtonGroupItem {\n /** Stable key for the segment. */\n id: string;\n /** Visible text and the accessible name. Changing it crossfades in place, so \"Share\" can answer \"Copied\". */\n label: string;\n /** Every other label this segment can show, such as [\"Copied\"]. The segment sizes to the widest, so a label change never resizes it. */\n reserve?: string[];\n /** An icon before the label. Swapping it for another morphs; re-rendering the same icon does not. Mark it `aria-hidden`. */\n icon?: ReactNode;\n /** Shows only the icon; the label becomes the accessible name and the tooltip. */\n iconOnly?: boolean;\n /** Visible content in place of the label, such as a live value. The label stays the accessible name. */\n content?: ReactNode;\n onSelect?: () => void;\n /** Renders the segment as a link. */\n href?: string;\n isDisabled?: boolean;\n}\n\nexport interface ButtonGroupMenuItem {\n id: string;\n label: string;\n /** An icon before the label. Mark it `aria-hidden`. */\n icon?: ReactNode;\n onSelect?: () => void;\n href?: string;\n isDisabled?: boolean;\n /** Colours the item as a destructive action. */\n destructive?: boolean;\n}\n\nexport interface ButtonGroupMenu {\n /** Accessible name and tooltip of the chevron segment, such as \"More actions\". */\n label: string;\n items: ButtonGroupMenuItem[];\n}\n\nexport interface ButtonGroupProps {\n items: ButtonGroupItem[];\n /** Adds a trailing chevron segment that opens these actions in a menu aligned to the group's edge. */\n menu?: ButtonGroupMenu;\n /** Accessible name of the group, such as \"Document actions\". */\n label: string;\n /** @default \"outline\" */\n variant?: ButtonGroupVariant;\n /** @default \"md\" */\n size?: ButtonGroupSize;\n /** @default \"horizontal\" */\n orientation?: ButtonGroupOrientation;\n /** When the row does not fit its container, segments with an icon drop their label and keep the icon. Horizontal only. @default true */\n collapseLabels?: boolean;\n /** Disables every segment and the menu. */\n isDisabled?: boolean;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\n/** Key of the chevron segment. Plain text, because the HTML parser rewrites control characters in server-rendered attributes. */\nconst MENU = \"button-group-menu\";\n\n/** Names the icon element, so swapping Link for Check morphs while a re-render of the same icon stays still. */\nfunction iconKey(node: ReactNode): string {\n if (!isValidElement(node))\n return typeof node === \"string\" || typeof node === \"number\" ? String(node) : \"\";\n const type = node.type as string | { displayName?: string; name?: string };\n return typeof type === \"string\" ? type : (type?.displayName ?? type?.name ?? \"icon\");\n}\n\nconst PHASE =\n \"col-start-1 row-start-1 inline-flex items-center gap-2 whitespace-nowrap group-data-[size=sm]/bg:gap-1.5\";\n\nfunction LeavingPhase({\n id,\n onDone,\n children,\n}: {\n id: number;\n onDone: (id: number) => void;\n children: ReactNode;\n}) {\n const remove = useCallback(() => onDone(id), [onDone, id]);\n useExitBackstop(true, remove);\n return (\n <span\n aria-hidden=\"true\"\n onAnimationEnd={(event) => {\n if (event.target === event.currentTarget) remove();\n }}\n className={cn(PHASE + \" motion-safe:animate-label-out motion-reduce:animate-fade-out\")}\n >\n {children}\n </span>\n );\n}\n\n/**\n * Icon and label of one segment. Every reserved label sits invisibly in the same\n * grid cell, so the segment is as wide as its widest state and a new label\n * crossfades inside a fixed box: it rises in from a soft blur while the old one\n * lifts away. A label nobody reserved still never snaps: the slot springs to its\n * width.\n */\nfunction SegmentContent({ item }: { item: ButtonGroupItem }) {\n const contentRef = useRef<HTMLSpanElement>(null);\n const phaseRef = useRef<HTMLSpanElement>(null);\n const glyph = iconKey(item.icon);\n const key =\n glyph + \"|\" + (item.content !== undefined ? \"\\u0000content\" : item.iconOnly ? \"\" : item.label);\n useMorphWidth(contentRef, key);\n\n const collapsible = item.icon ? \"group-data-[compact]/bg:hidden\" : \"\";\n const node = (\n <>\n {item.icon ? (\n <span className=\"inline-flex flex-none [&_svg]:size-(--icon-sm)\">{item.icon}</span>\n ) : null}\n {item.content !== undefined ? (\n <span className=\"inline-flex items-center tabular-nums\">{item.content}</span>\n ) : item.iconOnly ? null : (\n <span className={cn(\"inline-block \" + collapsible)}>{item.label}</span>\n )}\n </>\n );\n const { leaving, changed, remove } = useLeavingItems(key, node, () => phaseRef.current);\n const sizers =\n item.iconOnly || item.content !== undefined\n ? []\n : [...new Set([item.label, ...(item.reserve ?? [])])];\n\n return (\n <span\n className=\"button-slot [--morph-clip:inset(-50%_calc(var(--spacing)*-3))]\"\n aria-hidden=\"true\"\n >\n <span ref={contentRef} className=\"inline-grid flex-none items-center justify-items-center\">\n {sizers.map((text) => (\n <span key={text} className={cn(PHASE + \" pointer-events-none invisible\")}>\n {item.icon ? <span className=\"inline-block size-4 flex-none\" /> : null}\n <span className={cn(\"inline-block \" + collapsible)}>{text}</span>\n </span>\n ))}\n {leaving.map((entry) => (\n <LeavingPhase key={entry.id} id={entry.id} onDone={remove}>\n {entry.value}\n </LeavingPhase>\n ))}\n <span\n key={key}\n ref={phaseRef}\n className={cn(\n PHASE + (changed ? \" motion-safe:animate-label-in motion-reduce:animate-fade-in\" : \"\"),\n )}\n >\n {node}\n </span>\n </span>\n </span>\n );\n}\n\nconst inside = (node: HTMLElement, x: number, y: number) => {\n const rect = node.getBoundingClientRect();\n return x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom;\n};\n/** Segments are found by their data-key, so the group needs no ref per segment. */\nconst segmentsIn = (root: HTMLElement | null) =>\n Array.from(root?.querySelectorAll<HTMLElement>(\":scope > [data-key]\") ?? []);\nconst segmentIn = (root: HTMLElement | null, key: string | null) =>\n key === null ? undefined : segmentsIn(root).find((node) => node.dataset.key === key);\n/** The segment's box inside the group at sub-pixel precision, undoing any scale an ancestor applies. */\nfunction boxOf(group: HTMLElement, node: HTMLElement) {\n const outer = group.getBoundingClientRect();\n const inner = node.getBoundingClientRect();\n // offsetWidth is rounded to whole pixels, so only a real transform (more than a pixel apart) counts as scale.\n const scale =\n group.offsetWidth && Math.abs(outer.width - group.offsetWidth) > 1\n ? outer.width / group.offsetWidth\n : 1;\n return [\n (inner.left - outer.left) / scale - group.clientLeft + group.scrollLeft,\n (inner.top - outer.top) / scale - group.clientTop + group.scrollTop,\n inner.width / scale,\n inner.height / scale,\n ] as const;\n}\nconst inert = (node: HTMLElement) =>\n (node as HTMLButtonElement).disabled || node.getAttribute(\"aria-disabled\") === \"true\";\n\nconst TRAVEL =\n \"translate var(--duration-snappy) var(--ease-snappy), width var(--duration-snappy) var(--ease-snappy), height var(--duration-snappy) var(--ease-snappy), opacity var(--duration-quick) var(--ease-settle)\";\nconst FADE = \"opacity var(--duration-quick) var(--ease-settle)\";\nconst FADE_INSTANT = \"opacity var(--duration-instant) var(--ease-settle)\";\n\nconst focusRing =\n \"data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring) focus-visible:outline-solid focus-visible:outline-(length:--focus-ring-width) focus-visible:outline-offset-(--focus-ring-offset) focus-visible:outline-(--color-ring)\";\n\n/**\n * Related actions joined into one surface: a shared border and radius, hairline\n * dividers, no gaps. One soft highlight glides between segments under the\n * pointer or keyboard focus, the pressed segment answers in place, and an\n * optional chevron segment opens more actions in a menu aligned to the group's\n * edge.\n *\n * Tab visits every segment in order; the arrow keys along the orientation, Home\n * and End also move between them. A segment's label can change in place (\"Share\"\n * answers \"Copied\") without resizing it, and a label that changes right after its\n * segment is pressed is announced once.\n *\n * @example\n * <ButtonGroup label=\"Document actions\" items={[{ id: \"edit\", label: \"Edit\" }, { id: \"share\", label: \"Share\", reserve: [\"Copied\"] }]} />\n */\nexport function ButtonGroup({\n items,\n menu,\n label,\n variant = \"outline\",\n size = \"md\",\n orientation = \"horizontal\",\n collapseLabels = true,\n isDisabled = false,\n className,\n}: ButtonGroupProps) {\n const root = useRef<HTMLDivElement>(null);\n const highlight = useRef<HTMLSpanElement>(null);\n const vertical = orientation === \"vertical\";\n\n // The highlight sits on the pointer's segment, else the pressed one (touch), else the open menu's chevron, else keyboard focus.\n const [hover, setHover] = useState<string | null>(null);\n const [pressed, setPressed] = useState<string | null>(null);\n const [focused, setFocused] = useState<string | null>(null);\n const [menuOpen, setMenuOpen] = useState(false);\n const active = hover ?? pressed ?? (menuOpen ? MENU : null) ?? focused;\n const keys = [...items.map((item) => item.id), ...(menu ? [MENU] : [])];\n const activeIndex = active ? keys.indexOf(active) : -1;\n const inertKey = String(isDisabled) + items.map((item) => (item.isDisabled ? 1 : 0)).join(\"\");\n\n const shown = useRef<string | null>(null);\n\n useIsomorphicLayoutEffect(() => {\n const el = highlight.current;\n const group = root.current;\n if (!el || !group) return;\n const reduced = prefersReducedMotion();\n const node = segmentIn(group, active);\n // Hovering a disabled segment shows nothing; focus on one still shows where the keyboard is.\n if (!node || (inert(node) && active !== focused)) {\n if (shown.current) {\n el.style.transition = reduced ? FADE_INSTANT : FADE;\n el.style.opacity = \"0\";\n }\n shown.current = null;\n return;\n }\n const [x, y, width, height] = boxOf(group, node);\n // Arriving from nowhere, it appears in place; only moves between segments travel.\n const arriving = !shown.current || reduced;\n el.style.transition = arriving ? (reduced ? FADE_INSTANT : FADE) : TRAVEL;\n el.style.translate = x + \"px \" + y + \"px\";\n el.style.width = width + \"px\";\n el.style.height = height + \"px\";\n el.style.opacity = \"1\";\n shown.current = active;\n // inertKey re-checks a segment that turns disabled under a still pointer.\n }, [active, inertKey, focused]);\n\n // Segments resize when a label morphs or labels collapse; the highlight stays locked to its segment.\n const keyList = keys.join(\"\\u0000\");\n useEffect(() => {\n const group = root.current;\n const el = highlight.current;\n if (!group || !el || typeof ResizeObserver === \"undefined\") return;\n // Work is deferred a frame: a callback that changes layout inside the observer's own notification reports a loop error.\n let frame = 0;\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => {\n const node = segmentIn(group, shown.current);\n if (!node) return;\n const [x, y, width, height] = boxOf(group, node);\n el.style.transition = FADE;\n el.style.translate = x + \"px \" + y + \"px\";\n el.style.width = width + \"px\";\n el.style.height = height + \"px\";\n });\n });\n observer.observe(group);\n segmentsIn(group).forEach((node) => observer.observe(node));\n return () => {\n cancelAnimationFrame(frame);\n observer.disconnect();\n };\n }, [keyList]);\n\n // Labels collapse to icons when the row overflows, and come back once the container is wide enough for the full row again.\n const [overflowing, setCompact] = useState(false);\n const fullWidth = useRef(0);\n const blockedAt = useRef(0);\n const collapsible =\n collapseLabels &&\n !vertical &&\n items.some((item) => item.icon && !item.iconOnly && item.content === undefined);\n const compact = collapsible && overflowing;\n useEffect(() => {\n const group = root.current;\n const parent = group?.parentElement;\n if (!group || !parent || !collapsible || typeof ResizeObserver === \"undefined\") return;\n const check = () => {\n const box = getComputedStyle(parent);\n const available =\n parent.clientWidth - parseFloat(box.paddingLeft) - parseFloat(box.paddingRight);\n if (group.dataset.compact === undefined) {\n if (group.scrollWidth > group.clientWidth + 1) {\n fullWidth.current = group.scrollWidth + 2;\n blockedAt.current = available;\n setCompact(true);\n }\n } else if (available > blockedAt.current && available >= fullWidth.current) setCompact(false);\n };\n // Collapsing changes the layout being observed, so the check runs a frame later (see the highlight observer).\n let frame = 0;\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(check);\n });\n observer.observe(group);\n observer.observe(parent);\n return () => {\n cancelAnimationFrame(frame);\n observer.disconnect();\n };\n }, [collapsible, keyList]);\n\n // Pressing tracks the segment under the finger, so touch gets the same highlight a pointer gets on hover.\n useEffect(() => {\n if (!pressed) return;\n const release = () => setPressed(null);\n window.addEventListener(\"pointerup\", release);\n window.addEventListener(\"pointercancel\", release);\n return () => {\n window.removeEventListener(\"pointerup\", release);\n window.removeEventListener(\"pointercancel\", release);\n };\n }, [pressed]);\n\n const hit = (event: PointerEvent) => {\n for (const node of segmentsIn(root.current))\n if (inside(node, event.clientX, event.clientY))\n return inert(node) ? null : (node.dataset.key ?? null);\n return null;\n };\n const onPointerMove = (event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType !== \"touch\") setHover(hit(event));\n };\n const onPointerLeave = () => setHover(null);\n const onPointerDown = (event: PointerEvent<HTMLDivElement>) => {\n if (event.button === 0) setPressed(hit(event));\n };\n\n // Keyboard focus shows the highlight; a mouse click focuses without it, so nothing stays lit after the pointer leaves.\n const onFocus = (event: FocusEvent<HTMLDivElement>) => {\n const target = event.target as HTMLElement;\n const key = target.dataset.key;\n if (key === undefined) return;\n let visible = true;\n try {\n visible = target.matches(\":focus-visible\");\n } catch {\n /* older engines */\n }\n setFocused(visible ? key : null);\n };\n const onBlur = (event: FocusEvent<HTMLDivElement>) => {\n if (!root.current?.contains(event.relatedTarget)) setFocused(null);\n };\n\n // Tab visits every segment in order; arrow keys along the orientation, Home and End also move between them.\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.defaultPrevented) return;\n // Disabled segments stay focusable (aria-disabled), so the keyboard can find them and hear why nothing happens.\n const list = segmentsIn(root.current).filter((node) => !(node as HTMLButtonElement).disabled);\n const index = list.findIndex((node) => node === document.activeElement);\n if (index < 0) return;\n const rtl = !vertical && getComputedStyle(root.current!).direction === \"rtl\";\n const forward = vertical ? \"ArrowDown\" : rtl ? \"ArrowLeft\" : \"ArrowRight\";\n const backward = vertical ? \"ArrowUp\" : rtl ? \"ArrowRight\" : \"ArrowLeft\";\n const last = list.length - 1;\n const target =\n event.key === forward\n ? index === last\n ? 0\n : index + 1\n : event.key === backward\n ? index === 0\n ? last\n : index - 1\n : event.key === \"Home\"\n ? 0\n : event.key === \"End\"\n ? last\n : -1;\n if (target < 0) return;\n event.preventDefault();\n list[target]!.focus();\n };\n\n // A label that changes right after its segment is pressed, such as \"Copied\", is announced once; the quiet revert is not.\n const [announcement, setAnnouncement] = useState(\"\");\n const labels = useRef<Map<string, string> | null>(null);\n const activated = useRef({ key: \"\", at: 0 });\n const labelList = items.map((item) => item.id + \"\\u0000\" + item.label).join(\"\\u0001\");\n useEffect(() => {\n const previous = labels.current;\n labels.current = new Map(items.map((item) => [item.id, item.label]));\n if (!previous) return;\n const recent = performance.now() - activated.current.at < 1000;\n const changed = items.filter(\n (item) =>\n recent &&\n item.id === activated.current.key &&\n previous.has(item.id) &&\n previous.get(item.id) !== item.label &&\n item.content === undefined,\n );\n\n if (changed.length) setAnnouncement(changed.map((item) => item.label).join(\", \"));\n // labelList carries the only part of items this effect reads.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [labelList]);\n\n const select = (item: ButtonGroupItem) => {\n activated.current = { key: item.id, at: performance.now() };\n item.onSelect?.();\n };\n const quiet = (index: number) =>\n activeIndex >= 0 && (index === activeIndex || index === activeIndex + 1) ? \"\" : undefined;\n\n const segments = items.map((item, index) => {\n const off = item.isDisabled && !isDisabled;\n const named = item.iconOnly || item.content !== undefined || (compact && !!item.icon);\n const common = {\n className: cn(\"button-group-segment \" + focusRing),\n \"data-key\": item.id,\n \"data-quiet\": quiet(index),\n \"data-icon-only\": item.iconOnly ? \"\" : undefined,\n \"data-collapsible\":\n item.icon && !item.iconOnly && item.content === undefined ? \"\" : undefined,\n \"aria-label\": named ? item.label : undefined,\n title: item.iconOnly || (compact && item.icon) ? item.label : undefined,\n };\n const body = (\n <span className=\"button-group-content\">\n <SegmentContent item={item} />\n </span>\n );\n const name = named ? null : <span className=\"sr-only\">{item.label}</span>;\n if (item.href && !off && !isDisabled)\n return (\n <a key={item.id} {...common} href={item.href} onClick={() => select(item)}>\n {body}\n {name}\n </a>\n );\n return (\n <button\n key={item.id}\n {...common}\n type=\"button\"\n disabled={isDisabled}\n aria-disabled={off || undefined}\n onClick={off ? undefined : () => select(item)}\n >\n {body}\n {name}\n </button>\n );\n });\n\n return (\n <div\n ref={root}\n role=\"group\"\n aria-label={label}\n aria-disabled={isDisabled || undefined}\n data-orientation={orientation}\n data-variant={variant}\n data-size={size}\n data-compact={compact ? \"\" : undefined}\n onPointerMove={onPointerMove}\n onPointerLeave={onPointerLeave}\n onPointerDown={onPointerDown}\n onFocus={onFocus}\n onBlur={onBlur}\n onKeyDown={onKeyDown}\n className={cn(\"button-group group/bg\", className)}\n >\n <span\n ref={highlight}\n aria-hidden=\"true\"\n data-pressed={pressed && pressed === active ? \"\" : undefined}\n className=\"button-group-highlight\"\n />\n {segments}\n {menu ? (\n <MenuTrigger onOpenChange={setMenuOpen}>\n <AriaButton\n data-key={MENU}\n data-trigger=\"\"\n data-quiet={quiet(items.length)}\n aria-label={menu.label}\n isDisabled={isDisabled}\n className={cn(\"button-group-segment group/trigger \" + focusRing)}\n >\n <span className=\"button-group-content\">\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.75}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n className=\"size-4 motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-aria-expanded/trigger:rotate-180\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </span>\n </AriaButton>\n <Menu\n placement={vertical ? \"bottom start\" : \"bottom end\"}\n offset={6}\n shouldFocusWrap\n className={cn(\"menu-motion \" + (vertical ? \"[--menu-ox:left]\" : \"[--menu-ox:right]\"))}\n >\n {menu.items.map((action, index) => (\n <MenuItem\n key={action.id}\n id={action.id}\n textValue={action.label}\n href={action.href}\n isDisabled={action.isDisabled}\n onAction={action.onSelect}\n data-i={Math.min(index, 4)}\n className={cn(\n \"menu-item-motion gap-2.5\" +\n (action.destructive\n ? \" text-destructive-strong data-[focused]:bg-[color-mix(in_oklab,var(--color-destructive-strong)_8%,var(--color-card))]\"\n : \"\"),\n )}\n >\n {action.icon ? <span aria-hidden=\"true\">{action.icon}</span> : null}\n {action.label}\n </MenuItem>\n ))}\n </Menu>\n </MenuTrigger>\n ) : null}\n <span className=\"sr-only\" aria-live=\"polite\">\n {announcement}\n </span>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"button-group.js","names":["AriaButton","Menu","MenuItem"],"sources":["../../src/button-group/button-group.tsx"],"sourcesContent":["\"use client\";\n\nimport {\n isValidElement,\n useCallback,\n useEffect,\n useRef,\n useState,\n type FocusEvent,\n type KeyboardEvent,\n type PointerEvent,\n type ReactNode,\n} from \"react\";\nimport { Button as AriaButton } from \"react-aria-components\";\n\nimport { cn } from \"../lib/cn.js\";\nimport {\n useExitBackstop,\n useIsomorphicLayoutEffect,\n useLeavingItems,\n useMorphWidth,\n prefersReducedMotion,\n} from \"../lib/motion.js\";\nimport { Menu, MenuItem, MenuTrigger } from \"../menu/menu.js\";\n\nexport type ButtonGroupVariant = \"outline\" | \"solid\";\nexport type ButtonGroupSize = \"sm\" | \"md\";\nexport type ButtonGroupOrientation = \"horizontal\" | \"vertical\";\n\nexport interface ButtonGroupItem {\n /** Stable key for the segment. */\n id: string;\n /** Visible text and the accessible name. Changing it crossfades in place, so \"Share\" can answer \"Copied\". */\n label: string;\n /** Every other label this segment can show, such as [\"Copied\"]. The segment sizes to the widest, so a label change never resizes it. */\n reserve?: string[];\n /** An icon before the label. Swapping it for another morphs; re-rendering the same icon does not. Mark it `aria-hidden`. */\n icon?: ReactNode;\n /** Shows only the icon; the label becomes the accessible name and the tooltip. */\n iconOnly?: boolean;\n /** Visible content in place of the label, such as a live value. The label stays the accessible name. */\n content?: ReactNode;\n onSelect?: () => void;\n /** Renders the segment as a link. */\n href?: string;\n isDisabled?: boolean;\n}\n\nexport interface ButtonGroupMenuItem {\n id: string;\n label: string;\n /** An icon before the label. Mark it `aria-hidden`. */\n icon?: ReactNode;\n onSelect?: () => void;\n href?: string;\n isDisabled?: boolean;\n /** Colours the item as a destructive action. */\n destructive?: boolean;\n}\n\nexport interface ButtonGroupMenu {\n /** Accessible name and tooltip of the chevron segment, such as \"More actions\". */\n label: string;\n items: ButtonGroupMenuItem[];\n}\n\nexport interface ButtonGroupProps {\n items: ButtonGroupItem[];\n /** Adds a trailing chevron segment that opens these actions in a menu aligned to the group's edge. */\n menu?: ButtonGroupMenu;\n /** Accessible name of the group, such as \"Document actions\". */\n label: string;\n /** @default \"outline\" */\n variant?: ButtonGroupVariant;\n /** @default \"md\" */\n size?: ButtonGroupSize;\n /** @default \"horizontal\" */\n orientation?: ButtonGroupOrientation;\n /** When the row does not fit its container, segments with an icon drop their label and keep the icon. Horizontal only. @default true */\n collapseLabels?: boolean;\n /** Disables every segment and the menu. */\n isDisabled?: boolean;\n /** Extra classes. Merged last, so they win conflicts with ours. */\n className?: string;\n}\n\n/** Key of the chevron segment. Plain text, because the HTML parser rewrites control characters in server-rendered attributes. */\nconst MENU = \"button-group-menu\";\n\n/** Names the icon element, so swapping Link for Check morphs while a re-render of the same icon stays still. */\nfunction iconKey(node: ReactNode): string {\n if (!isValidElement(node))\n return typeof node === \"string\" || typeof node === \"number\" ? String(node) : \"\";\n const type = node.type as string | { displayName?: string; name?: string };\n return typeof type === \"string\" ? type : (type?.displayName ?? type?.name ?? \"icon\");\n}\n\nconst PHASE =\n \"col-start-1 row-start-1 inline-flex items-center gap-2 whitespace-nowrap group-data-[size=sm]/bg:gap-1.5\";\n\nfunction LeavingPhase({\n id,\n onDone,\n children,\n}: {\n id: number;\n onDone: (id: number) => void;\n children: ReactNode;\n}) {\n const remove = useCallback(() => onDone(id), [onDone, id]);\n useExitBackstop(true, remove);\n return (\n <span\n aria-hidden=\"true\"\n onAnimationEnd={(event) => {\n if (event.target === event.currentTarget) remove();\n }}\n className={cn(PHASE + \" motion-safe:animate-label-out motion-reduce:animate-fade-out\")}\n >\n {children}\n </span>\n );\n}\n\n/**\n * Icon and label of one segment. Every reserved label sits invisibly in the same\n * grid cell, so the segment is as wide as its widest state and a new label\n * crossfades inside a fixed box: it rises in from a soft blur while the old one\n * lifts away. A label nobody reserved still never snaps: the slot springs to its\n * width.\n */\nfunction SegmentContent({ item }: { item: ButtonGroupItem }) {\n const contentRef = useRef<HTMLSpanElement>(null);\n const phaseRef = useRef<HTMLSpanElement>(null);\n const glyph = iconKey(item.icon);\n const key =\n glyph + \"|\" + (item.content !== undefined ? \"\\u0000content\" : item.iconOnly ? \"\" : item.label);\n useMorphWidth(contentRef, key);\n\n const collapsible = item.icon ? \"group-data-[compact]/bg:hidden\" : \"\";\n const node = (\n <>\n {item.icon ? (\n <span className=\"inline-flex flex-none [&_svg]:size-(--icon-sm)\">{item.icon}</span>\n ) : null}\n {item.content !== undefined ? (\n <span className=\"inline-flex items-center tabular-nums\">{item.content}</span>\n ) : item.iconOnly ? null : (\n <span className={cn(\"inline-block \" + collapsible)}>{item.label}</span>\n )}\n </>\n );\n const { leaving, changed, remove } = useLeavingItems(key, node, () => phaseRef.current);\n const sizers =\n item.iconOnly || item.content !== undefined\n ? []\n : [...new Set([item.label, ...(item.reserve ?? [])])];\n\n return (\n <span\n className=\"button-slot [--morph-clip:inset(-50%_calc(var(--spacing)*-3))]\"\n aria-hidden=\"true\"\n >\n <span ref={contentRef} className=\"inline-grid flex-none items-center justify-items-center\">\n {sizers.map((text) => (\n <span key={text} className={cn(PHASE + \" pointer-events-none invisible\")}>\n {item.icon ? <span className=\"inline-block size-4 flex-none\" /> : null}\n <span className={cn(\"inline-block \" + collapsible)}>{text}</span>\n </span>\n ))}\n {leaving.map((entry) => (\n <LeavingPhase key={entry.id} id={entry.id} onDone={remove}>\n {entry.value}\n </LeavingPhase>\n ))}\n <span\n key={key}\n ref={phaseRef}\n className={cn(\n PHASE + (changed ? \" motion-safe:animate-label-in motion-reduce:animate-fade-in\" : \"\"),\n )}\n >\n {node}\n </span>\n </span>\n </span>\n );\n}\n\nconst inside = (node: HTMLElement, x: number, y: number) => {\n const rect = node.getBoundingClientRect();\n return x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom;\n};\n/** Segments are found by their data-key, so the group needs no ref per segment. */\nconst segmentsIn = (root: HTMLElement | null) =>\n Array.from(root?.querySelectorAll<HTMLElement>(\":scope > [data-key]\") ?? []);\nconst segmentIn = (root: HTMLElement | null, key: string | null) =>\n key === null ? undefined : segmentsIn(root).find((node) => node.dataset.key === key);\n/** The segment's box inside the group at sub-pixel precision, undoing any scale an ancestor applies. */\nfunction boxOf(group: HTMLElement, node: HTMLElement) {\n const outer = group.getBoundingClientRect();\n const inner = node.getBoundingClientRect();\n // offsetWidth is rounded to whole pixels, so only a real transform (more than a pixel apart) counts as scale.\n const scale =\n group.offsetWidth && Math.abs(outer.width - group.offsetWidth) > 1\n ? outer.width / group.offsetWidth\n : 1;\n return [\n (inner.left - outer.left) / scale - group.clientLeft + group.scrollLeft,\n (inner.top - outer.top) / scale - group.clientTop + group.scrollTop,\n inner.width / scale,\n inner.height / scale,\n ] as const;\n}\nconst inert = (node: HTMLElement) =>\n (node as HTMLButtonElement).disabled || node.getAttribute(\"aria-disabled\") === \"true\";\n\nconst TRAVEL =\n \"translate var(--duration-snappy) var(--ease-snappy), width var(--duration-snappy) var(--ease-snappy), height var(--duration-snappy) var(--ease-snappy), opacity var(--duration-quick) var(--ease-settle)\";\nconst FADE = \"opacity var(--duration-quick) var(--ease-settle)\";\nconst FADE_INSTANT = \"opacity var(--duration-instant) var(--ease-settle)\";\n\nconst focusRing =\n \"data-[focus-visible]:outline-solid data-[focus-visible]:outline-(length:--focus-ring-width) data-[focus-visible]:outline-offset-(--focus-ring-offset) data-[focus-visible]:outline-(--color-ring) focus-visible:outline-solid focus-visible:outline-(length:--focus-ring-width) focus-visible:outline-offset-(--focus-ring-offset) focus-visible:outline-(--color-ring)\";\n\n/**\n * Related actions joined into one surface: a shared border and radius, hairline\n * dividers, no gaps. One soft highlight glides between segments under the\n * pointer or keyboard focus, the pressed segment answers in place, and an\n * optional chevron segment opens more actions in a menu aligned to the group's\n * edge.\n *\n * Tab visits every segment in order; the arrow keys along the orientation, Home\n * and End also move between them. A segment's label can change in place (\"Share\"\n * answers \"Copied\") without resizing it, and a label that changes right after its\n * segment is pressed is announced once.\n *\n * @example\n * <ButtonGroup label=\"Document actions\" items={[{ id: \"edit\", label: \"Edit\" }, { id: \"share\", label: \"Share\", reserve: [\"Copied\"] }]} />\n */\nexport function ButtonGroup({\n items,\n menu,\n label,\n variant = \"outline\",\n size = \"md\",\n orientation = \"horizontal\",\n collapseLabels = true,\n isDisabled = false,\n className,\n}: ButtonGroupProps) {\n const root = useRef<HTMLDivElement>(null);\n const highlight = useRef<HTMLSpanElement>(null);\n const vertical = orientation === \"vertical\";\n\n // The highlight sits on the pointer's segment, else the pressed one (touch), else the open menu's chevron, else keyboard focus.\n const [hover, setHover] = useState<string | null>(null);\n const [pressed, setPressed] = useState<string | null>(null);\n const [focused, setFocused] = useState<string | null>(null);\n const [menuOpen, setMenuOpen] = useState(false);\n const active = hover ?? pressed ?? (menuOpen ? MENU : null) ?? focused;\n const keys = [...items.map((item) => item.id), ...(menu ? [MENU] : [])];\n const activeIndex = active ? keys.indexOf(active) : -1;\n const inertKey = String(isDisabled) + items.map((item) => (item.isDisabled ? 1 : 0)).join(\"\");\n\n const shown = useRef<string | null>(null);\n\n useIsomorphicLayoutEffect(() => {\n const el = highlight.current;\n const group = root.current;\n if (!el || !group) return;\n const reduced = prefersReducedMotion();\n const node = segmentIn(group, active);\n // Hovering a disabled segment shows nothing; focus on one still shows where the keyboard is.\n if (!node || (inert(node) && active !== focused)) {\n if (shown.current) {\n el.style.transition = reduced ? FADE_INSTANT : FADE;\n el.style.opacity = \"0\";\n }\n shown.current = null;\n return;\n }\n const [x, y, width, height] = boxOf(group, node);\n // Arriving from nowhere, it appears in place; only moves between segments travel.\n const arriving = !shown.current || reduced;\n el.style.transition = arriving ? (reduced ? FADE_INSTANT : FADE) : TRAVEL;\n el.style.translate = x + \"px \" + y + \"px\";\n el.style.width = width + \"px\";\n el.style.height = height + \"px\";\n el.style.opacity = \"1\";\n shown.current = active;\n // inertKey re-checks a segment that turns disabled under a still pointer.\n }, [active, inertKey, focused]);\n\n // Segments resize when a label morphs or labels collapse; the highlight stays locked to its segment.\n const keyList = keys.join(\"\\u0000\");\n useEffect(() => {\n const group = root.current;\n const el = highlight.current;\n if (!group || !el || typeof ResizeObserver === \"undefined\") return;\n // Work is deferred a frame: a callback that changes layout inside the observer's own notification reports a loop error.\n let frame = 0;\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(() => {\n const node = segmentIn(group, shown.current);\n if (!node) return;\n const [x, y, width, height] = boxOf(group, node);\n el.style.transition = FADE;\n el.style.translate = x + \"px \" + y + \"px\";\n el.style.width = width + \"px\";\n el.style.height = height + \"px\";\n });\n });\n observer.observe(group);\n segmentsIn(group).forEach((node) => observer.observe(node));\n return () => {\n cancelAnimationFrame(frame);\n observer.disconnect();\n };\n }, [keyList]);\n\n // Labels collapse to icons when the row overflows, and come back once the container is wide enough for the full row again.\n const [overflowing, setCompact] = useState(false);\n const fullWidth = useRef(0);\n const blockedAt = useRef(0);\n const collapsible =\n collapseLabels &&\n !vertical &&\n items.some((item) => item.icon && !item.iconOnly && item.content === undefined);\n const compact = collapsible && overflowing;\n useEffect(() => {\n const group = root.current;\n const parent = group?.parentElement;\n if (!group || !parent || !collapsible || typeof ResizeObserver === \"undefined\") return;\n const check = () => {\n const box = getComputedStyle(parent);\n const available =\n parent.clientWidth - parseFloat(box.paddingLeft) - parseFloat(box.paddingRight);\n if (group.dataset.compact === undefined) {\n if (group.scrollWidth > group.clientWidth + 1) {\n fullWidth.current = group.scrollWidth + 2;\n blockedAt.current = available;\n setCompact(true);\n }\n } else if (available > blockedAt.current && available >= fullWidth.current) setCompact(false);\n };\n // Collapsing changes the layout being observed, so the check runs a frame later (see the highlight observer).\n let frame = 0;\n const observer = new ResizeObserver(() => {\n cancelAnimationFrame(frame);\n frame = requestAnimationFrame(check);\n });\n observer.observe(group);\n observer.observe(parent);\n return () => {\n cancelAnimationFrame(frame);\n observer.disconnect();\n };\n }, [collapsible, keyList]);\n\n // Pressing tracks the segment under the finger, so touch gets the same highlight a pointer gets on hover.\n useEffect(() => {\n if (!pressed) return;\n const release = () => setPressed(null);\n window.addEventListener(\"pointerup\", release);\n window.addEventListener(\"pointercancel\", release);\n return () => {\n window.removeEventListener(\"pointerup\", release);\n window.removeEventListener(\"pointercancel\", release);\n };\n }, [pressed]);\n\n const hit = (event: PointerEvent) => {\n for (const node of segmentsIn(root.current))\n if (inside(node, event.clientX, event.clientY))\n return inert(node) ? null : (node.dataset.key ?? null);\n return null;\n };\n const onPointerMove = (event: PointerEvent<HTMLDivElement>) => {\n if (event.pointerType !== \"touch\") setHover(hit(event));\n };\n const onPointerLeave = () => setHover(null);\n const onPointerDown = (event: PointerEvent<HTMLDivElement>) => {\n if (event.button === 0) setPressed(hit(event));\n };\n\n // Keyboard focus shows the highlight; a mouse click focuses without it, so nothing stays lit after the pointer leaves.\n const onFocus = (event: FocusEvent<HTMLDivElement>) => {\n const target = event.target as HTMLElement;\n const key = target.dataset.key;\n if (key === undefined) return;\n let visible = true;\n try {\n visible = target.matches(\":focus-visible\");\n } catch {\n /* older engines */\n }\n setFocused(visible ? key : null);\n };\n const onBlur = (event: FocusEvent<HTMLDivElement>) => {\n if (!root.current?.contains(event.relatedTarget)) setFocused(null);\n };\n\n // Tab visits every segment in order; arrow keys along the orientation, Home and End also move between them.\n const onKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {\n if (event.defaultPrevented) return;\n // Disabled segments stay focusable (aria-disabled), so the keyboard can find them and hear why nothing happens.\n const list = segmentsIn(root.current).filter((node) => !(node as HTMLButtonElement).disabled);\n const index = list.findIndex((node) => node === document.activeElement);\n if (index < 0) return;\n const rtl = !vertical && getComputedStyle(root.current!).direction === \"rtl\";\n const forward = vertical ? \"ArrowDown\" : rtl ? \"ArrowLeft\" : \"ArrowRight\";\n const backward = vertical ? \"ArrowUp\" : rtl ? \"ArrowRight\" : \"ArrowLeft\";\n const last = list.length - 1;\n const target =\n event.key === forward\n ? index === last\n ? 0\n : index + 1\n : event.key === backward\n ? index === 0\n ? last\n : index - 1\n : event.key === \"Home\"\n ? 0\n : event.key === \"End\"\n ? last\n : -1;\n if (target < 0) return;\n event.preventDefault();\n list[target]!.focus();\n };\n\n // A label that changes right after its segment is pressed, such as \"Copied\", is announced once; the quiet revert is not.\n const [announcement, setAnnouncement] = useState(\"\");\n const labels = useRef<Map<string, string> | null>(null);\n const activated = useRef({ key: \"\", at: 0 });\n const labelList = items.map((item) => item.id + \"\\u0000\" + item.label).join(\"\\u0001\");\n useEffect(() => {\n const previous = labels.current;\n labels.current = new Map(items.map((item) => [item.id, item.label]));\n if (!previous) return;\n const recent = performance.now() - activated.current.at < 1000;\n const changed = items.filter(\n (item) =>\n recent &&\n item.id === activated.current.key &&\n previous.has(item.id) &&\n previous.get(item.id) !== item.label &&\n item.content === undefined,\n );\n\n if (changed.length) setAnnouncement(changed.map((item) => item.label).join(\", \"));\n // labelList carries the only part of items this effect reads.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [labelList]);\n\n const select = (item: ButtonGroupItem) => {\n activated.current = { key: item.id, at: performance.now() };\n item.onSelect?.();\n };\n const quiet = (index: number) =>\n activeIndex >= 0 && (index === activeIndex || index === activeIndex + 1) ? \"\" : undefined;\n\n const segments = items.map((item, index) => {\n const off = item.isDisabled && !isDisabled;\n const named = item.iconOnly || item.content !== undefined || (compact && !!item.icon);\n const common = {\n className: cn(\"button-group-segment \" + focusRing),\n \"data-key\": item.id,\n \"data-quiet\": quiet(index),\n \"data-icon-only\": item.iconOnly ? \"\" : undefined,\n \"data-collapsible\":\n item.icon && !item.iconOnly && item.content === undefined ? \"\" : undefined,\n \"aria-label\": named ? item.label : undefined,\n title: item.iconOnly || (compact && item.icon) ? item.label : undefined,\n };\n const body = (\n <span className=\"button-group-content\">\n <SegmentContent item={item} />\n </span>\n );\n const name = named ? null : <span className=\"sr-only\">{item.label}</span>;\n if (item.href && !off && !isDisabled)\n return (\n <a key={item.id} {...common} href={item.href} onClick={() => select(item)}>\n {body}\n {name}\n </a>\n );\n return (\n <button\n key={item.id}\n {...common}\n type=\"button\"\n disabled={isDisabled}\n aria-disabled={off || undefined}\n onClick={off ? undefined : () => select(item)}\n >\n {body}\n {name}\n </button>\n );\n });\n\n return (\n <div\n ref={root}\n role=\"group\"\n aria-label={label}\n aria-disabled={isDisabled || undefined}\n data-orientation={orientation}\n data-variant={variant}\n data-size={size}\n data-compact={compact ? \"\" : undefined}\n onPointerMove={onPointerMove}\n onPointerLeave={onPointerLeave}\n onPointerDown={onPointerDown}\n onFocus={onFocus}\n onBlur={onBlur}\n onKeyDown={onKeyDown}\n className={cn(\"button-group group/bg\", className)}\n >\n <span\n ref={highlight}\n aria-hidden=\"true\"\n data-pressed={pressed && pressed === active ? \"\" : undefined}\n className=\"button-group-highlight\"\n />\n {segments}\n {menu ? (\n <MenuTrigger onOpenChange={setMenuOpen}>\n <AriaButton\n data-key={MENU}\n data-trigger=\"\"\n data-quiet={quiet(items.length)}\n aria-label={menu.label}\n isDisabled={isDisabled}\n className={cn(\"button-group-segment group/trigger \" + focusRing)}\n >\n <span className=\"button-group-content\">\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.75}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n className=\"size-4 motion-safe:transition-[rotate] motion-safe:duration-(--duration-gentle) motion-safe:ease-gentle group-aria-expanded/trigger:rotate-180\"\n >\n <path d=\"m6 9 6 6 6-6\" />\n </svg>\n </span>\n </AriaButton>\n <Menu placement={vertical ? \"bottom start\" : \"bottom end\"} offset={6} shouldFocusWrap>\n {menu.items.map((action) => (\n <MenuItem\n key={action.id}\n id={action.id}\n textValue={action.label}\n href={action.href}\n isDisabled={action.isDisabled}\n onAction={action.onSelect}\n destructive={action.destructive}\n >\n {action.icon ? (\n <span\n aria-hidden=\"true\"\n className={action.destructive ? \"inline-flex\" : \"inline-flex text-text-secondary\"}\n >\n {action.icon}\n </span>\n ) : null}\n {action.label}\n </MenuItem>\n ))}\n </Menu>\n </MenuTrigger>\n ) : null}\n <span className=\"sr-only\" aria-live=\"polite\">\n {announcement}\n </span>\n </div>\n );\n}\n"],"mappings":"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"}
package/dist/card/card.js CHANGED
@@ -34,7 +34,7 @@ import { jsx } from "react/jsx-runtime";
34
34
  */
35
35
  const cardVariants = cva([
36
36
  "flex flex-col gap-6",
37
- "rounded-xl border-(length:--border-width) border-border py-6",
37
+ "rounded-panel border-(length:--border-width) border-border py-6",
38
38
  "bg-card text-card-foreground shadow-surface",
39
39
  "box-border font-sans"
40
40
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"card.js","names":[],"sources":["../../src/card/card.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, useState, type ComponentPropsWithoutRef } from \"react\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\n\n/**\n * Card, ported from the legacy shadcn component.\n *\n * Presentational, so there is no React Aria base -- React Aria ships no card,\n * and a container that is not focusable, selectable or interactive needs no\n * behaviour to inherit.\n *\n * Five parts, from six. The census (24 consumer files, the most-used\n * container in the app) reads: Card 55, CardContent 40, CardHeader 21,\n * CardTitle 19, CardDescription 8, CardFooter 2, **CardAction 0**.\n *\n * Dropping `CardAction` takes three things with it, which is most of the\n * legacy header's complexity: the `grid`, the `grid-rows-[auto_auto]`, and\n * the `has-data-[slot=card-action]:grid-cols-[1fr_auto]` rule that existed\n * only to reserve a column for it. With no action to place, a header is a\n * stack.\n *\n * Two more legacy rules are gone. `[.border-b]:pb-6` and `[.border-t]:pt-6`\n * added padding when the CONSUMER happened to add a border class -- clever,\n * but action at a distance through a class name we do not own, and the census\n * found zero application call sites relying on it (one call site passes\n * `border-b-0`, which removes a border it never had). `@container/card-header`\n * named a container nothing queried.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted.\n */\nexport const cardVariants = cva([\n \"flex flex-col gap-6\",\n \"rounded-xl border-(length:--border-width) border-border py-6\",\n \"bg-card text-card-foreground shadow-surface\",\n\n // box-border and font-sans are not boilerplate. The reset in\n // @seekalfred/styles is OPT-IN, so a consumer may load none: content-box\n // would add the padding to any width they set (the Alert shipped\n // overflowing its container by 46px), and an unstyled document renders\n // text in a serif (the Accordion shipped its panel that way). Declaring\n // the family HERE rather than on each part means one declaration covers\n // the whole subtree by inheritance.\n \"box-border font-sans\",\n]);\n\n/**\n * Padding is inline-only on the sections and block-only on the shell.\n *\n * That split is load-bearing rather than stylistic: it is what lets a\n * consumer run a divider or a full-bleed image edge to edge, which a single\n * `p-6` on the shell would make impossible.\n */\nexport const cardHeaderVariants = cva(\"flex flex-col gap-1.5 px-6\");\n\nexport const cardTitleVariants = cva([\n // leading-* AFTER text-*: tailwind-merge treats a later text-* as owning\n // line-height and silently strips a leading-* that precedes it.\n \"text-base leading-(--leading-none) font-semibold\",\n // The user-agent <h3> ships a 1em block margin and font-size 1.17em. The\n // legacy component was a <div> and never met either.\n \"m-0\",\n]);\n\nexport const cardDescriptionVariants = cva([\n \"text-sm leading-(--leading-normal) text-muted-foreground\",\n]);\n\nexport const cardContentVariants = cva(\"px-6\");\n\nexport const cardFooterVariants = cva(\"flex items-center px-6\");\n\n/**\n * A content container.\n *\n * @example\n * <Card>\n * <CardHeader>\n * <CardTitle>Learning Progress</CardTitle>\n * <CardDescription>Across all active courses.</CardDescription>\n * </CardHeader>\n * <CardContent>Twelve of eighteen modules complete.</CardContent>\n * </Card>\n */\nexport interface CardProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * Make the card respond to a pointer: it floats 2px on a spring while it is pointed\n * at, its border darkens and its shadow deepens. Mouse and pen only; touch never lifts.\n * Leave it off for a card that is not a target: a static card should not move.\n */\n interactive?: boolean;\n}\n\nexport const Card = forwardRef<HTMLDivElement, CardProps>(function Card(\n { className, interactive = false, onPointerEnter, onPointerLeave, ...props },\n ref,\n) {\n // Pointed at by a mouse or pen. Touch never lifts, so it is read from the pointer, not :hover (which a tap leaves stuck).\n const [pointed, setPointed] = useState(false);\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card\"\n data-interactive={interactive || undefined}\n data-pointed={(interactive && pointed) || undefined}\n onPointerEnter={(event) => {\n onPointerEnter?.(event);\n if (interactive && event.pointerType !== \"touch\") setPointed(true);\n }}\n onPointerLeave={(event) => {\n onPointerLeave?.(event);\n setPointed(false);\n }}\n className={cnAll(cardVariants(), interactive && \"card-lift\", className)}\n />\n );\n});\n\n/** Title and description, stacked. */\nexport type CardHeaderProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(function CardHeader(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-header\"\n className={cn(cardHeaderVariants(), className)}\n />\n );\n});\n\n/**\n * The card's heading.\n *\n * A REAL heading, unlike the legacy `<div class=\"font-semibold\">`. None of\n * the 19 legacy call sites wrapped one, so 19 card titles were invisible to\n * screen-reader heading navigation -- the same shape of finding as the Avatar\n * port's missing `alt`, and the migration is when it gets answered.\n */\nexport interface CardTitleProps extends ComponentPropsWithoutRef<\"h3\"> {\n /**\n * Heading level.\n *\n * `3` by default, because a card almost always sits under a section\n * heading. It is a prop rather than a constant because the correct level\n * is a fact about the PAGE, and a fixed one would be wrong somewhere; the\n * level changes semantics only, never the rendered size.\n *\n * @default 3\n */\n level?: 1 | 2 | 3 | 4 | 5 | 6;\n}\n\nexport const CardTitle = forwardRef<HTMLHeadingElement, CardTitleProps>(function CardTitle(\n { className, level = 3, ...props },\n ref,\n) {\n // Static union, not a template literal: `h${level}` is a string React\n // accepts but TypeScript cannot narrow to an intrinsic element, and the\n // cast that silences it would also accept `h7`.\n const Heading = ({ 1: \"h1\", 2: \"h2\", 3: \"h3\", 4: \"h4\", 5: \"h5\", 6: \"h6\" } as const)[level];\n\n return (\n <Heading\n ref={ref}\n {...props}\n data-slot=\"card-title\"\n className={cn(cardTitleVariants(), className)}\n />\n );\n});\n\n/** Secondary text under the title. */\nexport type CardDescriptionProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardDescription = forwardRef<HTMLDivElement, CardDescriptionProps>(\n function CardDescription({ className, ...props }, ref) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-description\"\n className={cn(cardDescriptionVariants(), className)}\n />\n );\n },\n);\n\n/** The card's body. */\nexport type CardContentProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardContent = forwardRef<HTMLDivElement, CardContentProps>(function CardContent(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-content\"\n className={cn(cardContentVariants(), className)}\n />\n );\n});\n\n/** Actions or metadata at the foot of the card. */\nexport type CardFooterProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(function CardFooter(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-footer\"\n className={cn(cardFooterVariants(), className)}\n />\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAa,eAAe,IAAI;CAC9B;CACA;CACA;CASA;AACF,CAAC;;;;;;;;AASD,MAAa,qBAAqB,IAAI,4BAA4B;AAElE,MAAa,oBAAoB,IAAI,CAGnC,oDAGA,KACF,CAAC;AAED,MAAa,0BAA0B,IAAI,CACzC,0DACF,CAAC;AAED,MAAa,sBAAsB,IAAI,MAAM;AAE7C,MAAa,qBAAqB,IAAI,wBAAwB;AAuB9D,MAAa,OAAO,WAAsC,SAAS,KACjE,EAAE,WAAW,cAAc,OAAO,gBAAgB,gBAAgB,GAAG,SACrE,KACA;CAEA,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,oBAAkB,eAAe,KAAA;EACjC,gBAAe,eAAe,WAAY,KAAA;EAC1C,iBAAiB,UAAU;GACzB,iBAAiB,KAAK;GACtB,IAAI,eAAe,MAAM,gBAAgB,SAAS,WAAW,IAAI;EACnE;EACA,iBAAiB,UAAU;GACzB,iBAAiB,KAAK;GACtB,WAAW,KAAK;EAClB;EACA,WAAW,MAAM,aAAa,GAAG,eAAe,aAAa,SAAS;CACvE,CAAA;AAEL,CAAC;AAKD,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL,CAAC;AAwBD,MAAa,YAAY,WAA+C,SAAS,UAC/E,EAAE,WAAW,QAAQ,GAAG,GAAG,SAC3B,KACA;CAIA,MAAM,UAAW;EAAE,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;CAAK,EAAY;CAEpF,OACE,oBAAC,SAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,kBAAkB,GAAG,SAAS;CAC7C,CAAA;AAEL,CAAC;AAKD,MAAa,kBAAkB,WAC7B,SAAS,gBAAgB,EAAE,WAAW,GAAG,SAAS,KAAK;CACrD,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,wBAAwB,GAAG,SAAS;CACnD,CAAA;AAEL,CACF;AAKA,MAAa,cAAc,WAA6C,SAAS,YAC/E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,oBAAoB,GAAG,SAAS;CAC/C,CAAA;AAEL,CAAC;AAKD,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL,CAAC"}
1
+ {"version":3,"file":"card.js","names":[],"sources":["../../src/card/card.tsx"],"sourcesContent":["\"use client\";\n\nimport { cva } from \"class-variance-authority\";\nimport { forwardRef, useState, type ComponentPropsWithoutRef } from \"react\";\n\nimport { cn, cnAll } from \"../lib/cn.js\";\n\n/**\n * Card, ported from the legacy shadcn component.\n *\n * Presentational, so there is no React Aria base -- React Aria ships no card,\n * and a container that is not focusable, selectable or interactive needs no\n * behaviour to inherit.\n *\n * Five parts, from six. The census (24 consumer files, the most-used\n * container in the app) reads: Card 55, CardContent 40, CardHeader 21,\n * CardTitle 19, CardDescription 8, CardFooter 2, **CardAction 0**.\n *\n * Dropping `CardAction` takes three things with it, which is most of the\n * legacy header's complexity: the `grid`, the `grid-rows-[auto_auto]`, and\n * the `has-data-[slot=card-action]:grid-cols-[1fr_auto]` rule that existed\n * only to reserve a column for it. With no action to place, a header is a\n * stack.\n *\n * Two more legacy rules are gone. `[.border-b]:pb-6` and `[.border-t]:pt-6`\n * added padding when the CONSUMER happened to add a border class -- clever,\n * but action at a distance through a class name we do not own, and the census\n * found zero application call sites relying on it (one call site passes\n * `border-b-0`, which removes a border it never had). `@container/card-header`\n * named a container nothing queried.\n *\n * Every string in the cva blocks below is a STATIC literal. Tailwind scans\n * source text, so a class assembled at runtime is invisible to it and would\n * simply never be emitted.\n */\nexport const cardVariants = cva([\n \"flex flex-col gap-6\",\n \"rounded-panel border-(length:--border-width) border-border py-6\",\n \"bg-card text-card-foreground shadow-surface\",\n\n // box-border and font-sans are not boilerplate. The reset in\n // @seekalfred/styles is OPT-IN, so a consumer may load none: content-box\n // would add the padding to any width they set (the Alert shipped\n // overflowing its container by 46px), and an unstyled document renders\n // text in a serif (the Accordion shipped its panel that way). Declaring\n // the family HERE rather than on each part means one declaration covers\n // the whole subtree by inheritance.\n \"box-border font-sans\",\n]);\n\n/**\n * Padding is inline-only on the sections and block-only on the shell.\n *\n * That split is load-bearing rather than stylistic: it is what lets a\n * consumer run a divider or a full-bleed image edge to edge, which a single\n * `p-6` on the shell would make impossible.\n */\nexport const cardHeaderVariants = cva(\"flex flex-col gap-1.5 px-6\");\n\nexport const cardTitleVariants = cva([\n // leading-* AFTER text-*: tailwind-merge treats a later text-* as owning\n // line-height and silently strips a leading-* that precedes it.\n \"text-base leading-(--leading-none) font-semibold\",\n // The user-agent <h3> ships a 1em block margin and font-size 1.17em. The\n // legacy component was a <div> and never met either.\n \"m-0\",\n]);\n\nexport const cardDescriptionVariants = cva([\n \"text-sm leading-(--leading-normal) text-muted-foreground\",\n]);\n\nexport const cardContentVariants = cva(\"px-6\");\n\nexport const cardFooterVariants = cva(\"flex items-center px-6\");\n\n/**\n * A content container.\n *\n * @example\n * <Card>\n * <CardHeader>\n * <CardTitle>Learning Progress</CardTitle>\n * <CardDescription>Across all active courses.</CardDescription>\n * </CardHeader>\n * <CardContent>Twelve of eighteen modules complete.</CardContent>\n * </Card>\n */\nexport interface CardProps extends ComponentPropsWithoutRef<\"div\"> {\n /**\n * Make the card respond to a pointer: it floats 2px on a spring while it is pointed\n * at, its border darkens and its shadow deepens. Mouse and pen only; touch never lifts.\n * Leave it off for a card that is not a target: a static card should not move.\n */\n interactive?: boolean;\n}\n\nexport const Card = forwardRef<HTMLDivElement, CardProps>(function Card(\n { className, interactive = false, onPointerEnter, onPointerLeave, ...props },\n ref,\n) {\n // Pointed at by a mouse or pen. Touch never lifts, so it is read from the pointer, not :hover (which a tap leaves stuck).\n const [pointed, setPointed] = useState(false);\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card\"\n data-interactive={interactive || undefined}\n data-pointed={(interactive && pointed) || undefined}\n onPointerEnter={(event) => {\n onPointerEnter?.(event);\n if (interactive && event.pointerType !== \"touch\") setPointed(true);\n }}\n onPointerLeave={(event) => {\n onPointerLeave?.(event);\n setPointed(false);\n }}\n className={cnAll(cardVariants(), interactive && \"card-lift\", className)}\n />\n );\n});\n\n/** Title and description, stacked. */\nexport type CardHeaderProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(function CardHeader(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-header\"\n className={cn(cardHeaderVariants(), className)}\n />\n );\n});\n\n/**\n * The card's heading.\n *\n * A REAL heading, unlike the legacy `<div class=\"font-semibold\">`. None of\n * the 19 legacy call sites wrapped one, so 19 card titles were invisible to\n * screen-reader heading navigation -- the same shape of finding as the Avatar\n * port's missing `alt`, and the migration is when it gets answered.\n */\nexport interface CardTitleProps extends ComponentPropsWithoutRef<\"h3\"> {\n /**\n * Heading level.\n *\n * `3` by default, because a card almost always sits under a section\n * heading. It is a prop rather than a constant because the correct level\n * is a fact about the PAGE, and a fixed one would be wrong somewhere; the\n * level changes semantics only, never the rendered size.\n *\n * @default 3\n */\n level?: 1 | 2 | 3 | 4 | 5 | 6;\n}\n\nexport const CardTitle = forwardRef<HTMLHeadingElement, CardTitleProps>(function CardTitle(\n { className, level = 3, ...props },\n ref,\n) {\n // Static union, not a template literal: `h${level}` is a string React\n // accepts but TypeScript cannot narrow to an intrinsic element, and the\n // cast that silences it would also accept `h7`.\n const Heading = ({ 1: \"h1\", 2: \"h2\", 3: \"h3\", 4: \"h4\", 5: \"h5\", 6: \"h6\" } as const)[level];\n\n return (\n <Heading\n ref={ref}\n {...props}\n data-slot=\"card-title\"\n className={cn(cardTitleVariants(), className)}\n />\n );\n});\n\n/** Secondary text under the title. */\nexport type CardDescriptionProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardDescription = forwardRef<HTMLDivElement, CardDescriptionProps>(\n function CardDescription({ className, ...props }, ref) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-description\"\n className={cn(cardDescriptionVariants(), className)}\n />\n );\n },\n);\n\n/** The card's body. */\nexport type CardContentProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardContent = forwardRef<HTMLDivElement, CardContentProps>(function CardContent(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-content\"\n className={cn(cardContentVariants(), className)}\n />\n );\n});\n\n/** Actions or metadata at the foot of the card. */\nexport type CardFooterProps = ComponentPropsWithoutRef<\"div\">;\n\nexport const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(function CardFooter(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n {...props}\n data-slot=\"card-footer\"\n className={cn(cardFooterVariants(), className)}\n />\n );\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAa,eAAe,IAAI;CAC9B;CACA;CACA;CASA;AACF,CAAC;;;;;;;;AASD,MAAa,qBAAqB,IAAI,4BAA4B;AAElE,MAAa,oBAAoB,IAAI,CAGnC,oDAGA,KACF,CAAC;AAED,MAAa,0BAA0B,IAAI,CACzC,0DACF,CAAC;AAED,MAAa,sBAAsB,IAAI,MAAM;AAE7C,MAAa,qBAAqB,IAAI,wBAAwB;AAuB9D,MAAa,OAAO,WAAsC,SAAS,KACjE,EAAE,WAAW,cAAc,OAAO,gBAAgB,gBAAgB,GAAG,SACrE,KACA;CAEA,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,oBAAkB,eAAe,KAAA;EACjC,gBAAe,eAAe,WAAY,KAAA;EAC1C,iBAAiB,UAAU;GACzB,iBAAiB,KAAK;GACtB,IAAI,eAAe,MAAM,gBAAgB,SAAS,WAAW,IAAI;EACnE;EACA,iBAAiB,UAAU;GACzB,iBAAiB,KAAK;GACtB,WAAW,KAAK;EAClB;EACA,WAAW,MAAM,aAAa,GAAG,eAAe,aAAa,SAAS;CACvE,CAAA;AAEL,CAAC;AAKD,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL,CAAC;AAwBD,MAAa,YAAY,WAA+C,SAAS,UAC/E,EAAE,WAAW,QAAQ,GAAG,GAAG,SAC3B,KACA;CAIA,MAAM,UAAW;EAAE,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;EAAM,GAAG;CAAK,EAAY;CAEpF,OACE,oBAAC,SAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,kBAAkB,GAAG,SAAS;CAC7C,CAAA;AAEL,CAAC;AAKD,MAAa,kBAAkB,WAC7B,SAAS,gBAAgB,EAAE,WAAW,GAAG,SAAS,KAAK;CACrD,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,wBAAwB,GAAG,SAAS;CACnD,CAAA;AAEL,CACF;AAKA,MAAa,cAAc,WAA6C,SAAS,YAC/E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,oBAAoB,GAAG,SAAS;CAC/C,CAAA;AAEL,CAAC;AAKD,MAAa,aAAa,WAA4C,SAAS,WAC7E,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,GAAI;EACJ,aAAU;EACV,WAAW,GAAG,mBAAmB,GAAG,SAAS;CAC9C,CAAA;AAEL,CAAC"}
@@ -0,0 +1,54 @@
1
+ import { ReactNode } from "react";
2
+ //#region src/card-stack/card-stack.d.ts
3
+ export type CardDecision = "left" | "right";
4
+ /**
5
+ * CardStack: a deck of cards reviewed one at a time. Drag or flick the top card
6
+ * left or right, or use the buttons and the arrow keys. The cards behind peek
7
+ * out below it and rise as it leaves; a card in the hand tips the way it is
8
+ * moving and shows a stamp (the action it will take); let go early and it springs
9
+ * home, let go far enough, or fast enough, and it is thrown. Undo brings the
10
+ * last card back from where it left, and Start over deals the deck again.
11
+ *
12
+ * Use it for quick triage where each item deserves a moment of attention:
13
+ * shortlisting destinations, candidates or design options. Use a list or a
14
+ * table when people need to compare items side by side.
15
+ *
16
+ * Keyboard: the stack takes one tab stop; Left and Right decide the top card,
17
+ * Backspace (or Ctrl or Cmd with Z) undoes. Every decision is announced politely
18
+ * and the buttons do the same, so nothing needs a drag. Reduced motion fades the
19
+ * card out and back instead of throwing it.
20
+ */
21
+ export interface CardStackProps<T> {
22
+ items: T[];
23
+ /** A stable key for an item. */
24
+ getKey: (item: T) => string;
25
+ /** A short name for the card, used in its accessible label and announcements, such as "Lisbon". */
26
+ getLabel: (item: T) => string;
27
+ /** The card's content. It fills the card, whose size the stack sets. */
28
+ renderCard: (item: T) => ReactNode;
29
+ /** Called when a card is decided. */
30
+ onDecide?: (item: T, decision: CardDecision) => void;
31
+ /** Called when a decision is undone. */
32
+ onUndo?: (item: T, decision: CardDecision) => void;
33
+ /** Called when the deck is dealt again. */
34
+ onReset?: () => void;
35
+ /** Action names for each side, used on the buttons and the stamps that appear while dragging. @default { left: "Pass", right: "Keep" } */
36
+ labels?: {
37
+ left: string;
38
+ right: string;
39
+ };
40
+ /** Past tense of each action for announcements, such as "skipped" and "shortlisted". Without it a decision reads "Lisbon: Keep". */
41
+ outcomes?: {
42
+ left: string;
43
+ right: string;
44
+ };
45
+ /** Accessible name for the stack. @default "Cards" */
46
+ label?: string;
47
+ /** Shown once every card has been decided. Receives a function that brings all cards back. */
48
+ renderEmpty?: (reset: () => void) => ReactNode;
49
+ /** Extra classes for the stack. Merged last. */
50
+ className?: string;
51
+ }
52
+ export declare function CardStack<T>({ items, getKey, getLabel, renderCard, onDecide, onUndo, onReset, labels, outcomes, label, renderEmpty, className }: CardStackProps<T>): import("react").JSX.Element;
53
+ //#endregion
54
+ //# sourceMappingURL=card-stack.d.ts.map