@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,297 @@
1
+ import {
2
+ addDays,
3
+ addMonths,
4
+ formatDisplay,
5
+ fromIsoDate,
6
+ isSameDay,
7
+ monthGrid,
8
+ monthLabel,
9
+ startOfMonth,
10
+ toIsoDate
11
+ } from "./chunk-MUPW2TFJ.js";
12
+ import {
13
+ cn
14
+ } from "./chunk-HCA5ODCD.js";
15
+
16
+ // src/date-range-picker/index.tsx
17
+ import { CalendarDays, ChevronLeft, ChevronRight, X } from "lucide-react";
18
+ import * as React from "react";
19
+ import { jsx, jsxs } from "react/jsx-runtime";
20
+ var PRESETS = [
21
+ {
22
+ label: "Today",
23
+ range: () => {
24
+ const today = toIsoDate(/* @__PURE__ */ new Date());
25
+ return { from: today, to: today };
26
+ }
27
+ },
28
+ {
29
+ label: "Yesterday",
30
+ range: () => {
31
+ const day = toIsoDate(addDays(/* @__PURE__ */ new Date(), -1));
32
+ return { from: day, to: day };
33
+ }
34
+ },
35
+ {
36
+ label: "Last 7 days",
37
+ range: () => ({
38
+ from: toIsoDate(addDays(/* @__PURE__ */ new Date(), -6)),
39
+ to: toIsoDate(/* @__PURE__ */ new Date())
40
+ })
41
+ },
42
+ {
43
+ label: "Last 30 days",
44
+ range: () => ({
45
+ from: toIsoDate(addDays(/* @__PURE__ */ new Date(), -29)),
46
+ to: toIsoDate(/* @__PURE__ */ new Date())
47
+ })
48
+ },
49
+ {
50
+ label: "This month",
51
+ range: () => ({
52
+ from: toIsoDate(startOfMonth(/* @__PURE__ */ new Date())),
53
+ to: toIsoDate(/* @__PURE__ */ new Date())
54
+ })
55
+ }
56
+ ];
57
+ var WEEKDAYS = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
58
+ function DateRangePicker({
59
+ value,
60
+ onChange,
61
+ disabled,
62
+ placeholder = "Any date",
63
+ className,
64
+ ariaLabel = "Date range"
65
+ }) {
66
+ const [open, setOpen] = React.useState(false);
67
+ const [hovered, setHovered] = React.useState(null);
68
+ const [pendingFrom, setPendingFrom] = React.useState(null);
69
+ const rootRef = React.useRef(null);
70
+ const from = fromIsoDate(value.from);
71
+ const to = fromIsoDate(value.to);
72
+ const [viewMonth, setViewMonth] = React.useState(
73
+ () => startOfMonth(from ?? /* @__PURE__ */ new Date())
74
+ );
75
+ React.useEffect(() => {
76
+ if (!open) return void 0;
77
+ const onPointerDown = (event) => {
78
+ if (!rootRef.current?.contains(event.target)) setOpen(false);
79
+ };
80
+ const onKeyDown = (event) => {
81
+ if (event.key === "Escape") setOpen(false);
82
+ };
83
+ document.addEventListener("mousedown", onPointerDown);
84
+ document.addEventListener("keydown", onKeyDown);
85
+ return () => {
86
+ document.removeEventListener("mousedown", onPointerDown);
87
+ document.removeEventListener("keydown", onKeyDown);
88
+ };
89
+ }, [open]);
90
+ function openPicker() {
91
+ setViewMonth(startOfMonth(fromIsoDate(value.from) ?? /* @__PURE__ */ new Date()));
92
+ setPendingFrom(null);
93
+ setHovered(null);
94
+ setOpen(true);
95
+ }
96
+ const anchor = pendingFrom ?? (to ? null : from);
97
+ const previewEnd = pendingFrom ? hovered : null;
98
+ function rangeBounds() {
99
+ if (pendingFrom) {
100
+ if (!previewEnd) return [pendingFrom, pendingFrom];
101
+ return previewEnd.getTime() < pendingFrom.getTime() ? [previewEnd, pendingFrom] : [pendingFrom, previewEnd];
102
+ }
103
+ return [from, to];
104
+ }
105
+ const [rangeStart, rangeEnd] = rangeBounds();
106
+ function selectDay(day) {
107
+ if (!anchor) {
108
+ setPendingFrom(day);
109
+ onChange({ from: toIsoDate(day), to: "" });
110
+ return;
111
+ }
112
+ const [start, end] = day.getTime() < anchor.getTime() ? [day, anchor] : [anchor, day];
113
+ onChange({ from: toIsoDate(start), to: toIsoDate(end) });
114
+ setPendingFrom(null);
115
+ setHovered(null);
116
+ setOpen(false);
117
+ }
118
+ function onGridKeyDown(event) {
119
+ const offsets = {
120
+ ArrowLeft: -1,
121
+ ArrowRight: 1,
122
+ ArrowUp: -7,
123
+ ArrowDown: 7
124
+ };
125
+ const offset = offsets[event.key];
126
+ if (offset === void 0) return;
127
+ const focused = document.activeElement;
128
+ const iso = focused?.dataset?.day;
129
+ const current = fromIsoDate(iso);
130
+ if (!current) return;
131
+ event.preventDefault();
132
+ const next = addDays(current, offset);
133
+ const nextIso = toIsoDate(next);
134
+ if (!rootRef.current?.querySelector(`[data-day="${nextIso}"]`)) {
135
+ setViewMonth(startOfMonth(offset < 0 ? next : addMonths(next, -1)));
136
+ }
137
+ window.requestAnimationFrame(() => {
138
+ rootRef.current?.querySelector(`[data-day="${nextIso}"]`)?.focus();
139
+ });
140
+ }
141
+ const label = value.from || value.to ? `${formatDisplay(value.from) || "Any"} \u2192 ${formatDisplay(value.to) || "Any"}` : placeholder;
142
+ const today = /* @__PURE__ */ new Date();
143
+ function renderMonth(month) {
144
+ return /* @__PURE__ */ jsxs("div", { className: "w-[15.75rem] shrink-0", children: [
145
+ /* @__PURE__ */ jsx("div", { className: "text-foreground mb-2 text-center text-sm font-medium whitespace-nowrap", children: monthLabel(month) }),
146
+ /* @__PURE__ */ jsx("div", { className: "text-muted-foreground grid grid-cols-7 text-center text-[11px]", children: WEEKDAYS.map((day) => /* @__PURE__ */ jsx("div", { className: "grid size-9 place-items-center", children: day }, day)) }),
147
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7", children: monthGrid(month).map((day) => {
148
+ const iso = toIsoDate(day);
149
+ const outside = day.getMonth() !== month.getMonth();
150
+ const isStart = isSameDay(day, rangeStart);
151
+ const isEnd = isSameDay(day, rangeEnd);
152
+ const inRange = rangeStart != null && rangeEnd != null && day.getTime() > rangeStart.getTime() && day.getTime() < rangeEnd.getTime();
153
+ const isToday = isSameDay(day, today);
154
+ return /* @__PURE__ */ jsx(
155
+ "button",
156
+ {
157
+ type: "button",
158
+ "data-day": iso,
159
+ onClick: () => selectDay(day),
160
+ onMouseEnter: () => setHovered(day),
161
+ "aria-label": day.toDateString(),
162
+ "aria-pressed": isStart || isEnd,
163
+ className: cn(
164
+ "focus-visible:ring-ring relative grid size-9 place-items-center rounded-md text-sm transition-colors focus-visible:ring-2 focus-visible:outline-none",
165
+ outside ? "text-muted-foreground/50" : "text-foreground",
166
+ inRange && "bg-primary/10 rounded-none",
167
+ (isStart || isEnd) && "bg-primary text-primary-foreground font-medium",
168
+ // Square off the inner edge so a range reads as one bar
169
+ // rather than two pills with a gap.
170
+ isStart && rangeEnd && !isEnd && "rounded-r-none",
171
+ isEnd && rangeStart && !isStart && "rounded-l-none",
172
+ !isStart && !isEnd && "hover:bg-muted",
173
+ isToday && !isStart && !isEnd && "ring-border font-semibold ring-1"
174
+ ),
175
+ children: day.getDate()
176
+ },
177
+ iso
178
+ );
179
+ }) })
180
+ ] }, monthLabel(month));
181
+ }
182
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: cn("relative", className), children: [
183
+ /* @__PURE__ */ jsxs(
184
+ "div",
185
+ {
186
+ className: cn(
187
+ "border-border bg-background flex items-center rounded-md border",
188
+ "focus-within:ring-ring/50 focus-within:border-ring focus-within:ring-2",
189
+ disabled && "opacity-50"
190
+ ),
191
+ children: [
192
+ /* @__PURE__ */ jsxs(
193
+ "button",
194
+ {
195
+ type: "button",
196
+ disabled,
197
+ "aria-haspopup": "dialog",
198
+ "aria-expanded": open,
199
+ "aria-label": ariaLabel,
200
+ onClick: () => open ? setOpen(false) : openPicker(),
201
+ className: "flex min-w-0 flex-1 items-center gap-2 px-2 py-1.5 text-left text-sm",
202
+ children: [
203
+ /* @__PURE__ */ jsx(
204
+ CalendarDays,
205
+ {
206
+ className: "text-muted-foreground size-4 shrink-0",
207
+ "aria-hidden": true
208
+ }
209
+ ),
210
+ /* @__PURE__ */ jsx(
211
+ "span",
212
+ {
213
+ className: cn(
214
+ "truncate",
215
+ !value.from && !value.to && "text-muted-foreground"
216
+ ),
217
+ children: label
218
+ }
219
+ )
220
+ ]
221
+ }
222
+ ),
223
+ (value.from || value.to) && !disabled ? /* @__PURE__ */ jsx(
224
+ "button",
225
+ {
226
+ type: "button",
227
+ "aria-label": "Clear date range",
228
+ onClick: () => {
229
+ onChange({ from: "", to: "" });
230
+ setPendingFrom(null);
231
+ },
232
+ className: "text-muted-foreground hover:text-foreground shrink-0 px-2",
233
+ children: /* @__PURE__ */ jsx(X, { className: "size-3.5", "aria-hidden": true })
234
+ }
235
+ ) : null
236
+ ]
237
+ }
238
+ ),
239
+ open ? /* @__PURE__ */ jsxs(
240
+ "div",
241
+ {
242
+ role: "dialog",
243
+ "aria-label": ariaLabel,
244
+ className: "border-border bg-background absolute top-full right-0 z-50 mt-1 flex w-max rounded-lg border p-3 shadow-lg",
245
+ children: [
246
+ /* @__PURE__ */ jsx("div", { className: "border-border mr-3 flex w-32 shrink-0 flex-col gap-0.5 border-r pr-3", children: PRESETS.map((preset) => /* @__PURE__ */ jsx(
247
+ "button",
248
+ {
249
+ type: "button",
250
+ className: "hover:bg-muted text-foreground rounded-md px-2 py-1.5 text-left text-xs",
251
+ onClick: () => {
252
+ onChange(preset.range());
253
+ setPendingFrom(null);
254
+ setOpen(false);
255
+ },
256
+ children: preset.label
257
+ },
258
+ preset.label
259
+ )) }),
260
+ /* @__PURE__ */ jsxs("div", { onKeyDown: onGridKeyDown, children: [
261
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
262
+ /* @__PURE__ */ jsx(
263
+ "button",
264
+ {
265
+ type: "button",
266
+ "aria-label": "Previous month",
267
+ className: "hover:bg-muted text-muted-foreground hover:text-foreground rounded-md p-1.5",
268
+ onClick: () => setViewMonth(addMonths(viewMonth, -1)),
269
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4", "aria-hidden": true })
270
+ }
271
+ ),
272
+ /* @__PURE__ */ jsx(
273
+ "button",
274
+ {
275
+ type: "button",
276
+ "aria-label": "Next month",
277
+ className: "hover:bg-muted text-muted-foreground hover:text-foreground rounded-md p-1.5",
278
+ onClick: () => setViewMonth(addMonths(viewMonth, 1)),
279
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4", "aria-hidden": true })
280
+ }
281
+ )
282
+ ] }),
283
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-5", onMouseLeave: () => setHovered(null), children: [
284
+ renderMonth(viewMonth),
285
+ renderMonth(addMonths(viewMonth, 1))
286
+ ] })
287
+ ] })
288
+ ]
289
+ }
290
+ ) : null
291
+ ] });
292
+ }
293
+
294
+ export {
295
+ DateRangePicker
296
+ };
297
+ //# sourceMappingURL=chunk-T2KXVH3O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/date-range-picker/index.tsx"],"sourcesContent":["\"use client\";\n\nimport { CalendarDays, ChevronLeft, ChevronRight, X } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { cn } from \"../utils\";\nimport {\n addDays,\n addMonths,\n formatDisplay,\n fromIsoDate,\n isSameDay,\n monthGrid,\n monthLabel,\n startOfMonth,\n toIsoDate,\n} from \"./dateRange\";\n\nexport type DateRangeValue = { from: string; to: string };\n\n/**\n * A two-month range calendar.\n *\n * Two separate date fields cost two columns and read as two unrelated\n * questions when they are one: which window. They also let the pair be set\n * backwards, which returns nothing and looks like a broken filter.\n *\n * Two months rather than one because almost every range anyone picks here\n * crosses a month boundary — \"last week of July into the first of August\" is a\n * single gesture with two months visible and a paging exercise with one.\n *\n * Presets are the useful half. The common ranges are the same handful every\n * time, and picking \"Last 7 days\" out of a list beats counting backwards on a\n * grid.\n */\nconst PRESETS: { label: string; range: () => DateRangeValue }[] = [\n {\n label: \"Today\",\n range: () => {\n const today = toIsoDate(new Date());\n return { from: today, to: today };\n },\n },\n {\n label: \"Yesterday\",\n range: () => {\n const day = toIsoDate(addDays(new Date(), -1));\n return { from: day, to: day };\n },\n },\n {\n label: \"Last 7 days\",\n range: () => ({\n from: toIsoDate(addDays(new Date(), -6)),\n to: toIsoDate(new Date()),\n }),\n },\n {\n label: \"Last 30 days\",\n range: () => ({\n from: toIsoDate(addDays(new Date(), -29)),\n to: toIsoDate(new Date()),\n }),\n },\n {\n label: \"This month\",\n range: () => ({\n from: toIsoDate(startOfMonth(new Date())),\n to: toIsoDate(new Date()),\n }),\n },\n];\n\nconst WEEKDAYS = [\"Su\", \"Mo\", \"Tu\", \"We\", \"Th\", \"Fr\", \"Sa\"];\n\nexport interface DateRangePickerProps {\n value: DateRangeValue;\n onChange: (next: DateRangeValue) => void;\n disabled?: boolean;\n placeholder?: string;\n className?: string;\n /** Names the trigger for screen readers when there is no visible label. */\n ariaLabel?: string;\n}\n\nexport function DateRangePicker({\n value,\n onChange,\n disabled,\n placeholder = \"Any date\",\n className,\n ariaLabel = \"Date range\",\n}: DateRangePickerProps) {\n const [open, setOpen] = React.useState(false);\n const [hovered, setHovered] = React.useState<Date | null>(null);\n /** Set while choosing the second end, so the range can preview under the cursor. */\n const [pendingFrom, setPendingFrom] = React.useState<Date | null>(null);\n const rootRef = React.useRef<HTMLDivElement>(null);\n\n const from = fromIsoDate(value.from);\n const to = fromIsoDate(value.to);\n\n const [viewMonth, setViewMonth] = React.useState<Date>(() =>\n startOfMonth(from ?? new Date()),\n );\n\n React.useEffect(() => {\n if (!open) return undefined;\n const onPointerDown = (event: MouseEvent) => {\n if (!rootRef.current?.contains(event.target as Node)) setOpen(false);\n };\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") setOpen(false);\n };\n document.addEventListener(\"mousedown\", onPointerDown);\n document.addEventListener(\"keydown\", onKeyDown);\n return () => {\n document.removeEventListener(\"mousedown\", onPointerDown);\n document.removeEventListener(\"keydown\", onKeyDown);\n };\n }, [open]);\n\n /**\n * Reopening lands on the current selection rather than wherever the view was\n * left. Done here rather than in an effect: it is a response to an event, and\n * setting state from an effect is what `react-hooks/set-state-in-effect`\n * rejects — correctly, since it costs an extra render every open.\n */\n function openPicker() {\n setViewMonth(startOfMonth(fromIsoDate(value.from) ?? new Date()));\n setPendingFrom(null);\n setHovered(null);\n setOpen(true);\n }\n\n const anchor = pendingFrom ?? (to ? null : from);\n const previewEnd = pendingFrom ? hovered : null;\n\n function rangeBounds(): [Date | null, Date | null] {\n if (pendingFrom) {\n if (!previewEnd) return [pendingFrom, pendingFrom];\n return previewEnd.getTime() < pendingFrom.getTime()\n ? [previewEnd, pendingFrom]\n : [pendingFrom, previewEnd];\n }\n return [from, to];\n }\n\n const [rangeStart, rangeEnd] = rangeBounds();\n\n function selectDay(day: Date) {\n if (!anchor) {\n // Starting a new range. Keeping only `from` means the next click closes\n // it, whichever side of the anchor it lands on.\n setPendingFrom(day);\n onChange({ from: toIsoDate(day), to: \"\" });\n return;\n }\n const [start, end] =\n day.getTime() < anchor.getTime() ? [day, anchor] : [anchor, day];\n onChange({ from: toIsoDate(start), to: toIsoDate(end) });\n setPendingFrom(null);\n setHovered(null);\n setOpen(false);\n }\n\n /** Arrow keys move by day/week across both months, as a calendar should. */\n function onGridKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {\n const offsets: Record<string, number> = {\n ArrowLeft: -1,\n ArrowRight: 1,\n ArrowUp: -7,\n ArrowDown: 7,\n };\n const offset = offsets[event.key];\n if (offset === undefined) return;\n const focused = document.activeElement as HTMLElement | null;\n const iso = focused?.dataset?.day;\n const current = fromIsoDate(iso);\n if (!current) return;\n event.preventDefault();\n const next = addDays(current, offset);\n const nextIso = toIsoDate(next);\n // Page the view when the move lands outside what is rendered.\n if (!rootRef.current?.querySelector(`[data-day=\"${nextIso}\"]`)) {\n setViewMonth(startOfMonth(offset < 0 ? next : addMonths(next, -1)));\n }\n window.requestAnimationFrame(() => {\n rootRef.current\n ?.querySelector<HTMLElement>(`[data-day=\"${nextIso}\"]`)\n ?.focus();\n });\n }\n\n const label =\n value.from || value.to\n ? `${formatDisplay(value.from) || \"Any\"} → ${formatDisplay(value.to) || \"Any\"}`\n : placeholder;\n\n const today = new Date();\n\n function renderMonth(month: Date) {\n return (\n <div key={monthLabel(month)} className=\"w-[15.75rem] shrink-0\">\n <div className=\"text-foreground mb-2 text-center text-sm font-medium whitespace-nowrap\">\n {monthLabel(month)}\n </div>\n <div className=\"text-muted-foreground grid grid-cols-7 text-center text-[11px]\">\n {WEEKDAYS.map((day) => (\n <div key={day} className=\"grid size-9 place-items-center\">\n {day}\n </div>\n ))}\n </div>\n <div className=\"grid grid-cols-7\">\n {monthGrid(month).map((day) => {\n const iso = toIsoDate(day);\n const outside = day.getMonth() !== month.getMonth();\n const isStart = isSameDay(day, rangeStart);\n const isEnd = isSameDay(day, rangeEnd);\n const inRange =\n rangeStart != null &&\n rangeEnd != null &&\n day.getTime() > rangeStart.getTime() &&\n day.getTime() < rangeEnd.getTime();\n const isToday = isSameDay(day, today);\n\n return (\n <button\n key={iso}\n type=\"button\"\n data-day={iso}\n // Outside days stay clickable — clicking one is how you reach\n // the neighbouring month without paging.\n onClick={() => selectDay(day)}\n onMouseEnter={() => setHovered(day)}\n aria-label={day.toDateString()}\n aria-pressed={isStart || isEnd}\n className={cn(\n \"focus-visible:ring-ring relative grid size-9 place-items-center rounded-md text-sm transition-colors focus-visible:ring-2 focus-visible:outline-none\",\n outside ? \"text-muted-foreground/50\" : \"text-foreground\",\n inRange && \"bg-primary/10 rounded-none\",\n (isStart || isEnd) &&\n \"bg-primary text-primary-foreground font-medium\",\n // Square off the inner edge so a range reads as one bar\n // rather than two pills with a gap.\n isStart && rangeEnd && !isEnd && \"rounded-r-none\",\n isEnd && rangeStart && !isStart && \"rounded-l-none\",\n !isStart && !isEnd && \"hover:bg-muted\",\n isToday &&\n !isStart &&\n !isEnd &&\n \"ring-border font-semibold ring-1\",\n )}\n >\n {day.getDate()}\n </button>\n );\n })}\n </div>\n </div>\n );\n }\n\n return (\n <div ref={rootRef} className={cn(\"relative\", className)}>\n <div\n className={cn(\n \"border-border bg-background flex items-center rounded-md border\",\n \"focus-within:ring-ring/50 focus-within:border-ring focus-within:ring-2\",\n disabled && \"opacity-50\",\n )}\n >\n <button\n type=\"button\"\n disabled={disabled}\n aria-haspopup=\"dialog\"\n aria-expanded={open}\n aria-label={ariaLabel}\n onClick={() => (open ? setOpen(false) : openPicker())}\n className=\"flex min-w-0 flex-1 items-center gap-2 px-2 py-1.5 text-left text-sm\"\n >\n <CalendarDays\n className=\"text-muted-foreground size-4 shrink-0\"\n aria-hidden\n />\n <span\n className={cn(\n \"truncate\",\n !value.from && !value.to && \"text-muted-foreground\",\n )}\n >\n {label}\n </span>\n </button>\n {(value.from || value.to) && !disabled ? (\n <button\n type=\"button\"\n aria-label=\"Clear date range\"\n onClick={() => {\n onChange({ from: \"\", to: \"\" });\n setPendingFrom(null);\n }}\n className=\"text-muted-foreground hover:text-foreground shrink-0 px-2\"\n >\n <X className=\"size-3.5\" aria-hidden />\n </button>\n ) : null}\n </div>\n\n {open ? (\n <div\n role=\"dialog\"\n aria-label={ariaLabel}\n // `w-max` so the calendar sets its own width instead of inheriting\n // the filter column, which squeezed the months until the numbers ran\n // together. Anchored right because this sits in the last column and\n // would otherwise open off the edge of the viewport.\n className=\"border-border bg-background absolute top-full right-0 z-50 mt-1 flex w-max rounded-lg border p-3 shadow-lg\"\n >\n <div className=\"border-border mr-3 flex w-32 shrink-0 flex-col gap-0.5 border-r pr-3\">\n {PRESETS.map((preset) => (\n <button\n key={preset.label}\n type=\"button\"\n className=\"hover:bg-muted text-foreground rounded-md px-2 py-1.5 text-left text-xs\"\n onClick={() => {\n onChange(preset.range());\n setPendingFrom(null);\n setOpen(false);\n }}\n >\n {preset.label}\n </button>\n ))}\n </div>\n\n <div onKeyDown={onGridKeyDown}>\n <div className=\"mb-2 flex items-center justify-between\">\n <button\n type=\"button\"\n aria-label=\"Previous month\"\n className=\"hover:bg-muted text-muted-foreground hover:text-foreground rounded-md p-1.5\"\n onClick={() => setViewMonth(addMonths(viewMonth, -1))}\n >\n <ChevronLeft className=\"size-4\" aria-hidden />\n </button>\n <button\n type=\"button\"\n aria-label=\"Next month\"\n className=\"hover:bg-muted text-muted-foreground hover:text-foreground rounded-md p-1.5\"\n onClick={() => setViewMonth(addMonths(viewMonth, 1))}\n >\n <ChevronRight className=\"size-4\" aria-hidden />\n </button>\n </div>\n <div className=\"flex gap-5\" onMouseLeave={() => setHovered(null)}>\n {renderMonth(viewMonth)}\n {renderMonth(addMonths(viewMonth, 1))}\n </div>\n </div>\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,29 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-HCA5ODCD.js";
4
+
5
+ // src/separator.tsx
6
+ import * as React from "react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ var Separator = React.forwardRef(
9
+ ({ className, orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ jsx(
10
+ "div",
11
+ {
12
+ ref,
13
+ role: decorative ? "none" : "separator",
14
+ "aria-orientation": orientation,
15
+ className: cn(
16
+ "bg-border shrink-0",
17
+ orientation === "horizontal" ? "h-px w-full" : "h-full w-px",
18
+ className
19
+ ),
20
+ ...props
21
+ }
22
+ )
23
+ );
24
+ Separator.displayName = "Separator";
25
+
26
+ export {
27
+ Separator
28
+ };
29
+ //# sourceMappingURL=chunk-T4WCCHJQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/separator.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"./utils\";\n\nexport type SeparatorProps = React.HTMLAttributes<HTMLDivElement> & {\n orientation?: \"horizontal\" | \"vertical\";\n decorative?: boolean;\n};\n\n/** Thin rule between sections. */\nexport const Separator = React.forwardRef<HTMLDivElement, SeparatorProps>(\n (\n { className, orientation = \"horizontal\", decorative = true, ...props },\n ref,\n ) => (\n <div\n ref={ref}\n role={decorative ? \"none\" : \"separator\"}\n aria-orientation={orientation}\n className={cn(\n \"bg-border shrink-0\",\n orientation === \"horizontal\" ? \"h-px w-full\" : \"h-full w-px\",\n className,\n )}\n {...props}\n />\n ),\n);\n\nSeparator.displayName = \"Separator\";\n"],"mappings":";;;;;AAEA,YAAY,WAAW;AAenB;AALG,IAAM,YAAkB;AAAA,EAC7B,CACE,EAAE,WAAW,cAAc,cAAc,aAAa,MAAM,GAAG,MAAM,GACrE,QAEA;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,MAAM,aAAa,SAAS;AAAA,MAC5B,oBAAkB;AAAA,MAClB,WAAW;AAAA,QACT;AAAA,QACA,gBAAgB,eAAe,gBAAgB;AAAA,QAC/C;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,UAAU,cAAc;","names":[]}
@@ -0,0 +1,93 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-HCA5ODCD.js";
4
+
5
+ // src/table.tsx
6
+ import * as React from "react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ var Table = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { className: "relative w-full overflow-auto", children: /* @__PURE__ */ jsx(
9
+ "table",
10
+ {
11
+ ref,
12
+ className: cn("w-full caption-bottom text-sm", className),
13
+ ...props
14
+ }
15
+ ) }));
16
+ Table.displayName = "Table";
17
+ var TableHeader = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("thead", { ref, className: cn("[&_tr]:border-b", className), ...props }));
18
+ TableHeader.displayName = "TableHeader";
19
+ var TableBody = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
20
+ "tbody",
21
+ {
22
+ ref,
23
+ className: cn("[&_tr:last-child]:border-0", className),
24
+ ...props
25
+ }
26
+ ));
27
+ TableBody.displayName = "TableBody";
28
+ var TableFooter = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
29
+ "tfoot",
30
+ {
31
+ ref,
32
+ className: cn(
33
+ "bg-muted/50 border-t font-medium [&>tr]:last:border-b-0",
34
+ className
35
+ ),
36
+ ...props
37
+ }
38
+ ));
39
+ TableFooter.displayName = "TableFooter";
40
+ var TableRow = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
41
+ "tr",
42
+ {
43
+ ref,
44
+ className: cn(
45
+ "hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
46
+ className
47
+ ),
48
+ ...props
49
+ }
50
+ ));
51
+ TableRow.displayName = "TableRow";
52
+ var TableHead = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
53
+ "th",
54
+ {
55
+ ref,
56
+ className: cn(
57
+ "text-muted-foreground h-12 px-4 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0",
58
+ className
59
+ ),
60
+ ...props
61
+ }
62
+ ));
63
+ TableHead.displayName = "TableHead";
64
+ var TableCell = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
65
+ "td",
66
+ {
67
+ ref,
68
+ className: cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className),
69
+ ...props
70
+ }
71
+ ));
72
+ TableCell.displayName = "TableCell";
73
+ var TableCaption = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
74
+ "caption",
75
+ {
76
+ ref,
77
+ className: cn("text-muted-foreground mt-4 text-sm", className),
78
+ ...props
79
+ }
80
+ ));
81
+ TableCaption.displayName = "TableCaption";
82
+
83
+ export {
84
+ Table,
85
+ TableHeader,
86
+ TableBody,
87
+ TableFooter,
88
+ TableRow,
89
+ TableHead,
90
+ TableCell,
91
+ TableCaption
92
+ };
93
+ //# sourceMappingURL=chunk-UC3ZIQPE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/table.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport { cn } from \"./utils\";\n\n/**\n * Low-level table primitives. Prefer `DataTable` for list panels; use these\n * when the row markup is custom (nested actions, expandable rows, etc.).\n */\nexport const Table = React.forwardRef<\n HTMLTableElement,\n React.HTMLAttributes<HTMLTableElement>\n>(({ className, ...props }, ref) => (\n <div className=\"relative w-full overflow-auto\">\n <table\n ref={ref}\n className={cn(\"w-full caption-bottom text-sm\", className)}\n {...props}\n />\n </div>\n));\nTable.displayName = \"Table\";\n\nexport const TableHeader = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <thead ref={ref} className={cn(\"[&_tr]:border-b\", className)} {...props} />\n));\nTableHeader.displayName = \"TableHeader\";\n\nexport const TableBody = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <tbody\n ref={ref}\n className={cn(\"[&_tr:last-child]:border-0\", className)}\n {...props}\n />\n));\nTableBody.displayName = \"TableBody\";\n\nexport const TableFooter = React.forwardRef<\n HTMLTableSectionElement,\n React.HTMLAttributes<HTMLTableSectionElement>\n>(({ className, ...props }, ref) => (\n <tfoot\n ref={ref}\n className={cn(\n \"bg-muted/50 border-t font-medium [&>tr]:last:border-b-0\",\n className,\n )}\n {...props}\n />\n));\nTableFooter.displayName = \"TableFooter\";\n\nexport const TableRow = React.forwardRef<\n HTMLTableRowElement,\n React.HTMLAttributes<HTMLTableRowElement>\n>(({ className, ...props }, ref) => (\n <tr\n ref={ref}\n className={cn(\n \"hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors\",\n className,\n )}\n {...props}\n />\n));\nTableRow.displayName = \"TableRow\";\n\nexport const TableHead = React.forwardRef<\n HTMLTableCellElement,\n React.ThHTMLAttributes<HTMLTableCellElement>\n>(({ className, ...props }, ref) => (\n <th\n ref={ref}\n className={cn(\n \"text-muted-foreground h-12 px-4 text-left align-middle font-medium [&:has([role=checkbox])]:pr-0\",\n className,\n )}\n {...props}\n />\n));\nTableHead.displayName = \"TableHead\";\n\nexport const TableCell = React.forwardRef<\n HTMLTableCellElement,\n React.TdHTMLAttributes<HTMLTableCellElement>\n>(({ className, ...props }, ref) => (\n <td\n ref={ref}\n className={cn(\"p-4 align-middle [&:has([role=checkbox])]:pr-0\", className)}\n {...props}\n />\n));\nTableCell.displayName = \"TableCell\";\n\nexport const TableCaption = React.forwardRef<\n HTMLTableCaptionElement,\n React.HTMLAttributes<HTMLTableCaptionElement>\n>(({ className, ...props }, ref) => (\n <caption\n ref={ref}\n className={cn(\"text-muted-foreground mt-4 text-sm\", className)}\n {...props}\n />\n));\nTableCaption.displayName = \"TableCaption\";\n"],"mappings":";;;;;AAAA,YAAY,WAAW;AAanB;AALG,IAAM,QAAc,iBAGzB,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAC1B,oBAAC,SAAI,WAAU,iCACb;AAAA,EAAC;AAAA;AAAA,IACC;AAAA,IACA,WAAW,GAAG,iCAAiC,SAAS;AAAA,IACvD,GAAG;AAAA;AACN,GACF,CACD;AACD,MAAM,cAAc;AAEb,IAAM,cAAoB,iBAG/B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAC1B,oBAAC,WAAM,KAAU,WAAW,GAAG,mBAAmB,SAAS,GAAI,GAAG,OAAO,CAC1E;AACD,YAAY,cAAc;AAEnB,IAAM,YAAkB,iBAG7B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAC1B;AAAA,EAAC;AAAA;AAAA,IACC;AAAA,IACA,WAAW,GAAG,8BAA8B,SAAS;AAAA,IACpD,GAAG;AAAA;AACN,CACD;AACD,UAAU,cAAc;AAEjB,IAAM,cAAoB,iBAG/B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAC1B;AAAA,EAAC;AAAA;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACC,GAAG;AAAA;AACN,CACD;AACD,YAAY,cAAc;AAEnB,IAAM,WAAiB,iBAG5B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAC1B;AAAA,EAAC;AAAA;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACC,GAAG;AAAA;AACN,CACD;AACD,SAAS,cAAc;AAEhB,IAAM,YAAkB,iBAG7B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAC1B;AAAA,EAAC;AAAA;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IACF;AAAA,IACC,GAAG;AAAA;AACN,CACD;AACD,UAAU,cAAc;AAEjB,IAAM,YAAkB,iBAG7B,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAC1B;AAAA,EAAC;AAAA;AAAA,IACC;AAAA,IACA,WAAW,GAAG,kDAAkD,SAAS;AAAA,IACxE,GAAG;AAAA;AACN,CACD;AACD,UAAU,cAAc;AAEjB,IAAM,eAAqB,iBAGhC,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QAC1B;AAAA,EAAC;AAAA;AAAA,IACC;AAAA,IACA,WAAW,GAAG,sCAAsC,SAAS;AAAA,IAC5D,GAAG;AAAA;AACN,CACD;AACD,aAAa,cAAc;","names":[]}
@@ -0,0 +1,28 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-HCA5ODCD.js";
4
+
5
+ // src/spinner.tsx
6
+ import { Loader2 } from "lucide-react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ var SIZES = {
9
+ sm: "size-3.5",
10
+ md: "size-4",
11
+ lg: "size-5",
12
+ xl: "size-10 border-2"
13
+ };
14
+ function Spinner({ size = "md", className, ...props }) {
15
+ return /* @__PURE__ */ jsx(
16
+ Loader2,
17
+ {
18
+ "aria-hidden": true,
19
+ className: cn("animate-spin", SIZES[size], className),
20
+ ...props
21
+ }
22
+ );
23
+ }
24
+
25
+ export {
26
+ Spinner
27
+ };
28
+ //# sourceMappingURL=chunk-UE7JY7VR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/spinner.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { Loader2 } from \"lucide-react\";\n\nimport { cn } from \"./utils\";\n\n/**\n * The one spinner.\n *\n * There were five spellings of `animate-spin` across twelve files — `h-4 w-4`,\n * `h-5 w-5`, `size-4`, bare, and a hand-drawn ring in `ProtectedRoute` — so the\n * same \"working\" signal came out a different size depending on which panel you\n * were on.\n *\n * It is `aria-hidden` on purpose. A spinner is decoration; what a screen reader\n * needs is the surrounding control's `aria-busy` or the panel's status text,\n * and announcing \"loading\" twice is worse than once. `Button` and `StatePanel`\n * both supply that.\n */\nconst SIZES = {\n sm: \"size-3.5\",\n md: \"size-4\",\n lg: \"size-5\",\n xl: \"size-10 border-2\",\n} as const;\n\nexport interface SpinnerProps extends React.SVGProps<SVGSVGElement> {\n size?: keyof typeof SIZES;\n}\n\nexport function Spinner({ size = \"md\", className, ...props }: SpinnerProps) {\n return (\n <Loader2\n aria-hidden\n className={cn(\"animate-spin\", SIZES[size], className)}\n {...props}\n />\n );\n}\n"],"mappings":";;;;;AACA,SAAS,eAAe;AA8BpB;AAbJ,IAAM,QAAQ;AAAA,EACZ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN;AAMO,SAAS,QAAQ,EAAE,OAAO,MAAM,WAAW,GAAG,MAAM,GAAiB;AAC1E,SACE;AAAA,IAAC;AAAA;AAAA,MACC,eAAW;AAAA,MACX,WAAW,GAAG,gBAAgB,MAAM,IAAI,GAAG,SAAS;AAAA,MACnD,GAAG;AAAA;AAAA,EACN;AAEJ;","names":[]}
@@ -0,0 +1,93 @@
1
+ // src/clipboard.ts
2
+ var TSV_DELIMITER = " ";
3
+ var CSV_DELIMITER = ",";
4
+ function normalizeRange(anchor, focus) {
5
+ if (!anchor || !focus) return null;
6
+ return {
7
+ r1: Math.min(anchor.r, focus.r),
8
+ r2: Math.max(anchor.r, focus.r),
9
+ c1: Math.min(anchor.c, focus.c),
10
+ c2: Math.max(anchor.c, focus.c)
11
+ };
12
+ }
13
+ function escapeCell(raw, delimiter) {
14
+ const value = raw == null ? "" : String(raw);
15
+ if (value.includes(delimiter) || value.includes("\n") || value.includes("\r") || value.includes('"')) {
16
+ return `"${value.replace(/"/g, '""')}"`;
17
+ }
18
+ return value;
19
+ }
20
+ function buildRangeText(range, columns, rows, delimiter, includeHeaders) {
21
+ if (!range) return "";
22
+ const lines = [];
23
+ if (includeHeaders) {
24
+ const header = [];
25
+ for (let c = range.c1; c <= range.c2; c++) {
26
+ header.push(escapeCell(columns[c]?.title, delimiter));
27
+ }
28
+ lines.push(header.join(delimiter));
29
+ }
30
+ for (let r = range.r1; r <= range.r2; r++) {
31
+ const line = [];
32
+ const row = rows[r];
33
+ for (let c = range.c1; c <= range.c2; c++) {
34
+ const column = columns[c];
35
+ line.push(
36
+ escapeCell(
37
+ column && row !== void 0 ? column.value(row) : "",
38
+ delimiter
39
+ )
40
+ );
41
+ }
42
+ lines.push(line.join(delimiter));
43
+ }
44
+ return lines.join("\n");
45
+ }
46
+ function fullRange(columns, rows) {
47
+ if (!rows.length || !columns.length) return null;
48
+ return { r1: 0, r2: rows.length - 1, c1: 0, c2: columns.length - 1 };
49
+ }
50
+ function describeRange(range) {
51
+ if (!range) return "No selection";
52
+ const rowCount = range.r2 - range.r1 + 1;
53
+ const colCount = range.c2 - range.c1 + 1;
54
+ if (rowCount === 1 && colCount === 1) return "1 cell selected";
55
+ return `${rowCount} \xD7 ${colCount} selected`;
56
+ }
57
+ function countCells(range) {
58
+ return {
59
+ rows: range.r2 - range.r1 + 1,
60
+ columns: range.c2 - range.c1 + 1
61
+ };
62
+ }
63
+ async function writeToClipboard(text) {
64
+ if (navigator.clipboard?.writeText) {
65
+ return navigator.clipboard.writeText(text);
66
+ }
67
+ const textarea = document.createElement("textarea");
68
+ textarea.value = text;
69
+ textarea.style.position = "fixed";
70
+ textarea.style.top = "-1000px";
71
+ document.body.appendChild(textarea);
72
+ textarea.select();
73
+ try {
74
+ if (!document.execCommand("copy")) {
75
+ throw new Error("Could not write to the clipboard");
76
+ }
77
+ } finally {
78
+ document.body.removeChild(textarea);
79
+ }
80
+ }
81
+
82
+ export {
83
+ TSV_DELIMITER,
84
+ CSV_DELIMITER,
85
+ normalizeRange,
86
+ escapeCell,
87
+ buildRangeText,
88
+ fullRange,
89
+ describeRange,
90
+ countCells,
91
+ writeToClipboard
92
+ };
93
+ //# sourceMappingURL=chunk-Y5WCT6XN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/clipboard.ts"],"sourcesContent":["/**\n * Range maths and clipboard text for the card table's spreadsheet selection.\n *\n * Ported from video-shorts-panel `app/src/common/components/ExcelTable.jsx`.\n * Kept free of React and the DOM (except `writeToClipboard`) so the escaping\n * and range rules are unit-testable.\n */\n\nexport type CellRef = { r: number; c: number };\n\nexport type CellRange = { r1: number; r2: number; c1: number; c2: number };\n\n/** Tab for the clipboard (Excel/Sheets split on it), comma for a .csv file. */\nexport const TSV_DELIMITER = \"\\t\";\nexport const CSV_DELIMITER = \",\";\n\n/**\n * Anchor and focus can be dragged in any direction, so the range is whichever\n * corners they happen to be — normalize to top-left / bottom-right.\n */\nexport function normalizeRange(\n anchor: CellRef | null | undefined,\n focus: CellRef | null | undefined,\n): CellRange | null {\n if (!anchor || !focus) return null;\n return {\n r1: Math.min(anchor.r, focus.r),\n r2: Math.max(anchor.r, focus.r),\n c1: Math.min(anchor.c, focus.c),\n c2: Math.max(anchor.c, focus.c),\n };\n}\n\n/**\n * A cell needs quoting once it carries the delimiters themselves — the same\n * rule Excel uses when it parses the clipboard. Without it, a title containing\n * a tab or a newline silently becomes extra columns or rows on paste.\n */\nexport function escapeCell(raw: unknown, delimiter: string): string {\n const value = raw == null ? \"\" : String(raw);\n if (\n value.includes(delimiter) ||\n value.includes(\"\\n\") ||\n value.includes(\"\\r\") ||\n value.includes('\"')\n ) {\n return `\"${value.replace(/\"/g, '\"\"')}\"`;\n }\n return value;\n}\n\nexport type TextColumn<Row> = {\n title: string;\n value: (row: Row) => string;\n};\n\n/** Renders a range as delimited text, optionally with a header line. */\nexport function buildRangeText<Row>(\n range: CellRange | null,\n columns: TextColumn<Row>[],\n rows: Row[],\n delimiter: string,\n includeHeaders: boolean,\n): string {\n if (!range) return \"\";\n const lines: string[] = [];\n\n if (includeHeaders) {\n const header: string[] = [];\n for (let c = range.c1; c <= range.c2; c++) {\n header.push(escapeCell(columns[c]?.title, delimiter));\n }\n lines.push(header.join(delimiter));\n }\n\n for (let r = range.r1; r <= range.r2; r++) {\n const line: string[] = [];\n const row = rows[r];\n for (let c = range.c1; c <= range.c2; c++) {\n const column = columns[c];\n line.push(\n escapeCell(\n column && row !== undefined ? column.value(row) : \"\",\n delimiter,\n ),\n );\n }\n lines.push(line.join(delimiter));\n }\n\n return lines.join(\"\\n\");\n}\n\n/** The whole grid, or null when there is nothing to select. */\nexport function fullRange<Row>(\n columns: TextColumn<Row>[],\n rows: Row[],\n): CellRange | null {\n if (!rows.length || !columns.length) return null;\n return { r1: 0, r2: rows.length - 1, c1: 0, c2: columns.length - 1 };\n}\n\n/** \"3 × 2 selected\" — what the toolbar reports back to the user. */\nexport function describeRange(range: CellRange | null): string {\n if (!range) return \"No selection\";\n const rowCount = range.r2 - range.r1 + 1;\n const colCount = range.c2 - range.c1 + 1;\n if (rowCount === 1 && colCount === 1) return \"1 cell selected\";\n return `${rowCount} × ${colCount} selected`;\n}\n\nexport function countCells(range: CellRange): {\n rows: number;\n columns: number;\n} {\n return {\n rows: range.r2 - range.r1 + 1,\n columns: range.c2 - range.c1 + 1,\n };\n}\n\n/**\n * `navigator.clipboard` is unavailable on http origins and older browsers, so\n * fall back to a hidden textarea + execCommand rather than failing the copy.\n */\nexport async function writeToClipboard(text: string): Promise<void> {\n if (navigator.clipboard?.writeText) {\n return navigator.clipboard.writeText(text);\n }\n\n const textarea = document.createElement(\"textarea\");\n textarea.value = text;\n textarea.style.position = \"fixed\";\n textarea.style.top = \"-1000px\";\n document.body.appendChild(textarea);\n textarea.select();\n try {\n if (!document.execCommand(\"copy\")) {\n throw new Error(\"Could not write to the clipboard\");\n }\n } finally {\n document.body.removeChild(textarea);\n }\n}\n"],"mappings":";AAaO,IAAM,gBAAgB;AACtB,IAAM,gBAAgB;AAMtB,SAAS,eACd,QACA,OACkB;AAClB,MAAI,CAAC,UAAU,CAAC,MAAO,QAAO;AAC9B,SAAO;AAAA,IACL,IAAI,KAAK,IAAI,OAAO,GAAG,MAAM,CAAC;AAAA,IAC9B,IAAI,KAAK,IAAI,OAAO,GAAG,MAAM,CAAC;AAAA,IAC9B,IAAI,KAAK,IAAI,OAAO,GAAG,MAAM,CAAC;AAAA,IAC9B,IAAI,KAAK,IAAI,OAAO,GAAG,MAAM,CAAC;AAAA,EAChC;AACF;AAOO,SAAS,WAAW,KAAc,WAA2B;AAClE,QAAM,QAAQ,OAAO,OAAO,KAAK,OAAO,GAAG;AAC3C,MACE,MAAM,SAAS,SAAS,KACxB,MAAM,SAAS,IAAI,KACnB,MAAM,SAAS,IAAI,KACnB,MAAM,SAAS,GAAG,GAClB;AACA,WAAO,IAAI,MAAM,QAAQ,MAAM,IAAI,CAAC;AAAA,EACtC;AACA,SAAO;AACT;AAQO,SAAS,eACd,OACA,SACA,MACA,WACA,gBACQ;AACR,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,QAAkB,CAAC;AAEzB,MAAI,gBAAgB;AAClB,UAAM,SAAmB,CAAC;AAC1B,aAAS,IAAI,MAAM,IAAI,KAAK,MAAM,IAAI,KAAK;AACzC,aAAO,KAAK,WAAW,QAAQ,CAAC,GAAG,OAAO,SAAS,CAAC;AAAA,IACtD;AACA,UAAM,KAAK,OAAO,KAAK,SAAS,CAAC;AAAA,EACnC;AAEA,WAAS,IAAI,MAAM,IAAI,KAAK,MAAM,IAAI,KAAK;AACzC,UAAM,OAAiB,CAAC;AACxB,UAAM,MAAM,KAAK,CAAC;AAClB,aAAS,IAAI,MAAM,IAAI,KAAK,MAAM,IAAI,KAAK;AACzC,YAAM,SAAS,QAAQ,CAAC;AACxB,WAAK;AAAA,QACH;AAAA,UACE,UAAU,QAAQ,SAAY,OAAO,MAAM,GAAG,IAAI;AAAA,UAClD;AAAA,QACF;AAAA,MACF;AAAA,IACF;AACA,UAAM,KAAK,KAAK,KAAK,SAAS,CAAC;AAAA,EACjC;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAGO,SAAS,UACd,SACA,MACkB;AAClB,MAAI,CAAC,KAAK,UAAU,CAAC,QAAQ,OAAQ,QAAO;AAC5C,SAAO,EAAE,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,IAAI,GAAG,IAAI,QAAQ,SAAS,EAAE;AACrE;AAGO,SAAS,cAAc,OAAiC;AAC7D,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,WAAW,MAAM,KAAK,MAAM,KAAK;AACvC,QAAM,WAAW,MAAM,KAAK,MAAM,KAAK;AACvC,MAAI,aAAa,KAAK,aAAa,EAAG,QAAO;AAC7C,SAAO,GAAG,QAAQ,SAAM,QAAQ;AAClC;AAEO,SAAS,WAAW,OAGzB;AACA,SAAO;AAAA,IACL,MAAM,MAAM,KAAK,MAAM,KAAK;AAAA,IAC5B,SAAS,MAAM,KAAK,MAAM,KAAK;AAAA,EACjC;AACF;AAMA,eAAsB,iBAAiB,MAA6B;AAClE,MAAI,UAAU,WAAW,WAAW;AAClC,WAAO,UAAU,UAAU,UAAU,IAAI;AAAA,EAC3C;AAEA,QAAM,WAAW,SAAS,cAAc,UAAU;AAClD,WAAS,QAAQ;AACjB,WAAS,MAAM,WAAW;AAC1B,WAAS,MAAM,MAAM;AACrB,WAAS,KAAK,YAAY,QAAQ;AAClC,WAAS,OAAO;AAChB,MAAI;AACF,QAAI,CAAC,SAAS,YAAY,MAAM,GAAG;AACjC,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AAAA,EACF,UAAE;AACA,aAAS,KAAK,YAAY,QAAQ;AAAA,EACpC;AACF;","names":[]}