cortena-ui 1.3.0 → 1.4.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 (280) hide show
  1. package/README.md +74 -3
  2. package/dist/a2ui/catalogue-doc.d.ts +44 -0
  3. package/dist/a2ui/catalogue-doc.js +169 -0
  4. package/dist/a2ui/catalogue-doc.js.map +1 -0
  5. package/dist/a2ui/catalogue.d.ts +44 -0
  6. package/dist/a2ui/catalogue.js +942 -0
  7. package/dist/a2ui/catalogue.js.map +1 -0
  8. package/dist/a2ui/index.d.ts +7 -0
  9. package/dist/a2ui/message.d.ts +232 -0
  10. package/dist/a2ui/message.js +365 -0
  11. package/dist/a2ui/message.js.map +1 -0
  12. package/dist/a2ui/renderer.d.ts +46 -0
  13. package/dist/a2ui/renderer.js +252 -0
  14. package/dist/a2ui/renderer.js.map +1 -0
  15. package/dist/a2ui/views.d.ts +80 -0
  16. package/dist/a2ui/views.js +596 -0
  17. package/dist/a2ui/views.js.map +1 -0
  18. package/dist/a2ui.d.ts +8 -0
  19. package/dist/a2ui.js +7 -0
  20. package/dist/chart.d.ts +9 -0
  21. package/dist/chart.js +9 -0
  22. package/dist/components/accordion.d.ts +22 -0
  23. package/dist/components/accordion.js +63 -0
  24. package/dist/components/accordion.js.map +1 -0
  25. package/dist/components/alert.d.ts +15 -0
  26. package/dist/components/alert.js +64 -0
  27. package/dist/components/alert.js.map +1 -0
  28. package/dist/components/avatar.d.ts +18 -0
  29. package/dist/components/avatar.js +40 -0
  30. package/dist/components/avatar.js.map +1 -0
  31. package/dist/components/badge.d.ts +12 -0
  32. package/dist/components/badge.js +47 -0
  33. package/dist/components/badge.js.map +1 -0
  34. package/dist/components/breadcrumb.d.ts +71 -0
  35. package/dist/components/breadcrumb.js +123 -0
  36. package/dist/components/breadcrumb.js.map +1 -0
  37. package/dist/components/button-link.d.ts +28 -0
  38. package/dist/components/button-link.js +24 -0
  39. package/dist/components/button-link.js.map +1 -0
  40. package/dist/components/button.d.ts +24 -0
  41. package/dist/components/button.js +65 -0
  42. package/dist/components/button.js.map +1 -0
  43. package/dist/components/calendar.d.ts +28 -0
  44. package/dist/components/calendar.js +123 -0
  45. package/dist/components/calendar.js.map +1 -0
  46. package/dist/components/card.d.ts +32 -0
  47. package/dist/components/card.js +57 -0
  48. package/dist/components/card.js.map +1 -0
  49. package/dist/components/chart/chart.d.ts +71 -0
  50. package/dist/components/chart/chart.js +167 -0
  51. package/dist/components/chart/chart.js.map +1 -0
  52. package/dist/components/chart/container.d.ts +88 -0
  53. package/dist/components/chart/container.js +189 -0
  54. package/dist/components/chart/container.js.map +1 -0
  55. package/dist/components/chart/data.js +128 -0
  56. package/dist/components/chart/data.js.map +1 -0
  57. package/dist/components/chart/index.d.ts +9 -0
  58. package/dist/components/chart/index.js +10 -0
  59. package/dist/components/chart/nivo-charts.js +225 -0
  60. package/dist/components/chart/nivo-charts.js.map +1 -0
  61. package/dist/components/chart/nivo-lazy.js +50 -0
  62. package/dist/components/chart/nivo-lazy.js.map +1 -0
  63. package/dist/components/chart/nivo-theme.d.ts +156 -0
  64. package/dist/components/chart/nivo-theme.js +161 -0
  65. package/dist/components/chart/nivo-theme.js.map +1 -0
  66. package/dist/components/chart/recharts-charts.js +276 -0
  67. package/dist/components/chart/recharts-charts.js.map +1 -0
  68. package/dist/components/chart/recharts-lazy.js +58 -0
  69. package/dist/components/chart/recharts-lazy.js.map +1 -0
  70. package/dist/components/chart/recharts-parts.d.ts +10 -0
  71. package/dist/components/chart/recharts-parts.js +25 -0
  72. package/dist/components/chart/recharts-parts.js.map +1 -0
  73. package/dist/components/chart/tokens.d.ts +62 -0
  74. package/dist/components/chart/tokens.js +192 -0
  75. package/dist/components/chart/tokens.js.map +1 -0
  76. package/dist/components/chart/types.d.ts +114 -0
  77. package/dist/components/chart/types.js +27 -0
  78. package/dist/components/chart/types.js.map +1 -0
  79. package/dist/components/checkbox.d.ts +20 -0
  80. package/dist/components/checkbox.js +24 -0
  81. package/dist/components/checkbox.js.map +1 -0
  82. package/dist/components/chip.d.ts +17 -0
  83. package/dist/components/chip.js +47 -0
  84. package/dist/components/chip.js.map +1 -0
  85. package/dist/components/combobox.d.ts +76 -0
  86. package/dist/components/combobox.js +201 -0
  87. package/dist/components/combobox.js.map +1 -0
  88. package/dist/components/command.d.ts +50 -0
  89. package/dist/components/command.js +117 -0
  90. package/dist/components/command.js.map +1 -0
  91. package/dist/components/data-table/data-table.d.ts +65 -0
  92. package/dist/components/data-table/data-table.js +588 -0
  93. package/dist/components/data-table/data-table.js.map +1 -0
  94. package/dist/components/data-table/export.d.ts +23 -0
  95. package/dist/components/data-table/export.js +95 -0
  96. package/dist/components/data-table/export.js.map +1 -0
  97. package/dist/components/data-table/features.d.ts +66 -0
  98. package/dist/components/data-table/features.js +103 -0
  99. package/dist/components/data-table/features.js.map +1 -0
  100. package/dist/components/data-table/index.d.ts +8 -0
  101. package/dist/components/data-table/parts.d.ts +79 -0
  102. package/dist/components/data-table/parts.js +400 -0
  103. package/dist/components/data-table/parts.js.map +1 -0
  104. package/dist/components/data-table/system-columns.js +84 -0
  105. package/dist/components/data-table/system-columns.js.map +1 -0
  106. package/dist/components/data-table/types.d.ts +172 -0
  107. package/dist/components/data-table/use-data-table.d.ts +19 -0
  108. package/dist/components/data-table/use-data-table.js +171 -0
  109. package/dist/components/data-table/use-data-table.js.map +1 -0
  110. package/dist/components/data-table/use-server-source.js +200 -0
  111. package/dist/components/data-table/use-server-source.js.map +1 -0
  112. package/dist/components/date-field.d.ts +54 -0
  113. package/dist/components/date-field.js +235 -0
  114. package/dist/components/date-field.js.map +1 -0
  115. package/dist/components/date-picker.d.ts +42 -0
  116. package/dist/components/date-picker.js +125 -0
  117. package/dist/components/date-picker.js.map +1 -0
  118. package/dist/components/dialog.d.ts +36 -0
  119. package/dist/components/dialog.js +97 -0
  120. package/dist/components/dialog.js.map +1 -0
  121. package/dist/components/dropdown-menu.d.ts +54 -0
  122. package/dist/components/dropdown-menu.js +191 -0
  123. package/dist/components/dropdown-menu.js.map +1 -0
  124. package/dist/components/dropzone.d.ts +48 -0
  125. package/dist/components/dropzone.js +185 -0
  126. package/dist/components/dropzone.js.map +1 -0
  127. package/dist/components/empty-state.d.ts +22 -0
  128. package/dist/components/empty-state.js +46 -0
  129. package/dist/components/empty-state.js.map +1 -0
  130. package/dist/components/error-banner.d.ts +17 -0
  131. package/dist/components/error-banner.js +52 -0
  132. package/dist/components/error-banner.js.map +1 -0
  133. package/dist/components/field.d.ts +64 -0
  134. package/dist/components/field.js +133 -0
  135. package/dist/components/field.js.map +1 -0
  136. package/dist/components/form.d.ts +37 -0
  137. package/dist/components/form.js +103 -0
  138. package/dist/components/form.js.map +1 -0
  139. package/dist/components/input.d.ts +17 -0
  140. package/dist/components/input.js +26 -0
  141. package/dist/components/input.js.map +1 -0
  142. package/dist/components/kbd.d.ts +11 -0
  143. package/dist/components/kbd.js +20 -0
  144. package/dist/components/kbd.js.map +1 -0
  145. package/dist/components/label.d.ts +14 -0
  146. package/dist/components/label.js +28 -0
  147. package/dist/components/label.js.map +1 -0
  148. package/dist/components/loading-skeleton.d.ts +22 -0
  149. package/dist/components/loading-skeleton.js +123 -0
  150. package/dist/components/loading-skeleton.js.map +1 -0
  151. package/dist/components/markdown.d.ts +44 -0
  152. package/dist/components/markdown.js +231 -0
  153. package/dist/components/markdown.js.map +1 -0
  154. package/dist/components/page-header.d.ts +26 -0
  155. package/dist/components/page-header.js +53 -0
  156. package/dist/components/page-header.js.map +1 -0
  157. package/dist/components/popover.d.ts +29 -0
  158. package/dist/components/popover.js +74 -0
  159. package/dist/components/popover.js.map +1 -0
  160. package/dist/components/progress.d.ts +22 -0
  161. package/dist/components/progress.js +49 -0
  162. package/dist/components/progress.js.map +1 -0
  163. package/dist/components/radio-group.d.ts +20 -0
  164. package/dist/components/radio-group.js +29 -0
  165. package/dist/components/radio-group.js.map +1 -0
  166. package/dist/components/scroll-area.d.ts +25 -0
  167. package/dist/components/scroll-area.js +39 -0
  168. package/dist/components/scroll-area.js.map +1 -0
  169. package/dist/components/section-card.d.ts +29 -0
  170. package/dist/components/section-card.js +55 -0
  171. package/dist/components/section-card.js.map +1 -0
  172. package/dist/components/segmented.d.ts +27 -0
  173. package/dist/components/segmented.js +32 -0
  174. package/dist/components/segmented.js.map +1 -0
  175. package/dist/components/select.d.ts +62 -0
  176. package/dist/components/select.js +97 -0
  177. package/dist/components/select.js.map +1 -0
  178. package/dist/components/separator.d.ts +19 -0
  179. package/dist/components/separator.js +23 -0
  180. package/dist/components/separator.js.map +1 -0
  181. package/dist/components/sheet.d.ts +35 -0
  182. package/dist/components/sheet.js +131 -0
  183. package/dist/components/sheet.js.map +1 -0
  184. package/dist/components/skeleton.d.ts +12 -0
  185. package/dist/components/skeleton.js +22 -0
  186. package/dist/components/skeleton.js.map +1 -0
  187. package/dist/components/sortable-list.d.ts +67 -0
  188. package/dist/components/sortable-list.js +206 -0
  189. package/dist/components/sortable-list.js.map +1 -0
  190. package/dist/components/spinner.d.ts +29 -0
  191. package/dist/components/spinner.js +34 -0
  192. package/dist/components/spinner.js.map +1 -0
  193. package/dist/components/status-dot.d.ts +26 -0
  194. package/dist/components/status-dot.js +47 -0
  195. package/dist/components/status-dot.js.map +1 -0
  196. package/dist/components/switch.d.ts +21 -0
  197. package/dist/components/switch.js +21 -0
  198. package/dist/components/switch.js.map +1 -0
  199. package/dist/components/tabs.d.ts +24 -0
  200. package/dist/components/tabs.js +60 -0
  201. package/dist/components/tabs.js.map +1 -0
  202. package/dist/components/textarea.d.ts +19 -0
  203. package/dist/components/textarea.js +47 -0
  204. package/dist/components/textarea.js.map +1 -0
  205. package/dist/components/toast.d.ts +47 -0
  206. package/dist/components/toast.js +106 -0
  207. package/dist/components/toast.js.map +1 -0
  208. package/dist/components/toolbar.d.ts +19 -0
  209. package/dist/components/toolbar.js +45 -0
  210. package/dist/components/toolbar.js.map +1 -0
  211. package/dist/components/tooltip.d.ts +28 -0
  212. package/dist/components/tooltip.js +59 -0
  213. package/dist/components/tooltip.js.map +1 -0
  214. package/dist/core.d.ts +44 -0
  215. package/dist/core.js +44 -0
  216. package/dist/data-table.d.ts +9 -0
  217. package/dist/data-table.js +7 -0
  218. package/dist/form.d.ts +7 -0
  219. package/dist/form.js +7 -0
  220. package/dist/hooks/use-cortena-theme.d.ts +98 -0
  221. package/dist/hooks/use-cortena-theme.js +145 -0
  222. package/dist/hooks/use-cortena-theme.js.map +1 -0
  223. package/dist/index.d.ts +75 -2678
  224. package/dist/index.js +66 -8598
  225. package/dist/lib/cn.d.ts +10 -0
  226. package/dist/lib/cn.js +15 -0
  227. package/dist/lib/cn.js.map +1 -0
  228. package/dist/lib/render.d.ts +23 -0
  229. package/dist/lib/render.js +20 -0
  230. package/dist/lib/render.js.map +1 -0
  231. package/dist/lib/shakeable.js +39 -0
  232. package/dist/lib/shakeable.js.map +1 -0
  233. package/dist/markdown.d.ts +3 -0
  234. package/dist/markdown.js +3 -0
  235. package/dist/sortable-list.d.ts +3 -0
  236. package/dist/sortable-list.js +3 -0
  237. package/package.json +37 -14
  238. package/src/a2ui/catalogue.ts +140 -103
  239. package/src/a2ui/views.tsx +5 -2
  240. package/src/components/alert.tsx +1 -1
  241. package/src/components/badge.tsx +1 -1
  242. package/src/components/button.tsx +1 -1
  243. package/src/components/chart/chart.tsx +72 -25
  244. package/src/components/chart/container.tsx +24 -39
  245. package/src/components/chart/index.tsx +4 -2
  246. package/src/components/chart/nivo-charts.tsx +41 -13
  247. package/src/components/chart/nivo-lazy.tsx +52 -0
  248. package/src/components/chart/recharts-charts.tsx +98 -3
  249. package/src/components/chart/recharts-lazy.tsx +72 -0
  250. package/src/components/chart/recharts-parts.tsx +34 -0
  251. package/src/components/chart/tokens.ts +1 -1
  252. package/src/components/chart/types.ts +14 -0
  253. package/src/components/chip.tsx +1 -1
  254. package/src/components/combobox.tsx +2 -2
  255. package/src/components/data-table/export.ts +18 -2
  256. package/src/components/data-table/features.ts +77 -45
  257. package/src/components/data-table/parts.tsx +15 -0
  258. package/src/components/data-table/use-data-table.ts +7 -2
  259. package/src/components/data-table/use-server-source.ts +17 -4
  260. package/src/components/date-field.tsx +1 -1
  261. package/src/components/error-banner.tsx +1 -1
  262. package/src/components/field.tsx +2 -2
  263. package/src/components/form.tsx +2 -2
  264. package/src/components/input.tsx +1 -1
  265. package/src/components/label.tsx +1 -1
  266. package/src/components/markdown.tsx +5 -4
  267. package/src/components/sheet.tsx +2 -2
  268. package/src/components/sortable-list.tsx +2 -2
  269. package/src/components/textarea.tsx +1 -1
  270. package/src/components/toast.tsx +1 -1
  271. package/src/entries/a2ui.ts +11 -0
  272. package/src/entries/chart.ts +10 -0
  273. package/src/entries/core.ts +91 -0
  274. package/src/entries/data-table.ts +9 -0
  275. package/src/entries/form.ts +24 -0
  276. package/src/entries/markdown.ts +9 -0
  277. package/src/entries/sortable-list.ts +13 -0
  278. package/src/index.ts +32 -98
  279. package/src/lib/shakeable.ts +33 -0
  280. package/dist/index.js.map +0 -1
@@ -0,0 +1,54 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ import { Menu } from "@base-ui/react/menu";
4
+ //#region src/components/dropdown-menu.d.ts
5
+ /**
6
+ * DropdownMenu over Base UI's Menu.
7
+ *
8
+ * `DropdownMenuContent` and `DropdownMenuSubContent` compose Portal +
9
+ * Positioner + Popup, so consumers keep writing one element; `side`, `align`
10
+ * and `sideOffset` are forwarded to the Positioner where Base UI reads them.
11
+ *
12
+ * Radix users: `asChild` on the trigger becomes `render={<Button />}`;
13
+ * `onSelect` on items is `onClick`; `DropdownMenuLabel` is Base UI's
14
+ * `Menu.GroupLabel` (place it inside a `DropdownMenuGroup` for the
15
+ * `aria-labelledby` wiring); `DropdownMenuCheckboxItem` reports through
16
+ * `onCheckedChange(checked, eventDetails)`; `DropdownMenuRadioGroup` through
17
+ * `onValueChange(value, eventDetails)`. Highlight state is `data-highlighted`,
18
+ * not `:focus`.
19
+ */
20
+ declare function DropdownMenu(props: Menu.Root.Props): React.JSX.Element;
21
+ declare function DropdownMenuTrigger(props: Menu.Trigger.Props): React.JSX.Element;
22
+ declare function DropdownMenuPortal(props: Menu.Portal.Props): React.JSX.Element;
23
+ declare function DropdownMenuGroup(props: Menu.Group.Props): React.JSX.Element;
24
+ declare function DropdownMenuSub(props: Menu.SubmenuRoot.Props): React.JSX.Element;
25
+ declare function DropdownMenuRadioGroup(props: Menu.RadioGroup.Props): React.JSX.Element;
26
+ type PositionerProps = Pick<Menu.Positioner.Props, "side" | "align" | "sideOffset" | "alignOffset" | "anchor" | "collisionBoundary" | "collisionPadding" | "collisionAvoidance" | "sticky" | "positionMethod">;
27
+ export interface DropdownMenuContentProps extends Menu.Popup.Props, PositionerProps {
28
+ /** Keep the menu mounted in the DOM while hidden. */
29
+ keepMounted?: boolean;
30
+ /** Where to portal the menu; defaults to `<body>`. */
31
+ container?: Menu.Portal.Props["container"];
32
+ }
33
+ declare function DropdownMenuContent({ className, side, align, sideOffset, alignOffset, anchor, collisionBoundary, collisionPadding, collisionAvoidance, sticky, positionMethod, keepMounted, container, ...props }: DropdownMenuContentProps): React.JSX.Element;
34
+ declare function DropdownMenuSubContent({ className, side, align, sideOffset, alignOffset, anchor, collisionBoundary, collisionPadding, collisionAvoidance, sticky, positionMethod, keepMounted, container, ...props }: DropdownMenuContentProps): React.JSX.Element;
35
+ export interface DropdownMenuItemProps extends Menu.Item.Props {
36
+ inset?: boolean;
37
+ variant?: "default" | "destructive";
38
+ }
39
+ declare function DropdownMenuItem({ className, inset, variant, ...props }: DropdownMenuItemProps): React.JSX.Element;
40
+ declare function DropdownMenuCheckboxItem({ className, children, ...props }: Menu.CheckboxItem.Props): React.JSX.Element;
41
+ declare function DropdownMenuRadioItem({ className, children, ...props }: Menu.RadioItem.Props): React.JSX.Element;
42
+ export interface DropdownMenuLabelProps extends Menu.GroupLabel.Props {
43
+ inset?: boolean;
44
+ }
45
+ declare function DropdownMenuLabel({ className, inset, ...props }: DropdownMenuLabelProps): React.JSX.Element;
46
+ declare function DropdownMenuSeparator({ className, ...props }: Menu.Separator.Props): React.JSX.Element;
47
+ export interface DropdownMenuSubTriggerProps extends Menu.SubmenuTrigger.Props {
48
+ inset?: boolean;
49
+ }
50
+ declare function DropdownMenuSubTrigger({ className, inset, children, ...props }: DropdownMenuSubTriggerProps): React.JSX.Element;
51
+ declare function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<"span">): React.JSX.Element;
52
+ //#endregion
53
+ export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger };
54
+ //# sourceMappingURL=dropdown-menu.d.ts.map
@@ -0,0 +1,191 @@
1
+ "use client";
2
+ "use client";
3
+ import { cn } from "../lib/cn.js";
4
+ import { Check, ChevronRight, Circle } from "lucide-react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { Menu } from "@base-ui/react/menu";
7
+ //#region src/components/dropdown-menu.tsx
8
+ /**
9
+ * DropdownMenu over Base UI's Menu.
10
+ *
11
+ * `DropdownMenuContent` and `DropdownMenuSubContent` compose Portal +
12
+ * Positioner + Popup, so consumers keep writing one element; `side`, `align`
13
+ * and `sideOffset` are forwarded to the Positioner where Base UI reads them.
14
+ *
15
+ * Radix users: `asChild` on the trigger becomes `render={<Button />}`;
16
+ * `onSelect` on items is `onClick`; `DropdownMenuLabel` is Base UI's
17
+ * `Menu.GroupLabel` (place it inside a `DropdownMenuGroup` for the
18
+ * `aria-labelledby` wiring); `DropdownMenuCheckboxItem` reports through
19
+ * `onCheckedChange(checked, eventDetails)`; `DropdownMenuRadioGroup` through
20
+ * `onValueChange(value, eventDetails)`. Highlight state is `data-highlighted`,
21
+ * not `:focus`.
22
+ */
23
+ function DropdownMenu(props) {
24
+ return /* @__PURE__ */ jsx(Menu.Root, { ...props });
25
+ }
26
+ function DropdownMenuTrigger(props) {
27
+ return /* @__PURE__ */ jsx(Menu.Trigger, {
28
+ "data-slot": "dropdown-menu-trigger",
29
+ ...props
30
+ });
31
+ }
32
+ function DropdownMenuPortal(props) {
33
+ return /* @__PURE__ */ jsx(Menu.Portal, {
34
+ "data-slot": "dropdown-menu-portal",
35
+ ...props
36
+ });
37
+ }
38
+ function DropdownMenuGroup(props) {
39
+ return /* @__PURE__ */ jsx(Menu.Group, {
40
+ "data-slot": "dropdown-menu-group",
41
+ ...props
42
+ });
43
+ }
44
+ function DropdownMenuSub(props) {
45
+ return /* @__PURE__ */ jsx(Menu.SubmenuRoot, { ...props });
46
+ }
47
+ function DropdownMenuRadioGroup(props) {
48
+ return /* @__PURE__ */ jsx(Menu.RadioGroup, {
49
+ "data-slot": "dropdown-menu-radio-group",
50
+ ...props
51
+ });
52
+ }
53
+ const popupClassName = [
54
+ "min-w-[8rem] overflow-hidden p-1 outline-none",
55
+ "rounded-[var(--ds-radius-md)] border border-[var(--ds-border)]",
56
+ "bg-[var(--ds-popover)] text-[color:var(--ds-popover-foreground)] shadow-[var(--ds-shadow-lg)]",
57
+ "open:animate-in open:fade-in-0 open:zoom-in-95",
58
+ "closed:animate-out closed:fade-out-0 closed:zoom-out-95",
59
+ "data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2",
60
+ "duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]"
61
+ ];
62
+ function DropdownMenuContent({ className, side, align, sideOffset = 4, alignOffset, anchor, collisionBoundary, collisionPadding, collisionAvoidance, sticky, positionMethod, keepMounted, container, ...props }) {
63
+ return /* @__PURE__ */ jsx(Menu.Portal, {
64
+ keepMounted,
65
+ container,
66
+ children: /* @__PURE__ */ jsx(Menu.Positioner, {
67
+ "data-slot": "dropdown-menu-positioner",
68
+ side,
69
+ align,
70
+ sideOffset,
71
+ alignOffset,
72
+ anchor,
73
+ collisionBoundary,
74
+ collisionPadding,
75
+ collisionAvoidance,
76
+ sticky,
77
+ positionMethod,
78
+ className: "isolate z-50",
79
+ children: /* @__PURE__ */ jsx(Menu.Popup, {
80
+ "data-slot": "dropdown-menu-content",
81
+ className: cn(popupClassName, className),
82
+ ...props
83
+ })
84
+ })
85
+ });
86
+ }
87
+ function DropdownMenuSubContent({ className, side, align, sideOffset, alignOffset, anchor, collisionBoundary, collisionPadding, collisionAvoidance, sticky, positionMethod, keepMounted, container, ...props }) {
88
+ return /* @__PURE__ */ jsx(Menu.Portal, {
89
+ keepMounted,
90
+ container,
91
+ children: /* @__PURE__ */ jsx(Menu.Positioner, {
92
+ "data-slot": "dropdown-menu-sub-positioner",
93
+ side,
94
+ align,
95
+ sideOffset,
96
+ alignOffset,
97
+ anchor,
98
+ collisionBoundary,
99
+ collisionPadding,
100
+ collisionAvoidance,
101
+ sticky,
102
+ positionMethod,
103
+ className: "isolate z-50",
104
+ children: /* @__PURE__ */ jsx(Menu.Popup, {
105
+ "data-slot": "dropdown-menu-sub-content",
106
+ className: cn(popupClassName, className),
107
+ ...props
108
+ })
109
+ })
110
+ });
111
+ }
112
+ const itemClassName = [
113
+ "relative flex cursor-default select-none items-center gap-2 px-2 py-1.5 outline-none",
114
+ "rounded-[var(--ds-radius-sm)] text-[length:var(--ds-text-body-sm)]",
115
+ "transition-colors duration-[var(--ds-duration-fast)]",
116
+ "data-[highlighted]:bg-[var(--ds-hover)] data-[highlighted]:text-[color:var(--ds-foreground)]",
117
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
118
+ "[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0"
119
+ ];
120
+ function DropdownMenuItem({ className, inset, variant = "default", ...props }) {
121
+ return /* @__PURE__ */ jsx(Menu.Item, {
122
+ "data-slot": "dropdown-menu-item",
123
+ "data-inset": inset ? "" : void 0,
124
+ "data-variant": variant,
125
+ className: cn(itemClassName, inset && "pl-8", variant === "destructive" && "text-[color:var(--ds-destructive)] data-[highlighted]:bg-[var(--ds-destructive-soft)] data-[highlighted]:text-[color:var(--ds-destructive)]", className),
126
+ ...props
127
+ });
128
+ }
129
+ function DropdownMenuCheckboxItem({ className, children, ...props }) {
130
+ return /* @__PURE__ */ jsxs(Menu.CheckboxItem, {
131
+ "data-slot": "dropdown-menu-checkbox-item",
132
+ className: cn(itemClassName, "pl-8 pr-2", className),
133
+ ...props,
134
+ children: [/* @__PURE__ */ jsx("span", {
135
+ className: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center",
136
+ children: /* @__PURE__ */ jsx(Menu.CheckboxItemIndicator, {
137
+ "data-slot": "dropdown-menu-checkbox-item-indicator",
138
+ children: /* @__PURE__ */ jsx(Check, { className: "size-3.5" })
139
+ })
140
+ }), children]
141
+ });
142
+ }
143
+ function DropdownMenuRadioItem({ className, children, ...props }) {
144
+ return /* @__PURE__ */ jsxs(Menu.RadioItem, {
145
+ "data-slot": "dropdown-menu-radio-item",
146
+ className: cn(itemClassName, "pl-8 pr-2", className),
147
+ ...props,
148
+ children: [/* @__PURE__ */ jsx("span", {
149
+ className: "pointer-events-none absolute left-2 flex size-3.5 items-center justify-center",
150
+ children: /* @__PURE__ */ jsx(Menu.RadioItemIndicator, {
151
+ "data-slot": "dropdown-menu-radio-item-indicator",
152
+ children: /* @__PURE__ */ jsx(Circle, { className: "size-2 fill-current" })
153
+ })
154
+ }), children]
155
+ });
156
+ }
157
+ function DropdownMenuLabel({ className, inset, ...props }) {
158
+ return /* @__PURE__ */ jsx(Menu.GroupLabel, {
159
+ "data-slot": "dropdown-menu-label",
160
+ "data-inset": inset ? "" : void 0,
161
+ className: cn("px-2 py-1.5 text-[length:var(--ds-text-caption-lg)] font-semibold text-[color:var(--ds-muted-foreground)]", inset && "pl-8", className),
162
+ ...props
163
+ });
164
+ }
165
+ function DropdownMenuSeparator({ className, ...props }) {
166
+ return /* @__PURE__ */ jsx(Menu.Separator, {
167
+ "data-slot": "dropdown-menu-separator",
168
+ className: cn("-mx-1 my-1 h-px bg-[var(--ds-border-subtle)]", className),
169
+ ...props
170
+ });
171
+ }
172
+ function DropdownMenuSubTrigger({ className, inset, children, ...props }) {
173
+ return /* @__PURE__ */ jsxs(Menu.SubmenuTrigger, {
174
+ "data-slot": "dropdown-menu-sub-trigger",
175
+ "data-inset": inset ? "" : void 0,
176
+ className: cn(itemClassName, "popup-open:bg-[var(--ds-hover)] popup-open:text-[color:var(--ds-foreground)]", inset && "pl-8", className),
177
+ ...props,
178
+ children: [children, /* @__PURE__ */ jsx(ChevronRight, { className: "ml-auto" })]
179
+ });
180
+ }
181
+ function DropdownMenuShortcut({ className, ...props }) {
182
+ return /* @__PURE__ */ jsx("span", {
183
+ "data-slot": "dropdown-menu-shortcut",
184
+ className: cn("ml-auto text-[length:var(--ds-text-caption)] tracking-widest text-[color:var(--ds-muted-foreground)]", className),
185
+ ...props
186
+ });
187
+ }
188
+ //#endregion
189
+ export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger };
190
+
191
+ //# sourceMappingURL=dropdown-menu.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dropdown-menu.js","names":["MenuPrimitive"],"sources":["../../src/components/dropdown-menu.tsx"],"sourcesContent":["\"use client\";\n\nimport { Menu as MenuPrimitive } from \"@base-ui/react/menu\";\nimport { Check, ChevronRight, Circle } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { cn } from \"@/lib/cn\";\n\n/**\n * DropdownMenu over Base UI's Menu.\n *\n * `DropdownMenuContent` and `DropdownMenuSubContent` compose Portal +\n * Positioner + Popup, so consumers keep writing one element; `side`, `align`\n * and `sideOffset` are forwarded to the Positioner where Base UI reads them.\n *\n * Radix users: `asChild` on the trigger becomes `render={<Button />}`;\n * `onSelect` on items is `onClick`; `DropdownMenuLabel` is Base UI's\n * `Menu.GroupLabel` (place it inside a `DropdownMenuGroup` for the\n * `aria-labelledby` wiring); `DropdownMenuCheckboxItem` reports through\n * `onCheckedChange(checked, eventDetails)`; `DropdownMenuRadioGroup` through\n * `onValueChange(value, eventDetails)`. Highlight state is `data-highlighted`,\n * not `:focus`.\n */\nfunction DropdownMenu(props: MenuPrimitive.Root.Props) {\n return <MenuPrimitive.Root {...props} />;\n}\n\nfunction DropdownMenuTrigger(props: MenuPrimitive.Trigger.Props) {\n return <MenuPrimitive.Trigger data-slot=\"dropdown-menu-trigger\" {...props} />;\n}\n\nfunction DropdownMenuPortal(props: MenuPrimitive.Portal.Props) {\n return <MenuPrimitive.Portal data-slot=\"dropdown-menu-portal\" {...props} />;\n}\n\nfunction DropdownMenuGroup(props: MenuPrimitive.Group.Props) {\n return <MenuPrimitive.Group data-slot=\"dropdown-menu-group\" {...props} />;\n}\n\nfunction DropdownMenuSub(props: MenuPrimitive.SubmenuRoot.Props) {\n return <MenuPrimitive.SubmenuRoot {...props} />;\n}\n\nfunction DropdownMenuRadioGroup(props: MenuPrimitive.RadioGroup.Props) {\n return <MenuPrimitive.RadioGroup data-slot=\"dropdown-menu-radio-group\" {...props} />;\n}\n\ntype PositionerProps = Pick<\n MenuPrimitive.Positioner.Props,\n | \"side\"\n | \"align\"\n | \"sideOffset\"\n | \"alignOffset\"\n | \"anchor\"\n | \"collisionBoundary\"\n | \"collisionPadding\"\n | \"collisionAvoidance\"\n | \"sticky\"\n | \"positionMethod\"\n>;\n\nexport interface DropdownMenuContentProps extends MenuPrimitive.Popup.Props, PositionerProps {\n /** Keep the menu mounted in the DOM while hidden. */\n keepMounted?: boolean;\n /** Where to portal the menu; defaults to `<body>`. */\n container?: MenuPrimitive.Portal.Props[\"container\"];\n}\n\nconst popupClassName = [\n \"min-w-[8rem] overflow-hidden p-1 outline-none\",\n \"rounded-[var(--ds-radius-md)] border border-[var(--ds-border)]\",\n \"bg-[var(--ds-popover)] text-[color:var(--ds-popover-foreground)] shadow-[var(--ds-shadow-lg)]\",\n \"open:animate-in open:fade-in-0 open:zoom-in-95\",\n \"closed:animate-out closed:fade-out-0 closed:zoom-out-95\",\n \"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2\",\n \"duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]\",\n];\n\nfunction DropdownMenuContent({\n className,\n side,\n align,\n sideOffset = 4,\n alignOffset,\n anchor,\n collisionBoundary,\n collisionPadding,\n collisionAvoidance,\n sticky,\n positionMethod,\n keepMounted,\n container,\n ...props\n}: DropdownMenuContentProps) {\n return (\n <MenuPrimitive.Portal keepMounted={keepMounted} container={container}>\n <MenuPrimitive.Positioner\n data-slot=\"dropdown-menu-positioner\"\n side={side}\n align={align}\n sideOffset={sideOffset}\n alignOffset={alignOffset}\n anchor={anchor}\n collisionBoundary={collisionBoundary}\n collisionPadding={collisionPadding}\n collisionAvoidance={collisionAvoidance}\n sticky={sticky}\n positionMethod={positionMethod}\n className=\"isolate z-50\"\n >\n <MenuPrimitive.Popup\n data-slot=\"dropdown-menu-content\"\n className={cn(popupClassName, className)}\n {...props}\n />\n </MenuPrimitive.Positioner>\n </MenuPrimitive.Portal>\n );\n}\n\nfunction DropdownMenuSubContent({\n className,\n side,\n align,\n sideOffset,\n alignOffset,\n anchor,\n collisionBoundary,\n collisionPadding,\n collisionAvoidance,\n sticky,\n positionMethod,\n keepMounted,\n container,\n ...props\n}: DropdownMenuContentProps) {\n return (\n <MenuPrimitive.Portal keepMounted={keepMounted} container={container}>\n <MenuPrimitive.Positioner\n data-slot=\"dropdown-menu-sub-positioner\"\n side={side}\n align={align}\n sideOffset={sideOffset}\n alignOffset={alignOffset}\n anchor={anchor}\n collisionBoundary={collisionBoundary}\n collisionPadding={collisionPadding}\n collisionAvoidance={collisionAvoidance}\n sticky={sticky}\n positionMethod={positionMethod}\n className=\"isolate z-50\"\n >\n <MenuPrimitive.Popup\n data-slot=\"dropdown-menu-sub-content\"\n className={cn(popupClassName, className)}\n {...props}\n />\n </MenuPrimitive.Positioner>\n </MenuPrimitive.Portal>\n );\n}\n\nconst itemClassName = [\n \"relative flex cursor-default select-none items-center gap-2 px-2 py-1.5 outline-none\",\n \"rounded-[var(--ds-radius-sm)] text-[length:var(--ds-text-body-sm)]\",\n \"transition-colors duration-[var(--ds-duration-fast)]\",\n \"data-[highlighted]:bg-[var(--ds-hover)] data-[highlighted]:text-[color:var(--ds-foreground)]\",\n \"data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n \"[&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0\",\n];\n\nexport interface DropdownMenuItemProps extends MenuPrimitive.Item.Props {\n inset?: boolean;\n variant?: \"default\" | \"destructive\";\n}\n\nfunction DropdownMenuItem({ className, inset, variant = \"default\", ...props }: DropdownMenuItemProps) {\n return (\n <MenuPrimitive.Item\n data-slot=\"dropdown-menu-item\"\n data-inset={inset ? \"\" : undefined}\n data-variant={variant}\n className={cn(\n itemClassName,\n inset && \"pl-8\",\n variant === \"destructive\" &&\n \"text-[color:var(--ds-destructive)] data-[highlighted]:bg-[var(--ds-destructive-soft)] data-[highlighted]:text-[color:var(--ds-destructive)]\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuCheckboxItem({ className, children, ...props }: MenuPrimitive.CheckboxItem.Props) {\n return (\n <MenuPrimitive.CheckboxItem\n data-slot=\"dropdown-menu-checkbox-item\"\n className={cn(itemClassName, \"pl-8 pr-2\", className)}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <MenuPrimitive.CheckboxItemIndicator data-slot=\"dropdown-menu-checkbox-item-indicator\">\n <Check className=\"size-3.5\" />\n </MenuPrimitive.CheckboxItemIndicator>\n </span>\n {children}\n </MenuPrimitive.CheckboxItem>\n );\n}\n\nfunction DropdownMenuRadioItem({ className, children, ...props }: MenuPrimitive.RadioItem.Props) {\n return (\n <MenuPrimitive.RadioItem\n data-slot=\"dropdown-menu-radio-item\"\n className={cn(itemClassName, \"pl-8 pr-2\", className)}\n {...props}\n >\n <span className=\"pointer-events-none absolute left-2 flex size-3.5 items-center justify-center\">\n <MenuPrimitive.RadioItemIndicator data-slot=\"dropdown-menu-radio-item-indicator\">\n <Circle className=\"size-2 fill-current\" />\n </MenuPrimitive.RadioItemIndicator>\n </span>\n {children}\n </MenuPrimitive.RadioItem>\n );\n}\n\nexport interface DropdownMenuLabelProps extends MenuPrimitive.GroupLabel.Props {\n inset?: boolean;\n}\n\nfunction DropdownMenuLabel({ className, inset, ...props }: DropdownMenuLabelProps) {\n return (\n <MenuPrimitive.GroupLabel\n data-slot=\"dropdown-menu-label\"\n data-inset={inset ? \"\" : undefined}\n className={cn(\n \"px-2 py-1.5 text-[length:var(--ds-text-caption-lg)] font-semibold text-[color:var(--ds-muted-foreground)]\",\n inset && \"pl-8\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DropdownMenuSeparator({ className, ...props }: MenuPrimitive.Separator.Props) {\n return (\n <MenuPrimitive.Separator\n data-slot=\"dropdown-menu-separator\"\n className={cn(\"-mx-1 my-1 h-px bg-[var(--ds-border-subtle)]\", className)}\n {...props}\n />\n );\n}\n\nexport interface DropdownMenuSubTriggerProps extends MenuPrimitive.SubmenuTrigger.Props {\n inset?: boolean;\n}\n\nfunction DropdownMenuSubTrigger({\n className,\n inset,\n children,\n ...props\n}: DropdownMenuSubTriggerProps) {\n return (\n <MenuPrimitive.SubmenuTrigger\n data-slot=\"dropdown-menu-sub-trigger\"\n data-inset={inset ? \"\" : undefined}\n className={cn(\n itemClassName,\n \"popup-open:bg-[var(--ds-hover)] popup-open:text-[color:var(--ds-foreground)]\",\n inset && \"pl-8\",\n className,\n )}\n {...props}\n >\n {children}\n <ChevronRight className=\"ml-auto\" />\n </MenuPrimitive.SubmenuTrigger>\n );\n}\n\nfunction DropdownMenuShortcut({ className, ...props }: React.ComponentProps<\"span\">) {\n return (\n <span\n data-slot=\"dropdown-menu-shortcut\"\n className={cn(\n \"ml-auto text-[length:var(--ds-text-caption)] tracking-widest text-[color:var(--ds-muted-foreground)]\",\n className,\n )}\n {...props}\n />\n );\n}\n\nexport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuRadioItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuGroup,\n DropdownMenuPortal,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuRadioGroup,\n};\n"],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ import { Accept, DropzoneState, FileRejection } from "react-dropzone";
4
+ //#region src/components/dropzone.d.ts
5
+ /** `1536` → `1.5 KB`, `0` → `0 B`; base 1024, one decimal above bytes. */
6
+ declare function formatFileSize(bytes: number): string;
7
+ export interface DropzoneProps extends Omit<React.ComponentProps<"div">, "children" | "onDrop" | "onDragEnter" | "onDragOver" | "onDragLeave"> {
8
+ /** Called with the accepted files after a drop, a paste or the file dialog. */
9
+ onFiles: (files: File[]) => void;
10
+ /** Called with the rejected files and their reasons. */
11
+ onReject?: (rejections: FileRejection[]) => void;
12
+ /** MIME type to extension map, e.g. `{ "image/png": [".png"] }`. */
13
+ accept?: Accept;
14
+ /** Allow more than one file; defaults to true. */
15
+ multiple?: boolean;
16
+ /** Largest accepted file in bytes. */
17
+ maxSize?: number;
18
+ /** Most files accepted per drop; `0` means unlimited. */
19
+ maxFiles?: number;
20
+ disabled?: boolean;
21
+ /** Do not open the file dialog on click; for a zone that wraps clickable content. @default false */
22
+ noClick?: boolean;
23
+ /** Do not open the file dialog on Enter or Space; pair with `noClick`. @default false */
24
+ noKeyboard?: boolean;
25
+ /** Primary line of the default body. */
26
+ label?: React.ReactNode;
27
+ /** Muted second line of the default body; derived from `accept` and `maxSize` when omitted. */
28
+ hint?: React.ReactNode;
29
+ /** Replace the default body. A function receives react-dropzone's state. */
30
+ children?: React.ReactNode | ((state: DropzoneState) => React.ReactNode);
31
+ }
32
+ declare function Dropzone({ onFiles, onReject, accept, multiple, maxSize, maxFiles, disabled, noClick, noKeyboard, label, hint, children, className, ref, ...props }: DropzoneProps): React.JSX.Element;
33
+ export interface FileListProps extends Omit<React.ComponentProps<"ul">, "children"> {
34
+ files: readonly File[];
35
+ /** Called with the file and its index when its remove button is pressed; the button is hidden when omitted. */
36
+ onRemove?: (file: File, index: number) => void;
37
+ /** Formats the size column; defaults to `formatFileSize`. */
38
+ formatSize?: (bytes: number) => string;
39
+ }
40
+ /**
41
+ * The files a Dropzone picked: name, size and a remove button per row. Rows
42
+ * are keyed by name, size and last-modified time so re-picking the same file
43
+ * keeps its row stable.
44
+ */
45
+ declare function FileList({ files, onRemove, formatSize, className, ...props }: FileListProps): React.JSX.Element | null;
46
+ //#endregion
47
+ export { type Accept, Dropzone, type DropzoneState, FileList, type FileRejection, formatFileSize };
48
+ //# sourceMappingURL=dropzone.d.ts.map
@@ -0,0 +1,185 @@
1
+ "use client";
2
+ "use client";
3
+ import { cn } from "../lib/cn.js";
4
+ import { FileIcon, Upload, X } from "lucide-react";
5
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
+ import { useDropzone } from "react-dropzone";
7
+ //#region src/components/dropzone.tsx
8
+ /**
9
+ * Dropzone.
10
+ *
11
+ * A drop target over react-dropzone's `useDropzone`. The root is a focusable
12
+ * region that opens the file dialog on click or Enter; the hidden `<input>` is
13
+ * what the dialog fills. Every state is exposed as a data attribute so the
14
+ * classes below (and a consumer's) can paint it:
15
+ *
16
+ * - `data-active` a drag is over the zone
17
+ * - `data-accept` every dragged file passes `accept` / `maxSize`
18
+ * - `data-reject` at least one dragged file fails
19
+ * - `data-disabled`
20
+ *
21
+ * `onFiles` receives only the accepted files; rejections go to `onReject`.
22
+ * `FileList` and `formatFileSize` are separate parts for listing what was
23
+ * picked, so a form can keep the zone and the list in different places.
24
+ *
25
+ * ## Wrapping a whole page as a drop target
26
+ *
27
+ * `noClick` and `noKeyboard` turn off the two activators that only make sense
28
+ * on a dashed box: clicking anywhere inside opens the file dialog, and Enter /
29
+ * Space on the focused root does the same. A page-sized zone wraps content
30
+ * that has its own buttons and inputs, so both have to go — otherwise every
31
+ * click in the page opens a file picker.
32
+ *
33
+ * ```tsx
34
+ * <Dropzone noClick noKeyboard onFiles={upload} className="min-h-dvh border-0 bg-transparent p-0">
35
+ * {({ isDragActive, open }) => (
36
+ * <>
37
+ * <PageContent />
38
+ * <Button onClick={open}>Browse…</Button>
39
+ * {isDragActive ? <DropHint /> : null}
40
+ * </>
41
+ * )}
42
+ * </Dropzone>
43
+ * ```
44
+ *
45
+ * The render-prop form gets react-dropzone's `open()`, which is the way back
46
+ * to the file dialog from a deliberate control. With `noClick` the root also
47
+ * drops its `cursor-pointer`, since it is no longer clickable.
48
+ */
49
+ const UNITS = [
50
+ "B",
51
+ "KB",
52
+ "MB",
53
+ "GB",
54
+ "TB"
55
+ ];
56
+ /** `1536` → `1.5 KB`, `0` → `0 B`; base 1024, one decimal above bytes. */
57
+ function formatFileSize(bytes) {
58
+ if (!Number.isFinite(bytes) || bytes < 0) return "0 B";
59
+ let value = bytes;
60
+ let unit = 0;
61
+ while (value >= 1024 && unit < UNITS.length - 1) {
62
+ value /= 1024;
63
+ unit += 1;
64
+ }
65
+ return `${unit === 0 ? Math.round(value) : Math.round(value * 10) / 10} ${UNITS[unit]}`;
66
+ }
67
+ /** Turns an `accept` map into a hint such as `PNG, JPG, PDF`. */
68
+ function describeAccept(accept) {
69
+ if (!accept) return void 0;
70
+ const exts = /* @__PURE__ */ new Set();
71
+ for (const [mime, list] of Object.entries(accept)) {
72
+ const items = typeof list === "string" ? [list] : list;
73
+ if (items.length > 0) for (const ext of items) exts.add(ext.replace(/^\./, "").toUpperCase());
74
+ else {
75
+ const sub = mime.split("/")[1];
76
+ if (sub && sub !== "*") exts.add(sub.toUpperCase());
77
+ }
78
+ }
79
+ return exts.size > 0 ? [...exts].join(", ") : void 0;
80
+ }
81
+ function Dropzone({ onFiles, onReject, accept, multiple = true, maxSize, maxFiles, disabled = false, noClick = false, noKeyboard = false, label, hint, children, className, ref, ...props }) {
82
+ const state = useDropzone({
83
+ accept,
84
+ multiple,
85
+ maxSize,
86
+ maxFiles,
87
+ disabled,
88
+ noClick,
89
+ noKeyboard,
90
+ onDropAccepted: (files) => onFiles(files),
91
+ onDropRejected: (rejections) => onReject?.(rejections)
92
+ });
93
+ const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject, isFocused, rootRef } = state;
94
+ const setRoot = (node) => {
95
+ rootRef.current = node;
96
+ if (typeof ref === "function") ref(node);
97
+ else if (ref) ref.current = node;
98
+ };
99
+ const derivedHint = hint ?? [describeAccept(accept), maxSize ? `up to ${formatFileSize(maxSize)}` : void 0].filter(Boolean).join(" ");
100
+ const body = typeof children === "function" ? children(state) : children !== void 0 ? children : /* @__PURE__ */ jsxs(Fragment, { children: [
101
+ /* @__PURE__ */ jsx("span", {
102
+ "data-slot": "dropzone-icon",
103
+ className: cn("flex size-10 items-center justify-center rounded-[var(--ds-radius-full)]", "bg-[var(--ds-muted)] text-[color:var(--ds-muted-foreground)]", "group-data-[active]/dropzone:bg-[var(--ds-primary-soft)] group-data-[active]/dropzone:text-[color:var(--ds-primary)]", "group-data-[reject]/dropzone:bg-[var(--ds-destructive-soft)] group-data-[reject]/dropzone:text-[color:var(--ds-destructive)]"),
104
+ children: /* @__PURE__ */ jsx(Upload, {
105
+ "aria-hidden": true,
106
+ className: "size-5"
107
+ })
108
+ }),
109
+ /* @__PURE__ */ jsx("span", {
110
+ "data-slot": "dropzone-label",
111
+ className: "text-[length:var(--ds-text-body-sm)] font-medium text-[color:var(--ds-foreground)]",
112
+ children: label ?? (isDragReject ? "Some of these files are not allowed" : isDragActive ? "Drop to upload" : multiple ? "Drop files here, or click to browse" : "Drop a file here, or click to browse")
113
+ }),
114
+ derivedHint ? /* @__PURE__ */ jsx("span", {
115
+ "data-slot": "dropzone-hint",
116
+ className: "text-[length:var(--ds-text-caption)] text-[color:var(--ds-muted-foreground)]",
117
+ children: derivedHint
118
+ }) : null
119
+ ] });
120
+ return /* @__PURE__ */ jsxs("div", {
121
+ ...getRootProps({
122
+ ...props,
123
+ ref: setRoot,
124
+ className: cn("group/dropzone flex flex-col items-center justify-center gap-2 p-6 text-center", "rounded-[var(--ds-radius-lg)] border border-dashed border-[var(--ds-border)] bg-[var(--ds-card)]", noClick ? "select-none outline-none" : "cursor-pointer select-none outline-none", "transition-[border-color,background-color] duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]", "hover:border-[var(--ds-border-strong)] hover:bg-[var(--ds-hover)]", "focus-visible:border-[var(--ds-ring)] focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40", "data-[active]:border-[var(--ds-primary)] data-[active]:bg-[var(--ds-primary-soft)]", "data-[reject]:border-[var(--ds-destructive)] data-[reject]:bg-[var(--ds-destructive-soft)]", "data-[disabled]:pointer-events-none data-[disabled]:opacity-50", className)
125
+ }),
126
+ "data-slot": "dropzone",
127
+ "data-active": isDragActive || void 0,
128
+ "data-accept": isDragActive && isDragAccept || void 0,
129
+ "data-reject": isDragReject || void 0,
130
+ "data-focused": isFocused || void 0,
131
+ "data-disabled": disabled || void 0,
132
+ "data-no-click": noClick || void 0,
133
+ "data-no-keyboard": noKeyboard || void 0,
134
+ children: [/* @__PURE__ */ jsx("input", {
135
+ "data-slot": "dropzone-input",
136
+ ...getInputProps()
137
+ }), body]
138
+ });
139
+ }
140
+ /**
141
+ * The files a Dropzone picked: name, size and a remove button per row. Rows
142
+ * are keyed by name, size and last-modified time so re-picking the same file
143
+ * keeps its row stable.
144
+ */
145
+ function FileList({ files, onRemove, formatSize = formatFileSize, className, ...props }) {
146
+ if (files.length === 0) return null;
147
+ return /* @__PURE__ */ jsx("ul", {
148
+ "data-slot": "file-list",
149
+ className: cn("flex list-none flex-col gap-1.5 p-0", className),
150
+ ...props,
151
+ children: files.map((file, index) => /* @__PURE__ */ jsxs("li", {
152
+ "data-slot": "file-list-item",
153
+ className: cn("flex items-center gap-3 px-3 py-2", "rounded-[var(--ds-radius-md)] border border-[var(--ds-border-subtle)] bg-[var(--ds-card)]"),
154
+ children: [
155
+ /* @__PURE__ */ jsx(FileIcon, {
156
+ "aria-hidden": true,
157
+ className: "size-4 shrink-0 text-[color:var(--ds-muted-foreground)]"
158
+ }),
159
+ /* @__PURE__ */ jsx("span", {
160
+ "data-slot": "file-list-name",
161
+ className: "min-w-0 flex-1 truncate text-[length:var(--ds-text-caption-lg)] text-[color:var(--ds-foreground)]",
162
+ title: file.name,
163
+ children: file.name
164
+ }),
165
+ /* @__PURE__ */ jsx("span", {
166
+ "data-slot": "file-list-size",
167
+ className: "shrink-0 font-[family-name:var(--ds-font-mono)] text-[length:var(--ds-text-caption)] text-[color:var(--ds-muted-foreground)]",
168
+ children: formatSize(file.size)
169
+ }),
170
+ onRemove ? /* @__PURE__ */ jsx("button", {
171
+ type: "button",
172
+ "data-slot": "file-list-remove",
173
+ "aria-label": `Remove ${file.name}`,
174
+ onClick: () => onRemove(file, index),
175
+ className: cn("inline-flex size-6 shrink-0 items-center justify-center rounded-[var(--ds-radius-sm)]", "text-[color:var(--ds-muted-foreground)] hover:bg-[var(--ds-hover)] hover:text-[color:var(--ds-foreground)]", "outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40", "[&_svg]:size-3.5"),
176
+ children: /* @__PURE__ */ jsx(X, { "aria-hidden": true })
177
+ }) : null
178
+ ]
179
+ }, `${file.name}-${file.size}-${file.lastModified}`))
180
+ });
181
+ }
182
+ //#endregion
183
+ export { Dropzone, FileList, formatFileSize };
184
+
185
+ //# sourceMappingURL=dropzone.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dropzone.js","names":[],"sources":["../../src/components/dropzone.tsx"],"sourcesContent":["\"use client\";\n\nimport { FileIcon, Upload, X } from \"lucide-react\";\nimport type * as React from \"react\";\nimport { useDropzone, type Accept, type DropzoneState, type FileRejection } from \"react-dropzone\";\nimport { cn } from \"@/lib/cn\";\n\n/**\n * Dropzone.\n *\n * A drop target over react-dropzone's `useDropzone`. The root is a focusable\n * region that opens the file dialog on click or Enter; the hidden `<input>` is\n * what the dialog fills. Every state is exposed as a data attribute so the\n * classes below (and a consumer's) can paint it:\n *\n * - `data-active` a drag is over the zone\n * - `data-accept` every dragged file passes `accept` / `maxSize`\n * - `data-reject` at least one dragged file fails\n * - `data-disabled`\n *\n * `onFiles` receives only the accepted files; rejections go to `onReject`.\n * `FileList` and `formatFileSize` are separate parts for listing what was\n * picked, so a form can keep the zone and the list in different places.\n *\n * ## Wrapping a whole page as a drop target\n *\n * `noClick` and `noKeyboard` turn off the two activators that only make sense\n * on a dashed box: clicking anywhere inside opens the file dialog, and Enter /\n * Space on the focused root does the same. A page-sized zone wraps content\n * that has its own buttons and inputs, so both have to go — otherwise every\n * click in the page opens a file picker.\n *\n * ```tsx\n * <Dropzone noClick noKeyboard onFiles={upload} className=\"min-h-dvh border-0 bg-transparent p-0\">\n * {({ isDragActive, open }) => (\n * <>\n * <PageContent />\n * <Button onClick={open}>Browse…</Button>\n * {isDragActive ? <DropHint /> : null}\n * </>\n * )}\n * </Dropzone>\n * ```\n *\n * The render-prop form gets react-dropzone's `open()`, which is the way back\n * to the file dialog from a deliberate control. With `noClick` the root also\n * drops its `cursor-pointer`, since it is no longer clickable.\n */\n\nconst UNITS = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"] as const;\n\n/** `1536` → `1.5 KB`, `0` → `0 B`; base 1024, one decimal above bytes. */\nfunction formatFileSize(bytes: number): string {\n if (!Number.isFinite(bytes) || bytes < 0) return \"0 B\";\n let value = bytes;\n let unit = 0;\n while (value >= 1024 && unit < UNITS.length - 1) {\n value /= 1024;\n unit += 1;\n }\n const rounded = unit === 0 ? Math.round(value) : Math.round(value * 10) / 10;\n return `${rounded} ${UNITS[unit]}`;\n}\n\n/** Turns an `accept` map into a hint such as `PNG, JPG, PDF`. */\nfunction describeAccept(accept: Accept | undefined): string | undefined {\n if (!accept) return undefined;\n const exts = new Set<string>();\n for (const [mime, list] of Object.entries(accept)) {\n const items = typeof list === \"string\" ? [list] : list;\n if (items.length > 0) {\n for (const ext of items) exts.add(ext.replace(/^\\./, \"\").toUpperCase());\n } else {\n const sub = mime.split(\"/\")[1];\n if (sub && sub !== \"*\") exts.add(sub.toUpperCase());\n }\n }\n return exts.size > 0 ? [...exts].join(\", \") : undefined;\n}\n\nexport interface DropzoneProps extends Omit<\n React.ComponentProps<\"div\">,\n \"children\" | \"onDrop\" | \"onDragEnter\" | \"onDragOver\" | \"onDragLeave\"\n> {\n /** Called with the accepted files after a drop, a paste or the file dialog. */\n onFiles: (files: File[]) => void;\n /** Called with the rejected files and their reasons. */\n onReject?: (rejections: FileRejection[]) => void;\n /** MIME type to extension map, e.g. `{ \"image/png\": [\".png\"] }`. */\n accept?: Accept;\n /** Allow more than one file; defaults to true. */\n multiple?: boolean;\n /** Largest accepted file in bytes. */\n maxSize?: number;\n /** Most files accepted per drop; `0` means unlimited. */\n maxFiles?: number;\n disabled?: boolean;\n /** Do not open the file dialog on click; for a zone that wraps clickable content. @default false */\n noClick?: boolean;\n /** Do not open the file dialog on Enter or Space; pair with `noClick`. @default false */\n noKeyboard?: boolean;\n /** Primary line of the default body. */\n label?: React.ReactNode;\n /** Muted second line of the default body; derived from `accept` and `maxSize` when omitted. */\n hint?: React.ReactNode;\n /** Replace the default body. A function receives react-dropzone's state. */\n children?: React.ReactNode | ((state: DropzoneState) => React.ReactNode);\n}\n\nfunction Dropzone({\n onFiles,\n onReject,\n accept,\n multiple = true,\n maxSize,\n maxFiles,\n disabled = false,\n noClick = false,\n noKeyboard = false,\n label,\n hint,\n children,\n className,\n ref,\n ...props\n}: DropzoneProps) {\n const state = useDropzone({\n accept,\n multiple,\n maxSize,\n maxFiles,\n disabled,\n noClick,\n noKeyboard,\n onDropAccepted: (files) => onFiles(files),\n onDropRejected: (rejections) => onReject?.(rejections),\n });\n const { getRootProps, getInputProps, isDragActive, isDragAccept, isDragReject, isFocused, rootRef } =\n state;\n // getRootProps would let a consumer `ref` replace its own; hold both.\n const setRoot = (node: HTMLDivElement | null) => {\n (rootRef as React.MutableRefObject<HTMLElement | null>).current = node;\n if (typeof ref === \"function\") ref(node);\n else if (ref) ref.current = node;\n };\n\n const derivedHint =\n hint ??\n [describeAccept(accept), maxSize ? `up to ${formatFileSize(maxSize)}` : undefined]\n .filter(Boolean)\n .join(\" \");\n\n const body =\n typeof children === \"function\" ? (\n children(state)\n ) : children !== undefined ? (\n children\n ) : (\n <>\n <span\n data-slot=\"dropzone-icon\"\n className={cn(\n \"flex size-10 items-center justify-center rounded-[var(--ds-radius-full)]\",\n \"bg-[var(--ds-muted)] text-[color:var(--ds-muted-foreground)]\",\n \"group-data-[active]/dropzone:bg-[var(--ds-primary-soft)] group-data-[active]/dropzone:text-[color:var(--ds-primary)]\",\n \"group-data-[reject]/dropzone:bg-[var(--ds-destructive-soft)] group-data-[reject]/dropzone:text-[color:var(--ds-destructive)]\",\n )}\n >\n <Upload aria-hidden className=\"size-5\" />\n </span>\n <span\n data-slot=\"dropzone-label\"\n className=\"text-[length:var(--ds-text-body-sm)] font-medium text-[color:var(--ds-foreground)]\"\n >\n {label ??\n (isDragReject\n ? \"Some of these files are not allowed\"\n : isDragActive\n ? \"Drop to upload\"\n : multiple\n ? \"Drop files here, or click to browse\"\n : \"Drop a file here, or click to browse\")}\n </span>\n {derivedHint ? (\n <span\n data-slot=\"dropzone-hint\"\n className=\"text-[length:var(--ds-text-caption)] text-[color:var(--ds-muted-foreground)]\"\n >\n {derivedHint}\n </span>\n ) : null}\n </>\n );\n\n return (\n <div\n {...getRootProps({\n ...props,\n ref: setRoot,\n className: cn(\n \"group/dropzone flex flex-col items-center justify-center gap-2 p-6 text-center\",\n \"rounded-[var(--ds-radius-lg)] border border-dashed border-[var(--ds-border)] bg-[var(--ds-card)]\",\n noClick ? \"select-none outline-none\" : \"cursor-pointer select-none outline-none\",\n \"transition-[border-color,background-color] duration-[var(--ds-duration-fast)] ease-[var(--ds-ease-out)]\",\n \"hover:border-[var(--ds-border-strong)] hover:bg-[var(--ds-hover)]\",\n \"focus-visible:border-[var(--ds-ring)] focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40\",\n \"data-[active]:border-[var(--ds-primary)] data-[active]:bg-[var(--ds-primary-soft)]\",\n \"data-[reject]:border-[var(--ds-destructive)] data-[reject]:bg-[var(--ds-destructive-soft)]\",\n \"data-[disabled]:pointer-events-none data-[disabled]:opacity-50\",\n className,\n ),\n })}\n data-slot=\"dropzone\"\n data-active={isDragActive || undefined}\n data-accept={(isDragActive && isDragAccept) || undefined}\n data-reject={isDragReject || undefined}\n data-focused={isFocused || undefined}\n data-disabled={disabled || undefined}\n data-no-click={noClick || undefined}\n data-no-keyboard={noKeyboard || undefined}\n >\n <input data-slot=\"dropzone-input\" {...getInputProps()} />\n {body}\n </div>\n );\n}\n\nexport interface FileListProps extends Omit<React.ComponentProps<\"ul\">, \"children\"> {\n files: readonly File[];\n /** Called with the file and its index when its remove button is pressed; the button is hidden when omitted. */\n onRemove?: (file: File, index: number) => void;\n /** Formats the size column; defaults to `formatFileSize`. */\n formatSize?: (bytes: number) => string;\n}\n\n/**\n * The files a Dropzone picked: name, size and a remove button per row. Rows\n * are keyed by name, size and last-modified time so re-picking the same file\n * keeps its row stable.\n */\nfunction FileList({ files, onRemove, formatSize = formatFileSize, className, ...props }: FileListProps) {\n if (files.length === 0) return null;\n return (\n <ul data-slot=\"file-list\" className={cn(\"flex list-none flex-col gap-1.5 p-0\", className)} {...props}>\n {files.map((file, index) => (\n <li\n key={`${file.name}-${file.size}-${file.lastModified}`}\n data-slot=\"file-list-item\"\n className={cn(\n \"flex items-center gap-3 px-3 py-2\",\n \"rounded-[var(--ds-radius-md)] border border-[var(--ds-border-subtle)] bg-[var(--ds-card)]\",\n )}\n >\n <FileIcon\n aria-hidden\n className=\"size-4 shrink-0 text-[color:var(--ds-muted-foreground)]\"\n />\n <span\n data-slot=\"file-list-name\"\n className=\"min-w-0 flex-1 truncate text-[length:var(--ds-text-caption-lg)] text-[color:var(--ds-foreground)]\"\n title={file.name}\n >\n {file.name}\n </span>\n <span\n data-slot=\"file-list-size\"\n className=\"shrink-0 font-[family-name:var(--ds-font-mono)] text-[length:var(--ds-text-caption)] text-[color:var(--ds-muted-foreground)]\"\n >\n {formatSize(file.size)}\n </span>\n {onRemove ? (\n <button\n type=\"button\"\n data-slot=\"file-list-remove\"\n aria-label={`Remove ${file.name}`}\n onClick={() => onRemove(file, index)}\n className={cn(\n \"inline-flex size-6 shrink-0 items-center justify-center rounded-[var(--ds-radius-sm)]\",\n \"text-[color:var(--ds-muted-foreground)] hover:bg-[var(--ds-hover)] hover:text-[color:var(--ds-foreground)]\",\n \"outline-none focus-visible:ring-[3px] focus-visible:ring-[var(--ds-ring)]/40\",\n \"[&_svg]:size-3.5\",\n )}\n >\n <X aria-hidden />\n </button>\n ) : null}\n </li>\n ))}\n </ul>\n );\n}\n\nexport { Dropzone, FileList, formatFileSize };\nexport type { Accept, FileRejection, DropzoneState };\n"],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ //#region src/components/empty-state.d.ts
4
+ export interface EmptyStateProps extends Omit<React.ComponentProps<"div">, "title"> {
5
+ /** A lucide icon; sized and coloured by the state. */
6
+ icon?: React.ReactNode;
7
+ title: React.ReactNode;
8
+ description?: React.ReactNode;
9
+ /** One or two buttons offering the next step. */
10
+ action?: React.ReactNode;
11
+ /** `compact` for an empty panel inside a card; `default` for an empty page. */
12
+ size?: "default" | "compact";
13
+ }
14
+ /**
15
+ * EmptyState — the centred, muted "nothing here yet" surface. The icon sits
16
+ * in a soft disc on the muted token; the title stays in the foreground so the
17
+ * eye lands on it, and everything else recedes.
18
+ */
19
+ declare function EmptyState({ className, icon, title, description, action, size, children, ...props }: EmptyStateProps): React.JSX.Element;
20
+ //#endregion
21
+ export { EmptyState };
22
+ //# sourceMappingURL=empty-state.d.ts.map
@@ -0,0 +1,46 @@
1
+ "use client";
2
+ "use client";
3
+ import { cn } from "../lib/cn.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/components/empty-state.tsx
6
+ /**
7
+ * EmptyState — the centred, muted "nothing here yet" surface. The icon sits
8
+ * in a soft disc on the muted token; the title stays in the foreground so the
9
+ * eye lands on it, and everything else recedes.
10
+ */
11
+ function EmptyState({ className, icon, title, description, action, size = "default", children, ...props }) {
12
+ return /* @__PURE__ */ jsxs("div", {
13
+ "data-slot": "empty-state",
14
+ "data-size": size,
15
+ className: cn("flex flex-col items-center justify-center text-center", size === "compact" ? "gap-2 px-4 py-8" : "gap-3 px-6 py-16", "text-[color:var(--ds-muted-foreground)]", className),
16
+ ...props,
17
+ children: [
18
+ icon ? /* @__PURE__ */ jsx("div", {
19
+ "data-slot": "empty-state-icon",
20
+ "aria-hidden": true,
21
+ className: cn("mb-1 flex items-center justify-center rounded-[var(--ds-radius-full)]", "bg-[var(--ds-muted)] text-[color:var(--ds-text-tertiary)]", size === "compact" ? "size-10 [&_svg]:size-5" : "size-12 [&_svg]:size-6"),
22
+ children: icon
23
+ }) : null,
24
+ /* @__PURE__ */ jsx("p", {
25
+ "data-slot": "empty-state-title",
26
+ className: cn("font-semibold text-[color:var(--ds-foreground)]", size === "compact" ? "text-[length:var(--ds-text-body-sm)] leading-[var(--ds-text-body-sm--line-height)]" : "text-[length:var(--ds-text-body-md)] leading-[var(--ds-text-body-md--line-height)]"),
27
+ children: title
28
+ }),
29
+ description ? /* @__PURE__ */ jsx("p", {
30
+ "data-slot": "empty-state-description",
31
+ className: cn("max-w-sm text-balance", size === "compact" ? "text-[length:var(--ds-text-caption-lg)] leading-[var(--ds-text-caption-lg--line-height)]" : "text-[length:var(--ds-text-body-sm)] leading-[var(--ds-text-body-sm--line-height)]"),
32
+ children: description
33
+ }) : null,
34
+ children,
35
+ action ? /* @__PURE__ */ jsx("div", {
36
+ "data-slot": "empty-state-action",
37
+ className: "mt-2 flex flex-wrap items-center justify-center gap-2",
38
+ children: action
39
+ }) : null
40
+ ]
41
+ });
42
+ }
43
+ //#endregion
44
+ export { EmptyState };
45
+
46
+ //# sourceMappingURL=empty-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"empty-state.js","names":[],"sources":["../../src/components/empty-state.tsx"],"sourcesContent":["\"use client\";\n\nimport type * as React from \"react\";\nimport { cn } from \"@/lib/cn\";\n\nexport interface EmptyStateProps extends Omit<React.ComponentProps<\"div\">, \"title\"> {\n /** A lucide icon; sized and coloured by the state. */\n icon?: React.ReactNode;\n title: React.ReactNode;\n description?: React.ReactNode;\n /** One or two buttons offering the next step. */\n action?: React.ReactNode;\n /** `compact` for an empty panel inside a card; `default` for an empty page. */\n size?: \"default\" | \"compact\";\n}\n\n/**\n * EmptyState — the centred, muted \"nothing here yet\" surface. The icon sits\n * in a soft disc on the muted token; the title stays in the foreground so the\n * eye lands on it, and everything else recedes.\n */\nfunction EmptyState({\n className,\n icon,\n title,\n description,\n action,\n size = \"default\",\n children,\n ...props\n}: EmptyStateProps) {\n return (\n <div\n data-slot=\"empty-state\"\n data-size={size}\n className={cn(\n \"flex flex-col items-center justify-center text-center\",\n size === \"compact\" ? \"gap-2 px-4 py-8\" : \"gap-3 px-6 py-16\",\n \"text-[color:var(--ds-muted-foreground)]\",\n className,\n )}\n {...props}\n >\n {icon ? (\n <div\n data-slot=\"empty-state-icon\"\n aria-hidden\n className={cn(\n \"mb-1 flex items-center justify-center rounded-[var(--ds-radius-full)]\",\n \"bg-[var(--ds-muted)] text-[color:var(--ds-text-tertiary)]\",\n size === \"compact\" ? \"size-10 [&_svg]:size-5\" : \"size-12 [&_svg]:size-6\",\n )}\n >\n {icon}\n </div>\n ) : null}\n <p\n data-slot=\"empty-state-title\"\n className={cn(\n \"font-semibold text-[color:var(--ds-foreground)]\",\n size === \"compact\"\n ? \"text-[length:var(--ds-text-body-sm)] leading-[var(--ds-text-body-sm--line-height)]\"\n : \"text-[length:var(--ds-text-body-md)] leading-[var(--ds-text-body-md--line-height)]\",\n )}\n >\n {title}\n </p>\n {description ? (\n <p\n data-slot=\"empty-state-description\"\n className={cn(\n \"max-w-sm text-balance\",\n size === \"compact\"\n ? \"text-[length:var(--ds-text-caption-lg)] leading-[var(--ds-text-caption-lg--line-height)]\"\n : \"text-[length:var(--ds-text-body-sm)] leading-[var(--ds-text-body-sm--line-height)]\",\n )}\n >\n {description}\n </p>\n ) : null}\n {children}\n {action ? (\n <div data-slot=\"empty-state-action\" className=\"mt-2 flex flex-wrap items-center justify-center gap-2\">\n {action}\n </div>\n ) : null}\n </div>\n );\n}\n\nexport { EmptyState };\n"],"mappings":";;;;;;;;;;AAqBA,SAAS,WAAW,EAClB,WACA,MACA,OACA,aACA,QACA,OAAO,WACP,UACA,GAAG,SACe;CAClB,OACE,qBAAC,OAAD;EACE,aAAU;EACV,aAAW;EACX,WAAW,GACT,yDACA,SAAS,YAAY,oBAAoB,oBACzC,2CACA,SACF;EACA,GAAI;EATN,UAAA;GAWG,OACC,oBAAC,OAAD;IACE,aAAU;IACV,eAAA;IACA,WAAW,GACT,yEACA,6DACA,SAAS,YAAY,2BAA2B,wBAClD;IAEC,UAAA;GACE,CAAA,IACH;GACJ,oBAAC,KAAD;IACE,aAAU;IACV,WAAW,GACT,mDACA,SAAS,YACL,uFACA,oFACN;IAEC,UAAA;GACA,CAAA;GACF,cACC,oBAAC,KAAD;IACE,aAAU;IACV,WAAW,GACT,yBACA,SAAS,YACL,6FACA,oFACN;IAEC,UAAA;GACA,CAAA,IACD;GACH;GACA,SACC,oBAAC,OAAD;IAAK,aAAU;IAAqB,WAAU;IAC3C,UAAA;GACE,CAAA,IACH;EACD;;AAET"}
@@ -0,0 +1,17 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ //#region src/components/error-banner.d.ts
4
+ export interface ErrorBannerProps extends Omit<React.ComponentProps<"div">, "children"> {
5
+ message: string;
6
+ onDismiss?: () => void;
7
+ onRetry?: () => void;
8
+ }
9
+ /**
10
+ * ErrorBanner — the one error surface every screen uses: a warning icon, the
11
+ * message, and optional Retry / Dismiss actions. Renders nothing for an empty
12
+ * message so callers can pass their error state straight through.
13
+ */
14
+ declare function ErrorBanner({ message, onDismiss, onRetry, className, ...props }: ErrorBannerProps): React.JSX.Element | null;
15
+ //#endregion
16
+ export { ErrorBanner };
17
+ //# sourceMappingURL=error-banner.d.ts.map