@inshorts/glint-ui 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +3 -9
  3. package/dist/alert.cjs +96 -0
  4. package/dist/alert.cjs.map +1 -0
  5. package/dist/alert.d.cts +29 -0
  6. package/dist/alert.d.ts +29 -0
  7. package/dist/alert.js +10 -0
  8. package/dist/alert.js.map +1 -0
  9. package/dist/autocomplete.cjs +578 -0
  10. package/dist/autocomplete.cjs.map +1 -0
  11. package/dist/autocomplete.d.cts +56 -0
  12. package/dist/autocomplete.d.ts +56 -0
  13. package/dist/autocomplete.js +11 -0
  14. package/dist/autocomplete.js.map +1 -0
  15. package/dist/badge.cjs +89 -0
  16. package/dist/badge.cjs.map +1 -0
  17. package/dist/badge.d.cts +25 -0
  18. package/dist/badge.d.ts +25 -0
  19. package/dist/badge.js +10 -0
  20. package/dist/badge.js.map +1 -0
  21. package/dist/breadcrumb.cjs +88 -0
  22. package/dist/breadcrumb.cjs.map +1 -0
  23. package/dist/breadcrumb.d.cts +14 -0
  24. package/dist/breadcrumb.d.ts +14 -0
  25. package/dist/breadcrumb.js +8 -0
  26. package/dist/breadcrumb.js.map +1 -0
  27. package/dist/button.cjs +143 -0
  28. package/dist/button.cjs.map +1 -0
  29. package/dist/button.d.cts +34 -0
  30. package/dist/button.d.ts +34 -0
  31. package/dist/button.js +9 -0
  32. package/dist/button.js.map +1 -0
  33. package/dist/card.cjs +111 -0
  34. package/dist/card.cjs.map +1 -0
  35. package/dist/card.d.cts +15 -0
  36. package/dist/card.d.ts +15 -0
  37. package/dist/card.js +18 -0
  38. package/dist/card.js.map +1 -0
  39. package/dist/checkbox.cjs +66 -0
  40. package/dist/checkbox.cjs.map +1 -0
  41. package/{src/checkbox.tsx → dist/checkbox.d.cts} +5 -22
  42. package/dist/checkbox.d.ts +25 -0
  43. package/dist/checkbox.js +10 -0
  44. package/dist/checkbox.js.map +1 -0
  45. package/dist/chunk-43ZUFY63.js +85 -0
  46. package/dist/chunk-43ZUFY63.js.map +1 -0
  47. package/dist/chunk-67ACNS42.js +29 -0
  48. package/dist/chunk-67ACNS42.js.map +1 -0
  49. package/dist/chunk-6URKHMCF.js +76 -0
  50. package/dist/chunk-6URKHMCF.js.map +1 -0
  51. package/dist/chunk-ASU6CUTZ.js +85 -0
  52. package/dist/chunk-ASU6CUTZ.js.map +1 -0
  53. package/dist/chunk-EWSBFU54.js +53 -0
  54. package/dist/chunk-EWSBFU54.js.map +1 -0
  55. package/dist/chunk-G4ZQUSAJ.js +38 -0
  56. package/dist/chunk-G4ZQUSAJ.js.map +1 -0
  57. package/dist/chunk-GMCUXEFI.js +67 -0
  58. package/dist/chunk-GMCUXEFI.js.map +1 -0
  59. package/dist/chunk-HBBOR43A.js +85 -0
  60. package/dist/chunk-HBBOR43A.js.map +1 -0
  61. package/dist/chunk-HCA5ODCD.js +11 -0
  62. package/dist/chunk-HCA5ODCD.js.map +1 -0
  63. package/dist/chunk-HYRJYUPI.js +26 -0
  64. package/dist/chunk-HYRJYUPI.js.map +1 -0
  65. package/dist/chunk-IRCTGJQB.js +26 -0
  66. package/dist/chunk-IRCTGJQB.js.map +1 -0
  67. package/dist/chunk-JDE5XYPT.js +30 -0
  68. package/dist/chunk-JDE5XYPT.js.map +1 -0
  69. package/dist/chunk-JSJEEKR3.js +59 -0
  70. package/dist/chunk-JSJEEKR3.js.map +1 -0
  71. package/dist/chunk-KR3BCFP7.js +85 -0
  72. package/dist/chunk-KR3BCFP7.js.map +1 -0
  73. package/dist/chunk-MQT5RAQV.js +202 -0
  74. package/dist/chunk-MQT5RAQV.js.map +1 -0
  75. package/dist/chunk-MUPW2TFJ.js +102 -0
  76. package/dist/chunk-MUPW2TFJ.js.map +1 -0
  77. package/dist/chunk-PM2VE7DJ.js +100 -0
  78. package/dist/chunk-PM2VE7DJ.js.map +1 -0
  79. package/dist/chunk-PPYR3O3L.js +44 -0
  80. package/dist/chunk-PPYR3O3L.js.map +1 -0
  81. package/dist/chunk-PUTC52SI.js +98 -0
  82. package/dist/chunk-PUTC52SI.js.map +1 -0
  83. package/dist/chunk-PVK4WQJZ.js +513 -0
  84. package/dist/chunk-PVK4WQJZ.js.map +1 -0
  85. package/dist/chunk-PWAKTEXF.js +18 -0
  86. package/dist/chunk-PWAKTEXF.js.map +1 -0
  87. package/dist/chunk-QTFWORA7.js +26 -0
  88. package/dist/chunk-QTFWORA7.js.map +1 -0
  89. package/dist/chunk-RUPL2H6V.js +49 -0
  90. package/dist/chunk-RUPL2H6V.js.map +1 -0
  91. package/dist/chunk-S7PVZAPL.js +28 -0
  92. package/dist/chunk-S7PVZAPL.js.map +1 -0
  93. package/dist/chunk-SQDV2AKJ.js +49 -0
  94. package/dist/chunk-SQDV2AKJ.js.map +1 -0
  95. package/dist/chunk-T2KXVH3O.js +297 -0
  96. package/dist/chunk-T2KXVH3O.js.map +1 -0
  97. package/dist/chunk-T4WCCHJQ.js +29 -0
  98. package/dist/chunk-T4WCCHJQ.js.map +1 -0
  99. package/dist/chunk-UC3ZIQPE.js +93 -0
  100. package/dist/chunk-UC3ZIQPE.js.map +1 -0
  101. package/dist/chunk-UE7JY7VR.js +28 -0
  102. package/dist/chunk-UE7JY7VR.js.map +1 -0
  103. package/dist/chunk-WVNRFO5F.js +70 -0
  104. package/dist/chunk-WVNRFO5F.js.map +1 -0
  105. package/dist/chunk-Y5WCT6XN.js +93 -0
  106. package/dist/chunk-Y5WCT6XN.js.map +1 -0
  107. package/dist/chunk-YDICVETX.js +151 -0
  108. package/dist/chunk-YDICVETX.js.map +1 -0
  109. package/dist/chunk-YSU4VWRD.js +90 -0
  110. package/dist/chunk-YSU4VWRD.js.map +1 -0
  111. package/dist/chunk-ZFSQVMLJ.js +126 -0
  112. package/dist/chunk-ZFSQVMLJ.js.map +1 -0
  113. package/dist/chunk-ZHATD73Y.js +46 -0
  114. package/dist/chunk-ZHATD73Y.js.map +1 -0
  115. package/dist/chunk-ZLAA4DGG.js +111 -0
  116. package/dist/chunk-ZLAA4DGG.js.map +1 -0
  117. package/dist/chunk-ZP252OJZ.js +56 -0
  118. package/dist/chunk-ZP252OJZ.js.map +1 -0
  119. package/dist/clipboard.cjs +125 -0
  120. package/dist/clipboard.cjs.map +1 -0
  121. package/dist/clipboard.d.cts +52 -0
  122. package/dist/clipboard.d.ts +52 -0
  123. package/dist/clipboard.js +23 -0
  124. package/dist/clipboard.js.map +1 -0
  125. package/dist/collapsible.cjs +94 -0
  126. package/dist/collapsible.cjs.map +1 -0
  127. package/dist/collapsible.d.cts +21 -0
  128. package/dist/collapsible.d.ts +21 -0
  129. package/dist/collapsible.js +9 -0
  130. package/dist/collapsible.js.map +1 -0
  131. package/dist/confirm-dialog.cjs +369 -0
  132. package/dist/confirm-dialog.cjs.map +1 -0
  133. package/dist/confirm-dialog.d.cts +16 -0
  134. package/dist/confirm-dialog.d.ts +16 -0
  135. package/dist/confirm-dialog.js +13 -0
  136. package/dist/confirm-dialog.js.map +1 -0
  137. package/dist/copy-button.cjs +102 -0
  138. package/dist/copy-button.cjs.map +1 -0
  139. package/dist/copy-button.d.cts +18 -0
  140. package/dist/copy-button.d.ts +18 -0
  141. package/dist/copy-button.js +10 -0
  142. package/dist/copy-button.js.map +1 -0
  143. package/dist/data-table.cjs +140 -0
  144. package/dist/data-table.cjs.map +1 -0
  145. package/dist/data-table.d.cts +81 -0
  146. package/dist/data-table.d.ts +81 -0
  147. package/dist/data-table.js +8 -0
  148. package/dist/data-table.js.map +1 -0
  149. package/dist/date-range-picker/dateRange.cjs +138 -0
  150. package/dist/date-range-picker/dateRange.cjs.map +1 -0
  151. package/dist/date-range-picker/dateRange.d.cts +57 -0
  152. package/dist/date-range-picker/dateRange.d.ts +57 -0
  153. package/dist/date-range-picker/dateRange.js +31 -0
  154. package/dist/date-range-picker/dateRange.js.map +1 -0
  155. package/dist/date-range-picker/index.cjs +375 -0
  156. package/dist/date-range-picker/index.cjs.map +1 -0
  157. package/dist/date-range-picker/index.d.cts +18 -0
  158. package/dist/date-range-picker/index.d.ts +18 -0
  159. package/dist/date-range-picker/index.js +10 -0
  160. package/dist/date-range-picker/index.js.map +1 -0
  161. package/dist/dialogLayer.cjs +121 -0
  162. package/dist/dialogLayer.cjs.map +1 -0
  163. package/dist/dialogLayer.d.cts +11 -0
  164. package/dist/dialogLayer.d.ts +11 -0
  165. package/dist/dialogLayer.js +8 -0
  166. package/dist/dialogLayer.js.map +1 -0
  167. package/dist/feature-access-gate.cjs +160 -0
  168. package/dist/feature-access-gate.cjs.map +1 -0
  169. package/dist/feature-access-gate.d.cts +14 -0
  170. package/dist/feature-access-gate.d.ts +14 -0
  171. package/dist/feature-access-gate.js +11 -0
  172. package/dist/feature-access-gate.js.map +1 -0
  173. package/dist/field.cjs +58 -0
  174. package/dist/field.cjs.map +1 -0
  175. package/dist/field.d.cts +23 -0
  176. package/dist/field.d.ts +23 -0
  177. package/dist/field.js +8 -0
  178. package/dist/field.js.map +1 -0
  179. package/dist/image-uploader.cjs +293 -0
  180. package/dist/image-uploader.cjs.map +1 -0
  181. package/dist/image-uploader.d.cts +34 -0
  182. package/dist/image-uploader.d.ts +34 -0
  183. package/dist/image-uploader.js +12 -0
  184. package/dist/image-uploader.js.map +1 -0
  185. package/dist/index.cjs +3012 -0
  186. package/dist/index.cjs.map +1 -0
  187. package/dist/index.d.cts +41 -0
  188. package/dist/index.d.ts +41 -0
  189. package/dist/index.js +232 -0
  190. package/dist/index.js.map +1 -0
  191. package/dist/input.cjs +58 -0
  192. package/dist/input.cjs.map +1 -0
  193. package/dist/input.d.cts +7 -0
  194. package/dist/input.d.ts +7 -0
  195. package/dist/input.js +10 -0
  196. package/dist/input.js.map +1 -0
  197. package/dist/label.cjs +67 -0
  198. package/dist/label.cjs.map +1 -0
  199. package/dist/label.d.cts +7 -0
  200. package/dist/label.d.ts +7 -0
  201. package/dist/label.js +9 -0
  202. package/dist/label.js.map +1 -0
  203. package/dist/modal.cjs +212 -0
  204. package/dist/modal.cjs.map +1 -0
  205. package/dist/modal.d.cts +39 -0
  206. package/dist/modal.d.ts +39 -0
  207. package/dist/modal.js +12 -0
  208. package/dist/modal.js.map +1 -0
  209. package/dist/page-header.cjs +78 -0
  210. package/dist/page-header.cjs.map +1 -0
  211. package/dist/page-header.d.cts +35 -0
  212. package/dist/page-header.d.ts +35 -0
  213. package/dist/page-header.js +8 -0
  214. package/dist/page-header.js.map +1 -0
  215. package/dist/pagination.cjs +237 -0
  216. package/dist/pagination.cjs.map +1 -0
  217. package/dist/pagination.d.cts +15 -0
  218. package/dist/pagination.d.ts +15 -0
  219. package/dist/pagination.js +11 -0
  220. package/dist/pagination.js.map +1 -0
  221. package/dist/panel-brand-mark.cjs +59 -0
  222. package/dist/panel-brand-mark.cjs.map +1 -0
  223. package/dist/panel-brand-mark.d.cts +13 -0
  224. package/dist/panel-brand-mark.d.ts +13 -0
  225. package/dist/panel-brand-mark.js +8 -0
  226. package/dist/panel-brand-mark.js.map +1 -0
  227. package/dist/panel-sign-in.cjs +256 -0
  228. package/dist/panel-sign-in.cjs.map +1 -0
  229. package/dist/panel-sign-in.d.cts +31 -0
  230. package/dist/panel-sign-in.d.ts +31 -0
  231. package/dist/panel-sign-in.js +14 -0
  232. package/dist/panel-sign-in.js.map +1 -0
  233. package/dist/segmented-control.cjs +126 -0
  234. package/dist/segmented-control.cjs.map +1 -0
  235. package/dist/segmented-control.d.cts +60 -0
  236. package/dist/segmented-control.d.ts +60 -0
  237. package/dist/segmented-control.js +9 -0
  238. package/dist/segmented-control.js.map +1 -0
  239. package/dist/select.cjs +648 -0
  240. package/dist/select.cjs.map +1 -0
  241. package/dist/select.d.cts +30 -0
  242. package/dist/select.d.ts +30 -0
  243. package/dist/select.js +12 -0
  244. package/dist/select.js.map +1 -0
  245. package/dist/separator.cjs +70 -0
  246. package/dist/separator.cjs.map +1 -0
  247. package/dist/separator.d.cts +13 -0
  248. package/dist/separator.d.ts +13 -0
  249. package/dist/separator.js +9 -0
  250. package/dist/separator.js.map +1 -0
  251. package/dist/side-drawer.cjs +321 -0
  252. package/dist/side-drawer.cjs.map +1 -0
  253. package/dist/side-drawer.d.cts +21 -0
  254. package/dist/side-drawer.d.ts +21 -0
  255. package/dist/side-drawer.js +13 -0
  256. package/dist/side-drawer.js.map +1 -0
  257. package/dist/spinner.cjs +57 -0
  258. package/dist/spinner.cjs.map +1 -0
  259. package/{src/spinner.tsx → dist/spinner.d.cts} +11 -22
  260. package/dist/spinner.d.ts +27 -0
  261. package/dist/spinner.js +8 -0
  262. package/dist/spinner.js.map +1 -0
  263. package/dist/spreadsheet-grid.cjs +238 -0
  264. package/dist/spreadsheet-grid.cjs.map +1 -0
  265. package/dist/spreadsheet-grid.d.cts +48 -0
  266. package/dist/spreadsheet-grid.d.ts +48 -0
  267. package/dist/spreadsheet-grid.js +8 -0
  268. package/dist/spreadsheet-grid.js.map +1 -0
  269. package/dist/state-panel.cjs +86 -0
  270. package/dist/state-panel.cjs.map +1 -0
  271. package/dist/state-panel.d.cts +23 -0
  272. package/dist/state-panel.d.ts +23 -0
  273. package/dist/state-panel.js +9 -0
  274. package/dist/state-panel.js.map +1 -0
  275. package/dist/switch.cjs +87 -0
  276. package/dist/switch.cjs.map +1 -0
  277. package/dist/switch.d.cts +13 -0
  278. package/dist/switch.d.ts +13 -0
  279. package/dist/switch.js +9 -0
  280. package/dist/switch.js.map +1 -0
  281. package/dist/table.cjs +139 -0
  282. package/dist/table.cjs.map +1 -0
  283. package/dist/table.d.cts +16 -0
  284. package/dist/table.d.ts +16 -0
  285. package/dist/table.js +22 -0
  286. package/dist/table.js.map +1 -0
  287. package/dist/tabs.cjs +168 -0
  288. package/dist/tabs.cjs.map +1 -0
  289. package/dist/tabs.d.cts +59 -0
  290. package/dist/tabs.d.ts +59 -0
  291. package/dist/tabs.js +11 -0
  292. package/dist/tabs.js.map +1 -0
  293. package/dist/textarea.cjs +66 -0
  294. package/dist/textarea.cjs.map +1 -0
  295. package/dist/textarea.d.cts +18 -0
  296. package/dist/textarea.d.ts +18 -0
  297. package/dist/textarea.js +10 -0
  298. package/dist/textarea.js.map +1 -0
  299. package/dist/utils.cjs +35 -0
  300. package/dist/utils.cjs.map +1 -0
  301. package/dist/utils.d.cts +5 -0
  302. package/dist/utils.d.ts +5 -0
  303. package/dist/utils.js +7 -0
  304. package/dist/utils.js.map +1 -0
  305. package/package.json +395 -19
  306. package/src/alert.tsx +0 -74
  307. package/src/autocomplete.tsx +0 -666
  308. package/src/badge.tsx +0 -60
  309. package/src/breadcrumb.tsx +0 -71
  310. package/src/button.tsx +0 -113
  311. package/src/card.tsx +0 -79
  312. package/src/clipboard.ts +0 -144
  313. package/src/collapsible.tsx +0 -63
  314. package/src/confirm-dialog.tsx +0 -84
  315. package/src/copy-button.tsx +0 -58
  316. package/src/data-table.tsx +0 -190
  317. package/src/date-range-picker/dateRange.ts +0 -142
  318. package/src/date-range-picker/index.tsx +0 -366
  319. package/src/dialogLayer.ts +0 -142
  320. package/src/feature-access-gate.tsx +0 -38
  321. package/src/field.tsx +0 -56
  322. package/src/image-uploader.tsx +0 -181
  323. package/src/index.ts +0 -37
  324. package/src/input.tsx +0 -16
  325. package/src/label.tsx +0 -23
  326. package/src/modal.tsx +0 -117
  327. package/src/page-header.tsx +0 -75
  328. package/src/pagination.tsx +0 -116
  329. package/src/panel-brand-mark.tsx +0 -32
  330. package/src/panel-sign-in.tsx +0 -134
  331. package/src/segmented-control.tsx +0 -155
  332. package/src/select.tsx +0 -105
  333. package/src/separator.tsx +0 -32
  334. package/src/side-drawer.tsx +0 -87
  335. package/src/spreadsheet-grid.tsx +0 -264
  336. package/src/state-panel.tsx +0 -46
  337. package/src/switch.tsx +0 -46
  338. package/src/table.tsx +0 -110
  339. package/src/tabs.tsx +0 -201
  340. package/src/textarea.tsx +0 -32
  341. package/src/utils.ts +0 -6
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/autocomplete.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { Check, ChevronDown, Search, X } from \"lucide-react\";\n\nimport { useHasMounted } from \"./modal\";\nimport { cn } from \"./utils\";\n\n/** Room the list wants below the control before it gives up and flips. */\nconst PREFERRED_LIST_HEIGHT = 224;\n/** Below this there is no point opening downward at all. */\nconst MIN_LIST_HEIGHT = 120;\nconst VIEWPORT_MARGIN = 8;\nconst ANCHOR_GAP = 4;\n\ntype DropdownPosition = {\n left: number;\n width: number;\n maxHeight: number;\n /** One of the two is set: `top` opens downward, `bottom` opens upward. */\n top?: number;\n bottom?: number;\n};\n\nexport type AutocompleteOption = {\n value: string;\n label: string;\n disabled?: boolean;\n};\n\ntype BaseAutocompleteProps = {\n options: AutocompleteOption[];\n placeholder?: string;\n disabled?: boolean;\n className?: string;\n emptyMessage?: string;\n id?: string;\n /**\n * Accessible name for the combobox.\n *\n * `Field` renders its label as a `<span>`, so without this the control is\n * announced as an unnamed combobox and cannot be queried by name — the\n * filter toolbars work around it by counting comboboxes on the page.\n */\n ariaLabel?: string;\n /**\n * `compact` is the spreadsheet cell: no search icon, no min height, and a\n * list wide enough to read a name. The closed control still shows the\n * chosen label, and typing filters the list.\n */\n size?: \"default\" | \"compact\";\n /**\n * Type a value and press Enter (or paste a list) to add it, like antd\n * `Select mode=\"tags\"`. With `multiple` a pasted list is split into several\n * values; on a single-value control it accepts one typed value the options\n * do not offer — an id for a record the list has not loaded, say.\n */\n creatable?: boolean;\n /**\n * Shows the clear button once a value is chosen. Off for dropdowns that\n * always keep one of their options, where clearing would blank a required\n * field.\n */\n clearable?: boolean;\n};\n\ntype SingleAutocompleteProps = BaseAutocompleteProps & {\n multiple?: false;\n value: string;\n onChange: (value: string) => void;\n};\n\ntype MultipleAutocompleteProps = BaseAutocompleteProps & {\n multiple: true;\n value: string[];\n onChange: (value: string[]) => void;\n};\n\nexport type AutocompleteProps =\n SingleAutocompleteProps | MultipleAutocompleteProps;\n\nfunction normalizeQuery(query: string) {\n return query.trim().toLowerCase();\n}\n\nfunction filterOptions(options: AutocompleteOption[], query: string) {\n const normalized = normalizeQuery(query);\n if (!normalized) return options;\n\n return options.filter(\n (option) =>\n option.label.toLowerCase().includes(normalized) ||\n option.value.toLowerCase().includes(normalized),\n );\n}\n\nfunction getOptionLabel(options: AutocompleteOption[], value: string) {\n return options.find((option) => option.value === value)?.label ?? value;\n}\n\nfunction parseTagTokens(raw: string): string[] {\n const seen = new Set<string>();\n const tokens: string[] = [];\n for (const part of raw.split(/[\\s,]+/)) {\n const token = part.trim();\n if (!token || seen.has(token)) continue;\n seen.add(token);\n tokens.push(token);\n }\n return tokens;\n}\n\nexport function Autocomplete(props: AutocompleteProps) {\n const {\n options,\n placeholder = \"Search…\",\n disabled = false,\n className,\n emptyMessage = \"No results found\",\n id,\n ariaLabel,\n size = \"default\",\n creatable = false,\n clearable = true,\n } = props;\n\n const multiple = props.multiple ?? false;\n const singleValue = multiple\n ? undefined\n : (props as SingleAutocompleteProps).value;\n const multipleValue = multiple\n ? (props as MultipleAutocompleteProps).value\n : undefined;\n\n // Memoized so dependent callbacks below keep stable identities across renders.\n const selectedValues = React.useMemo<string[]>(() => {\n if (multiple) return multipleValue ?? [];\n return singleValue ? [singleValue] : [];\n }, [multiple, multipleValue, singleValue]);\n\n const onSingleChange = !multiple\n ? (props as SingleAutocompleteProps).onChange\n : undefined;\n const onMultipleChange = multiple\n ? (props as MultipleAutocompleteProps).onChange\n : undefined;\n\n const containerRef = React.useRef<HTMLDivElement>(null);\n const dropdownRef = React.useRef<HTMLDivElement>(null);\n const inputRef = React.useRef<HTMLInputElement>(null);\n const listRef = React.useRef<HTMLUListElement>(null);\n const ignoreBlurRef = React.useRef(false);\n\n const mounted = useHasMounted();\n const [open, setOpen] = React.useState(false);\n const [position, setPosition] = React.useState<DropdownPosition | null>(null);\n const [query, setQuery] = React.useState(\"\");\n const [highlightedIndex, setHighlightedIndex] = React.useState(0);\n\n const filteredOptions = React.useMemo(\n () => filterOptions(options, query),\n [options, query],\n );\n\n // Clamp during render instead of via a setState-in-effect: if the option list\n // shrinks below the stored index, fall back to the last valid row.\n const safeHighlightedIndex =\n highlightedIndex >= filteredOptions.length\n ? Math.max(filteredOptions.length - 1, 0)\n : highlightedIndex;\n\n const close = React.useCallback(() => {\n setOpen(false);\n setQuery(\"\");\n setHighlightedIndex(0);\n }, []);\n\n const openDropdown = React.useCallback(() => {\n if (disabled) return;\n setOpen(true);\n setHighlightedIndex(0);\n }, [disabled]);\n\n const commitSingleValue = React.useCallback(\n (value: string) => {\n if (!multiple) {\n onSingleChange?.(value);\n close();\n }\n },\n [close, multiple, onSingleChange],\n );\n\n const toggleMultipleValue = React.useCallback(\n (value: string) => {\n if (!multiple || !onMultipleChange) return;\n\n onMultipleChange(\n selectedValues.includes(value)\n ? selectedValues.filter((item) => item !== value)\n : [...selectedValues, value],\n );\n setQuery(\"\");\n setHighlightedIndex(0);\n },\n [multiple, onMultipleChange, selectedValues],\n );\n\n const commitCustomValues = React.useCallback(\n (raw: string) => {\n if (!creatable) return;\n\n if (!multiple) {\n const value = raw.trim();\n if (!value) return;\n commitSingleValue(value);\n return;\n }\n\n if (!onMultipleChange) return;\n const tokens = parseTagTokens(raw);\n if (!tokens.length) return;\n const next = [...selectedValues];\n for (const token of tokens) {\n if (!next.includes(token)) next.push(token);\n }\n onMultipleChange(next);\n setQuery(\"\");\n setHighlightedIndex(0);\n },\n [commitSingleValue, creatable, multiple, onMultipleChange, selectedValues],\n );\n\n const removeValue = React.useCallback(\n (value: string) => {\n if (!multiple || !onMultipleChange) return;\n onMultipleChange(selectedValues.filter((item) => item !== value));\n window.requestAnimationFrame(() => inputRef.current?.focus());\n },\n [multiple, onMultipleChange, selectedValues],\n );\n\n const selectOption = React.useCallback(\n (option: AutocompleteOption) => {\n if (option.disabled) return;\n\n ignoreBlurRef.current = true;\n window.setTimeout(() => {\n ignoreBlurRef.current = false;\n }, 0);\n\n if (multiple) {\n toggleMultipleValue(option.value);\n window.requestAnimationFrame(() => inputRef.current?.focus());\n return;\n }\n\n commitSingleValue(option.value);\n },\n [commitSingleValue, multiple, toggleMultipleValue],\n );\n\n const selectHighlighted = React.useCallback(() => {\n const option = filteredOptions[safeHighlightedIndex];\n if (!option) return;\n selectOption(option);\n }, [filteredOptions, safeHighlightedIndex, selectOption]);\n\n React.useEffect(() => {\n if (!open) return;\n\n function handlePointerDown(event: PointerEvent) {\n const target = event.target as Node;\n // The list is portalled to the body, so containment has to be checked\n // against both halves or picking an option would close the control\n // before the choice registered.\n if (\n containerRef.current?.contains(target) ||\n dropdownRef.current?.contains(target)\n ) {\n return;\n }\n close();\n }\n\n document.addEventListener(\"pointerdown\", handlePointerDown);\n return () => document.removeEventListener(\"pointerdown\", handlePointerDown);\n }, [close, open]);\n\n /*\n The list is measured against the viewport and rendered in a portal.\n\n Anchored inside the control it was clipped by whatever `overflow` ancestor\n happened to be above it — inside a dialog body that is the scroll region,\n so the last field on a form showed one row of a six-hundred-row list. It\n opens downward when there is room, upward when there is not, and shrinks to\n what is left when neither side can take the whole list.\n */\n React.useLayoutEffect(() => {\n // Not cleared on close: the render is gated on `open`, and the layout\n // effect recomputes before the next paint, so a stale value is never seen.\n if (!open) return;\n\n const update = () => {\n const anchor = containerRef.current;\n if (!anchor) return;\n const rect = anchor.getBoundingClientRect();\n const below =\n window.innerHeight - rect.bottom - ANCHOR_GAP - VIEWPORT_MARGIN;\n const above = rect.top - ANCHOR_GAP - VIEWPORT_MARGIN;\n const flip =\n below < Math.min(PREFERRED_LIST_HEIGHT, MIN_LIST_HEIGHT) &&\n above > below;\n const room = flip ? above : below;\n const width = Math.max(rect.width, size === \"compact\" ? 220 : 0);\n const left = Math.max(\n VIEWPORT_MARGIN,\n Math.min(rect.left, window.innerWidth - width - VIEWPORT_MARGIN),\n );\n setPosition({\n left,\n width,\n maxHeight: Math.max(\n MIN_LIST_HEIGHT,\n Math.min(PREFERRED_LIST_HEIGHT, room),\n ),\n ...(flip\n ? { bottom: window.innerHeight - rect.top + ANCHOR_GAP }\n : { top: rect.bottom + ANCHOR_GAP }),\n });\n };\n\n update();\n // Capture, so a scroll in any ancestor — the dialog body included — moves\n // the list with its control instead of leaving it behind.\n window.addEventListener(\"scroll\", update, true);\n window.addEventListener(\"resize\", update);\n return () => {\n window.removeEventListener(\"scroll\", update, true);\n window.removeEventListener(\"resize\", update);\n };\n }, [open, size]);\n\n React.useEffect(() => {\n if (!open || !listRef.current) return;\n\n const highlighted = listRef.current.children[safeHighlightedIndex] as\n HTMLElement | undefined;\n\n highlighted?.scrollIntoView({ block: \"nearest\" });\n }, [safeHighlightedIndex, open]);\n\n function handleInputKeyDown(event: React.KeyboardEvent<HTMLInputElement>) {\n if (disabled) return;\n\n switch (event.key) {\n case \"ArrowDown\":\n event.preventDefault();\n if (!open) {\n openDropdown();\n return;\n }\n setHighlightedIndex(\n Math.min(safeHighlightedIndex + 1, filteredOptions.length - 1),\n );\n break;\n case \"ArrowUp\":\n event.preventDefault();\n if (!open) {\n openDropdown();\n return;\n }\n setHighlightedIndex(Math.max(safeHighlightedIndex - 1, 0));\n break;\n case \"Enter\":\n event.preventDefault();\n if (creatable && query.trim()) {\n // A single-value control filters its list as you type, so a match\n // is what Enter means there; only an unmatched query is taken as a\n // new value. Tags have no such list to defer to.\n if (multiple || filteredOptions.length === 0) {\n commitCustomValues(query);\n return;\n }\n }\n if (!open) {\n openDropdown();\n return;\n }\n selectHighlighted();\n break;\n case \",\":\n if (creatable && multiple && query.trim()) {\n event.preventDefault();\n commitCustomValues(query);\n }\n break;\n case \"Escape\":\n event.preventDefault();\n close();\n break;\n case \"Backspace\":\n if (multiple && query.length === 0 && selectedValues.length > 0) {\n event.preventDefault();\n removeValue(selectedValues[selectedValues.length - 1]);\n }\n break;\n case \"Tab\":\n close();\n break;\n default:\n break;\n }\n }\n\n const displayValue =\n !multiple && !open && !query && selectedValues[0]\n ? getOptionLabel(options, selectedValues[0])\n : query;\n\n const inputPlaceholder =\n multiple && selectedValues.length > 0 ? \"\" : placeholder;\n\n const createLabel = query.trim();\n const canCreate =\n creatable && Boolean(createLabel) && !selectedValues.includes(createLabel);\n\n const listboxId = id ? `${id}-listbox` : undefined;\n\n return (\n <div\n ref={containerRef}\n className={cn(\n \"relative\",\n size === \"compact\" ? \"w-auto min-w-24\" : \"w-full\",\n className,\n )}\n >\n <div\n className={cn(\n \"border-border bg-background flex w-full cursor-pointer items-center rounded-md border transition-colors\",\n \"focus-within:border-ring focus-within:ring-ring/30 focus-within:ring-2\",\n size === \"compact\"\n ? \"min-h-0 gap-0.5 border-transparent bg-transparent px-1 py-0.5 text-xs shadow-none\"\n : \"min-h-9 gap-1.5 px-2 py-1.5 text-sm shadow-sm\",\n disabled && \"cursor-not-allowed opacity-50\",\n open && \"border-ring ring-ring/30 ring-2\",\n )}\n onPointerDown={(event) => {\n if (disabled) return;\n if ((event.target as HTMLElement).closest(\"[data-option]\")) return;\n if ((event.target as HTMLElement).closest(\"[data-chip-remove]\"))\n return;\n\n if (!(event.target as HTMLElement).closest(\"input\")) {\n event.preventDefault();\n inputRef.current?.focus();\n }\n\n openDropdown();\n }}\n >\n {size === \"compact\" ? null : (\n <Search className=\"text-muted-foreground size-4 shrink-0\" />\n )}\n\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-1\">\n {multiple &&\n selectedValues.map((value) => (\n <span\n key={value}\n className=\"bg-primary/10 text-primary inline-flex max-w-full items-center gap-1 rounded-md px-2 py-0.5 text-xs font-medium\"\n >\n <span className=\"truncate\">\n {getOptionLabel(options, value)}\n </span>\n <button\n type=\"button\"\n data-chip-remove\n className=\"text-primary/70 hover:bg-primary/15 hover:text-primary rounded-sm transition\"\n onPointerDown={(event) => {\n event.preventDefault();\n event.stopPropagation();\n removeValue(value);\n }}\n aria-label={`Remove ${getOptionLabel(options, value)}`}\n >\n <X className=\"size-3\" />\n </button>\n </span>\n ))}\n\n <input\n ref={inputRef}\n id={id}\n type=\"text\"\n role=\"combobox\"\n aria-label={ariaLabel}\n aria-expanded={open}\n aria-controls={listboxId}\n aria-autocomplete=\"list\"\n disabled={disabled}\n value={displayValue}\n placeholder={inputPlaceholder}\n className={cn(\n \"placeholder:text-muted-foreground flex-1 bg-transparent outline-none\",\n size === \"compact\" ? \"min-w-0\" : \"min-w-[6rem]\",\n )}\n onFocus={openDropdown}\n onBlur={() => {\n if (ignoreBlurRef.current) return;\n close();\n }}\n onChange={(event) => {\n setQuery(event.target.value);\n setHighlightedIndex(0);\n if (!open) setOpen(true);\n }}\n onKeyDown={handleInputKeyDown}\n onPaste={(event) => {\n if (!creatable || !multiple) return;\n const text = event.clipboardData.getData(\"text\");\n if (!/[\\s,]/.test(text)) return;\n event.preventDefault();\n commitCustomValues(text);\n }}\n />\n </div>\n\n {clearable && !multiple && selectedValues[0] && !disabled && (\n <button\n type=\"button\"\n className=\"text-muted-foreground hover:bg-muted hover:text-foreground rounded-sm p-0.5 transition\"\n onPointerDown={(event) => {\n event.preventDefault();\n event.stopPropagation();\n onSingleChange?.(\"\");\n setQuery(\"\");\n inputRef.current?.focus();\n }}\n aria-label=\"Clear selection\"\n >\n <X className=\"size-3.5\" />\n </button>\n )}\n\n <ChevronDown\n className={cn(\n \"text-muted-foreground shrink-0 transition-transform\",\n size === \"compact\" ? \"size-3\" : \"size-4\",\n open && \"rotate-180\",\n )}\n />\n </div>\n\n {open && mounted && position\n ? createPortal(\n <div\n ref={dropdownRef}\n data-autocomplete-dropdown\n style={{\n position: \"fixed\",\n left: position.left,\n width: position.width,\n top: position.top,\n bottom: position.bottom,\n // Above the modal shell (z-100) it is usually opened inside.\n zIndex: 200,\n }}\n className=\"border-border bg-background overflow-hidden rounded-md border shadow-lg\"\n >\n <ul\n ref={listRef}\n id={listboxId}\n role=\"listbox\"\n aria-multiselectable={multiple || undefined}\n className=\"overflow-y-auto p-1\"\n style={{ maxHeight: position.maxHeight }}\n >\n {filteredOptions.length === 0 && !canCreate ? (\n <li className=\"text-muted-foreground px-3 py-6 text-center text-sm\">\n {emptyMessage}\n </li>\n ) : (\n <>\n {filteredOptions.map((option, index) => {\n const isSelected = selectedValues.includes(option.value);\n const isHighlighted = index === safeHighlightedIndex;\n\n return (\n <li\n key={option.value}\n data-option\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled || undefined}\n className={cn(\n \"flex cursor-pointer items-center gap-2 rounded-sm transition-colors\",\n size === \"compact\"\n ? \"px-2 py-1 text-xs\"\n : \"px-2.5 py-2 text-sm\",\n isHighlighted && \"bg-muted\",\n option.disabled && \"cursor-not-allowed opacity-50\",\n )}\n onMouseEnter={() => setHighlightedIndex(index)}\n onPointerDown={(event) => {\n event.preventDefault();\n event.stopPropagation();\n selectOption(option);\n }}\n >\n {multiple ? (\n <span\n className={cn(\n \"border-border flex size-4 shrink-0 items-center justify-center rounded border\",\n isSelected &&\n \"border-primary bg-primary text-primary-foreground\",\n )}\n >\n {isSelected ? <Check className=\"size-3\" /> : null}\n </span>\n ) : (\n <Check\n className={cn(\n \"text-primary size-4 shrink-0\",\n isSelected ? \"opacity-100\" : \"opacity-0\",\n )}\n />\n )}\n <span className=\"truncate\">{option.label}</span>\n </li>\n );\n })}\n {canCreate ? (\n <li\n data-option\n role=\"option\"\n aria-selected={false}\n className=\"hover:bg-muted flex cursor-pointer items-center gap-2 rounded-sm px-2.5 py-2 text-sm transition-colors\"\n onPointerDown={(event) => {\n event.preventDefault();\n event.stopPropagation();\n ignoreBlurRef.current = true;\n window.setTimeout(() => {\n ignoreBlurRef.current = false;\n }, 0);\n commitCustomValues(query);\n window.requestAnimationFrame(() =>\n inputRef.current?.focus(),\n );\n }}\n >\n Add “{createLabel}”\n </li>\n ) : null}\n </>\n )}\n </ul>\n </div>,\n document.body,\n )\n : null}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,18 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-HCA5ODCD.js";
4
+
5
+ // src/input.tsx
6
+ import * as React from "react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ var inputClass = "h-9 w-full rounded-md border border-border bg-background px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50";
9
+ var Input = React.forwardRef(
10
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx("input", { ref, className: cn(inputClass, className), ...props })
11
+ );
12
+ Input.displayName = "Input";
13
+
14
+ export {
15
+ inputClass,
16
+ Input
17
+ };
18
+ //# sourceMappingURL=chunk-PWAKTEXF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/input.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport { cn } from \"./utils\";\n\nexport const inputClass =\n \"h-9 w-full rounded-md border border-border bg-background px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50\";\n\nexport type InputProps = React.InputHTMLAttributes<HTMLInputElement>;\n\nexport const Input = React.forwardRef<HTMLInputElement, InputProps>(\n ({ className, ...props }, ref) => (\n <input ref={ref} className={cn(inputClass, className)} {...props} />\n ),\n);\n\nInput.displayName = \"Input\";\n"],"mappings":";;;;;AAAA,YAAY,WAAW;AAWnB;AAPG,IAAM,aACX;AAIK,IAAM,QAAc;AAAA,EACzB,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QACxB,oBAAC,WAAM,KAAU,WAAW,GAAG,YAAY,SAAS,GAAI,GAAG,OAAO;AAEtE;AAEA,MAAM,cAAc;","names":[]}
@@ -0,0 +1,26 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-HCA5ODCD.js";
4
+
5
+ // src/label.tsx
6
+ import * as React from "react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ var Label = React.forwardRef(
9
+ ({ className, ...props }, ref) => /* @__PURE__ */ jsx(
10
+ "label",
11
+ {
12
+ ref,
13
+ className: cn(
14
+ "text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
15
+ className
16
+ ),
17
+ ...props
18
+ }
19
+ )
20
+ );
21
+ Label.displayName = "Label";
22
+
23
+ export {
24
+ Label
25
+ };
26
+ //# sourceMappingURL=chunk-QTFWORA7.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/label.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"./utils\";\n\nexport type LabelProps = React.LabelHTMLAttributes<HTMLLabelElement>;\n\n/** Form label — pair with a native control via `htmlFor`. */\nexport const Label = React.forwardRef<HTMLLabelElement, LabelProps>(\n ({ className, ...props }, ref) => (\n <label\n ref={ref}\n className={cn(\n \"text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70\",\n className,\n )}\n {...props}\n />\n ),\n);\n\nLabel.displayName = \"Label\";\n"],"mappings":";;;;;AAEA,YAAY,WAAW;AASnB;AAFG,IAAM,QAAc;AAAA,EACzB,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,QACxB;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,MAAM,cAAc;","names":[]}
@@ -0,0 +1,49 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-HCA5ODCD.js";
4
+
5
+ // src/page-header.tsx
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ function PageHeader({
8
+ title,
9
+ description,
10
+ eyebrow,
11
+ actions,
12
+ className
13
+ }) {
14
+ const heading = /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
15
+ eyebrow,
16
+ /* @__PURE__ */ jsx(
17
+ "h1",
18
+ {
19
+ className: cn(
20
+ "text-2xl font-semibold tracking-tight",
21
+ eyebrow != null && "mt-1"
22
+ ),
23
+ children: title
24
+ }
25
+ ),
26
+ description != null ? /* @__PURE__ */ jsx("p", { className: "text-muted-foreground mt-1 text-sm", children: description }) : null
27
+ ] });
28
+ if (!actions) {
29
+ return /* @__PURE__ */ jsx("div", { className, children: heading });
30
+ }
31
+ return /* @__PURE__ */ jsxs(
32
+ "div",
33
+ {
34
+ className: cn(
35
+ "flex flex-wrap items-center justify-between gap-3",
36
+ className
37
+ ),
38
+ children: [
39
+ heading,
40
+ actions
41
+ ]
42
+ }
43
+ );
44
+ }
45
+
46
+ export {
47
+ PageHeader
48
+ };
49
+ //# sourceMappingURL=chunk-RUPL2H6V.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/page-header.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport { cn } from \"./utils\";\n\n/**\n * The title block every panel opens with.\n *\n * Twenty Managers wrote the same `<h1>` and subtitle by hand, differing only in\n * whether the row said `gap-3` or `gap-x-4 gap-y-2` and whether the inner block\n * carried `min-w-0` — differences nobody chose, which is what made them worth\n * removing.\n *\n * `description` takes a node rather than a string because several panels put a\n * `<code>` in theirs, naming the Firebase field they write. That is the useful\n * kind of subtitle and it should not have to opt out of the shared component to\n * stay.\n *\n * `min-w-0` is not optional here. Without it a long title refuses to shrink and\n * pushes the actions off the row instead of wrapping — which is the bug the\n * three panels that set it had already hit, and the seven that did not had not\n * yet.\n */\nexport interface PageHeaderProps {\n title: React.ReactNode;\n description?: React.ReactNode;\n /**\n * Sits above the title: a breadcrumb, or the back link the filter editors\n * use. Four panels had one, each spacing it by hand.\n */\n eyebrow?: React.ReactNode;\n /** Buttons belonging to the page, laid out at the end of the title row. */\n actions?: React.ReactNode;\n className?: string;\n}\n\nexport function PageHeader({\n title,\n description,\n eyebrow,\n actions,\n className,\n}: PageHeaderProps) {\n const heading = (\n <div className=\"min-w-0\">\n {eyebrow}\n <h1\n className={cn(\n \"text-2xl font-semibold tracking-tight\",\n eyebrow != null && \"mt-1\",\n )}\n >\n {title}\n </h1>\n {description != null ? (\n <p className=\"text-muted-foreground mt-1 text-sm\">{description}</p>\n ) : null}\n </div>\n );\n\n if (!actions) {\n return <div className={className}>{heading}</div>;\n }\n\n return (\n <div\n className={cn(\n \"flex flex-wrap items-center justify-between gap-3\",\n className,\n )}\n >\n {heading}\n {actions}\n </div>\n );\n}\n"],"mappings":";;;;;AA2CI,SAEE,KAFF;AARG,SAAS,WAAW;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAoB;AAClB,QAAM,UACJ,qBAAC,SAAI,WAAU,WACZ;AAAA;AAAA,IACD;AAAA,MAAC;AAAA;AAAA,QACC,WAAW;AAAA,UACT;AAAA,UACA,WAAW,QAAQ;AAAA,QACrB;AAAA,QAEC;AAAA;AAAA,IACH;AAAA,IACC,eAAe,OACd,oBAAC,OAAE,WAAU,sCAAsC,uBAAY,IAC7D;AAAA,KACN;AAGF,MAAI,CAAC,SAAS;AACZ,WAAO,oBAAC,SAAI,WAAuB,mBAAQ;AAAA,EAC7C;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,MACF;AAAA,MAEC;AAAA;AAAA,QACA;AAAA;AAAA;AAAA,EACH;AAEJ;","names":[]}
@@ -0,0 +1,28 @@
1
+ import {
2
+ StatePanel
3
+ } from "./chunk-G4ZQUSAJ.js";
4
+ import {
5
+ Alert
6
+ } from "./chunk-ZP252OJZ.js";
7
+
8
+ // src/feature-access-gate.tsx
9
+ import { Fragment, jsx } from "react/jsx-runtime";
10
+ function FeatureAccessGate({
11
+ isLoading,
12
+ allowed,
13
+ deniedMessage,
14
+ children
15
+ }) {
16
+ if (isLoading) {
17
+ return /* @__PURE__ */ jsx(StatePanel, { loading: true, className: "p-6", children: "Checking permissions\u2026" });
18
+ }
19
+ if (!allowed) {
20
+ return /* @__PURE__ */ jsx(Alert, { children: deniedMessage });
21
+ }
22
+ return /* @__PURE__ */ jsx(Fragment, { children });
23
+ }
24
+
25
+ export {
26
+ FeatureAccessGate
27
+ };
28
+ //# sourceMappingURL=chunk-S7PVZAPL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/feature-access-gate.tsx"],"sourcesContent":["import * as React from \"react\";\n\nimport { Alert } from \"./alert\";\nimport { StatePanel } from \"./state-panel\";\n\ntype FeatureAccessGateProps = {\n isLoading?: boolean;\n allowed: boolean;\n deniedMessage: React.ReactNode;\n children: React.ReactNode;\n};\n\n/**\n * Shared loading / denied shell for feature Managers (showcase, headers, …).\n */\nexport function FeatureAccessGate({\n isLoading,\n allowed,\n deniedMessage,\n children,\n}: FeatureAccessGateProps) {\n if (isLoading) {\n return (\n <StatePanel loading className=\"p-6\">\n Checking permissions…\n </StatePanel>\n );\n }\n\n if (!allowed) {\n // `Alert`, not a hand-rolled banner: this is the denial surface for\n // seventeen panels, and it was the one copy the Alert migration missed —\n // so being refused a panel announced nothing to a screen reader.\n return <Alert>{deniedMessage}</Alert>;\n }\n\n return <>{children}</>;\n}\n"],"mappings":";;;;;;;;AAuBM,SAaG,UAbH;AARC,SAAS,kBAAkB;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA2B;AACzB,MAAI,WAAW;AACb,WACE,oBAAC,cAAW,SAAO,MAAC,WAAU,OAAM,wCAEpC;AAAA,EAEJ;AAEA,MAAI,CAAC,SAAS;AAIZ,WAAO,oBAAC,SAAO,yBAAc;AAAA,EAC/B;AAEA,SAAO,gCAAG,UAAS;AACrB;","names":[]}
@@ -0,0 +1,49 @@
1
+ import {
2
+ cn
3
+ } from "./chunk-HCA5ODCD.js";
4
+
5
+ // src/badge.tsx
6
+ import * as React from "react";
7
+ import { cva } from "class-variance-authority";
8
+ import { jsx } from "react/jsx-runtime";
9
+ var badgeVariants = cva(
10
+ "inline-flex items-center gap-1 rounded font-medium whitespace-nowrap",
11
+ {
12
+ variants: {
13
+ variant: {
14
+ neutral: "bg-muted text-muted-foreground",
15
+ success: "bg-success/15 text-success",
16
+ warning: "bg-warning/15 text-warning",
17
+ danger: "bg-destructive/15 text-destructive",
18
+ info: "bg-info/15 text-info"
19
+ },
20
+ size: {
21
+ /** Inline chips — a category, a tenant, a permission. */
22
+ sm: "px-1.5 py-0.5 text-xs",
23
+ /** A row's status. */
24
+ md: "px-2 py-0.5 text-xs"
25
+ }
26
+ },
27
+ defaultVariants: {
28
+ variant: "neutral",
29
+ size: "md"
30
+ }
31
+ }
32
+ );
33
+ var Badge = React.forwardRef(
34
+ ({ className, variant, size, ...props }, ref) => /* @__PURE__ */ jsx(
35
+ "span",
36
+ {
37
+ ref,
38
+ className: cn(badgeVariants({ variant, size }), className),
39
+ ...props
40
+ }
41
+ )
42
+ );
43
+ Badge.displayName = "Badge";
44
+
45
+ export {
46
+ badgeVariants,
47
+ Badge
48
+ };
49
+ //# sourceMappingURL=chunk-SQDV2AKJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/badge.tsx"],"sourcesContent":["import * as React from \"react\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nimport { cn } from \"./utils\";\n\n/**\n * The status pill every list panel was spelling out by hand.\n *\n * Variants are semantic rather than named after a colour, because the same\n * status means the same thing on every panel and should not have to be\n * re-decided per table — Deleted was `bg-rose-100 text-rose-800` in one place\n * and `text-rose-900` two files away.\n *\n * These stay light-on-dark-text in both themes on purpose. A pill is a small\n * block of colour whose whole job is to be picked out of a row of text, and\n * inverting it per theme made the same status read as two different states.\n */\nconst badgeVariants = cva(\n \"inline-flex items-center gap-1 rounded font-medium whitespace-nowrap\",\n {\n variants: {\n variant: {\n neutral: \"bg-muted text-muted-foreground\",\n success: \"bg-success/15 text-success\",\n warning: \"bg-warning/15 text-warning\",\n danger: \"bg-destructive/15 text-destructive\",\n info: \"bg-info/15 text-info\",\n },\n size: {\n /** Inline chips — a category, a tenant, a permission. */\n sm: \"px-1.5 py-0.5 text-xs\",\n /** A row's status. */\n md: \"px-2 py-0.5 text-xs\",\n },\n },\n defaultVariants: {\n variant: \"neutral\",\n size: \"md\",\n },\n },\n);\n\nexport interface BadgeProps\n extends\n React.HTMLAttributes<HTMLSpanElement>,\n VariantProps<typeof badgeVariants> {}\n\nexport const Badge = React.forwardRef<HTMLSpanElement, BadgeProps>(\n ({ className, variant, size, ...props }, ref) => (\n <span\n ref={ref}\n className={cn(badgeVariants({ variant, size }), className)}\n {...props}\n />\n ),\n);\n\nBadge.displayName = \"Badge\";\n\nexport { badgeVariants };\n"],"mappings":";;;;;AAAA,YAAY,WAAW;AACvB,SAAS,WAA8B;AAgDnC;AAhCJ,IAAM,gBAAgB;AAAA,EACpB;AAAA,EACA;AAAA,IACE,UAAU;AAAA,MACR,SAAS;AAAA,QACP,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,QACT,QAAQ;AAAA,QACR,MAAM;AAAA,MACR;AAAA,MACA,MAAM;AAAA;AAAA,QAEJ,IAAI;AAAA;AAAA,QAEJ,IAAI;AAAA,MACN;AAAA,IACF;AAAA,IACA,iBAAiB;AAAA,MACf,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AAAA,EACF;AACF;AAOO,IAAM,QAAc;AAAA,EACzB,CAAC,EAAE,WAAW,SAAS,MAAM,GAAG,MAAM,GAAG,QACvC;AAAA,IAAC;AAAA;AAAA,MACC;AAAA,MACA,WAAW,GAAG,cAAc,EAAE,SAAS,KAAK,CAAC,GAAG,SAAS;AAAA,MACxD,GAAG;AAAA;AAAA,EACN;AAEJ;AAEA,MAAM,cAAc;","names":[]}
@@ -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":[]}