@burtson-labs/ui 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) 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/agent-run.d.ts +22 -0
  7. package/dist/components/agent-run.js +131 -0
  8. package/dist/components/agent-run.js.map +1 -0
  9. package/dist/components/alert-dialog.d.ts +12 -10
  10. package/dist/components/alert-dialog.js +29 -20
  11. package/dist/components/alert-dialog.js.map +1 -1
  12. package/dist/components/alert.d.ts +5 -3
  13. package/dist/components/alert.js +11 -7
  14. package/dist/components/alert.js.map +1 -1
  15. package/dist/components/app-shell.d.ts +7 -6
  16. package/dist/components/app-shell.js +21 -14
  17. package/dist/components/app-shell.js.map +1 -1
  18. package/dist/components/attachment.d.ts +3 -3
  19. package/dist/components/attachment.js +10 -7
  20. package/dist/components/attachment.js.map +1 -1
  21. package/dist/components/audio-player.d.ts +2 -2
  22. package/dist/components/audio-player.js +8 -6
  23. package/dist/components/audio-player.js.map +1 -1
  24. package/dist/components/avatar-upload.d.ts +19 -0
  25. package/dist/components/avatar-upload.js +359 -0
  26. package/dist/components/avatar-upload.js.map +1 -0
  27. package/dist/components/avatar.d.ts +3 -3
  28. package/dist/components/avatar.js +10 -7
  29. package/dist/components/avatar.js.map +1 -1
  30. package/dist/components/badge.d.ts +1 -1
  31. package/dist/components/badge.js +12 -10
  32. package/dist/components/badge.js.map +1 -1
  33. package/dist/components/breadcrumb.d.ts +8 -8
  34. package/dist/components/breadcrumb.js +24 -17
  35. package/dist/components/breadcrumb.js.map +1 -1
  36. package/dist/components/button.d.ts +8 -1
  37. package/dist/components/button.js +12 -3
  38. package/dist/components/button.js.map +1 -1
  39. package/dist/components/card.d.ts +8 -8
  40. package/dist/components/card.js +32 -23
  41. package/dist/components/card.js.map +1 -1
  42. package/dist/components/chat-history.d.ts +1 -1
  43. package/dist/components/chat-history.js +10 -9
  44. package/dist/components/chat-history.js.map +1 -1
  45. package/dist/components/chat-layout.d.ts +1 -1
  46. package/dist/components/chat-layout.js +7 -3
  47. package/dist/components/chat-layout.js.map +1 -1
  48. package/dist/components/checkbox-card.d.ts +2 -2
  49. package/dist/components/checkbox-card.js +9 -7
  50. package/dist/components/checkbox-card.js.map +1 -1
  51. package/dist/components/checkbox.d.ts +23 -2
  52. package/dist/components/checkbox.js +48 -6
  53. package/dist/components/checkbox.js.map +1 -1
  54. package/dist/components/collapsible.d.ts +2 -2
  55. package/dist/components/collapsible.js +7 -5
  56. package/dist/components/collapsible.js.map +1 -1
  57. package/dist/components/combobox.d.ts +14 -4
  58. package/dist/components/combobox.js +18 -8
  59. package/dist/components/combobox.js.map +1 -1
  60. package/dist/components/command.d.ts +68 -12
  61. package/dist/components/command.js +38 -25
  62. package/dist/components/command.js.map +1 -1
  63. package/dist/components/composer.d.ts +2 -2
  64. package/dist/components/composer.js +9 -7
  65. package/dist/components/composer.js.map +1 -1
  66. package/dist/components/connection-status.d.ts +3 -3
  67. package/dist/components/connection-status.js +18 -14
  68. package/dist/components/connection-status.js.map +1 -1
  69. package/dist/components/context-menu.d.ts +14 -13
  70. package/dist/components/context-menu.js +33 -23
  71. package/dist/components/context-menu.js.map +1 -1
  72. package/dist/components/conversation.d.ts +1 -1
  73. package/dist/components/conversation.js +6 -5
  74. package/dist/components/conversation.js.map +1 -1
  75. package/dist/components/copy-button.d.ts +1 -1
  76. package/dist/components/copy-button.js +3 -2
  77. package/dist/components/copy-button.js.map +1 -1
  78. package/dist/components/data-table.d.ts +7 -2
  79. package/dist/components/data-table.js +16 -42
  80. package/dist/components/data-table.js.map +1 -1
  81. package/dist/components/dialog.d.ts +18 -9
  82. package/dist/components/dialog.js +38 -22
  83. package/dist/components/dialog.js.map +1 -1
  84. package/dist/components/dropdown-menu.d.ts +14 -13
  85. package/dist/components/dropdown-menu.js +33 -23
  86. package/dist/components/dropdown-menu.js.map +1 -1
  87. package/dist/components/editor-tabs.d.ts +1 -1
  88. package/dist/components/editor-tabs.js +6 -5
  89. package/dist/components/editor-tabs.js.map +1 -1
  90. package/dist/components/empty-state.d.ts +5 -5
  91. package/dist/components/empty-state.js +16 -11
  92. package/dist/components/empty-state.js.map +1 -1
  93. package/dist/components/error-summary.d.ts +21 -0
  94. package/dist/components/error-summary.js +62 -0
  95. package/dist/components/error-summary.js.map +1 -0
  96. package/dist/components/field.d.ts +66 -7
  97. package/dist/components/field.js +151 -20
  98. package/dist/components/field.js.map +1 -1
  99. package/dist/components/form-actions.d.ts +18 -0
  100. package/dist/components/form-actions.js +30 -0
  101. package/dist/components/form-actions.js.map +1 -0
  102. package/dist/components/icon-button.d.ts +3 -1
  103. package/dist/components/icon-button.js +5 -3
  104. package/dist/components/icon-button.js.map +1 -1
  105. package/dist/components/input.d.ts +42 -7
  106. package/dist/components/input.js +53 -10
  107. package/dist/components/input.js.map +1 -1
  108. package/dist/components/kbd.d.ts +2 -2
  109. package/dist/components/kbd.js +7 -5
  110. package/dist/components/kbd.js.map +1 -1
  111. package/dist/components/label.d.ts +6 -1
  112. package/dist/components/label.js +10 -4
  113. package/dist/components/label.js.map +1 -1
  114. package/dist/components/markdown.d.ts +4 -3
  115. package/dist/components/markdown.js +7 -6
  116. package/dist/components/markdown.js.map +1 -1
  117. package/dist/components/menubar.d.ts +16 -14
  118. package/dist/components/menubar.js +39 -27
  119. package/dist/components/menubar.js.map +1 -1
  120. package/dist/components/message-actions.d.ts +3 -3
  121. package/dist/components/message-actions.js +15 -12
  122. package/dist/components/message-actions.js.map +1 -1
  123. package/dist/components/message.d.ts +1 -1
  124. package/dist/components/message.js +5 -3
  125. package/dist/components/message.js.map +1 -1
  126. package/dist/components/mobile-nav.d.ts +1 -1
  127. package/dist/components/mobile-nav.js +6 -5
  128. package/dist/components/mobile-nav.js.map +1 -1
  129. package/dist/components/native-select.d.ts +16 -0
  130. package/dist/components/native-select.js +33 -0
  131. package/dist/components/native-select.js.map +1 -0
  132. package/dist/components/navigation-menu.d.ts +12 -11
  133. package/dist/components/navigation-menu.js +32 -23
  134. package/dist/components/navigation-menu.js.map +1 -1
  135. package/dist/components/number-input.d.ts +23 -0
  136. package/dist/components/number-input.js +45 -0
  137. package/dist/components/number-input.js.map +1 -0
  138. package/dist/components/onboarding-checklist.d.ts +1 -1
  139. package/dist/components/onboarding-checklist.js +4 -3
  140. package/dist/components/onboarding-checklist.js.map +1 -1
  141. package/dist/components/page-header.d.ts +7 -6
  142. package/dist/components/page-header.js +22 -15
  143. package/dist/components/page-header.js.map +1 -1
  144. package/dist/components/pagination.d.ts +1 -1
  145. package/dist/components/pagination.js +3 -2
  146. package/dist/components/pagination.js.map +1 -1
  147. package/dist/components/popover.d.ts +13 -10
  148. package/dist/components/popover.js +23 -17
  149. package/dist/components/popover.js.map +1 -1
  150. package/dist/components/progress.d.ts +1 -1
  151. package/dist/components/progress.js +6 -5
  152. package/dist/components/progress.js.map +1 -1
  153. package/dist/components/radio-group.d.ts +22 -3
  154. package/dist/components/radio-group.js +51 -10
  155. package/dist/components/radio-group.js.map +1 -1
  156. package/dist/components/reasoning.d.ts +3 -3
  157. package/dist/components/reasoning.js +9 -7
  158. package/dist/components/reasoning.js.map +1 -1
  159. package/dist/components/scroll-area.d.ts +3 -2
  160. package/dist/components/scroll-area.js +10 -7
  161. package/dist/components/scroll-area.js.map +1 -1
  162. package/dist/components/secret-input.d.ts +3 -2
  163. package/dist/components/secret-input.js +6 -4
  164. package/dist/components/secret-input.js.map +1 -1
  165. package/dist/components/select.d.ts +11 -6
  166. package/dist/components/select.js +24 -18
  167. package/dist/components/select.js.map +1 -1
  168. package/dist/components/separator.d.ts +1 -1
  169. package/dist/components/separator.js +4 -3
  170. package/dist/components/separator.js.map +1 -1
  171. package/dist/components/sheet.d.ts +10 -8
  172. package/dist/components/sheet.js +31 -20
  173. package/dist/components/sheet.js.map +1 -1
  174. package/dist/components/skeleton.d.ts +2 -1
  175. package/dist/components/skeleton.js +6 -4
  176. package/dist/components/skeleton.js.map +1 -1
  177. package/dist/components/slider.d.ts +10 -6
  178. package/dist/components/slider.js +15 -7
  179. package/dist/components/slider.js.map +1 -1
  180. package/dist/components/source.d.ts +2 -2
  181. package/dist/components/source.js +9 -7
  182. package/dist/components/source.js.map +1 -1
  183. package/dist/components/spinner.d.ts +4 -2
  184. package/dist/components/spinner.js +4 -3
  185. package/dist/components/spinner.js.map +1 -1
  186. package/dist/components/stat-card.d.ts +27 -2
  187. package/dist/components/stat-card.js +69 -9
  188. package/dist/components/stat-card.js.map +1 -1
  189. package/dist/components/status.d.ts +7 -6
  190. package/dist/components/status.js +11 -5
  191. package/dist/components/status.js.map +1 -1
  192. package/dist/components/steps.d.ts +1 -1
  193. package/dist/components/steps.js +4 -3
  194. package/dist/components/steps.js.map +1 -1
  195. package/dist/components/switch.d.ts +46 -2
  196. package/dist/components/switch.js +85 -8
  197. package/dist/components/switch.js.map +1 -1
  198. package/dist/components/table.d.ts +29 -12
  199. package/dist/components/table.js +79 -34
  200. package/dist/components/table.js.map +1 -1
  201. package/dist/components/tabs.d.ts +9 -4
  202. package/dist/components/tabs.js +22 -13
  203. package/dist/components/tabs.js.map +1 -1
  204. package/dist/components/textarea.d.ts +6 -1
  205. package/dist/components/textarea.js +3 -3
  206. package/dist/components/textarea.js.map +1 -1
  207. package/dist/components/toast.d.ts +10 -6
  208. package/dist/components/toast.js +27 -18
  209. package/dist/components/toast.js.map +1 -1
  210. package/dist/components/toaster.d.ts +4 -2
  211. package/dist/components/toaster.js +6 -2
  212. package/dist/components/toaster.js.map +1 -1
  213. package/dist/components/tool-call.d.ts +3 -3
  214. package/dist/components/tool-call.js +16 -12
  215. package/dist/components/tool-call.js.map +1 -1
  216. package/dist/components/toolbar.d.ts +9 -4
  217. package/dist/components/toolbar.js +14 -9
  218. package/dist/components/toolbar.js.map +1 -1
  219. package/dist/components/tooltip.d.ts +3 -2
  220. package/dist/components/tooltip.js +7 -4
  221. package/dist/components/tooltip.js.map +1 -1
  222. package/dist/components/tour.js +2 -2
  223. package/dist/components/tour.js.map +1 -1
  224. package/dist/components/tree-view.d.ts +1 -1
  225. package/dist/components/tree-view.js +9 -5
  226. package/dist/components/tree-view.js.map +1 -1
  227. package/dist/components/voice-recorder.d.ts +1 -1
  228. package/dist/components/voice-recorder.js +4 -2
  229. package/dist/components/voice-recorder.js.map +1 -1
  230. package/dist/index.d.ts +8 -1
  231. package/dist/index.js +19 -12
  232. package/dist/lib/media.d.ts +18 -0
  233. package/dist/lib/media.js +34 -0
  234. package/dist/lib/media.js.map +1 -0
  235. package/dist/lib/utils.d.ts +32 -0
  236. package/dist/lib/utils.js +33 -1
  237. package/dist/lib/utils.js.map +1 -1
  238. package/dist/mui/index.js +20 -8
  239. package/dist/mui/index.js.map +1 -1
  240. package/dist/styles.css +1 -1
  241. package/dist/theme.css +36 -17
  242. package/dist/tokens.d.ts +78 -36
  243. package/dist/tokens.js +63 -32
  244. package/dist/tokens.js.map +1 -1
  245. package/package.json +5 -3
@@ -7,24 +7,25 @@ import * as ContextMenuPrimitive from '../primitives/vendor/radix/react-context-
7
7
  * DropdownMenu, so a command can appear in both.
8
8
  */
9
9
  declare function ContextMenu(props: React.ComponentProps<typeof ContextMenuPrimitive.Root>): React.JSX.Element;
10
- declare function ContextMenuTrigger(props: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>): React.JSX.Element;
10
+ declare const ContextMenuTrigger: React.ForwardRefExoticComponent<Omit<ContextMenuPrimitive.ContextMenuTriggerProps & React.RefAttributes<HTMLSpanElement>, "ref"> & React.RefAttributes<HTMLSpanElement>>;
11
11
  declare function ContextMenuGroup(props: React.ComponentProps<typeof ContextMenuPrimitive.Group>): React.JSX.Element;
12
- declare function ContextMenuContent({ className, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Content>): React.JSX.Element;
13
- declare function ContextMenuItem({ className, inset, variant, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {
12
+ declare const ContextMenuContent: React.ForwardRefExoticComponent<Omit<ContextMenuPrimitive.ContextMenuContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
13
+ export interface ContextMenuItemProps extends React.ComponentProps<typeof ContextMenuPrimitive.Item> {
14
14
  inset?: boolean;
15
15
  variant?: 'default' | 'destructive';
16
- }): React.JSX.Element;
17
- declare function ContextMenuCheckboxItem({ className, children, checked, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>): React.JSX.Element;
16
+ }
17
+ declare const ContextMenuItem: React.ForwardRefExoticComponent<Omit<ContextMenuItemProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
18
+ declare const ContextMenuCheckboxItem: React.ForwardRefExoticComponent<Omit<ContextMenuPrimitive.ContextMenuCheckboxItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
18
19
  declare function ContextMenuRadioGroup(props: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>): React.JSX.Element;
19
- declare function ContextMenuRadioItem({ className, children, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>): React.JSX.Element;
20
- declare function ContextMenuLabel({ className, inset, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Label> & {
20
+ declare const ContextMenuRadioItem: React.ForwardRefExoticComponent<Omit<ContextMenuPrimitive.ContextMenuRadioItemProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
21
+ declare const ContextMenuLabel: React.ForwardRefExoticComponent<Omit<ContextMenuPrimitive.ContextMenuLabelProps & React.RefAttributes<HTMLDivElement> & {
21
22
  inset?: boolean;
22
- }): React.JSX.Element;
23
- declare function ContextMenuSeparator({ className, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.Separator>): React.JSX.Element;
24
- declare function ContextMenuShortcut({ className, ...props }: React.ComponentProps<'span'>): React.JSX.Element;
23
+ }, "ref"> & React.RefAttributes<HTMLDivElement>>;
24
+ declare const ContextMenuSeparator: React.ForwardRefExoticComponent<Omit<ContextMenuPrimitive.ContextMenuSeparatorProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
25
+ declare const ContextMenuShortcut: React.ForwardRefExoticComponent<Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLSpanElement>, HTMLSpanElement>, "ref"> & React.RefAttributes<HTMLSpanElement>>;
25
26
  declare function ContextMenuSub(props: React.ComponentProps<typeof ContextMenuPrimitive.Sub>): React.JSX.Element;
26
- declare function ContextMenuSubTrigger({ className, inset, children, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & {
27
+ declare const ContextMenuSubTrigger: React.ForwardRefExoticComponent<Omit<ContextMenuPrimitive.ContextMenuSubTriggerProps & React.RefAttributes<HTMLDivElement> & {
27
28
  inset?: boolean;
28
- }): React.JSX.Element;
29
- declare function ContextMenuSubContent({ className, ...props }: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>): React.JSX.Element;
29
+ }, "ref"> & React.RefAttributes<HTMLDivElement>>;
30
+ declare const ContextMenuSubContent: React.ForwardRefExoticComponent<Omit<ContextMenuPrimitive.ContextMenuSubContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
30
31
  export { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger, };
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/utils.js";
3
- import { menuItemClasses, surfaceClasses } from "./popover.js";
3
+ import { menuItemClasses, menuLabelClasses, surfaceClasses } from "./popover.js";
4
4
  import { CheckboxItem, Content, Group, Item, ItemIndicator, Label, Portal, RadioGroup, RadioItem, Root, Separator, Sub, SubContent, SubTrigger, Trigger } from "../primitives/vendor/radix/react-context-menu/context-menu.js";
5
- import "react";
5
+ import * as React from "react";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
7
  import ChevronRight from "@burtson-labs/icons/react/chevron-right";
8
8
  import Check from "@burtson-labs/icons/react/check";
@@ -19,36 +19,40 @@ function ContextMenu(props) {
19
19
  ...props
20
20
  });
21
21
  }
22
- function ContextMenuTrigger(props) {
22
+ var ContextMenuTrigger = React.forwardRef(function ContextMenuTrigger(props, ref) {
23
23
  return /* @__PURE__ */ jsx(Trigger, {
24
+ ref,
24
25
  "data-slot": "context-menu-trigger",
25
26
  ...props
26
27
  });
27
- }
28
+ });
28
29
  function ContextMenuGroup(props) {
29
30
  return /* @__PURE__ */ jsx(Group, {
30
31
  "data-slot": "context-menu-group",
31
32
  ...props
32
33
  });
33
34
  }
34
- function ContextMenuContent({ className, ...props }) {
35
+ var ContextMenuContent = React.forwardRef(function ContextMenuContent({ className, ...props }, ref) {
35
36
  return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(Content, {
37
+ ref,
36
38
  "data-slot": "context-menu-content",
37
39
  className: cn(surfaceClasses, "max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto p-1", className),
38
40
  ...props
39
41
  }) });
40
- }
41
- function ContextMenuItem({ className, inset, variant = "default", ...props }) {
42
+ });
43
+ var ContextMenuItem = React.forwardRef(function ContextMenuItem({ className, inset, variant = "default", ...props }, ref) {
42
44
  return /* @__PURE__ */ jsx(Item, {
45
+ ref,
43
46
  "data-slot": "context-menu-item",
44
47
  "data-inset": inset || void 0,
45
48
  "data-variant": variant,
46
49
  className: cn(menuItemClasses, "data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:*:[svg]:!text-destructive", className),
47
50
  ...props
48
51
  });
49
- }
50
- function ContextMenuCheckboxItem({ className, children, checked, ...props }) {
52
+ });
53
+ var ContextMenuCheckboxItem = React.forwardRef(function ContextMenuCheckboxItem({ className, children, checked, ...props }, ref) {
51
54
  return /* @__PURE__ */ jsxs(CheckboxItem, {
55
+ ref,
52
56
  "data-slot": "context-menu-checkbox-item",
53
57
  className: cn(menuItemClasses, "pl-8", className),
54
58
  checked,
@@ -61,15 +65,16 @@ function ContextMenuCheckboxItem({ className, children, checked, ...props }) {
61
65
  }) })
62
66
  }), children]
63
67
  });
64
- }
68
+ });
65
69
  function ContextMenuRadioGroup(props) {
66
70
  return /* @__PURE__ */ jsx(RadioGroup, {
67
71
  "data-slot": "context-menu-radio-group",
68
72
  ...props
69
73
  });
70
74
  }
71
- function ContextMenuRadioItem({ className, children, ...props }) {
75
+ var ContextMenuRadioItem = React.forwardRef(function ContextMenuRadioItem({ className, children, ...props }, ref) {
72
76
  return /* @__PURE__ */ jsxs(RadioItem, {
77
+ ref,
73
78
  "data-slot": "context-menu-radio-item",
74
79
  className: cn(menuItemClasses, "pl-8", className),
75
80
  ...props,
@@ -78,37 +83,41 @@ function ContextMenuRadioItem({ className, children, ...props }) {
78
83
  children: /* @__PURE__ */ jsx(ItemIndicator, { children: /* @__PURE__ */ jsx("span", { className: "block size-2 rounded-full bg-foreground" }) })
79
84
  }), children]
80
85
  });
81
- }
82
- function ContextMenuLabel({ className, inset, ...props }) {
86
+ });
87
+ var ContextMenuLabel = React.forwardRef(function ContextMenuLabel({ className, inset, ...props }, ref) {
83
88
  return /* @__PURE__ */ jsx(Label, {
89
+ ref,
84
90
  "data-slot": "context-menu-label",
85
91
  "data-inset": inset || void 0,
86
- className: cn("px-2 pt-2 pb-1 text-[11px] font-semibold tracking-[0.08em] text-muted-foreground uppercase data-[inset]:pl-8", className),
92
+ className: cn(menuLabelClasses, "data-[inset]:pl-8", className),
87
93
  ...props
88
94
  });
89
- }
90
- function ContextMenuSeparator({ className, ...props }) {
95
+ });
96
+ var ContextMenuSeparator = React.forwardRef(function ContextMenuSeparator({ className, ...props }, ref) {
91
97
  return /* @__PURE__ */ jsx(Separator, {
98
+ ref,
92
99
  "data-slot": "context-menu-separator",
93
100
  className: cn("-mx-1 my-1 h-px bg-border", className),
94
101
  ...props
95
102
  });
96
- }
97
- function ContextMenuShortcut({ className, ...props }) {
103
+ });
104
+ var ContextMenuShortcut = React.forwardRef(function ContextMenuShortcut({ className, ...props }, ref) {
98
105
  return /* @__PURE__ */ jsx("span", {
106
+ ref,
99
107
  "data-slot": "context-menu-shortcut",
100
108
  className: cn("ml-auto text-xs tracking-widest text-muted-foreground", className),
101
109
  ...props
102
110
  });
103
- }
111
+ });
104
112
  function ContextMenuSub(props) {
105
113
  return /* @__PURE__ */ jsx(Sub, {
106
114
  "data-slot": "context-menu-sub",
107
115
  ...props
108
116
  });
109
117
  }
110
- function ContextMenuSubTrigger({ className, inset, children, ...props }) {
118
+ var ContextMenuSubTrigger = React.forwardRef(function ContextMenuSubTrigger({ className, inset, children, ...props }, ref) {
111
119
  return /* @__PURE__ */ jsxs(SubTrigger, {
120
+ ref,
112
121
  "data-slot": "context-menu-sub-trigger",
113
122
  "data-inset": inset || void 0,
114
123
  className: cn(menuItemClasses, "data-[state=open]:bg-secondary", className),
@@ -118,14 +127,15 @@ function ContextMenuSubTrigger({ className, inset, children, ...props }) {
118
127
  "aria-hidden": true
119
128
  })]
120
129
  });
121
- }
122
- function ContextMenuSubContent({ className, ...props }) {
130
+ });
131
+ var ContextMenuSubContent = React.forwardRef(function ContextMenuSubContent({ className, ...props }, ref) {
123
132
  return /* @__PURE__ */ jsx(Portal, { children: /* @__PURE__ */ jsx(SubContent, {
133
+ ref,
124
134
  "data-slot": "context-menu-sub-content",
125
135
  className: cn(surfaceClasses, "min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden p-1", className),
126
136
  ...props
127
137
  }) });
128
- }
138
+ });
129
139
  //#endregion
130
140
  export { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger };
131
141
 
@@ -1 +1 @@
1
- {"version":3,"file":"context-menu.js","names":["ContextMenuPrimitive.Root","ContextMenuPrimitive.Trigger","ContextMenuPrimitive.Group","ContextMenuPrimitive.Portal","ContextMenuPrimitive.Content","ContextMenuPrimitive.Item","itemClasses","ContextMenuPrimitive.CheckboxItem","ContextMenuPrimitive.ItemIndicator","ContextMenuPrimitive.RadioGroup","ContextMenuPrimitive.RadioItem","ContextMenuPrimitive.Label","ContextMenuPrimitive.Separator","ContextMenuPrimitive.Sub","ContextMenuPrimitive.SubTrigger","ContextMenuPrimitive.SubContent"],"sources":["../../src/components/context-menu.tsx"],"sourcesContent":["import Check from '@burtson-labs/icons/react/check';\nimport ChevronRight from '@burtson-labs/icons/react/chevron-right';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\nimport * as ContextMenuPrimitive from '../primitives/vendor/radix/react-context-menu';\n\nimport { menuItemClasses as itemClasses, surfaceClasses } from './popover';\n\n/**\n * A right-click menu. Wrap the target in ContextMenuTrigger (use asChild to keep\n * your element); the menu opens at the pointer, and from the keyboard with the\n * context-menu key or Shift+F10 on the focused target. Same rows as\n * DropdownMenu, so a command can appear in both.\n */\nfunction ContextMenu(props: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {\n return <ContextMenuPrimitive.Root data-slot=\"context-menu\" {...props} />;\n}\n\nfunction ContextMenuTrigger(props: React.ComponentProps<typeof ContextMenuPrimitive.Trigger>) {\n return <ContextMenuPrimitive.Trigger data-slot=\"context-menu-trigger\" {...props} />;\n}\n\nfunction ContextMenuGroup(props: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {\n return <ContextMenuPrimitive.Group data-slot=\"context-menu-group\" {...props} />;\n}\n\nfunction ContextMenuContent({\n className,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Content>) {\n return (\n <ContextMenuPrimitive.Portal>\n <ContextMenuPrimitive.Content\n data-slot=\"context-menu-content\"\n className={cn(\n surfaceClasses,\n 'max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto p-1',\n className,\n )}\n {...props}\n />\n </ContextMenuPrimitive.Portal>\n );\n}\n\nfunction ContextMenuItem({\n className,\n inset,\n variant = 'default',\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Item> & {\n inset?: boolean;\n variant?: 'default' | 'destructive';\n}) {\n return (\n <ContextMenuPrimitive.Item\n data-slot=\"context-menu-item\"\n data-inset={inset || undefined}\n data-variant={variant}\n className={cn(\n itemClasses,\n 'data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:*:[svg]:!text-destructive',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction ContextMenuCheckboxItem({\n className,\n children,\n checked,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) {\n return (\n <ContextMenuPrimitive.CheckboxItem\n data-slot=\"context-menu-checkbox-item\"\n className={cn(itemClasses, 'pl-8', className)}\n checked={checked}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <ContextMenuPrimitive.ItemIndicator>\n <Check className=\"size-4 text-foreground\" aria-hidden />\n </ContextMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </ContextMenuPrimitive.CheckboxItem>\n );\n}\n\nfunction ContextMenuRadioGroup(\n props: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>,\n) {\n return <ContextMenuPrimitive.RadioGroup data-slot=\"context-menu-radio-group\" {...props} />;\n}\n\nfunction ContextMenuRadioItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>) {\n return (\n <ContextMenuPrimitive.RadioItem\n data-slot=\"context-menu-radio-item\"\n className={cn(itemClasses, 'pl-8', className)}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <ContextMenuPrimitive.ItemIndicator>\n <span className=\"block size-2 rounded-full bg-foreground\" />\n </ContextMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </ContextMenuPrimitive.RadioItem>\n );\n}\n\nfunction ContextMenuLabel({\n className,\n inset,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Label> & { inset?: boolean }) {\n return (\n <ContextMenuPrimitive.Label\n data-slot=\"context-menu-label\"\n data-inset={inset || undefined}\n className={cn(\n 'px-2 pt-2 pb-1 text-[11px] font-semibold tracking-[0.08em] text-muted-foreground uppercase data-[inset]:pl-8',\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction ContextMenuSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.Separator>) {\n return (\n <ContextMenuPrimitive.Separator\n data-slot=\"context-menu-separator\"\n className={cn('-mx-1 my-1 h-px bg-border', className)}\n {...props}\n />\n );\n}\n\nfunction ContextMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"context-menu-shortcut\"\n className={cn('ml-auto text-xs tracking-widest text-muted-foreground', className)}\n {...props}\n />\n );\n}\n\nfunction ContextMenuSub(props: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {\n return <ContextMenuPrimitive.Sub data-slot=\"context-menu-sub\" {...props} />;\n}\n\nfunction ContextMenuSubTrigger({\n className,\n inset,\n children,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & { inset?: boolean }) {\n return (\n <ContextMenuPrimitive.SubTrigger\n data-slot=\"context-menu-sub-trigger\"\n data-inset={inset || undefined}\n className={cn(itemClasses, 'data-[state=open]:bg-secondary', className)}\n {...props}\n >\n {children}\n <ChevronRight className=\"ml-auto size-4\" aria-hidden />\n </ContextMenuPrimitive.SubTrigger>\n );\n}\n\nfunction ContextMenuSubContent({\n className,\n ...props\n}: React.ComponentProps<typeof ContextMenuPrimitive.SubContent>) {\n return (\n <ContextMenuPrimitive.Portal>\n <ContextMenuPrimitive.SubContent\n data-slot=\"context-menu-sub-content\"\n className={cn(\n surfaceClasses,\n 'min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden p-1',\n className,\n )}\n {...props}\n />\n </ContextMenuPrimitive.Portal>\n );\n}\n\nexport {\n ContextMenu,\n ContextMenuCheckboxItem,\n ContextMenuContent,\n ContextMenuGroup,\n ContextMenuItem,\n ContextMenuLabel,\n ContextMenuRadioGroup,\n ContextMenuRadioItem,\n ContextMenuSeparator,\n ContextMenuShortcut,\n ContextMenuSub,\n ContextMenuSubContent,\n ContextMenuSubTrigger,\n ContextMenuTrigger,\n};\n"],"mappings":";;;;;;;;;;;;;;;AAeA,SAAS,YAAY,OAA+D;CAClF,OAAO,oBAACA,MAAD;EAA2B,aAAU;EAAe,GAAI;CAAQ,CAAA;AACzE;AAEA,SAAS,mBAAmB,OAAkE;CAC5F,OAAO,oBAACC,SAAD;EAA8B,aAAU;EAAuB,GAAI;CAAQ,CAAA;AACpF;AAEA,SAAS,iBAAiB,OAAgE;CACxF,OAAO,oBAACC,OAAD;EAA4B,aAAU;EAAqB,GAAI;CAAQ,CAAA;AAChF;AAEA,SAAS,mBAAmB,EAC1B,WACA,GAAG,SACyD;CAC5D,OACE,oBAACC,QAAD,EAAA,UACE,oBAACC,SAAD;EACE,aAAU;EACV,WAAW,GACT,gBACA,mKACA,SACF;EACA,GAAI;CACL,CAAA,EAC0B,CAAA;AAEjC;AAEA,SAAS,gBAAgB,EACvB,WACA,OACA,UAAU,WACV,GAAG,SAIF;CACD,OACE,oBAACC,MAAD;EACE,aAAU;EACV,cAAY,SAAS,KAAA;EACrB,gBAAc;EACd,WAAW,GACTC,iBACA,uJACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,wBAAwB,EAC/B,WACA,UACA,SACA,GAAG,SAC8D;CACjE,OACE,qBAACC,cAAD;EACE,aAAU;EACV,WAAW,GAAGD,iBAAa,QAAQ,SAAS;EACnC;EACT,GAAI;EAJN,UAAA,CAME,oBAAC,QAAD;GAAM,WAAU;GACd,UAAA,oBAACE,eAAD,EAAA,UACE,oBAAC,OAAD;IAAO,WAAU;IAAyB,eAAA;GAAa,CAAA,EACrB,CAAA;EAChC,CAAA,GACL,QACgC;;AAEvC;AAEA,SAAS,sBACP,OACA;CACA,OAAO,oBAACC,YAAD;EAAiC,aAAU;EAA2B,GAAI;CAAQ,CAAA;AAC3F;AAEA,SAAS,qBAAqB,EAC5B,WACA,UACA,GAAG,SAC2D;CAC9D,OACE,qBAACC,WAAD;EACE,aAAU;EACV,WAAW,GAAGJ,iBAAa,QAAQ,SAAS;EAC5C,GAAI;EAHN,UAAA,CAKE,oBAAC,QAAD;GAAM,WAAU;GACd,UAAA,oBAACE,eAAD,EAAA,UACE,oBAAC,QAAD,EAAM,WAAU,0CAA2C,CAAA,EACzB,CAAA;EAChC,CAAA,GACL,QAC6B;;AAEpC;AAEA,SAAS,iBAAiB,EACxB,WACA,OACA,GAAG,SAC6E;CAChF,OACE,oBAACG,OAAD;EACE,aAAU;EACV,cAAY,SAAS,KAAA;EACrB,WAAW,GACT,gHACA,SACF;EACA,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,qBAAqB,EAC5B,WACA,GAAG,SAC2D;CAC9D,OACE,oBAACC,WAAD;EACE,aAAU;EACV,WAAW,GAAG,6BAA6B,SAAS;EACpD,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,oBAAoB,EAAE,WAAW,GAAG,SAAuC;CAClF,OACE,oBAAC,QAAD;EACE,aAAU;EACV,WAAW,GAAG,yDAAyD,SAAS;EAChF,GAAI;CACL,CAAA;AAEL;AAEA,SAAS,eAAe,OAA8D;CACpF,OAAO,oBAACC,KAAD;EAA0B,aAAU;EAAmB,GAAI;CAAQ,CAAA;AAC5E;AAEA,SAAS,sBAAsB,EAC7B,WACA,OACA,UACA,GAAG,SACkF;CACrF,OACE,qBAACC,YAAD;EACE,aAAU;EACV,cAAY,SAAS,KAAA;EACrB,WAAW,GAAGR,iBAAa,kCAAkC,SAAS;EACtE,GAAI;EAJN,UAAA,CAMG,UACD,oBAAC,cAAD;GAAc,WAAU;GAAiB,eAAA;EAAa,CAAA,CACvB;;AAErC;AAEA,SAAS,sBAAsB,EAC7B,WACA,GAAG,SAC4D;CAC/D,OACE,oBAACH,QAAD,EAAA,UACE,oBAACY,YAAD;EACE,aAAU;EACV,WAAW,GACT,gBACA,2FACA,SACF;EACA,GAAI;CACL,CAAA,EAC0B,CAAA;AAEjC"}
1
+ {"version":3,"file":"context-menu.js","names":["ContextMenuPrimitive.Root","ContextMenuPrimitive.Trigger","ContextMenuPrimitive.Group","ContextMenuPrimitive.Portal","ContextMenuPrimitive.Content","ContextMenuPrimitive.Item","itemClasses","ContextMenuPrimitive.CheckboxItem","ContextMenuPrimitive.ItemIndicator","ContextMenuPrimitive.RadioGroup","ContextMenuPrimitive.RadioItem","ContextMenuPrimitive.Label","ContextMenuPrimitive.Separator","ContextMenuPrimitive.Sub","ContextMenuPrimitive.SubTrigger","ContextMenuPrimitive.SubContent"],"sources":["../../src/components/context-menu.tsx"],"sourcesContent":["import Check from '@burtson-labs/icons/react/check';\nimport ChevronRight from '@burtson-labs/icons/react/chevron-right';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\nimport * as ContextMenuPrimitive from '../primitives/vendor/radix/react-context-menu';\n\nimport { menuItemClasses as itemClasses, menuLabelClasses, surfaceClasses } from './popover';\n\n/**\n * A right-click menu. Wrap the target in ContextMenuTrigger (use asChild to keep\n * your element); the menu opens at the pointer, and from the keyboard with the\n * context-menu key or Shift+F10 on the focused target. Same rows as\n * DropdownMenu, so a command can appear in both.\n */\nfunction ContextMenu(props: React.ComponentProps<typeof ContextMenuPrimitive.Root>) {\n return <ContextMenuPrimitive.Root data-slot=\"context-menu\" {...props} />;\n}\n\nconst ContextMenuTrigger = React.forwardRef<\n HTMLSpanElement,\n React.ComponentProps<typeof ContextMenuPrimitive.Trigger>\n>(function ContextMenuTrigger(props, ref) {\n return <ContextMenuPrimitive.Trigger ref={ref} data-slot=\"context-menu-trigger\" {...props} />;\n});\n\nfunction ContextMenuGroup(props: React.ComponentProps<typeof ContextMenuPrimitive.Group>) {\n return <ContextMenuPrimitive.Group data-slot=\"context-menu-group\" {...props} />;\n}\n\nconst ContextMenuContent = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof ContextMenuPrimitive.Content>\n>(function ContextMenuContent({ className, ...props }, ref) {\n return (\n <ContextMenuPrimitive.Portal>\n <ContextMenuPrimitive.Content\n ref={ref}\n data-slot=\"context-menu-content\"\n className={cn(\n surfaceClasses,\n 'max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto p-1',\n className,\n )}\n {...props}\n />\n </ContextMenuPrimitive.Portal>\n );\n});\n\nexport interface ContextMenuItemProps extends React.ComponentProps<\n typeof ContextMenuPrimitive.Item\n> {\n inset?: boolean;\n variant?: 'default' | 'destructive';\n}\n\nconst ContextMenuItem = React.forwardRef<HTMLDivElement, ContextMenuItemProps>(\n function ContextMenuItem({ className, inset, variant = 'default', ...props }, ref) {\n return (\n <ContextMenuPrimitive.Item\n ref={ref}\n data-slot=\"context-menu-item\"\n data-inset={inset || undefined}\n data-variant={variant}\n className={cn(\n itemClasses,\n 'data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:*:[svg]:!text-destructive',\n className,\n )}\n {...props}\n />\n );\n },\n);\n\nconst ContextMenuCheckboxItem = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>\n>(function ContextMenuCheckboxItem({ className, children, checked, ...props }, ref) {\n return (\n <ContextMenuPrimitive.CheckboxItem\n ref={ref}\n data-slot=\"context-menu-checkbox-item\"\n className={cn(itemClasses, 'pl-8', className)}\n checked={checked}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <ContextMenuPrimitive.ItemIndicator>\n <Check className=\"size-4 text-foreground\" aria-hidden />\n </ContextMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </ContextMenuPrimitive.CheckboxItem>\n );\n});\n\nfunction ContextMenuRadioGroup(\n props: React.ComponentProps<typeof ContextMenuPrimitive.RadioGroup>,\n) {\n return <ContextMenuPrimitive.RadioGroup data-slot=\"context-menu-radio-group\" {...props} />;\n}\n\nconst ContextMenuRadioItem = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof ContextMenuPrimitive.RadioItem>\n>(function ContextMenuRadioItem({ className, children, ...props }, ref) {\n return (\n <ContextMenuPrimitive.RadioItem\n ref={ref}\n data-slot=\"context-menu-radio-item\"\n className={cn(itemClasses, 'pl-8', className)}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <ContextMenuPrimitive.ItemIndicator>\n <span className=\"block size-2 rounded-full bg-foreground\" />\n </ContextMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </ContextMenuPrimitive.RadioItem>\n );\n});\n\nconst ContextMenuLabel = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof ContextMenuPrimitive.Label> & { inset?: boolean }\n>(function ContextMenuLabel({ className, inset, ...props }, ref) {\n return (\n <ContextMenuPrimitive.Label\n ref={ref}\n data-slot=\"context-menu-label\"\n data-inset={inset || undefined}\n className={cn(menuLabelClasses, 'data-[inset]:pl-8', className)}\n {...props}\n />\n );\n});\n\nconst ContextMenuSeparator = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof ContextMenuPrimitive.Separator>\n>(function ContextMenuSeparator({ className, ...props }, ref) {\n return (\n <ContextMenuPrimitive.Separator\n ref={ref}\n data-slot=\"context-menu-separator\"\n className={cn('-mx-1 my-1 h-px bg-border', className)}\n {...props}\n />\n );\n});\n\nconst ContextMenuShortcut = React.forwardRef<HTMLSpanElement, React.ComponentProps<'span'>>(\n function ContextMenuShortcut({ className, ...props }, ref) {\n return (\n <span\n ref={ref}\n data-slot=\"context-menu-shortcut\"\n className={cn('ml-auto text-xs tracking-widest text-muted-foreground', className)}\n {...props}\n />\n );\n },\n);\n\nfunction ContextMenuSub(props: React.ComponentProps<typeof ContextMenuPrimitive.Sub>) {\n return <ContextMenuPrimitive.Sub data-slot=\"context-menu-sub\" {...props} />;\n}\n\nconst ContextMenuSubTrigger = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof ContextMenuPrimitive.SubTrigger> & { inset?: boolean }\n>(function ContextMenuSubTrigger({ className, inset, children, ...props }, ref) {\n return (\n <ContextMenuPrimitive.SubTrigger\n ref={ref}\n data-slot=\"context-menu-sub-trigger\"\n data-inset={inset || undefined}\n className={cn(itemClasses, 'data-[state=open]:bg-secondary', className)}\n {...props}\n >\n {children}\n <ChevronRight className=\"ml-auto size-4\" aria-hidden />\n </ContextMenuPrimitive.SubTrigger>\n );\n});\n\nconst ContextMenuSubContent = React.forwardRef<\n HTMLDivElement,\n React.ComponentProps<typeof ContextMenuPrimitive.SubContent>\n>(function ContextMenuSubContent({ className, ...props }, ref) {\n return (\n <ContextMenuPrimitive.Portal>\n <ContextMenuPrimitive.SubContent\n ref={ref}\n data-slot=\"context-menu-sub-content\"\n className={cn(\n surfaceClasses,\n 'min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-hidden p-1',\n className,\n )}\n {...props}\n />\n </ContextMenuPrimitive.Portal>\n );\n});\n\nexport {\n ContextMenu,\n ContextMenuCheckboxItem,\n ContextMenuContent,\n ContextMenuGroup,\n ContextMenuItem,\n ContextMenuLabel,\n ContextMenuRadioGroup,\n ContextMenuRadioItem,\n ContextMenuSeparator,\n ContextMenuShortcut,\n ContextMenuSub,\n ContextMenuSubContent,\n ContextMenuSubTrigger,\n ContextMenuTrigger,\n};\n"],"mappings":"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"}
@@ -10,5 +10,5 @@ export interface ConversationProps extends React.ComponentProps<'div'> {
10
10
  * streams, unless the reader has scrolled up; then a "Jump to latest" button
11
11
  * appears instead of yanking them back down.
12
12
  */
13
- declare function Conversation({ children, empty, label, className, ...props }: ConversationProps): React.JSX.Element;
13
+ declare const Conversation: React.ForwardRefExoticComponent<Omit<ConversationProps, "ref"> & React.RefAttributes<HTMLDivElement>>;
14
14
  export { Conversation };
@@ -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 * as React from "react";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import ArrowDown from "@burtson-labs/icons/react/arrow-down";
@@ -9,7 +9,7 @@ import ArrowDown from "@burtson-labs/icons/react/arrow-down";
9
9
  * streams, unless the reader has scrolled up; then a "Jump to latest" button
10
10
  * appears instead of yanking them back down.
11
11
  */
12
- function Conversation({ children, empty, label = "Conversation", className, ...props }) {
12
+ var Conversation = React.forwardRef(function Conversation({ children, empty, label = "Conversation", className, ...props }, ref) {
13
13
  const scroller = React.useRef(null);
14
14
  const content = React.useRef(null);
15
15
  const pinned = React.useRef(true);
@@ -44,6 +44,7 @@ function Conversation({ children, empty, label = "Conversation", className, ...p
44
44
  setAtBottom(bottom);
45
45
  };
46
46
  return /* @__PURE__ */ jsxs("div", {
47
+ ref,
47
48
  "data-slot": "conversation",
48
49
  className: cn("relative min-h-0", className),
49
50
  ...props,
@@ -55,7 +56,7 @@ function Conversation({ children, empty, label = "Conversation", className, ...p
55
56
  "aria-relevant": "additions text",
56
57
  tabIndex: 0,
57
58
  onScroll,
58
- className: "size-full overflow-y-auto outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20",
59
+ className: cn("size-full overflow-y-auto", focusRingClasses, "focus-visible:outline-offset-[-2px]"),
59
60
  children: /* @__PURE__ */ jsx("div", {
60
61
  ref: content,
61
62
  className: "grid gap-5 p-4",
@@ -68,11 +69,11 @@ function Conversation({ children, empty, label = "Conversation", className, ...p
68
69
  setAtBottom(true);
69
70
  scrollToBottom(typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches ? "auto" : "smooth");
70
71
  },
71
- className: "absolute bottom-3 left-1/2 inline-flex -translate-x-1/2 animate-in items-center gap-1.5 rounded-full border border-border-strong bg-surface-raised px-3 py-1.5 text-xs font-semibold shadow-md outline-none hover:bg-muted focus-visible:ring-[3px] focus-visible:ring-ring/20 [&_svg]:size-3.5",
72
+ className: cn("absolute bottom-3 left-1/2 inline-flex -translate-x-1/2 animate-in items-center gap-1.5 rounded-full border border-border-strong bg-surface-raised px-3 py-1.5 text-xs font-semibold shadow-md hover:bg-muted [&_svg]:size-3.5 pointer-coarse:min-h-11", "outline-none focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ring"),
72
73
  children: [/* @__PURE__ */ jsx(ArrowDown, { "aria-hidden": true }), " Jump to latest"]
73
74
  })]
74
75
  });
75
- }
76
+ });
76
77
  //#endregion
77
78
  export { Conversation };
78
79
 
@@ -1 +1 @@
1
- {"version":3,"file":"conversation.js","names":[],"sources":["../../src/components/conversation.tsx"],"sourcesContent":["import ArrowDown from '@burtson-labs/icons/react/arrow-down';\nimport * as React from 'react';\n\nimport { cn } from '../lib/utils';\n\nexport interface ConversationProps extends React.ComponentProps<'div'> {\n /** Shown instead of the messages when there are none. */\n empty?: React.ReactNode;\n /** Accessible name for the message log. */\n label?: string;\n}\n\n/**\n * A scrolling message log that stays pinned to the newest message while it\n * streams, unless the reader has scrolled up; then a \"Jump to latest\" button\n * appears instead of yanking them back down.\n */\nfunction Conversation({\n children,\n empty,\n label = 'Conversation',\n className,\n ...props\n}: ConversationProps) {\n const scroller = React.useRef<HTMLDivElement>(null);\n const content = React.useRef<HTMLDivElement>(null);\n const pinned = React.useRef(true);\n const [atBottom, setAtBottom] = React.useState(true);\n const hasMessages = React.Children.toArray(children).length > 0;\n\n const scrollToBottom = React.useCallback((behavior: ScrollBehavior = 'auto') => {\n const el = scroller.current;\n if (!el) return;\n if (typeof el.scrollTo === 'function') el.scrollTo({ top: el.scrollHeight, behavior });\n else el.scrollTop = el.scrollHeight;\n }, []);\n\n React.useEffect(() => {\n const el = content.current;\n if (!el || typeof ResizeObserver === 'undefined') return;\n // Content grows while a reply streams; follow it only if pinned.\n const ro = new ResizeObserver(() => {\n if (pinned.current) scrollToBottom();\n });\n ro.observe(el);\n return () => ro.disconnect();\n }, [scrollToBottom]);\n\n React.useEffect(() => {\n if (pinned.current) scrollToBottom();\n }, [children, scrollToBottom]);\n\n const onScroll = () => {\n const el = scroller.current;\n if (!el) return;\n const bottom = el.scrollHeight - el.scrollTop - el.clientHeight < 32;\n pinned.current = bottom;\n setAtBottom(bottom);\n };\n\n return (\n <div data-slot=\"conversation\" className={cn('relative min-h-0', className)} {...props}>\n <div\n ref={scroller}\n role=\"log\"\n aria-label={label}\n aria-live=\"polite\"\n aria-relevant=\"additions text\"\n // A scrollable region must be reachable by keyboard (WCAG 2.1.1).\n // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex\n tabIndex={0}\n onScroll={onScroll}\n className=\"size-full overflow-y-auto outline-none focus-visible:ring-[3px] focus-visible:ring-ring/20\"\n >\n <div ref={content} className=\"grid gap-5 p-4\">\n {hasMessages ? children : empty}\n </div>\n </div>\n {!atBottom && (\n <button\n type=\"button\"\n onClick={() => {\n pinned.current = true;\n setAtBottom(true);\n scrollToBottom(\n typeof matchMedia === 'function' &&\n matchMedia('(prefers-reduced-motion: reduce)').matches\n ? 'auto'\n : 'smooth',\n );\n }}\n className=\"absolute bottom-3 left-1/2 inline-flex -translate-x-1/2 animate-in items-center gap-1.5 rounded-full border border-border-strong bg-surface-raised px-3 py-1.5 text-xs font-semibold shadow-md outline-none hover:bg-muted focus-visible:ring-[3px] focus-visible:ring-ring/20 [&_svg]:size-3.5\"\n >\n <ArrowDown aria-hidden /> Jump to latest\n </button>\n )}\n </div>\n );\n}\n\nexport { Conversation };\n"],"mappings":";;;;;;;;;;;AAiBA,SAAS,aAAa,EACpB,UACA,OACA,QAAQ,gBACR,WACA,GAAG,SACiB;CACpB,MAAM,WAAW,MAAM,OAAuB,IAAI;CAClD,MAAM,UAAU,MAAM,OAAuB,IAAI;CACjD,MAAM,SAAS,MAAM,OAAO,IAAI;CAChC,MAAM,CAAC,UAAU,eAAe,MAAM,SAAS,IAAI;CACnD,MAAM,cAAc,MAAM,SAAS,QAAQ,QAAQ,CAAC,CAAC,SAAS;CAE9D,MAAM,iBAAiB,MAAM,aAAa,WAA2B,WAAW;EAC9E,MAAM,KAAK,SAAS;EACpB,IAAI,CAAC,IAAI;EACT,IAAI,OAAO,GAAG,aAAa,YAAY,GAAG,SAAS;GAAE,KAAK,GAAG;GAAc;EAAS,CAAC;OAChF,GAAG,YAAY,GAAG;CACzB,GAAG,CAAC,CAAC;CAEL,MAAM,gBAAgB;EACpB,MAAM,KAAK,QAAQ;EACnB,IAAI,CAAC,MAAM,OAAO,mBAAmB,aAAa;EAElD,MAAM,KAAK,IAAI,qBAAqB;GAClC,IAAI,OAAO,SAAS,eAAe;EACrC,CAAC;EACD,GAAG,QAAQ,EAAE;EACb,aAAa,GAAG,WAAW;CAC7B,GAAG,CAAC,cAAc,CAAC;CAEnB,MAAM,gBAAgB;EACpB,IAAI,OAAO,SAAS,eAAe;CACrC,GAAG,CAAC,UAAU,cAAc,CAAC;CAE7B,MAAM,iBAAiB;EACrB,MAAM,KAAK,SAAS;EACpB,IAAI,CAAC,IAAI;EACT,MAAM,SAAS,GAAG,eAAe,GAAG,YAAY,GAAG,eAAe;EAClE,OAAO,UAAU;EACjB,YAAY,MAAM;CACpB;CAEA,OACE,qBAAC,OAAD;EAAK,aAAU;EAAe,WAAW,GAAG,oBAAoB,SAAS;EAAG,GAAI;EAAhF,UAAA,CACE,oBAAC,OAAD;GACE,KAAK;GACL,MAAK;GACL,cAAY;GACZ,aAAU;GACV,iBAAc;GAGd,UAAU;GACA;GACV,WAAU;GAEV,UAAA,oBAAC,OAAD;IAAK,KAAK;IAAS,WAAU;IAC1B,UAAA,cAAc,WAAW;GACvB,CAAA;EACF,CAAA,GACJ,CAAC,YACA,qBAAC,UAAD;GACE,MAAK;GACL,eAAe;IACb,OAAO,UAAU;IACjB,YAAY,IAAI;IAChB,eACE,OAAO,eAAe,cACpB,WAAW,kCAAkC,CAAC,CAAC,UAC7C,SACA,QACN;GACF;GACA,WAAU;GAZZ,UAAA,CAcE,oBAAC,WAAD,EAAW,eAAA,KAAa,CAAA,GAAC,iBACnB;EAEP,CAAA,CAAA;;AAET"}
1
+ {"version":3,"file":"conversation.js","names":[],"sources":["../../src/components/conversation.tsx"],"sourcesContent":["import ArrowDown from '@burtson-labs/icons/react/arrow-down';\nimport * as React from 'react';\n\nimport { cn, focusRingClasses } from '../lib/utils';\n\nexport interface ConversationProps extends React.ComponentProps<'div'> {\n /** Shown instead of the messages when there are none. */\n empty?: React.ReactNode;\n /** Accessible name for the message log. */\n label?: string;\n}\n\n/**\n * A scrolling message log that stays pinned to the newest message while it\n * streams, unless the reader has scrolled up; then a \"Jump to latest\" button\n * appears instead of yanking them back down.\n */\nconst Conversation = React.forwardRef<HTMLDivElement, ConversationProps>(function Conversation(\n { children, empty, label = 'Conversation', className, ...props },\n ref,\n) {\n const scroller = React.useRef<HTMLDivElement>(null);\n const content = React.useRef<HTMLDivElement>(null);\n const pinned = React.useRef(true);\n const [atBottom, setAtBottom] = React.useState(true);\n const hasMessages = React.Children.toArray(children).length > 0;\n\n const scrollToBottom = React.useCallback((behavior: ScrollBehavior = 'auto') => {\n const el = scroller.current;\n if (!el) return;\n if (typeof el.scrollTo === 'function') el.scrollTo({ top: el.scrollHeight, behavior });\n else el.scrollTop = el.scrollHeight;\n }, []);\n\n React.useEffect(() => {\n const el = content.current;\n if (!el || typeof ResizeObserver === 'undefined') return;\n // Content grows while a reply streams; follow it only if pinned.\n const ro = new ResizeObserver(() => {\n if (pinned.current) scrollToBottom();\n });\n ro.observe(el);\n return () => ro.disconnect();\n }, [scrollToBottom]);\n\n React.useEffect(() => {\n if (pinned.current) scrollToBottom();\n }, [children, scrollToBottom]);\n\n const onScroll = () => {\n const el = scroller.current;\n if (!el) return;\n const bottom = el.scrollHeight - el.scrollTop - el.clientHeight < 32;\n pinned.current = bottom;\n setAtBottom(bottom);\n };\n\n return (\n <div\n ref={ref}\n data-slot=\"conversation\"\n className={cn('relative min-h-0', className)}\n {...props}\n >\n <div\n ref={scroller}\n role=\"log\"\n aria-label={label}\n aria-live=\"polite\"\n aria-relevant=\"additions text\"\n // A scrollable region must be reachable by keyboard (WCAG 2.1.1).\n // eslint-disable-next-line jsx-a11y/no-noninteractive-tabindex\n tabIndex={0}\n onScroll={onScroll}\n className={cn(\n 'size-full overflow-y-auto',\n focusRingClasses,\n 'focus-visible:outline-offset-[-2px]',\n )}\n >\n <div ref={content} className=\"grid gap-5 p-4\">\n {hasMessages ? children : empty}\n </div>\n </div>\n {!atBottom && (\n <button\n type=\"button\"\n onClick={() => {\n pinned.current = true;\n setAtBottom(true);\n scrollToBottom(\n typeof matchMedia === 'function' &&\n matchMedia('(prefers-reduced-motion: reduce)').matches\n ? 'auto'\n : 'smooth',\n );\n }}\n className={cn(\n 'absolute bottom-3 left-1/2 inline-flex -translate-x-1/2 animate-in items-center gap-1.5 rounded-full border border-border-strong bg-surface-raised px-3 py-1.5 text-xs font-semibold shadow-md hover:bg-muted [&_svg]:size-3.5 pointer-coarse:min-h-11',\n focusRingClasses,\n )}\n >\n <ArrowDown aria-hidden /> Jump to latest\n </button>\n )}\n </div>\n );\n});\n\nexport { Conversation };\n"],"mappings":";;;;;;;;;;;AAiBA,IAAM,eAAe,MAAM,WAA8C,SAAS,aAChF,EAAE,UAAU,OAAO,QAAQ,gBAAgB,WAAW,GAAG,SACzD,KACA;CACA,MAAM,WAAW,MAAM,OAAuB,IAAI;CAClD,MAAM,UAAU,MAAM,OAAuB,IAAI;CACjD,MAAM,SAAS,MAAM,OAAO,IAAI;CAChC,MAAM,CAAC,UAAU,eAAe,MAAM,SAAS,IAAI;CACnD,MAAM,cAAc,MAAM,SAAS,QAAQ,QAAQ,CAAC,CAAC,SAAS;CAE9D,MAAM,iBAAiB,MAAM,aAAa,WAA2B,WAAW;EAC9E,MAAM,KAAK,SAAS;EACpB,IAAI,CAAC,IAAI;EACT,IAAI,OAAO,GAAG,aAAa,YAAY,GAAG,SAAS;GAAE,KAAK,GAAG;GAAc;EAAS,CAAC;OAChF,GAAG,YAAY,GAAG;CACzB,GAAG,CAAC,CAAC;CAEL,MAAM,gBAAgB;EACpB,MAAM,KAAK,QAAQ;EACnB,IAAI,CAAC,MAAM,OAAO,mBAAmB,aAAa;EAElD,MAAM,KAAK,IAAI,qBAAqB;GAClC,IAAI,OAAO,SAAS,eAAe;EACrC,CAAC;EACD,GAAG,QAAQ,EAAE;EACb,aAAa,GAAG,WAAW;CAC7B,GAAG,CAAC,cAAc,CAAC;CAEnB,MAAM,gBAAgB;EACpB,IAAI,OAAO,SAAS,eAAe;CACrC,GAAG,CAAC,UAAU,cAAc,CAAC;CAE7B,MAAM,iBAAiB;EACrB,MAAM,KAAK,SAAS;EACpB,IAAI,CAAC,IAAI;EACT,MAAM,SAAS,GAAG,eAAe,GAAG,YAAY,GAAG,eAAe;EAClE,OAAO,UAAU;EACjB,YAAY,MAAM;CACpB;CAEA,OACE,qBAAC,OAAD;EACO;EACL,aAAU;EACV,WAAW,GAAG,oBAAoB,SAAS;EAC3C,GAAI;EAJN,UAAA,CAME,oBAAC,OAAD;GACE,KAAK;GACL,MAAK;GACL,cAAY;GACZ,aAAU;GACV,iBAAc;GAGd,UAAU;GACA;GACV,WAAW,GACT,6BACA,kBACA,qCACF;GAEA,UAAA,oBAAC,OAAD;IAAK,KAAK;IAAS,WAAU;IAC1B,UAAA,cAAc,WAAW;GACvB,CAAA;EACF,CAAA,GACJ,CAAC,YACA,qBAAC,UAAD;GACE,MAAK;GACL,eAAe;IACb,OAAO,UAAU;IACjB,YAAY,IAAI;IAChB,eACE,OAAO,eAAe,cACpB,WAAW,kCAAkC,CAAC,CAAC,UAC7C,SACA,QACN;GACF;GACA,WAAW,GACT,0PAAA,4HAEF;GAfF,UAAA,CAiBE,oBAAC,WAAD,EAAW,eAAA,KAAa,CAAA,GAAC,iBACnB;EAEP,CAAA,CAAA;;AAET,CAAC"}
@@ -13,5 +13,5 @@ export interface CopyButtonProps extends Omit<IconButtonProps, 'label' | 'childr
13
13
  * browser blocks the clipboard it raises a toast (needs a mounted Toaster)
14
14
  * asking the person to copy by hand.
15
15
  */
16
- declare function CopyButton({ value, label, variant, size, onCopySuccess, onCopyError, ...props }: CopyButtonProps): React.JSX.Element;
16
+ declare const CopyButton: React.ForwardRefExoticComponent<Omit<CopyButtonProps, "ref"> & React.RefAttributes<HTMLButtonElement>>;
17
17
  export { CopyButton };
@@ -11,7 +11,7 @@ import Copy from "@burtson-labs/icons/react/copy";
11
11
  * browser blocks the clipboard it raises a toast (needs a mounted Toaster)
12
12
  * asking the person to copy by hand.
13
13
  */
14
- function CopyButton({ value, label = "Copy", variant = "ghost", size = "icon-sm", onCopySuccess, onCopyError, ...props }) {
14
+ var CopyButton = React.forwardRef(function CopyButton({ value, label = "Copy", variant = "ghost", size = "icon-sm", onCopySuccess, onCopyError, ...props }, ref) {
15
15
  const [message, setMessage] = React.useState("");
16
16
  const [copied, setCopied] = React.useState(false);
17
17
  const timer = React.useRef(void 0);
@@ -35,6 +35,7 @@ function CopyButton({ value, label = "Copy", variant = "ghost", size = "icon-sm"
35
35
  }
36
36
  };
37
37
  return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(IconButton, {
38
+ ref,
38
39
  "data-slot": "copy-button",
39
40
  label: copied ? "Copied" : label,
40
41
  variant,
@@ -50,7 +51,7 @@ function CopyButton({ value, label = "Copy", variant = "ghost", size = "icon-sm"
50
51
  className: "sr-only",
51
52
  children: message
52
53
  })] });
53
- }
54
+ });
54
55
  //#endregion
55
56
  export { CopyButton };
56
57
 
@@ -1 +1 @@
1
- {"version":3,"file":"copy-button.js","names":["_Fragment"],"sources":["../../src/components/copy-button.tsx"],"sourcesContent":["import Check from '@burtson-labs/icons/react/check';\nimport Copy from '@burtson-labs/icons/react/copy';\nimport * as React from 'react';\n\nimport { IconButton, type IconButtonProps } from './icon-button';\nimport { toast } from './toaster';\n\nexport interface CopyButtonProps extends Omit<IconButtonProps, 'label' | 'children' | 'onClick'> {\n /** The text to put on the clipboard. */\n value: string;\n /** Accessible name, e.g. \"Copy API key\". */\n label?: string;\n onCopySuccess?: () => void;\n onCopyError?: (error: unknown) => void;\n}\n\n/**\n * Icon button that copies `value` and shows a check for a moment. When the\n * browser blocks the clipboard it raises a toast (needs a mounted Toaster)\n * asking the person to copy by hand.\n */\nfunction CopyButton({\n value,\n label = 'Copy',\n variant = 'ghost',\n size = 'icon-sm',\n onCopySuccess,\n onCopyError,\n ...props\n}: CopyButtonProps) {\n const [message, setMessage] = React.useState('');\n const [copied, setCopied] = React.useState(false);\n const timer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n React.useEffect(() => () => clearTimeout(timer.current), []);\n\n const copy = async () => {\n try {\n await navigator.clipboard.writeText(value);\n setCopied(true);\n setMessage('Copied to clipboard.');\n onCopySuccess?.();\n clearTimeout(timer.current);\n timer.current = setTimeout(() => setCopied(false), 1600);\n } catch (error) {\n setMessage('Copy blocked. Select the text and copy it manually.');\n onCopyError?.(error);\n toast({\n title: 'Copy blocked by the browser',\n description: 'Select the text and copy it by hand.',\n variant: 'destructive',\n });\n }\n };\n\n return (\n <>\n <IconButton\n data-slot=\"copy-button\"\n label={copied ? 'Copied' : label}\n variant={variant}\n size={size}\n onClick={() => void copy()}\n {...props}\n >\n {copied ? <Check aria-hidden className=\"text-success\" /> : <Copy aria-hidden />}\n </IconButton>\n <span role=\"status\" className=\"sr-only\">\n {message}\n </span>\n </>\n );\n}\n\nexport { CopyButton };\n"],"mappings":";;;;;;;;;;;;;AAqBA,SAAS,WAAW,EAClB,OACA,QAAQ,QACR,UAAU,SACV,OAAO,WACP,eACA,aACA,GAAG,SACe;CAClB,MAAM,CAAC,SAAS,cAAc,MAAM,SAAS,EAAE;CAC/C,MAAM,CAAC,QAAQ,aAAa,MAAM,SAAS,KAAK;CAChD,MAAM,QAAQ,MAAM,OAAkD,KAAA,CAAS;CAC/E,MAAM,sBAAsB,aAAa,MAAM,OAAO,GAAG,CAAC,CAAC;CAE3D,MAAM,OAAO,YAAY;EACvB,IAAI;GACF,MAAM,UAAU,UAAU,UAAU,KAAK;GACzC,UAAU,IAAI;GACd,WAAW,sBAAsB;GACjC,gBAAgB;GAChB,aAAa,MAAM,OAAO;GAC1B,MAAM,UAAU,iBAAiB,UAAU,KAAK,GAAG,IAAI;EACzD,SAAS,OAAO;GACd,WAAW,qDAAqD;GAChE,cAAc,KAAK;GACnB,MAAM;IACJ,OAAO;IACP,aAAa;IACb,SAAS;GACX,CAAC;EACH;CACF;CAEA,OACE,qBAAAA,UAAA,EAAA,UAAA,CACE,oBAAC,YAAD;EACE,aAAU;EACV,OAAO,SAAS,WAAW;EAClB;EACH;EACN,eAAe,KAAK,KAAK;EACzB,GAAI;EAEH,UAAA,SAAS,oBAAC,OAAD;GAAO,eAAA;GAAY,WAAU;EAAgB,CAAA,IAAI,oBAAC,MAAD,EAAM,eAAA,KAAa,CAAA;CACpE,CAAA,GACZ,oBAAC,QAAD;EAAM,MAAK;EAAS,WAAU;EAC3B,UAAA;CACG,CAAA,CACN,EAAA,CAAA;AAEN"}
1
+ {"version":3,"file":"copy-button.js","names":["_Fragment"],"sources":["../../src/components/copy-button.tsx"],"sourcesContent":["import Check from '@burtson-labs/icons/react/check';\nimport Copy from '@burtson-labs/icons/react/copy';\nimport * as React from 'react';\n\nimport { IconButton, type IconButtonProps } from './icon-button';\nimport { toast } from './toaster';\n\nexport interface CopyButtonProps extends Omit<IconButtonProps, 'label' | 'children' | 'onClick'> {\n /** The text to put on the clipboard. */\n value: string;\n /** Accessible name, e.g. \"Copy API key\". */\n label?: string;\n onCopySuccess?: () => void;\n onCopyError?: (error: unknown) => void;\n}\n\n/**\n * Icon button that copies `value` and shows a check for a moment. When the\n * browser blocks the clipboard it raises a toast (needs a mounted Toaster)\n * asking the person to copy by hand.\n */\nconst CopyButton = React.forwardRef<HTMLButtonElement, CopyButtonProps>(function CopyButton(\n {\n value,\n label = 'Copy',\n variant = 'ghost',\n size = 'icon-sm',\n onCopySuccess,\n onCopyError,\n ...props\n },\n ref,\n) {\n const [message, setMessage] = React.useState('');\n const [copied, setCopied] = React.useState(false);\n const timer = React.useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n React.useEffect(() => () => clearTimeout(timer.current), []);\n\n const copy = async () => {\n try {\n await navigator.clipboard.writeText(value);\n setCopied(true);\n setMessage('Copied to clipboard.');\n onCopySuccess?.();\n clearTimeout(timer.current);\n timer.current = setTimeout(() => setCopied(false), 1600);\n } catch (error) {\n setMessage('Copy blocked. Select the text and copy it manually.');\n onCopyError?.(error);\n toast({\n title: 'Copy blocked by the browser',\n description: 'Select the text and copy it by hand.',\n variant: 'destructive',\n });\n }\n };\n\n return (\n <>\n <IconButton\n ref={ref}\n data-slot=\"copy-button\"\n label={copied ? 'Copied' : label}\n variant={variant}\n size={size}\n onClick={() => void copy()}\n {...props}\n >\n {copied ? <Check aria-hidden className=\"text-success\" /> : <Copy aria-hidden />}\n </IconButton>\n <span role=\"status\" className=\"sr-only\">\n {message}\n </span>\n </>\n );\n});\n\nexport { CopyButton };\n"],"mappings":";;;;;;;;;;;;;AAqBA,IAAM,aAAa,MAAM,WAA+C,SAAS,WAC/E,EACE,OACA,QAAQ,QACR,UAAU,SACV,OAAO,WACP,eACA,aACA,GAAG,SAEL,KACA;CACA,MAAM,CAAC,SAAS,cAAc,MAAM,SAAS,EAAE;CAC/C,MAAM,CAAC,QAAQ,aAAa,MAAM,SAAS,KAAK;CAChD,MAAM,QAAQ,MAAM,OAAkD,KAAA,CAAS;CAC/E,MAAM,sBAAsB,aAAa,MAAM,OAAO,GAAG,CAAC,CAAC;CAE3D,MAAM,OAAO,YAAY;EACvB,IAAI;GACF,MAAM,UAAU,UAAU,UAAU,KAAK;GACzC,UAAU,IAAI;GACd,WAAW,sBAAsB;GACjC,gBAAgB;GAChB,aAAa,MAAM,OAAO;GAC1B,MAAM,UAAU,iBAAiB,UAAU,KAAK,GAAG,IAAI;EACzD,SAAS,OAAO;GACd,WAAW,qDAAqD;GAChE,cAAc,KAAK;GACnB,MAAM;IACJ,OAAO;IACP,aAAa;IACb,SAAS;GACX,CAAC;EACH;CACF;CAEA,OACE,qBAAAA,UAAA,EAAA,UAAA,CACE,oBAAC,YAAD;EACO;EACL,aAAU;EACV,OAAO,SAAS,WAAW;EAClB;EACH;EACN,eAAe,KAAK,KAAK;EACzB,GAAI;EAEH,UAAA,SAAS,oBAAC,OAAD;GAAO,eAAA;GAAY,WAAU;EAAgB,CAAA,IAAI,oBAAC,MAAD,EAAM,eAAA,KAAa,CAAA;CACpE,CAAA,GACZ,oBAAC,QAAD;EAAM,MAAK;EAAS,WAAU;EAC3B,UAAA;CACG,CAAA,CACN,EAAA,CAAA;AAEN,CAAC"}
@@ -98,9 +98,14 @@ export interface DataTableProps<T> extends Omit<React.ComponentProps<'div'>, 'ch
98
98
  stickyHeader?: boolean;
99
99
  /**
100
100
  * Below this width the rows become a list of cards (see DataTableColumn
101
- * `card`). `md` (768px) by default; `false` keeps the table everywhere.
101
+ * `card`). Cards need a column that says where it goes: with no `card`
102
+ * placement and no `cardsBelow`, the table stays a table (a scrolling
103
+ * region) at every width. Set `cardsBelow` (`md` is 768px) to get cards
104
+ * with the first column as the title; `false` keeps the table.
102
105
  */
103
106
  cardsBelow?: DataTableBreakpoint | false;
107
+ /** Keep the first column in view while the table scrolls sideways. */
108
+ pinFirstColumn?: boolean;
104
109
  }
105
110
  /**
106
111
  * A table recipe for records: sort, search, select, page, and row actions,
@@ -108,5 +113,5 @@ export interface DataTableProps<T> extends Omit<React.ComponentProps<'div'>, 'ch
108
113
  * come from a server; the table never reorders rows itself. On a phone the
109
114
  * rows become cards (`cardsBelow`), with the same columns.
110
115
  */
111
- declare function DataTable<T>({ 'aria-label': ariaLabel, columns, rows: rowsProp, getRowId, getRowLabel, sort, onSortChange, filter, onFilterChange, filterPlaceholder, selected, onSelectedChange, onRowAction, rowActions, page, pageCount, onPageChange, pageSummary, pageSize, pageSizeOptions, onPageSizeChange, totalRows, paginate, rowNoun, loading, error, onRetry, empty, toolbar, density, stickyHeader, cardsBelow, className, ...props }: DataTableProps<T>): React.JSX.Element;
116
+ declare function DataTable<T>({ 'aria-label': ariaLabel, columns, rows: rowsProp, getRowId, getRowLabel, sort, onSortChange, filter, onFilterChange, filterPlaceholder, selected, onSelectedChange, onRowAction, rowActions, page, pageCount, onPageChange, pageSummary, pageSize, pageSizeOptions, onPageSizeChange, totalRows, paginate, rowNoun, loading, error, onRetry, empty, toolbar, density, stickyHeader, cardsBelow, pinFirstColumn, className, ...props }: DataTableProps<T>): React.JSX.Element;
112
117
  export { DataTable };
@@ -1,7 +1,8 @@
1
1
  "use client";
2
- import { cn } from "../lib/utils.js";
2
+ import { cn, focusRingClasses, noOutlineClasses } from "../lib/utils.js";
3
+ import { useNarrowerThan } from "../lib/media.js";
3
4
  import { Button } from "./button.js";
4
- import { Input } from "./input.js";
5
+ import { SearchInput } from "./input.js";
5
6
  import { Checkbox } from "./checkbox.js";
6
7
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./select.js";
7
8
  import { Pagination, pageWindow, paginationSummary, usePagination } from "./pagination.js";
@@ -9,38 +10,16 @@ import { Skeleton } from "./skeleton.js";
9
10
  import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table.js";
10
11
  import * as React from "react";
11
12
  import { jsx, jsxs } from "react/jsx-runtime";
12
- import Search from "@burtson-labs/icons/react/search";
13
13
  import ChevronsUpDown from "@burtson-labs/icons/react/chevrons-up-down";
14
14
  import ArrowUp from "@burtson-labs/icons/react/arrow-up";
15
15
  import ArrowDown from "@burtson-labs/icons/react/arrow-down";
16
16
  //#region src/components/data-table.tsx
17
- var BREAKPOINTS = {
18
- sm: 640,
19
- md: 768,
20
- lg: 1024,
21
- xl: 1280
22
- };
23
17
  var hideBelowClass = {
24
18
  sm: "hidden sm:table-cell",
25
19
  md: "hidden md:table-cell",
26
20
  lg: "hidden lg:table-cell",
27
21
  xl: "hidden xl:table-cell"
28
22
  };
29
- var subscribeNone = () => () => void 0;
30
- /**
31
- * True while the viewport is narrower than `bp`. Without matchMedia (server
32
- * render, tests) it is false, so the table is the fallback.
33
- */
34
- function useNarrowerThan(bp) {
35
- const query = bp ? `(max-width: ${BREAKPOINTS[bp] - .02}px)` : null;
36
- const subscribe = React.useCallback((onChange) => {
37
- if (!query || typeof matchMedia !== "function") return () => void 0;
38
- const mq = matchMedia(query);
39
- mq.addEventListener?.("change", onChange);
40
- return () => mq.removeEventListener?.("change", onChange);
41
- }, [query]);
42
- return React.useSyncExternalStore(query ? subscribe : subscribeNone, () => Boolean(query && typeof matchMedia === "function" && matchMedia(query).matches), () => false);
43
- }
44
23
  var textOf = (node) => typeof node === "string" || typeof node === "number" ? String(node) : void 0;
45
24
  var nextSort = (current, id) => {
46
25
  if (!current || current.columnId !== id) return {
@@ -59,7 +38,7 @@ var INTERACTIVE = "a,button,input,select,textarea,[role=\"checkbox\"],[role=\"me
59
38
  * come from a server; the table never reorders rows itself. On a phone the
60
39
  * rows become cards (`cardsBelow`), with the same columns.
61
40
  */
62
- function DataTable({ "aria-label": ariaLabel, columns, rows: rowsProp, getRowId, getRowLabel, sort, onSortChange, filter, onFilterChange, filterPlaceholder = "Search", selected, onSelectedChange, onRowAction, rowActions, page, pageCount, onPageChange, pageSummary, pageSize, pageSizeOptions, onPageSizeChange, totalRows, paginate, rowNoun, loading = false, error, onRetry, empty = "No results.", toolbar, density = "default", stickyHeader, cardsBelow = "md", className, ...props }) {
41
+ function DataTable({ "aria-label": ariaLabel, columns, rows: rowsProp, getRowId, getRowLabel, sort, onSortChange, filter, onFilterChange, filterPlaceholder = "Search", selected, onSelectedChange, onRowAction, rowActions, page, pageCount, onPageChange, pageSummary, pageSize, pageSizeOptions, onPageSizeChange, totalRows, paginate, rowNoun, loading = false, error, onRetry, empty = "No results.", toolbar, density = "default", stickyHeader, cardsBelow, pinFirstColumn, className, ...props }) {
63
42
  const paginateOptions = paginate === true ? {} : paginate || void 0;
64
43
  const paging = usePagination(rowsProp, {
65
44
  defaultPageSize: paginateOptions?.pageSize,
@@ -68,7 +47,8 @@ function DataTable({ "aria-label": ariaLabel, columns, rows: rowsProp, getRowId,
68
47
  resetKey: `${filter ?? ""}\u0000${sort?.columnId ?? ""}\u0000${sort?.direction ?? ""}`
69
48
  });
70
49
  const rows = paginateOptions ? paging.rows : rowsProp;
71
- const cards = useNarrowerThan(cardsBelow);
50
+ const placed = columns.some((c) => c.card !== void 0);
51
+ const cards = useNarrowerThan(cardsBelow ?? (placed ? "md" : false));
72
52
  const uid = React.useId();
73
53
  const selectable = Boolean(onSelectedChange);
74
54
  const selectedSet = React.useMemo(() => new Set(selected ?? []), [selected]);
@@ -196,19 +176,12 @@ function DataTable({ "aria-label": ariaLabel, columns, rows: rowsProp, getRowId,
196
176
  const toolbarNode = showToolbar && /* @__PURE__ */ jsxs("div", {
197
177
  className: "flex flex-wrap items-center gap-2",
198
178
  children: [
199
- onFilterChange && /* @__PURE__ */ jsxs("div", {
200
- className: "relative min-w-0 basis-full sm:max-w-xs sm:flex-1 sm:basis-0",
201
- children: [/* @__PURE__ */ jsx(Search, {
202
- className: "pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground",
203
- "aria-hidden": true
204
- }), /* @__PURE__ */ jsx(Input, {
205
- type: "search",
206
- value: filter ?? "",
207
- onChange: (e) => onFilterChange(e.target.value),
208
- placeholder: filterPlaceholder,
209
- "aria-label": `Search ${ariaLabel.toLowerCase()}`,
210
- className: "pl-8"
211
- })]
179
+ onFilterChange && /* @__PURE__ */ jsx(SearchInput, {
180
+ value: filter ?? "",
181
+ onChange: (e) => onFilterChange(e.target.value),
182
+ placeholder: filterPlaceholder,
183
+ "aria-label": `Search ${ariaLabel.toLowerCase()}`,
184
+ containerClassName: "min-w-0 basis-full sm:max-w-xs sm:flex-1 sm:basis-0"
212
185
  }),
213
186
  cardSelectAll,
214
187
  selectable && /* @__PURE__ */ jsx("span", {
@@ -225,7 +198,7 @@ function DataTable({ "aria-label": ariaLabel, columns, rows: rowsProp, getRowId,
225
198
  return /* @__PURE__ */ jsxs("div", {
226
199
  "data-slot": "data-table",
227
200
  "data-layout": cards ? "cards" : "table",
228
- className: cn("grid min-w-0 grid-cols-[minmax(0,1fr)] gap-3", className),
201
+ className: cn("grid w-full min-w-0 grid-cols-[minmax(0,1fr)] gap-3", className),
229
202
  ...props,
230
203
  children: [
231
204
  toolbarNode,
@@ -252,6 +225,7 @@ function DataTable({ "aria-label": ariaLabel, columns, rows: rowsProp, getRowId,
252
225
  "aria-busy": loading || void 0,
253
226
  density,
254
227
  stickyHeader,
228
+ pinFirstColumn,
255
229
  className: cn(loading && rows.length > 0 && "opacity-60 transition-opacity"),
256
230
  children: [/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, {
257
231
  className: "hover:bg-transparent",
@@ -274,7 +248,7 @@ function DataTable({ "aria-label": ariaLabel, columns, rows: rowsProp, getRowId,
274
248
  children: col.sortable && onSortChange ? /* @__PURE__ */ jsxs("button", {
275
249
  type: "button",
276
250
  onClick: () => onSortChange(nextSort(sort, col.id)),
277
- className: cn("-mx-1.5 inline-flex h-7 items-center gap-1 rounded-sm px-1.5 uppercase outline-none hover:bg-muted hover:text-foreground focus-visible:ring-[3px] focus-visible:ring-ring/20", sorted && "text-foreground", col.numeric && "flex-row-reverse"),
251
+ className: cn("-mx-1.5 inline-flex h-7 items-center gap-1 rounded-sm px-1.5 uppercase hover:bg-muted hover:text-foreground", focusRingClasses, "focus-visible:outline-offset-[-2px]", sorted && "text-foreground", col.numeric && "flex-row-reverse"),
278
252
  children: [col.header, sorted === "asc" ? /* @__PURE__ */ jsx(ArrowUp, {
279
253
  className: "size-3.5",
280
254
  "aria-hidden": true
@@ -450,7 +424,7 @@ function DataTableCards({ "aria-label": ariaLabel, columns, rows, getRowId, getR
450
424
  type: "button",
451
425
  "data-row-action": "",
452
426
  onClick: () => onRowAction(row),
453
- className: "text-left outline-hidden! after:absolute after:inset-0 after:rounded-lg",
427
+ className: cn("text-left after:absolute after:inset-0 after:rounded-lg", noOutlineClasses),
454
428
  children: show(c, row)
455
429
  }) : show(c, row)
456
430
  }, c.id))