@inshorts/glint-ui 0.5.4 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (340) hide show
  1. package/README.md +3 -1
  2. package/dist/alert.cjs +96 -0
  3. package/dist/alert.cjs.map +1 -0
  4. package/dist/alert.d.cts +29 -0
  5. package/dist/alert.d.ts +29 -0
  6. package/dist/alert.js +10 -0
  7. package/dist/alert.js.map +1 -0
  8. package/dist/autocomplete.cjs +578 -0
  9. package/dist/autocomplete.cjs.map +1 -0
  10. package/dist/autocomplete.d.cts +56 -0
  11. package/dist/autocomplete.d.ts +56 -0
  12. package/dist/autocomplete.js +11 -0
  13. package/dist/autocomplete.js.map +1 -0
  14. package/dist/badge.cjs +89 -0
  15. package/dist/badge.cjs.map +1 -0
  16. package/dist/badge.d.cts +25 -0
  17. package/dist/badge.d.ts +25 -0
  18. package/dist/badge.js +10 -0
  19. package/dist/badge.js.map +1 -0
  20. package/dist/breadcrumb.cjs +88 -0
  21. package/dist/breadcrumb.cjs.map +1 -0
  22. package/dist/breadcrumb.d.cts +14 -0
  23. package/dist/breadcrumb.d.ts +14 -0
  24. package/dist/breadcrumb.js +8 -0
  25. package/dist/breadcrumb.js.map +1 -0
  26. package/dist/button.cjs +143 -0
  27. package/dist/button.cjs.map +1 -0
  28. package/dist/button.d.cts +34 -0
  29. package/dist/button.d.ts +34 -0
  30. package/dist/button.js +9 -0
  31. package/dist/button.js.map +1 -0
  32. package/dist/card.cjs +111 -0
  33. package/dist/card.cjs.map +1 -0
  34. package/dist/card.d.cts +15 -0
  35. package/dist/card.d.ts +15 -0
  36. package/dist/card.js +18 -0
  37. package/dist/card.js.map +1 -0
  38. package/dist/checkbox.cjs +66 -0
  39. package/dist/checkbox.cjs.map +1 -0
  40. package/{src/checkbox.tsx → dist/checkbox.d.cts} +5 -22
  41. package/dist/checkbox.d.ts +25 -0
  42. package/dist/checkbox.js +10 -0
  43. package/dist/checkbox.js.map +1 -0
  44. package/dist/chunk-5MEOZHJA.js +513 -0
  45. package/dist/chunk-5MEOZHJA.js.map +1 -0
  46. package/dist/chunk-67ACNS42.js +29 -0
  47. package/dist/chunk-67ACNS42.js.map +1 -0
  48. package/dist/chunk-A6GRLKOO.js +85 -0
  49. package/dist/chunk-A6GRLKOO.js.map +1 -0
  50. package/dist/chunk-DS3QYIKL.js +90 -0
  51. package/dist/chunk-DS3QYIKL.js.map +1 -0
  52. package/dist/chunk-EWSBFU54.js +53 -0
  53. package/dist/chunk-EWSBFU54.js.map +1 -0
  54. package/dist/chunk-G4ZQUSAJ.js +38 -0
  55. package/dist/chunk-G4ZQUSAJ.js.map +1 -0
  56. package/dist/chunk-GMCUXEFI.js +67 -0
  57. package/dist/chunk-GMCUXEFI.js.map +1 -0
  58. package/dist/chunk-HBBOR43A.js +85 -0
  59. package/dist/chunk-HBBOR43A.js.map +1 -0
  60. package/dist/chunk-HCA5ODCD.js +11 -0
  61. package/dist/chunk-HCA5ODCD.js.map +1 -0
  62. package/dist/chunk-HYRJYUPI.js +26 -0
  63. package/dist/chunk-HYRJYUPI.js.map +1 -0
  64. package/dist/chunk-IRCTGJQB.js +26 -0
  65. package/dist/chunk-IRCTGJQB.js.map +1 -0
  66. package/dist/chunk-ISPICB4W.js +70 -0
  67. package/dist/chunk-ISPICB4W.js.map +1 -0
  68. package/dist/chunk-JDE5XYPT.js +30 -0
  69. package/dist/chunk-JDE5XYPT.js.map +1 -0
  70. package/dist/chunk-JSJEEKR3.js +59 -0
  71. package/dist/chunk-JSJEEKR3.js.map +1 -0
  72. package/dist/chunk-KR3BCFP7.js +85 -0
  73. package/dist/chunk-KR3BCFP7.js.map +1 -0
  74. package/dist/chunk-MLRSOTFI.js +81 -0
  75. package/dist/chunk-MLRSOTFI.js.map +1 -0
  76. package/dist/chunk-MQT5RAQV.js +202 -0
  77. package/dist/chunk-MQT5RAQV.js.map +1 -0
  78. package/dist/chunk-MUPW2TFJ.js +102 -0
  79. package/dist/chunk-MUPW2TFJ.js.map +1 -0
  80. package/dist/chunk-OUYT6SPU.js +76 -0
  81. package/dist/chunk-OUYT6SPU.js.map +1 -0
  82. package/dist/chunk-PM2VE7DJ.js +100 -0
  83. package/dist/chunk-PM2VE7DJ.js.map +1 -0
  84. package/dist/chunk-PPYR3O3L.js +44 -0
  85. package/dist/chunk-PPYR3O3L.js.map +1 -0
  86. package/dist/chunk-PUTC52SI.js +98 -0
  87. package/dist/chunk-PUTC52SI.js.map +1 -0
  88. package/dist/chunk-PWAKTEXF.js +18 -0
  89. package/dist/chunk-PWAKTEXF.js.map +1 -0
  90. package/dist/chunk-QTFWORA7.js +26 -0
  91. package/dist/chunk-QTFWORA7.js.map +1 -0
  92. package/dist/chunk-RUPL2H6V.js +49 -0
  93. package/dist/chunk-RUPL2H6V.js.map +1 -0
  94. package/dist/chunk-S7PVZAPL.js +28 -0
  95. package/dist/chunk-S7PVZAPL.js.map +1 -0
  96. package/dist/chunk-SQDV2AKJ.js +49 -0
  97. package/dist/chunk-SQDV2AKJ.js.map +1 -0
  98. package/dist/chunk-T2KXVH3O.js +297 -0
  99. package/dist/chunk-T2KXVH3O.js.map +1 -0
  100. package/dist/chunk-T4WCCHJQ.js +29 -0
  101. package/dist/chunk-T4WCCHJQ.js.map +1 -0
  102. package/dist/chunk-UC3ZIQPE.js +93 -0
  103. package/dist/chunk-UC3ZIQPE.js.map +1 -0
  104. package/dist/chunk-UE7JY7VR.js +28 -0
  105. package/dist/chunk-UE7JY7VR.js.map +1 -0
  106. package/dist/chunk-Y5WCT6XN.js +93 -0
  107. package/dist/chunk-Y5WCT6XN.js.map +1 -0
  108. package/dist/chunk-YDICVETX.js +151 -0
  109. package/dist/chunk-YDICVETX.js.map +1 -0
  110. package/dist/chunk-ZFSQVMLJ.js +126 -0
  111. package/dist/chunk-ZFSQVMLJ.js.map +1 -0
  112. package/dist/chunk-ZHATD73Y.js +46 -0
  113. package/dist/chunk-ZHATD73Y.js.map +1 -0
  114. package/dist/chunk-ZLAA4DGG.js +111 -0
  115. package/dist/chunk-ZLAA4DGG.js.map +1 -0
  116. package/dist/chunk-ZP252OJZ.js +56 -0
  117. package/dist/chunk-ZP252OJZ.js.map +1 -0
  118. package/dist/clipboard.cjs +125 -0
  119. package/dist/clipboard.cjs.map +1 -0
  120. package/dist/clipboard.d.cts +52 -0
  121. package/dist/clipboard.d.ts +52 -0
  122. package/dist/clipboard.js +23 -0
  123. package/dist/clipboard.js.map +1 -0
  124. package/dist/collapsible.cjs +94 -0
  125. package/dist/collapsible.cjs.map +1 -0
  126. package/dist/collapsible.d.cts +21 -0
  127. package/dist/collapsible.d.ts +21 -0
  128. package/dist/collapsible.js +9 -0
  129. package/dist/collapsible.js.map +1 -0
  130. package/dist/confirm-dialog.cjs +369 -0
  131. package/dist/confirm-dialog.cjs.map +1 -0
  132. package/dist/confirm-dialog.d.cts +16 -0
  133. package/dist/confirm-dialog.d.ts +16 -0
  134. package/dist/confirm-dialog.js +13 -0
  135. package/dist/confirm-dialog.js.map +1 -0
  136. package/dist/copy-button.cjs +102 -0
  137. package/dist/copy-button.cjs.map +1 -0
  138. package/dist/copy-button.d.cts +18 -0
  139. package/dist/copy-button.d.ts +18 -0
  140. package/dist/copy-button.js +10 -0
  141. package/dist/copy-button.js.map +1 -0
  142. package/dist/data-table.cjs +140 -0
  143. package/dist/data-table.cjs.map +1 -0
  144. package/dist/data-table.d.cts +81 -0
  145. package/dist/data-table.d.ts +81 -0
  146. package/dist/data-table.js +8 -0
  147. package/dist/data-table.js.map +1 -0
  148. package/dist/date-range-picker/dateRange.cjs +138 -0
  149. package/dist/date-range-picker/dateRange.cjs.map +1 -0
  150. package/dist/date-range-picker/dateRange.d.cts +57 -0
  151. package/dist/date-range-picker/dateRange.d.ts +57 -0
  152. package/dist/date-range-picker/dateRange.js +31 -0
  153. package/dist/date-range-picker/dateRange.js.map +1 -0
  154. package/dist/date-range-picker/index.cjs +375 -0
  155. package/dist/date-range-picker/index.cjs.map +1 -0
  156. package/dist/date-range-picker/index.d.cts +18 -0
  157. package/dist/date-range-picker/index.d.ts +18 -0
  158. package/dist/date-range-picker/index.js +10 -0
  159. package/dist/date-range-picker/index.js.map +1 -0
  160. package/dist/dialogLayer.cjs +121 -0
  161. package/dist/dialogLayer.cjs.map +1 -0
  162. package/dist/dialogLayer.d.cts +11 -0
  163. package/dist/dialogLayer.d.ts +11 -0
  164. package/dist/dialogLayer.js +8 -0
  165. package/dist/dialogLayer.js.map +1 -0
  166. package/dist/feature-access-gate.cjs +160 -0
  167. package/dist/feature-access-gate.cjs.map +1 -0
  168. package/dist/feature-access-gate.d.cts +14 -0
  169. package/dist/feature-access-gate.d.ts +14 -0
  170. package/dist/feature-access-gate.js +11 -0
  171. package/dist/feature-access-gate.js.map +1 -0
  172. package/dist/field.cjs +58 -0
  173. package/dist/field.cjs.map +1 -0
  174. package/dist/field.d.cts +23 -0
  175. package/dist/field.d.ts +23 -0
  176. package/dist/field.js +8 -0
  177. package/dist/field.js.map +1 -0
  178. package/dist/image-uploader.cjs +293 -0
  179. package/dist/image-uploader.cjs.map +1 -0
  180. package/dist/image-uploader.d.cts +34 -0
  181. package/dist/image-uploader.d.ts +34 -0
  182. package/dist/image-uploader.js +12 -0
  183. package/dist/image-uploader.js.map +1 -0
  184. package/dist/index.cjs +3008 -0
  185. package/dist/index.cjs.map +1 -0
  186. package/dist/index.d.cts +41 -0
  187. package/dist/index.d.ts +41 -0
  188. package/dist/index.js +232 -0
  189. package/dist/index.js.map +1 -0
  190. package/dist/input.cjs +58 -0
  191. package/dist/input.cjs.map +1 -0
  192. package/dist/input.d.cts +7 -0
  193. package/dist/input.d.ts +7 -0
  194. package/dist/input.js +10 -0
  195. package/dist/input.js.map +1 -0
  196. package/dist/label.cjs +67 -0
  197. package/dist/label.cjs.map +1 -0
  198. package/dist/label.d.cts +7 -0
  199. package/dist/label.d.ts +7 -0
  200. package/dist/label.js +9 -0
  201. package/dist/label.js.map +1 -0
  202. package/dist/modal.cjs +212 -0
  203. package/dist/modal.cjs.map +1 -0
  204. package/dist/modal.d.cts +39 -0
  205. package/dist/modal.d.ts +39 -0
  206. package/dist/modal.js +12 -0
  207. package/dist/modal.js.map +1 -0
  208. package/dist/page-header.cjs +78 -0
  209. package/dist/page-header.cjs.map +1 -0
  210. package/dist/page-header.d.cts +35 -0
  211. package/dist/page-header.d.ts +35 -0
  212. package/dist/page-header.js +8 -0
  213. package/dist/page-header.js.map +1 -0
  214. package/dist/pagination.cjs +237 -0
  215. package/dist/pagination.cjs.map +1 -0
  216. package/dist/pagination.d.cts +15 -0
  217. package/dist/pagination.d.ts +15 -0
  218. package/dist/pagination.js +11 -0
  219. package/dist/pagination.js.map +1 -0
  220. package/dist/panel-brand-mark.cjs +59 -0
  221. package/dist/panel-brand-mark.cjs.map +1 -0
  222. package/dist/panel-brand-mark.d.cts +13 -0
  223. package/dist/panel-brand-mark.d.ts +13 -0
  224. package/dist/panel-brand-mark.js +8 -0
  225. package/dist/panel-brand-mark.js.map +1 -0
  226. package/dist/panel-sign-in.cjs +256 -0
  227. package/dist/panel-sign-in.cjs.map +1 -0
  228. package/dist/panel-sign-in.d.cts +31 -0
  229. package/dist/panel-sign-in.d.ts +31 -0
  230. package/dist/panel-sign-in.js +14 -0
  231. package/dist/panel-sign-in.js.map +1 -0
  232. package/dist/segmented-control.cjs +122 -0
  233. package/dist/segmented-control.cjs.map +1 -0
  234. package/dist/segmented-control.d.cts +58 -0
  235. package/dist/segmented-control.d.ts +58 -0
  236. package/dist/segmented-control.js +9 -0
  237. package/dist/segmented-control.js.map +1 -0
  238. package/dist/select.cjs +648 -0
  239. package/dist/select.cjs.map +1 -0
  240. package/dist/select.d.cts +30 -0
  241. package/dist/select.d.ts +30 -0
  242. package/dist/select.js +12 -0
  243. package/dist/select.js.map +1 -0
  244. package/dist/separator.cjs +70 -0
  245. package/dist/separator.cjs.map +1 -0
  246. package/dist/separator.d.cts +13 -0
  247. package/dist/separator.d.ts +13 -0
  248. package/dist/separator.js +9 -0
  249. package/dist/separator.js.map +1 -0
  250. package/dist/side-drawer.cjs +321 -0
  251. package/dist/side-drawer.cjs.map +1 -0
  252. package/dist/side-drawer.d.cts +21 -0
  253. package/dist/side-drawer.d.ts +21 -0
  254. package/dist/side-drawer.js +13 -0
  255. package/dist/side-drawer.js.map +1 -0
  256. package/dist/spinner.cjs +57 -0
  257. package/dist/spinner.cjs.map +1 -0
  258. package/{src/spinner.tsx → dist/spinner.d.cts} +11 -22
  259. package/dist/spinner.d.ts +27 -0
  260. package/dist/spinner.js +8 -0
  261. package/dist/spinner.js.map +1 -0
  262. package/dist/spreadsheet-grid.cjs +238 -0
  263. package/dist/spreadsheet-grid.cjs.map +1 -0
  264. package/dist/spreadsheet-grid.d.cts +48 -0
  265. package/dist/spreadsheet-grid.d.ts +48 -0
  266. package/dist/spreadsheet-grid.js +8 -0
  267. package/dist/spreadsheet-grid.js.map +1 -0
  268. package/dist/state-panel.cjs +86 -0
  269. package/dist/state-panel.cjs.map +1 -0
  270. package/dist/state-panel.d.cts +23 -0
  271. package/dist/state-panel.d.ts +23 -0
  272. package/dist/state-panel.js +9 -0
  273. package/dist/state-panel.js.map +1 -0
  274. package/dist/switch.cjs +87 -0
  275. package/dist/switch.cjs.map +1 -0
  276. package/dist/switch.d.cts +13 -0
  277. package/dist/switch.d.ts +13 -0
  278. package/dist/switch.js +9 -0
  279. package/dist/switch.js.map +1 -0
  280. package/dist/table.cjs +139 -0
  281. package/dist/table.cjs.map +1 -0
  282. package/dist/table.d.cts +16 -0
  283. package/dist/table.d.ts +16 -0
  284. package/dist/table.js +22 -0
  285. package/dist/table.js.map +1 -0
  286. package/dist/tabs.cjs +168 -0
  287. package/dist/tabs.cjs.map +1 -0
  288. package/dist/tabs.d.cts +59 -0
  289. package/dist/tabs.d.ts +59 -0
  290. package/dist/tabs.js +11 -0
  291. package/dist/tabs.js.map +1 -0
  292. package/dist/textarea.cjs +66 -0
  293. package/dist/textarea.cjs.map +1 -0
  294. package/dist/textarea.d.cts +18 -0
  295. package/dist/textarea.d.ts +18 -0
  296. package/dist/textarea.js +10 -0
  297. package/dist/textarea.js.map +1 -0
  298. package/dist/utils.cjs +35 -0
  299. package/dist/utils.cjs.map +1 -0
  300. package/dist/utils.d.cts +5 -0
  301. package/dist/utils.d.ts +5 -0
  302. package/dist/utils.js +7 -0
  303. package/dist/utils.js.map +1 -0
  304. package/package.json +391 -14
  305. package/src/alert.tsx +0 -74
  306. package/src/autocomplete.tsx +0 -666
  307. package/src/badge.tsx +0 -60
  308. package/src/breadcrumb.tsx +0 -71
  309. package/src/button.tsx +0 -113
  310. package/src/card.tsx +0 -79
  311. package/src/clipboard.ts +0 -144
  312. package/src/collapsible.tsx +0 -63
  313. package/src/confirm-dialog.tsx +0 -84
  314. package/src/copy-button.tsx +0 -58
  315. package/src/data-table.tsx +0 -190
  316. package/src/date-range-picker/dateRange.ts +0 -142
  317. package/src/date-range-picker/index.tsx +0 -366
  318. package/src/dialogLayer.ts +0 -142
  319. package/src/feature-access-gate.tsx +0 -38
  320. package/src/field.tsx +0 -56
  321. package/src/image-uploader.tsx +0 -181
  322. package/src/index.ts +0 -37
  323. package/src/input.tsx +0 -16
  324. package/src/label.tsx +0 -23
  325. package/src/modal.tsx +0 -117
  326. package/src/page-header.tsx +0 -75
  327. package/src/pagination.tsx +0 -116
  328. package/src/panel-brand-mark.tsx +0 -32
  329. package/src/panel-sign-in.tsx +0 -134
  330. package/src/segmented-control.tsx +0 -155
  331. package/src/select.tsx +0 -105
  332. package/src/separator.tsx +0 -32
  333. package/src/side-drawer.tsx +0 -87
  334. package/src/spreadsheet-grid.tsx +0 -264
  335. package/src/state-panel.tsx +0 -46
  336. package/src/switch.tsx +0 -47
  337. package/src/table.tsx +0 -110
  338. package/src/tabs.tsx +0 -201
  339. package/src/textarea.tsx +0 -32
  340. package/src/utils.ts +0 -6
@@ -0,0 +1,151 @@
1
+ import {
2
+ Input
3
+ } from "./chunk-PWAKTEXF.js";
4
+ import {
5
+ Button
6
+ } from "./chunk-KR3BCFP7.js";
7
+ import {
8
+ cn
9
+ } from "./chunk-HCA5ODCD.js";
10
+
11
+ // src/image-uploader.tsx
12
+ import { ImagePlus, Loader2, X } from "lucide-react";
13
+ import * as React from "react";
14
+ import { jsx, jsxs } from "react/jsx-runtime";
15
+ function ImageUploader({
16
+ value = "",
17
+ onChange,
18
+ disabled,
19
+ label,
20
+ className,
21
+ allowUrlPaste = true,
22
+ accept = "image/*,.gif",
23
+ uploadFile,
24
+ uploadFromUrl
25
+ }) {
26
+ const inputRef = React.useRef(null);
27
+ const [uploading, setUploading] = React.useState(false);
28
+ const [error, setError] = React.useState(null);
29
+ const busy = Boolean(disabled || uploading);
30
+ const handleFile = async (file) => {
31
+ if (!uploadFile) {
32
+ setError("File upload is not configured for this panel.");
33
+ return;
34
+ }
35
+ setUploading(true);
36
+ setError(null);
37
+ try {
38
+ onChange(await uploadFile(file));
39
+ } catch (err) {
40
+ setError(err instanceof Error ? err.message : "Upload failed");
41
+ } finally {
42
+ setUploading(false);
43
+ if (inputRef.current) inputRef.current.value = "";
44
+ }
45
+ };
46
+ const handleUrl = async (imageUrl) => {
47
+ const trimmed = imageUrl.trim();
48
+ if (!trimmed) {
49
+ onChange("");
50
+ return;
51
+ }
52
+ if (trimmed === value) return;
53
+ if (!uploadFromUrl || /^https?:\/\//i.test(trimmed)) {
54
+ setError(null);
55
+ onChange(trimmed);
56
+ return;
57
+ }
58
+ setUploading(true);
59
+ setError(null);
60
+ try {
61
+ onChange(await uploadFromUrl(trimmed));
62
+ } catch (err) {
63
+ setError(err instanceof Error ? err.message : "Upload failed");
64
+ } finally {
65
+ setUploading(false);
66
+ }
67
+ };
68
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-2", className), children: [
69
+ label ? /* @__PURE__ */ jsx("div", { className: "text-muted-foreground text-xs font-medium", children: label }) : null,
70
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
71
+ /* @__PURE__ */ jsx(
72
+ "input",
73
+ {
74
+ ref: inputRef,
75
+ type: "file",
76
+ accept,
77
+ className: "hidden",
78
+ disabled: busy,
79
+ onChange: (e) => {
80
+ const file = e.target.files?.[0];
81
+ if (file) void handleFile(file);
82
+ }
83
+ }
84
+ ),
85
+ /* @__PURE__ */ jsxs(
86
+ Button,
87
+ {
88
+ type: "button",
89
+ variant: "outline",
90
+ size: "sm",
91
+ disabled: busy,
92
+ onClick: () => inputRef.current?.click(),
93
+ children: [
94
+ uploading ? /* @__PURE__ */ jsx(Loader2, { className: "h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(ImagePlus, { className: "h-4 w-4" }),
95
+ uploading ? "Uploading\u2026" : "Upload"
96
+ ]
97
+ }
98
+ ),
99
+ value ? /* @__PURE__ */ jsxs(
100
+ Button,
101
+ {
102
+ type: "button",
103
+ variant: "ghost",
104
+ size: "sm",
105
+ disabled: busy,
106
+ onClick: () => onChange(""),
107
+ "aria-label": "Clear image",
108
+ children: [
109
+ /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
110
+ "Clear"
111
+ ]
112
+ }
113
+ ) : null
114
+ ] }),
115
+ allowUrlPaste ? /* @__PURE__ */ jsx(
116
+ Input,
117
+ {
118
+ defaultValue: value,
119
+ disabled: busy,
120
+ placeholder: "Or paste image URL\u2026",
121
+ onBlur: (e) => {
122
+ void handleUrl(e.target.value);
123
+ },
124
+ onKeyDown: (e) => {
125
+ if (e.key === "Enter") {
126
+ e.preventDefault();
127
+ void handleUrl(e.target.value);
128
+ }
129
+ }
130
+ },
131
+ value || "empty"
132
+ ) : null,
133
+ value ? (
134
+ // eslint-disable-next-line @next/next/no-img-element -- CDN URLs vary
135
+ /* @__PURE__ */ jsx(
136
+ "img",
137
+ {
138
+ src: value,
139
+ alt: "",
140
+ className: "border-border max-h-24 rounded border bg-white object-contain p-1"
141
+ }
142
+ )
143
+ ) : null,
144
+ error ? /* @__PURE__ */ jsx("p", { className: "text-destructive text-xs", role: "alert", children: error }) : null
145
+ ] });
146
+ }
147
+
148
+ export {
149
+ ImageUploader
150
+ };
151
+ //# sourceMappingURL=chunk-YDICVETX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/image-uploader.tsx"],"sourcesContent":["\"use client\";\n\nimport { ImagePlus, Loader2, X } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { Button } from \"./button\";\nimport { Input } from \"./input\";\nimport { cn } from \"./utils\";\n\nexport type ImageUploaderProps = {\n value?: string;\n onChange: (url: string) => void;\n disabled?: boolean;\n label?: string;\n className?: string;\n /** Allow pasting a remote URL in addition to file upload. Default true. */\n allowUrlPaste?: boolean;\n accept?: string;\n /**\n * Upload a picked file and return its hosted URL.\n * Injected by the panel — Upload stays enabled either way; without a\n * handler, picking a file surfaces a configuration error.\n */\n uploadFile?: (file: File) => Promise<string>;\n /**\n * Optional transform for pasted values (e.g. host a relative path).\n * When omitted, any pasted non-empty string is applied as-is. Absolute\n * `http(s)` URLs never call this.\n */\n uploadFromUrl?: (imageUrl: string) => Promise<string>;\n};\n\n/**\n * Reusable image uploader.\n *\n * Upload and URL paste are both enabled by default. Hosting is injected:\n * each panel passes `uploadFile` / `uploadFromUrl` so the kit stays free of\n * panel-specific API routes.\n */\nexport function ImageUploader({\n value = \"\",\n onChange,\n disabled,\n label,\n className,\n allowUrlPaste = true,\n accept = \"image/*,.gif\",\n uploadFile,\n uploadFromUrl,\n}: ImageUploaderProps) {\n const inputRef = React.useRef<HTMLInputElement>(null);\n const [uploading, setUploading] = React.useState(false);\n const [error, setError] = React.useState<string | null>(null);\n const busy = Boolean(disabled || uploading);\n\n const handleFile = async (file: File) => {\n if (!uploadFile) {\n setError(\"File upload is not configured for this panel.\");\n return;\n }\n setUploading(true);\n setError(null);\n try {\n onChange(await uploadFile(file));\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Upload failed\");\n } finally {\n setUploading(false);\n if (inputRef.current) inputRef.current.value = \"\";\n }\n };\n\n const handleUrl = async (imageUrl: string) => {\n const trimmed = imageUrl.trim();\n if (!trimmed) {\n onChange(\"\");\n return;\n }\n if (trimmed === value) return;\n\n // Absolute URLs — and any paste when no transform is configured — apply\n // as-is. Paste is never gated behind a disabled input.\n if (!uploadFromUrl || /^https?:\\/\\//i.test(trimmed)) {\n setError(null);\n onChange(trimmed);\n return;\n }\n\n setUploading(true);\n setError(null);\n try {\n onChange(await uploadFromUrl(trimmed));\n } catch (err) {\n setError(err instanceof Error ? err.message : \"Upload failed\");\n } finally {\n setUploading(false);\n }\n };\n\n return (\n <div className={cn(\"space-y-2\", className)}>\n {label ? (\n <div className=\"text-muted-foreground text-xs font-medium\">{label}</div>\n ) : null}\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <input\n ref={inputRef}\n type=\"file\"\n accept={accept}\n className=\"hidden\"\n disabled={busy}\n onChange={(e) => {\n const file = e.target.files?.[0];\n if (file) void handleFile(file);\n }}\n />\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={busy}\n onClick={() => inputRef.current?.click()}\n >\n {uploading ? (\n <Loader2 className=\"h-4 w-4 animate-spin\" />\n ) : (\n <ImagePlus className=\"h-4 w-4\" />\n )}\n {uploading ? \"Uploading…\" : \"Upload\"}\n </Button>\n {value ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n disabled={busy}\n onClick={() => onChange(\"\")}\n aria-label=\"Clear image\"\n >\n <X className=\"h-4 w-4\" />\n Clear\n </Button>\n ) : null}\n </div>\n\n {allowUrlPaste ? (\n <Input\n key={value || \"empty\"}\n defaultValue={value}\n disabled={busy}\n placeholder=\"Or paste image URL…\"\n onBlur={(e) => {\n void handleUrl(e.target.value);\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n e.preventDefault();\n void handleUrl((e.target as HTMLInputElement).value);\n }\n }}\n />\n ) : null}\n\n {value ? (\n // eslint-disable-next-line @next/next/no-img-element -- CDN URLs vary\n <img\n src={value}\n alt=\"\"\n className=\"border-border max-h-24 rounded border bg-white object-contain p-1\"\n />\n ) : null}\n\n {error ? (\n <p className=\"text-destructive text-xs\" role=\"alert\">\n {error}\n </p>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,126 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-HCA5ODCD.js";
4
+
5
+ // src/tabs.tsx
6
+ import * as React from "react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ function TabPanel({
9
+ id,
10
+ labelledBy,
11
+ active,
12
+ children,
13
+ className
14
+ }) {
15
+ return /* @__PURE__ */ jsx(
16
+ "div",
17
+ {
18
+ role: "tabpanel",
19
+ id,
20
+ "aria-labelledby": labelledBy,
21
+ hidden: !active,
22
+ className,
23
+ children: active ? children : null
24
+ }
25
+ );
26
+ }
27
+ function Tabs({
28
+ tabs,
29
+ value,
30
+ onChange,
31
+ ariaLabel,
32
+ className
33
+ }) {
34
+ const refs = React.useRef(/* @__PURE__ */ new Map());
35
+ const reactId = React.useId();
36
+ const hasInlinePanels = tabs.some((tab) => tab.panel != null);
37
+ function tabDomId(key) {
38
+ return `${reactId}-tab-${key}`;
39
+ }
40
+ function panelDomId(tab) {
41
+ return tab.panelId ?? `${reactId}-panel-${tab.key}`;
42
+ }
43
+ function focusTab(key) {
44
+ onChange(key);
45
+ refs.current.get(key)?.focus();
46
+ }
47
+ function onKeyDown(event) {
48
+ const step = event.key === "ArrowRight" ? 1 : event.key === "ArrowLeft" ? -1 : 0;
49
+ if (step === 0) return;
50
+ const selectable = tabs.filter((tab) => !tab.disabled);
51
+ if (selectable.length < 2) return;
52
+ const current = selectable.findIndex((tab) => tab.key === value);
53
+ const next = selectable[(current + step + selectable.length) % selectable.length];
54
+ event.preventDefault();
55
+ focusTab(next.key);
56
+ }
57
+ const tablist = /* @__PURE__ */ jsx(
58
+ "div",
59
+ {
60
+ role: "tablist",
61
+ "aria-label": ariaLabel,
62
+ onKeyDown,
63
+ className: cn(
64
+ "border-border flex flex-wrap gap-1 border-b",
65
+ !hasInlinePanels ? className : void 0
66
+ ),
67
+ children: tabs.map((tab) => {
68
+ const active = tab.key === value;
69
+ const id = tabDomId(tab.key);
70
+ const controls = tab.panel != null || tab.panelId ? panelDomId(tab) : void 0;
71
+ return /* @__PURE__ */ jsxs(
72
+ "button",
73
+ {
74
+ id,
75
+ ref: (node) => {
76
+ refs.current.set(tab.key, node);
77
+ },
78
+ type: "button",
79
+ role: "tab",
80
+ "aria-selected": active,
81
+ "aria-controls": controls,
82
+ disabled: tab.disabled,
83
+ tabIndex: active ? 0 : -1,
84
+ onClick: () => onChange(tab.key),
85
+ className: cn(
86
+ "cursor-pointer rounded-t-md px-3 py-1.5 text-sm font-medium transition",
87
+ "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none",
88
+ "disabled:cursor-not-allowed disabled:opacity-50",
89
+ // The filled-pill active state the five hand-rolled strips
90
+ // already used. Adopting it keeps this a deduplication rather
91
+ // than a restyle of every panel that has tabs.
92
+ active ? "bg-primary text-primary-foreground" : "text-muted-foreground hover:bg-muted"
93
+ ),
94
+ children: [
95
+ tab.label,
96
+ tab.badge != null ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground ml-1.5 text-xs", children: tab.badge }) : null
97
+ ]
98
+ },
99
+ tab.key
100
+ );
101
+ })
102
+ }
103
+ );
104
+ if (!hasInlinePanels) return tablist;
105
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-3", className), children: [
106
+ tablist,
107
+ tabs.map(
108
+ (tab) => tab.panel != null ? /* @__PURE__ */ jsx(
109
+ TabPanel,
110
+ {
111
+ id: panelDomId(tab),
112
+ labelledBy: tabDomId(tab.key),
113
+ active: tab.key === value,
114
+ children: tab.panel
115
+ },
116
+ tab.key
117
+ ) : null
118
+ )
119
+ ] });
120
+ }
121
+
122
+ export {
123
+ TabPanel,
124
+ Tabs
125
+ };
126
+ //# sourceMappingURL=chunk-ZFSQVMLJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tabs.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"./utils\";\n\n/**\n * The tab strip five Managers were each drawing by hand, with the same\n * `rounded-t-md px-3 py-1.5 text-sm font-medium` and their own idea of what an\n * active tab looks like.\n *\n * It is a controlled component with no state of its own: every caller already\n * held the active key, usually because a URL or a query depends on it.\n *\n * Roles are the reason to use this rather than styling buttons again. A hand\n * -rolled strip is a row of buttons to a screen reader, which says nothing\n * about how many tabs there are or which is selected; `tablist` / `tab` /\n * `aria-selected` do. Arrow-key navigation comes with that expectation, so it\n * is implemented here too.\n *\n * Optional `panel` on a tab (or a sibling `TabPanel`) wires APG `aria-controls`\n * / `role=\"tabpanel\"`. Callers that already render content from `value` can\n * keep doing so and pass `panelId` for association, or switch to `panel`.\n */\nexport interface TabItem<Key extends string = string> {\n key: Key;\n label: React.ReactNode;\n /** Rendered after the label — a count, usually. */\n badge?: React.ReactNode;\n disabled?: boolean;\n /**\n * Optional panel body. When any tab provides `panel`, panels render under\n * the strip with `role=\"tabpanel\"`.\n */\n panel?: React.ReactNode;\n /**\n * External panel element id for `aria-controls` when panels live outside\n * this component.\n */\n panelId?: string;\n}\n\nexport interface TabsProps<Key extends string = string> {\n tabs: ReadonlyArray<TabItem<Key>>;\n value: Key;\n onChange: (key: Key) => void;\n /** Names the strip for screen readers. */\n ariaLabel?: string;\n className?: string;\n}\n\nexport interface TabPanelProps {\n /** Must match the controlling tab's generated or explicit id. */\n id: string;\n /** Id of the tab that labels this panel. */\n labelledBy: string;\n active: boolean;\n children: React.ReactNode;\n className?: string;\n}\n\n/** Companion panel for callers that keep content outside `Tabs`. */\nexport function TabPanel({\n id,\n labelledBy,\n active,\n children,\n className,\n}: TabPanelProps) {\n return (\n <div\n role=\"tabpanel\"\n id={id}\n aria-labelledby={labelledBy}\n hidden={!active}\n className={className}\n >\n {active ? children : null}\n </div>\n );\n}\n\nexport function Tabs<Key extends string = string>({\n tabs,\n value,\n onChange,\n ariaLabel,\n className,\n}: TabsProps<Key>) {\n const refs = React.useRef(new Map<Key, HTMLButtonElement | null>());\n const reactId = React.useId();\n const hasInlinePanels = tabs.some((tab) => tab.panel != null);\n\n function tabDomId(key: Key) {\n return `${reactId}-tab-${key}`;\n }\n\n function panelDomId(tab: TabItem<Key>) {\n return tab.panelId ?? `${reactId}-panel-${tab.key}`;\n }\n\n function focusTab(key: Key) {\n onChange(key);\n // The newly selected tab has to take focus, or the next arrow press is\n // still measured from the old one.\n refs.current.get(key)?.focus();\n }\n\n function onKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {\n const step =\n event.key === \"ArrowRight\" ? 1 : event.key === \"ArrowLeft\" ? -1 : 0;\n if (step === 0) return;\n\n const selectable = tabs.filter((tab) => !tab.disabled);\n if (selectable.length < 2) return;\n\n const current = selectable.findIndex((tab) => tab.key === value);\n // Wraps, which is what a tablist is expected to do.\n const next =\n selectable[(current + step + selectable.length) % selectable.length];\n\n event.preventDefault();\n focusTab(next.key);\n }\n\n const tablist = (\n <div\n role=\"tablist\"\n aria-label={ariaLabel}\n onKeyDown={onKeyDown}\n className={cn(\n \"border-border flex flex-wrap gap-1 border-b\",\n !hasInlinePanels ? className : undefined,\n )}\n >\n {tabs.map((tab) => {\n const active = tab.key === value;\n const id = tabDomId(tab.key);\n const controls =\n tab.panel != null || tab.panelId ? panelDomId(tab) : undefined;\n return (\n <button\n key={tab.key}\n id={id}\n ref={(node) => {\n refs.current.set(tab.key, node);\n }}\n type=\"button\"\n role=\"tab\"\n aria-selected={active}\n aria-controls={controls}\n disabled={tab.disabled}\n // Only the active tab is reachable by Tab; arrows move within the\n // strip. Without this a keyboard user pages through every tab to\n // get past the strip.\n tabIndex={active ? 0 : -1}\n onClick={() => onChange(tab.key)}\n className={cn(\n \"cursor-pointer rounded-t-md px-3 py-1.5 text-sm font-medium transition\",\n \"focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n // The filled-pill active state the five hand-rolled strips\n // already used. Adopting it keeps this a deduplication rather\n // than a restyle of every panel that has tabs.\n active\n ? \"bg-primary text-primary-foreground\"\n : \"text-muted-foreground hover:bg-muted\",\n )}\n >\n {tab.label}\n {tab.badge != null ? (\n <span className=\"text-muted-foreground ml-1.5 text-xs\">\n {tab.badge}\n </span>\n ) : null}\n </button>\n );\n })}\n </div>\n );\n\n if (!hasInlinePanels) return tablist;\n\n return (\n <div className={cn(\"space-y-3\", className)}>\n {tablist}\n {tabs.map((tab) =>\n tab.panel != null ? (\n <TabPanel\n key={tab.key}\n id={panelDomId(tab)}\n labelledBy={tabDomId(tab.key)}\n active={tab.key === value}\n >\n {tab.panel}\n </TabPanel>\n ) : null,\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,46 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-HCA5ODCD.js";
4
+
5
+ // src/switch.tsx
6
+ import * as React from "react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ var Switch = React.forwardRef(
9
+ ({ checked, onCheckedChange, className, disabled, id, ...props }, ref) => /* @__PURE__ */ jsx(
10
+ "button",
11
+ {
12
+ ref,
13
+ id,
14
+ type: "button",
15
+ role: "switch",
16
+ "aria-checked": checked,
17
+ disabled,
18
+ onClick: () => onCheckedChange(!checked),
19
+ className: cn(
20
+ // 24×44px track meets DESIGN.md 24px minimum target size.
21
+ "relative inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border transition-colors",
22
+ "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none",
23
+ "disabled:cursor-not-allowed disabled:opacity-50",
24
+ checked ? "border-primary bg-primary" : "border-border bg-muted",
25
+ className
26
+ ),
27
+ ...props,
28
+ children: /* @__PURE__ */ jsx(
29
+ "span",
30
+ {
31
+ "aria-hidden": true,
32
+ className: cn(
33
+ "bg-background pointer-events-none block size-5 rounded-full shadow-sm transition-transform",
34
+ checked ? "translate-x-5" : "translate-x-0.5"
35
+ )
36
+ }
37
+ )
38
+ }
39
+ )
40
+ );
41
+ Switch.displayName = "Switch";
42
+
43
+ export {
44
+ Switch
45
+ };
46
+ //# sourceMappingURL=chunk-ZHATD73Y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/switch.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"./utils\";\n\nexport type SwitchProps = Omit<\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n \"onChange\"\n> & {\n checked: boolean;\n onCheckedChange: (checked: boolean) => void;\n};\n\n/** Compact accessible toggle — use instead of full-width checkbox inputs. */\nexport const Switch = React.forwardRef<HTMLButtonElement, SwitchProps>(\n ({ checked, onCheckedChange, className, disabled, id, ...props }, ref) => (\n <button\n ref={ref}\n id={id}\n type=\"button\"\n role=\"switch\"\n aria-checked={checked}\n disabled={disabled}\n onClick={() => onCheckedChange(!checked)}\n className={cn(\n // 24×44px track meets DESIGN.md 24px minimum target size.\n \"relative inline-flex h-6 w-11 shrink-0 cursor-pointer items-center rounded-full border transition-colors\",\n \"focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none\",\n \"disabled:cursor-not-allowed disabled:opacity-50\",\n checked ? \"border-primary bg-primary\" : \"border-border bg-muted\",\n className,\n )}\n {...props}\n >\n <span\n aria-hidden\n className={cn(\n \"bg-background pointer-events-none block size-5 rounded-full shadow-sm transition-transform\",\n checked ? \"translate-x-5\" : \"translate-x-0.5\",\n )}\n />\n </button>\n ),\n);\n\nSwitch.displayName = \"Switch\";\n"],"mappings":";;;;;AAEA,YAAY,WAAW;AAiCjB;AApBC,IAAM,SAAe;AAAA,EAC1B,CAAC,EAAE,SAAS,iBAAiB,WAAW,UAAU,IAAI,GAAG,MAAM,GAAG,QAChE;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA;AAAA,MACA,MAAK;AAAA,MACL,MAAK;AAAA,MACL,gBAAc;AAAA,MACd;AAAA,MACA,SAAS,MAAM,gBAAgB,CAAC,OAAO;AAAA,MACvC,WAAW;AAAA;AAAA,QAET;AAAA,QACA;AAAA,QACA;AAAA,QACA,UAAU,8BAA8B;AAAA,QACxC;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEJ;AAAA,QAAC;AAAA;AAAA,UACC,eAAW;AAAA,UACX,WAAW;AAAA,YACT;AAAA,YACA,UAAU,kBAAkB;AAAA,UAC9B;AAAA;AAAA,MACF;AAAA;AAAA,EACF;AAEJ;AAEA,OAAO,cAAc;","names":[]}
@@ -0,0 +1,111 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-HCA5ODCD.js";
4
+
5
+ // src/data-table.tsx
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ var HEADER_VARIANTS = {
8
+ uppercase: "border-border bg-muted/40 text-muted-foreground border-b text-xs tracking-wide uppercase",
9
+ plain: "text-muted-foreground border-border border-b text-left"
10
+ };
11
+ var DENSITY = {
12
+ comfortable: "px-4 py-3",
13
+ compact: "px-3 py-2"
14
+ };
15
+ var ALIGN = {
16
+ left: "text-left",
17
+ right: "text-right",
18
+ center: "text-center"
19
+ };
20
+ function DataTable({
21
+ columns,
22
+ rows,
23
+ rowKey,
24
+ variant = "uppercase",
25
+ density = "comfortable",
26
+ stickyHeader = false,
27
+ rowClassName,
28
+ emptyMessage,
29
+ footer,
30
+ ariaLabel,
31
+ className,
32
+ tableClassName
33
+ }) {
34
+ return /* @__PURE__ */ jsx(
35
+ "div",
36
+ {
37
+ className: cn(
38
+ "border-border bg-background/80 overflow-x-auto rounded-lg border",
39
+ className
40
+ ),
41
+ children: /* @__PURE__ */ jsxs(
42
+ "table",
43
+ {
44
+ "aria-label": ariaLabel,
45
+ className: cn("min-w-full text-left text-sm", tableClassName),
46
+ children: [
47
+ /* @__PURE__ */ jsx(
48
+ "thead",
49
+ {
50
+ className: cn(
51
+ HEADER_VARIANTS[variant],
52
+ stickyHeader && "bg-muted sticky top-0 z-10"
53
+ ),
54
+ children: /* @__PURE__ */ jsx("tr", { children: columns.map((column) => /* @__PURE__ */ jsx(
55
+ "th",
56
+ {
57
+ scope: "col",
58
+ "aria-sort": column.ariaSort,
59
+ className: cn(
60
+ DENSITY[density],
61
+ "font-medium",
62
+ ALIGN[column.align ?? "left"],
63
+ column.headerClassName
64
+ ),
65
+ children: column.title
66
+ },
67
+ column.key
68
+ )) })
69
+ }
70
+ ),
71
+ /* @__PURE__ */ jsx("tbody", { children: rows.length === 0 && emptyMessage != null ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
72
+ "td",
73
+ {
74
+ colSpan: columns.length,
75
+ className: "text-muted-foreground px-4 py-6 text-center text-sm",
76
+ children: emptyMessage
77
+ }
78
+ ) }) : rows.map((row, index) => /* @__PURE__ */ jsx(
79
+ "tr",
80
+ {
81
+ className: cn(
82
+ "border-border/70 border-b last:border-0",
83
+ rowClassName?.(row, index)
84
+ ),
85
+ children: columns.map((column) => /* @__PURE__ */ jsx(
86
+ "td",
87
+ {
88
+ className: cn(
89
+ DENSITY[density],
90
+ ALIGN[column.align ?? "left"],
91
+ column.className
92
+ ),
93
+ children: column.cell(row, index)
94
+ },
95
+ column.key
96
+ ))
97
+ },
98
+ rowKey(row, index)
99
+ )) }),
100
+ footer != null ? /* @__PURE__ */ jsx("tfoot", { children: footer }) : null
101
+ ]
102
+ }
103
+ )
104
+ }
105
+ );
106
+ }
107
+
108
+ export {
109
+ DataTable
110
+ };
111
+ //# sourceMappingURL=chunk-ZLAA4DGG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/data-table.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport { cn } from \"./utils\";\n\n/**\n * The list table every feature panel was hand-rolling.\n *\n * Seventeen files repeated the same wrapper string\n * (`border-border bg-background/80 overflow-x-auto rounded-lg border`) and the\n * header had already drifted into two incompatible looks, so Campaigns and\n * Targets did not resemble each other. Both are kept as `variant` rather than\n * unified here: collapsing them is a visual change to ten panels and belongs in\n * its own reviewable diff, not smuggled in with a refactor.\n *\n * The column contract deliberately echoes `GridColumn` in\n * `spreadsheet-grid.tsx` — `key`, `title`, a renderer, an optional class — so\n * the repo has one shape to learn. It drops that type's `value: (row) => string`\n * because there is no clipboard or CSV here; nothing would keep it honest, and\n * a required field nobody reads gets filled in with lies.\n *\n * **Not for every table.** `GroupTable` renders parent and child rows with\n * different column counts, and `BusinessTable`'s rows are dnd-kit sortables that\n * own their own refs and transforms. `PublishModal`, `BulkCreateModal` and the\n * clone queue in `MetaAdSetsPanel` are grids of inputs rather than lists of\n * values — every cell is an editable control, and they size their header and\n * body rows differently on purpose. All five are legitimately tables of a\n * different kind; bending this component around them would cost more than it\n * saves.\n */\nexport type TableColumn<Row> = {\n key: string;\n /** Node, not string: a header can carry a select-all box or a sort control. */\n title: React.ReactNode;\n cell: (row: Row, index: number) => React.ReactNode;\n align?: \"left\" | \"right\" | \"center\";\n /**\n * Set on a sortable column so a screen reader announces the current sort.\n * Only the sorted column says `ascending`/`descending`; the rest say `none`,\n * which is what marks them as sortable at all.\n */\n ariaSort?: \"ascending\" | \"descending\" | \"none\";\n /** Extra classes for this column's `<td>`. */\n className?: string;\n /** Extra classes for this column's `<th>`. */\n headerClassName?: string;\n};\n\nexport interface DataTableProps<Row> {\n columns: TableColumn<Row>[];\n rows: Row[];\n /** Stable React key per row. */\n rowKey: (row: Row, index: number) => string;\n /**\n * `uppercase` — the tinted small-caps header (campaigns, showcase, filters).\n * `plain` — the quieter left-aligned header (ads, targets, creatives).\n */\n variant?: \"uppercase\" | \"plain\";\n /**\n * `comfortable` — the roomy default, for tables of a handful of columns.\n * `compact` — what every table of eight columns or more had already chosen by\n * hand, because the roomy padding pushes them into a horizontal scroll.\n */\n density?: \"comfortable\" | \"compact\";\n /** Pins the header for a table inside its own scroll container. */\n stickyHeader?: boolean;\n rowClassName?: (row: Row, index: number) => string | undefined;\n /** Rendered in place of `<tbody>` rows when there are none. */\n emptyMessage?: React.ReactNode;\n /**\n * Rows for a `<tfoot>` — a totals line, typically. The caller supplies the\n * `<tr>` itself because a summary row rarely lines up with the columns above\n * it; designer-counts spans its first two.\n */\n footer?: React.ReactNode;\n /** Names the table for screen readers when no visible caption fits. */\n ariaLabel?: string;\n /** Classes for the scroll wrapper. */\n className?: string;\n /** Classes for the `<table>`. */\n tableClassName?: string;\n}\n\nconst HEADER_VARIANTS = {\n uppercase:\n \"border-border bg-muted/40 text-muted-foreground border-b text-xs tracking-wide uppercase\",\n plain: \"text-muted-foreground border-border border-b text-left\",\n} as const;\n\nconst DENSITY = {\n comfortable: \"px-4 py-3\",\n compact: \"px-3 py-2\",\n} as const;\n\nconst ALIGN = {\n left: \"text-left\",\n right: \"text-right\",\n center: \"text-center\",\n} as const;\n\nexport function DataTable<Row>({\n columns,\n rows,\n rowKey,\n variant = \"uppercase\",\n density = \"comfortable\",\n stickyHeader = false,\n rowClassName,\n emptyMessage,\n footer,\n ariaLabel,\n className,\n tableClassName,\n}: DataTableProps<Row>) {\n return (\n <div\n className={cn(\n \"border-border bg-background/80 overflow-x-auto rounded-lg border\",\n className,\n )}\n >\n <table\n aria-label={ariaLabel}\n className={cn(\"min-w-full text-left text-sm\", tableClassName)}\n >\n <thead\n className={cn(\n HEADER_VARIANTS[variant],\n stickyHeader && \"bg-muted sticky top-0 z-10\",\n )}\n >\n <tr>\n {columns.map((column) => (\n <th\n key={column.key}\n scope=\"col\"\n aria-sort={column.ariaSort}\n className={cn(\n DENSITY[density],\n \"font-medium\",\n ALIGN[column.align ?? \"left\"],\n column.headerClassName,\n )}\n >\n {column.title}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {rows.length === 0 && emptyMessage != null ? (\n <tr>\n {/* One cell across the whole width — a short row would otherwise\n leave the message sitting under the first column. */}\n <td\n colSpan={columns.length}\n className=\"text-muted-foreground px-4 py-6 text-center text-sm\"\n >\n {emptyMessage}\n </td>\n </tr>\n ) : (\n rows.map((row, index) => (\n <tr\n key={rowKey(row, index)}\n className={cn(\n \"border-border/70 border-b last:border-0\",\n rowClassName?.(row, index),\n )}\n >\n {columns.map((column) => (\n <td\n key={column.key}\n className={cn(\n DENSITY[density],\n ALIGN[column.align ?? \"left\"],\n column.className,\n )}\n >\n {column.cell(row, index)}\n </td>\n ))}\n </tr>\n ))\n )}\n </tbody>\n {footer != null ? <tfoot>{footer}</tfoot> : null}\n </table>\n </div>\n );\n}\n"],"mappings":";;;;;AAwHM,SAYQ,KAZR;AAtCN,IAAM,kBAAkB;AAAA,EACtB,WACE;AAAA,EACF,OAAO;AACT;AAEA,IAAM,UAAU;AAAA,EACd,aAAa;AAAA,EACb,SAAS;AACX;AAEA,IAAM,QAAQ;AAAA,EACZ,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AACV;AAEO,SAAS,UAAe;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,UAAU;AAAA,EACV,eAAe;AAAA,EACf;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAwB;AACtB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEA;AAAA,QAAC;AAAA;AAAA,UACC,cAAY;AAAA,UACZ,WAAW,GAAG,gCAAgC,cAAc;AAAA,UAE5D;AAAA;AAAA,cAAC;AAAA;AAAA,gBACC,WAAW;AAAA,kBACT,gBAAgB,OAAO;AAAA,kBACvB,gBAAgB;AAAA,gBAClB;AAAA,gBAEA,8BAAC,QACE,kBAAQ,IAAI,CAAC,WACZ;AAAA,kBAAC;AAAA;AAAA,oBAEC,OAAM;AAAA,oBACN,aAAW,OAAO;AAAA,oBAClB,WAAW;AAAA,sBACT,QAAQ,OAAO;AAAA,sBACf;AAAA,sBACA,MAAM,OAAO,SAAS,MAAM;AAAA,sBAC5B,OAAO;AAAA,oBACT;AAAA,oBAEC,iBAAO;AAAA;AAAA,kBAVH,OAAO;AAAA,gBAWd,CACD,GACH;AAAA;AAAA,YACF;AAAA,YACA,oBAAC,WACE,eAAK,WAAW,KAAK,gBAAgB,OACpC,oBAAC,QAGC;AAAA,cAAC;AAAA;AAAA,gBACC,SAAS,QAAQ;AAAA,gBACjB,WAAU;AAAA,gBAET;AAAA;AAAA,YACH,GACF,IAEA,KAAK,IAAI,CAAC,KAAK,UACb;AAAA,cAAC;AAAA;AAAA,gBAEC,WAAW;AAAA,kBACT;AAAA,kBACA,eAAe,KAAK,KAAK;AAAA,gBAC3B;AAAA,gBAEC,kBAAQ,IAAI,CAAC,WACZ;AAAA,kBAAC;AAAA;AAAA,oBAEC,WAAW;AAAA,sBACT,QAAQ,OAAO;AAAA,sBACf,MAAM,OAAO,SAAS,MAAM;AAAA,sBAC5B,OAAO;AAAA,oBACT;AAAA,oBAEC,iBAAO,KAAK,KAAK,KAAK;AAAA;AAAA,kBAPlB,OAAO;AAAA,gBAQd,CACD;AAAA;AAAA,cAjBI,OAAO,KAAK,KAAK;AAAA,YAkBxB,CACD,GAEL;AAAA,YACC,UAAU,OAAO,oBAAC,WAAO,kBAAO,IAAW;AAAA;AAAA;AAAA,MAC9C;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -0,0 +1,56 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-HCA5ODCD.js";
4
+
5
+ // src/alert.tsx
6
+ import * as React from "react";
7
+ import { cva } from "class-variance-authority";
8
+ import { jsx, jsxs } from "react/jsx-runtime";
9
+ var alertVariants = cva("rounded-lg border px-3 py-2 text-sm", {
10
+ variants: {
11
+ variant: {
12
+ danger: "border-destructive/30 bg-destructive/5 text-destructive",
13
+ warning: "border-warning/30 bg-warning/5 text-warning",
14
+ success: "border-success/30 bg-success/5 text-success",
15
+ info: "border-info/30 bg-info/5 text-info",
16
+ /**
17
+ * The quiet state of a banner that also has loud ones — "no default
18
+ * campaign set" next to "this is the default". Without it those callers
19
+ * keep a ternary of raw class strings and the shared component only
20
+ * covers half of what they draw.
21
+ */
22
+ neutral: "border-border bg-muted/40 text-muted-foreground"
23
+ }
24
+ },
25
+ defaultVariants: {
26
+ // Failure is what a banner almost always reports here, and defaulting to
27
+ // it means the noisy case cannot be forgotten.
28
+ variant: "danger"
29
+ }
30
+ });
31
+ var Alert = React.forwardRef(
32
+ ({ className, variant, action, children, ...props }, ref) => /* @__PURE__ */ jsxs(
33
+ "div",
34
+ {
35
+ ref,
36
+ role: variant === "danger" || variant == null ? "alert" : "status",
37
+ className: cn(
38
+ alertVariants({ variant }),
39
+ action && "flex flex-wrap items-center justify-between gap-3",
40
+ className
41
+ ),
42
+ ...props,
43
+ children: [
44
+ action ? /* @__PURE__ */ jsx("span", { children }) : children,
45
+ action
46
+ ]
47
+ }
48
+ )
49
+ );
50
+ Alert.displayName = "Alert";
51
+
52
+ export {
53
+ alertVariants,
54
+ Alert
55
+ };
56
+ //# sourceMappingURL=chunk-ZP252OJZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/alert.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"./utils\";\n\n/**\n * The inline message banner — a failed load, a warning above a form, a note.\n *\n * This was the most duplicated markup in the panel: thirty blocks across\n * twenty-eight files, spelled seven different ways. `px-3 py-2` in most places\n * and `px-4 py-3` in others, `text-rose-700` here and `text-destructive`\n * there — all meaning \"something went wrong\".\n *\n * `role` is derived from the variant rather than left to callers. An error a\n * user needs to act on should interrupt a screen reader; a note should not.\n * Almost none of the hand-rolled copies set either.\n */\nconst alertVariants = cva(\"rounded-lg border px-3 py-2 text-sm\", {\n variants: {\n variant: {\n danger: \"border-destructive/30 bg-destructive/5 text-destructive\",\n warning: \"border-warning/30 bg-warning/5 text-warning\",\n success: \"border-success/30 bg-success/5 text-success\",\n info: \"border-info/30 bg-info/5 text-info\",\n /**\n * The quiet state of a banner that also has loud ones — \"no default\n * campaign set\" next to \"this is the default\". Without it those callers\n * keep a ternary of raw class strings and the shared component only\n * covers half of what they draw.\n */\n neutral: \"border-border bg-muted/40 text-muted-foreground\",\n },\n },\n defaultVariants: {\n // Failure is what a banner almost always reports here, and defaulting to\n // it means the noisy case cannot be forgotten.\n variant: \"danger\",\n },\n});\n\nexport interface AlertProps\n extends\n React.HTMLAttributes<HTMLDivElement>,\n VariantProps<typeof alertVariants> {\n /**\n * A control belonging to the message, such as Retry. Given one, the banner\n * lays out as a row and keeps the action at its end.\n */\n action?: React.ReactNode;\n}\n\nexport const Alert = React.forwardRef<HTMLDivElement, AlertProps>(\n ({ className, variant, action, children, ...props }, ref) => (\n <div\n ref={ref}\n role={variant === \"danger\" || variant == null ? \"alert\" : \"status\"}\n className={cn(\n alertVariants({ variant }),\n action && \"flex flex-wrap items-center justify-between gap-3\",\n className,\n )}\n {...props}\n >\n {/* Wrapped so the action sits opposite the text rather than being\n pushed around by whatever the caller passed as children. */}\n {action ? <span>{children}</span> : children}\n {action}\n </div>\n ),\n);\n\nAlert.displayName = \"Alert\";\n\nexport { alertVariants };\n"],"mappings":";;;;;AAAA,YAAY,WAAW;AACvB,SAAS,WAA8B;AAoDnC,SAYY,KAZZ;AApCJ,IAAM,gBAAgB,IAAI,uCAAuC;AAAA,EAC/D,UAAU;AAAA,IACR,SAAS;AAAA,MACP,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,SAAS;AAAA,MACT,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAON,SAAS;AAAA,IACX;AAAA,EACF;AAAA,EACA,iBAAiB;AAAA;AAAA;AAAA,IAGf,SAAS;AAAA,EACX;AACF,CAAC;AAaM,IAAM,QAAc;AAAA,EACzB,CAAC,EAAE,WAAW,SAAS,QAAQ,UAAU,GAAG,MAAM,GAAG,QACnD;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,MAAM,YAAY,YAAY,WAAW,OAAO,UAAU;AAAA,MAC1D,WAAW;AAAA,QACT,cAAc,EAAE,QAAQ,CAAC;AAAA,QACzB,UAAU;AAAA,QACV;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAIH;AAAA,iBAAS,oBAAC,UAAM,UAAS,IAAU;AAAA,QACnC;AAAA;AAAA;AAAA,EACH;AAEJ;AAEA,MAAM,cAAc;","names":[]}