@burtson-labs/ui 0.14.0 → 0.14.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 (239) hide show
  1. package/LICENSES/DEPENDENCIES.txt +1 -1
  2. package/README.md +15 -11
  3. package/dist/components/accordion.d.ts +3 -3
  4. package/dist/components/accordion.js +13 -10
  5. package/dist/components/accordion.js.map +1 -1
  6. package/dist/components/alert-dialog.d.ts +12 -10
  7. package/dist/components/alert-dialog.js +29 -20
  8. package/dist/components/alert-dialog.js.map +1 -1
  9. package/dist/components/alert.d.ts +5 -3
  10. package/dist/components/alert.js +11 -7
  11. package/dist/components/alert.js.map +1 -1
  12. package/dist/components/app-shell.d.ts +7 -6
  13. package/dist/components/app-shell.js +21 -14
  14. package/dist/components/app-shell.js.map +1 -1
  15. package/dist/components/attachment.d.ts +3 -3
  16. package/dist/components/attachment.js +10 -7
  17. package/dist/components/attachment.js.map +1 -1
  18. package/dist/components/audio-player.d.ts +2 -2
  19. package/dist/components/audio-player.js +8 -6
  20. package/dist/components/audio-player.js.map +1 -1
  21. package/dist/components/avatar.d.ts +3 -3
  22. package/dist/components/avatar.js +10 -7
  23. package/dist/components/avatar.js.map +1 -1
  24. package/dist/components/badge.d.ts +1 -1
  25. package/dist/components/badge.js +12 -10
  26. package/dist/components/badge.js.map +1 -1
  27. package/dist/components/breadcrumb.d.ts +8 -8
  28. package/dist/components/breadcrumb.js +24 -17
  29. package/dist/components/breadcrumb.js.map +1 -1
  30. package/dist/components/button.d.ts +8 -1
  31. package/dist/components/button.js +12 -3
  32. package/dist/components/button.js.map +1 -1
  33. package/dist/components/card.d.ts +8 -8
  34. package/dist/components/card.js +32 -23
  35. package/dist/components/card.js.map +1 -1
  36. package/dist/components/chat-history.d.ts +1 -1
  37. package/dist/components/chat-history.js +10 -9
  38. package/dist/components/chat-history.js.map +1 -1
  39. package/dist/components/chat-layout.d.ts +1 -1
  40. package/dist/components/chat-layout.js +7 -3
  41. package/dist/components/chat-layout.js.map +1 -1
  42. package/dist/components/checkbox-card.d.ts +2 -2
  43. package/dist/components/checkbox-card.js +9 -7
  44. package/dist/components/checkbox-card.js.map +1 -1
  45. package/dist/components/checkbox.d.ts +23 -2
  46. package/dist/components/checkbox.js +48 -6
  47. package/dist/components/checkbox.js.map +1 -1
  48. package/dist/components/collapsible.d.ts +2 -2
  49. package/dist/components/collapsible.js +7 -5
  50. package/dist/components/collapsible.js.map +1 -1
  51. package/dist/components/combobox.d.ts +14 -4
  52. package/dist/components/combobox.js +18 -8
  53. package/dist/components/combobox.js.map +1 -1
  54. package/dist/components/command.d.ts +68 -12
  55. package/dist/components/command.js +38 -25
  56. package/dist/components/command.js.map +1 -1
  57. package/dist/components/composer.d.ts +2 -2
  58. package/dist/components/composer.js +9 -7
  59. package/dist/components/composer.js.map +1 -1
  60. package/dist/components/connection-status.d.ts +3 -3
  61. package/dist/components/connection-status.js +18 -14
  62. package/dist/components/connection-status.js.map +1 -1
  63. package/dist/components/context-menu.d.ts +14 -13
  64. package/dist/components/context-menu.js +33 -23
  65. package/dist/components/context-menu.js.map +1 -1
  66. package/dist/components/conversation.d.ts +1 -1
  67. package/dist/components/conversation.js +6 -5
  68. package/dist/components/conversation.js.map +1 -1
  69. package/dist/components/copy-button.d.ts +1 -1
  70. package/dist/components/copy-button.js +3 -2
  71. package/dist/components/copy-button.js.map +1 -1
  72. package/dist/components/data-table.d.ts +7 -2
  73. package/dist/components/data-table.js +16 -42
  74. package/dist/components/data-table.js.map +1 -1
  75. package/dist/components/dialog.d.ts +18 -9
  76. package/dist/components/dialog.js +38 -22
  77. package/dist/components/dialog.js.map +1 -1
  78. package/dist/components/dropdown-menu.d.ts +14 -13
  79. package/dist/components/dropdown-menu.js +33 -23
  80. package/dist/components/dropdown-menu.js.map +1 -1
  81. package/dist/components/editor-tabs.d.ts +1 -1
  82. package/dist/components/editor-tabs.js +6 -5
  83. package/dist/components/editor-tabs.js.map +1 -1
  84. package/dist/components/empty-state.d.ts +5 -5
  85. package/dist/components/empty-state.js +16 -11
  86. package/dist/components/empty-state.js.map +1 -1
  87. package/dist/components/error-summary.d.ts +21 -0
  88. package/dist/components/error-summary.js +62 -0
  89. package/dist/components/error-summary.js.map +1 -0
  90. package/dist/components/field.d.ts +66 -7
  91. package/dist/components/field.js +151 -20
  92. package/dist/components/field.js.map +1 -1
  93. package/dist/components/form-actions.d.ts +18 -0
  94. package/dist/components/form-actions.js +30 -0
  95. package/dist/components/form-actions.js.map +1 -0
  96. package/dist/components/icon-button.d.ts +3 -1
  97. package/dist/components/icon-button.js +5 -3
  98. package/dist/components/icon-button.js.map +1 -1
  99. package/dist/components/input.d.ts +42 -7
  100. package/dist/components/input.js +53 -10
  101. package/dist/components/input.js.map +1 -1
  102. package/dist/components/kbd.d.ts +2 -2
  103. package/dist/components/kbd.js +7 -5
  104. package/dist/components/kbd.js.map +1 -1
  105. package/dist/components/label.d.ts +6 -1
  106. package/dist/components/label.js +10 -4
  107. package/dist/components/label.js.map +1 -1
  108. package/dist/components/markdown.d.ts +4 -3
  109. package/dist/components/markdown.js +7 -6
  110. package/dist/components/markdown.js.map +1 -1
  111. package/dist/components/menubar.d.ts +16 -14
  112. package/dist/components/menubar.js +39 -27
  113. package/dist/components/menubar.js.map +1 -1
  114. package/dist/components/message-actions.d.ts +3 -3
  115. package/dist/components/message-actions.js +15 -12
  116. package/dist/components/message-actions.js.map +1 -1
  117. package/dist/components/message.d.ts +1 -1
  118. package/dist/components/message.js +5 -3
  119. package/dist/components/message.js.map +1 -1
  120. package/dist/components/mobile-nav.d.ts +1 -1
  121. package/dist/components/mobile-nav.js +6 -5
  122. package/dist/components/mobile-nav.js.map +1 -1
  123. package/dist/components/native-select.d.ts +16 -0
  124. package/dist/components/native-select.js +33 -0
  125. package/dist/components/native-select.js.map +1 -0
  126. package/dist/components/navigation-menu.d.ts +12 -11
  127. package/dist/components/navigation-menu.js +32 -23
  128. package/dist/components/navigation-menu.js.map +1 -1
  129. package/dist/components/number-input.d.ts +23 -0
  130. package/dist/components/number-input.js +45 -0
  131. package/dist/components/number-input.js.map +1 -0
  132. package/dist/components/onboarding-checklist.d.ts +1 -1
  133. package/dist/components/onboarding-checklist.js +4 -3
  134. package/dist/components/onboarding-checklist.js.map +1 -1
  135. package/dist/components/page-header.d.ts +7 -6
  136. package/dist/components/page-header.js +22 -15
  137. package/dist/components/page-header.js.map +1 -1
  138. package/dist/components/pagination.d.ts +1 -1
  139. package/dist/components/pagination.js +3 -2
  140. package/dist/components/pagination.js.map +1 -1
  141. package/dist/components/popover.d.ts +13 -10
  142. package/dist/components/popover.js +23 -17
  143. package/dist/components/popover.js.map +1 -1
  144. package/dist/components/progress.d.ts +1 -1
  145. package/dist/components/progress.js +6 -5
  146. package/dist/components/progress.js.map +1 -1
  147. package/dist/components/radio-group.d.ts +22 -3
  148. package/dist/components/radio-group.js +51 -10
  149. package/dist/components/radio-group.js.map +1 -1
  150. package/dist/components/reasoning.d.ts +3 -3
  151. package/dist/components/reasoning.js +9 -7
  152. package/dist/components/reasoning.js.map +1 -1
  153. package/dist/components/scroll-area.d.ts +3 -2
  154. package/dist/components/scroll-area.js +10 -7
  155. package/dist/components/scroll-area.js.map +1 -1
  156. package/dist/components/secret-input.d.ts +3 -2
  157. package/dist/components/secret-input.js +6 -4
  158. package/dist/components/secret-input.js.map +1 -1
  159. package/dist/components/select.d.ts +11 -6
  160. package/dist/components/select.js +24 -18
  161. package/dist/components/select.js.map +1 -1
  162. package/dist/components/separator.d.ts +1 -1
  163. package/dist/components/separator.js +4 -3
  164. package/dist/components/separator.js.map +1 -1
  165. package/dist/components/sheet.d.ts +10 -8
  166. package/dist/components/sheet.js +31 -20
  167. package/dist/components/sheet.js.map +1 -1
  168. package/dist/components/skeleton.d.ts +2 -1
  169. package/dist/components/skeleton.js +6 -4
  170. package/dist/components/skeleton.js.map +1 -1
  171. package/dist/components/slider.d.ts +10 -6
  172. package/dist/components/slider.js +15 -7
  173. package/dist/components/slider.js.map +1 -1
  174. package/dist/components/source.d.ts +2 -2
  175. package/dist/components/source.js +9 -7
  176. package/dist/components/source.js.map +1 -1
  177. package/dist/components/spinner.d.ts +4 -2
  178. package/dist/components/spinner.js +4 -3
  179. package/dist/components/spinner.js.map +1 -1
  180. package/dist/components/stat-card.d.ts +27 -2
  181. package/dist/components/stat-card.js +69 -9
  182. package/dist/components/stat-card.js.map +1 -1
  183. package/dist/components/status.d.ts +7 -6
  184. package/dist/components/status.js +11 -5
  185. package/dist/components/status.js.map +1 -1
  186. package/dist/components/steps.d.ts +1 -1
  187. package/dist/components/steps.js +4 -3
  188. package/dist/components/steps.js.map +1 -1
  189. package/dist/components/switch.d.ts +46 -2
  190. package/dist/components/switch.js +85 -8
  191. package/dist/components/switch.js.map +1 -1
  192. package/dist/components/table.d.ts +29 -12
  193. package/dist/components/table.js +79 -34
  194. package/dist/components/table.js.map +1 -1
  195. package/dist/components/tabs.d.ts +9 -4
  196. package/dist/components/tabs.js +22 -13
  197. package/dist/components/tabs.js.map +1 -1
  198. package/dist/components/textarea.d.ts +6 -1
  199. package/dist/components/textarea.js +3 -3
  200. package/dist/components/textarea.js.map +1 -1
  201. package/dist/components/toast.d.ts +10 -6
  202. package/dist/components/toast.js +27 -18
  203. package/dist/components/toast.js.map +1 -1
  204. package/dist/components/toaster.d.ts +4 -2
  205. package/dist/components/toaster.js +6 -2
  206. package/dist/components/toaster.js.map +1 -1
  207. package/dist/components/tool-call.d.ts +2 -2
  208. package/dist/components/tool-call.js +10 -8
  209. package/dist/components/tool-call.js.map +1 -1
  210. package/dist/components/toolbar.d.ts +9 -4
  211. package/dist/components/toolbar.js +14 -9
  212. package/dist/components/toolbar.js.map +1 -1
  213. package/dist/components/tooltip.d.ts +3 -2
  214. package/dist/components/tooltip.js +7 -4
  215. package/dist/components/tooltip.js.map +1 -1
  216. package/dist/components/tour.js +2 -2
  217. package/dist/components/tour.js.map +1 -1
  218. package/dist/components/tree-view.d.ts +1 -1
  219. package/dist/components/tree-view.js +9 -5
  220. package/dist/components/tree-view.js.map +1 -1
  221. package/dist/components/voice-recorder.d.ts +1 -1
  222. package/dist/components/voice-recorder.js +4 -2
  223. package/dist/components/voice-recorder.js.map +1 -1
  224. package/dist/index.d.ts +6 -1
  225. package/dist/index.js +17 -12
  226. package/dist/lib/media.d.ts +18 -0
  227. package/dist/lib/media.js +34 -0
  228. package/dist/lib/media.js.map +1 -0
  229. package/dist/lib/utils.d.ts +32 -0
  230. package/dist/lib/utils.js +33 -1
  231. package/dist/lib/utils.js.map +1 -1
  232. package/dist/mui/index.js +20 -8
  233. package/dist/mui/index.js.map +1 -1
  234. package/dist/styles.css +1 -1
  235. package/dist/theme.css +36 -17
  236. package/dist/tokens.d.ts +78 -36
  237. package/dist/tokens.js +63 -32
  238. package/dist/tokens.js.map +1 -1
  239. package/package.json +5 -3
@@ -1,7 +1,8 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/utils.js";
3
3
  import { Close as DialogClose, Content as DialogContent, Description as DialogDescription, Dialog, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from "../primitives/vendor/radix/react-dialog/dialog.js";
4
- import "react";
4
+ import { overlayClasses, overlayCloseClasses } from "./dialog.js";
5
+ import * as React from "react";
5
6
  import { jsx, jsxs } from "react/jsx-runtime";
6
7
  import X from "@burtson-labs/icons/react/x";
7
8
  //#region src/components/sheet.tsx
@@ -11,31 +12,36 @@ function Sheet(props) {
11
12
  ...props
12
13
  });
13
14
  }
14
- function SheetTrigger(props) {
15
+ var SheetTrigger = React.forwardRef(function SheetTrigger(props, ref) {
15
16
  return /* @__PURE__ */ jsx(DialogTrigger, {
17
+ ref,
16
18
  "data-slot": "sheet-trigger",
17
19
  ...props
18
20
  });
19
- }
20
- function SheetClose(props) {
21
+ });
22
+ var SheetClose = React.forwardRef(function SheetClose(props, ref) {
21
23
  return /* @__PURE__ */ jsx(DialogClose, {
24
+ ref,
22
25
  "data-slot": "sheet-close",
23
26
  ...props
24
27
  });
25
- }
28
+ });
26
29
  var sideClasses = {
27
30
  right: "inset-y-0 right-0 h-full w-3/4 border-l [--bl-sheet-x:100%] sm:max-w-sm",
28
31
  left: "inset-y-0 left-0 h-full w-3/4 border-r [--bl-sheet-x:-100%] sm:max-w-sm",
29
32
  top: "inset-x-0 top-0 max-h-[85dvh] rounded-b-xl border-b pt-[env(safe-area-inset-top)] [--bl-sheet-x:0] [--bl-sheet-y:-100%]",
30
33
  bottom: "inset-x-0 bottom-0 max-h-[85dvh] rounded-t-xl border-t pb-[env(safe-area-inset-bottom)] [--bl-sheet-x:0] [--bl-sheet-y:100%]"
31
34
  };
32
- function SheetContent({ className, children, side = "right", handle = side === "bottom", showCloseButton = true, ...props }) {
35
+ /** A panel from an edge: 16px radius on the free corners, elevation 5. */
36
+ var SheetContent = React.forwardRef(function SheetContent({ className, children, side = "right", handle = side === "bottom", showCloseButton = true, ...props }, ref) {
33
37
  return /* @__PURE__ */ jsxs(DialogPortal, { children: [/* @__PURE__ */ jsx(DialogOverlay, {
34
38
  "data-slot": "sheet-overlay",
35
- className: "fixed inset-0 z-50 bg-black/55 backdrop-blur-[3px] data-[state=closed]:animate-fade-out data-[state=open]:animate-fade-in"
39
+ className: overlayClasses
36
40
  }), /* @__PURE__ */ jsxs(DialogContent, {
41
+ ref,
37
42
  "data-slot": "sheet-content",
38
- className: cn("fixed z-50 max-h-dvh overflow-y-auto overscroll-contain flex flex-col gap-4 border-border-strong bg-surface-raised text-foreground shadow-[0_24px_80px_rgb(0_0_0_/_0.28)] outline-none data-[state=closed]:animate-sheet-out data-[state=open]:animate-sheet-in", sideClasses[side], className),
43
+ "data-side": side,
44
+ className: cn("fixed z-50 max-h-dvh overflow-y-auto overscroll-contain flex flex-col gap-4 border-border-strong bg-surface-raised text-foreground shadow-xl outline-none data-[state=closed]:animate-sheet-out data-[state=open]:animate-sheet-in", sideClasses[side], className),
39
45
  ...props,
40
46
  children: [
41
47
  handle && /* @__PURE__ */ jsx("div", {
@@ -45,7 +51,8 @@ function SheetContent({ className, children, side = "right", handle = side === "
45
51
  }),
46
52
  children,
47
53
  showCloseButton && /* @__PURE__ */ jsxs(DialogClose, {
48
- className: cn("absolute right-3 grid size-8 place-items-center rounded-md text-muted-foreground transition-colors outline-none hover:bg-muted hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/20 pointer-coarse:size-11 [&_svg]:size-4 pointer-coarse:[&_svg]:size-5", side === "top" ? "top-[calc(env(safe-area-inset-top)+0.75rem)]" : "top-3"),
54
+ "data-slot": "sheet-close",
55
+ className: cn(overlayCloseClasses, side === "top" && "top-[calc(env(safe-area-inset-top)+0.75rem)] pointer-coarse:top-[calc(env(safe-area-inset-top)+0.5rem)]"),
49
56
  children: [/* @__PURE__ */ jsx(X, { "aria-hidden": true }), /* @__PURE__ */ jsx("span", {
50
57
  className: "sr-only",
51
58
  children: "Close"
@@ -53,35 +60,39 @@ function SheetContent({ className, children, side = "right", handle = side === "
53
60
  })
54
61
  ]
55
62
  })] });
56
- }
57
- function SheetHeader({ className, ...props }) {
63
+ });
64
+ var SheetHeader = React.forwardRef(function SheetHeader({ className, ...props }, ref) {
58
65
  return /* @__PURE__ */ jsx("div", {
66
+ ref,
59
67
  "data-slot": "sheet-header",
60
- className: cn("flex flex-col gap-1.5 p-4", className),
68
+ className: cn("flex flex-col gap-1.5 p-4 pr-12", className),
61
69
  ...props
62
70
  });
63
- }
64
- function SheetFooter({ className, ...props }) {
71
+ });
72
+ var SheetFooter = React.forwardRef(function SheetFooter({ className, ...props }, ref) {
65
73
  return /* @__PURE__ */ jsx("div", {
74
+ ref,
66
75
  "data-slot": "sheet-footer",
67
76
  className: cn("mt-auto flex flex-col gap-2 p-4", className),
68
77
  ...props
69
78
  });
70
- }
71
- function SheetTitle({ className, ...props }) {
79
+ });
80
+ var SheetTitle = React.forwardRef(function SheetTitle({ className, ...props }, ref) {
72
81
  return /* @__PURE__ */ jsx(DialogTitle, {
82
+ ref,
73
83
  "data-slot": "sheet-title",
74
84
  className: cn("text-base font-semibold tracking-[-0.02em] text-foreground", className),
75
85
  ...props
76
86
  });
77
- }
78
- function SheetDescription({ className, ...props }) {
87
+ });
88
+ var SheetDescription = React.forwardRef(function SheetDescription({ className, ...props }, ref) {
79
89
  return /* @__PURE__ */ jsx(DialogDescription, {
90
+ ref,
80
91
  "data-slot": "sheet-description",
81
- className: cn("text-sm text-muted-foreground", className),
92
+ className: cn("text-sm leading-5 text-muted-foreground", className),
82
93
  ...props
83
94
  });
84
- }
95
+ });
85
96
  //#endregion
86
97
  export { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger };
87
98
 
@@ -1 +1 @@
1
- {"version":3,"file":"sheet.js","names":["SheetPrimitive.Root","SheetPrimitive.Trigger","SheetPrimitive.Close","SheetPrimitive.Portal","SheetPrimitive.Overlay","SheetPrimitive.Content","SheetPrimitive.Title","SheetPrimitive.Description"],"sources":["../../src/components/sheet.tsx"],"sourcesContent":["import X from '@burtson-labs/icons/react/x';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\nimport * as SheetPrimitive from '../primitives/vendor/radix/react-dialog';\n\nfunction Sheet(props: React.ComponentProps<typeof SheetPrimitive.Root>) {\n return <SheetPrimitive.Root data-slot=\"sheet\" {...props} />;\n}\n\nfunction SheetTrigger(props: React.ComponentProps<typeof SheetPrimitive.Trigger>) {\n return <SheetPrimitive.Trigger data-slot=\"sheet-trigger\" {...props} />;\n}\n\nfunction SheetClose(props: React.ComponentProps<typeof SheetPrimitive.Close>) {\n return <SheetPrimitive.Close data-slot=\"sheet-close\" {...props} />;\n}\n\nconst sideClasses = {\n right: 'inset-y-0 right-0 h-full w-3/4 border-l [--bl-sheet-x:100%] sm:max-w-sm',\n left: 'inset-y-0 left-0 h-full w-3/4 border-r [--bl-sheet-x:-100%] sm:max-w-sm',\n // Top and bottom sheets are the mobile drawers. Each keeps clear of the\n // notch or home indicator on its edge (safe-area insets).\n top: 'inset-x-0 top-0 max-h-[85dvh] rounded-b-xl border-b pt-[env(safe-area-inset-top)] [--bl-sheet-x:0] [--bl-sheet-y:-100%]',\n bottom:\n 'inset-x-0 bottom-0 max-h-[85dvh] rounded-t-xl border-t pb-[env(safe-area-inset-bottom)] [--bl-sheet-x:0] [--bl-sheet-y:100%]',\n} as const;\n\nfunction SheetContent({\n className,\n children,\n side = 'right',\n handle = side === 'bottom',\n showCloseButton = true,\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Content> & {\n side?: keyof typeof sideClasses;\n /**\n * The X in the corner. Turn it off when the sheet draws its own header\n * with a close control (a SheetClose), so there are not two.\n */\n showCloseButton?: boolean;\n /**\n * A grab bar at the top edge, the cue that this is a drawer. Visual only:\n * it does not drag; close with the button, Escape or the overlay. On by\n * default for bottom sheets.\n */\n handle?: boolean;\n}) {\n return (\n <SheetPrimitive.Portal>\n <SheetPrimitive.Overlay\n data-slot=\"sheet-overlay\"\n className=\"fixed inset-0 z-50 bg-black/55 backdrop-blur-[3px] data-[state=closed]:animate-fade-out data-[state=open]:animate-fade-in\"\n />\n <SheetPrimitive.Content\n data-slot=\"sheet-content\"\n className={cn(\n 'fixed z-50 max-h-dvh overflow-y-auto overscroll-contain flex flex-col gap-4 border-border-strong bg-surface-raised text-foreground shadow-[0_24px_80px_rgb(0_0_0_/_0.28)] outline-none data-[state=closed]:animate-sheet-out data-[state=open]:animate-sheet-in',\n sideClasses[side],\n className,\n )}\n {...props}\n >\n {handle && (\n <div\n data-slot=\"sheet-handle\"\n aria-hidden\n className=\"mx-auto mt-2 -mb-2 h-1 w-10 shrink-0 rounded-full bg-border-strong\"\n />\n )}\n {children}\n {/* 44px on touch screens, 32px with a mouse. */}\n {showCloseButton && (\n <SheetPrimitive.Close\n className={cn(\n 'absolute right-3 grid size-8 place-items-center rounded-md text-muted-foreground transition-colors outline-none hover:bg-muted hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/20 pointer-coarse:size-11 [&_svg]:size-4 pointer-coarse:[&_svg]:size-5',\n side === 'top' ? 'top-[calc(env(safe-area-inset-top)+0.75rem)]' : 'top-3',\n )}\n >\n <X aria-hidden />\n <span className=\"sr-only\">Close</span>\n </SheetPrimitive.Close>\n )}\n </SheetPrimitive.Content>\n </SheetPrimitive.Portal>\n );\n}\n\nfunction SheetHeader({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"sheet-header\"\n className={cn('flex flex-col gap-1.5 p-4', className)}\n {...props}\n />\n );\n}\n\nfunction SheetFooter({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"sheet-footer\"\n className={cn('mt-auto flex flex-col gap-2 p-4', className)}\n {...props}\n />\n );\n}\n\nfunction SheetTitle({ className, ...props }: React.ComponentProps<typeof SheetPrimitive.Title>) {\n return (\n <SheetPrimitive.Title\n data-slot=\"sheet-title\"\n className={cn('text-base font-semibold tracking-[-0.02em] text-foreground', className)}\n {...props}\n />\n );\n}\n\nfunction SheetDescription({\n className,\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Description>) {\n return (\n <SheetPrimitive.Description\n data-slot=\"sheet-description\"\n className={cn('text-sm text-muted-foreground', className)}\n {...props}\n />\n );\n}\n\nexport {\n Sheet,\n SheetClose,\n SheetContent,\n SheetDescription,\n SheetFooter,\n SheetHeader,\n SheetTitle,\n SheetTrigger,\n};\n"],"mappings":";;;;;;;AAMA,SAAS,MAAM,OAAyD;CACtE,OAAO,oBAACA,QAAD;EAAqB,aAAU;EAAQ,GAAI;CAAQ,CAAA;AAC5D;AAEA,SAAS,aAAa,OAA4D;CAChF,OAAO,oBAACC,eAAD;EAAwB,aAAU;EAAgB,GAAI;CAAQ,CAAA;AACvE;AAEA,SAAS,WAAW,OAA0D;CAC5E,OAAO,oBAACC,aAAD;EAAsB,aAAU;EAAc,GAAI;CAAQ,CAAA;AACnE;AAEA,IAAM,cAAc;CAClB,OAAO;CACP,MAAM;CAGN,KAAK;CACL,QACE;AACJ;AAEA,SAAS,aAAa,EACpB,WACA,UACA,OAAO,SACP,SAAS,SAAS,UAClB,kBAAkB,MAClB,GAAG,SAcF;CACD,OACE,qBAACC,cAAD,EAAA,UAAA,CACE,oBAACC,eAAD;EACE,aAAU;EACV,WAAU;CACX,CAAA,GACD,qBAACC,eAAD;EACE,aAAU;EACV,WAAW,GACT,mQACA,YAAY,OACZ,SACF;EACA,GAAI;EAPN,UAAA;GASG,UACC,oBAAC,OAAD;IACE,aAAU;IACV,eAAA;IACA,WAAU;GACX,CAAA;GAEF;GAEA,mBACC,qBAACH,aAAD;IACE,WAAW,GACT,gRACA,SAAS,QAAQ,iDAAiD,OACpE;IAJF,UAAA,CAME,oBAAC,GAAD,EAAG,eAAA,KAAa,CAAA,GAChB,oBAAC,QAAD;KAAM,WAAU;KAAU,UAAA;IAAW,CAAA,CACjB;;EAEF;CACH,CAAA,CAAA,EAAA,CAAA;AAE3B;AAEA,SAAS,YAAY,EAAE,WAAW,GAAG,SAAsC;CACzE,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,6BAA6B,SAAS;EACpD,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,YAAY,EAAE,WAAW,GAAG,SAAsC;CACzE,OACE,oBAAC,OAAD;EACE,aAAU;EACV,WAAW,GAAG,mCAAmC,SAAS;EAC1D,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,WAAW,EAAE,WAAW,GAAG,SAA4D;CAC9F,OACE,oBAACI,aAAD;EACE,aAAU;EACV,WAAW,GAAG,8DAA8D,SAAS;EACrF,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,iBAAiB,EACxB,WACA,GAAG,SACuD;CAC1D,OACE,oBAACC,mBAAD;EACE,aAAU;EACV,WAAW,GAAG,iCAAiC,SAAS;EACxD,GAAI;CACL,CAAA;AAEL"}
1
+ {"version":3,"file":"sheet.js","names":["SheetPrimitive.Root","SheetPrimitive.Trigger","SheetPrimitive.Close","SheetPrimitive.Portal","SheetPrimitive.Overlay","SheetPrimitive.Content","SheetPrimitive.Title","SheetPrimitive.Description"],"sources":["../../src/components/sheet.tsx"],"sourcesContent":["import X from '@burtson-labs/icons/react/x';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\nimport * as SheetPrimitive from '../primitives/vendor/radix/react-dialog';\n\nimport { overlayClasses, overlayCloseClasses } from './dialog';\n\nfunction Sheet(props: React.ComponentProps<typeof SheetPrimitive.Root>) {\n return <SheetPrimitive.Root data-slot=\"sheet\" {...props} />;\n}\n\nconst SheetTrigger = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<typeof SheetPrimitive.Trigger>\n>(function SheetTrigger(props, ref) {\n return <SheetPrimitive.Trigger ref={ref} data-slot=\"sheet-trigger\" {...props} />;\n});\n\nconst SheetClose = React.forwardRef<\n HTMLButtonElement,\n React.ComponentProps<typeof SheetPrimitive.Close>\n>(function SheetClose(props, ref) {\n return <SheetPrimitive.Close ref={ref} data-slot=\"sheet-close\" {...props} />;\n});\n\nconst sideClasses = {\n right: 'inset-y-0 right-0 h-full w-3/4 border-l [--bl-sheet-x:100%] sm:max-w-sm',\n left: 'inset-y-0 left-0 h-full w-3/4 border-r [--bl-sheet-x:-100%] sm:max-w-sm',\n // Top and bottom sheets are the mobile drawers. Each keeps clear of the\n // notch or home indicator on its edge (safe-area insets).\n top: 'inset-x-0 top-0 max-h-[85dvh] rounded-b-xl border-b pt-[env(safe-area-inset-top)] [--bl-sheet-x:0] [--bl-sheet-y:-100%]',\n bottom:\n 'inset-x-0 bottom-0 max-h-[85dvh] rounded-t-xl border-t pb-[env(safe-area-inset-bottom)] [--bl-sheet-x:0] [--bl-sheet-y:100%]',\n} as const;\n\nexport interface SheetContentProps extends React.ComponentProps<typeof SheetPrimitive.Content> {\n side?: keyof typeof sideClasses;\n /**\n * The X in the corner. Turn it off when the sheet draws its own header\n * with a close control (a SheetClose), so there are not two.\n */\n showCloseButton?: boolean;\n /**\n * A grab bar at the top edge, the cue that this is a drawer. Visual only:\n * it does not drag; close with the button, Escape or the overlay. On by\n * default for bottom sheets.\n */\n handle?: boolean;\n}\n\n/** A panel from an edge: 16px radius on the free corners, elevation 5. */\nconst SheetContent = React.forwardRef<HTMLDivElement, SheetContentProps>(function SheetContent(\n {\n className,\n children,\n side = 'right',\n handle = side === 'bottom',\n showCloseButton = true,\n ...props\n },\n ref,\n) {\n return (\n <SheetPrimitive.Portal>\n <SheetPrimitive.Overlay data-slot=\"sheet-overlay\" className={overlayClasses} />\n <SheetPrimitive.Content\n ref={ref}\n data-slot=\"sheet-content\"\n data-side={side}\n className={cn(\n 'fixed z-50 max-h-dvh overflow-y-auto overscroll-contain flex flex-col gap-4 border-border-strong bg-surface-raised text-foreground shadow-xl outline-none data-[state=closed]:animate-sheet-out data-[state=open]:animate-sheet-in',\n sideClasses[side],\n className,\n )}\n {...props}\n >\n {handle && (\n <div\n data-slot=\"sheet-handle\"\n aria-hidden\n className=\"mx-auto mt-2 -mb-2 h-1 w-10 shrink-0 rounded-full bg-border-strong\"\n />\n )}\n {children}\n {showCloseButton && (\n <SheetPrimitive.Close\n data-slot=\"sheet-close\"\n className={cn(\n overlayCloseClasses,\n side === 'top' &&\n 'top-[calc(env(safe-area-inset-top)+0.75rem)] pointer-coarse:top-[calc(env(safe-area-inset-top)+0.5rem)]',\n )}\n >\n <X aria-hidden />\n <span className=\"sr-only\">Close</span>\n </SheetPrimitive.Close>\n )}\n </SheetPrimitive.Content>\n </SheetPrimitive.Portal>\n );\n});\n\nconst SheetHeader = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(\n function SheetHeader({ className, ...props }, ref) {\n return (\n <div\n ref={ref}\n data-slot=\"sheet-header\"\n className={cn('flex flex-col gap-1.5 p-4 pr-12', className)}\n {...props}\n />\n );\n },\n);\n\nconst SheetFooter = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(\n function SheetFooter({ className, ...props }, ref) {\n return (\n <div\n ref={ref}\n data-slot=\"sheet-footer\"\n className={cn('mt-auto flex flex-col gap-2 p-4', className)}\n {...props}\n />\n );\n },\n);\n\nconst SheetTitle = React.forwardRef<\n HTMLHeadingElement,\n React.ComponentProps<typeof SheetPrimitive.Title>\n>(function SheetTitle({ className, ...props }, ref) {\n return (\n <SheetPrimitive.Title\n ref={ref}\n data-slot=\"sheet-title\"\n className={cn('text-base font-semibold tracking-[-0.02em] text-foreground', className)}\n {...props}\n />\n );\n});\n\nconst SheetDescription = React.forwardRef<\n HTMLParagraphElement,\n React.ComponentProps<typeof SheetPrimitive.Description>\n>(function SheetDescription({ className, ...props }, ref) {\n return (\n <SheetPrimitive.Description\n ref={ref}\n data-slot=\"sheet-description\"\n className={cn('text-sm leading-5 text-muted-foreground', className)}\n {...props}\n />\n );\n});\n\nexport {\n Sheet,\n SheetClose,\n SheetContent,\n SheetDescription,\n SheetFooter,\n SheetHeader,\n SheetTitle,\n SheetTrigger,\n};\n"],"mappings":";;;;;;;;AAQA,SAAS,MAAM,OAAyD;CACtE,OAAO,oBAACA,QAAD;EAAqB,aAAU;EAAQ,GAAI;CAAQ,CAAA;AAC5D;AAEA,IAAM,eAAe,MAAM,WAGzB,SAAS,aAAa,OAAO,KAAK;CAClC,OAAO,oBAACC,eAAD;EAA6B;EAAK,aAAU;EAAgB,GAAI;CAAQ,CAAA;AACjF,CAAC;AAED,IAAM,aAAa,MAAM,WAGvB,SAAS,WAAW,OAAO,KAAK;CAChC,OAAO,oBAACC,aAAD;EAA2B;EAAK,aAAU;EAAc,GAAI;CAAQ,CAAA;AAC7E,CAAC;AAED,IAAM,cAAc;CAClB,OAAO;CACP,MAAM;CAGN,KAAK;CACL,QACE;AACJ;;AAkBA,IAAM,eAAe,MAAM,WAA8C,SAAS,aAChF,EACE,WACA,UACA,OAAO,SACP,SAAS,SAAS,UAClB,kBAAkB,MAClB,GAAG,SAEL,KACA;CACA,OACE,qBAACC,cAAD,EAAA,UAAA,CACE,oBAACC,eAAD;EAAwB,aAAU;EAAgB,WAAW;CAAiB,CAAA,GAC9E,qBAACC,eAAD;EACO;EACL,aAAU;EACV,aAAW;EACX,WAAW,GACT,sOACA,YAAY,OACZ,SACF;EACA,GAAI;EATN,UAAA;GAWG,UACC,oBAAC,OAAD;IACE,aAAU;IACV,eAAA;IACA,WAAU;GACX,CAAA;GAEF;GACA,mBACC,qBAACH,aAAD;IACE,aAAU;IACV,WAAW,GACT,qBACA,SAAS,SACP,yGACJ;IANF,UAAA,CAQE,oBAAC,GAAD,EAAG,eAAA,KAAa,CAAA,GAChB,oBAAC,QAAD;KAAM,WAAU;KAAU,UAAA;IAAW,CAAA,CACjB;;EAEF;CACH,CAAA,CAAA,EAAA,CAAA;AAE3B,CAAC;AAED,IAAM,cAAc,MAAM,WACxB,SAAS,YAAY,EAAE,WAAW,GAAG,SAAS,KAAK;CACjD,OACE,oBAAC,OAAD;EACO;EACL,aAAU;EACV,WAAW,GAAG,mCAAmC,SAAS;EAC1D,GAAI;CACL,CAAA;AAEL,CACF;AAEA,IAAM,cAAc,MAAM,WACxB,SAAS,YAAY,EAAE,WAAW,GAAG,SAAS,KAAK;CACjD,OACE,oBAAC,OAAD;EACO;EACL,aAAU;EACV,WAAW,GAAG,mCAAmC,SAAS;EAC1D,GAAI;CACL,CAAA;AAEL,CACF;AAEA,IAAM,aAAa,MAAM,WAGvB,SAAS,WAAW,EAAE,WAAW,GAAG,SAAS,KAAK;CAClD,OACE,oBAACI,aAAD;EACO;EACL,aAAU;EACV,WAAW,GAAG,8DAA8D,SAAS;EACrF,GAAI;CACL,CAAA;AAEL,CAAC;AAED,IAAM,mBAAmB,MAAM,WAG7B,SAAS,iBAAiB,EAAE,WAAW,GAAG,SAAS,KAAK;CACxD,OACE,oBAACC,mBAAD;EACO;EACL,aAAU;EACV,WAAW,GAAG,2CAA2C,SAAS;EAClE,GAAI;CACL,CAAA;AAEL,CAAC"}
@@ -1,3 +1,4 @@
1
1
  import * as React from 'react';
2
- declare function Skeleton({ className, ...props }: React.ComponentProps<'div'>): React.JSX.Element;
2
+ /** A placeholder block. Give it the size of what it stands in for; hidden from screen readers. */
3
+ declare const Skeleton: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
3
4
  export { Skeleton };
@@ -1,16 +1,18 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/utils.js";
3
- import "react";
3
+ import * as React from "react";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region src/components/skeleton.tsx
6
- function Skeleton({ className, ...props }) {
6
+ /** A placeholder block. Give it the size of what it stands in for; hidden from screen readers. */
7
+ var Skeleton = React.forwardRef(function Skeleton({ className, ...props }, ref) {
7
8
  return /* @__PURE__ */ jsx("div", {
9
+ ref,
8
10
  "data-slot": "skeleton",
9
11
  "aria-hidden": true,
10
- className: cn("animate-pulse rounded-md bg-foreground/[0.06]", className),
12
+ className: cn("animate-pulse rounded-md bg-foreground/[0.08]", className),
11
13
  ...props
12
14
  });
13
- }
15
+ });
14
16
  //#endregion
15
17
  export { Skeleton };
16
18
 
@@ -1 +1 @@
1
- {"version":3,"file":"skeleton.js","names":[],"sources":["../../src/components/skeleton.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nfunction Skeleton({ className, ...props }: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"skeleton\"\n aria-hidden\n className={cn('animate-pulse rounded-md bg-foreground/[0.06]', className)}\n {...props}\n />\n );\n}\n\nexport { Skeleton };\n"],"mappings":";;;;;AAIA,SAAS,SAAS,EAAE,WAAW,GAAG,SAAsC;CACtE,OACE,oBAAC,OAAD;EACE,aAAU;EACV,eAAA;EACA,WAAW,GAAG,iDAAiD,SAAS;EACxE,GAAI;CACL,CAAA;AAEL"}
1
+ {"version":3,"file":"skeleton.js","names":[],"sources":["../../src/components/skeleton.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\n/** A placeholder block. Give it the size of what it stands in for; hidden from screen readers. */\nconst Skeleton = React.forwardRef<HTMLDivElement, React.ComponentProps<'div'>>(function Skeleton(\n { className, ...props },\n ref,\n) {\n return (\n <div\n ref={ref}\n data-slot=\"skeleton\"\n aria-hidden\n className={cn('animate-pulse rounded-md bg-foreground/[0.08]', className)}\n {...props}\n />\n );\n});\n\nexport { Skeleton };\n"],"mappings":";;;;;;AAKA,IAAM,WAAW,MAAM,WAAwD,SAAS,SACtF,EAAE,WAAW,GAAG,SAChB,KACA;CACA,OACE,oBAAC,OAAD;EACO;EACL,aAAU;EACV,eAAA;EACA,WAAW,GAAG,iDAAiD,SAAS;EACxE,GAAI;CACL,CAAA;AAEL,CAAC"}
@@ -1,15 +1,19 @@
1
1
  import * as React from 'react';
2
- export interface SliderProps extends Omit<React.ComponentProps<'input'>, 'type' | 'value' | 'onChange'> {
3
- value: number;
4
- onValueChange: (value: number) => void;
2
+ export interface SliderProps extends Omit<React.ComponentProps<'input'>, 'type' | 'value' | 'defaultValue' | 'onChange'> {
3
+ /** Controlled value. Leave out for an uncontrolled slider. */
4
+ value?: number;
5
+ /** The starting value when uncontrolled. */
6
+ defaultValue?: number;
7
+ onValueChange?: (value: number) => void;
5
8
  min?: number;
6
9
  max?: number;
7
10
  step?: number;
8
11
  }
9
12
  /**
10
13
  * A single-value range slider on the native input, so keyboard, touch and
11
- * screen readers work without extra code. Pass `aria-valuetext` when the raw
12
- * number isn't meaningful ("45% change").
14
+ * screen readers work without extra code. The track is 6px; the thumb 16px,
15
+ * with the whole 44px-tall input as its target on touch screens. Pass
16
+ * `aria-valuetext` when the raw number isn't meaningful ("45% change").
13
17
  */
14
- declare function Slider({ className, value, onValueChange, min, max, step, style, ...props }: SliderProps): React.JSX.Element;
18
+ declare const Slider: React.ForwardRefExoticComponent<Omit<SliderProps, "ref"> & React.RefAttributes<HTMLInputElement>>;
15
19
  export { Slider };
@@ -1,31 +1,39 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/utils.js";
3
- import "react";
3
+ import * as React from "react";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region src/components/slider.tsx
6
6
  /**
7
7
  * A single-value range slider on the native input, so keyboard, touch and
8
- * screen readers work without extra code. Pass `aria-valuetext` when the raw
9
- * number isn't meaningful ("45% change").
8
+ * screen readers work without extra code. The track is 6px; the thumb 16px,
9
+ * with the whole 44px-tall input as its target on touch screens. Pass
10
+ * `aria-valuetext` when the raw number isn't meaningful ("45% change").
10
11
  */
11
- function Slider({ className, value, onValueChange, min = 0, max = 100, step = 1, style, ...props }) {
12
+ var Slider = React.forwardRef(function Slider({ className, value: valueProp, defaultValue, onValueChange, min = 0, max = 100, step = 1, style, ...props }, ref) {
13
+ const [own, setOwn] = React.useState(defaultValue ?? min);
14
+ const value = valueProp ?? own;
12
15
  const percent = max > min ? Math.min(100, Math.max(0, (value - min) / (max - min) * 100)) : 0;
13
16
  return /* @__PURE__ */ jsx("input", {
17
+ ref,
14
18
  type: "range",
15
19
  "data-slot": "slider",
16
20
  min,
17
21
  max,
18
22
  step,
19
23
  value,
20
- onChange: (event) => onValueChange(Number(event.target.value)),
24
+ onChange: (event) => {
25
+ const next = Number(event.target.value);
26
+ if (valueProp === void 0) setOwn(next);
27
+ onValueChange?.(next);
28
+ },
21
29
  style: {
22
30
  ...style,
23
31
  ["--bl-slider-fill"]: `${percent}%`
24
32
  },
25
- className: cn("h-5 w-full min-w-0 cursor-pointer appearance-none bg-transparent outline-none disabled:cursor-not-allowed disabled:opacity-60", "[&::-webkit-slider-runnable-track]:h-1.5 [&::-webkit-slider-runnable-track]:rounded-full", "[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_right,var(--color-brand)_var(--bl-slider-fill),var(--color-muted)_var(--bl-slider-fill))]", "[&::-moz-range-track]:h-1.5 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-muted", "[&::-moz-range-progress]:h-1.5 [&::-moz-range-progress]:rounded-full [&::-moz-range-progress]:bg-brand", "[&::-webkit-slider-thumb]:-mt-[5px] [&::-webkit-slider-thumb]:size-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-brand [&::-webkit-slider-thumb]:bg-background [&::-webkit-slider-thumb]:shadow-sm", "[&::-moz-range-thumb]:size-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-brand [&::-moz-range-thumb]:bg-background", "focus-visible:[&::-webkit-slider-thumb]:ring-[3px] focus-visible:[&::-webkit-slider-thumb]:ring-ring/30", "focus-visible:[&::-moz-range-thumb]:ring-[3px] focus-visible:[&::-moz-range-thumb]:ring-ring/30", className),
33
+ className: cn("h-5 w-full min-w-0 cursor-pointer appearance-none bg-transparent outline-none disabled:cursor-not-allowed disabled:opacity-60 pointer-coarse:h-11", "[&::-webkit-slider-runnable-track]:h-1.5 [&::-webkit-slider-runnable-track]:rounded-full", "[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_right,var(--color-brand)_var(--bl-slider-fill),var(--color-border-strong)_var(--bl-slider-fill))]", "[&::-moz-range-track]:h-1.5 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-border-strong", "[&::-moz-range-progress]:h-1.5 [&::-moz-range-progress]:rounded-full [&::-moz-range-progress]:bg-brand", "[&::-webkit-slider-thumb]:-mt-[5px] [&::-webkit-slider-thumb]:size-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-brand [&::-webkit-slider-thumb]:bg-background [&::-webkit-slider-thumb]:shadow-sm", "[&::-moz-range-thumb]:size-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-brand [&::-moz-range-thumb]:bg-background", "focus-visible:[&::-webkit-slider-thumb]:shadow-[0_0_0_2px_var(--background),0_0_0_4px_var(--ring)]", "focus-visible:[&::-moz-range-thumb]:shadow-[0_0_0_2px_var(--background),0_0_0_4px_var(--ring)]", className),
26
34
  ...props
27
35
  });
28
- }
36
+ });
29
37
  //#endregion
30
38
  export { Slider };
31
39
 
@@ -1 +1 @@
1
- {"version":3,"file":"slider.js","names":[],"sources":["../../src/components/slider.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nexport interface SliderProps extends Omit<\n React.ComponentProps<'input'>,\n 'type' | 'value' | 'onChange'\n> {\n value: number;\n onValueChange: (value: number) => void;\n min?: number;\n max?: number;\n step?: number;\n}\n\n/**\n * A single-value range slider on the native input, so keyboard, touch and\n * screen readers work without extra code. Pass `aria-valuetext` when the raw\n * number isn't meaningful (\"45% change\").\n */\nfunction Slider({\n className,\n value,\n onValueChange,\n min = 0,\n max = 100,\n step = 1,\n style,\n ...props\n}: SliderProps) {\n const percent = max > min ? Math.min(100, Math.max(0, ((value - min) / (max - min)) * 100)) : 0;\n return (\n <input\n type=\"range\"\n data-slot=\"slider\"\n min={min}\n max={max}\n step={step}\n value={value}\n onChange={(event) => onValueChange(Number(event.target.value))}\n style={{ ...style, ['--bl-slider-fill' as string]: `${percent}%` }}\n className={cn(\n 'h-5 w-full min-w-0 cursor-pointer appearance-none bg-transparent outline-none disabled:cursor-not-allowed disabled:opacity-60',\n // Track: brand up to the thumb, muted after it.\n '[&::-webkit-slider-runnable-track]:h-1.5 [&::-webkit-slider-runnable-track]:rounded-full',\n '[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_right,var(--color-brand)_var(--bl-slider-fill),var(--color-muted)_var(--bl-slider-fill))]',\n '[&::-moz-range-track]:h-1.5 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-muted',\n '[&::-moz-range-progress]:h-1.5 [&::-moz-range-progress]:rounded-full [&::-moz-range-progress]:bg-brand',\n '[&::-webkit-slider-thumb]:-mt-[5px] [&::-webkit-slider-thumb]:size-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-brand [&::-webkit-slider-thumb]:bg-background [&::-webkit-slider-thumb]:shadow-sm',\n '[&::-moz-range-thumb]:size-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-brand [&::-moz-range-thumb]:bg-background',\n 'focus-visible:[&::-webkit-slider-thumb]:ring-[3px] focus-visible:[&::-webkit-slider-thumb]:ring-ring/30',\n 'focus-visible:[&::-moz-range-thumb]:ring-[3px] focus-visible:[&::-moz-range-thumb]:ring-ring/30',\n className,\n )}\n {...props}\n />\n );\n}\n\nexport { Slider };\n"],"mappings":";;;;;;;;;;AAoBA,SAAS,OAAO,EACd,WACA,OACA,eACA,MAAM,GACN,MAAM,KACN,OAAO,GACP,OACA,GAAG,SACW;CACd,MAAM,UAAU,MAAM,MAAM,KAAK,IAAI,KAAK,KAAK,IAAI,IAAK,QAAQ,QAAQ,MAAM,OAAQ,GAAG,CAAC,IAAI;CAC9F,OACE,oBAAC,SAAD;EACE,MAAK;EACL,aAAU;EACL;EACA;EACC;EACC;EACP,WAAW,UAAU,cAAc,OAAO,MAAM,OAAO,KAAK,CAAC;EAC7D,OAAO;GAAE,GAAG;IAAQ,qBAA+B,GAAG,QAAQ;EAAG;EACjE,WAAW,GACT,iIAEA,4FACA,uJACA,iGACA,0GACA,+SACA,yKACA,2GACA,mGACA,SACF;EACA,GAAI;CACL,CAAA;AAEL"}
1
+ {"version":3,"file":"slider.js","names":[],"sources":["../../src/components/slider.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nexport interface SliderProps extends Omit<\n React.ComponentProps<'input'>,\n 'type' | 'value' | 'defaultValue' | 'onChange'\n> {\n /** Controlled value. Leave out for an uncontrolled slider. */\n value?: number;\n /** The starting value when uncontrolled. */\n defaultValue?: number;\n onValueChange?: (value: number) => void;\n min?: number;\n max?: number;\n step?: number;\n}\n\n/**\n * A single-value range slider on the native input, so keyboard, touch and\n * screen readers work without extra code. The track is 6px; the thumb 16px,\n * with the whole 44px-tall input as its target on touch screens. Pass\n * `aria-valuetext` when the raw number isn't meaningful (\"45% change\").\n */\nconst Slider = React.forwardRef<HTMLInputElement, SliderProps>(function Slider(\n {\n className,\n value: valueProp,\n defaultValue,\n onValueChange,\n min = 0,\n max = 100,\n step = 1,\n style,\n ...props\n },\n ref,\n) {\n const [own, setOwn] = React.useState(defaultValue ?? min);\n const value = valueProp ?? own;\n const percent = max > min ? Math.min(100, Math.max(0, ((value - min) / (max - min)) * 100)) : 0;\n return (\n <input\n ref={ref}\n type=\"range\"\n data-slot=\"slider\"\n min={min}\n max={max}\n step={step}\n value={value}\n onChange={(event) => {\n const next = Number(event.target.value);\n if (valueProp === undefined) setOwn(next);\n onValueChange?.(next);\n }}\n style={{ ...style, ['--bl-slider-fill' as string]: `${percent}%` }}\n className={cn(\n 'h-5 w-full min-w-0 cursor-pointer appearance-none bg-transparent outline-none disabled:cursor-not-allowed disabled:opacity-60 pointer-coarse:h-11',\n // Track: brand up to the thumb, the strong border tone after it.\n '[&::-webkit-slider-runnable-track]:h-1.5 [&::-webkit-slider-runnable-track]:rounded-full',\n '[&::-webkit-slider-runnable-track]:bg-[linear-gradient(to_right,var(--color-brand)_var(--bl-slider-fill),var(--color-border-strong)_var(--bl-slider-fill))]',\n '[&::-moz-range-track]:h-1.5 [&::-moz-range-track]:rounded-full [&::-moz-range-track]:bg-border-strong',\n '[&::-moz-range-progress]:h-1.5 [&::-moz-range-progress]:rounded-full [&::-moz-range-progress]:bg-brand',\n '[&::-webkit-slider-thumb]:-mt-[5px] [&::-webkit-slider-thumb]:size-4 [&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:border-2 [&::-webkit-slider-thumb]:border-brand [&::-webkit-slider-thumb]:bg-background [&::-webkit-slider-thumb]:shadow-sm',\n '[&::-moz-range-thumb]:size-4 [&::-moz-range-thumb]:rounded-full [&::-moz-range-thumb]:border-2 [&::-moz-range-thumb]:border-brand [&::-moz-range-thumb]:bg-background',\n // Focus: the 2px ring-colour outline, 2px out, drawn as shadows on the\n // thumb (outlines on range thumbs are not reliable across engines).\n 'focus-visible:[&::-webkit-slider-thumb]:shadow-[0_0_0_2px_var(--background),0_0_0_4px_var(--ring)]',\n 'focus-visible:[&::-moz-range-thumb]:shadow-[0_0_0_2px_var(--background),0_0_0_4px_var(--ring)]',\n className,\n )}\n {...props}\n />\n );\n});\n\nexport { Slider };\n"],"mappings":";;;;;;;;;;;AAwBA,IAAM,SAAS,MAAM,WAA0C,SAAS,OACtE,EACE,WACA,OAAO,WACP,cACA,eACA,MAAM,GACN,MAAM,KACN,OAAO,GACP,OACA,GAAG,SAEL,KACA;CACA,MAAM,CAAC,KAAK,UAAU,MAAM,SAAS,gBAAgB,GAAG;CACxD,MAAM,QAAQ,aAAa;CAC3B,MAAM,UAAU,MAAM,MAAM,KAAK,IAAI,KAAK,KAAK,IAAI,IAAK,QAAQ,QAAQ,MAAM,OAAQ,GAAG,CAAC,IAAI;CAC9F,OACE,oBAAC,SAAD;EACO;EACL,MAAK;EACL,aAAU;EACL;EACA;EACC;EACC;EACP,WAAW,UAAU;GACnB,MAAM,OAAO,OAAO,MAAM,OAAO,KAAK;GACtC,IAAI,cAAc,KAAA,GAAW,OAAO,IAAI;GACxC,gBAAgB,IAAI;EACtB;EACA,OAAO;GAAE,GAAG;IAAQ,qBAA+B,GAAG,QAAQ;EAAG;EACjE,WAAW,GACT,qJAEA,4FACA,+JACA,yGACA,0GACA,+SACA,yKAGA,sGACA,kGACA,SACF;EACA,GAAI;CACL,CAAA;AAEL,CAAC"}
@@ -20,12 +20,12 @@ export interface SourceCitationProps extends Omit<React.ComponentProps<typeof Po
20
20
  source: Source;
21
21
  }
22
22
  /** An inline [n] marker in an answer; opens the source it points to. */
23
- declare function SourceCitation({ index, source, className, ...props }: SourceCitationProps): React.JSX.Element;
23
+ declare const SourceCitation: React.ForwardRefExoticComponent<Omit<SourceCitationProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
24
24
  export interface SourceListProps extends React.ComponentProps<'section'> {
25
25
  sources: Source[];
26
26
  /** Heading; also the list's accessible name. */
27
27
  label?: string;
28
28
  }
29
29
  /** Numbered sources under an answer, in citation order. */
30
- declare function SourceList({ sources, label, className, ...props }: SourceListProps): React.JSX.Element | null;
30
+ declare const SourceList: React.ForwardRefExoticComponent<Omit<SourceListProps, "ref"> & React.RefAttributes<HTMLElement>>;
31
31
  export { SourceCitation, SourceList };
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { cn } from "../lib/utils.js";
2
+ import { cn, focusRingClasses } from "../lib/utils.js";
3
3
  import { Popover, PopoverContent, PopoverTrigger } from "./popover.js";
4
4
  import * as React from "react";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -45,7 +45,7 @@ function SourceBody({ source, index }) {
45
45
  href,
46
46
  target: "_blank",
47
47
  rel: "noopener noreferrer",
48
- className: "min-w-0 font-medium text-foreground underline-offset-2 outline-none hover:underline focus-visible:underline",
48
+ className: cn("min-w-0 rounded-xs font-medium text-foreground underline-offset-2 hover:underline", focusRingClasses),
49
49
  children: [/* @__PURE__ */ jsx("span", {
50
50
  className: "line-clamp-2",
51
51
  children: source.title
@@ -75,11 +75,12 @@ function SourceBody({ source, index }) {
75
75
  });
76
76
  }
77
77
  /** An inline [n] marker in an answer; opens the source it points to. */
78
- function SourceCitation({ index, source, className, ...props }) {
78
+ var SourceCitation = React.forwardRef(function SourceCitation({ index, source, className, ...props }, ref) {
79
79
  return /* @__PURE__ */ jsxs(Popover, { children: [/* @__PURE__ */ jsx(PopoverTrigger, {
80
+ ref,
80
81
  "data-slot": "source-citation",
81
82
  "aria-label": `Source ${index}: ${source.title}`,
82
- className: cn("mx-0.5 inline-grid h-4 min-w-4 translate-y-[-0.1em] place-items-center rounded-xs border bg-surface-muted px-1 align-baseline font-mono text-[10px] leading-none font-semibold text-muted-foreground tabular-nums transition-colors outline-none hover:border-brand/40 hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/20 data-[state=open]:border-brand data-[state=open]:text-foreground", className),
83
+ className: cn("mx-0.5 inline-grid h-4 min-w-4 translate-y-[-0.1em] place-items-center rounded-xs border bg-surface-muted px-1 align-baseline font-mono text-[10px] leading-none font-semibold text-muted-foreground tabular-nums transition-colors hover:border-brand/40 hover:text-foreground data-[state=open]:border-brand data-[state=open]:text-foreground", focusRingClasses, className),
83
84
  ...props,
84
85
  children: index
85
86
  }), /* @__PURE__ */ jsx(PopoverContent, {
@@ -88,12 +89,13 @@ function SourceCitation({ index, source, className, ...props }) {
88
89
  className: "w-80 p-3 text-sm",
89
90
  children: /* @__PURE__ */ jsx(SourceBody, { source })
90
91
  })] });
91
- }
92
+ });
92
93
  /** Numbered sources under an answer, in citation order. */
93
- function SourceList({ sources, label = "Sources", className, ...props }) {
94
+ var SourceList = React.forwardRef(function SourceList({ sources, label = "Sources", className, ...props }, ref) {
94
95
  const headingId = React.useId();
95
96
  if (sources.length === 0) return null;
96
97
  return /* @__PURE__ */ jsxs("section", {
98
+ ref,
97
99
  "data-slot": "source-list",
98
100
  "aria-labelledby": headingId,
99
101
  className: cn("grid gap-2", className),
@@ -113,7 +115,7 @@ function SourceList({ sources, label = "Sources", className, ...props }) {
113
115
  }, source.id))
114
116
  })]
115
117
  });
116
- }
118
+ });
117
119
  //#endregion
118
120
  export { SourceCitation, SourceList, safeSourceHref };
119
121
 
@@ -1 +1 @@
1
- {"version":3,"file":"source.js","names":[],"sources":["../../src/components/source.tsx"],"sourcesContent":["import ExternalLink from '@burtson-labs/icons/react/external-link';\nimport FileText from '@burtson-labs/icons/react/file-text';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nimport { Popover, PopoverContent, PopoverTrigger } from './popover';\n\n/** Where an answer came from. The app supplies provenance; this only shows it. */\nexport interface Source {\n id: string;\n title: string;\n /** Only http(s) links are rendered as links. */\n url?: string;\n /** The passage the answer relied on. */\n snippet?: string;\n /** Short location, e.g. \"page 4\" or \"Updated 2 days ago\". */\n meta?: string;\n icon?: React.ReactNode;\n}\n\n/** An http(s) URL, or undefined for anything else (javascript:, data:, relative). */\nexport function safeSourceHref(url: string | undefined): string | undefined {\n if (!url) return undefined;\n try {\n const parsed = new URL(url);\n return parsed.protocol === 'https:' || parsed.protocol === 'http:' ? parsed.href : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction hostOf(href: string | undefined): string | undefined {\n if (!href) return undefined;\n try {\n return new URL(href).hostname.replace(/^www\\./, '');\n } catch {\n return undefined;\n }\n}\n\nfunction SourceBody({ source, index }: { source: Source; index?: number }) {\n const href = safeSourceHref(source.url);\n const host = hostOf(href);\n const location = [host, source.meta].filter(Boolean).join(' · ');\n return (\n <div className=\"grid min-w-0 gap-1\">\n <div className=\"flex min-w-0 items-start gap-2\">\n {index !== undefined && (\n <span className=\"mt-px grid h-5 min-w-5 shrink-0 place-items-center rounded-sm border bg-surface-muted px-1 font-mono text-[11px] font-semibold text-muted-foreground tabular-nums\">\n {index}\n </span>\n )}\n <span className=\"mt-0.5 shrink-0 text-muted-foreground [&_svg]:size-4\">\n {source.icon ?? <FileText aria-hidden />}\n </span>\n {href ? (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"min-w-0 font-medium text-foreground underline-offset-2 outline-none hover:underline focus-visible:underline\"\n >\n <span className=\"line-clamp-2\">{source.title}</span>\n <span className=\"sr-only\"> (opens in a new tab)</span>\n </a>\n ) : (\n <span className=\"line-clamp-2 min-w-0 font-medium text-foreground\">{source.title}</span>\n )}\n {href && (\n <ExternalLink className=\"mt-1 size-3 shrink-0 text-muted-foreground\" aria-hidden />\n )}\n </div>\n {location && <p className=\"truncate text-xs text-muted-foreground\">{location}</p>}\n {source.snippet && (\n <blockquote className=\"line-clamp-3 border-l-2 pl-2 text-xs leading-5 text-muted-foreground\">\n {source.snippet}\n </blockquote>\n )}\n </div>\n );\n}\n\nexport interface SourceCitationProps extends Omit<\n React.ComponentProps<typeof PopoverTrigger>,\n 'children'\n> {\n /** 1-based number shown in the text; matches the SourceList entry. */\n index: number;\n source: Source;\n}\n\n/** An inline [n] marker in an answer; opens the source it points to. */\nfunction SourceCitation({ index, source, className, ...props }: SourceCitationProps) {\n return (\n <Popover>\n <PopoverTrigger\n data-slot=\"source-citation\"\n aria-label={`Source ${index}: ${source.title}`}\n className={cn(\n 'mx-0.5 inline-grid h-4 min-w-4 translate-y-[-0.1em] place-items-center rounded-xs border bg-surface-muted px-1 align-baseline font-mono text-[10px] leading-none font-semibold text-muted-foreground tabular-nums transition-colors outline-none hover:border-brand/40 hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/20 data-[state=open]:border-brand data-[state=open]:text-foreground',\n className,\n )}\n {...props}\n >\n {index}\n </PopoverTrigger>\n <PopoverContent side=\"top\" align=\"start\" className=\"w-80 p-3 text-sm\">\n <SourceBody source={source} />\n </PopoverContent>\n </Popover>\n );\n}\n\nexport interface SourceListProps extends React.ComponentProps<'section'> {\n sources: Source[];\n /** Heading; also the list's accessible name. */\n label?: string;\n}\n\n/** Numbered sources under an answer, in citation order. */\nfunction SourceList({ sources, label = 'Sources', className, ...props }: SourceListProps) {\n const headingId = React.useId();\n if (sources.length === 0) return null;\n return (\n <section\n data-slot=\"source-list\"\n aria-labelledby={headingId}\n className={cn('grid gap-2', className)}\n {...props}\n >\n <h3\n id={headingId}\n className=\"text-[11px] font-semibold tracking-[0.06em] text-muted-foreground uppercase\"\n >\n {label}\n </h3>\n <ol className=\"grid gap-1.5 sm:grid-cols-2\">\n {sources.map((source, i) => (\n <li\n key={source.id}\n className=\"min-w-0 rounded-md border bg-surface p-2.5 text-sm transition-colors hover:border-border-strong\"\n >\n <SourceBody source={source} index={i + 1} />\n </li>\n ))}\n </ol>\n </section>\n );\n}\n\nexport { SourceCitation, SourceList };\n"],"mappings":";;;;;;;;;AAsBA,SAAgB,eAAe,KAA6C;CAC1E,IAAI,CAAC,KAAK,OAAO,KAAA;CACjB,IAAI;EACF,MAAM,SAAS,IAAI,IAAI,GAAG;EAC1B,OAAO,OAAO,aAAa,YAAY,OAAO,aAAa,UAAU,OAAO,OAAO,KAAA;CACrF,QAAQ;EACN;CACF;AACF;AAEA,SAAS,OAAO,MAA8C;CAC5D,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,IAAI;EACF,OAAO,IAAI,IAAI,IAAI,CAAC,CAAC,SAAS,QAAQ,UAAU,EAAE;CACpD,QAAQ;EACN;CACF;AACF;AAEA,SAAS,WAAW,EAAE,QAAQ,SAA6C;CACzE,MAAM,OAAO,eAAe,OAAO,GAAG;CAEtC,MAAM,WAAW,CADJ,OAAO,IACF,GAAM,OAAO,IAAI,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,KAAK;CAC/D,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACE,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACG,UAAU,KAAA,KACT,oBAAC,QAAD;MAAM,WAAU;MACb,UAAA;KACG,CAAA;KAER,oBAAC,QAAD;MAAM,WAAU;MACb,UAAA,OAAO,QAAQ,oBAAC,UAAD,EAAU,eAAA,KAAa,CAAA;KACnC,CAAA;KACL,OACC,qBAAC,KAAD;MACQ;MACN,QAAO;MACP,KAAI;MACJ,WAAU;MAJZ,UAAA,CAME,oBAAC,QAAD;OAAM,WAAU;OAAgB,UAAA,OAAO;MAAY,CAAA,GACnD,oBAAC,QAAD;OAAM,WAAU;OAAU,UAAA;MAA2B,CAAA,CACpD;KAEH,CAAA,IAAA,oBAAC,QAAD;MAAM,WAAU;MAAoD,UAAA,OAAO;KAAY,CAAA;KAExF,QACC,oBAAC,cAAD;MAAc,WAAU;MAA6C,eAAA;KAAa,CAAA;IAEjF;;GACJ,YAAY,oBAAC,KAAD;IAAG,WAAU;IAA0C,UAAA;GAAY,CAAA;GAC/E,OAAO,WACN,oBAAC,cAAD;IAAY,WAAU;IACnB,UAAA,OAAO;GACE,CAAA;EAEX;;AAET;;AAYA,SAAS,eAAe,EAAE,OAAO,QAAQ,WAAW,GAAG,SAA8B;CACnF,OACE,qBAAC,SAAD,EAAA,UAAA,CACE,oBAAC,gBAAD;EACE,aAAU;EACV,cAAY,UAAU,MAAM,IAAI,OAAO;EACvC,WAAW,GACT,qZACA,SACF;EACA,GAAI;EAEH,UAAA;CACa,CAAA,GAChB,oBAAC,gBAAD;EAAgB,MAAK;EAAM,OAAM;EAAQ,WAAU;EACjD,UAAA,oBAAC,YAAD,EAAoB,OAAS,CAAA;CACf,CAAA,CACT,EAAA,CAAA;AAEb;;AASA,SAAS,WAAW,EAAE,SAAS,QAAQ,WAAW,WAAW,GAAG,SAA0B;CACxF,MAAM,YAAY,MAAM,MAAM;CAC9B,IAAI,QAAQ,WAAW,GAAG,OAAO;CACjC,OACE,qBAAC,WAAD;EACE,aAAU;EACV,mBAAiB;EACjB,WAAW,GAAG,cAAc,SAAS;EACrC,GAAI;EAJN,UAAA,CAME,oBAAC,MAAD;GACE,IAAI;GACJ,WAAU;GAET,UAAA;EACC,CAAA,GACJ,oBAAC,MAAD;GAAI,WAAU;GACX,UAAA,QAAQ,KAAK,QAAQ,MACpB,oBAAC,MAAD;IAEE,WAAU;IAEV,UAAA,oBAAC,YAAD;KAAoB;KAAQ,OAAO,IAAI;IAAI,CAAA;GACzC,GAJG,OAAO,EAIV,CACL;EACC,CAAA,CACG;;AAEb"}
1
+ {"version":3,"file":"source.js","names":[],"sources":["../../src/components/source.tsx"],"sourcesContent":["import ExternalLink from '@burtson-labs/icons/react/external-link';\nimport FileText from '@burtson-labs/icons/react/file-text';\nimport * as React from 'react';\n\nimport { cn, focusRingClasses } from '../lib/utils';\n\nimport { Popover, PopoverContent, PopoverTrigger } from './popover';\n\n/** Where an answer came from. The app supplies provenance; this only shows it. */\nexport interface Source {\n id: string;\n title: string;\n /** Only http(s) links are rendered as links. */\n url?: string;\n /** The passage the answer relied on. */\n snippet?: string;\n /** Short location, e.g. \"page 4\" or \"Updated 2 days ago\". */\n meta?: string;\n icon?: React.ReactNode;\n}\n\n/** An http(s) URL, or undefined for anything else (javascript:, data:, relative). */\nexport function safeSourceHref(url: string | undefined): string | undefined {\n if (!url) return undefined;\n try {\n const parsed = new URL(url);\n return parsed.protocol === 'https:' || parsed.protocol === 'http:' ? parsed.href : undefined;\n } catch {\n return undefined;\n }\n}\n\nfunction hostOf(href: string | undefined): string | undefined {\n if (!href) return undefined;\n try {\n return new URL(href).hostname.replace(/^www\\./, '');\n } catch {\n return undefined;\n }\n}\n\nfunction SourceBody({ source, index }: { source: Source; index?: number }) {\n const href = safeSourceHref(source.url);\n const host = hostOf(href);\n const location = [host, source.meta].filter(Boolean).join(' · ');\n return (\n <div className=\"grid min-w-0 gap-1\">\n <div className=\"flex min-w-0 items-start gap-2\">\n {index !== undefined && (\n <span className=\"mt-px grid h-5 min-w-5 shrink-0 place-items-center rounded-sm border bg-surface-muted px-1 font-mono text-[11px] font-semibold text-muted-foreground tabular-nums\">\n {index}\n </span>\n )}\n <span className=\"mt-0.5 shrink-0 text-muted-foreground [&_svg]:size-4\">\n {source.icon ?? <FileText aria-hidden />}\n </span>\n {href ? (\n <a\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={cn(\n 'min-w-0 rounded-xs font-medium text-foreground underline-offset-2 hover:underline',\n focusRingClasses,\n )}\n >\n <span className=\"line-clamp-2\">{source.title}</span>\n <span className=\"sr-only\"> (opens in a new tab)</span>\n </a>\n ) : (\n <span className=\"line-clamp-2 min-w-0 font-medium text-foreground\">{source.title}</span>\n )}\n {href && (\n <ExternalLink className=\"mt-1 size-3 shrink-0 text-muted-foreground\" aria-hidden />\n )}\n </div>\n {location && <p className=\"truncate text-xs text-muted-foreground\">{location}</p>}\n {source.snippet && (\n <blockquote className=\"line-clamp-3 border-l-2 pl-2 text-xs leading-5 text-muted-foreground\">\n {source.snippet}\n </blockquote>\n )}\n </div>\n );\n}\n\nexport interface SourceCitationProps extends Omit<\n React.ComponentProps<typeof PopoverTrigger>,\n 'children'\n> {\n /** 1-based number shown in the text; matches the SourceList entry. */\n index: number;\n source: Source;\n}\n\n/** An inline [n] marker in an answer; opens the source it points to. */\nconst SourceCitation = React.forwardRef<HTMLButtonElement, SourceCitationProps>(\n function SourceCitation({ index, source, className, ...props }, ref) {\n return (\n <Popover>\n <PopoverTrigger\n ref={ref}\n data-slot=\"source-citation\"\n aria-label={`Source ${index}: ${source.title}`}\n className={cn(\n 'mx-0.5 inline-grid h-4 min-w-4 translate-y-[-0.1em] place-items-center rounded-xs border bg-surface-muted px-1 align-baseline font-mono text-[10px] leading-none font-semibold text-muted-foreground tabular-nums transition-colors hover:border-brand/40 hover:text-foreground data-[state=open]:border-brand data-[state=open]:text-foreground',\n focusRingClasses,\n className,\n )}\n {...props}\n >\n {index}\n </PopoverTrigger>\n <PopoverContent side=\"top\" align=\"start\" className=\"w-80 p-3 text-sm\">\n <SourceBody source={source} />\n </PopoverContent>\n </Popover>\n );\n },\n);\n\nexport interface SourceListProps extends React.ComponentProps<'section'> {\n sources: Source[];\n /** Heading; also the list's accessible name. */\n label?: string;\n}\n\n/** Numbered sources under an answer, in citation order. */\nconst SourceList = React.forwardRef<HTMLElement, SourceListProps>(function SourceList(\n { sources, label = 'Sources', className, ...props },\n ref,\n) {\n const headingId = React.useId();\n if (sources.length === 0) return null;\n return (\n <section\n ref={ref}\n data-slot=\"source-list\"\n aria-labelledby={headingId}\n className={cn('grid gap-2', className)}\n {...props}\n >\n <h3\n id={headingId}\n className=\"text-[11px] font-semibold tracking-[0.06em] text-muted-foreground uppercase\"\n >\n {label}\n </h3>\n <ol className=\"grid gap-1.5 sm:grid-cols-2\">\n {sources.map((source, i) => (\n <li\n key={source.id}\n className=\"min-w-0 rounded-md border bg-surface p-2.5 text-sm transition-colors hover:border-border-strong\"\n >\n <SourceBody source={source} index={i + 1} />\n </li>\n ))}\n </ol>\n </section>\n );\n});\n\nexport { SourceCitation, SourceList };\n"],"mappings":";;;;;;;;;AAsBA,SAAgB,eAAe,KAA6C;CAC1E,IAAI,CAAC,KAAK,OAAO,KAAA;CACjB,IAAI;EACF,MAAM,SAAS,IAAI,IAAI,GAAG;EAC1B,OAAO,OAAO,aAAa,YAAY,OAAO,aAAa,UAAU,OAAO,OAAO,KAAA;CACrF,QAAQ;EACN;CACF;AACF;AAEA,SAAS,OAAO,MAA8C;CAC5D,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,IAAI;EACF,OAAO,IAAI,IAAI,IAAI,CAAC,CAAC,SAAS,QAAQ,UAAU,EAAE;CACpD,QAAQ;EACN;CACF;AACF;AAEA,SAAS,WAAW,EAAE,QAAQ,SAA6C;CACzE,MAAM,OAAO,eAAe,OAAO,GAAG;CAEtC,MAAM,WAAW,CADJ,OAAO,IACF,GAAM,OAAO,IAAI,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,KAAK;CAC/D,OACE,qBAAC,OAAD;EAAK,WAAU;EAAf,UAAA;GACE,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACG,UAAU,KAAA,KACT,oBAAC,QAAD;MAAM,WAAU;MACb,UAAA;KACG,CAAA;KAER,oBAAC,QAAD;MAAM,WAAU;MACb,UAAA,OAAO,QAAQ,oBAAC,UAAD,EAAU,eAAA,KAAa,CAAA;KACnC,CAAA;KACL,OACC,qBAAC,KAAD;MACQ;MACN,QAAO;MACP,KAAI;MACJ,WAAW,GACT,qFACA,gBACF;MAPF,UAAA,CASE,oBAAC,QAAD;OAAM,WAAU;OAAgB,UAAA,OAAO;MAAY,CAAA,GACnD,oBAAC,QAAD;OAAM,WAAU;OAAU,UAAA;MAA2B,CAAA,CACpD;KAEH,CAAA,IAAA,oBAAC,QAAD;MAAM,WAAU;MAAoD,UAAA,OAAO;KAAY,CAAA;KAExF,QACC,oBAAC,cAAD;MAAc,WAAU;MAA6C,eAAA;KAAa,CAAA;IAEjF;;GACJ,YAAY,oBAAC,KAAD;IAAG,WAAU;IAA0C,UAAA;GAAY,CAAA;GAC/E,OAAO,WACN,oBAAC,cAAD;IAAY,WAAU;IACnB,UAAA,OAAO;GACE,CAAA;EAEX;;AAET;;AAYA,IAAM,iBAAiB,MAAM,WAC3B,SAAS,eAAe,EAAE,OAAO,QAAQ,WAAW,GAAG,SAAS,KAAK;CACnE,OACE,qBAAC,SAAD,EAAA,UAAA,CACE,oBAAC,gBAAD;EACO;EACL,aAAU;EACV,cAAY,UAAU,MAAM,IAAI,OAAO;EACvC,WAAW,GACT,oVACA,kBACA,SACF;EACA,GAAI;EAEH,UAAA;CACa,CAAA,GAChB,oBAAC,gBAAD;EAAgB,MAAK;EAAM,OAAM;EAAQ,WAAU;EACjD,UAAA,oBAAC,YAAD,EAAoB,OAAS,CAAA;CACf,CAAA,CACT,EAAA,CAAA;AAEb,CACF;;AASA,IAAM,aAAa,MAAM,WAAyC,SAAS,WACzE,EAAE,SAAS,QAAQ,WAAW,WAAW,GAAG,SAC5C,KACA;CACA,MAAM,YAAY,MAAM,MAAM;CAC9B,IAAI,QAAQ,WAAW,GAAG,OAAO;CACjC,OACE,qBAAC,WAAD;EACO;EACL,aAAU;EACV,mBAAiB;EACjB,WAAW,GAAG,cAAc,SAAS;EACrC,GAAI;EALN,UAAA,CAOE,oBAAC,MAAD;GACE,IAAI;GACJ,WAAU;GAET,UAAA;EACC,CAAA,GACJ,oBAAC,MAAD;GAAI,WAAU;GACX,UAAA,QAAQ,KAAK,QAAQ,MACpB,oBAAC,MAAD;IAEE,WAAU;IAEV,UAAA,oBAAC,YAAD;KAAoB;KAAQ,OAAO,IAAI;IAAI,CAAA;GACzC,GAJG,OAAO,EAIV,CACL;EACC,CAAA,CACG;;AAEb,CAAC"}
@@ -1,6 +1,8 @@
1
1
  import Loader from '@burtson-labs/icons/react/loader';
2
2
  import * as React from 'react';
3
- declare function Spinner({ className, label, ...props }: React.ComponentProps<typeof Loader> & {
3
+ export interface SpinnerProps extends React.ComponentProps<typeof Loader> {
4
+ /** What is loading, for screen readers. */
4
5
  label?: string;
5
- }): React.JSX.Element;
6
+ }
7
+ declare const Spinner: React.ForwardRefExoticComponent<Omit<SpinnerProps, "ref"> & React.RefAttributes<SVGSVGElement>>;
6
8
  export { Spinner };
@@ -1,18 +1,19 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/utils.js";
3
- import "react";
3
+ import * as React from "react";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  import Loader from "@burtson-labs/icons/react/loader";
6
6
  //#region src/components/spinner.tsx
7
- function Spinner({ className, label = "Loading", ...props }) {
7
+ var Spinner = React.forwardRef(function Spinner({ className, label = "Loading", ...props }, ref) {
8
8
  return /* @__PURE__ */ jsx(Loader, {
9
+ ref,
9
10
  role: "status",
10
11
  "aria-label": label,
11
12
  "data-slot": "spinner",
12
13
  className: cn("size-4 animate-spin", className),
13
14
  ...props
14
15
  });
15
- }
16
+ });
16
17
  //#endregion
17
18
  export { Spinner };
18
19
 
@@ -1 +1 @@
1
- {"version":3,"file":"spinner.js","names":[],"sources":["../../src/components/spinner.tsx"],"sourcesContent":["import Loader from '@burtson-labs/icons/react/loader';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nfunction Spinner({\n className,\n label = 'Loading',\n ...props\n}: React.ComponentProps<typeof Loader> & { label?: string }) {\n return (\n <Loader\n role=\"status\"\n aria-label={label}\n data-slot=\"spinner\"\n className={cn('size-4 animate-spin', className)}\n {...props}\n />\n );\n}\n\nexport { Spinner };\n"],"mappings":";;;;;;AAKA,SAAS,QAAQ,EACf,WACA,QAAQ,WACR,GAAG,SACwD;CAC3D,OACE,oBAAC,QAAD;EACE,MAAK;EACL,cAAY;EACZ,aAAU;EACV,WAAW,GAAG,uBAAuB,SAAS;EAC9C,GAAI;CACL,CAAA;AAEL"}
1
+ {"version":3,"file":"spinner.js","names":[],"sources":["../../src/components/spinner.tsx"],"sourcesContent":["import Loader from '@burtson-labs/icons/react/loader';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nexport interface SpinnerProps extends React.ComponentProps<typeof Loader> {\n /** What is loading, for screen readers. */\n label?: string;\n}\n\nconst Spinner = React.forwardRef<SVGSVGElement, SpinnerProps>(function Spinner(\n { className, label = 'Loading', ...props },\n ref,\n) {\n return (\n <Loader\n ref={ref}\n role=\"status\"\n aria-label={label}\n data-slot=\"spinner\"\n className={cn('size-4 animate-spin', className)}\n {...props}\n />\n );\n});\n\nexport { Spinner };\n"],"mappings":";;;;;;AAUA,IAAM,UAAU,MAAM,WAAwC,SAAS,QACrE,EAAE,WAAW,QAAQ,WAAW,GAAG,SACnC,KACA;CACA,OACE,oBAAC,QAAD;EACO;EACL,MAAK;EACL,cAAY;EACZ,aAAU;EACV,WAAW,GAAG,uBAAuB,SAAS;EAC9C,GAAI;CACL,CAAA;AAEL,CAAC"}
@@ -7,5 +7,30 @@ export interface StatCardProps extends React.ComponentProps<typeof Card> {
7
7
  detail?: React.ReactNode;
8
8
  trend?: React.ReactNode;
9
9
  }
10
- declare function StatCard({ label, value, icon, detail, trend, className, ...props }: StatCardProps): React.JSX.Element;
11
- export { StatCard };
10
+ /** One metric with context. A row of them on a phone is better as a StatStrip. */
11
+ declare const StatCard: React.ForwardRefExoticComponent<Omit<StatCardProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
12
+ export interface StatStripItem {
13
+ label: React.ReactNode;
14
+ value: React.ReactNode;
15
+ detail?: React.ReactNode;
16
+ /** Makes the figure a link to this page. */
17
+ href?: string;
18
+ /** Makes the figure a button. */
19
+ onSelect?: () => void;
20
+ }
21
+ export interface StatStripProps extends Omit<React.ComponentProps<typeof Card>, 'children'> {
22
+ /** Two to four figures. */
23
+ stats: StatStripItem[];
24
+ /**
25
+ * Renders each linked figure through your router's link: it receives
26
+ * `href` and the figure's content as children.
27
+ */
28
+ renderLink?: (href: string, children: React.ReactNode, className: string) => React.ReactNode;
29
+ }
30
+ /**
31
+ * Up to four headline figures in one card, side by side: the phone form of a
32
+ * row of StatCards, which would otherwise stack into a screen of boxes. A
33
+ * figure with `href` or `onSelect` is a target.
34
+ */
35
+ declare const StatStrip: React.ForwardRefExoticComponent<Omit<StatStripProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
36
+ export { StatCard, StatStrip };
@@ -1,12 +1,15 @@
1
1
  "use client";
2
- import { cn } from "../lib/utils.js";
2
+ import { cn, focusRingClasses } from "../lib/utils.js";
3
3
  import { Card } from "./card.js";
4
- import "react";
5
- import { jsx, jsxs } from "react/jsx-runtime";
4
+ import * as React from "react";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region src/components/stat-card.tsx
7
- function StatCard({ label, value, icon, detail, trend, className, ...props }) {
7
+ /** One metric with context. A row of them on a phone is better as a StatStrip. */
8
+ var StatCard = React.forwardRef(function StatCard({ label, value, icon, detail, trend, className, ...props }, ref) {
8
9
  return /* @__PURE__ */ jsxs(Card, {
10
+ ref,
9
11
  density: "compact",
12
+ "data-slot": "stat-card",
10
13
  className: cn("min-w-0", className),
11
14
  ...props,
12
15
  children: [/* @__PURE__ */ jsxs("div", {
@@ -15,29 +18,86 @@ function StatCard({ label, value, icon, detail, trend, className, ...props }) {
15
18
  className: "min-w-0",
16
19
  children: [
17
20
  /* @__PURE__ */ jsx("div", {
18
- className: "text-[11px] font-semibold tracking-[0.08em] text-muted-foreground uppercase",
21
+ "data-slot": "stat-card-label",
22
+ className: "text-[11px] font-semibold tracking-[0.06em] text-muted-foreground uppercase",
19
23
  children: label
20
24
  }),
21
25
  /* @__PURE__ */ jsx("div", {
22
- className: "mt-1 truncate text-2xl font-semibold tracking-[-0.035em] text-foreground",
26
+ "data-slot": "stat-card-value",
27
+ className: "mt-1 truncate text-2xl font-semibold tracking-[-0.035em] text-foreground tabular-nums",
23
28
  children: value
24
29
  }),
25
30
  detail ? /* @__PURE__ */ jsx("div", {
31
+ "data-slot": "stat-card-detail",
26
32
  className: "mt-1 text-xs text-muted-foreground",
27
33
  children: detail
28
34
  }) : null
29
35
  ]
30
36
  }), icon ? /* @__PURE__ */ jsx("div", {
31
- className: "grid size-9 shrink-0 place-items-center rounded-lg border border-brand/15 bg-brand-soft text-brand [&_svg]:size-4.5",
37
+ className: "grid size-9 shrink-0 place-items-center rounded-lg border border-brand/15 bg-brand-soft text-brand [&_svg]:size-4",
32
38
  children: icon
33
39
  }) : null]
34
40
  }), trend ? /* @__PURE__ */ jsx("div", {
41
+ "data-slot": "stat-card-trend",
35
42
  className: "border-t border-border px-4 py-2.5 text-xs",
36
43
  children: trend
37
44
  }) : null]
38
45
  });
39
- }
46
+ });
47
+ /**
48
+ * Up to four headline figures in one card, side by side: the phone form of a
49
+ * row of StatCards, which would otherwise stack into a screen of boxes. A
50
+ * figure with `href` or `onSelect` is a target.
51
+ */
52
+ var StatStrip = React.forwardRef(function StatStrip({ stats, renderLink, className, ...props }, ref) {
53
+ const cell = cn("grid min-w-0 content-start px-3 py-3 text-left first:rounded-l-[inherit] last:rounded-r-[inherit]", focusRingClasses, "focus-visible:outline-offset-[-2px]");
54
+ const interactive = cn(cell, "transition-colors hover:bg-muted/60 active:bg-muted");
55
+ return /* @__PURE__ */ jsx(Card, {
56
+ ref,
57
+ "data-slot": "stat-strip",
58
+ className: cn("gap-0 py-0", className),
59
+ ...props,
60
+ children: /* @__PURE__ */ jsx("div", {
61
+ className: "grid divide-x divide-border",
62
+ style: { gridTemplateColumns: `repeat(${Math.max(1, stats.length)}, minmax(0, 1fr))` },
63
+ children: stats.map((s, i) => {
64
+ const body = /* @__PURE__ */ jsxs(Fragment, { children: [
65
+ /* @__PURE__ */ jsx("span", {
66
+ className: "text-[10.5px] leading-4 font-semibold tracking-[0.06em] text-muted-foreground uppercase",
67
+ children: s.label
68
+ }),
69
+ /* @__PURE__ */ jsx("span", {
70
+ className: "mt-1 text-xl font-semibold tracking-[-0.03em] tabular-nums",
71
+ children: s.value
72
+ }),
73
+ s.detail && /* @__PURE__ */ jsx("span", {
74
+ className: "mt-0.5 text-[11.5px] leading-4 text-muted-foreground",
75
+ children: s.detail
76
+ })
77
+ ] });
78
+ if (s.href) return /* @__PURE__ */ jsx(React.Fragment, { children: renderLink ? renderLink(s.href, body, interactive) : /* @__PURE__ */ jsx("a", {
79
+ href: s.href,
80
+ "data-slot": "stat-strip-item",
81
+ className: interactive,
82
+ children: body
83
+ }) }, i);
84
+ if (s.onSelect) return /* @__PURE__ */ jsx("button", {
85
+ type: "button",
86
+ "data-slot": "stat-strip-item",
87
+ onClick: s.onSelect,
88
+ className: interactive,
89
+ children: body
90
+ }, i);
91
+ return /* @__PURE__ */ jsx("div", {
92
+ "data-slot": "stat-strip-item",
93
+ className: cell,
94
+ children: body
95
+ }, i);
96
+ })
97
+ })
98
+ });
99
+ });
40
100
  //#endregion
41
- export { StatCard };
101
+ export { StatCard, StatStrip };
42
102
 
43
103
  //# sourceMappingURL=stat-card.js.map