@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,30 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-HCA5ODCD.js";
4
+
5
+ // src/panel-brand-mark.tsx
6
+ import { jsx } from "react/jsx-runtime";
7
+ function PanelBrandMark({
8
+ children,
9
+ size = "md",
10
+ className,
11
+ ...props
12
+ }) {
13
+ return /* @__PURE__ */ jsx(
14
+ "div",
15
+ {
16
+ className: cn(
17
+ "bg-primary text-primary-foreground grid shrink-0 place-items-center rounded-lg",
18
+ size === "sm" ? "h-9 w-9" : "h-10 w-10",
19
+ className
20
+ ),
21
+ ...props,
22
+ children
23
+ }
24
+ );
25
+ }
26
+
27
+ export {
28
+ PanelBrandMark
29
+ };
30
+ //# sourceMappingURL=chunk-JDE5XYPT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/panel-brand-mark.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport { cn } from \"./utils\";\n\nexport type PanelBrandMarkProps = React.HTMLAttributes<HTMLDivElement> & {\n /** Icon inside the mark (lucide, etc.). */\n children: React.ReactNode;\n size?: \"sm\" | \"md\";\n};\n\n/**\n * Quiet brand tile for sidebar / sign-in — solid primary, no decorative gradients.\n */\nexport function PanelBrandMark({\n children,\n size = \"md\",\n className,\n ...props\n}: PanelBrandMarkProps) {\n return (\n <div\n className={cn(\n \"bg-primary text-primary-foreground grid shrink-0 place-items-center rounded-lg\",\n size === \"sm\" ? \"h-9 w-9\" : \"h-10 w-10\",\n className,\n )}\n {...props}\n >\n {children}\n </div>\n );\n}\n"],"mappings":";;;;;AAoBI;AAPG,SAAS,eAAe;AAAA,EAC7B;AAAA,EACA,OAAO;AAAA,EACP;AAAA,EACA,GAAG;AACL,GAAwB;AACtB,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA,SAAS,OAAO,YAAY;AAAA,QAC5B;AAAA,MACF;AAAA,MACC,GAAG;AAAA,MAEH;AAAA;AAAA,EACH;AAEJ;","names":[]}
@@ -0,0 +1,59 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-HCA5ODCD.js";
4
+
5
+ // src/breadcrumb.tsx
6
+ import { ChevronRight } from "lucide-react";
7
+ import { jsx, jsxs } from "react/jsx-runtime";
8
+ function Breadcrumb({ items, className }) {
9
+ return /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: cn("text-sm", className), children: /* @__PURE__ */ jsx("ol", { className: "flex flex-wrap items-center gap-1.5", children: items.map((item, index) => {
10
+ const isLast = index === items.length - 1;
11
+ const isInteractive = !isLast && (item.href || item.onClick);
12
+ return /* @__PURE__ */ jsxs(
13
+ "li",
14
+ {
15
+ className: "flex items-center gap-1.5",
16
+ children: [
17
+ index > 0 && /* @__PURE__ */ jsx(
18
+ ChevronRight,
19
+ {
20
+ className: "text-muted-foreground/70 size-3.5 shrink-0",
21
+ "aria-hidden": true
22
+ }
23
+ ),
24
+ isInteractive ? item.href ? /* @__PURE__ */ jsx(
25
+ "a",
26
+ {
27
+ href: item.href,
28
+ className: "text-muted-foreground hover:text-foreground transition",
29
+ children: item.label
30
+ }
31
+ ) : /* @__PURE__ */ jsx(
32
+ "button",
33
+ {
34
+ type: "button",
35
+ onClick: item.onClick,
36
+ className: "text-muted-foreground hover:text-foreground transition",
37
+ children: item.label
38
+ }
39
+ ) : /* @__PURE__ */ jsx(
40
+ "span",
41
+ {
42
+ className: cn(
43
+ isLast ? "text-foreground font-medium" : "text-muted-foreground"
44
+ ),
45
+ "aria-current": isLast ? "page" : void 0,
46
+ children: item.label
47
+ }
48
+ )
49
+ ]
50
+ },
51
+ `${item.label}-${index}`
52
+ );
53
+ }) }) });
54
+ }
55
+
56
+ export {
57
+ Breadcrumb
58
+ };
59
+ //# sourceMappingURL=chunk-JSJEEKR3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/breadcrumb.tsx"],"sourcesContent":["import { ChevronRight } from \"lucide-react\";\nimport * as React from \"react\";\n\nimport { cn } from \"./utils\";\n\nexport type BreadcrumbItem = {\n label: string;\n href?: string;\n onClick?: () => void;\n};\n\ninterface BreadcrumbProps {\n items: BreadcrumbItem[];\n className?: string;\n}\n\nexport function Breadcrumb({ items, className }: BreadcrumbProps) {\n return (\n <nav aria-label=\"Breadcrumb\" className={cn(\"text-sm\", className)}>\n <ol className=\"flex flex-wrap items-center gap-1.5\">\n {items.map((item, index) => {\n const isLast = index === items.length - 1;\n const isInteractive = !isLast && (item.href || item.onClick);\n\n return (\n <li\n key={`${item.label}-${index}`}\n className=\"flex items-center gap-1.5\"\n >\n {index > 0 && (\n <ChevronRight\n className=\"text-muted-foreground/70 size-3.5 shrink-0\"\n aria-hidden\n />\n )}\n {isInteractive ? (\n item.href ? (\n <a\n href={item.href}\n className=\"text-muted-foreground hover:text-foreground transition\"\n >\n {item.label}\n </a>\n ) : (\n <button\n type=\"button\"\n onClick={item.onClick}\n className=\"text-muted-foreground hover:text-foreground transition\"\n >\n {item.label}\n </button>\n )\n ) : (\n <span\n className={cn(\n isLast\n ? \"text-foreground font-medium\"\n : \"text-muted-foreground\",\n )}\n aria-current={isLast ? \"page\" : undefined}\n >\n {item.label}\n </span>\n )}\n </li>\n );\n })}\n </ol>\n </nav>\n );\n}\n"],"mappings":";;;;;AAAA,SAAS,oBAAoB;AAyBjB,SAKI,KALJ;AATL,SAAS,WAAW,EAAE,OAAO,UAAU,GAAoB;AAChE,SACE,oBAAC,SAAI,cAAW,cAAa,WAAW,GAAG,WAAW,SAAS,GAC7D,8BAAC,QAAG,WAAU,uCACX,gBAAM,IAAI,CAAC,MAAM,UAAU;AAC1B,UAAM,SAAS,UAAU,MAAM,SAAS;AACxC,UAAM,gBAAgB,CAAC,WAAW,KAAK,QAAQ,KAAK;AAEpD,WACE;AAAA,MAAC;AAAA;AAAA,QAEC,WAAU;AAAA,QAET;AAAA,kBAAQ,KACP;AAAA,YAAC;AAAA;AAAA,cACC,WAAU;AAAA,cACV,eAAW;AAAA;AAAA,UACb;AAAA,UAED,gBACC,KAAK,OACH;AAAA,YAAC;AAAA;AAAA,cACC,MAAM,KAAK;AAAA,cACX,WAAU;AAAA,cAET,eAAK;AAAA;AAAA,UACR,IAEA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,KAAK;AAAA,cACd,WAAU;AAAA,cAET,eAAK;AAAA;AAAA,UACR,IAGF;AAAA,YAAC;AAAA;AAAA,cACC,WAAW;AAAA,gBACT,SACI,gCACA;AAAA,cACN;AAAA,cACA,gBAAc,SAAS,SAAS;AAAA,cAE/B,eAAK;AAAA;AAAA,UACR;AAAA;AAAA;AAAA,MApCG,GAAG,KAAK,KAAK,IAAI,KAAK;AAAA,IAsC7B;AAAA,EAEJ,CAAC,GACH,GACF;AAEJ;","names":[]}
@@ -0,0 +1,85 @@
1
+ import {
2
+ Spinner
3
+ } from "./chunk-UE7JY7VR.js";
4
+ import {
5
+ cn
6
+ } from "./chunk-HCA5ODCD.js";
7
+
8
+ // src/button.tsx
9
+ import * as React from "react";
10
+ import { cva } from "class-variance-authority";
11
+ import { Slot } from "@radix-ui/react-slot";
12
+ import { jsx, jsxs } from "react/jsx-runtime";
13
+ var buttonVariants = cva(
14
+ "inline-flex h-9 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md px-3 text-sm font-medium transition duration-200 active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
15
+ {
16
+ variants: {
17
+ variant: {
18
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
19
+ secondary: "bg-muted text-foreground hover:bg-muted/80",
20
+ ghost: "hover:bg-muted",
21
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
22
+ outline: "border border-border bg-background hover:bg-muted"
23
+ },
24
+ size: {
25
+ sm: "h-8 px-2.5",
26
+ md: "h-9 px-3",
27
+ lg: "h-10 px-4",
28
+ icon: "h-9 w-9 px-0",
29
+ // Square, matching `sm`'s height — for an icon-only control sitting in
30
+ // a table row or beside `sm` buttons, where `icon` is a row taller.
31
+ "icon-sm": "h-8 w-8 px-0"
32
+ }
33
+ },
34
+ defaultVariants: {
35
+ variant: "default",
36
+ size: "md"
37
+ }
38
+ }
39
+ );
40
+ var ICON_SIZES = /* @__PURE__ */ new Set(["icon", "icon-sm"]);
41
+ var Button = React.forwardRef(
42
+ ({
43
+ className,
44
+ variant,
45
+ size,
46
+ asChild = false,
47
+ loading,
48
+ disabled,
49
+ children,
50
+ ...props
51
+ }, ref) => {
52
+ const Comp = asChild ? Slot : "button";
53
+ if (asChild) {
54
+ return /* @__PURE__ */ jsx(
55
+ Comp,
56
+ {
57
+ ref,
58
+ className: cn(buttonVariants({ variant, size }), className),
59
+ ...props,
60
+ children
61
+ }
62
+ );
63
+ }
64
+ return /* @__PURE__ */ jsxs(
65
+ "button",
66
+ {
67
+ ref,
68
+ disabled: disabled || loading,
69
+ "aria-busy": loading || void 0,
70
+ className: cn(buttonVariants({ variant, size }), className),
71
+ ...props,
72
+ children: [
73
+ loading ? /* @__PURE__ */ jsx(Spinner, { size: "sm" }) : null,
74
+ loading && size != null && ICON_SIZES.has(size) ? null : children
75
+ ]
76
+ }
77
+ );
78
+ }
79
+ );
80
+ Button.displayName = "Button";
81
+
82
+ export {
83
+ Button
84
+ };
85
+ //# sourceMappingURL=chunk-KR3BCFP7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/button.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport { Slot } from \"@radix-ui/react-slot\";\n\nimport { Spinner } from \"./spinner\";\nimport { cn } from \"./utils\";\n\nconst buttonVariants = cva(\n \"inline-flex h-9 cursor-pointer items-center justify-center gap-2 whitespace-nowrap rounded-md px-3 text-sm font-medium transition duration-200 active:scale-[0.98] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n variant: {\n default: \"bg-primary text-primary-foreground hover:bg-primary/90\",\n secondary: \"bg-muted text-foreground hover:bg-muted/80\",\n ghost: \"hover:bg-muted\",\n destructive:\n \"bg-destructive text-destructive-foreground hover:bg-destructive/90\",\n outline: \"border border-border bg-background hover:bg-muted\",\n },\n size: {\n sm: \"h-8 px-2.5\",\n md: \"h-9 px-3\",\n lg: \"h-10 px-4\",\n icon: \"h-9 w-9 px-0\",\n // Square, matching `sm`'s height — for an icon-only control sitting in\n // a table row or beside `sm` buttons, where `icon` is a row taller.\n \"icon-sm\": \"h-8 w-8 px-0\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"md\",\n },\n },\n);\n\nexport interface ButtonProps\n extends\n React.ButtonHTMLAttributes<HTMLButtonElement>,\n VariantProps<typeof buttonVariants> {\n asChild?: boolean;\n /**\n * Work is in flight: shows a spinner, disables the button, and sets\n * `aria-busy`.\n *\n * This exists because the panel had 272 disabled buttons and only 22 that\n * said anything about why — the rest went dead on click with no spinner and\n * no label change, which reads as a broken button rather than a busy one.\n * Making the feedback part of `disabled` rather than something each caller\n * remembers separately is the only way that ratio stays fixed.\n *\n * The label is kept rather than swapped. \"Saving…\" loses which button you\n * pressed, and the spinner already says it is working. An icon-only button\n * is the exception — there is no room beside the glyph, so the spinner takes\n * its place; the `aria-label` is what names the button either way.\n *\n * \"Icon-only\" means an icon `size`. A square button that grew a spinner\n * beside its glyph would not just look wrong, it would resize mid-request\n * and shove whatever sits next to it sideways.\n */\n loading?: boolean;\n}\n\n/** Sizes with no room for a spinner beside the glyph. */\nconst ICON_SIZES = new Set<string>([\"icon\", \"icon-sm\"]);\n\nexport const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(\n (\n {\n className,\n variant,\n size,\n asChild = false,\n loading,\n disabled,\n children,\n ...props\n },\n ref,\n ) => {\n const Comp = asChild ? Slot : \"button\";\n\n // `asChild` renders someone else's element, which may not take a spinner or\n // a disabled attribute — so the busy treatment is only applied to a real\n // button, and passing both is a mistake worth not papering over.\n if (asChild) {\n return (\n <Comp\n ref={ref}\n className={cn(buttonVariants({ variant, size }), className)}\n {...props}\n >\n {children}\n </Comp>\n );\n }\n\n return (\n <button\n ref={ref}\n disabled={disabled || loading}\n aria-busy={loading || undefined}\n className={cn(buttonVariants({ variant, size }), className)}\n {...props}\n >\n {loading ? <Spinner size=\"sm\" /> : null}\n {loading && size != null && ICON_SIZES.has(size) ? null : children}\n </button>\n );\n },\n);\n\nButton.displayName = \"Button\";\n"],"mappings":";;;;;;;;AAAA,YAAY,WAAW;AACvB,SAAS,WAA8B;AACvC,SAAS,YAAY;AAqFb,cAWF,YAXE;AAhFR,IAAM,iBAAiB;AAAA,EACrB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,WAAW;AAAA,QACX,OAAO;AAAA,QACP,aACE;AAAA,QACF,SAAS;AAAA,MACX;AAAA,MACA,MAAM;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,IAAI;AAAA,QACJ,MAAM;AAAA;AAAA;AAAA,QAGN,WAAW;AAAA,MACb;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AAAA,EACF;AACF;AA8BA,IAAM,aAAa,oBAAI,IAAY,CAAC,QAAQ,SAAS,CAAC;AAE/C,IAAM,SAAe;AAAA,EAC1B,CACE;AAAA,IACE;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,GACA,QACG;AACH,UAAM,OAAO,UAAU,OAAO;AAK9B,QAAI,SAAS;AACX,aACE;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA,WAAW,GAAG,eAAe,EAAE,SAAS,KAAK,CAAC,GAAG,SAAS;AAAA,UACzD,GAAG;AAAA,UAEH;AAAA;AAAA,MACH;AAAA,IAEJ;AAEA,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,UAAU,YAAY;AAAA,QACtB,aAAW,WAAW;AAAA,QACtB,WAAW,GAAG,eAAe,EAAE,SAAS,KAAK,CAAC,GAAG,SAAS;AAAA,QACzD,GAAG;AAAA,QAEH;AAAA,oBAAU,oBAAC,WAAQ,MAAK,MAAK,IAAK;AAAA,UAClC,WAAW,QAAQ,QAAQ,WAAW,IAAI,IAAI,IAAI,OAAO;AAAA;AAAA;AAAA,IAC5D;AAAA,EAEJ;AACF;AAEA,OAAO,cAAc;","names":[]}
@@ -0,0 +1,81 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-HCA5ODCD.js";
4
+
5
+ // src/segmented-control.tsx
6
+ import * as React from "react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ var TONE_ON = "bg-primary text-primary-foreground";
9
+ var TONE_OFF = "text-muted-foreground hover:bg-muted";
10
+ var TONES = {
11
+ emphasis: { on: TONE_ON, off: TONE_OFF },
12
+ primary: { on: TONE_ON, off: TONE_OFF }
13
+ };
14
+ var TRACKS = {
15
+ contained: "border-border inline-flex rounded-md border p-0.5",
16
+ loose: "flex flex-wrap gap-2"
17
+ };
18
+ function SegmentedControl({
19
+ options,
20
+ value,
21
+ onChange,
22
+ ariaLabel,
23
+ variant = "contained",
24
+ tone = "emphasis",
25
+ fill = false,
26
+ className
27
+ }) {
28
+ const refs = React.useRef(/* @__PURE__ */ new Map());
29
+ function onKeyDown(event) {
30
+ const step = event.key === "ArrowRight" || event.key === "ArrowDown" ? 1 : event.key === "ArrowLeft" || event.key === "ArrowUp" ? -1 : 0;
31
+ if (step === 0) return;
32
+ const selectable = options.filter((option) => !option.disabled);
33
+ if (selectable.length < 2) return;
34
+ const current = selectable.findIndex((option) => option.value === value);
35
+ const next = selectable[(current + step + selectable.length) % selectable.length];
36
+ event.preventDefault();
37
+ onChange(next.value);
38
+ refs.current.get(next.value)?.focus();
39
+ }
40
+ return /* @__PURE__ */ jsx(
41
+ "div",
42
+ {
43
+ role: "radiogroup",
44
+ "aria-label": ariaLabel,
45
+ onKeyDown,
46
+ className: cn(TRACKS[variant], className),
47
+ children: options.map((option) => {
48
+ const checked = option.value === value;
49
+ return /* @__PURE__ */ jsx(
50
+ "button",
51
+ {
52
+ ref: (node) => {
53
+ refs.current.set(option.value, node);
54
+ },
55
+ type: "button",
56
+ role: "radio",
57
+ "aria-checked": checked,
58
+ disabled: option.disabled,
59
+ title: option.title,
60
+ tabIndex: checked ? 0 : -1,
61
+ onClick: () => onChange(option.value),
62
+ className: cn(
63
+ "cursor-pointer rounded px-3 py-1 text-sm font-medium transition",
64
+ "focus-visible:ring-ring focus-visible:ring-2 focus-visible:outline-none",
65
+ "disabled:cursor-not-allowed disabled:opacity-50",
66
+ fill && "flex-1",
67
+ checked ? TONES[tone].on : TONES[tone].off
68
+ ),
69
+ children: option.label
70
+ },
71
+ option.value
72
+ );
73
+ })
74
+ }
75
+ );
76
+ }
77
+
78
+ export {
79
+ SegmentedControl
80
+ };
81
+ //# sourceMappingURL=chunk-MLRSOTFI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/segmented-control.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"./utils\";\n\n/**\n * The one-of-N picker seven panels were each drawing by hand.\n *\n * Not `Tabs`, which is a bottom-bordered strip sitting above the content it\n * switches. This is the inline version — a bordered track with the selected\n * option filled in — used for a filter or a setting that sits among other\n * controls rather than heading a panel.\n *\n * **Roles are the reason to use this.** The seven copies had three different\n * ideas about what they were: one said `tablist`/`tab`, one said\n * `group`/`aria-pressed`, and the other five said nothing at all — so on five\n * of seven a screen reader heard a row of unlabelled buttons with no\n * indication which one was active. None of the three was right. `tablist`\n * without a `tabpanel` is a promise the markup does not keep, and\n * `aria-pressed` describes independent toggles, not a choice of one. Picking a\n * value out of a fixed set is a radio group, so that is what this is, with the\n * roving tabindex and arrow keys that come with the role.\n *\n * `tone` is kept for call-site compatibility. Both `emphasis` and `primary`\n * resolve to `bg-primary text-primary-foreground` — neutral brand primary is\n * already the ink fill five panels used, and a raw `slate-950` utility would\n * escape brand recoloring. Prefer `primary` in new code.\n */\nexport interface SegmentedOption<Value extends string> {\n value: Value;\n label: React.ReactNode;\n disabled?: boolean;\n /**\n * Hover text. Worth setting on a disabled option — \"Everyone\" going grey\n * says nothing about which permission would bring it back.\n */\n title?: string;\n}\n\nexport interface SegmentedControlProps<Value extends string> {\n options: ReadonlyArray<SegmentedOption<Value>>;\n value: Value;\n onChange: (value: Value) => void;\n /**\n * Required: the group never has a visible label — the options name the\n * choice between them, which tells you nothing about what is being chosen.\n */\n ariaLabel: string;\n /**\n * `contained` — one bordered track around the options (five panels).\n * `loose` — free-standing pills with a gap between them (two panels).\n */\n variant?: \"contained\" | \"loose\";\n /** Prefer `primary`. `emphasis` is an alias kept for existing call sites. */\n tone?: \"emphasis\" | \"primary\";\n /** Options share the width equally, for a control that fills its column. */\n fill?: boolean;\n className?: string;\n}\n\nconst TONE_ON = \"bg-primary text-primary-foreground\";\nconst TONE_OFF = \"text-muted-foreground hover:bg-muted\";\n\nconst TONES = {\n emphasis: { on: TONE_ON, off: TONE_OFF },\n primary: { on: TONE_ON, off: TONE_OFF },\n} as const;\n\nconst TRACKS = {\n contained: \"border-border inline-flex rounded-md border p-0.5\",\n loose: \"flex flex-wrap gap-2\",\n} as const;\n\nexport function SegmentedControl<Value extends string>({\n options,\n value,\n onChange,\n ariaLabel,\n variant = \"contained\",\n tone = \"emphasis\",\n fill = false,\n className,\n}: SegmentedControlProps<Value>) {\n const refs = React.useRef(new Map<Value, HTMLButtonElement | null>());\n\n function onKeyDown(event: React.KeyboardEvent<HTMLDivElement>) {\n const step =\n event.key === \"ArrowRight\" || event.key === \"ArrowDown\"\n ? 1\n : event.key === \"ArrowLeft\" || event.key === \"ArrowUp\"\n ? -1\n : 0;\n if (step === 0) return;\n\n const selectable = options.filter((option) => !option.disabled);\n if (selectable.length < 2) return;\n\n const current = selectable.findIndex((option) => option.value === value);\n // Wraps, which is what a radio group is expected to do.\n const next =\n selectable[(current + step + selectable.length) % selectable.length];\n\n event.preventDefault();\n onChange(next.value);\n // The newly checked option has to take focus, or the next arrow press is\n // still measured from the old one.\n refs.current.get(next.value)?.focus();\n }\n\n return (\n <div\n role=\"radiogroup\"\n aria-label={ariaLabel}\n onKeyDown={onKeyDown}\n className={cn(TRACKS[variant], className)}\n >\n {options.map((option) => {\n const checked = option.value === value;\n return (\n <button\n key={option.value}\n ref={(node) => {\n refs.current.set(option.value, node);\n }}\n type=\"button\"\n role=\"radio\"\n aria-checked={checked}\n disabled={option.disabled}\n title={option.title}\n // Only the checked option is reachable by Tab; arrows move within\n // the group. Without this a keyboard user pages through every\n // option to get past the control.\n tabIndex={checked ? 0 : -1}\n onClick={() => onChange(option.value)}\n className={cn(\n \"cursor-pointer rounded px-3 py-1 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 fill && \"flex-1\",\n checked ? TONES[tone].on : TONES[tone].off,\n )}\n >\n {option.label}\n </button>\n );\n })}\n </div>\n );\n}\n"],"mappings":";;;;;AAEA,YAAY,WAAW;AAsHb;AA3DV,IAAM,UAAU;AAChB,IAAM,WAAW;AAEjB,IAAM,QAAQ;AAAA,EACZ,UAAU,EAAE,IAAI,SAAS,KAAK,SAAS;AAAA,EACvC,SAAS,EAAE,IAAI,SAAS,KAAK,SAAS;AACxC;AAEA,IAAM,SAAS;AAAA,EACb,WAAW;AAAA,EACX,OAAO;AACT;AAEO,SAAS,iBAAuC;AAAA,EACrD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,UAAU;AAAA,EACV,OAAO;AAAA,EACP,OAAO;AAAA,EACP;AACF,GAAiC;AAC/B,QAAM,OAAa,aAAO,oBAAI,IAAqC,CAAC;AAEpE,WAAS,UAAU,OAA4C;AAC7D,UAAM,OACJ,MAAM,QAAQ,gBAAgB,MAAM,QAAQ,cACxC,IACA,MAAM,QAAQ,eAAe,MAAM,QAAQ,YACzC,KACA;AACR,QAAI,SAAS,EAAG;AAEhB,UAAM,aAAa,QAAQ,OAAO,CAAC,WAAW,CAAC,OAAO,QAAQ;AAC9D,QAAI,WAAW,SAAS,EAAG;AAE3B,UAAM,UAAU,WAAW,UAAU,CAAC,WAAW,OAAO,UAAU,KAAK;AAEvE,UAAM,OACJ,YAAY,UAAU,OAAO,WAAW,UAAU,WAAW,MAAM;AAErE,UAAM,eAAe;AACrB,aAAS,KAAK,KAAK;AAGnB,SAAK,QAAQ,IAAI,KAAK,KAAK,GAAG,MAAM;AAAA,EACtC;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,MAAK;AAAA,MACL,cAAY;AAAA,MACZ;AAAA,MACA,WAAW,GAAG,OAAO,OAAO,GAAG,SAAS;AAAA,MAEvC,kBAAQ,IAAI,CAAC,WAAW;AACvB,cAAM,UAAU,OAAO,UAAU;AACjC,eACE;AAAA,UAAC;AAAA;AAAA,YAEC,KAAK,CAAC,SAAS;AACb,mBAAK,QAAQ,IAAI,OAAO,OAAO,IAAI;AAAA,YACrC;AAAA,YACA,MAAK;AAAA,YACL,MAAK;AAAA,YACL,gBAAc;AAAA,YACd,UAAU,OAAO;AAAA,YACjB,OAAO,OAAO;AAAA,YAId,UAAU,UAAU,IAAI;AAAA,YACxB,SAAS,MAAM,SAAS,OAAO,KAAK;AAAA,YACpC,WAAW;AAAA,cACT;AAAA,cACA;AAAA,cACA;AAAA,cACA,QAAQ;AAAA,cACR,UAAU,MAAM,IAAI,EAAE,KAAK,MAAM,IAAI,EAAE;AAAA,YACzC;AAAA,YAEC,iBAAO;AAAA;AAAA,UAtBH,OAAO;AAAA,QAuBd;AAAA,MAEJ,CAAC;AAAA;AAAA,EACH;AAEJ;","names":[]}
@@ -0,0 +1,202 @@
1
+ // src/spreadsheet-grid.tsx
2
+ import * as React from "react";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ function isInRange(range, r, c) {
5
+ if (!range) return false;
6
+ return r >= range.r1 && r <= range.r2 && c >= range.c1 && c <= range.c2;
7
+ }
8
+ function SpreadsheetGrid({
9
+ columns,
10
+ rows,
11
+ rowKey,
12
+ loading,
13
+ emptyMessage = "Nothing to show.",
14
+ loadingMessage = "Loading\u2026",
15
+ ariaLabel = "Data grid",
16
+ range,
17
+ onRangeChange,
18
+ onCopy
19
+ }) {
20
+ const gridRef = React.useRef(null);
21
+ const anchorRef = React.useRef(null);
22
+ const focusRef = React.useRef(null);
23
+ const [dragging, setDragging] = React.useState(false);
24
+ const setSelection = React.useCallback(
25
+ (anchor, focus) => {
26
+ anchorRef.current = anchor;
27
+ focusRef.current = focus;
28
+ onRangeChange({ anchor, focus });
29
+ },
30
+ [onRangeChange]
31
+ );
32
+ const selectCell = React.useCallback(
33
+ (r, c, extend) => {
34
+ if (extend && anchorRef.current)
35
+ setSelection(anchorRef.current, { r, c });
36
+ else setSelection({ r, c }, { r, c });
37
+ },
38
+ [setSelection]
39
+ );
40
+ const selectColumn = React.useCallback(
41
+ (c, extend) => {
42
+ const lastRow = Math.max(rows.length - 1, 0);
43
+ const anchorCol = extend && anchorRef.current ? anchorRef.current.c : c;
44
+ setSelection({ r: 0, c: anchorCol }, { r: lastRow, c });
45
+ },
46
+ [rows.length, setSelection]
47
+ );
48
+ const selectRow = React.useCallback(
49
+ (r, extend) => {
50
+ const lastCol = columns.length - 1;
51
+ const anchorRow = extend && anchorRef.current ? anchorRef.current.r : r;
52
+ setSelection({ r: anchorRow, c: 0 }, { r, c: lastCol });
53
+ },
54
+ [columns.length, setSelection]
55
+ );
56
+ const selectAll = React.useCallback(() => {
57
+ if (!rows.length || !columns.length) return;
58
+ setSelection({ r: 0, c: 0 }, { r: rows.length - 1, c: columns.length - 1 });
59
+ }, [rows.length, columns.length, setSelection]);
60
+ const scrollCellIntoView = React.useCallback((r, c) => {
61
+ const cell = gridRef.current?.querySelector(`[data-cell="${r}-${c}"]`);
62
+ cell?.scrollIntoView({ block: "nearest", inline: "nearest" });
63
+ }, []);
64
+ const moveFocus = React.useCallback(
65
+ (dr, dc, extend) => {
66
+ if (!rows.length || !columns.length) return;
67
+ const base = focusRef.current || { r: 0, c: 0 };
68
+ const r = Math.min(Math.max(base.r + dr, 0), rows.length - 1);
69
+ const c = Math.min(Math.max(base.c + dc, 0), columns.length - 1);
70
+ selectCell(r, c, extend);
71
+ scrollCellIntoView(r, c);
72
+ },
73
+ [rows.length, columns.length, selectCell, scrollCellIntoView]
74
+ );
75
+ React.useEffect(() => {
76
+ if (!dragging) return;
77
+ const stop = () => setDragging(false);
78
+ window.addEventListener("mouseup", stop);
79
+ return () => window.removeEventListener("mouseup", stop);
80
+ }, [dragging]);
81
+ const handleKeyDown = (event) => {
82
+ const meta = event.metaKey || event.ctrlKey;
83
+ if (meta && event.key.toLowerCase() === "c") {
84
+ event.preventDefault();
85
+ onCopy();
86
+ return;
87
+ }
88
+ if (meta && event.key.toLowerCase() === "a") {
89
+ event.preventDefault();
90
+ selectAll();
91
+ return;
92
+ }
93
+ switch (event.key) {
94
+ case "ArrowUp":
95
+ event.preventDefault();
96
+ moveFocus(-1, 0, event.shiftKey);
97
+ break;
98
+ case "ArrowDown":
99
+ event.preventDefault();
100
+ moveFocus(1, 0, event.shiftKey);
101
+ break;
102
+ case "ArrowLeft":
103
+ event.preventDefault();
104
+ moveFocus(0, -1, event.shiftKey);
105
+ break;
106
+ case "ArrowRight":
107
+ case "Tab":
108
+ event.preventDefault();
109
+ moveFocus(0, 1, event.shiftKey && event.key !== "Tab");
110
+ break;
111
+ case "Escape":
112
+ setSelection(null, null);
113
+ break;
114
+ default:
115
+ break;
116
+ }
117
+ };
118
+ return /* @__PURE__ */ jsx(
119
+ "div",
120
+ {
121
+ ref: gridRef,
122
+ tabIndex: 0,
123
+ role: "grid",
124
+ "aria-label": ariaLabel,
125
+ onKeyDown: handleKeyDown,
126
+ className: "border-border bg-background/80 overflow-x-auto rounded-lg border outline-none",
127
+ children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm whitespace-nowrap select-none", children: [
128
+ /* @__PURE__ */ jsx("thead", { className: "text-muted-foreground border-border border-b text-left", children: /* @__PURE__ */ jsxs("tr", { children: [
129
+ /* @__PURE__ */ jsx(
130
+ "th",
131
+ {
132
+ scope: "col",
133
+ className: "w-10 cursor-pointer px-2 py-2 text-center font-medium",
134
+ title: "Select everything",
135
+ onMouseDown: () => selectAll(),
136
+ children: "#"
137
+ }
138
+ ),
139
+ columns.map((column, c) => /* @__PURE__ */ jsx(
140
+ "th",
141
+ {
142
+ scope: "col",
143
+ className: "cursor-pointer px-3 py-2 font-medium",
144
+ title: `Select the ${column.title} column`,
145
+ onMouseDown: (event) => selectColumn(c, event.shiftKey),
146
+ children: column.title
147
+ },
148
+ column.key
149
+ ))
150
+ ] }) }),
151
+ /* @__PURE__ */ jsx("tbody", { children: rows.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
152
+ "td",
153
+ {
154
+ colSpan: columns.length + 1,
155
+ className: "text-muted-foreground px-3 py-6 text-center",
156
+ children: loading ? loadingMessage : emptyMessage
157
+ }
158
+ ) }) : rows.map((row, r) => /* @__PURE__ */ jsxs(
159
+ "tr",
160
+ {
161
+ className: "border-border border-b",
162
+ children: [
163
+ /* @__PURE__ */ jsx(
164
+ "td",
165
+ {
166
+ className: "text-muted-foreground w-10 cursor-pointer px-2 py-2 text-center text-xs",
167
+ title: "Select this row",
168
+ onMouseDown: (event) => selectRow(r, event.shiftKey),
169
+ children: r + 1
170
+ }
171
+ ),
172
+ columns.map((column, c) => /* @__PURE__ */ jsx(
173
+ "td",
174
+ {
175
+ "data-cell": `${r}-${c}`,
176
+ onMouseDown: (event) => {
177
+ selectCell(r, c, event.shiftKey);
178
+ setDragging(true);
179
+ gridRef.current?.focus();
180
+ },
181
+ onMouseEnter: () => {
182
+ if (dragging) setSelection(anchorRef.current, { r, c });
183
+ },
184
+ className: `px-3 py-2 ${column.className || ""} ${isInRange(range, r, c) ? "bg-primary/15" : ""}`,
185
+ title: column.className?.includes("truncate") ? column.value(row) : void 0,
186
+ children: column.render ? column.render(row) : column.value(row)
187
+ },
188
+ column.key
189
+ ))
190
+ ]
191
+ },
192
+ rowKey ? rowKey(row, r) : String(r)
193
+ )) })
194
+ ] })
195
+ }
196
+ );
197
+ }
198
+
199
+ export {
200
+ SpreadsheetGrid
201
+ };
202
+ //# sourceMappingURL=chunk-MQT5RAQV.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/spreadsheet-grid.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { type CellRange, type CellRef } from \"./clipboard\";\n\n/**\n * A column knows three things: its heading, the text it contributes to a copy\n * or CSV export, and optionally how to draw itself.\n *\n * Keeping `value` and `render` together is what stops an exported sheet from\n * drifting from the visible grid.\n */\nexport type GridColumn<Row> = {\n key: string;\n title: string;\n /** Text used for clipboard and CSV. Never JSX. */\n value: (row: Row) => string;\n /** Defaults to `value`. Use for links, tiles, truncation. */\n render?: (row: Row) => React.ReactNode;\n /** Extra classes for the <td>. */\n className?: string;\n};\n\nexport interface SpreadsheetGridProps<Row> {\n columns: GridColumn<Row>[];\n rows: Row[];\n /** Stable React key per row. Defaults to the row index. */\n rowKey?: (row: Row, index: number) => string;\n loading?: boolean;\n emptyMessage?: string;\n loadingMessage?: string;\n ariaLabel?: string;\n /** Lifted so a toolbar can copy and describe the current selection. */\n range: CellRange | null;\n onRangeChange: (selection: {\n anchor: CellRef | null;\n focus: CellRef | null;\n }) => void;\n onCopy: () => void;\n}\n\nfunction isInRange(range: CellRange | null, r: number, c: number): boolean {\n if (!range) return false;\n return r >= range.r1 && r <= range.r2 && c >= range.c1 && c <= range.c2;\n}\n\n/**\n * Spreadsheet-like grid: click-drag a range, shift+click to extend, arrows to\n * move, click a heading to take the column, click the row number to take the\n * row, Cmd/Ctrl+A for everything, Escape to clear, Cmd/Ctrl+C to copy.\n *\n * Ported from video-shorts-panel's ExcelTable, which several panels share for\n * exactly this reason — a table people paste into a sheet.\n */\nexport function SpreadsheetGrid<Row>({\n columns,\n rows,\n rowKey,\n loading,\n emptyMessage = \"Nothing to show.\",\n loadingMessage = \"Loading…\",\n ariaLabel = \"Data grid\",\n range,\n onRangeChange,\n onCopy,\n}: SpreadsheetGridProps<Row>) {\n const gridRef = React.useRef<HTMLDivElement>(null);\n const anchorRef = React.useRef<CellRef | null>(null);\n const focusRef = React.useRef<CellRef | null>(null);\n const [dragging, setDragging] = React.useState(false);\n\n const setSelection = React.useCallback(\n (anchor: CellRef | null, focus: CellRef | null) => {\n anchorRef.current = anchor;\n focusRef.current = focus;\n onRangeChange({ anchor, focus });\n },\n [onRangeChange],\n );\n\n const selectCell = React.useCallback(\n (r: number, c: number, extend?: boolean) => {\n if (extend && anchorRef.current)\n setSelection(anchorRef.current, { r, c });\n else setSelection({ r, c }, { r, c });\n },\n [setSelection],\n );\n\n const selectColumn = React.useCallback(\n (c: number, extend?: boolean) => {\n const lastRow = Math.max(rows.length - 1, 0);\n const anchorCol = extend && anchorRef.current ? anchorRef.current.c : c;\n setSelection({ r: 0, c: anchorCol }, { r: lastRow, c });\n },\n [rows.length, setSelection],\n );\n\n const selectRow = React.useCallback(\n (r: number, extend?: boolean) => {\n const lastCol = columns.length - 1;\n const anchorRow = extend && anchorRef.current ? anchorRef.current.r : r;\n setSelection({ r: anchorRow, c: 0 }, { r, c: lastCol });\n },\n [columns.length, setSelection],\n );\n\n const selectAll = React.useCallback(() => {\n if (!rows.length || !columns.length) return;\n setSelection({ r: 0, c: 0 }, { r: rows.length - 1, c: columns.length - 1 });\n }, [rows.length, columns.length, setSelection]);\n\n const scrollCellIntoView = React.useCallback((r: number, c: number) => {\n const cell = gridRef.current?.querySelector(`[data-cell=\"${r}-${c}\"]`);\n cell?.scrollIntoView({ block: \"nearest\", inline: \"nearest\" });\n }, []);\n\n const moveFocus = React.useCallback(\n (dr: number, dc: number, extend?: boolean) => {\n if (!rows.length || !columns.length) return;\n const base = focusRef.current || { r: 0, c: 0 };\n const r = Math.min(Math.max(base.r + dr, 0), rows.length - 1);\n const c = Math.min(Math.max(base.c + dc, 0), columns.length - 1);\n selectCell(r, c, extend);\n scrollCellIntoView(r, c);\n },\n [rows.length, columns.length, selectCell, scrollCellIntoView],\n );\n\n // Dragging must end even if the pointer is released outside the grid.\n React.useEffect(() => {\n if (!dragging) return;\n const stop = () => setDragging(false);\n window.addEventListener(\"mouseup\", stop);\n return () => window.removeEventListener(\"mouseup\", stop);\n }, [dragging]);\n\n const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {\n const meta = event.metaKey || event.ctrlKey;\n if (meta && event.key.toLowerCase() === \"c\") {\n event.preventDefault();\n onCopy();\n return;\n }\n if (meta && event.key.toLowerCase() === \"a\") {\n event.preventDefault();\n selectAll();\n return;\n }\n switch (event.key) {\n case \"ArrowUp\":\n event.preventDefault();\n moveFocus(-1, 0, event.shiftKey);\n break;\n case \"ArrowDown\":\n event.preventDefault();\n moveFocus(1, 0, event.shiftKey);\n break;\n case \"ArrowLeft\":\n event.preventDefault();\n moveFocus(0, -1, event.shiftKey);\n break;\n case \"ArrowRight\":\n case \"Tab\":\n event.preventDefault();\n moveFocus(0, 1, event.shiftKey && event.key !== \"Tab\");\n break;\n case \"Escape\":\n setSelection(null, null);\n break;\n default:\n break;\n }\n };\n\n return (\n <div\n ref={gridRef}\n tabIndex={0}\n role=\"grid\"\n aria-label={ariaLabel}\n onKeyDown={handleKeyDown}\n className=\"border-border bg-background/80 overflow-x-auto rounded-lg border outline-none\"\n >\n <table className=\"w-full text-sm whitespace-nowrap select-none\">\n <thead className=\"text-muted-foreground border-border border-b text-left\">\n <tr>\n <th\n scope=\"col\"\n className=\"w-10 cursor-pointer px-2 py-2 text-center font-medium\"\n title=\"Select everything\"\n onMouseDown={() => selectAll()}\n >\n #\n </th>\n {columns.map((column, c) => (\n <th\n key={column.key}\n scope=\"col\"\n className=\"cursor-pointer px-3 py-2 font-medium\"\n title={`Select the ${column.title} column`}\n onMouseDown={(event) => selectColumn(c, event.shiftKey)}\n >\n {column.title}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {rows.length === 0 ? (\n <tr>\n <td\n colSpan={columns.length + 1}\n className=\"text-muted-foreground px-3 py-6 text-center\"\n >\n {loading ? loadingMessage : emptyMessage}\n </td>\n </tr>\n ) : (\n rows.map((row, r) => (\n <tr\n key={rowKey ? rowKey(row, r) : String(r)}\n className=\"border-border border-b\"\n >\n <td\n className=\"text-muted-foreground w-10 cursor-pointer px-2 py-2 text-center text-xs\"\n title=\"Select this row\"\n onMouseDown={(event) => selectRow(r, event.shiftKey)}\n >\n {r + 1}\n </td>\n {columns.map((column, c) => (\n <td\n key={column.key}\n data-cell={`${r}-${c}`}\n onMouseDown={(event) => {\n selectCell(r, c, event.shiftKey);\n setDragging(true);\n gridRef.current?.focus();\n }}\n onMouseEnter={() => {\n if (dragging) setSelection(anchorRef.current, { r, c });\n }}\n className={`px-3 py-2 ${column.className || \"\"} ${\n isInRange(range, r, c) ? \"bg-primary/15\" : \"\"\n }`}\n title={\n column.className?.includes(\"truncate\")\n ? column.value(row)\n : undefined\n }\n >\n {column.render ? column.render(row) : column.value(row)}\n </td>\n ))}\n </tr>\n ))\n )}\n </tbody>\n </table>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,102 @@
1
+ // src/date-range-picker/dateRange.ts
2
+ var pad = (n) => String(n).padStart(2, "0");
3
+ function toIsoDate(date) {
4
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
5
+ }
6
+ function fromIsoDate(value) {
7
+ if (!value) return null;
8
+ const trimmed = value.trim();
9
+ const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(trimmed);
10
+ if (!match) return null;
11
+ const date = new Date(
12
+ Number(match[1]),
13
+ Number(match[2]) - 1,
14
+ Number(match[3])
15
+ );
16
+ return toIsoDate(date) === trimmed ? date : null;
17
+ }
18
+ function startOfMonth(date) {
19
+ return new Date(date.getFullYear(), date.getMonth(), 1);
20
+ }
21
+ function addMonths(date, count) {
22
+ return new Date(date.getFullYear(), date.getMonth() + count, 1);
23
+ }
24
+ function addDays(date, count) {
25
+ return new Date(date.getFullYear(), date.getMonth(), date.getDate() + count);
26
+ }
27
+ function isSameDay(a, b) {
28
+ if (!a || !b) return false;
29
+ return toIsoDate(a) === toIsoDate(b);
30
+ }
31
+ function monthGrid(month) {
32
+ const first = startOfMonth(month);
33
+ const start = addDays(first, -first.getDay());
34
+ return Array.from({ length: 42 }, (_, index) => addDays(start, index));
35
+ }
36
+ function monthLabel(month, locale = "en-IN") {
37
+ return month.toLocaleDateString(locale, { month: "long", year: "numeric" });
38
+ }
39
+ function formatDisplay(value, locale = "en-IN") {
40
+ const date = fromIsoDate(value);
41
+ if (!date) return "";
42
+ return date.toLocaleDateString(locale, {
43
+ day: "numeric",
44
+ month: "short",
45
+ year: "numeric"
46
+ });
47
+ }
48
+ function startOfDayMs(value) {
49
+ const date = fromIsoDate(value);
50
+ return date ? date.getTime() : 0;
51
+ }
52
+ function endOfDayMs(value) {
53
+ const date = fromIsoDate(value);
54
+ if (!date) return 0;
55
+ return new Date(
56
+ date.getFullYear(),
57
+ date.getMonth(),
58
+ date.getDate(),
59
+ 23,
60
+ 59,
61
+ 59,
62
+ 999
63
+ ).getTime();
64
+ }
65
+ function endAt2359Ms(value) {
66
+ const date = fromIsoDate(value);
67
+ if (!date) return 0;
68
+ return new Date(
69
+ date.getFullYear(),
70
+ date.getMonth(),
71
+ date.getDate(),
72
+ 23,
73
+ 59,
74
+ 59,
75
+ 0
76
+ ).getTime();
77
+ }
78
+ function createdRangeMs(from, to) {
79
+ const startDate = from.trim() || to.trim();
80
+ const endDate = to.trim() || from.trim();
81
+ return {
82
+ start_time: startOfDayMs(startDate),
83
+ end_time: endAt2359Ms(endDate)
84
+ };
85
+ }
86
+
87
+ export {
88
+ toIsoDate,
89
+ fromIsoDate,
90
+ startOfMonth,
91
+ addMonths,
92
+ addDays,
93
+ isSameDay,
94
+ monthGrid,
95
+ monthLabel,
96
+ formatDisplay,
97
+ startOfDayMs,
98
+ endOfDayMs,
99
+ endAt2359Ms,
100
+ createdRangeMs
101
+ };
102
+ //# sourceMappingURL=chunk-MUPW2TFJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/date-range-picker/dateRange.ts"],"sourcesContent":["/**\n * Date helpers for the range picker, all in local time.\n *\n * Deliberately not `toISOString()`: that converts to UTC first, so on any\n * negative-offset day it returns yesterday's date and the picker highlights the\n * wrong cell. Every value here is built and read as the local calendar day the\n * user is actually looking at.\n */\n\n/** `YYYY-MM-DD`, the value shape the filters store. */\nexport type IsoDate = string;\n\nconst pad = (n: number) => String(n).padStart(2, \"0\");\n\nexport function toIsoDate(date: Date): IsoDate {\n return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;\n}\n\n/** Parses `YYYY-MM-DD` into a local midnight Date; null for anything else. */\nexport function fromIsoDate(value: string | null | undefined): Date | null {\n if (!value) return null;\n const trimmed = value.trim();\n const match = /^(\\d{4})-(\\d{2})-(\\d{2})$/.exec(trimmed);\n if (!match) return null;\n const date = new Date(\n Number(match[1]),\n Number(match[2]) - 1,\n Number(match[3]),\n );\n // Rejects 2026-02-31, which would otherwise roll silently into March.\n return toIsoDate(date) === trimmed ? date : null;\n}\n\nexport function startOfMonth(date: Date): Date {\n return new Date(date.getFullYear(), date.getMonth(), 1);\n}\n\nexport function addMonths(date: Date, count: number): Date {\n return new Date(date.getFullYear(), date.getMonth() + count, 1);\n}\n\nexport function addDays(date: Date, count: number): Date {\n return new Date(date.getFullYear(), date.getMonth(), date.getDate() + count);\n}\n\nexport function isSameDay(a: Date | null, b: Date | null): boolean {\n if (!a || !b) return false;\n return toIsoDate(a) === toIsoDate(b);\n}\n\n/**\n * The six-week grid for a month, Sunday-first, padded with the neighbouring\n * months so every row has seven cells.\n *\n * Always six rows, never five: a grid that changes height as you page through\n * months makes everything below it jump.\n */\nexport function monthGrid(month: Date): Date[] {\n const first = startOfMonth(month);\n const start = addDays(first, -first.getDay());\n return Array.from({ length: 42 }, (_, index) => addDays(start, index));\n}\n\nexport function monthLabel(month: Date, locale = \"en-IN\"): string {\n return month.toLocaleDateString(locale, { month: \"long\", year: \"numeric\" });\n}\n\n/** Human form for the trigger — \"22 Aug 2026\". */\nexport function formatDisplay(value: string, locale = \"en-IN\"): string {\n const date = fromIsoDate(value);\n if (!date) return \"\";\n return date.toLocaleDateString(locale, {\n day: \"numeric\",\n month: \"short\",\n year: \"numeric\",\n });\n}\n\nexport function startOfDayMs(value: string): number {\n const date = fromIsoDate(value);\n return date ? date.getTime() : 0;\n}\n\n/**\n * The end of a day as milliseconds.\n *\n * A range picked as whole days has to include everything created on the last\n * day, so \"to\" means 23:59:59.999 rather than that morning's midnight — which\n * would drop a whole day of cards without saying so.\n */\nexport function endOfDayMs(value: string): number {\n const date = fromIsoDate(value);\n if (!date) return 0;\n return new Date(\n date.getFullYear(),\n date.getMonth(),\n date.getDate(),\n 23,\n 59,\n 59,\n 999,\n ).getTime();\n}\n\n/**\n * 11:59 PM on that local day, as 23:59:59.\n *\n * The next midnight is already the following date, and the feed includes that\n * whole day — so the 28th through the 28th came back with the 29th. Stopping\n * at 11:59 PM keeps the end on the day that was picked.\n */\nexport function endAt2359Ms(value: string): number {\n const date = fromIsoDate(value);\n if (!date) return 0;\n return new Date(\n date.getFullYear(),\n date.getMonth(),\n date.getDate(),\n 23,\n 59,\n 59,\n 0,\n ).getTime();\n}\n\n/**\n * Created-date window for the feed.\n *\n * A blank end means the start day, so one selected date is that whole day.\n * `end_time` is 11:59 PM on the end date.\n */\nexport function createdRangeMs(\n from: string,\n to: string,\n): { start_time: number; end_time: number } {\n const startDate = from.trim() || to.trim();\n const endDate = to.trim() || from.trim();\n return {\n start_time: startOfDayMs(startDate),\n end_time: endAt2359Ms(endDate),\n };\n}\n"],"mappings":"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","names":[]}