@crewlethq/ui 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (497) hide show
  1. package/README.md +1714 -14
  2. package/dist/Accordion/Accordion.d.ts +54 -0
  3. package/dist/Accordion/index.d.ts +4 -0
  4. package/dist/Accordion/index.js +6 -0
  5. package/dist/Accordion-ZI2WLD5C.css +87 -0
  6. package/dist/AddPill/AddPill.d.ts +114 -0
  7. package/dist/AddPill/index.d.ts +3 -0
  8. package/dist/AddPill/index.js +3 -0
  9. package/dist/AddPill-DFWXYLIQ.css +434 -0
  10. package/dist/Announcer/Announcer.d.ts +15 -0
  11. package/dist/Announcer/index.d.ts +2 -0
  12. package/dist/Announcer/index.js +3 -0
  13. package/dist/AppShell/AppShell.d.ts +193 -0
  14. package/dist/AppShell/index.d.ts +3 -0
  15. package/dist/AppShell/index.js +6 -0
  16. package/dist/AppShell-NVE6LHNI.css +395 -0
  17. package/dist/Avatar/Avatar.d.ts +104 -0
  18. package/dist/Avatar/index.d.ts +3 -0
  19. package/dist/Avatar/index.js +2 -0
  20. package/dist/Avatar-P2KRQM2B.css +156 -0
  21. package/dist/BrandLockup/BrandLockup.d.ts +41 -0
  22. package/dist/BrandLockup/index.d.ts +3 -0
  23. package/dist/BrandLockup/index.js +2 -0
  24. package/dist/BrandLockup-6ISDEHPJ.css +108 -0
  25. package/dist/Button/Button.d.ts +88 -0
  26. package/dist/Button/index.d.ts +3 -0
  27. package/dist/Button/index.js +3 -0
  28. package/dist/Button-D4J6B4MA.css +302 -0
  29. package/dist/CalendarGrid/CalendarGrid.d.ts +87 -0
  30. package/dist/CalendarGrid/index.d.ts +3 -0
  31. package/dist/CalendarGrid/index.js +2 -0
  32. package/dist/CalendarGrid-V6TQ5QKS.css +128 -0
  33. package/dist/Callout/Callout.d.ts +47 -0
  34. package/dist/Callout/index.d.ts +3 -0
  35. package/dist/Callout/index.js +2 -0
  36. package/dist/Callout-S7QDWHDL.css +111 -0
  37. package/dist/Canvas/Canvas.d.ts +234 -0
  38. package/dist/Canvas/geometry.d.ts +249 -0
  39. package/dist/Canvas/index.d.ts +5 -0
  40. package/dist/Canvas/index.js +8 -0
  41. package/dist/Canvas/layerNode.d.ts +20 -0
  42. package/dist/Canvas-Q6JETCWR.css +315 -0
  43. package/dist/Card/Card.d.ts +164 -0
  44. package/dist/Card/headerSlot.d.ts +65 -0
  45. package/dist/Card/index.d.ts +5 -0
  46. package/dist/Card/index.js +6 -0
  47. package/dist/Card-RLR254LD.css +450 -0
  48. package/dist/Charts/ActivityStrip.d.ts +45 -0
  49. package/dist/Charts/BarList.d.ts +51 -0
  50. package/dist/Charts/Legend.d.ts +32 -0
  51. package/dist/Charts/StackedBar.d.ts +38 -0
  52. package/dist/Charts/TimeSeries.d.ts +92 -0
  53. package/dist/Charts/dataColor.d.ts +20 -0
  54. package/dist/Charts/index.d.ts +12 -0
  55. package/dist/Charts/index.js +3 -0
  56. package/dist/Charts-D7G2BSXH.css +277 -0
  57. package/dist/Checkbox/Checkbox.d.ts +45 -0
  58. package/dist/Checkbox/index.d.ts +3 -0
  59. package/dist/Checkbox/index.js +2 -0
  60. package/dist/Checkbox-VGW26YLQ.css +111 -0
  61. package/dist/CodeBlock/CodeBlock.d.ts +119 -0
  62. package/dist/CodeBlock/index.d.ts +3 -0
  63. package/dist/CodeBlock/index.js +7 -0
  64. package/dist/CodeBlock-GYNJ2HI6.css +188 -0
  65. package/dist/Combobox/Combobox.d.ts +89 -0
  66. package/dist/Combobox/index.d.ts +3 -0
  67. package/dist/Combobox/index.js +8 -0
  68. package/dist/Combobox-4FUVYIE7.css +80 -0
  69. package/dist/CommandPalette/CommandPalette.d.ts +77 -0
  70. package/dist/CommandPalette/index.d.ts +3 -0
  71. package/dist/CommandPalette/index.js +11 -0
  72. package/dist/CommandPalette-CSJDUCQA.css +115 -0
  73. package/dist/Container/Container.d.ts +17 -0
  74. package/dist/Container/index.d.ts +3 -0
  75. package/dist/Container/index.js +2 -0
  76. package/dist/Container-3TSKMGNI.css +52 -0
  77. package/dist/Copyable/CopyButton.d.ts +64 -0
  78. package/dist/Copyable/Copyable.d.ts +42 -0
  79. package/dist/Copyable/index.d.ts +5 -0
  80. package/dist/Copyable/index.js +6 -0
  81. package/dist/Copyable-5PNRFOLS.css +125 -0
  82. package/dist/Count/Count.d.ts +24 -0
  83. package/dist/Count/index.d.ts +3 -0
  84. package/dist/Count/index.js +3 -0
  85. package/dist/Count-ZVCJ6VPD.css +20 -0
  86. package/dist/DataTable/CopyableCell.d.ts +12 -0
  87. package/dist/DataTable/DataTable.d.ts +615 -0
  88. package/dist/DataTable/RowActionsMenu.d.ts +7 -0
  89. package/dist/DataTable/index.d.ts +7 -0
  90. package/dist/DataTable/index.js +14 -0
  91. package/dist/DataTable-ITNACQ2X.css +2020 -0
  92. package/dist/DataView/DataView.d.ts +109 -0
  93. package/dist/DataView/DataViewToolbar.d.ts +87 -0
  94. package/dist/DataView/FilterAxisBar.d.ts +103 -0
  95. package/dist/DataView/columns.d.ts +35 -0
  96. package/dist/DataView/filters.d.ts +184 -0
  97. package/dist/DataView/index.d.ts +11 -0
  98. package/dist/DataView/index.js +25 -0
  99. package/dist/DataView-256YF32J.css +441 -0
  100. package/dist/DateTimePicker/DateTimePicker.d.ts +91 -0
  101. package/dist/DateTimePicker/index.d.ts +3 -0
  102. package/dist/DateTimePicker/index.js +9 -0
  103. package/dist/DateTimePicker-22VNXRWC.css +199 -0
  104. package/dist/DescriptionList/DescriptionList.d.ts +36 -0
  105. package/dist/DescriptionList/index.d.ts +3 -0
  106. package/dist/DescriptionList/index.js +2 -0
  107. package/dist/DescriptionList-YYY3V3O6.css +59 -0
  108. package/dist/DiffList/DiffList.d.ts +47 -0
  109. package/dist/DiffList/index.d.ts +3 -0
  110. package/dist/DiffList/index.js +3 -0
  111. package/dist/DiffList-3EX64AAE.css +94 -0
  112. package/dist/Disclosure/Disclosure.d.ts +56 -0
  113. package/dist/Disclosure/anatomy.d.ts +43 -0
  114. package/dist/Disclosure/index.d.ts +5 -0
  115. package/dist/Disclosure/index.js +6 -0
  116. package/dist/Disclosure-VKBJJIZ7.css +176 -0
  117. package/dist/EmptyState/EmptyState.d.ts +39 -0
  118. package/dist/EmptyState/index.d.ts +3 -0
  119. package/dist/EmptyState/index.js +3 -0
  120. package/dist/EmptyState-C6HRBGT7.css +73 -0
  121. package/dist/EmptyValue/EmptyValue.d.ts +31 -0
  122. package/dist/EmptyValue/index.d.ts +3 -0
  123. package/dist/EmptyValue/index.js +3 -0
  124. package/dist/EmptyValue-4B6ODXHT.css +12 -0
  125. package/dist/EntityChip/EntityChip.d.ts +40 -0
  126. package/dist/EntityChip/index.d.ts +3 -0
  127. package/dist/EntityChip/index.js +3 -0
  128. package/dist/EntityChip-LUCPZBBG.css +64 -0
  129. package/dist/ErrorBoundary/ErrorBoundary.d.ts +58 -0
  130. package/dist/ErrorBoundary/index.d.ts +3 -0
  131. package/dist/ErrorBoundary/index.js +4 -0
  132. package/dist/ErrorBoundary-ME36CQTA.css +59 -0
  133. package/dist/Eyebrow/Eyebrow.d.ts +18 -0
  134. package/dist/Eyebrow/index.d.ts +3 -0
  135. package/dist/Eyebrow/index.js +2 -0
  136. package/dist/Eyebrow-5NREGHQH.css +46 -0
  137. package/dist/FilterChip/FilterChip.d.ts +100 -0
  138. package/dist/FilterChip/index.d.ts +3 -0
  139. package/dist/FilterChip/index.js +3 -0
  140. package/dist/FilterChip-GTGLLOXE.css +107 -0
  141. package/dist/Fullscreen/index.d.ts +2 -0
  142. package/dist/Fullscreen/index.js +1 -0
  143. package/dist/Fullscreen/useFullscreen.d.ts +32 -0
  144. package/dist/IconButton/IconButton.d.ts +45 -0
  145. package/dist/IconButton/index.d.ts +3 -0
  146. package/dist/IconButton/index.js +3 -0
  147. package/dist/IconButton-VQGNMJDI.css +158 -0
  148. package/dist/ImageUpload/ImageUpload.d.ts +37 -0
  149. package/dist/ImageUpload/index.d.ts +3 -0
  150. package/dist/ImageUpload/index.js +3 -0
  151. package/dist/ImageUpload-HKFIZR6D.css +193 -0
  152. package/dist/InlineCode/InlineCode.d.ts +37 -0
  153. package/dist/InlineCode/index.d.ts +3 -0
  154. package/dist/InlineCode/index.js +2 -0
  155. package/dist/InlineCode-6WSR5COL.css +65 -0
  156. package/dist/Input/FormField.d.ts +61 -0
  157. package/dist/Input/FormRow.d.ts +6 -0
  158. package/dist/Input/FormSection.d.ts +28 -0
  159. package/dist/Input/Input.d.ts +98 -0
  160. package/dist/Input/Label.d.ts +31 -0
  161. package/dist/Input/ReadOnlyField.d.ts +30 -0
  162. package/dist/Input/Textarea.d.ts +16 -0
  163. package/dist/Input/index.d.ts +15 -0
  164. package/dist/Input/index.js +5 -0
  165. package/dist/Input-2PU5CPKK.css +616 -0
  166. package/dist/Kbd/Kbd.d.ts +40 -0
  167. package/dist/Kbd/index.d.ts +3 -0
  168. package/dist/Kbd/index.js +3 -0
  169. package/dist/Kbd-W66MXQI5.css +61 -0
  170. package/dist/Layer/LayerHost.d.ts +23 -0
  171. package/dist/Layer/index.d.ts +7 -0
  172. package/dist/Layer/index.js +3 -0
  173. package/dist/Layer/place.d.ts +63 -0
  174. package/dist/Layer/stack.d.ts +159 -0
  175. package/dist/Layer-NAFSGCFA.css +23 -0
  176. package/dist/Layout/Layout.d.ts +104 -0
  177. package/dist/Layout/index.d.ts +3 -0
  178. package/dist/Layout/index.js +2 -0
  179. package/dist/Layout-2ZCV3IGF.css +171 -0
  180. package/dist/Link/Link.d.ts +45 -0
  181. package/dist/Link/index.d.ts +3 -0
  182. package/dist/Link/index.js +3 -0
  183. package/dist/Link-6BEFNMN2.css +80 -0
  184. package/dist/List/List.d.ts +73 -0
  185. package/dist/List/index.d.ts +3 -0
  186. package/dist/List/index.js +3 -0
  187. package/dist/List-XTHESXLR.css +161 -0
  188. package/dist/ListInput/ListInput.d.ts +68 -0
  189. package/dist/ListInput/index.d.ts +3 -0
  190. package/dist/ListInput/index.js +10 -0
  191. package/dist/ListInput-O7LJKOIG.css +147 -0
  192. package/dist/Listbox/index.d.ts +5 -0
  193. package/dist/Listbox/index.js +2 -0
  194. package/dist/Listbox/useListbox.d.ts +97 -0
  195. package/dist/Listbox/useOptionKeys.d.ts +64 -0
  196. package/dist/Listbox-X2DOTSQA.css +110 -0
  197. package/dist/Menu/Menu.d.ts +118 -0
  198. package/dist/Menu/index.d.ts +3 -0
  199. package/dist/Menu/index.js +7 -0
  200. package/dist/Menu-6DE7I66I.css +222 -0
  201. package/dist/Meter/Meter.d.ts +126 -0
  202. package/dist/Meter/index.d.ts +3 -0
  203. package/dist/Meter/index.js +3 -0
  204. package/dist/Meter-SKOOXQBC.css +87 -0
  205. package/dist/Modal/ConfirmModal.d.ts +74 -0
  206. package/dist/Modal/Modal.d.ts +154 -0
  207. package/dist/Modal/index.d.ts +5 -0
  208. package/dist/Modal/index.js +8 -0
  209. package/dist/Modal-XYQJ4BL6.css +484 -0
  210. package/dist/NewItemsNotice/NewItemsNotice.d.ts +28 -0
  211. package/dist/NewItemsNotice/index.d.ts +3 -0
  212. package/dist/NewItemsNotice/index.js +4 -0
  213. package/dist/NewItemsNotice-T4DSSPVV.css +60 -0
  214. package/dist/OrgLabel/OrgLabel.d.ts +117 -0
  215. package/dist/OrgLabel/index.d.ts +3 -0
  216. package/dist/OrgLabel/index.js +2 -0
  217. package/dist/OrgLabel-D7U6QJZD.css +293 -0
  218. package/dist/OrgNode/OrgNode.d.ts +103 -0
  219. package/dist/OrgNode/index.d.ts +3 -0
  220. package/dist/OrgNode/index.js +3 -0
  221. package/dist/OrgNode-BT7T2Q2X.css +214 -0
  222. package/dist/OrgTable/OrgTable.d.ts +113 -0
  223. package/dist/OrgTable/index.d.ts +3 -0
  224. package/dist/OrgTable/index.js +11 -0
  225. package/dist/OrgTable-VWMFC3TB.css +360 -0
  226. package/dist/PageHeader/PageHeader.d.ts +35 -0
  227. package/dist/PageHeader/index.d.ts +3 -0
  228. package/dist/PageHeader/index.js +3 -0
  229. package/dist/PageHeader-6EYWQXRE.css +88 -0
  230. package/dist/Popover/Popover.d.ts +91 -0
  231. package/dist/Popover/index.d.ts +3 -0
  232. package/dist/Popover/index.js +4 -0
  233. package/dist/Popover-WJGCF3ZQ.css +185 -0
  234. package/dist/PricingCard/PricingCard.d.ts +56 -0
  235. package/dist/PricingCard/index.d.ts +3 -0
  236. package/dist/PricingCard/index.js +4 -0
  237. package/dist/PricingCard-JFI7RYXP.css +278 -0
  238. package/dist/Prose/Prose.d.ts +33 -0
  239. package/dist/Prose/index.d.ts +3 -0
  240. package/dist/Prose/index.js +2 -0
  241. package/dist/Prose-BTXNFT2Q.css +99 -0
  242. package/dist/RelativeTime/RelativeTime.d.ts +31 -0
  243. package/dist/RelativeTime/clock.d.ts +9 -0
  244. package/dist/RelativeTime/format.d.ts +38 -0
  245. package/dist/RelativeTime/index.d.ts +6 -0
  246. package/dist/RelativeTime/index.js +4 -0
  247. package/dist/RelativeTime-EBAQPGO2.css +10 -0
  248. package/dist/SearchTrigger/SearchTrigger.d.ts +29 -0
  249. package/dist/SearchTrigger/index.d.ts +3 -0
  250. package/dist/SearchTrigger/index.js +2 -0
  251. package/dist/SearchTrigger-QXOYAUUY.css +79 -0
  252. package/dist/Section/Section.d.ts +41 -0
  253. package/dist/Section/index.d.ts +3 -0
  254. package/dist/Section/index.js +3 -0
  255. package/dist/Section-QRUPRIQ2.css +127 -0
  256. package/dist/SegmentedControl/SegmentedControl.d.ts +101 -0
  257. package/dist/SegmentedControl/index.d.ts +4 -0
  258. package/dist/SegmentedControl/index.js +5 -0
  259. package/dist/SegmentedControl-X3BI3XU4.css +68 -0
  260. package/dist/Select/Select.d.ts +153 -0
  261. package/dist/Select/index.d.ts +3 -0
  262. package/dist/Select/index.js +5 -0
  263. package/dist/Select-2X77FSWR.css +350 -0
  264. package/dist/Shortcut/index.d.ts +2 -0
  265. package/dist/Shortcut/index.js +4 -0
  266. package/dist/Shortcut/useShortcut.d.ts +68 -0
  267. package/dist/SidebarNav/SidebarNav.d.ts +117 -0
  268. package/dist/SidebarNav/index.d.ts +3 -0
  269. package/dist/SidebarNav/index.js +3 -0
  270. package/dist/SidebarNav-QGSFKZYA.css +319 -0
  271. package/dist/Skeleton/Skeleton.d.ts +50 -0
  272. package/dist/Skeleton/index.d.ts +3 -0
  273. package/dist/Skeleton/index.js +3 -0
  274. package/dist/Skeleton-V735HRGM.css +421 -0
  275. package/dist/StatCard/StatCard.d.ts +56 -0
  276. package/dist/StatCard/index.d.ts +3 -0
  277. package/dist/StatCard/index.js +3 -0
  278. package/dist/StatCard-KYPKZUWX.css +111 -0
  279. package/dist/StatGroup/StatGroup.d.ts +20 -0
  280. package/dist/StatGroup/index.d.ts +3 -0
  281. package/dist/StatGroup/index.js +2 -0
  282. package/dist/StatGroup-TGB6AEKM.css +80 -0
  283. package/dist/StatusDot/StatusDot.d.ts +25 -0
  284. package/dist/StatusDot/index.d.ts +3 -0
  285. package/dist/StatusDot/index.js +2 -0
  286. package/dist/StatusDot-HA2L2FSX.css +84 -0
  287. package/dist/Switch/Switch.d.ts +38 -0
  288. package/dist/Switch/index.d.ts +3 -0
  289. package/dist/Switch/index.js +2 -0
  290. package/dist/Switch-U4OWK4CL.css +169 -0
  291. package/dist/Table/Table.d.ts +51 -0
  292. package/dist/Table/index.d.ts +3 -0
  293. package/dist/Table/index.js +3 -0
  294. package/dist/Table-MDOLVUAW.css +159 -0
  295. package/dist/Tabs/Tabs.d.ts +97 -0
  296. package/dist/Tabs/index.d.ts +3 -0
  297. package/dist/Tabs/index.js +4 -0
  298. package/dist/Tabs/roving.d.ts +67 -0
  299. package/dist/Tabs-VLICJFVB.css +275 -0
  300. package/dist/Tag/Tag.d.ts +107 -0
  301. package/dist/Tag/index.d.ts +3 -0
  302. package/dist/Tag/index.js +3 -0
  303. package/dist/Tag-VNZEYLB6.css +406 -0
  304. package/dist/TagGroup/TagGroup.d.ts +41 -0
  305. package/dist/TagGroup/index.d.ts +3 -0
  306. package/dist/TagGroup/index.js +4 -0
  307. package/dist/TagGroup-APC2EFH4.css +75 -0
  308. package/dist/TagsInput/TagsInput.d.ts +112 -0
  309. package/dist/TagsInput/index.d.ts +3 -0
  310. package/dist/TagsInput/index.js +10 -0
  311. package/dist/TagsInput-W4P36PRJ.css +168 -0
  312. package/dist/Text/Text.d.ts +67 -0
  313. package/dist/Text/index.d.ts +3 -0
  314. package/dist/Text/index.js +2 -0
  315. package/dist/Text-XIPYYZX2.css +154 -0
  316. package/dist/ThemeSwitcher/ThemeSwitcher.d.ts +47 -0
  317. package/dist/ThemeSwitcher/index.d.ts +4 -0
  318. package/dist/ThemeSwitcher/index.js +6 -0
  319. package/dist/ThemeSwitcher/preferences.d.ts +51 -0
  320. package/dist/TimeWindowPicker/TimeWindowPicker.d.ts +231 -0
  321. package/dist/TimeWindowPicker/index.d.ts +3 -0
  322. package/dist/TimeWindowPicker/index.js +13 -0
  323. package/dist/TimeWindowPicker/mask.d.ts +82 -0
  324. package/dist/TimeWindowPicker-7I7KWCMU.css +403 -0
  325. package/dist/Timeline/Timeline.d.ts +51 -0
  326. package/dist/Timeline/index.d.ts +3 -0
  327. package/dist/Timeline/index.js +3 -0
  328. package/dist/Timeline-27SYKOFX.css +144 -0
  329. package/dist/Toaster/Toaster.d.ts +108 -0
  330. package/dist/Toaster/index.d.ts +3 -0
  331. package/dist/Toaster/index.js +5 -0
  332. package/dist/Toaster-NVDCEIP5.css +306 -0
  333. package/dist/Toolbar/Toolbar.d.ts +62 -0
  334. package/dist/Toolbar/index.d.ts +3 -0
  335. package/dist/Toolbar/index.js +4 -0
  336. package/dist/Toolbar-IP6LXN45.css +48 -0
  337. package/dist/Tooltip/Tooltip.d.ts +51 -0
  338. package/dist/Tooltip/index.d.ts +3 -0
  339. package/dist/Tooltip/index.js +4 -0
  340. package/dist/Tooltip-LKQF7CE4.css +71 -0
  341. package/dist/Tree/index.d.ts +8 -0
  342. package/dist/Tree/index.js +1 -0
  343. package/dist/Tree/layout.d.ts +160 -0
  344. package/dist/Tree/model.d.ts +110 -0
  345. package/dist/Tree/useMeasuredSizes.d.ts +61 -0
  346. package/dist/Tree/useTreeState.d.ts +89 -0
  347. package/dist/TreeCanvas/TreeCanvas.d.ts +384 -0
  348. package/dist/TreeCanvas/index.d.ts +3 -0
  349. package/dist/TreeCanvas/index.js +10 -0
  350. package/dist/TreeCanvas/motion.d.ts +59 -0
  351. package/dist/TreeCanvas-WBNQPHC7.css +1098 -0
  352. package/dist/TreeGrid/TreeGrid.d.ts +142 -0
  353. package/dist/TreeGrid/index.d.ts +3 -0
  354. package/dist/TreeGrid/index.js +8 -0
  355. package/dist/TreeGrid-NSW7VMOX.css +132 -0
  356. package/dist/VisuallyHidden/VisuallyHidden.d.ts +23 -0
  357. package/dist/VisuallyHidden/index.d.ts +3 -0
  358. package/dist/VisuallyHidden/index.js +2 -0
  359. package/dist/VisuallyHidden-L552H7OA.css +21 -0
  360. package/dist/chunk-22O7LAW2.js +41 -0
  361. package/dist/chunk-23DHIMJA.js +38 -0
  362. package/dist/chunk-2EOKFKHX.js +263 -0
  363. package/dist/chunk-2HQR4U4D.js +143 -0
  364. package/dist/chunk-2PRH365R.js +74 -0
  365. package/dist/chunk-2ZQMA2WB.js +57 -0
  366. package/dist/chunk-34VGEDVV.js +907 -0
  367. package/dist/chunk-3CZ7XAVY.js +139 -0
  368. package/dist/chunk-3EYF6HKB.js +135 -0
  369. package/dist/chunk-3QRYCPXQ.js +116 -0
  370. package/dist/chunk-3ZP55QZE.js +40 -0
  371. package/dist/chunk-53DVJCVW.js +14 -0
  372. package/dist/chunk-5C5YMZQU.js +51 -0
  373. package/dist/chunk-5EKIWVOR.js +132 -0
  374. package/dist/chunk-5KPQ6D7N.js +54 -0
  375. package/dist/chunk-5SDBMKUO.js +297 -0
  376. package/dist/chunk-5VNUYO76.js +62 -0
  377. package/dist/chunk-5WS6L6FA.js +114 -0
  378. package/dist/chunk-6ZFVFA56.js +108 -0
  379. package/dist/chunk-7EM5T4ME.js +39 -0
  380. package/dist/chunk-7FZ53WDJ.js +163 -0
  381. package/dist/chunk-7HPRF5XN.js +525 -0
  382. package/dist/chunk-7RPGY3JE.js +43 -0
  383. package/dist/chunk-A2UXBCLO.js +48 -0
  384. package/dist/chunk-AV2DXAEL.js +27 -0
  385. package/dist/chunk-BHHIIK3M.js +17 -0
  386. package/dist/chunk-BZFWDIV6.js +9 -0
  387. package/dist/chunk-BZYDZBAN.js +123 -0
  388. package/dist/chunk-CMEE37QJ.js +628 -0
  389. package/dist/chunk-D35VTV2Z.js +106 -0
  390. package/dist/chunk-DD5BBGBE.js +162 -0
  391. package/dist/chunk-EN6K2IFA.js +256 -0
  392. package/dist/chunk-ESCO4XYW.js +144 -0
  393. package/dist/chunk-EUCC37WY.js +14 -0
  394. package/dist/chunk-EV6IMYJ6.js +62 -0
  395. package/dist/chunk-F27EPQ7D.js +68 -0
  396. package/dist/chunk-F4PJZ4ED.js +245 -0
  397. package/dist/chunk-F6OE5FH3.js +49 -0
  398. package/dist/chunk-FBZAKNSX.js +263 -0
  399. package/dist/chunk-FEFTGVMF.js +46 -0
  400. package/dist/chunk-GTOUGQQK.js +147 -0
  401. package/dist/chunk-GXQ3B46I.js +216 -0
  402. package/dist/chunk-H4FBTAYS.js +130 -0
  403. package/dist/chunk-HOXGBLEY.js +37 -0
  404. package/dist/chunk-HQNHV7D6.js +54 -0
  405. package/dist/chunk-HZ5XZNPS.js +98 -0
  406. package/dist/chunk-IAJLHVUA.js +28 -0
  407. package/dist/chunk-IJ7QXNIJ.js +1 -0
  408. package/dist/chunk-ITHXJHBB.js +280 -0
  409. package/dist/chunk-JCSA3XVH.js +32 -0
  410. package/dist/chunk-KLJCCMF3.js +97 -0
  411. package/dist/chunk-LWKJQTOM.js +77 -0
  412. package/dist/chunk-MNAXYY3R.js +215 -0
  413. package/dist/chunk-NDCATSW7.js +229 -0
  414. package/dist/chunk-NN62JQLR.js +29 -0
  415. package/dist/chunk-NPZY6YYL.js +251 -0
  416. package/dist/chunk-NTRZGNEX.js +529 -0
  417. package/dist/chunk-O6232G37.js +273 -0
  418. package/dist/chunk-O67VF5MS.js +58 -0
  419. package/dist/chunk-ODEQTPFM.js +6 -0
  420. package/dist/chunk-OFHBK7RI.js +498 -0
  421. package/dist/chunk-PNFASD6F.js +98 -0
  422. package/dist/chunk-PQADV6P6.js +19 -0
  423. package/dist/chunk-PZSWF74H.js +15 -0
  424. package/dist/chunk-Q4ZPPART.js +26 -0
  425. package/dist/chunk-QEUY3A5P.js +186 -0
  426. package/dist/chunk-QNCVHRZL.js +17 -0
  427. package/dist/chunk-QVE4GJAT.js +15 -0
  428. package/dist/chunk-QYOFACSP.js +36 -0
  429. package/dist/chunk-RA2ULG6H.js +29 -0
  430. package/dist/chunk-RDGD7Z7I.js +350 -0
  431. package/dist/chunk-RK4XPHVG.js +16 -0
  432. package/dist/chunk-RSAQOKH3.js +38 -0
  433. package/dist/chunk-S2XHHEW4.js +84 -0
  434. package/dist/chunk-S4GZWSKR.js +137 -0
  435. package/dist/chunk-SFZOKW2G.js +52 -0
  436. package/dist/chunk-UG46QBLT.js +325 -0
  437. package/dist/chunk-UOGSZKLT.js +134 -0
  438. package/dist/chunk-V2XMNLMS.js +52 -0
  439. package/dist/chunk-W4ENKCXJ.js +40 -0
  440. package/dist/chunk-W5J7R5EB.js +107 -0
  441. package/dist/chunk-WNI2WSJF.js +27 -0
  442. package/dist/chunk-WOYYD2YR.js +823 -0
  443. package/dist/chunk-WQX73TWF.js +1652 -0
  444. package/dist/chunk-XDTYBF2F.js +36 -0
  445. package/dist/chunk-XN4JBAN3.js +32 -0
  446. package/dist/chunk-XV6WRMBC.js +211 -0
  447. package/dist/chunk-YLYOWZ4V.js +33 -0
  448. package/dist/chunk-YODNNSZL.js +63 -0
  449. package/dist/chunk-ZCR6YGQ4.js +1 -0
  450. package/dist/chunk-ZFATEERP.js +144 -0
  451. package/dist/generated/index.d.ts +83 -0
  452. package/dist/generated/index.js +91 -0
  453. package/dist/index.d.ts +1 -1217
  454. package/dist/index.js +91 -4518
  455. package/dist/styles.css +14898 -4274
  456. package/dist/utils/cx.d.ts +10 -0
  457. package/dist/utils/headingLevel.d.ts +16 -0
  458. package/dist/utils/index.d.ts +6 -0
  459. package/dist/utils/index.js +4 -0
  460. package/dist/utils/tone.d.ts +25 -0
  461. package/dist/utils/useClipboard.d.ts +82 -0
  462. package/package.json +18 -10
  463. package/scripts/bundle-css.mjs +25 -0
  464. package/scripts/check-css-literals.mjs +166 -0
  465. package/scripts/check-css-variables.mjs +184 -0
  466. package/scripts/check-css.mjs +27 -0
  467. package/scripts/css-source.mjs +67 -0
  468. package/scripts/write-barrel.mjs +60 -0
  469. package/dist/Accordion-NIMJQIOI.css +0 -88
  470. package/dist/AppShell-ZW3NSFBP.css +0 -51
  471. package/dist/Avatar-V6NG6Q45.css +0 -67
  472. package/dist/Button-XGCTXXYP.css +0 -174
  473. package/dist/Callout-LEGDIMAM.css +0 -52
  474. package/dist/Card-BOZGBML7.css +0 -86
  475. package/dist/Checkbox-OCUICIIZ.css +0 -58
  476. package/dist/CodeBlock-IEMBNIQC.css +0 -90
  477. package/dist/Container-LTKBA2FW.css +0 -21
  478. package/dist/Copyable-IB57OTHZ.css +0 -123
  479. package/dist/DataTable-3GALZUW2.css +0 -1593
  480. package/dist/DateTimePicker-NMSSGVPK.css +0 -312
  481. package/dist/Eyebrow-AVBORAS2.css +0 -30
  482. package/dist/IconButton-GB4PJDG2.css +0 -82
  483. package/dist/ImageUpload-3HQDF2KC.css +0 -158
  484. package/dist/Input-ZHUTN7CU.css +0 -210
  485. package/dist/Kbd-Q77CDZY7.css +0 -23
  486. package/dist/Modal-FJVG66S4.css +0 -138
  487. package/dist/Popover-FUFULUE2.css +0 -123
  488. package/dist/PricingCard-NKYV3JHA.css +0 -238
  489. package/dist/Section-665ZIPG6.css +0 -20
  490. package/dist/Select-GPRF7PRB.css +0 -294
  491. package/dist/Skeleton-3WEFLRDN.css +0 -378
  492. package/dist/StatCard-RTJIVTRB.css +0 -50
  493. package/dist/Table-AR3TRY2E.css +0 -89
  494. package/dist/Tabs-XZ4JIH3E.css +0 -153
  495. package/dist/Tag-R5QDJLYJ.css +0 -163
  496. package/dist/TimeWindowPicker-L3G5Q7UT.css +0 -629
  497. package/dist/Toaster-2O65VOR4.css +0 -264
package/dist/index.js CHANGED
@@ -1,4518 +1,91 @@
1
- import './Accordion-NIMJQIOI.css';
2
- import React, { createContext, forwardRef, useId, useRef, useImperativeHandle, useEffect, useState, useCallback, useMemo, useContext, Children, isValidElement, cloneElement, useLayoutEffect, Fragment as Fragment$1 } from 'react';
3
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
- import './AppShell-ZW3NSFBP.css';
5
- import './Avatar-V6NG6Q45.css';
6
- import './Button-XGCTXXYP.css';
7
- import './Callout-LEGDIMAM.css';
8
- import './Card-BOZGBML7.css';
9
- import './CodeBlock-IEMBNIQC.css';
10
- import './Copyable-IB57OTHZ.css';
11
- import './Container-LTKBA2FW.css';
12
- import './DataTable-3GALZUW2.css';
13
- import { createPortal } from 'react-dom';
14
- import './DateTimePicker-NMSSGVPK.css';
15
- import './Eyebrow-AVBORAS2.css';
16
- import './IconButton-GB4PJDG2.css';
17
- import './ImageUpload-3HQDF2KC.css';
18
- import './Checkbox-OCUICIIZ.css';
19
- import './Input-ZHUTN7CU.css';
20
- import './Kbd-Q77CDZY7.css';
21
- import './Modal-FJVG66S4.css';
22
- import './Popover-FUFULUE2.css';
23
- import './PricingCard-NKYV3JHA.css';
24
- import './Section-665ZIPG6.css';
25
- import './Select-GPRF7PRB.css';
26
- import './Skeleton-3WEFLRDN.css';
27
- import './StatCard-RTJIVTRB.css';
28
- import './Table-AR3TRY2E.css';
29
- import './Tabs-XZ4JIH3E.css';
30
- import './TimeWindowPicker-L3G5Q7UT.css';
31
- import './Tag-R5QDJLYJ.css';
32
- import './Toaster-2O65VOR4.css';
33
-
34
- // src/Accordion/index.ts
35
- var AccordionContext = createContext(null);
36
- var toArray = (input) => {
37
- if (input === void 0) return [];
38
- return Array.isArray(input) ? input : [input];
39
- };
40
- var AccordionRoot = ({
41
- type = "single",
42
- collapsible = true,
43
- defaultValue,
44
- value,
45
- onValueChange,
46
- className = "",
47
- children,
48
- ...rest
49
- }) => {
50
- const isControlled = value !== void 0;
51
- const [internal, setInternal] = useState(() => toArray(defaultValue));
52
- const open = isControlled ? toArray(value) : internal;
53
- const commit = useCallback(
54
- (next) => {
55
- if (!isControlled) setInternal(next);
56
- if (onValueChange) onValueChange(type === "single" ? next[0] ?? "" : next);
57
- },
58
- [isControlled, onValueChange, type]
59
- );
60
- const isOpen = useCallback((item) => open.includes(item), [open]);
61
- const toggle = useCallback(
62
- (item) => {
63
- const currentlyOpen = open.includes(item);
64
- if (type === "single") {
65
- if (currentlyOpen) commit(collapsible ? [] : [item]);
66
- else commit([item]);
67
- return;
68
- }
69
- commit(currentlyOpen ? open.filter((entry) => entry !== item) : [...open, item]);
70
- },
71
- [open, type, collapsible, commit]
72
- );
73
- const ctx = useMemo(() => ({ isOpen, toggle }), [isOpen, toggle]);
74
- const classes = ["crewlet-accordion", className].filter(Boolean).join(" ");
75
- return /* @__PURE__ */ jsx(AccordionContext.Provider, { value: ctx, children: /* @__PURE__ */ jsx("div", { ...rest, className: classes, children }) });
76
- };
77
- var moveFocus = (current, direction) => {
78
- const root = current.closest(".crewlet-accordion");
79
- if (!root) return;
80
- const triggers = Array.from(
81
- root.querySelectorAll(".crewlet-accordion__trigger:not([disabled])")
82
- );
83
- if (triggers.length === 0) return;
84
- const index = triggers.indexOf(current);
85
- const target = direction === "next" ? triggers[(index + 1) % triggers.length] : direction === "prev" ? triggers[(index - 1 + triggers.length) % triggers.length] : direction === "first" ? triggers[0] : triggers[triggers.length - 1];
86
- target?.focus();
87
- };
88
- var AccordionItem = ({
89
- value,
90
- title,
91
- disabled = false,
92
- className = "",
93
- children,
94
- ...rest
95
- }) => {
96
- const ctx = useContext(AccordionContext);
97
- if (!ctx) {
98
- throw new Error("AccordionItem must be rendered inside an Accordion.");
99
- }
100
- const open = ctx.isOpen(value);
101
- const rid = useId();
102
- const triggerId = `${rid}-trigger`;
103
- const panelId = `${rid}-panel`;
104
- const onKeyDown = (event) => {
105
- switch (event.key) {
106
- case "ArrowDown":
107
- event.preventDefault();
108
- moveFocus(event.currentTarget, "next");
109
- break;
110
- case "ArrowUp":
111
- event.preventDefault();
112
- moveFocus(event.currentTarget, "prev");
113
- break;
114
- case "Home":
115
- event.preventDefault();
116
- moveFocus(event.currentTarget, "first");
117
- break;
118
- case "End":
119
- event.preventDefault();
120
- moveFocus(event.currentTarget, "last");
121
- break;
122
- }
123
- };
124
- const classes = [
125
- "crewlet-accordion__item",
126
- open ? "is-open" : "",
127
- disabled ? "is-disabled" : "",
128
- className
129
- ].filter(Boolean).join(" ");
130
- return /* @__PURE__ */ jsxs("div", { ...rest, className: classes, "data-state": open ? "open" : "closed", children: [
131
- /* @__PURE__ */ jsx("h3", { className: "crewlet-accordion__heading", children: /* @__PURE__ */ jsxs(
132
- "button",
133
- {
134
- type: "button",
135
- id: triggerId,
136
- className: "crewlet-accordion__trigger",
137
- "aria-expanded": open,
138
- "aria-controls": panelId,
139
- disabled,
140
- onClick: () => ctx.toggle(value),
141
- onKeyDown,
142
- children: [
143
- /* @__PURE__ */ jsx("span", { className: "crewlet-accordion__title", children: title }),
144
- /* @__PURE__ */ jsx("span", { className: "crewlet-accordion__icon material-symbols-outlined", "aria-hidden": true, children: "expand_more" })
145
- ]
146
- }
147
- ) }),
148
- /* @__PURE__ */ jsx(
149
- "div",
150
- {
151
- id: panelId,
152
- role: "region",
153
- "aria-labelledby": triggerId,
154
- className: "crewlet-accordion__panel",
155
- hidden: !open,
156
- children: /* @__PURE__ */ jsx("div", { className: "crewlet-accordion__panel-inner", children })
157
- }
158
- )
159
- ] });
160
- };
161
- var Accordion = Object.assign(AccordionRoot, { Item: AccordionItem });
162
- var AppShell = ({
163
- sidebar,
164
- topbar,
165
- footer,
166
- children,
167
- sidebarWidth = 280,
168
- topbarHeight = 64,
169
- className = "",
170
- style,
171
- ...rest
172
- }) => {
173
- const composedStyle = {
174
- ...style,
175
- ["--crewlet-app-shell-sidebar-width"]: `${sidebarWidth}px`,
176
- ["--crewlet-app-shell-topbar-height"]: `${topbarHeight}px`
177
- };
178
- return /* @__PURE__ */ jsxs("div", { className: `crewlet-app-shell ${className}`.trim(), style: composedStyle, ...rest, children: [
179
- sidebar,
180
- /* @__PURE__ */ jsxs("div", { className: "crewlet-app-shell__column", children: [
181
- topbar,
182
- /* @__PURE__ */ jsx("main", { className: "crewlet-app-shell__main", children }),
183
- footer
184
- ] })
185
- ] });
186
- };
187
- var getInitials = (name) => {
188
- if (!name) {
189
- return "?";
190
- }
191
- const words = name.trim().split(/\s+/).filter(Boolean);
192
- if (words.length === 0) {
193
- return "?";
194
- }
195
- return words.slice(0, 2).map((word) => (Array.from(word)[0] ?? "").toUpperCase()).join("");
196
- };
197
- var TINT_COUNT = 10;
198
- var avatarTint = (seed) => {
199
- let hash = 0;
200
- for (let i = 0; i < seed.length; i += 1) {
201
- hash = hash * 31 + seed.charCodeAt(i) >>> 0;
202
- }
203
- return hash % TINT_COUNT;
204
- };
205
- var Avatar = ({
206
- src,
207
- name,
208
- size = 64,
209
- shape = "circle",
210
- colorSeed,
211
- className = "",
212
- style,
213
- ...rest
214
- }) => {
215
- const [imageFailed, setImageFailed] = useState(false);
216
- useEffect(() => {
217
- setImageFailed(false);
218
- }, [src]);
219
- const dimensions = { width: size, height: size };
220
- const shapeClass = `crewlet-avatar--${shape}`;
221
- const classes = (...extra) => ["crewlet-avatar", ...extra, shapeClass, className].filter(Boolean).join(" ");
222
- if (src && !imageFailed) {
223
- return /* @__PURE__ */ jsx(
224
- "img",
225
- {
226
- ...rest,
227
- className: classes("crewlet-avatar--image"),
228
- style: { ...dimensions, ...style },
229
- src,
230
- alt: name ?? "",
231
- onError: () => setImageFailed(true)
232
- }
233
- );
234
- }
235
- return /* @__PURE__ */ jsx(
236
- "span",
237
- {
238
- ...rest,
239
- className: classes(
240
- "crewlet-avatar--fallback",
241
- ...colorSeed ? ["crewlet-avatar--tint", `crewlet-avatar--tint-${avatarTint(colorSeed)}`] : []
242
- ),
243
- style: { ...dimensions, fontSize: Math.max(12, Math.round(size / 3)), ...style },
244
- role: "img",
245
- "aria-label": name ? `${name} avatar` : "Avatar",
246
- children: getInitials(name)
247
- }
248
- );
249
- };
250
- var renderInner = (leadingIcon, children, loading = false) => /* @__PURE__ */ jsxs(Fragment, { children: [
251
- loading ? /* @__PURE__ */ jsx("span", { className: "crewlet-btn__spinner", "aria-hidden": true }) : leadingIcon ? /* @__PURE__ */ jsx("span", { className: "crewlet-btn__icon", children: leadingIcon }) : null,
252
- /* @__PURE__ */ jsx("span", { className: "crewlet-btn__label", children })
253
- ] });
254
- var Button = ({
255
- variant = "primary",
256
- size = "medium",
257
- shape = "square",
258
- leadingIcon,
259
- className = "",
260
- type = "button",
261
- asChild = false,
262
- loading = false,
263
- disabled,
264
- children,
265
- ...rest
266
- }) => {
267
- const classes = [
268
- "crewlet-btn",
269
- `crewlet-btn--${variant}`,
270
- `crewlet-btn--${size}`,
271
- `crewlet-btn--${shape}`,
272
- loading ? "crewlet-btn--loading" : "",
273
- className
274
- ].filter(Boolean).join(" ");
275
- if (asChild) {
276
- const child = Children.only(children);
277
- if (!isValidElement(child)) {
278
- throw new Error("Button with asChild expects exactly one valid React element child.");
279
- }
280
- const childEl = child;
281
- const mergedClassName = [childEl.props.className, classes].filter(Boolean).join(" ");
282
- return cloneElement(childEl, { ...rest, className: mergedClassName }, renderInner(
283
- leadingIcon,
284
- childEl.props.children
285
- ));
286
- }
287
- return /* @__PURE__ */ jsx(
288
- "button",
289
- {
290
- type,
291
- className: classes,
292
- disabled: disabled || loading,
293
- "aria-busy": loading || void 0,
294
- ...rest,
295
- children: renderInner(leadingIcon, children, loading)
296
- }
297
- );
298
- };
299
- var Callout = ({
300
- variant = "info",
301
- title,
302
- icon,
303
- className = "",
304
- children,
305
- ...rest
306
- }) => {
307
- const classes = ["crewlet-callout", `crewlet-callout--${variant}`, className].filter(Boolean).join(" ");
308
- return /* @__PURE__ */ jsxs("div", { role: "status", ...rest, className: classes, children: [
309
- icon ? /* @__PURE__ */ jsx("span", { className: "crewlet-callout__icon", "aria-hidden": true, children: icon }) : null,
310
- /* @__PURE__ */ jsxs("div", { className: "crewlet-callout__content", children: [
311
- title ? /* @__PURE__ */ jsx("span", { className: "crewlet-callout__title", children: title }) : null,
312
- children
313
- ] })
314
- ] });
315
- };
316
- var CardRoot = ({
317
- variant = "default",
318
- padding = "md",
319
- interactive = false,
320
- className = "",
321
- children,
322
- ...rest
323
- }) => {
324
- const classes = [
325
- "crewlet-card",
326
- `crewlet-card--${variant}`,
327
- `crewlet-card--p-${padding}`,
328
- interactive ? "is-interactive" : "",
329
- className
330
- ].filter(Boolean).join(" ");
331
- return /* @__PURE__ */ jsx("div", { ...rest, className: classes, children });
332
- };
333
- var CardHeader = ({ className = "", children, ...rest }) => /* @__PURE__ */ jsx("div", { ...rest, className: `crewlet-card__header ${className}`.trim(), children });
334
- var CardBody = ({ className = "", children, ...rest }) => /* @__PURE__ */ jsx("div", { ...rest, className: `crewlet-card__body ${className}`.trim(), children });
335
- var CardFooter = ({ className = "", children, ...rest }) => /* @__PURE__ */ jsx("div", { ...rest, className: `crewlet-card__footer ${className}`.trim(), children });
336
- var CardTitle = ({ as: As = "h3", className = "", children, ...rest }) => /* @__PURE__ */ jsx(As, { ...rest, className: `crewlet-card__title ${className}`.trim(), children });
337
- var CardDescription = ({ className = "", children, ...rest }) => /* @__PURE__ */ jsx("p", { ...rest, className: `crewlet-card__description ${className}`.trim(), children });
338
- var Card = Object.assign(CardRoot, {
339
- Header: CardHeader,
340
- Body: CardBody,
341
- Footer: CardFooter,
342
- Title: CardTitle,
343
- Description: CardDescription
344
- });
345
- var COPIED_DURATION_MS = 1500;
346
- var truncateValue = (text, length, variant) => {
347
- if (text.length <= length) return text;
348
- if (variant === "chip") {
349
- const head = Math.max(4, length - 5);
350
- const tail = Math.min(4, Math.max(2, length - head - 1));
351
- return `${text.slice(0, head)}\u2026${text.slice(-tail)}`;
352
- }
353
- return `${text.slice(0, length)}\u2026`;
354
- };
355
- var writeToClipboard = async (text) => {
356
- if (typeof navigator !== "undefined" && navigator.clipboard && window.isSecureContext) {
357
- await navigator.clipboard.writeText(text);
358
- return;
359
- }
360
- const ta = document.createElement("textarea");
361
- ta.value = text;
362
- ta.style.position = "fixed";
363
- ta.style.opacity = "0";
364
- document.body.appendChild(ta);
365
- ta.focus();
366
- ta.select();
367
- document.execCommand("copy");
368
- document.body.removeChild(ta);
369
- };
370
- var Copyable = ({
371
- value,
372
- display,
373
- variant = "chip",
374
- monospace = false,
375
- truncate = false,
376
- truncateLength,
377
- ariaLabel,
378
- className = ""
379
- }) => {
380
- const text = value === void 0 || value === null ? "" : String(value);
381
- const [copied, setCopied] = useState(false);
382
- const timerRef = useRef(null);
383
- useEffect(() => () => {
384
- if (timerRef.current) clearTimeout(timerRef.current);
385
- }, []);
386
- if (!text) return null;
387
- const resolvedLength = truncateLength ?? (variant === "chip" ? 14 : 24);
388
- const shouldTruncate = truncate || variant === "chip" && text.length > resolvedLength;
389
- const rendered = display ?? (shouldTruncate ? truncateValue(text, resolvedLength, variant) : text);
390
- const onCopy = async (event) => {
391
- event.stopPropagation();
392
- event.preventDefault();
393
- try {
394
- await writeToClipboard(text);
395
- setCopied(true);
396
- if (timerRef.current) clearTimeout(timerRef.current);
397
- timerRef.current = setTimeout(() => setCopied(false), COPIED_DURATION_MS);
398
- } catch {
399
- }
400
- };
401
- const classes = [
402
- "crewlet-copyable",
403
- `crewlet-copyable--${variant}`,
404
- monospace ? "crewlet-copyable--monospace" : "",
405
- copied ? "is-copied" : "",
406
- className
407
- ].filter(Boolean).join(" ");
408
- return /* @__PURE__ */ jsxs("span", { className: classes, children: [
409
- /* @__PURE__ */ jsx(
410
- "span",
411
- {
412
- className: "crewlet-copyable__value",
413
- title: text,
414
- role: "button",
415
- tabIndex: 0,
416
- onClick: onCopy,
417
- onKeyDown: (e) => {
418
- if (e.key === "Enter" || e.key === " ") onCopy(e);
419
- },
420
- children: rendered
421
- }
422
- ),
423
- /* @__PURE__ */ jsx(
424
- "button",
425
- {
426
- type: "button",
427
- className: "crewlet-copyable__button",
428
- onClick: onCopy,
429
- "aria-label": ariaLabel || "Copy to clipboard",
430
- title: "Copy",
431
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: copied ? "check" : "content_copy" })
432
- }
433
- )
434
- ] });
435
- };
436
- var CodeBlock = ({
437
- code,
438
- language,
439
- filename,
440
- showLineNumbers = false,
441
- copyable = true,
442
- prompt,
443
- className = ""
444
- }) => {
445
- const lines = code.replace(/\n+$/, "").split("\n");
446
- const showHeader = Boolean(filename) || Boolean(language) || copyable;
447
- const classes = [
448
- "crewlet-codeblock",
449
- showLineNumbers ? "crewlet-codeblock--numbered" : "",
450
- className
451
- ].filter(Boolean).join(" ");
452
- return /* @__PURE__ */ jsxs("div", { className: classes, children: [
453
- showHeader ? /* @__PURE__ */ jsxs("div", { className: "crewlet-codeblock__header", children: [
454
- /* @__PURE__ */ jsxs("span", { className: "crewlet-codeblock__meta", children: [
455
- filename ? /* @__PURE__ */ jsx("span", { className: "crewlet-codeblock__filename", children: filename }) : null,
456
- language && !filename ? /* @__PURE__ */ jsx("span", { className: "crewlet-codeblock__lang", children: language }) : null
457
- ] }),
458
- copyable ? /* @__PURE__ */ jsx(
459
- Copyable,
460
- {
461
- value: code,
462
- display: "Copy",
463
- variant: "inline",
464
- ariaLabel: "Copy code to clipboard",
465
- className: "crewlet-codeblock__copy"
466
- }
467
- ) : null
468
- ] }) : null,
469
- /* @__PURE__ */ jsx("pre", { className: "crewlet-codeblock__pre", "data-language": language, children: /* @__PURE__ */ jsx("code", { className: "crewlet-codeblock__code", children: lines.map((line2, index) => /* @__PURE__ */ jsxs("span", { className: "crewlet-codeblock__line", children: [
470
- showLineNumbers ? /* @__PURE__ */ jsx("span", { className: "crewlet-codeblock__ln", "aria-hidden": true, children: index + 1 }) : null,
471
- prompt ? /* @__PURE__ */ jsx("span", { className: "crewlet-codeblock__prompt", "aria-hidden": true, children: prompt }) : null,
472
- /* @__PURE__ */ jsx("span", { className: "crewlet-codeblock__linecontent", children: line2 || "\xA0" })
473
- ] }, `${index}-${line2}`)) }) })
474
- ] });
475
- };
476
- var Container = ({
477
- size = "lg",
478
- className = "",
479
- children,
480
- ...rest
481
- }) => {
482
- const classes = ["crewlet-container", `crewlet-container--${size}`, className].filter(Boolean).join(" ");
483
- return /* @__PURE__ */ jsx("div", { ...rest, className: classes, children });
484
- };
485
- var Modal = ({
486
- open,
487
- onClose,
488
- title,
489
- subtitle,
490
- footer,
491
- children,
492
- size = "sm",
493
- closeOnBackdrop = true,
494
- showCloseButton = true,
495
- className = "",
496
- labelledBy,
497
- describedBy
498
- }) => {
499
- const dialogRef = useRef(null);
500
- const onCloseRef = useRef(onClose);
501
- useEffect(() => {
502
- onCloseRef.current = onClose;
503
- });
504
- useEffect(() => {
505
- if (!open) return void 0;
506
- const onKey = (event) => {
507
- if (event.key === "Escape") onCloseRef.current();
508
- };
509
- document.addEventListener("keydown", onKey);
510
- const prevOverflow = document.body.style.overflow;
511
- document.body.style.overflow = "hidden";
512
- const previouslyFocused = document.activeElement;
513
- requestAnimationFrame(() => {
514
- dialogRef.current?.focus();
515
- });
516
- return () => {
517
- document.removeEventListener("keydown", onKey);
518
- document.body.style.overflow = prevOverflow;
519
- previouslyFocused?.focus?.();
520
- };
521
- }, [open]);
522
- if (!open || typeof document === "undefined") return null;
523
- const onBackdropMouseDown = () => {
524
- if (closeOnBackdrop) onClose();
525
- };
526
- const stopPropagation = (event) => event.stopPropagation();
527
- return createPortal(
528
- /* @__PURE__ */ jsx(
529
- "div",
530
- {
531
- className: "crewlet-modal-overlay",
532
- role: "presentation",
533
- onMouseDown: onBackdropMouseDown,
534
- children: /* @__PURE__ */ jsxs(
535
- "div",
536
- {
537
- ref: dialogRef,
538
- className: `crewlet-modal crewlet-modal--${size} ${className}`.trim(),
539
- role: "dialog",
540
- "aria-modal": "true",
541
- "aria-labelledby": labelledBy,
542
- "aria-describedby": describedBy,
543
- tabIndex: -1,
544
- onMouseDown: stopPropagation,
545
- children: [
546
- (title || subtitle || showCloseButton) && /* @__PURE__ */ jsxs("div", { className: "crewlet-modal__header", children: [
547
- /* @__PURE__ */ jsxs("div", { className: "crewlet-modal__heading", children: [
548
- title ? /* @__PURE__ */ jsx("h2", { className: "crewlet-modal__title", id: labelledBy, children: title }) : null,
549
- subtitle ? /* @__PURE__ */ jsx("p", { className: "crewlet-modal__subtitle", children: subtitle }) : null
550
- ] }),
551
- showCloseButton ? /* @__PURE__ */ jsx(
552
- "button",
553
- {
554
- type: "button",
555
- className: "crewlet-modal__close",
556
- onClick: onClose,
557
- "aria-label": "Close",
558
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: "close" })
559
- }
560
- ) : null
561
- ] }),
562
- children !== void 0 ? /* @__PURE__ */ jsx("div", { className: "crewlet-modal__body", children }) : null,
563
- footer ? /* @__PURE__ */ jsx("div", { className: "crewlet-modal__footer", children: footer }) : null
564
- ]
565
- }
566
- )
567
- }
568
- ),
569
- document.body
570
- );
571
- };
572
- var COPIED_DURATION_MS2 = 1500;
573
- var writeToClipboard2 = async (text) => {
574
- if (typeof navigator !== "undefined" && navigator.clipboard && window.isSecureContext) {
575
- await navigator.clipboard.writeText(text);
576
- return;
577
- }
578
- const ta = document.createElement("textarea");
579
- ta.value = text;
580
- ta.style.position = "fixed";
581
- ta.style.opacity = "0";
582
- document.body.appendChild(ta);
583
- ta.focus();
584
- ta.select();
585
- document.execCommand("copy");
586
- document.body.removeChild(ta);
587
- };
588
- var CopyableCell = ({ value, children, ariaLabel }) => {
589
- const text = value === void 0 || value === null ? "" : String(value);
590
- const [copied, setCopied] = useState(false);
591
- const timer = useRef(null);
592
- useEffect(() => () => {
593
- if (timer.current) clearTimeout(timer.current);
594
- }, []);
595
- const onCopy = async (event) => {
596
- event.stopPropagation();
597
- if (!text) return;
598
- try {
599
- await writeToClipboard2(text);
600
- setCopied(true);
601
- if (timer.current) clearTimeout(timer.current);
602
- timer.current = setTimeout(() => setCopied(false), COPIED_DURATION_MS2);
603
- } catch {
604
- }
605
- };
606
- return /* @__PURE__ */ jsxs("span", { className: "crewlet-data-table__copy-cell", children: [
607
- /* @__PURE__ */ jsx("span", { className: "crewlet-data-table__copy-cell-value", children }),
608
- text && /* @__PURE__ */ jsx(
609
- "button",
610
- {
611
- type: "button",
612
- className: `crewlet-data-table__copy-cell-button${copied ? " is-copied" : ""}`,
613
- onClick: onCopy,
614
- "aria-label": ariaLabel || "Copy to clipboard",
615
- title: "Copy",
616
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: copied ? "check" : "content_copy" })
617
- }
618
- )
619
- ] });
620
- };
621
- function Popover({
622
- trigger,
623
- children,
624
- align = "start",
625
- side = "bottom",
626
- width = "auto",
627
- className = "",
628
- defaultOpen = false,
629
- onOpenChange
630
- }) {
631
- const [open, setOpen] = useState(defaultOpen);
632
- useEffect(() => {
633
- onOpenChange?.(open);
634
- }, [open, onOpenChange]);
635
- const [coords, setCoords] = useState(
636
- { top: 0, left: 0, width: 0, side }
637
- );
638
- const triggerRef = useRef(null);
639
- const panelRef = useRef(null);
640
- const close = () => setOpen(false);
641
- const toggle = () => setOpen((o) => !o);
642
- const reposition = () => {
643
- const trig = triggerRef.current;
644
- if (!trig) return;
645
- const rect = trig.getBoundingClientRect();
646
- const panelHeight = panelRef.current?.offsetHeight ?? 200;
647
- const viewportH = window.innerHeight;
648
- let chosenSide = side;
649
- if (side === "bottom" && rect.bottom + panelHeight + 12 > viewportH) {
650
- chosenSide = "top";
651
- }
652
- const top = chosenSide === "bottom" ? rect.bottom + window.scrollY + 6 : rect.top + window.scrollY - panelHeight - 6;
653
- const left = align === "end" ? rect.right + window.scrollX : rect.left + window.scrollX;
654
- setCoords({ top, left, width: rect.width, side: chosenSide });
655
- };
656
- useEffect(() => {
657
- if (!open) return void 0;
658
- reposition();
659
- const onClick = (event) => {
660
- const target = event.target;
661
- if (target && panelRef.current?.contains(target)) return;
662
- if (target && triggerRef.current?.contains(target)) return;
663
- setOpen(false);
664
- };
665
- const onKey = (event) => {
666
- if (event.key === "Escape") {
667
- event.preventDefault();
668
- setOpen(false);
669
- triggerRef.current?.focus();
670
- }
671
- };
672
- const onLayout = () => reposition();
673
- document.addEventListener("mousedown", onClick);
674
- document.addEventListener("keydown", onKey);
675
- window.addEventListener("resize", onLayout);
676
- window.addEventListener("scroll", onLayout, true);
677
- return () => {
678
- document.removeEventListener("mousedown", onClick);
679
- document.removeEventListener("keydown", onKey);
680
- window.removeEventListener("resize", onLayout);
681
- window.removeEventListener("scroll", onLayout, true);
682
- };
683
- }, [open, align, side]);
684
- const triggerEl = trigger(open, toggle);
685
- const triggerWithRef = React.cloneElement(triggerEl, {
686
- ref: triggerRef,
687
- "aria-expanded": open,
688
- "aria-haspopup": "menu"
689
- });
690
- const panelStyle = {
691
- top: coords.top,
692
- [align === "end" ? "right" : "left"]: align === "end" ? typeof document !== "undefined" ? document.documentElement.clientWidth - coords.left : 0 : coords.left
693
- };
694
- if (width === "match") panelStyle.width = coords.width;
695
- return /* @__PURE__ */ jsxs(Fragment, { children: [
696
- triggerWithRef,
697
- open && createPortal(
698
- /* @__PURE__ */ jsx(
699
- "div",
700
- {
701
- ref: panelRef,
702
- className: `crewlet-popover crewlet-popover--${coords.side} ${className}`,
703
- style: panelStyle,
704
- role: "menu",
705
- tabIndex: -1,
706
- onMouseDown: (e) => e.stopPropagation(),
707
- children: typeof children === "function" ? children(close) : children
708
- }
709
- ),
710
- document.body
711
- )
712
- ] });
713
- }
714
- var RowActionsMenu = ({ actions, ariaLabel = "Row actions" }) => {
715
- const items = (actions || []).filter(
716
- (a) => !!a && a.visible !== false
717
- );
718
- if (items.length === 0) return null;
719
- return /* @__PURE__ */ jsx(
720
- Popover,
721
- {
722
- align: "end",
723
- side: "bottom",
724
- width: "auto",
725
- className: "crewlet-data-table__row-actions-popover",
726
- trigger: (open, toggle) => /* @__PURE__ */ jsx(
727
- "button",
728
- {
729
- type: "button",
730
- className: `crewlet-data-table__row-actions-trigger${open ? " is-open" : ""}`,
731
- onClick: (e) => {
732
- e.stopPropagation();
733
- toggle();
734
- },
735
- "aria-label": ariaLabel,
736
- "aria-haspopup": "menu",
737
- "aria-expanded": open,
738
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: "more_vert" })
739
- }
740
- ),
741
- children: (close) => /* @__PURE__ */ jsx("ul", { className: "crewlet-data-table__row-actions-list", role: "menu", children: items.map((action, i) => {
742
- const cls = [
743
- "crewlet-data-table__row-actions-item",
744
- action.variant && `crewlet-data-table__row-actions-item--${action.variant}`,
745
- action.disabled && "is-disabled"
746
- ].filter(Boolean).join(" ");
747
- return /* @__PURE__ */ jsxs(Fragment$1, { children: [
748
- action.divider === "before" && /* @__PURE__ */ jsx("li", { className: "crewlet-data-table__row-actions-divider", role: "separator" }),
749
- /* @__PURE__ */ jsx("li", { role: "none", children: /* @__PURE__ */ jsxs(
750
- "button",
751
- {
752
- type: "button",
753
- role: "menuitem",
754
- className: cls,
755
- disabled: action.disabled,
756
- onClick: (e) => {
757
- e.stopPropagation();
758
- if (action.disabled) return;
759
- action.onClick?.();
760
- close();
761
- },
762
- children: [
763
- action.icon && /* @__PURE__ */ jsx(
764
- "span",
765
- {
766
- className: "material-symbols-outlined crewlet-data-table__row-actions-item-icon",
767
- "aria-hidden": true,
768
- children: action.icon
769
- }
770
- ),
771
- /* @__PURE__ */ jsxs("span", { className: "crewlet-data-table__row-actions-item-text", children: [
772
- /* @__PURE__ */ jsx("span", { className: "crewlet-data-table__row-actions-item-label", children: action.label }),
773
- action.description && /* @__PURE__ */ jsx("span", { className: "crewlet-data-table__row-actions-item-description", children: action.description })
774
- ] })
775
- ]
776
- }
777
- ) }),
778
- action.divider === "after" && /* @__PURE__ */ jsx("li", { className: "crewlet-data-table__row-actions-divider", role: "separator" })
779
- ] }, i);
780
- }) })
781
- }
782
- );
783
- };
784
- var DataTable = ({
785
- data = [],
786
- columns = {},
787
- storageKey,
788
- defaultColumnOrder = null,
789
- defaultItemsPerPage = 10,
790
- minColumnWidth = 80,
791
- resizable = true,
792
- itemsPerPageOptions = [5, 10, 20, 50, 100],
793
- onRowAction = null,
794
- actionLabel = "Delete",
795
- actionIcon = "delete",
796
- getRowKey,
797
- rowKey,
798
- emptyMessage = "No data available",
799
- showActions = true,
800
- className = "",
801
- variant = "default",
802
- density = "compact",
803
- sort: sortProp,
804
- onSortChange,
805
- rowActions,
806
- archivedPredicate,
807
- archivedLabel = "archived",
808
- loading = false,
809
- skeletonRows = 5,
810
- error = null,
811
- paginated = true,
812
- showSettings = true,
813
- onRowClick,
814
- renderAccessoryRow,
815
- renderToolbar,
816
- onClearFilters,
817
- filtersActive = false,
818
- clearFiltersLabel = "Clear filters",
819
- renderExpandedRow,
820
- defaultExpandedKey = null,
821
- hasMore = false,
822
- onLoadMore,
823
- loadingMore = false,
824
- loadMoreLabel = "Load more",
825
- title,
826
- description,
827
- icon,
828
- settingsVariant,
829
- defaultWrapLines
830
- }) => {
831
- const headerVisibleFlag = title != null || description != null || icon != null;
832
- const effectiveSettingsVariant = settingsVariant ?? (headerVisibleFlag ? "compact" : "rich");
833
- const effectiveDefaultWrapLines = defaultWrapLines ?? true;
834
- const resolveRowKey = (row) => {
835
- if (getRowKey) return getRowKey(row);
836
- if (rowKey) return row?.[rowKey];
837
- return row?.id;
838
- };
839
- const isCompact = variant === "compact";
840
- const settingsEnabled = showSettings;
841
- const paginationEnabled = paginated;
842
- const settingsInHeader = settingsEnabled && !isCompact;
843
- const generateDefaultColumnWidths = () => {
844
- const widths = {};
845
- Object.keys(columns).forEach((key) => {
846
- const col = columns[key];
847
- const rawExplicit = col?.width ?? col?.defaultWidth;
848
- let px = null;
849
- if (rawExplicit != null) {
850
- const parsed = typeof rawExplicit === "number" ? rawExplicit : parseInt(String(rawExplicit), 10);
851
- if (Number.isFinite(parsed)) px = parsed;
852
- }
853
- if (px == null) {
854
- const labelStr = typeof col?.label === "string" ? col.label : key;
855
- const estimated = Math.ceil(labelStr.length * 7.5) + 56;
856
- px = Math.max(120, estimated);
857
- }
858
- widths[key] = Math.max(minColumnWidth, px);
859
- });
860
- if (showActions) {
861
- widths.__actions = 60;
862
- }
863
- return widths;
864
- };
865
- const generateDefaultVisibleColumns = () => {
866
- const visible = {};
867
- Object.keys(columns).forEach((key) => {
868
- visible[key] = columns[key]?.defaultVisible !== false;
869
- });
870
- return visible;
871
- };
872
- const generateDefaultColumnOrder = () => {
873
- return defaultColumnOrder || Object.keys(columns);
874
- };
875
- const defaultColumnWidths = generateDefaultColumnWidths();
876
- const defaultVisibleColumns = generateDefaultVisibleColumns();
877
- const defaultOrder = generateDefaultColumnOrder();
878
- const [columnWidths, setColumnWidths] = useState(() => {
879
- if (!storageKey) return defaultColumnWidths;
880
- const saved = localStorage.getItem(`${storageKey}_columnWidths`);
881
- if (!saved) return defaultColumnWidths;
882
- try {
883
- const parsed = JSON.parse(saved);
884
- const merged = { ...defaultColumnWidths };
885
- Object.entries(parsed).forEach(([key, value]) => {
886
- const px = typeof value === "number" ? value : parseInt(String(value), 10);
887
- if (!Number.isFinite(px)) return;
888
- const col = columns[key];
889
- if (col?.width != null) return;
890
- merged[key] = key.startsWith("__") ? px : Math.max(minColumnWidth, px);
891
- });
892
- return merged;
893
- } catch {
894
- return defaultColumnWidths;
895
- }
896
- });
897
- const [itemsPerPage, setItemsPerPage] = useState(() => {
898
- if (!storageKey) return defaultItemsPerPage;
899
- const saved = localStorage.getItem(`${storageKey}_itemsPerPage`);
900
- return saved ? parseInt(saved, 10) : defaultItemsPerPage;
901
- });
902
- const [visibleColumns, setVisibleColumns] = useState(() => {
903
- if (!storageKey) return defaultVisibleColumns;
904
- const saved = localStorage.getItem(`${storageKey}_visibleColumns`);
905
- if (saved) {
906
- const savedVisible = JSON.parse(saved);
907
- const validVisible = {};
908
- Object.keys(columns).forEach((key) => {
909
- validVisible[key] = Object.prototype.hasOwnProperty.call(savedVisible, key) ? savedVisible[key] : columns[key]?.defaultVisible !== false;
910
- });
911
- return validVisible;
912
- }
913
- return defaultVisibleColumns;
914
- });
915
- const [columnOrder, setColumnOrder] = useState(() => {
916
- if (!storageKey) return defaultOrder;
917
- const saved = localStorage.getItem(`${storageKey}_columnOrder`);
918
- if (saved) {
919
- const savedOrder = JSON.parse(saved);
920
- const validOrder = savedOrder.filter((key) => Object.prototype.hasOwnProperty.call(columns, key));
921
- const newColumns = defaultOrder.filter((key) => !validOrder.includes(key));
922
- return validOrder.length > 0 ? [...validOrder, ...newColumns] : defaultOrder;
923
- }
924
- return defaultOrder;
925
- });
926
- const [sortConfig, setSortConfig] = useState(
927
- { key: null, direction: null }
928
- );
929
- const [tableSettingsOpen, setTableSettingsOpen] = useState(false);
930
- const [wrapLines, setWrapLines] = useState(() => {
931
- if (!storageKey) return effectiveDefaultWrapLines;
932
- const saved = localStorage.getItem(`${storageKey}_wrapLines`);
933
- return saved == null ? effectiveDefaultWrapLines : saved === "true";
934
- });
935
- const [draftItemsPerPage, setDraftItemsPerPage] = useState(0);
936
- const [draftWrapLines, setDraftWrapLines] = useState(effectiveDefaultWrapLines);
937
- const [draftColumnWidths, setDraftColumnWidths] = useState({});
938
- const [draftVisibleColumns, setDraftVisibleColumns] = useState({});
939
- const [draftColumnOrder, setDraftColumnOrder] = useState([]);
940
- const [draftSortConfig, setDraftSortConfig] = useState(
941
- { key: null, direction: null }
942
- );
943
- const [resetConfirmed, setResetConfirmed] = useState(false);
944
- const resetConfirmTimer = useRef(null);
945
- const pendingFitOnApplyRef = useRef(false);
946
- const [expandedKeys, setExpandedKeys] = useState(
947
- () => defaultExpandedKey != null ? /* @__PURE__ */ new Set([defaultExpandedKey]) : /* @__PURE__ */ new Set()
948
- );
949
- const toggleExpanded = (key) => {
950
- setExpandedKeys((prev) => {
951
- const next = new Set(prev);
952
- if (next.has(key)) {
953
- next.delete(key);
954
- } else {
955
- next.add(key);
956
- }
957
- return next;
958
- });
959
- };
960
- const [draggedColumn, setDraggedColumn] = useState(null);
961
- const [resizingColumn, setResizingColumn] = useState(null);
962
- const [startX, setStartX] = useState(0);
963
- const [startWidth, setStartWidth] = useState(0);
964
- const dragTimeoutRef = useRef(null);
965
- const settingsButtonRef = useRef(null);
966
- const scrollRef = useRef(null);
967
- const [needsFit, setNeedsFit] = useState(true);
968
- const resetToDefault = () => {
969
- const newDefaultWidths = generateDefaultColumnWidths();
970
- const newDefaultVisible = generateDefaultVisibleColumns();
971
- const newDefaultOrder = generateDefaultColumnOrder();
972
- setColumnWidths(newDefaultWidths);
973
- setVisibleColumns(newDefaultVisible);
974
- setColumnOrder(newDefaultOrder);
975
- setItemsPerPage(defaultItemsPerPage);
976
- setSortConfig({ key: null, direction: null });
977
- setNeedsFit(true);
978
- if (storageKey) {
979
- localStorage.removeItem(`${storageKey}_columnWidths`);
980
- localStorage.removeItem(`${storageKey}_visibleColumns`);
981
- localStorage.removeItem(`${storageKey}_columnOrder`);
982
- localStorage.removeItem(`${storageKey}_itemsPerPage`);
983
- }
984
- };
985
- useLayoutEffect(() => {
986
- if (!needsFit) return;
987
- const stickyColumnPresent = columnOrder.some(
988
- (k) => columns[k]?.sticky === "right" && visibleColumns[k] !== false
989
- );
990
- if (!stickyColumnPresent && storageKey && localStorage.getItem(`${storageKey}_columnWidths`)) {
991
- setNeedsFit(false);
992
- return;
993
- }
994
- const el = scrollRef.current;
995
- if (!el) return;
996
- const containerWidth = el.clientWidth;
997
- if (containerWidth <= 0) return;
998
- const visibleAllKeys = columnOrder.filter((key) => visibleColumns[key] !== false);
999
- const isFixedWidthColumn = (key) => {
1000
- const col = columns[key];
1001
- if (!col) return false;
1002
- if (col.sticky === "right") return false;
1003
- return col.width != null;
1004
- };
1005
- const visibleStickyKeys = visibleAllKeys.filter((key) => columns[key]?.sticky === "right");
1006
- const visibleFixedKeys = visibleAllKeys.filter((key) => isFixedWidthColumn(key));
1007
- const visibleDataKeys = visibleAllKeys.filter(
1008
- (key) => columns[key]?.sticky !== "right" && !isFixedWidthColumn(key)
1009
- );
1010
- if (visibleDataKeys.length === 0) {
1011
- setNeedsFit(false);
1012
- return;
1013
- }
1014
- let chromeWidth = 0;
1015
- if (expandable) chromeWidth += 32;
1016
- if (hasActionColumn) chromeWidth += isCompact ? 48 : columnWidths.__actions ?? 60;
1017
- chromeWidth += visibleStickyKeys.reduce(
1018
- (sum, key) => sum + (columnWidths[key] ?? minColumnWidth),
1019
- 0
1020
- );
1021
- chromeWidth += visibleFixedKeys.reduce(
1022
- (sum, key) => sum + (columnWidths[key] ?? minColumnWidth),
1023
- 0
1024
- );
1025
- if (visibleStickyKeys.length > 0) {
1026
- chromeWidth += 24;
1027
- }
1028
- const dataTotal = visibleDataKeys.reduce(
1029
- (sum, key) => sum + (columnWidths[key] ?? minColumnWidth),
1030
- 0
1031
- );
1032
- const available = containerWidth - chromeWidth;
1033
- const leftover = available - dataTotal;
1034
- setNeedsFit(false);
1035
- if (Math.abs(leftover) <= 1) return;
1036
- const next = { ...columnWidths };
1037
- let distributed = 0;
1038
- visibleDataKeys.forEach((key, idx) => {
1039
- const current = columnWidths[key] ?? minColumnWidth;
1040
- const rawShare = idx === visibleDataKeys.length - 1 ? leftover - distributed : Math.round(leftover * (current / dataTotal));
1041
- const proposed = current + rawShare;
1042
- const clamped = Math.max(minColumnWidth, proposed);
1043
- const realShare = clamped - current;
1044
- next[key] = clamped;
1045
- distributed += realShare;
1046
- });
1047
- setColumnWidths(next);
1048
- }, [needsFit]);
1049
- useEffect(() => {
1050
- const el = scrollRef.current;
1051
- if (!el) return;
1052
- let lastWidth = el.clientWidth;
1053
- const observer = new ResizeObserver(() => {
1054
- const w = el.clientWidth;
1055
- if (w > 0 && w !== lastWidth) {
1056
- lastWidth = w;
1057
- setNeedsFit(true);
1058
- }
1059
- });
1060
- observer.observe(el);
1061
- return () => observer.disconnect();
1062
- }, []);
1063
- useEffect(() => {
1064
- if (storageKey) {
1065
- localStorage.setItem(`${storageKey}_columnWidths`, JSON.stringify(columnWidths));
1066
- }
1067
- }, [columnWidths, storageKey]);
1068
- useEffect(() => {
1069
- if (storageKey) {
1070
- localStorage.setItem(`${storageKey}_itemsPerPage`, itemsPerPage.toString());
1071
- }
1072
- }, [itemsPerPage, storageKey]);
1073
- useEffect(() => {
1074
- if (storageKey) {
1075
- localStorage.setItem(`${storageKey}_visibleColumns`, JSON.stringify(visibleColumns));
1076
- }
1077
- }, [visibleColumns, storageKey]);
1078
- useEffect(() => {
1079
- if (storageKey) {
1080
- localStorage.setItem(`${storageKey}_columnOrder`, JSON.stringify(columnOrder));
1081
- }
1082
- }, [columnOrder, storageKey]);
1083
- useEffect(() => {
1084
- if (storageKey) {
1085
- localStorage.setItem(`${storageKey}_wrapLines`, String(wrapLines));
1086
- }
1087
- }, [wrapLines, storageKey]);
1088
- const handleMouseDownResize = (e, columnKey) => {
1089
- e.preventDefault();
1090
- e.stopPropagation();
1091
- setResizingColumn(columnKey);
1092
- setStartX(e.clientX);
1093
- setStartWidth(columnWidths[columnKey] ?? minColumnWidth);
1094
- };
1095
- useEffect(() => {
1096
- const handleMouseMove = (e) => {
1097
- if (resizingColumn) {
1098
- const diff = e.clientX - startX;
1099
- const newWidth = Math.max(minColumnWidth, startWidth + diff);
1100
- setColumnWidths((prev) => ({
1101
- ...prev,
1102
- [resizingColumn]: newWidth
1103
- }));
1104
- }
1105
- };
1106
- const handleMouseUp = () => {
1107
- if (resizingColumn) {
1108
- setResizingColumn(null);
1109
- }
1110
- };
1111
- if (resizingColumn) {
1112
- document.body.classList.add("crewlet-data-table-resizing");
1113
- document.addEventListener("mousemove", handleMouseMove);
1114
- document.addEventListener("mouseup", handleMouseUp);
1115
- return () => {
1116
- document.body.classList.remove("crewlet-data-table-resizing");
1117
- document.removeEventListener("mousemove", handleMouseMove);
1118
- document.removeEventListener("mouseup", handleMouseUp);
1119
- };
1120
- }
1121
- return void 0;
1122
- }, [resizingColumn, startX, startWidth]);
1123
- const controlledSort = sortProp !== void 0;
1124
- const effectiveSort = controlledSort ? sortProp ? { key: sortProp.key, direction: sortProp.direction } : { key: null, direction: null } : sortConfig;
1125
- const handleSort = (columnKey) => {
1126
- if (!columns[columnKey]?.sortable && columns[columnKey]?.sortable !== void 0) {
1127
- return;
1128
- }
1129
- let direction = "asc";
1130
- if (effectiveSort.key === columnKey) {
1131
- if (effectiveSort.direction === "asc") {
1132
- direction = "desc";
1133
- } else if (effectiveSort.direction === "desc") {
1134
- direction = null;
1135
- }
1136
- }
1137
- const next = direction ? { key: columnKey, direction } : null;
1138
- if (controlledSort) {
1139
- onSortChange?.(next);
1140
- } else {
1141
- setSortConfig({ key: next?.key ?? null, direction: next?.direction ?? null });
1142
- }
1143
- };
1144
- const getSortedData = () => {
1145
- const sortKey = effectiveSort.key;
1146
- if (!sortKey || !effectiveSort.direction) {
1147
- return [...data];
1148
- }
1149
- const column = columns[sortKey];
1150
- const accessor = column?.sortValue ?? ((row) => row[sortKey]);
1151
- return [...data].sort((a, b) => {
1152
- const aValue = accessor(a);
1153
- const bValue = accessor(b);
1154
- if (aValue === null || aValue === void 0) return 1;
1155
- if (bValue === null || bValue === void 0) return -1;
1156
- let comparison = 0;
1157
- if (typeof aValue === "string" && typeof bValue === "string") {
1158
- comparison = aValue.localeCompare(bValue, void 0, { numeric: true, sensitivity: "base" });
1159
- } else if (typeof aValue === "number" && typeof bValue === "number") {
1160
- comparison = aValue - bValue;
1161
- } else if (aValue instanceof Date && bValue instanceof Date) {
1162
- comparison = aValue.getTime() - bValue.getTime();
1163
- } else {
1164
- comparison = String(aValue).localeCompare(String(bValue), void 0, { numeric: true, sensitivity: "base" });
1165
- }
1166
- return effectiveSort.direction === "asc" ? comparison : -comparison;
1167
- });
1168
- };
1169
- const [currentPage, setCurrentPage] = useState(1);
1170
- const sortedDataMemo = getSortedData();
1171
- const totalPages = paginationEnabled ? Math.max(1, Math.ceil(sortedDataMemo.length / Math.max(1, itemsPerPage))) : 1;
1172
- useEffect(() => {
1173
- if (currentPage > totalPages) setCurrentPage(totalPages);
1174
- if (currentPage < 1) setCurrentPage(1);
1175
- }, [totalPages, currentPage]);
1176
- useEffect(() => {
1177
- setCurrentPage(1);
1178
- }, [effectiveSort.key, effectiveSort.direction, sortedDataMemo.length]);
1179
- const getDisplayedData = () => {
1180
- if (!paginationEnabled) return sortedDataMemo;
1181
- const start = (currentPage - 1) * itemsPerPage;
1182
- return sortedDataMemo.slice(start, start + itemsPerPage);
1183
- };
1184
- const handleToggleColumn = (columnKey) => {
1185
- setDraftVisibleColumns((prev) => ({ ...prev, [columnKey]: !prev[columnKey] }));
1186
- };
1187
- const handleItemsPerPageChange = (value) => {
1188
- setDraftItemsPerPage(value);
1189
- };
1190
- const handleResetTableSettings = () => {
1191
- setDraftColumnWidths(generateDefaultColumnWidths());
1192
- setDraftItemsPerPage(defaultItemsPerPage);
1193
- setDraftVisibleColumns(generateDefaultVisibleColumns());
1194
- setDraftColumnOrder(generateDefaultColumnOrder());
1195
- setDraftWrapLines(effectiveDefaultWrapLines);
1196
- setDraftSortConfig({ key: null, direction: null });
1197
- pendingFitOnApplyRef.current = true;
1198
- setResetConfirmed(true);
1199
- if (resetConfirmTimer.current) clearTimeout(resetConfirmTimer.current);
1200
- resetConfirmTimer.current = setTimeout(() => setResetConfirmed(false), 1500);
1201
- };
1202
- const openTableSettings = () => {
1203
- setDraftColumnWidths({ ...columnWidths });
1204
- setDraftItemsPerPage(itemsPerPage);
1205
- setDraftVisibleColumns({ ...visibleColumns });
1206
- setDraftColumnOrder([...columnOrder]);
1207
- setDraftWrapLines(wrapLines);
1208
- setDraftSortConfig({ key: effectiveSort.key, direction: effectiveSort.direction });
1209
- setResetConfirmed(false);
1210
- pendingFitOnApplyRef.current = false;
1211
- if (resetConfirmTimer.current) {
1212
- clearTimeout(resetConfirmTimer.current);
1213
- resetConfirmTimer.current = null;
1214
- }
1215
- setTableSettingsOpen(true);
1216
- };
1217
- const applyTableSettings = () => {
1218
- setColumnWidths(draftColumnWidths);
1219
- setItemsPerPage(draftItemsPerPage);
1220
- setVisibleColumns(draftVisibleColumns);
1221
- setColumnOrder(draftColumnOrder);
1222
- setWrapLines(draftWrapLines);
1223
- if (controlledSort) {
1224
- onSortChange?.(
1225
- draftSortConfig.key && draftSortConfig.direction ? { key: draftSortConfig.key, direction: draftSortConfig.direction } : null
1226
- );
1227
- } else {
1228
- setSortConfig(draftSortConfig);
1229
- }
1230
- if (pendingFitOnApplyRef.current) {
1231
- pendingFitOnApplyRef.current = false;
1232
- if (storageKey) {
1233
- localStorage.removeItem(`${storageKey}_columnWidths`);
1234
- }
1235
- setNeedsFit(true);
1236
- }
1237
- setTableSettingsOpen(false);
1238
- };
1239
- const cancelTableSettings = () => {
1240
- pendingFitOnApplyRef.current = false;
1241
- setTableSettingsOpen(false);
1242
- };
1243
- const handleDragStart = (e, columnKey) => {
1244
- setDraggedColumn(columnKey);
1245
- e.dataTransfer.effectAllowed = "move";
1246
- };
1247
- const handleDragOver = (e, columnKey) => {
1248
- e.preventDefault();
1249
- e.dataTransfer.dropEffect = "move";
1250
- if (columnKey && draggedColumn && draggedColumn !== columnKey) {
1251
- if (dragTimeoutRef.current) {
1252
- clearTimeout(dragTimeoutRef.current);
1253
- }
1254
- dragTimeoutRef.current = setTimeout(() => {
1255
- setDraftColumnOrder((prev) => {
1256
- const newOrder = [...prev];
1257
- const draggedIndex = newOrder.indexOf(draggedColumn);
1258
- const targetIndex = newOrder.indexOf(columnKey);
1259
- if (draggedIndex === -1 || targetIndex === -1 || draggedIndex === targetIndex) {
1260
- return prev;
1261
- }
1262
- newOrder.splice(draggedIndex, 1);
1263
- newOrder.splice(targetIndex, 0, draggedColumn);
1264
- return newOrder;
1265
- });
1266
- }, 5);
1267
- }
1268
- };
1269
- const handleDrop = (e, _targetColumnKey) => {
1270
- e.preventDefault();
1271
- setDraggedColumn(null);
1272
- };
1273
- const handleDragEnd = () => {
1274
- if (dragTimeoutRef.current) {
1275
- clearTimeout(dragTimeoutRef.current);
1276
- }
1277
- setDraggedColumn(null);
1278
- };
1279
- useEffect(() => {
1280
- const handleEscKey = (event) => {
1281
- if (event.key === "Escape" && tableSettingsOpen) {
1282
- setTableSettingsOpen(false);
1283
- }
1284
- };
1285
- document.addEventListener("keydown", handleEscKey);
1286
- return () => {
1287
- document.removeEventListener("keydown", handleEscKey);
1288
- };
1289
- }, [tableSettingsOpen]);
1290
- const renderCellContent = (row, columnKey) => {
1291
- const column = columns[columnKey];
1292
- const value = row[columnKey];
1293
- if (column?.render) {
1294
- return column.render(row, value);
1295
- }
1296
- return value !== null && value !== void 0 ? value : "\u2013";
1297
- };
1298
- const renderBodyCell = (row, columnKey) => {
1299
- const column = columns[columnKey];
1300
- if (!column) return null;
1301
- const style = {};
1302
- if (isCompact) {
1303
- if (columnWidths[columnKey] != null) {
1304
- style.width = `${columnWidths[columnKey]}px`;
1305
- }
1306
- if (column.mono) {
1307
- style.fontFamily = "var(--font-family-mono)";
1308
- style.fontSize = 11;
1309
- }
1310
- } else {
1311
- style.width = `${columnWidths[columnKey]}px`;
1312
- }
1313
- const rendered = renderCellContent(row, columnKey);
1314
- let content = rendered;
1315
- if (isCompact && column.copyable) {
1316
- const raw = column.copyValue ? column.copyValue(row) : row[columnKey];
1317
- const copyText = raw !== void 0 && raw !== null ? String(raw) : "";
1318
- content = /* @__PURE__ */ jsx(CopyableCell, { value: copyText, ariaLabel: `Copy ${typeof column.label === "string" ? column.label : columnKey}`, children: rendered });
1319
- }
1320
- const tdClassName = isCompact && column.sticky === "right" ? "crewlet-data-table__td--sticky-right" : void 0;
1321
- return /* @__PURE__ */ jsx("td", { style, className: tdClassName, children: content }, columnKey);
1322
- };
1323
- const renderHeaderCell = (columnKey) => {
1324
- if (!visibleColumns[columnKey]) return null;
1325
- const column = columns[columnKey];
1326
- if (!column) return null;
1327
- const isSortable = column.sortable !== false;
1328
- const isActive = effectiveSort.key === columnKey;
1329
- const arrow = !isSortable ? null : isActive ? effectiveSort.direction === "asc" ? "arrow_upward" : "arrow_downward" : "unfold_more";
1330
- if (isCompact) {
1331
- const style = {};
1332
- if (columnWidths[columnKey] != null) {
1333
- style.width = `${columnWidths[columnKey]}px`;
1334
- }
1335
- const isSticky = column.sticky === "right";
1336
- const visibleStickyExists = columnOrder.some(
1337
- (k) => columns[k]?.sticky === "right" && visibleColumns[k] !== false
1338
- );
1339
- const suppressResizer = !resizable || isSticky || visibleStickyExists;
1340
- const resizer = suppressResizer ? null : /* @__PURE__ */ jsx(
1341
- "span",
1342
- {
1343
- className: "crewlet-data-table__resizer",
1344
- onMouseDown: (e) => {
1345
- e.stopPropagation();
1346
- handleMouseDownResize(e, columnKey);
1347
- },
1348
- onClick: (e) => e.stopPropagation(),
1349
- "aria-hidden": true
1350
- }
1351
- );
1352
- const stickyClass = isSticky ? " crewlet-data-table__th--sticky-right" : "";
1353
- if (!isSortable) {
1354
- return /* @__PURE__ */ jsxs("th", { style, className: stickyClass.trim() || void 0, children: [
1355
- column.label,
1356
- resizer
1357
- ] }, columnKey);
1358
- }
1359
- const thClass = `crewlet-data-table__th--sortable${isActive ? " is-active" : ""}${stickyClass}`;
1360
- return /* @__PURE__ */ jsxs("th", { style, className: thClass, children: [
1361
- /* @__PURE__ */ jsxs(
1362
- "button",
1363
- {
1364
- type: "button",
1365
- className: "crewlet-data-table__sort-button",
1366
- onClick: () => handleSort(columnKey),
1367
- children: [
1368
- /* @__PURE__ */ jsx("span", { children: column.label }),
1369
- arrow && /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", children: arrow })
1370
- ]
1371
- }
1372
- ),
1373
- resizer
1374
- ] }, columnKey);
1375
- }
1376
- return /* @__PURE__ */ jsxs(
1377
- "th",
1378
- {
1379
- style: { width: `${columnWidths[columnKey]}px` },
1380
- className: isSortable ? "crewlet-data-table__th--sortable" : void 0,
1381
- onClick: isSortable ? () => handleSort(columnKey) : void 0,
1382
- children: [
1383
- /* @__PURE__ */ jsxs("div", { className: "crewlet-data-table__header-content", children: [
1384
- /* @__PURE__ */ jsx("span", { children: column.label }),
1385
- isSortable && /* @__PURE__ */ jsx("span", { className: `material-symbols-outlined crewlet-data-table__sort-icon${isActive ? " is-active" : ""}`, children: arrow })
1386
- ] }),
1387
- /* @__PURE__ */ jsx(
1388
- "div",
1389
- {
1390
- className: "crewlet-data-table__column-resizer",
1391
- onMouseDown: (e) => handleMouseDownResize(e, columnKey),
1392
- onClick: (e) => e.stopPropagation()
1393
- }
1394
- )
1395
- ]
1396
- },
1397
- columnKey
1398
- );
1399
- };
1400
- const hasActionColumn = !!rowActions || showActions && !!onRowAction;
1401
- const renderActionsHeader = () => {
1402
- if (!hasActionColumn) return null;
1403
- if (!settingsInHeader) {
1404
- return /* @__PURE__ */ jsx(
1405
- "th",
1406
- {
1407
- className: "crewlet-data-table__th--chrome crewlet-data-table__th--actions",
1408
- style: { width: 1 }
1409
- },
1410
- "__actions"
1411
- );
1412
- }
1413
- return /* @__PURE__ */ jsxs("th", { style: { width: `${columnWidths.__actions}px`, position: "relative" }, children: [
1414
- "Actions",
1415
- /* @__PURE__ */ jsx(
1416
- "button",
1417
- {
1418
- type: "button",
1419
- className: "crewlet-data-table__header-btn crewlet-data-table__header-btn--reset",
1420
- onClick: (e) => {
1421
- e.stopPropagation();
1422
- resetToDefault();
1423
- },
1424
- title: "Reset to Default",
1425
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", children: "settings_backup_restore" })
1426
- }
1427
- ),
1428
- /* @__PURE__ */ jsx(
1429
- "button",
1430
- {
1431
- type: "button",
1432
- ref: settingsButtonRef,
1433
- className: "crewlet-data-table__header-btn crewlet-data-table__header-btn--settings",
1434
- onClick: (e) => {
1435
- e.stopPropagation();
1436
- openTableSettings();
1437
- },
1438
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", children: "settings" })
1439
- }
1440
- )
1441
- ] }, "__actions");
1442
- };
1443
- const renderActionsCell = (row) => {
1444
- if (!hasActionColumn) return null;
1445
- if (rowActions) {
1446
- const actions = rowActions(row);
1447
- return /* @__PURE__ */ jsx(
1448
- "td",
1449
- {
1450
- className: "crewlet-data-table__td--actions",
1451
- style: { textAlign: "right", width: 1 },
1452
- children: /* @__PURE__ */ jsx(RowActionsMenu, { actions })
1453
- },
1454
- "__actions"
1455
- );
1456
- }
1457
- return /* @__PURE__ */ jsx(
1458
- "td",
1459
- {
1460
- className: "crewlet-data-table__td--actions",
1461
- style: { width: `${columnWidths.__actions}px`, textAlign: "center" },
1462
- children: /* @__PURE__ */ jsx(
1463
- "button",
1464
- {
1465
- type: "button",
1466
- className: "crewlet-data-table__action-btn",
1467
- onClick: () => onRowAction(row),
1468
- title: actionLabel,
1469
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", children: actionIcon })
1470
- }
1471
- )
1472
- },
1473
- "__actions"
1474
- );
1475
- };
1476
- const displayedData = getDisplayedData();
1477
- const visibleColumnKeys = columnOrder.filter((key) => visibleColumns[key]);
1478
- const spacerCols = isCompact ? 1 : 0;
1479
- const colSpan = visibleColumnKeys.length + (hasActionColumn ? 1 : 0) + spacerCols;
1480
- const wrapLinesClass = wrapLines ? " crewlet-data-table--wrap-lines" : "";
1481
- const rootClass = isCompact ? `crewlet-data-table crewlet-data-table--compact crewlet-data-table--density-${density}${wrapLinesClass} ${className}`.trim() : `crewlet-data-table crewlet-data-table--default${wrapLinesClass} ${className}`.trim();
1482
- const expandable = !!renderExpandedRow;
1483
- const expandColSpan = colSpan + (expandable ? 1 : 0);
1484
- const visibleRowKeys = displayedData.map((row) => resolveRowKey(row));
1485
- const allExpanded = expandable && visibleRowKeys.length > 0 && visibleRowKeys.every((k) => expandedKeys.has(k));
1486
- const toggleExpandAll = () => {
1487
- setExpandedKeys(() => allExpanded ? /* @__PURE__ */ new Set() : new Set(visibleRowKeys));
1488
- };
1489
- const renderExpandHeader = () => {
1490
- if (!expandable) return null;
1491
- return /* @__PURE__ */ jsx(
1492
- "th",
1493
- {
1494
- className: "crewlet-data-table__expand-header",
1495
- style: { width: 32 },
1496
- children: /* @__PURE__ */ jsx(
1497
- "button",
1498
- {
1499
- type: "button",
1500
- className: "crewlet-data-table__expand-all-btn",
1501
- onClick: toggleExpandAll,
1502
- "aria-label": allExpanded ? "Collapse all rows" : "Expand all rows",
1503
- "aria-expanded": allExpanded,
1504
- title: allExpanded ? "Collapse all" : "Expand all",
1505
- children: /* @__PURE__ */ jsx(
1506
- "span",
1507
- {
1508
- className: `material-symbols-outlined crewlet-data-table__chevron${allExpanded ? " is-open" : ""}`,
1509
- "aria-hidden": true,
1510
- children: "chevron_right"
1511
- }
1512
- )
1513
- }
1514
- )
1515
- },
1516
- "__expand"
1517
- );
1518
- };
1519
- const renderExpandCell = (_row, isOpen, canExpand) => {
1520
- if (!expandable) return null;
1521
- return /* @__PURE__ */ jsx("td", { className: "crewlet-data-table__expand-cell", "aria-hidden": true, style: { width: 32 }, children: canExpand && /* @__PURE__ */ jsx(
1522
- "span",
1523
- {
1524
- className: `material-symbols-outlined crewlet-data-table__chevron${isOpen ? " is-open" : ""}`,
1525
- children: "chevron_right"
1526
- }
1527
- ) }, "__expand");
1528
- };
1529
- const topControlsVisible = isCompact;
1530
- const headerVisible = title != null || description != null || icon != null;
1531
- const renderTopControls = topControlsVisible ? /* @__PURE__ */ jsxs("div", { className: "crewlet-data-table__toolbar-end", children: [
1532
- /* @__PURE__ */ jsxs("div", { className: "crewlet-data-table__pagination", "aria-label": "Pagination", children: [
1533
- /* @__PURE__ */ jsx(
1534
- "button",
1535
- {
1536
- type: "button",
1537
- className: "crewlet-data-table__page-btn",
1538
- onClick: () => setCurrentPage((p) => Math.max(1, p - 1)),
1539
- disabled: currentPage <= 1,
1540
- "aria-label": "Previous page",
1541
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: "chevron_left" })
1542
- }
1543
- ),
1544
- /* @__PURE__ */ jsx("span", { className: "crewlet-data-table__page-number", "aria-live": "polite", children: currentPage }),
1545
- /* @__PURE__ */ jsx(
1546
- "button",
1547
- {
1548
- type: "button",
1549
- className: "crewlet-data-table__page-btn",
1550
- onClick: () => setCurrentPage((p) => Math.min(totalPages, p + 1)),
1551
- disabled: currentPage >= totalPages,
1552
- "aria-label": "Next page",
1553
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: "chevron_right" })
1554
- }
1555
- )
1556
- ] }),
1557
- settingsEnabled && /* @__PURE__ */ jsx(
1558
- "button",
1559
- {
1560
- type: "button",
1561
- ref: settingsButtonRef,
1562
- className: "crewlet-data-table__settings-btn",
1563
- onClick: openTableSettings,
1564
- "aria-label": "Table settings",
1565
- title: "Table settings",
1566
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: "settings" })
1567
- }
1568
- )
1569
- ] }) : null;
1570
- const toolbarVisible = renderToolbar != null || topControlsVisible && !headerVisible;
1571
- return /* @__PURE__ */ jsxs("div", { className: rootClass, children: [
1572
- headerVisible && /* @__PURE__ */ jsxs("header", { className: "crewlet-data-table__header", children: [
1573
- icon != null && /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined crewlet-data-table__header-icon", "aria-hidden": true, children: icon }),
1574
- /* @__PURE__ */ jsxs("div", { className: "crewlet-data-table__header-text", children: [
1575
- title != null && /* @__PURE__ */ jsx("h3", { className: "crewlet-data-table__title", children: title }),
1576
- description != null && /* @__PURE__ */ jsx("p", { className: "crewlet-data-table__description", children: description })
1577
- ] }),
1578
- renderTopControls
1579
- ] }),
1580
- toolbarVisible && /* @__PURE__ */ jsxs("div", { className: "crewlet-data-table__toolbar", children: [
1581
- renderToolbar != null && /* @__PURE__ */ jsx("div", { className: "crewlet-data-table__toolbar-start", children: renderToolbar }),
1582
- onClearFilters != null && filtersActive && /* @__PURE__ */ jsx(
1583
- "button",
1584
- {
1585
- type: "button",
1586
- className: "crewlet-data-table__clear-filters",
1587
- onClick: onClearFilters,
1588
- children: clearFiltersLabel
1589
- }
1590
- ),
1591
- topControlsVisible && !headerVisible && renderTopControls
1592
- ] }),
1593
- /* @__PURE__ */ jsx(
1594
- "div",
1595
- {
1596
- className: [
1597
- "crewlet-data-table__rail",
1598
- /*
1599
- * When the table carries a sticky-right action column the
1600
- * row is treated as a frozen, screen-fitting surface: the
1601
- * fit pass redistributes regular columns to consume only
1602
- * the room left after the sticky pane and the gutter, the
1603
- * resize handles are suppressed, and the scroll container
1604
- * locks horizontal overflow. The modifier class flips that
1605
- * behaviour on so the CSS can also drop the scrollbar mask
1606
- * that would otherwise float over rows when no scrollbar
1607
- * is present.
1608
- */
1609
- isCompact && columnOrder.some(
1610
- (k) => columns[k]?.sticky === "right" && visibleColumns[k] !== false
1611
- ) ? "crewlet-data-table__rail--frozen" : void 0
1612
- ].filter(Boolean).join(" "),
1613
- style: isCompact ? {
1614
- "--crewlet-data-table-sticky-pane-width": `${columnOrder.filter((key) => columns[key]?.sticky === "right" && visibleColumns[key] !== false).reduce((sum, key) => sum + (columnWidths[key] ?? minColumnWidth), 0)}px`
1615
- } : void 0,
1616
- children: /* @__PURE__ */ jsx("div", { className: "crewlet-data-table__scroll", ref: scrollRef, children: /* @__PURE__ */ jsxs("table", { className: "crewlet-data-table__table", children: [
1617
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
1618
- renderExpandHeader(),
1619
- columnOrder.filter((key) => columns[key]?.sticky !== "right").map((columnKey) => renderHeaderCell(columnKey)),
1620
- isCompact && /* @__PURE__ */ jsx(
1621
- "th",
1622
- {
1623
- className: "crewlet-data-table__th--chrome crewlet-data-table__th--spacer",
1624
- "aria-hidden": true
1625
- },
1626
- "__spacer"
1627
- ),
1628
- columnOrder.filter((key) => columns[key]?.sticky === "right").map((columnKey) => renderHeaderCell(columnKey)),
1629
- renderActionsHeader()
1630
- ] }) }),
1631
- /* @__PURE__ */ jsx("tbody", { children: loading && displayedData.length === 0 ? (
1632
- /*
1633
- * Skeleton rows live inside the real <tbody> and reuse the
1634
- * live column set + widths, so the shimmer lines up exactly
1635
- * with the data that replaces it. Bar widths cycle a fixed
1636
- * pattern (not Math.random) so re-renders never reshuffle
1637
- * the shimmer.
1638
- */
1639
- Array.from({ length: Math.max(1, skeletonRows) }, (_, rowIdx) => {
1640
- const widths = [72, 46, 84, 58, 64];
1641
- const visibleKeys = columnOrder.filter(
1642
- (key) => columns[key]?.sticky !== "right" && visibleColumns[key] !== false
1643
- );
1644
- return /* @__PURE__ */ jsxs("tr", { className: "crewlet-data-table__skeleton-row", "aria-hidden": true, children: [
1645
- expandable && /* @__PURE__ */ jsx("td", { className: "crewlet-data-table__expand-cell", style: { width: 32 } }, "__expand"),
1646
- visibleKeys.map((columnKey, colIdx) => /* @__PURE__ */ jsx(
1647
- "td",
1648
- {
1649
- style: columnWidths[columnKey] != null ? { width: `${columnWidths[columnKey]}px` } : void 0,
1650
- children: /* @__PURE__ */ jsx(
1651
- "span",
1652
- {
1653
- className: "crewlet-data-table__skeleton-bar",
1654
- style: {
1655
- width: `${widths[(rowIdx + colIdx) % widths.length]}%`,
1656
- animationDelay: `${rowIdx * 0.1}s`
1657
- }
1658
- }
1659
- )
1660
- },
1661
- columnKey
1662
- )),
1663
- isCompact && /* @__PURE__ */ jsx("td", { className: "crewlet-data-table__td--spacer", "aria-hidden": true }, "__spacer"),
1664
- columnOrder.filter((key) => columns[key]?.sticky === "right" && visibleColumns[key] !== false).map((columnKey) => /* @__PURE__ */ jsx("td", { className: "crewlet-data-table__td--sticky-right" }, columnKey)),
1665
- hasActionColumn && /* @__PURE__ */ jsx("td", { className: "crewlet-data-table__td--actions" }, "__actions")
1666
- ] }, `__skeleton_${rowIdx}`);
1667
- })
1668
- ) : error ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
1669
- "td",
1670
- {
1671
- colSpan: expandColSpan,
1672
- className: "crewlet-data-table__empty",
1673
- children: error
1674
- }
1675
- ) }) : displayedData.length === 0 ? /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
1676
- "td",
1677
- {
1678
- colSpan: expandColSpan,
1679
- className: "crewlet-data-table__empty",
1680
- children: emptyMessage
1681
- }
1682
- ) }) : displayedData.map((row) => {
1683
- const trKey = resolveRowKey(row);
1684
- const isArchived = archivedPredicate ? archivedPredicate(row) : false;
1685
- const expanded = expandable ? expandedKeys.has(trKey) : false;
1686
- const expandedContent = expandable ? renderExpandedRow(row) : null;
1687
- const canExpand = expandable && expandedContent != null;
1688
- const isClickable = !!onRowClick || canExpand;
1689
- const trClass = [
1690
- isArchived ? "crewlet-data-table__row--archived" : void 0,
1691
- isClickable ? "crewlet-data-table__row--clickable" : void 0,
1692
- canExpand ? "crewlet-data-table__row--expandable" : void 0,
1693
- expanded ? "is-expanded" : void 0
1694
- ].filter(Boolean).join(" ");
1695
- const accessory = renderAccessoryRow ? renderAccessoryRow(row) : null;
1696
- const onRowSurfaceClick = canExpand ? () => toggleExpanded(trKey) : onRowClick ? () => onRowClick(row) : void 0;
1697
- return /* @__PURE__ */ jsxs(React.Fragment, { children: [
1698
- /* @__PURE__ */ jsxs(
1699
- "tr",
1700
- {
1701
- className: trClass || void 0,
1702
- title: isArchived ? `This row is ${archivedLabel}` : void 0,
1703
- onClick: onRowSurfaceClick,
1704
- tabIndex: canExpand ? 0 : void 0,
1705
- "aria-expanded": canExpand ? expanded : void 0,
1706
- onKeyDown: canExpand ? (e) => {
1707
- if (e.key === "Enter" || e.key === " ") {
1708
- e.preventDefault();
1709
- toggleExpanded(trKey);
1710
- }
1711
- } : void 0,
1712
- children: [
1713
- renderExpandCell(row, expanded, canExpand),
1714
- columnOrder.filter((columnKey) => columns[columnKey]?.sticky !== "right").map((columnKey) => {
1715
- if (!visibleColumns[columnKey]) return null;
1716
- return renderBodyCell(row, columnKey);
1717
- }),
1718
- isCompact && /* @__PURE__ */ jsx("td", { className: "crewlet-data-table__td--spacer", "aria-hidden": true }, "__spacer"),
1719
- columnOrder.filter((columnKey) => columns[columnKey]?.sticky === "right").map((columnKey) => {
1720
- if (!visibleColumns[columnKey]) return null;
1721
- return renderBodyCell(row, columnKey);
1722
- }),
1723
- renderActionsCell(row)
1724
- ]
1725
- }
1726
- ),
1727
- expanded && expandedContent != null && /* @__PURE__ */ jsx(
1728
- "tr",
1729
- {
1730
- className: [
1731
- "crewlet-data-table__expanded-row",
1732
- isArchived ? "crewlet-data-table__row--archived" : void 0
1733
- ].filter(Boolean).join(" "),
1734
- children: /* @__PURE__ */ jsx("td", { colSpan: expandColSpan, className: "crewlet-data-table__expanded-cell", children: expandedContent })
1735
- }
1736
- ),
1737
- accessory != null && /* @__PURE__ */ jsx(
1738
- "tr",
1739
- {
1740
- className: [
1741
- "crewlet-data-table__accessory-row",
1742
- isArchived ? "crewlet-data-table__row--archived" : void 0
1743
- ].filter(Boolean).join(" "),
1744
- children: /* @__PURE__ */ jsx("td", { colSpan, className: "crewlet-data-table__accessory-cell", children: accessory })
1745
- }
1746
- )
1747
- ] }, trKey ?? JSON.stringify(row));
1748
- }) })
1749
- ] }) })
1750
- }
1751
- ),
1752
- hasMore && onLoadMore && /* @__PURE__ */ jsx("div", { className: "crewlet-data-table__load-more", children: /* @__PURE__ */ jsx(
1753
- Button,
1754
- {
1755
- variant: "outline",
1756
- size: "small",
1757
- onClick: onLoadMore,
1758
- disabled: loadingMore,
1759
- children: loadingMore ? "Loading\u2026" : loadMoreLabel
1760
- }
1761
- ) }),
1762
- /* @__PURE__ */ jsxs(
1763
- Modal,
1764
- {
1765
- open: tableSettingsOpen,
1766
- onClose: cancelTableSettings,
1767
- title: "Table Settings",
1768
- size: effectiveSettingsVariant === "compact" ? "sm" : "lg",
1769
- footer: /* @__PURE__ */ jsxs("div", { className: "crewlet-data-table__settings-footer", children: [
1770
- /* @__PURE__ */ jsx(
1771
- Button,
1772
- {
1773
- variant: "secondary",
1774
- onClick: handleResetTableSettings,
1775
- leadingIcon: resetConfirmed ? /* @__PURE__ */ jsx(
1776
- "span",
1777
- {
1778
- className: "material-symbols-outlined crewlet-data-table__reset-confirm-icon",
1779
- "aria-hidden": true,
1780
- children: "check"
1781
- }
1782
- ) : void 0,
1783
- "aria-live": "polite",
1784
- children: "Reset to Default"
1785
- }
1786
- ),
1787
- /* @__PURE__ */ jsxs("div", { className: "crewlet-data-table__settings-footer-right", children: [
1788
- /* @__PURE__ */ jsx(Button, { variant: "tertiary", onClick: cancelTableSettings, children: "Cancel" }),
1789
- /* @__PURE__ */ jsx(Button, { variant: "primary", onClick: applyTableSettings, children: "Apply" })
1790
- ] })
1791
- ] }),
1792
- children: [
1793
- /* @__PURE__ */ jsxs("div", { className: "crewlet-data-table__settings-section", children: [
1794
- /* @__PURE__ */ jsx("h3", { className: "crewlet-data-table__settings-section-title", children: effectiveSettingsVariant === "compact" ? "Page size" : "Items per page" }),
1795
- /* @__PURE__ */ jsxs("div", { className: "crewlet-data-table__items-per-page-options", children: [
1796
- itemsPerPageOptions.map((value) => /* @__PURE__ */ jsx(
1797
- "button",
1798
- {
1799
- type: "button",
1800
- className: `crewlet-data-table__items-per-page-btn${draftItemsPerPage === value ? " is-active" : ""}`,
1801
- onClick: () => handleItemsPerPageChange(value),
1802
- children: value
1803
- },
1804
- value
1805
- )),
1806
- /* @__PURE__ */ jsx(
1807
- "button",
1808
- {
1809
- type: "button",
1810
- className: `crewlet-data-table__items-per-page-btn${draftItemsPerPage === data.length ? " is-active" : ""}`,
1811
- onClick: () => handleItemsPerPageChange(data.length),
1812
- children: "All"
1813
- }
1814
- )
1815
- ] })
1816
- ] }),
1817
- /* @__PURE__ */ jsx("div", { className: "crewlet-data-table__settings-section", children: /* @__PURE__ */ jsxs("label", { className: "crewlet-data-table__toggle-row", children: [
1818
- /* @__PURE__ */ jsx(
1819
- "input",
1820
- {
1821
- type: "checkbox",
1822
- className: "crewlet-data-table__toggle-checkbox",
1823
- checked: draftWrapLines,
1824
- onChange: (e) => setDraftWrapLines(e.target.checked)
1825
- }
1826
- ),
1827
- /* @__PURE__ */ jsxs("span", { className: "crewlet-data-table__toggle-text", children: [
1828
- /* @__PURE__ */ jsx("span", { className: "crewlet-data-table__toggle-label", children: "Wrap lines" }),
1829
- /* @__PURE__ */ jsx("span", { className: "crewlet-data-table__toggle-hint", children: "Enable to wrap table cell content, disable to truncate text." })
1830
- ] })
1831
- ] }) }),
1832
- effectiveSettingsVariant === "rich" && /* @__PURE__ */ jsxs("div", { className: "crewlet-data-table__settings-section", children: [
1833
- /* @__PURE__ */ jsx("h3", { className: "crewlet-data-table__settings-section-title", children: "Column Order & Visibility" }),
1834
- /* @__PURE__ */ jsx("p", { className: "crewlet-data-table__settings-section-hint", children: "Drag to reorder, check/uncheck to show/hide" }),
1835
- /* @__PURE__ */ jsx("div", { className: "crewlet-data-table__column-toggles", children: draftColumnOrder.map((key) => /* @__PURE__ */ jsxs(
1836
- "div",
1837
- {
1838
- className: `crewlet-data-table__column-toggle-item${draggedColumn === key ? " is-dragging" : ""}`,
1839
- draggable: true,
1840
- onDragStart: (e) => handleDragStart(e, key),
1841
- onDragOver: (e) => handleDragOver(e, key),
1842
- onDrop: (e) => handleDrop(e),
1843
- onDragEnd: handleDragEnd,
1844
- children: [
1845
- /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined crewlet-data-table__column-drag-handle", children: "drag_indicator" }),
1846
- /* @__PURE__ */ jsx(
1847
- "input",
1848
- {
1849
- type: "checkbox",
1850
- checked: !!draftVisibleColumns[key],
1851
- onChange: () => handleToggleColumn(key),
1852
- className: "crewlet-data-table__column-toggle-checkbox"
1853
- }
1854
- ),
1855
- /* @__PURE__ */ jsx("span", { className: "crewlet-data-table__column-toggle-label", children: columns[key]?.label && String(columns[key]?.label).trim() !== "" ? columns[key]?.label : key })
1856
- ]
1857
- },
1858
- key
1859
- )) })
1860
- ] })
1861
- ]
1862
- }
1863
- )
1864
- ] });
1865
- };
1866
- var pad = (n) => String(n).padStart(2, "0");
1867
- var formatValue = (p) => {
1868
- return `${p.year}-${pad(p.month + 1)}-${pad(p.day)}T${pad(p.hour)}:${pad(p.minute)}:${pad(p.second)}`;
1869
- };
1870
- var parseValue = (s) => {
1871
- if (!s) return null;
1872
- const tIndex = s.indexOf("T");
1873
- const datePart = tIndex >= 0 ? s.slice(0, tIndex) : s;
1874
- const timePart = tIndex >= 0 ? s.slice(tIndex + 1) : "00:00:00";
1875
- const dateBits = datePart.split("-");
1876
- if (dateBits.length < 3) return null;
1877
- const y = parseInt(dateBits[0], 10);
1878
- const m = parseInt(dateBits[1], 10);
1879
- const d = parseInt(dateBits[2], 10);
1880
- if (!Number.isFinite(y) || !Number.isFinite(m) || !Number.isFinite(d)) return null;
1881
- const timeBits = timePart.split(":");
1882
- const h = parseInt(timeBits[0] || "0", 10);
1883
- const mn = parseInt(timeBits[1] || "0", 10);
1884
- const sec = parseInt(timeBits[2] || "0", 10);
1885
- return {
1886
- year: y,
1887
- month: m - 1,
1888
- day: d,
1889
- hour: Number.isFinite(h) ? h : 0,
1890
- minute: Number.isFinite(mn) ? mn : 0,
1891
- second: Number.isFinite(sec) ? sec : 0
1892
- };
1893
- };
1894
- var daysInMonth = (year, month) => new Date(year, month + 1, 0).getDate();
1895
- var startWeekday = (year, month, firstDay) => {
1896
- const d = new Date(year, month, 1).getDay();
1897
- return (d - firstDay + 7) % 7;
1898
- };
1899
- var MONTH_NAMES = [
1900
- "January",
1901
- "February",
1902
- "March",
1903
- "April",
1904
- "May",
1905
- "June",
1906
- "July",
1907
- "August",
1908
- "September",
1909
- "October",
1910
- "November",
1911
- "December"
1912
- ];
1913
- var WEEKDAY_LABELS_SUN = ["S", "M", "T", "W", "T", "F", "S"];
1914
- var WEEKDAY_LABELS_MON = ["M", "T", "W", "T", "F", "S", "S"];
1915
- var formatLabel = (s, showTime) => {
1916
- const parsed = parseValue(s);
1917
- if (!parsed) return "";
1918
- const date = new Date(parsed.year, parsed.month, parsed.day, parsed.hour, parsed.minute, parsed.second);
1919
- if (showTime) {
1920
- return date.toLocaleString(void 0, {
1921
- year: "numeric",
1922
- month: "short",
1923
- day: "numeric",
1924
- hour: "2-digit",
1925
- minute: "2-digit",
1926
- second: "2-digit",
1927
- hour12: false
1928
- });
1929
- }
1930
- return date.toLocaleDateString(void 0, {
1931
- year: "numeric",
1932
- month: "short",
1933
- day: "numeric"
1934
- });
1935
- };
1936
- var compareDates = (a, b) => {
1937
- if (a.year !== b.year) return a.year - b.year;
1938
- if (a.month !== b.month) return a.month - b.month;
1939
- return a.day - b.day;
1940
- };
1941
- var parseBoundDate = (s) => {
1942
- const p = parseValue(s);
1943
- if (!p) return null;
1944
- return { year: p.year, month: p.month, day: p.day };
1945
- };
1946
- function DateTimePicker({
1947
- value,
1948
- onChange,
1949
- placeholder = "Pick a date",
1950
- showTime = true,
1951
- minDate,
1952
- maxDate,
1953
- firstDayOfWeek = 0,
1954
- ariaLabel,
1955
- className = "",
1956
- disabled = false,
1957
- size = "md"
1958
- }) {
1959
- const parsed = useMemo(() => parseValue(value), [value]);
1960
- const minBound = useMemo(() => parseBoundDate(minDate), [minDate]);
1961
- const maxBound = useMemo(() => parseBoundDate(maxDate), [maxDate]);
1962
- const today = useMemo(() => {
1963
- const now = /* @__PURE__ */ new Date();
1964
- return { year: now.getFullYear(), month: now.getMonth(), day: now.getDate() };
1965
- }, []);
1966
- const [viewMonth, setViewMonth] = useState(() => {
1967
- if (parsed) return { year: parsed.year, month: parsed.month };
1968
- return { year: today.year, month: today.month };
1969
- });
1970
- useEffect(() => {
1971
- if (parsed) setViewMonth({ year: parsed.year, month: parsed.month });
1972
- }, [parsed?.year, parsed?.month]);
1973
- const weekdayLabels = firstDayOfWeek === 1 ? WEEKDAY_LABELS_MON : WEEKDAY_LABELS_SUN;
1974
- const totalDays = daysInMonth(viewMonth.year, viewMonth.month);
1975
- const leading = startWeekday(viewMonth.year, viewMonth.month, firstDayOfWeek);
1976
- const cells = useMemo(() => {
1977
- const out = [];
1978
- const prevTotal = daysInMonth(
1979
- viewMonth.month === 0 ? viewMonth.year - 1 : viewMonth.year,
1980
- viewMonth.month === 0 ? 11 : viewMonth.month - 1
1981
- );
1982
- for (let i = 0; i < leading; i += 1) {
1983
- out.push({ day: prevTotal - leading + 1 + i, inMonth: false });
1984
- }
1985
- for (let d = 1; d <= totalDays; d += 1) {
1986
- out.push({ day: d, inMonth: true });
1987
- }
1988
- while (out.length < 42) {
1989
- out.push({ day: out.length - leading - totalDays + 1, inMonth: false });
1990
- }
1991
- return out;
1992
- }, [viewMonth.year, viewMonth.month, leading, totalDays]);
1993
- const stepMonth = (delta) => {
1994
- setViewMonth((prev) => {
1995
- const next = prev.month + delta;
1996
- const carry = Math.floor(next / 12);
1997
- const normalised = (next % 12 + 12) % 12;
1998
- return { year: prev.year + carry, month: normalised };
1999
- });
2000
- };
2001
- const stepYear = (delta) => {
2002
- setViewMonth((prev) => ({ year: prev.year + delta, month: prev.month }));
2003
- };
2004
- const pickDay = (day, inMonth) => {
2005
- let targetYear = viewMonth.year;
2006
- let targetMonth = viewMonth.month;
2007
- if (!inMonth) {
2008
- if (day > 20) {
2009
- targetMonth -= 1;
2010
- if (targetMonth < 0) {
2011
- targetMonth = 11;
2012
- targetYear -= 1;
2013
- }
2014
- } else {
2015
- targetMonth += 1;
2016
- if (targetMonth > 11) {
2017
- targetMonth = 0;
2018
- targetYear += 1;
2019
- }
2020
- }
2021
- setViewMonth({ year: targetYear, month: targetMonth });
2022
- }
2023
- onChange(formatValue({
2024
- year: targetYear,
2025
- month: targetMonth,
2026
- day,
2027
- hour: parsed?.hour ?? 0,
2028
- minute: parsed?.minute ?? 0,
2029
- second: parsed?.second ?? 0
2030
- }));
2031
- };
2032
- const setTime = (hour, minute, second) => {
2033
- const base = parsed ?? {
2034
- year: today.year,
2035
- month: today.month,
2036
- day: today.day,
2037
- hour: 0,
2038
- minute: 0,
2039
- second: 0
2040
- };
2041
- onChange(formatValue({ ...base, hour, minute, second }));
2042
- };
2043
- const isOutOfBounds = (cellDate) => {
2044
- if (minBound && compareDates(cellDate, minBound) < 0) return true;
2045
- if (maxBound && compareDates(cellDate, maxBound) > 0) return true;
2046
- return false;
2047
- };
2048
- const isToday = (cellYear, cellMonth, cellDay) => cellYear === today.year && cellMonth === today.month && cellDay === today.day;
2049
- const isSelected = (cellYear, cellMonth, cellDay) => {
2050
- if (!parsed) return false;
2051
- return cellYear === parsed.year && cellMonth === parsed.month && cellDay === parsed.day;
2052
- };
2053
- const triggerLabel = formatLabel(value, showTime);
2054
- const wrapClass = [
2055
- "crewlet-datetime",
2056
- `crewlet-datetime--${size}`,
2057
- value ? "is-set" : "",
2058
- disabled ? "is-disabled" : "",
2059
- className
2060
- ].filter(Boolean).join(" ");
2061
- return /* @__PURE__ */ jsx(
2062
- Popover,
2063
- {
2064
- align: "start",
2065
- side: "bottom",
2066
- width: "match",
2067
- className: "crewlet-datetime__popover",
2068
- trigger: (open, toggle) => /* @__PURE__ */ jsxs(
2069
- "button",
2070
- {
2071
- type: "button",
2072
- className: `${wrapClass}${open ? " is-open" : ""}`,
2073
- onClick: () => {
2074
- if (!disabled) toggle();
2075
- },
2076
- disabled,
2077
- "aria-haspopup": "dialog",
2078
- "aria-expanded": open,
2079
- "aria-label": ariaLabel || "Date",
2080
- children: [
2081
- /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined crewlet-datetime__icon", "aria-hidden": true, children: "calendar_today" }),
2082
- /* @__PURE__ */ jsx("span", { className: `crewlet-datetime__label${value ? "" : " is-placeholder"}`, children: value ? triggerLabel : placeholder }),
2083
- /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined crewlet-datetime__chevron", "aria-hidden": true, children: open ? "expand_less" : "expand_more" })
2084
- ]
2085
- }
2086
- ),
2087
- children: (close) => /* @__PURE__ */ jsxs("div", { className: "crewlet-datetime__panel", role: "dialog", "aria-label": "Date picker", children: [
2088
- /* @__PURE__ */ jsxs("div", { className: "crewlet-datetime__nav", children: [
2089
- /* @__PURE__ */ jsx(
2090
- "button",
2091
- {
2092
- type: "button",
2093
- className: "crewlet-datetime__nav-btn",
2094
- onClick: () => stepYear(-1),
2095
- "aria-label": "Previous year",
2096
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: "keyboard_double_arrow_left" })
2097
- }
2098
- ),
2099
- /* @__PURE__ */ jsx(
2100
- "button",
2101
- {
2102
- type: "button",
2103
- className: "crewlet-datetime__nav-btn",
2104
- onClick: () => stepMonth(-1),
2105
- "aria-label": "Previous month",
2106
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: "chevron_left" })
2107
- }
2108
- ),
2109
- /* @__PURE__ */ jsxs("div", { className: "crewlet-datetime__title", "aria-live": "polite", children: [
2110
- MONTH_NAMES[viewMonth.month],
2111
- " ",
2112
- viewMonth.year
2113
- ] }),
2114
- /* @__PURE__ */ jsx(
2115
- "button",
2116
- {
2117
- type: "button",
2118
- className: "crewlet-datetime__nav-btn",
2119
- onClick: () => stepMonth(1),
2120
- "aria-label": "Next month",
2121
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: "chevron_right" })
2122
- }
2123
- ),
2124
- /* @__PURE__ */ jsx(
2125
- "button",
2126
- {
2127
- type: "button",
2128
- className: "crewlet-datetime__nav-btn",
2129
- onClick: () => stepYear(1),
2130
- "aria-label": "Next year",
2131
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: "keyboard_double_arrow_right" })
2132
- }
2133
- )
2134
- ] }),
2135
- /* @__PURE__ */ jsx("div", { className: "crewlet-datetime__weekdays", "aria-hidden": true, children: weekdayLabels.map((label, i) => /* @__PURE__ */ jsx("span", { className: "crewlet-datetime__weekday", children: label }, i)) }),
2136
- /* @__PURE__ */ jsx("div", { className: "crewlet-datetime__grid", role: "grid", children: cells.map((cell, i) => {
2137
- let cellYear = viewMonth.year;
2138
- let cellMonth = viewMonth.month;
2139
- if (!cell.inMonth) {
2140
- if (cell.day > 20) {
2141
- cellMonth -= 1;
2142
- if (cellMonth < 0) {
2143
- cellMonth = 11;
2144
- cellYear -= 1;
2145
- }
2146
- } else {
2147
- cellMonth += 1;
2148
- if (cellMonth > 11) {
2149
- cellMonth = 0;
2150
- cellYear += 1;
2151
- }
2152
- }
2153
- }
2154
- const outOfBounds = isOutOfBounds({ year: cellYear, month: cellMonth, day: cell.day });
2155
- const todayCell = isToday(cellYear, cellMonth, cell.day);
2156
- const selected = isSelected(cellYear, cellMonth, cell.day);
2157
- const cls = [
2158
- "crewlet-datetime__cell",
2159
- cell.inMonth ? "" : "is-outside",
2160
- outOfBounds ? "is-disabled" : "",
2161
- todayCell ? "is-today" : "",
2162
- selected ? "is-selected" : ""
2163
- ].filter(Boolean).join(" ");
2164
- return /* @__PURE__ */ jsx(
2165
- "button",
2166
- {
2167
- type: "button",
2168
- role: "gridcell",
2169
- className: cls,
2170
- disabled: outOfBounds,
2171
- onClick: () => {
2172
- if (outOfBounds) return;
2173
- pickDay(cell.day, cell.inMonth);
2174
- if (!showTime) close();
2175
- },
2176
- children: cell.day
2177
- },
2178
- i
2179
- );
2180
- }) }),
2181
- showTime && /* @__PURE__ */ jsxs("div", { className: "crewlet-datetime__time", children: [
2182
- /* @__PURE__ */ jsx("span", { className: "crewlet-datetime__time-label", children: "Time" }),
2183
- /* @__PURE__ */ jsx(
2184
- TimeUnitInput,
2185
- {
2186
- value: parsed?.hour ?? 0,
2187
- max: 23,
2188
- ariaLabel: "Hour",
2189
- onCommit: (n) => setTime(n, parsed?.minute ?? 0, parsed?.second ?? 0)
2190
- }
2191
- ),
2192
- /* @__PURE__ */ jsx("span", { className: "crewlet-datetime__time-sep", children: ":" }),
2193
- /* @__PURE__ */ jsx(
2194
- TimeUnitInput,
2195
- {
2196
- value: parsed?.minute ?? 0,
2197
- max: 59,
2198
- ariaLabel: "Minute",
2199
- onCommit: (n) => setTime(parsed?.hour ?? 0, n, parsed?.second ?? 0)
2200
- }
2201
- ),
2202
- /* @__PURE__ */ jsx("span", { className: "crewlet-datetime__time-sep", children: ":" }),
2203
- /* @__PURE__ */ jsx(
2204
- TimeUnitInput,
2205
- {
2206
- value: parsed?.second ?? 0,
2207
- max: 59,
2208
- ariaLabel: "Second",
2209
- onCommit: (n) => setTime(parsed?.hour ?? 0, parsed?.minute ?? 0, n)
2210
- }
2211
- )
2212
- ] }),
2213
- /* @__PURE__ */ jsxs("div", { className: "crewlet-datetime__footer", children: [
2214
- /* @__PURE__ */ jsx(
2215
- "button",
2216
- {
2217
- type: "button",
2218
- className: "crewlet-datetime__footer-btn",
2219
- onClick: () => {
2220
- onChange("");
2221
- },
2222
- children: "Clear"
2223
- }
2224
- ),
2225
- /* @__PURE__ */ jsx("span", { className: "crewlet-datetime__footer-spacer" }),
2226
- /* @__PURE__ */ jsx(
2227
- "button",
2228
- {
2229
- type: "button",
2230
- className: "crewlet-datetime__footer-btn is-primary",
2231
- onClick: () => close(),
2232
- children: "Done"
2233
- }
2234
- )
2235
- ] })
2236
- ] })
2237
- }
2238
- );
2239
- }
2240
- function TimeUnitInput({ value, max, ariaLabel, onCommit }) {
2241
- const [display, setDisplay] = useState(() => pad(value));
2242
- useEffect(() => {
2243
- setDisplay(pad(value));
2244
- }, [value]);
2245
- const commit = () => {
2246
- const n = Math.max(0, Math.min(max, parseInt(display, 10) || 0));
2247
- setDisplay(pad(n));
2248
- if (n !== value) onCommit(n);
2249
- };
2250
- return /* @__PURE__ */ jsx(
2251
- "input",
2252
- {
2253
- type: "text",
2254
- inputMode: "numeric",
2255
- pattern: "[0-9]*",
2256
- maxLength: 2,
2257
- className: "crewlet-datetime__time-input",
2258
- value: display,
2259
- onChange: (e) => setDisplay(e.target.value.replace(/[^0-9]/g, "").slice(0, 2)),
2260
- onBlur: commit,
2261
- onKeyDown: (e) => {
2262
- if (e.key === "Enter") {
2263
- e.preventDefault();
2264
- e.currentTarget.blur();
2265
- }
2266
- },
2267
- onFocus: (e) => e.currentTarget.select(),
2268
- "aria-label": ariaLabel
2269
- }
2270
- );
2271
- }
2272
- var Eyebrow = ({
2273
- as: As = "span",
2274
- variant = "muted",
2275
- className = "",
2276
- children,
2277
- ...rest
2278
- }) => {
2279
- const classes = ["crewlet-eyebrow", `crewlet-eyebrow--${variant}`, className].filter(Boolean).join(" ");
2280
- return /* @__PURE__ */ jsx(As, { ...rest, className: classes, children });
2281
- };
2282
- var IconButton = ({
2283
- size = "sm",
2284
- variant = "ghost",
2285
- className = "",
2286
- type = "button",
2287
- asChild = false,
2288
- children,
2289
- ...rest
2290
- }) => {
2291
- const classes = [
2292
- "crewlet-icon-btn",
2293
- `crewlet-icon-btn--${size}`,
2294
- `crewlet-icon-btn--${variant}`,
2295
- className
2296
- ].filter(Boolean).join(" ");
2297
- if (asChild) {
2298
- const child = Children.only(children);
2299
- if (!isValidElement(child)) {
2300
- throw new Error("IconButton with asChild expects exactly one valid React element child.");
2301
- }
2302
- const childEl = child;
2303
- const merged = [childEl.props.className, classes].filter(Boolean).join(" ");
2304
- return cloneElement(childEl, { ...rest, className: merged });
2305
- }
2306
- return /* @__PURE__ */ jsx("button", { type, className: classes, ...rest, children });
2307
- };
2308
- var ImageUpload = ({
2309
- src,
2310
- name,
2311
- size = 64,
2312
- shape = "circle",
2313
- readOnly = false,
2314
- uploading = false,
2315
- accept = "image/*",
2316
- onSelect,
2317
- onRemove,
2318
- className = "",
2319
- style,
2320
- ...rest
2321
- }) => {
2322
- const inputRef = useRef(null);
2323
- const interactive = !readOnly && !uploading;
2324
- const canRemove = Boolean(src) && !readOnly && Boolean(onRemove);
2325
- const openPicker = () => {
2326
- if (!interactive) {
2327
- return;
2328
- }
2329
- inputRef.current?.click();
2330
- };
2331
- const handleChange = (event) => {
2332
- const file = event.target.files?.[0];
2333
- event.target.value = "";
2334
- if (file) {
2335
- onSelect(file);
2336
- }
2337
- };
2338
- const handleRemove = (event) => {
2339
- event.stopPropagation();
2340
- onRemove?.();
2341
- };
2342
- const empty = !src && !readOnly;
2343
- const wrapperClasses = [
2344
- "crewlet-image-upload",
2345
- `crewlet-image-upload--${shape}`,
2346
- uploading ? "is-uploading" : "",
2347
- empty ? "is-empty" : "",
2348
- className
2349
- ].filter(Boolean).join(" ");
2350
- const sizeStyle = {
2351
- "--crewlet-image-upload-size": `${size}px`,
2352
- ...style
2353
- };
2354
- const triggerLabel = uploading ? "Uploading image" : src ? "Change image" : "Upload image";
2355
- return /* @__PURE__ */ jsxs("div", { ...rest, className: wrapperClasses, style: sizeStyle, children: [
2356
- /* @__PURE__ */ jsxs(
2357
- "button",
2358
- {
2359
- type: "button",
2360
- className: "crewlet-image-upload__trigger",
2361
- onClick: openPicker,
2362
- disabled: !interactive,
2363
- "aria-label": triggerLabel,
2364
- "aria-busy": uploading,
2365
- children: [
2366
- /* @__PURE__ */ jsx(Avatar, { src, name, size, shape }),
2367
- !readOnly ? /* @__PURE__ */ jsx("span", { className: "crewlet-image-upload__overlay", "aria-hidden": true, children: uploading ? /* @__PURE__ */ jsx("span", { className: "crewlet-image-upload__spinner" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2368
- /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", children: "photo_camera" }),
2369
- /* @__PURE__ */ jsx("span", { className: "crewlet-image-upload__overlay-text", children: src ? "Change" : "Upload" })
2370
- ] }) }) : null
2371
- ]
2372
- }
2373
- ),
2374
- canRemove ? /* @__PURE__ */ jsx(
2375
- "button",
2376
- {
2377
- type: "button",
2378
- className: "crewlet-image-upload__remove",
2379
- onClick: handleRemove,
2380
- disabled: uploading,
2381
- "aria-label": "Remove image",
2382
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: "close" })
2383
- }
2384
- ) : null,
2385
- !readOnly ? /* @__PURE__ */ jsx(
2386
- "input",
2387
- {
2388
- ref: inputRef,
2389
- type: "file",
2390
- accept,
2391
- className: "crewlet-image-upload__input",
2392
- onChange: handleChange,
2393
- tabIndex: -1
2394
- }
2395
- ) : null
2396
- ] });
2397
- };
2398
- var Checkbox = forwardRef(function Checkbox2({
2399
- label,
2400
- description,
2401
- tone = "default",
2402
- className = "",
2403
- disabled,
2404
- id,
2405
- ...rest
2406
- }, ref) {
2407
- const generatedId = useId();
2408
- const inputId = id ?? generatedId;
2409
- const describedBy = description ? `${inputId}-description` : void 0;
2410
- const classes = [
2411
- "crewlet-checkbox",
2412
- tone === "danger" ? "crewlet-checkbox--danger" : "",
2413
- disabled ? "is-disabled" : "",
2414
- className
2415
- ].filter(Boolean).join(" ");
2416
- return /* @__PURE__ */ jsxs("div", { className: classes, children: [
2417
- /* @__PURE__ */ jsx(
2418
- "input",
2419
- {
2420
- ref,
2421
- id: inputId,
2422
- type: "checkbox",
2423
- className: "crewlet-checkbox__control",
2424
- disabled,
2425
- "aria-describedby": describedBy,
2426
- ...rest
2427
- }
2428
- ),
2429
- /* @__PURE__ */ jsxs("label", { className: "crewlet-checkbox__text", htmlFor: inputId, children: [
2430
- /* @__PURE__ */ jsx("span", { className: "crewlet-checkbox__label", children: label }),
2431
- description && /* @__PURE__ */ jsx("span", { className: "crewlet-checkbox__description", id: describedBy, children: description })
2432
- ] })
2433
- ] });
2434
- });
2435
- var Input = forwardRef(function Input2({
2436
- inputSize = "md",
2437
- error = false,
2438
- className = "",
2439
- containerClassName = "",
2440
- trailing,
2441
- disabled,
2442
- ...rest
2443
- }, ref) {
2444
- const containerClasses = [
2445
- "crewlet-input",
2446
- `crewlet-input--${inputSize}`,
2447
- error ? "is-error" : "",
2448
- disabled ? "is-disabled" : "",
2449
- containerClassName
2450
- ].filter(Boolean).join(" ");
2451
- return /* @__PURE__ */ jsxs("div", { className: containerClasses, children: [
2452
- /* @__PURE__ */ jsx(
2453
- "input",
2454
- {
2455
- ref,
2456
- className: `crewlet-input__control ${className}`.trim(),
2457
- disabled,
2458
- ...rest
2459
- }
2460
- ),
2461
- trailing ? /* @__PURE__ */ jsx("span", { className: "crewlet-input__trailing", children: trailing }) : null
2462
- ] });
2463
- });
2464
- var Textarea = forwardRef(function Textarea2({ error = false, autoResize = false, trailing, className = "", value, disabled, ...rest }, ref) {
2465
- const localRef = useRef(null);
2466
- useImperativeHandle(ref, () => localRef.current);
2467
- useEffect(() => {
2468
- if (!autoResize) return;
2469
- const el = localRef.current;
2470
- if (!el) return;
2471
- el.style.height = "auto";
2472
- el.style.height = `${el.scrollHeight}px`;
2473
- }, [autoResize, value]);
2474
- const classes = [
2475
- "crewlet-textarea",
2476
- error ? "is-error" : "",
2477
- disabled ? "is-disabled" : "",
2478
- autoResize ? "is-auto-resize" : "",
2479
- className
2480
- ].filter(Boolean).join(" ");
2481
- const control = /* @__PURE__ */ jsx(
2482
- "textarea",
2483
- {
2484
- ref: localRef,
2485
- className: classes,
2486
- value,
2487
- disabled,
2488
- ...rest
2489
- }
2490
- );
2491
- if (!trailing) return control;
2492
- return /* @__PURE__ */ jsxs("span", { className: "crewlet-textarea-shell", children: [
2493
- control,
2494
- /* @__PURE__ */ jsx("span", { className: "crewlet-input__trailing", children: trailing })
2495
- ] });
2496
- });
2497
- var Label = ({ required, action, className = "", children, ...rest }) => {
2498
- const classes = ["crewlet-label", action ? "crewlet-label--with-action" : "", className].filter(Boolean).join(" ");
2499
- if (action) {
2500
- return /* @__PURE__ */ jsxs("div", { className: classes, children: [
2501
- /* @__PURE__ */ jsxs("label", { ...rest, className: "crewlet-label__text", children: [
2502
- children,
2503
- required ? /* @__PURE__ */ jsx("span", { className: "crewlet-label__required", "aria-hidden": true, children: "*" }) : null
2504
- ] }),
2505
- /* @__PURE__ */ jsx("span", { className: "crewlet-label__action", children: action })
2506
- ] });
2507
- }
2508
- return /* @__PURE__ */ jsxs("label", { ...rest, className: classes, children: [
2509
- children,
2510
- required ? /* @__PURE__ */ jsx("span", { className: "crewlet-label__required", "aria-hidden": true, children: "*" }) : null
2511
- ] });
2512
- };
2513
- var FormField = ({
2514
- label,
2515
- required,
2516
- helper,
2517
- error,
2518
- labelAction,
2519
- htmlFor,
2520
- className = "",
2521
- children,
2522
- ...rest
2523
- }) => {
2524
- const autoId = useId();
2525
- const id = htmlFor ?? autoId;
2526
- const helperId = helper ? `${id}-helper` : void 0;
2527
- const errorId = error ? `${id}-error` : void 0;
2528
- const describedBy = errorId ?? helperId;
2529
- const classes = ["crewlet-form-field", error ? "is-error" : "", className].filter(Boolean).join(" ");
2530
- return /* @__PURE__ */ jsxs("div", { ...rest, className: classes, children: [
2531
- label !== void 0 ? /* @__PURE__ */ jsx(Label, { htmlFor: id, required, action: labelAction, children: label }) : null,
2532
- typeof children === "function" ? children({ id, describedBy }) : children,
2533
- error ? /* @__PURE__ */ jsx("div", { id: errorId, className: "crewlet-form-field__error", role: "alert", children: error }) : helper ? /* @__PURE__ */ jsx("div", { id: helperId, className: "crewlet-form-field__helper", children: helper }) : null
2534
- ] });
2535
- };
2536
- var FormRow = ({ columns = 2, className = "", children, ...rest }) => {
2537
- const classes = ["crewlet-form-row", `crewlet-form-row--${columns}`, className].filter(Boolean).join(" ");
2538
- return /* @__PURE__ */ jsx("div", { ...rest, className: classes, children });
2539
- };
2540
- var Kbd = ({ subtle = false, className = "", children, ...rest }) => /* @__PURE__ */ jsx("kbd", { ...rest, className: ["crewlet-kbd", subtle ? "crewlet-kbd--subtle" : "", className].filter(Boolean).join(" "), children });
2541
- var ConfirmModal = ({
2542
- open,
2543
- onClose,
2544
- onConfirm,
2545
- title,
2546
- message,
2547
- children,
2548
- confirmLabel = "Confirm",
2549
- cancelLabel = "Cancel",
2550
- destructive = false,
2551
- confirmVariant,
2552
- confirmDisabled = false,
2553
- submitting = false,
2554
- size = "sm"
2555
- }) => {
2556
- const resolvedVariant = confirmVariant ?? (destructive ? "danger" : "primary");
2557
- return /* @__PURE__ */ jsxs(
2558
- Modal,
2559
- {
2560
- open,
2561
- onClose,
2562
- title,
2563
- size,
2564
- closeOnBackdrop: !submitting,
2565
- footer: /* @__PURE__ */ jsxs(Fragment, { children: [
2566
- /* @__PURE__ */ jsx(Button, { variant: "tertiary", onClick: onClose, disabled: submitting, children: cancelLabel }),
2567
- /* @__PURE__ */ jsx(
2568
- Button,
2569
- {
2570
- variant: resolvedVariant,
2571
- onClick: () => {
2572
- void onConfirm();
2573
- },
2574
- disabled: confirmDisabled || submitting,
2575
- children: submitting ? "Working\u2026" : confirmLabel
2576
- }
2577
- )
2578
- ] }),
2579
- children: [
2580
- message ? /* @__PURE__ */ jsx("p", { className: "crewlet-modal__message", children: message }) : null,
2581
- children
2582
- ]
2583
- }
2584
- );
2585
- };
2586
- var PricingCardRoot = forwardRef(function PricingCardRoot2({
2587
- selected = false,
2588
- pending = false,
2589
- unavailable = false,
2590
- disabled = false,
2591
- interactive = true,
2592
- className = "",
2593
- type,
2594
- children,
2595
- onClick,
2596
- ...rest
2597
- }, ref) {
2598
- const classes = [
2599
- "crewlet-pricing-card",
2600
- selected ? "is-selected" : "",
2601
- pending ? "is-pending" : "",
2602
- unavailable ? "is-unavailable" : "",
2603
- disabled ? "is-disabled" : "",
2604
- interactive ? "is-interactive" : "",
2605
- className
2606
- ].filter(Boolean).join(" ");
2607
- if (!interactive) {
2608
- const { "aria-pressed": _ignored, ...divRest } = rest;
2609
- return /* @__PURE__ */ jsx("div", { className: classes, ...divRest, children });
2610
- }
2611
- const onClickGuarded = disabled || unavailable ? void 0 : onClick;
2612
- return /* @__PURE__ */ jsx(
2613
- "button",
2614
- {
2615
- ref,
2616
- type: type ?? "button",
2617
- className: classes,
2618
- disabled: disabled || void 0,
2619
- "aria-pressed": selected || void 0,
2620
- "aria-disabled": unavailable && !disabled ? true : void 0,
2621
- onClick: onClickGuarded,
2622
- ...rest,
2623
- children
2624
- }
2625
- );
2626
- });
2627
- var Header = ({ className = "", children, ...rest }) => /* @__PURE__ */ jsx("div", { ...rest, className: `crewlet-pricing-card__header ${className}`.trim(), children });
2628
- var Name = ({ className = "", children, ...rest }) => /* @__PURE__ */ jsx("span", { ...rest, className: `crewlet-pricing-card__name ${className}`.trim(), children });
2629
- var Tag = ({ variant = "neutral", className = "", children, ...rest }) => /* @__PURE__ */ jsx(
2630
- "span",
2631
- {
2632
- ...rest,
2633
- className: `crewlet-pricing-card__tag crewlet-pricing-card__tag--${variant} ${className}`.trim(),
2634
- children
2635
- }
2636
- );
2637
- var Price = ({ className = "", children, ...rest }) => /* @__PURE__ */ jsx("div", { ...rest, className: `crewlet-pricing-card__price ${className}`.trim(), children });
2638
- var Amount = ({ strike = false, discounted = false, className = "", children, ...rest }) => {
2639
- const classes = [
2640
- "crewlet-pricing-card__amount",
2641
- strike ? "crewlet-pricing-card__amount--strike" : "",
2642
- discounted ? "crewlet-pricing-card__amount--discounted" : "",
2643
- className
2644
- ].filter(Boolean).join(" ");
2645
- return /* @__PURE__ */ jsx("span", { ...rest, className: classes, children });
2646
- };
2647
- var Cadence = ({ className = "", children, ...rest }) => /* @__PURE__ */ jsx("span", { ...rest, className: `crewlet-pricing-card__cadence ${className}`.trim(), children });
2648
- var PriceNote = ({ className = "", children, ...rest }) => /* @__PURE__ */ jsx("span", { ...rest, className: `crewlet-pricing-card__price-note ${className}`.trim(), children });
2649
- var Body = ({ className = "", children, ...rest }) => /* @__PURE__ */ jsx("div", { ...rest, className: `crewlet-pricing-card__body ${className}`.trim(), children });
2650
- var Footer = ({ className = "", children, ...rest }) => /* @__PURE__ */ jsx("div", { ...rest, className: `crewlet-pricing-card__footer ${className}`.trim(), children });
2651
- var Grid = ({ className = "", children, ...rest }) => /* @__PURE__ */ jsx("div", { ...rest, className: `crewlet-pricing-card-grid ${className}`.trim(), children });
2652
- var PricingCard = Object.assign(PricingCardRoot, {
2653
- Header,
2654
- Name,
2655
- Tag,
2656
- Price,
2657
- Amount,
2658
- Cadence,
2659
- PriceNote,
2660
- Body,
2661
- Footer,
2662
- Grid
2663
- });
2664
- var Section = ({
2665
- as: As = "section",
2666
- spacing = "md",
2667
- className = "",
2668
- children,
2669
- ...rest
2670
- }) => {
2671
- const classes = ["crewlet-section", `crewlet-section--${spacing}`, className].filter(Boolean).join(" ");
2672
- return /* @__PURE__ */ jsx(As, { ...rest, className: classes, children });
2673
- };
2674
- function Select({
2675
- id,
2676
- value,
2677
- onChange,
2678
- options,
2679
- placeholder = "Select an option",
2680
- disabled = false,
2681
- className = "",
2682
- menuClassName = "",
2683
- size = "md",
2684
- align = "left",
2685
- renderTrigger,
2686
- renderOption,
2687
- ariaLabel,
2688
- name,
2689
- searchable = false,
2690
- searchPlaceholder = "Search\u2026",
2691
- multiple = false,
2692
- renderMultiLabel
2693
- }) {
2694
- const selectedValues = useMemo(() => {
2695
- if (multiple) {
2696
- return Array.isArray(value) ? value : [];
2697
- }
2698
- if (value === void 0 || value === null) return [];
2699
- return Array.isArray(value) ? value : [value];
2700
- }, [value, multiple]);
2701
- const isValueSelected = useCallback(
2702
- (v) => selectedValues.some((sv) => String(sv) === String(v)),
2703
- [selectedValues]
2704
- );
2705
- const [open, setOpen] = useState(false);
2706
- const [highlight, setHighlight] = useState(-1);
2707
- const [menuRect, setMenuRect] = useState(
2708
- { top: 0, left: 0, width: 0, side: "bottom" }
2709
- );
2710
- const [search, setSearch] = useState("");
2711
- const wrapRef = useRef(null);
2712
- const triggerRef = useRef(null);
2713
- const panelRef = useRef(null);
2714
- const searchRef = useRef(null);
2715
- const reposition = useCallback(() => {
2716
- const trig = triggerRef.current;
2717
- if (!trig) return;
2718
- const rect = trig.getBoundingClientRect();
2719
- const panelHeight = panelRef.current?.offsetHeight ?? 240;
2720
- const viewportH = window.innerHeight;
2721
- let chosenSide = "bottom";
2722
- if (rect.bottom + panelHeight + 12 > viewportH && rect.top > panelHeight + 12) {
2723
- chosenSide = "top";
2724
- }
2725
- setMenuRect({
2726
- top: chosenSide === "bottom" ? rect.bottom + window.scrollY + 6 : rect.top + window.scrollY - panelHeight - 6,
2727
- left: rect.left + window.scrollX,
2728
- width: rect.width,
2729
- side: chosenSide
2730
- });
2731
- }, []);
2732
- const selectedIndex = useMemo(() => {
2733
- if (multiple) return -1;
2734
- if (selectedValues.length === 0) return -1;
2735
- return options.findIndex((o) => String(o.value) === String(selectedValues[0]));
2736
- }, [options, selectedValues, multiple]);
2737
- const selectedOption = selectedIndex >= 0 ? options[selectedIndex] ?? null : null;
2738
- const selectedOptions = useMemo(
2739
- () => options.filter((o) => isValueSelected(o.value)),
2740
- [options, isValueSelected]
2741
- );
2742
- const visibleOptions = useMemo(() => {
2743
- if (!searchable) return options;
2744
- const needle = search.trim().toLowerCase();
2745
- if (needle === "") return options;
2746
- return options.filter((opt) => {
2747
- const label = String(opt.label ?? "").toLowerCase();
2748
- if (label.includes(needle)) return true;
2749
- const desc = String(opt.description ?? "").toLowerCase();
2750
- return desc.length > 0 && desc.includes(needle);
2751
- });
2752
- }, [options, searchable, search]);
2753
- useEffect(() => {
2754
- if (!open) return;
2755
- if (searchable && search.trim() !== "") {
2756
- setHighlight(0);
2757
- return;
2758
- }
2759
- setHighlight(selectedIndex >= 0 ? selectedIndex : 0);
2760
- }, [open, selectedIndex, searchable, search]);
2761
- useEffect(() => {
2762
- if (!searchable) return;
2763
- if (open) {
2764
- setSearch("");
2765
- requestAnimationFrame(() => {
2766
- searchRef.current?.focus();
2767
- });
2768
- }
2769
- }, [open, searchable]);
2770
- useEffect(() => {
2771
- if (!open) return void 0;
2772
- const onDocMouseDown = (e) => {
2773
- const target = e.target;
2774
- if (target && wrapRef.current?.contains(target)) return;
2775
- if (target && panelRef.current?.contains(target)) return;
2776
- setOpen(false);
2777
- };
2778
- document.addEventListener("mousedown", onDocMouseDown);
2779
- return () => document.removeEventListener("mousedown", onDocMouseDown);
2780
- }, [open]);
2781
- useLayoutEffect(() => {
2782
- if (!open) return void 0;
2783
- reposition();
2784
- const onLayout = () => reposition();
2785
- window.addEventListener("resize", onLayout);
2786
- window.addEventListener("scroll", onLayout, true);
2787
- return () => {
2788
- window.removeEventListener("resize", onLayout);
2789
- window.removeEventListener("scroll", onLayout, true);
2790
- };
2791
- }, [open, reposition]);
2792
- const choose = useCallback(
2793
- (option) => {
2794
- if (option?.disabled) return;
2795
- if (multiple) {
2796
- const next = isValueSelected(option.value) ? selectedValues.filter((v) => String(v) !== String(option.value)) : [...selectedValues, option.value];
2797
- onChange?.(next, option);
2798
- return;
2799
- }
2800
- onChange?.(option.value, option);
2801
- setOpen(false);
2802
- triggerRef.current?.focus();
2803
- },
2804
- [onChange, multiple, isValueSelected, selectedValues]
2805
- );
2806
- const onTriggerKeyDown = (e) => {
2807
- if (disabled) return;
2808
- if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
2809
- e.preventDefault();
2810
- setOpen(true);
2811
- }
2812
- };
2813
- const onPanelKeyDown = (e) => {
2814
- const list = visibleOptions;
2815
- if (e.key === "Escape") {
2816
- e.preventDefault();
2817
- setOpen(false);
2818
- triggerRef.current?.focus();
2819
- return;
2820
- }
2821
- if (list.length === 0) return;
2822
- if (e.key === "ArrowDown") {
2823
- e.preventDefault();
2824
- setHighlight((h) => {
2825
- const next = h < 0 ? 0 : (h + 1) % list.length;
2826
- let probe = next;
2827
- for (let i = 0; i < list.length; i += 1) {
2828
- if (!list[probe]?.disabled) return probe;
2829
- probe = (probe + 1) % list.length;
2830
- }
2831
- return h;
2832
- });
2833
- return;
2834
- }
2835
- if (e.key === "ArrowUp") {
2836
- e.preventDefault();
2837
- setHighlight((h) => {
2838
- const len = list.length;
2839
- const next = h < 0 ? len - 1 : (h - 1 + len) % len;
2840
- let probe = next;
2841
- for (let i = 0; i < len; i += 1) {
2842
- if (!list[probe]?.disabled) return probe;
2843
- probe = (probe - 1 + len) % len;
2844
- }
2845
- return h;
2846
- });
2847
- return;
2848
- }
2849
- if (e.key === "Enter") {
2850
- e.preventDefault();
2851
- const target = list[highlight];
2852
- if (target && !target.disabled) choose(target);
2853
- return;
2854
- }
2855
- if (e.key === "Home") {
2856
- e.preventDefault();
2857
- setHighlight(0);
2858
- return;
2859
- }
2860
- if (e.key === "End") {
2861
- e.preventDefault();
2862
- setHighlight(list.length - 1);
2863
- }
2864
- };
2865
- let defaultMultiLabel = placeholder;
2866
- if (multiple && selectedOptions.length === 1) {
2867
- defaultMultiLabel = selectedOptions[0].label;
2868
- } else if (multiple && selectedOptions.length > 1) {
2869
- defaultMultiLabel = `${selectedOptions.length} selected`;
2870
- }
2871
- const hasMultiSelection = multiple && selectedOptions.length > 0;
2872
- const triggerContent = renderTrigger ? renderTrigger(selectedOption, { open }) : multiple ? /* @__PURE__ */ jsx("span", { className: `crewlet-select__label${hasMultiSelection ? "" : " is-placeholder"}`, children: renderMultiLabel ? renderMultiLabel(selectedOptions) : defaultMultiLabel }) : /* @__PURE__ */ jsx("span", { className: `crewlet-select__label${selectedOption ? "" : " is-placeholder"}`, children: selectedOption ? selectedOption.label : placeholder });
2873
- const wrapClass = [
2874
- "crewlet-select",
2875
- `crewlet-select--${size}`,
2876
- open ? "is-open" : "",
2877
- disabled ? "is-disabled" : "",
2878
- className
2879
- ].filter(Boolean).join(" ");
2880
- const menuClass = [
2881
- "crewlet-select__menu",
2882
- `crewlet-select__menu--${align}`,
2883
- // Propagate the size onto the menu too. The wrapper class lives
2884
- // outside the portaled menu, so without this the sm trigger
2885
- // would still open a default-padded menu.
2886
- `crewlet-select__menu--${size}`,
2887
- menuClassName
2888
- ].filter(Boolean).join(" ");
2889
- return /* @__PURE__ */ jsxs("div", { className: wrapClass, ref: wrapRef, children: [
2890
- /* @__PURE__ */ jsxs(
2891
- "button",
2892
- {
2893
- ref: triggerRef,
2894
- type: "button",
2895
- id,
2896
- name,
2897
- className: "crewlet-select__trigger",
2898
- "aria-haspopup": "listbox",
2899
- "aria-expanded": open,
2900
- "aria-controls": id ? `${id}-listbox` : void 0,
2901
- "aria-label": ariaLabel,
2902
- disabled,
2903
- onClick: () => setOpen((o) => !o),
2904
- onKeyDown: onTriggerKeyDown,
2905
- children: [
2906
- triggerContent,
2907
- /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined crewlet-select__chevron", "aria-hidden": true, children: open ? "expand_less" : "expand_more" })
2908
- ]
2909
- }
2910
- ),
2911
- open && createPortal(
2912
- /* @__PURE__ */ jsxs(
2913
- "div",
2914
- {
2915
- ref: (el) => {
2916
- panelRef.current = el;
2917
- if (!searchable) el?.focus();
2918
- },
2919
- className: `${menuClass} crewlet-select__menu--${menuRect.side}`,
2920
- style: {
2921
- position: "absolute",
2922
- top: menuRect.top,
2923
- left: menuRect.left,
2924
- width: menuRect.width
2925
- },
2926
- tabIndex: -1,
2927
- onKeyDown: onPanelKeyDown,
2928
- onMouseLeave: () => setHighlight(-1),
2929
- onMouseDown: (e) => e.stopPropagation(),
2930
- children: [
2931
- searchable && /* @__PURE__ */ jsxs("div", { className: "crewlet-select__search", children: [
2932
- /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: "search" }),
2933
- /* @__PURE__ */ jsx(
2934
- "input",
2935
- {
2936
- ref: searchRef,
2937
- type: "text",
2938
- className: "crewlet-select__search-input",
2939
- placeholder: searchPlaceholder,
2940
- value: search,
2941
- onChange: (e) => setSearch(e.target.value),
2942
- onKeyDown: onPanelKeyDown,
2943
- "aria-label": "Search options"
2944
- }
2945
- )
2946
- ] }),
2947
- /* @__PURE__ */ jsxs(
2948
- "ul",
2949
- {
2950
- id: id ? `${id}-listbox` : void 0,
2951
- className: "crewlet-select__list",
2952
- role: "listbox",
2953
- children: [
2954
- visibleOptions.length === 0 && /* @__PURE__ */ jsx("li", { className: "crewlet-select__empty", "aria-hidden": true, children: "No matches" }),
2955
- visibleOptions.map((opt, i) => {
2956
- const isSelected = isValueSelected(opt.value);
2957
- const isHighlight = i === highlight;
2958
- const itemClass = [
2959
- "crewlet-select__option",
2960
- multiple ? "crewlet-select__option--multi" : "",
2961
- isSelected ? "is-selected" : "",
2962
- isHighlight ? "is-highlight" : "",
2963
- opt.disabled ? "is-disabled" : ""
2964
- ].filter(Boolean).join(" ");
2965
- return /* @__PURE__ */ jsx(
2966
- "li",
2967
- {
2968
- role: "option",
2969
- "aria-selected": isSelected,
2970
- "aria-disabled": opt.disabled || void 0,
2971
- className: itemClass,
2972
- onMouseEnter: () => !opt.disabled && setHighlight(i),
2973
- onMouseDown: (e) => {
2974
- e.preventDefault();
2975
- choose(opt);
2976
- },
2977
- children: renderOption ? renderOption(opt, { active: isHighlight, selected: isSelected }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2978
- /* @__PURE__ */ jsxs("div", { className: "crewlet-select__option-row", children: [
2979
- multiple && /* @__PURE__ */ jsx(
2980
- "span",
2981
- {
2982
- className: `crewlet-select__option-checkbox${isSelected ? " is-checked" : ""}`,
2983
- "aria-hidden": true,
2984
- children: isSelected && /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: "check" })
2985
- }
2986
- ),
2987
- /* @__PURE__ */ jsx("span", { className: "crewlet-select__option-label", children: opt.label }),
2988
- !multiple && isSelected && /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined crewlet-select__option-check", "aria-hidden": true, children: "check" })
2989
- ] }),
2990
- opt.description && /* @__PURE__ */ jsx("div", { className: "crewlet-select__option-desc", children: opt.description })
2991
- ] })
2992
- },
2993
- opt.value
2994
- );
2995
- })
2996
- ]
2997
- }
2998
- )
2999
- ]
3000
- }
3001
- ),
3002
- document.body
3003
- )
3004
- ] });
3005
- }
3006
- var range = (n) => Array.from({ length: Math.max(0, n) }, (_, i) => i);
3007
- var clampColumns = (n) => Math.min(4, Math.max(1, Math.round(n)));
3008
- var cx = (...classes) => classes.filter(Boolean).join(" ");
3009
- var line = (...modifiers) => cx("crewlet-skeleton__line", ...modifiers.map((modifier) => `crewlet-skeleton__line--${modifier}`));
3010
- var Skeleton = ({
3011
- variant = "card",
3012
- rows = 1,
3013
- columns = 1,
3014
- count = 4,
3015
- className = "",
3016
- style,
3017
- width = "100%",
3018
- height = 400,
3019
- borderRadius = 12,
3020
- marginTop = 32,
3021
- marginBottom = 0,
3022
- glowColor = "blue",
3023
- animationDuration = 2
3024
- }) => {
3025
- const rootClass = (...modifiers) => cx("crewlet-skeleton", `crewlet-skeleton--${variant}`, ...modifiers, className);
3026
- switch (variant) {
3027
- case "box":
3028
- return /* @__PURE__ */ jsx("div", { className: rootClass(), style: { marginTop, marginBottom, ...style }, "aria-hidden": true, children: /* @__PURE__ */ jsx(
3029
- "div",
3030
- {
3031
- className: "crewlet-skeleton__glow-box",
3032
- style: {
3033
- height: typeof height === "number" ? `${height}px` : height,
3034
- width: typeof width === "number" ? `${width}px` : width,
3035
- borderRadius: `${borderRadius}px`
3036
- },
3037
- children: /* @__PURE__ */ jsx(
3038
- "div",
3039
- {
3040
- className: `crewlet-skeleton__glow-wave crewlet-skeleton__glow-wave--${glowColor}`,
3041
- style: { animationDuration: `${animationDuration}s` }
3042
- }
3043
- )
3044
- }
3045
- ) });
3046
- case "text":
3047
- return /* @__PURE__ */ jsx("div", { className: rootClass(), style, "aria-hidden": true, children: range(rows).map((i) => /* @__PURE__ */ jsx("div", { className: line(i === 0 ? "title" : "text", ...i === rows - 1 ? ["short"] : []) }, i)) });
3048
- case "card":
3049
- return /* @__PURE__ */ jsxs("div", { className: rootClass(), style, "aria-hidden": true, children: [
3050
- /* @__PURE__ */ jsx("div", { className: line("title") }),
3051
- /* @__PURE__ */ jsx("div", { className: line("text") }),
3052
- /* @__PURE__ */ jsx("div", { className: line("text", "short") })
3053
- ] });
3054
- case "grid": {
3055
- const cols = clampColumns(columns);
3056
- return /* @__PURE__ */ jsx("div", { className: rootClass(`crewlet-skeleton--cols-${cols}`), style, "aria-hidden": true, children: range(rows * cols).map((i) => /* @__PURE__ */ jsxs("div", { className: "crewlet-skeleton__card", children: [
3057
- /* @__PURE__ */ jsx("div", { className: line("title") }),
3058
- /* @__PURE__ */ jsx("div", { className: line("text") }),
3059
- /* @__PURE__ */ jsx("div", { className: line("text", "short") })
3060
- ] }, i)) });
3061
- }
3062
- case "list":
3063
- return /* @__PURE__ */ jsx("div", { className: rootClass(), style, "aria-hidden": true, children: range(rows).map((i) => /* @__PURE__ */ jsxs("div", { className: "crewlet-skeleton__list-item", children: [
3064
- /* @__PURE__ */ jsx("div", { className: "crewlet-skeleton__avatar" }),
3065
- /* @__PURE__ */ jsxs("div", { className: "crewlet-skeleton__list-content", children: [
3066
- /* @__PURE__ */ jsx("div", { className: line("title") }),
3067
- /* @__PURE__ */ jsx("div", { className: line("text", "short") })
3068
- ] })
3069
- ] }, i)) });
3070
- case "table":
3071
- return /* @__PURE__ */ jsx("div", { className: rootClass(), style, "aria-hidden": true, children: /* @__PURE__ */ jsxs("table", { className: "crewlet-skeleton__table", children: [
3072
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: range(columns).map((i) => /* @__PURE__ */ jsx("th", { className: "crewlet-skeleton__th", children: /* @__PURE__ */ jsx("div", { className: line("title") }) }, i)) }) }),
3073
- /* @__PURE__ */ jsx("tbody", { children: range(rows).map((rowIndex) => /* @__PURE__ */ jsx("tr", { className: "crewlet-skeleton__tr", children: range(columns).map((colIndex) => /* @__PURE__ */ jsx("td", { className: "crewlet-skeleton__td", children: /* @__PURE__ */ jsx("div", { className: line("text") }) }, colIndex)) }, rowIndex)) })
3074
- ] }) });
3075
- case "info-grid": {
3076
- const cols = clampColumns(columns);
3077
- return /* @__PURE__ */ jsx("div", { className: rootClass(`crewlet-skeleton--cols-${cols}`), style, "aria-hidden": true, children: range(rows * cols).map((i) => /* @__PURE__ */ jsxs("div", { className: "crewlet-skeleton__info-item", children: [
3078
- /* @__PURE__ */ jsx("div", { className: line("label") }),
3079
- /* @__PURE__ */ jsx("div", { className: line("value") })
3080
- ] }, i)) });
3081
- }
3082
- case "pricing-card":
3083
- return /* @__PURE__ */ jsx("div", { className: rootClass(), style, "aria-hidden": true, children: range(count).map((i) => /* @__PURE__ */ jsxs("div", { className: "crewlet-skeleton__pricing-card", children: [
3084
- /* @__PURE__ */ jsxs("div", { className: "crewlet-skeleton__pricing-card-header", children: [
3085
- /* @__PURE__ */ jsx("div", { className: "crewlet-skeleton__circle" }),
3086
- /* @__PURE__ */ jsx("div", { className: line("heading") })
3087
- ] }),
3088
- /* @__PURE__ */ jsxs("div", { className: "crewlet-skeleton__pricing-card-description", children: [
3089
- /* @__PURE__ */ jsx("div", { className: line("text") }),
3090
- /* @__PURE__ */ jsx("div", { className: line("text", "wide") }),
3091
- /* @__PURE__ */ jsx("div", { className: line("text", "three-quarter") })
3092
- ] }),
3093
- /* @__PURE__ */ jsx("div", { className: "crewlet-skeleton__pricing-card-features", children: range(5).map((f) => /* @__PURE__ */ jsxs("div", { className: "crewlet-skeleton__pricing-card-feature", children: [
3094
- /* @__PURE__ */ jsx("div", { className: "crewlet-skeleton__circle crewlet-skeleton__circle--small" }),
3095
- /* @__PURE__ */ jsx("div", { className: line("feature") })
3096
- ] }, f)) }),
3097
- /* @__PURE__ */ jsx("div", { className: "crewlet-skeleton__button" })
3098
- ] }, i)) });
3099
- default:
3100
- return /* @__PURE__ */ jsx("div", { className: cx("crewlet-skeleton", className), style, "aria-hidden": true, children: /* @__PURE__ */ jsx("div", { className: "crewlet-skeleton__line" }) });
3101
- }
3102
- };
3103
- var StatCard = ({
3104
- label,
3105
- value,
3106
- sub,
3107
- icon,
3108
- tone = "neutral",
3109
- loading = false,
3110
- className = "",
3111
- ...rest
3112
- }) => {
3113
- const classes = ["crewlet-statcard", `crewlet-statcard--${tone}`, className].filter(Boolean).join(" ");
3114
- return /* @__PURE__ */ jsxs("div", { ...rest, className: classes, children: [
3115
- icon ? /* @__PURE__ */ jsx("span", { className: "crewlet-statcard__icon", "aria-hidden": true, children: icon }) : null,
3116
- /* @__PURE__ */ jsxs("div", { className: "crewlet-statcard__body", children: [
3117
- /* @__PURE__ */ jsx("div", { className: "crewlet-statcard__value", children: loading ? /* @__PURE__ */ jsx("span", { className: "crewlet-statcard__placeholder", children: "--" }) : value }),
3118
- /* @__PURE__ */ jsx("div", { className: "crewlet-statcard__label", children: label }),
3119
- sub ? /* @__PURE__ */ jsx("div", { className: "crewlet-statcard__sub", children: sub }) : null
3120
- ] })
3121
- ] });
3122
- };
3123
- var Table = ({
3124
- headers,
3125
- data,
3126
- emptyMessage = "No data available",
3127
- actionButton,
3128
- className = ""
3129
- }) => {
3130
- return /* @__PURE__ */ jsxs("div", { className: `crewlet-table ${className}`, children: [
3131
- /* @__PURE__ */ jsxs("div", { className: "crewlet-table__container", children: [
3132
- /* @__PURE__ */ jsx("div", { className: "crewlet-table__header", style: { gridTemplateColumns: `repeat(${headers.length}, 1fr)` }, children: headers.map((header, index) => /* @__PURE__ */ jsx("span", { children: header }, index)) }),
3133
- data && data.length > 0 ? /* @__PURE__ */ jsx("div", { className: "crewlet-table__body", children: data.map((row, rowIndex) => /* @__PURE__ */ jsx(
3134
- "div",
3135
- {
3136
- className: "crewlet-table__row",
3137
- style: { gridTemplateColumns: `repeat(${headers.length}, 1fr)` },
3138
- children: row.map((cell, cellIndex) => /* @__PURE__ */ jsx("span", { children: cell }, cellIndex))
3139
- },
3140
- rowIndex
3141
- )) }) : /* @__PURE__ */ jsx("div", { className: "crewlet-table__empty", children: /* @__PURE__ */ jsx("span", { children: emptyMessage }) })
3142
- ] }),
3143
- actionButton && /* @__PURE__ */ jsx(
3144
- "button",
3145
- {
3146
- className: "crewlet-table__action-btn",
3147
- onClick: actionButton.onClick,
3148
- children: /* @__PURE__ */ jsx("span", { className: "crewlet-table__action-text", children: actionButton.label })
3149
- }
3150
- )
3151
- ] });
3152
- };
3153
- function Tabs({
3154
- items,
3155
- variant = "pill",
3156
- size = "md",
3157
- ariaLabel,
3158
- className = "",
3159
- value,
3160
- onValueChange,
3161
- renderItem
3162
- }) {
3163
- const navRef = useRef(null);
3164
- useLayoutEffect(() => {
3165
- if (variant !== "underline") return;
3166
- const nav = navRef.current;
3167
- if (!nav) return;
3168
- const update = () => {
3169
- const active = nav.querySelector(".crewlet-tabs__tab.is-active");
3170
- if (!active) {
3171
- nav.style.setProperty("--crewlet-tabs-slider-width", "0px");
3172
- return;
3173
- }
3174
- const navRect = nav.getBoundingClientRect();
3175
- const activeRect = active.getBoundingClientRect();
3176
- const left = activeRect.left - navRect.left;
3177
- const width = activeRect.width;
3178
- nav.style.setProperty("--crewlet-tabs-slider-left", `${left}px`);
3179
- nav.style.setProperty("--crewlet-tabs-slider-width", `${width}px`);
3180
- };
3181
- update();
3182
- const mutation = new MutationObserver(update);
3183
- nav.querySelectorAll(".crewlet-tabs__tab").forEach((tab) => {
3184
- mutation.observe(tab, {
3185
- attributes: true,
3186
- attributeFilter: ["class"]
3187
- });
3188
- });
3189
- const resize = new ResizeObserver(update);
3190
- resize.observe(nav);
3191
- return () => {
3192
- mutation.disconnect();
3193
- resize.disconnect();
3194
- };
3195
- }, [variant, value, items]);
3196
- const rootClass = [
3197
- "crewlet-tabs",
3198
- `crewlet-tabs--${variant}`,
3199
- `crewlet-tabs--${size}`,
3200
- className
3201
- ].filter(Boolean).join(" ");
3202
- return /* @__PURE__ */ jsx(
3203
- "nav",
3204
- {
3205
- ref: navRef,
3206
- className: rootClass,
3207
- "aria-label": ariaLabel,
3208
- role: "tablist",
3209
- children: items.map((item) => {
3210
- const isActive = value !== void 0 && value === item.value;
3211
- const tabClassName = [
3212
- "crewlet-tabs__tab",
3213
- isActive ? "is-active" : void 0,
3214
- item.disabled ? "is-disabled" : void 0
3215
- ].filter(Boolean).join(" ");
3216
- const children = /* @__PURE__ */ jsxs(Fragment, { children: [
3217
- item.icon ? /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined crewlet-tabs__icon", "aria-hidden": true, children: item.icon }) : null,
3218
- /* @__PURE__ */ jsx("span", { className: "crewlet-tabs__label", children: item.label })
3219
- ] });
3220
- const onSelect = () => {
3221
- if (item.disabled) return;
3222
- onValueChange?.(item.value);
3223
- };
3224
- if (renderItem) {
3225
- return /* @__PURE__ */ jsx(React.Fragment, { children: renderItem({ item, className: tabClassName, isActive, onSelect, children }) }, item.value);
3226
- }
3227
- return /* @__PURE__ */ jsx(
3228
- "button",
3229
- {
3230
- type: "button",
3231
- role: "tab",
3232
- "aria-selected": isActive,
3233
- "aria-current": isActive ? "page" : void 0,
3234
- disabled: item.disabled,
3235
- className: tabClassName,
3236
- onClick: onSelect,
3237
- children
3238
- },
3239
- item.value
3240
- );
3241
- })
3242
- }
3243
- );
3244
- }
3245
- var DURATION_RX = /^\s*(\d+)\s*(m|h|d|w)\s*$/i;
3246
- function parseTimeWindowDuration(s) {
3247
- const m = DURATION_RX.exec(s);
3248
- if (!m) return 0;
3249
- const n = parseInt(m[1], 10);
3250
- if (!Number.isFinite(n)) return 0;
3251
- const unit = m[2].toLowerCase();
3252
- const factor = unit === "m" ? 6e4 : unit === "h" ? 36e5 : unit === "d" ? 864e5 : 6048e5;
3253
- return n * factor;
3254
- }
3255
- function resolveTimeWindow(value) {
3256
- if (!value) return { from: void 0, to: void 0 };
3257
- if (value.kind === "relative" && value.duration) {
3258
- const ms = parseTimeWindowDuration(value.duration);
3259
- if (ms <= 0) return { from: void 0, to: void 0 };
3260
- return {
3261
- from: new Date(Date.now() - ms).toISOString(),
3262
- to: void 0
3263
- };
3264
- }
3265
- if (value.kind === "absolute") {
3266
- const suffix = value.timezone === "utc" ? "Z" : "";
3267
- return {
3268
- from: value.from ? new Date(value.from + suffix).toISOString() : void 0,
3269
- to: value.to ? new Date(value.to + suffix).toISOString() : void 0
3270
- };
3271
- }
3272
- return { from: void 0, to: void 0 };
3273
- }
3274
- var CHIP_GROUPS = [
3275
- { label: "Minutes", unit: "m", values: [5, 10, 15, 30, 45] },
3276
- { label: "Hours", unit: "h", values: [1, 2, 3, 6, 8, 12] },
3277
- { label: "Days", unit: "d", values: [1, 2, 3, 4, 5, 6] },
3278
- { label: "Weeks", unit: "w", values: [1, 2, 3, 4] }
3279
- ];
3280
- var UNIT_LABELS = {
3281
- m: "Minutes",
3282
- h: "Hours",
3283
- d: "Days",
3284
- w: "Weeks"
3285
- };
3286
- var pad2 = (n) => String(n).padStart(2, "0");
3287
- var parseDateTime = (s) => {
3288
- if (!s) return null;
3289
- const tIndex = s.indexOf("T");
3290
- const datePart = tIndex >= 0 ? s.slice(0, tIndex) : s;
3291
- const timePart = tIndex >= 0 ? s.slice(tIndex + 1) : "00:00:00";
3292
- const dateBits = datePart.split("-");
3293
- if (dateBits.length < 3) return null;
3294
- const y = parseInt(dateBits[0], 10);
3295
- const m = parseInt(dateBits[1], 10);
3296
- const d = parseInt(dateBits[2], 10);
3297
- if (!Number.isFinite(y) || !Number.isFinite(m) || !Number.isFinite(d)) return null;
3298
- const timeBits = timePart.split(":");
3299
- const h = parseInt(timeBits[0] || "0", 10);
3300
- const mn = parseInt(timeBits[1] || "0", 10);
3301
- const sec = parseInt(timeBits[2] || "0", 10);
3302
- return {
3303
- year: y,
3304
- month: m - 1,
3305
- day: d,
3306
- hour: Number.isFinite(h) ? h : 0,
3307
- minute: Number.isFinite(mn) ? mn : 0,
3308
- second: Number.isFinite(sec) ? sec : 0
3309
- };
3310
- };
3311
- var formatDateTime = (p) => `${p.year}-${pad2(p.month + 1)}-${pad2(p.day)}T${pad2(p.hour)}:${pad2(p.minute)}:${pad2(p.second)}`;
3312
- var formatDateOnly = (p) => `${p.year}/${pad2(p.month + 1)}/${pad2(p.day)}`;
3313
- var formatTimeOnly = (p) => `${pad2(p.hour)}:${pad2(p.minute)}:${pad2(p.second)}`;
3314
- var parseDateOnly = (s) => {
3315
- const m = /^(\d{4})[/-](\d{1,2})[/-](\d{1,2})$/.exec(s.trim());
3316
- if (!m) return null;
3317
- return {
3318
- year: parseInt(m[1], 10),
3319
- month: parseInt(m[2], 10) - 1,
3320
- day: parseInt(m[3], 10)
3321
- };
3322
- };
3323
- var parseTimeOnly = (s) => {
3324
- const m = /^(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?$/.exec(s.trim());
3325
- if (!m) return null;
3326
- return {
3327
- hour: parseInt(m[1], 10),
3328
- minute: parseInt(m[2], 10),
3329
- second: m[3] ? parseInt(m[3], 10) : 0
3330
- };
3331
- };
3332
- var dateDigitFilter = (digit, pos, display) => {
3333
- switch (pos) {
3334
- // YYYY: 2000-2030
3335
- case 0:
3336
- return digit === "2";
3337
- // must start with 2
3338
- case 1:
3339
- return digit === "0";
3340
- // 20xx
3341
- case 2:
3342
- return /[0-3]/.test(digit);
3343
- // 2000/2010/2020/2030
3344
- case 3:
3345
- return display[2] === "3" ? digit === "0" : /[0-9]/.test(digit);
3346
- // MM (month): 01-12
3347
- case 5:
3348
- return /[01]/.test(digit);
3349
- // 0x or 1x
3350
- case 6:
3351
- if (display[5] === "0") return /[1-9]/.test(digit);
3352
- if (display[5] === "1") return /[0-2]/.test(digit);
3353
- return true;
3354
- // DD: 01-31
3355
- case 8:
3356
- return /[0-3]/.test(digit);
3357
- case 9:
3358
- if (display[8] === "0") return /[1-9]/.test(digit);
3359
- if (display[8] === "3") return /[01]/.test(digit);
3360
- return /[0-9]/.test(digit);
3361
- default:
3362
- return true;
3363
- }
3364
- };
3365
- var timeDigitFilter = (digit, pos, display) => {
3366
- switch (pos) {
3367
- // HH: 00-24
3368
- case 0:
3369
- return /[0-2]/.test(digit);
3370
- case 1:
3371
- return display[0] === "2" ? /[0-4]/.test(digit) : /[0-9]/.test(digit);
3372
- // MM (minute): 00-60
3373
- case 3:
3374
- return /[0-6]/.test(digit);
3375
- case 4:
3376
- return display[3] === "6" ? digit === "0" : /[0-9]/.test(digit);
3377
- // SS: 00-60
3378
- case 6:
3379
- return /[0-6]/.test(digit);
3380
- case 7:
3381
- return display[6] === "6" ? digit === "0" : /[0-9]/.test(digit);
3382
- default:
3383
- return true;
3384
- }
3385
- };
3386
- var isValidDateInput = (s) => {
3387
- const m = /^(\d{4})\/(\d{2})\/(\d{2})$/.exec(s);
3388
- if (!m) return false;
3389
- const y = parseInt(m[1], 10);
3390
- const mo = parseInt(m[2], 10);
3391
- const d = parseInt(m[3], 10);
3392
- return y >= 2e3 && y <= 2030 && mo >= 1 && mo <= 12 && d >= 1 && d <= 31;
3393
- };
3394
- var isValidTimeInput = (s) => {
3395
- const m = /^(\d{2}):(\d{2}):(\d{2})$/.exec(s);
3396
- if (!m) return false;
3397
- const h = parseInt(m[1], 10);
3398
- const mi = parseInt(m[2], 10);
3399
- const se = parseInt(m[3], 10);
3400
- return h >= 0 && h <= 24 && mi >= 0 && mi <= 60 && se >= 0 && se <= 60;
3401
- };
3402
- var DATE_SEGMENTS = [
3403
- { start: 0, end: 3, min: 2e3, max: 2030 },
3404
- // YYYY
3405
- { start: 5, end: 6, min: 1, max: 12 },
3406
- // MM
3407
- { start: 8, end: 9, min: 1, max: 31 }
3408
- // DD
3409
- ];
3410
- var TIME_SEGMENTS = [
3411
- { start: 0, end: 1, min: 0, max: 24 },
3412
- // HH
3413
- { start: 3, end: 4, min: 0, max: 60 },
3414
- // MM
3415
- { start: 6, end: 7, min: 0, max: 60 }
3416
- // SS
3417
- ];
3418
- var MONTH_NAMES2 = [
3419
- "January",
3420
- "February",
3421
- "March",
3422
- "April",
3423
- "May",
3424
- "June",
3425
- "July",
3426
- "August",
3427
- "September",
3428
- "October",
3429
- "November",
3430
- "December"
3431
- ];
3432
- var WEEKDAY_LABELS_SUN2 = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
3433
- var WEEKDAY_LABELS_MON2 = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
3434
- var daysInMonth2 = (year, month) => new Date(year, month + 1, 0).getDate();
3435
- var startWeekday2 = (year, month, firstDay) => {
3436
- const d = new Date(year, month, 1).getDay();
3437
- return (d - firstDay + 7) % 7;
3438
- };
3439
- var compareDates2 = (a, b) => {
3440
- if (a.year !== b.year) return a.year - b.year;
3441
- if (a.month !== b.month) return a.month - b.month;
3442
- return a.day - b.day;
3443
- };
3444
- var isBetween = (cell, lo, hi) => compareDates2(cell, lo) >= 0 && compareDates2(cell, hi) <= 0;
3445
- function Calendar({
3446
- year,
3447
- month,
3448
- rangeStart,
3449
- rangeEnd,
3450
- onSelectDate,
3451
- onHoverDate,
3452
- firstDayOfWeek = 0,
3453
- onPrev,
3454
- onNext
3455
- }) {
3456
- const totalDays = daysInMonth2(year, month);
3457
- const leading = startWeekday2(year, month, firstDayOfWeek);
3458
- const weekdayLabels = firstDayOfWeek === 1 ? WEEKDAY_LABELS_MON2 : WEEKDAY_LABELS_SUN2;
3459
- const today = useMemo(() => {
3460
- const now = /* @__PURE__ */ new Date();
3461
- return { year: now.getFullYear(), month: now.getMonth(), day: now.getDate() };
3462
- }, []);
3463
- const cells = useMemo(() => {
3464
- const out = [];
3465
- const prevTotal = daysInMonth2(
3466
- month === 0 ? year - 1 : year,
3467
- month === 0 ? 11 : month - 1
3468
- );
3469
- for (let i = 0; i < leading; i += 1) {
3470
- out.push({ day: prevTotal - leading + 1 + i, inMonth: false });
3471
- }
3472
- for (let d = 1; d <= totalDays; d += 1) {
3473
- out.push({ day: d, inMonth: true });
3474
- }
3475
- while (out.length < 42) {
3476
- out.push({ day: out.length - leading - totalDays + 1, inMonth: false });
3477
- }
3478
- return out;
3479
- }, [year, month, leading, totalDays]);
3480
- const titleClass = [
3481
- "crewlet-time-window__calendar-title",
3482
- onPrev ? "has-prev" : "",
3483
- onNext ? "has-next" : ""
3484
- ].filter(Boolean).join(" ");
3485
- return /* @__PURE__ */ jsxs("div", { className: "crewlet-time-window__calendar", children: [
3486
- /* @__PURE__ */ jsxs("div", { className: titleClass, children: [
3487
- onPrev && /* @__PURE__ */ jsx(
3488
- "button",
3489
- {
3490
- type: "button",
3491
- className: "crewlet-time-window__calendar-nav crewlet-time-window__calendar-nav--prev",
3492
- onClick: onPrev,
3493
- "aria-label": "Previous month",
3494
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: "chevron_left" })
3495
- }
3496
- ),
3497
- /* @__PURE__ */ jsxs("span", { className: "crewlet-time-window__calendar-title-text", children: [
3498
- MONTH_NAMES2[month],
3499
- " ",
3500
- year
3501
- ] }),
3502
- onNext && /* @__PURE__ */ jsx(
3503
- "button",
3504
- {
3505
- type: "button",
3506
- className: "crewlet-time-window__calendar-nav crewlet-time-window__calendar-nav--next",
3507
- onClick: onNext,
3508
- "aria-label": "Next month",
3509
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: "chevron_right" })
3510
- }
3511
- )
3512
- ] }),
3513
- /* @__PURE__ */ jsx("div", { className: "crewlet-time-window__weekdays", "aria-hidden": true, children: weekdayLabels.map((label, i) => /* @__PURE__ */ jsx("span", { className: "crewlet-time-window__weekday", children: label }, i)) }),
3514
- /* @__PURE__ */ jsx(
3515
- "div",
3516
- {
3517
- className: "crewlet-time-window__grid",
3518
- role: "grid",
3519
- onMouseLeave: onHoverDate ? () => onHoverDate(null) : void 0,
3520
- children: cells.map((cell, i) => {
3521
- let cellYear = year;
3522
- let cellMonth = month;
3523
- if (!cell.inMonth) {
3524
- if (cell.day > 20) {
3525
- cellMonth -= 1;
3526
- if (cellMonth < 0) {
3527
- cellMonth = 11;
3528
- cellYear -= 1;
3529
- }
3530
- } else {
3531
- cellMonth += 1;
3532
- if (cellMonth > 11) {
3533
- cellMonth = 0;
3534
- cellYear += 1;
3535
- }
3536
- }
3537
- }
3538
- const cellCoord = { year: cellYear, month: cellMonth, day: cell.day };
3539
- const isFuture = compareDates2(cellCoord, today) > 0;
3540
- const isStart = !isFuture && !!rangeStart && compareDates2(cellCoord, rangeStart) === 0;
3541
- const isEnd = !isFuture && !!rangeEnd && compareDates2(cellCoord, rangeEnd) === 0;
3542
- const isSel = isStart || isEnd;
3543
- let inRange = false;
3544
- if (!isFuture && rangeStart && rangeEnd && !isSel) {
3545
- const lo = compareDates2(rangeStart, rangeEnd) <= 0 ? rangeStart : rangeEnd;
3546
- const hi = compareDates2(rangeStart, rangeEnd) <= 0 ? rangeEnd : rangeStart;
3547
- inRange = isBetween(cellCoord, lo, hi);
3548
- }
3549
- const isToday = today.year === cellYear && today.month === cellMonth && today.day === cell.day;
3550
- const cls = [
3551
- "crewlet-time-window__cell",
3552
- cell.inMonth ? "" : "is-outside",
3553
- isFuture ? "is-future" : "",
3554
- isToday ? "is-today" : "",
3555
- isSel ? "is-selected" : "",
3556
- inRange ? "is-in-range" : ""
3557
- ].filter(Boolean).join(" ");
3558
- return /* @__PURE__ */ jsx(
3559
- "button",
3560
- {
3561
- type: "button",
3562
- role: "gridcell",
3563
- className: cls,
3564
- disabled: isFuture,
3565
- onClick: () => !isFuture && onSelectDate(cellCoord),
3566
- onMouseEnter: onHoverDate && !isFuture ? () => onHoverDate(cellCoord) : void 0,
3567
- children: cell.day
3568
- },
3569
- i
3570
- );
3571
- })
3572
- }
3573
- )
3574
- ] });
3575
- }
3576
- var formatLocalDateLabel = (s) => {
3577
- if (!s) return "";
3578
- const d = new Date(s);
3579
- if (Number.isNaN(d.getTime())) return s;
3580
- const pad22 = (n) => String(n).padStart(2, "0");
3581
- return `${d.getFullYear()}/${pad22(d.getMonth() + 1)}/${pad22(d.getDate())} ${pad22(d.getHours())}:${pad22(d.getMinutes())}:${pad22(d.getSeconds())}`;
3582
- };
3583
- var triggerLabelFor = (value, placeholder) => {
3584
- if (value?.kind === "relative" && value.duration) {
3585
- const m = DURATION_RX.exec(value.duration);
3586
- if (m) {
3587
- const n = parseInt(m[1], 10);
3588
- const unit = m[2].toLowerCase();
3589
- return `Last ${n} ${UNIT_LABELS[unit]?.toLowerCase() || unit}`;
3590
- }
3591
- return `Last ${value.duration}`;
3592
- }
3593
- if (value?.kind === "absolute") {
3594
- if (!value.from && !value.to) return placeholder;
3595
- const from = value.from ? formatLocalDateLabel(value.from) : "earliest";
3596
- const to = value.to ? formatLocalDateLabel(value.to) : "now";
3597
- return `${from} \u2192 ${to}`;
3598
- }
3599
- return placeholder;
3600
- };
3601
- function TimeWindowPicker({
3602
- value,
3603
- onChange,
3604
- allowRelative = true,
3605
- allowAbsolute = true,
3606
- placeholder = "Pick a time window",
3607
- ariaLabel,
3608
- className = "",
3609
- disabled = false,
3610
- size = "md"
3611
- }) {
3612
- const triggerLabel = triggerLabelFor(value, placeholder);
3613
- const isSet = !!(value?.kind === "relative" && value.duration || value?.kind === "absolute" && (value.from || value.to));
3614
- const wrapClass = [
3615
- "crewlet-time-window",
3616
- `crewlet-time-window--${size}`,
3617
- isSet ? "is-set" : "",
3618
- disabled ? "is-disabled" : "",
3619
- className
3620
- ].filter(Boolean).join(" ");
3621
- return /* @__PURE__ */ jsx(
3622
- Popover,
3623
- {
3624
- align: "start",
3625
- side: "bottom",
3626
- width: "auto",
3627
- className: "crewlet-time-window__popover",
3628
- trigger: (open, toggle) => /* @__PURE__ */ jsxs(
3629
- "button",
3630
- {
3631
- type: "button",
3632
- className: `${wrapClass}${open ? " is-open" : ""}`,
3633
- onClick: () => {
3634
- if (!disabled) toggle();
3635
- },
3636
- disabled,
3637
- "aria-haspopup": "dialog",
3638
- "aria-expanded": open,
3639
- "aria-label": ariaLabel || "Time window",
3640
- children: [
3641
- /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined crewlet-time-window__icon", "aria-hidden": true, children: "schedule" }),
3642
- /* @__PURE__ */ jsx("span", { className: `crewlet-time-window__label${isSet ? "" : " is-placeholder"}`, children: triggerLabel }),
3643
- /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined crewlet-time-window__chevron", "aria-hidden": true, children: open ? "expand_less" : "expand_more" })
3644
- ]
3645
- }
3646
- ),
3647
- children: (close) => /* @__PURE__ */ jsx(
3648
- PanelContent,
3649
- {
3650
- value,
3651
- onChange,
3652
- close,
3653
- allowRelative,
3654
- allowAbsolute
3655
- }
3656
- )
3657
- }
3658
- );
3659
- }
3660
- function PanelContent({ value, onChange, close, allowRelative, allowAbsolute }) {
3661
- const initialMode = (() => {
3662
- if (value?.kind === "absolute" && allowAbsolute) return "absolute";
3663
- if (allowRelative) return "relative";
3664
- return "absolute";
3665
- })();
3666
- const [mode, setMode] = useState(initialMode);
3667
- const [draft, setDraft] = useState(value || { kind: initialMode });
3668
- const handleApply = () => {
3669
- onChange(draft);
3670
- close();
3671
- };
3672
- return /* @__PURE__ */ jsxs("div", { className: "crewlet-time-window__panel", children: [
3673
- /* @__PURE__ */ jsxs("div", { className: "crewlet-time-window__header", children: [
3674
- /* @__PURE__ */ jsxs("div", { className: "crewlet-time-window__tabs", role: "tablist", "aria-label": "Time window mode", children: [
3675
- allowAbsolute && /* @__PURE__ */ jsx(
3676
- "button",
3677
- {
3678
- type: "button",
3679
- role: "tab",
3680
- "aria-selected": mode === "absolute",
3681
- className: `crewlet-time-window__tab${mode === "absolute" ? " is-active" : ""}`,
3682
- onClick: () => setMode("absolute"),
3683
- children: "Absolute"
3684
- }
3685
- ),
3686
- allowRelative && /* @__PURE__ */ jsx(
3687
- "button",
3688
- {
3689
- type: "button",
3690
- role: "tab",
3691
- "aria-selected": mode === "relative",
3692
- className: `crewlet-time-window__tab${mode === "relative" ? " is-active" : ""}`,
3693
- onClick: () => setMode("relative"),
3694
- children: "Relative"
3695
- }
3696
- )
3697
- ] }),
3698
- /* @__PURE__ */ jsx("div", { className: "crewlet-time-window__tz", children: /* @__PURE__ */ jsx(
3699
- Select,
3700
- {
3701
- value: draft.timezone || "local",
3702
- onChange: (v) => setDraft({ ...draft, timezone: v }),
3703
- options: [
3704
- { value: "local", label: "Local time zone" },
3705
- { value: "utc", label: "UTC" }
3706
- ],
3707
- size: "sm",
3708
- align: "right",
3709
- ariaLabel: "Time zone"
3710
- }
3711
- ) })
3712
- ] }),
3713
- mode === "absolute" && allowAbsolute && /* @__PURE__ */ jsx(AbsolutePane, { draft, setDraft }),
3714
- mode === "relative" && allowRelative && /* @__PURE__ */ jsx(RelativePane, { draft, setDraft }),
3715
- /* @__PURE__ */ jsxs("div", { className: "crewlet-time-window__footer", children: [
3716
- /* @__PURE__ */ jsx(
3717
- "button",
3718
- {
3719
- type: "button",
3720
- className: "crewlet-time-window__footer-btn",
3721
- onClick: close,
3722
- children: "Cancel"
3723
- }
3724
- ),
3725
- /* @__PURE__ */ jsx(
3726
- "button",
3727
- {
3728
- type: "button",
3729
- className: "crewlet-time-window__footer-btn is-primary",
3730
- onClick: handleApply,
3731
- children: "Apply"
3732
- }
3733
- )
3734
- ] })
3735
- ] });
3736
- }
3737
- function AbsolutePane({ draft, setDraft }) {
3738
- const fromParsed = parseDateTime(draft.from);
3739
- const toParsed = parseDateTime(draft.to);
3740
- const [leftMonth, setLeftMonth] = useState(() => {
3741
- if (fromParsed) return { year: fromParsed.year, month: fromParsed.month };
3742
- const now = /* @__PURE__ */ new Date();
3743
- return { year: now.getFullYear(), month: now.getMonth() };
3744
- });
3745
- const rightMonth = useMemo(() => {
3746
- let m = leftMonth.month + 1;
3747
- let y = leftMonth.year;
3748
- if (m > 11) {
3749
- m = 0;
3750
- y += 1;
3751
- }
3752
- return { year: y, month: m };
3753
- }, [leftMonth]);
3754
- const stepLeft = (delta) => {
3755
- let m = leftMonth.month + delta;
3756
- let y = leftMonth.year;
3757
- while (m < 0) {
3758
- m += 12;
3759
- y -= 1;
3760
- }
3761
- while (m > 11) {
3762
- m -= 12;
3763
- y += 1;
3764
- }
3765
- setLeftMonth({ year: y, month: m });
3766
- };
3767
- const commitFromDate = (s) => {
3768
- const date = parseDateOnly(s);
3769
- if (!date) return;
3770
- const time = fromParsed ? { hour: fromParsed.hour, minute: fromParsed.minute, second: fromParsed.second } : { hour: 0, minute: 0, second: 0 };
3771
- setDraft({
3772
- ...draft,
3773
- kind: "absolute",
3774
- from: formatDateTime({ ...date, ...time }),
3775
- to: draft.to || ""
3776
- });
3777
- };
3778
- const commitFromTime = (s) => {
3779
- const time = parseTimeOnly(s);
3780
- if (!time) return;
3781
- const date = fromParsed ? { year: fromParsed.year, month: fromParsed.month, day: fromParsed.day } : null;
3782
- if (!date) return;
3783
- setDraft({
3784
- ...draft,
3785
- kind: "absolute",
3786
- from: formatDateTime({ ...date, ...time }),
3787
- to: draft.to || ""
3788
- });
3789
- };
3790
- const commitToDate = (s) => {
3791
- const date = parseDateOnly(s);
3792
- if (!date) return;
3793
- const time = toParsed ? { hour: toParsed.hour, minute: toParsed.minute, second: toParsed.second } : { hour: 23, minute: 59, second: 59 };
3794
- setDraft({
3795
- ...draft,
3796
- kind: "absolute",
3797
- from: draft.from || "",
3798
- to: formatDateTime({ ...date, ...time })
3799
- });
3800
- };
3801
- const commitToTime = (s) => {
3802
- const time = parseTimeOnly(s);
3803
- if (!time) return;
3804
- const date = toParsed ? { year: toParsed.year, month: toParsed.month, day: toParsed.day } : null;
3805
- if (!date) return;
3806
- setDraft({
3807
- ...draft,
3808
- kind: "absolute",
3809
- from: draft.from || "",
3810
- to: formatDateTime({ ...date, ...time })
3811
- });
3812
- };
3813
- const fromCoord = fromParsed ? { year: fromParsed.year, month: fromParsed.month, day: fromParsed.day } : null;
3814
- const toCoord = toParsed ? { year: toParsed.year, month: toParsed.month, day: toParsed.day } : null;
3815
- const [phase, setPhase] = useState(
3816
- fromCoord && !toCoord ? "end" : "start"
3817
- );
3818
- const [hoverCoord, setHoverCoord] = useState(null);
3819
- const pickDay = (date) => {
3820
- if (phase === "start") {
3821
- const time = fromParsed ?? { hour: 0, minute: 0, second: 0 };
3822
- setDraft({
3823
- ...draft,
3824
- kind: "absolute",
3825
- from: formatDateTime({ ...date, hour: time.hour, minute: time.minute, second: time.second }),
3826
- to: ""
3827
- });
3828
- setPhase("end");
3829
- return;
3830
- }
3831
- const fromCmp = fromCoord ? compareDates2(date, fromCoord) : 0;
3832
- if (fromCoord && fromCmp < 0) {
3833
- const toTime = { hour: 23, minute: 59, second: 59 };
3834
- setDraft({
3835
- ...draft,
3836
- kind: "absolute",
3837
- from: formatDateTime({ ...date, hour: 0, minute: 0, second: 0 }),
3838
- to: formatDateTime({ ...fromCoord, hour: toTime.hour, minute: toTime.minute, second: toTime.second })
3839
- });
3840
- } else {
3841
- const time = toParsed ?? { hour: 23, minute: 59, second: 59 };
3842
- setDraft({
3843
- ...draft,
3844
- kind: "absolute",
3845
- from: draft.from || "",
3846
- to: formatDateTime({ ...date, hour: time.hour, minute: time.minute, second: time.second })
3847
- });
3848
- }
3849
- setPhase("start");
3850
- setHoverCoord(null);
3851
- };
3852
- const previewEnd = toCoord ?? (phase === "end" ? hoverCoord : null);
3853
- return /* @__PURE__ */ jsxs("div", { className: "crewlet-time-window__absolute", children: [
3854
- /* @__PURE__ */ jsxs("div", { className: "crewlet-time-window__cal-row", children: [
3855
- /* @__PURE__ */ jsx(
3856
- Calendar,
3857
- {
3858
- year: leftMonth.year,
3859
- month: leftMonth.month,
3860
- rangeStart: fromCoord,
3861
- rangeEnd: previewEnd,
3862
- onSelectDate: pickDay,
3863
- onHoverDate: setHoverCoord,
3864
- onPrev: () => stepLeft(-1)
3865
- }
3866
- ),
3867
- /* @__PURE__ */ jsx(
3868
- Calendar,
3869
- {
3870
- year: rightMonth.year,
3871
- month: rightMonth.month,
3872
- rangeStart: fromCoord,
3873
- rangeEnd: previewEnd,
3874
- onSelectDate: pickDay,
3875
- onHoverDate: setHoverCoord,
3876
- onNext: () => stepLeft(1)
3877
- }
3878
- )
3879
- ] }),
3880
- /* @__PURE__ */ jsxs("div", { className: "crewlet-time-window__inputs", children: [
3881
- /* @__PURE__ */ jsxs("div", { className: "crewlet-time-window__field", children: [
3882
- /* @__PURE__ */ jsx("span", { className: "crewlet-time-window__field-label", children: "Start date and time" }),
3883
- /* @__PURE__ */ jsxs("div", { className: "crewlet-time-window__field-row", children: [
3884
- /* @__PURE__ */ jsx(
3885
- MaskedInput,
3886
- {
3887
- mask: "YYYY/MM/DD",
3888
- value: fromParsed ? formatDateOnly(fromParsed) : "",
3889
- onCommit: commitFromDate,
3890
- digitFilter: dateDigitFilter,
3891
- validate: isValidDateInput,
3892
- segments: DATE_SEGMENTS,
3893
- className: "is-date",
3894
- ariaLabel: "Start date"
3895
- }
3896
- ),
3897
- /* @__PURE__ */ jsx(
3898
- MaskedInput,
3899
- {
3900
- mask: "HH:MM:SS",
3901
- value: fromParsed ? formatTimeOnly(fromParsed) : "",
3902
- onCommit: commitFromTime,
3903
- digitFilter: timeDigitFilter,
3904
- validate: isValidTimeInput,
3905
- segments: TIME_SEGMENTS,
3906
- className: "is-time",
3907
- ariaLabel: "Start time"
3908
- }
3909
- )
3910
- ] })
3911
- ] }),
3912
- /* @__PURE__ */ jsxs("div", { className: "crewlet-time-window__field", children: [
3913
- /* @__PURE__ */ jsx("span", { className: "crewlet-time-window__field-label", children: "End date and time" }),
3914
- /* @__PURE__ */ jsxs("div", { className: "crewlet-time-window__field-row", children: [
3915
- /* @__PURE__ */ jsx(
3916
- MaskedInput,
3917
- {
3918
- mask: "YYYY/MM/DD",
3919
- value: toParsed ? formatDateOnly(toParsed) : "",
3920
- onCommit: commitToDate,
3921
- digitFilter: dateDigitFilter,
3922
- validate: isValidDateInput,
3923
- segments: DATE_SEGMENTS,
3924
- className: "is-date",
3925
- ariaLabel: "End date"
3926
- }
3927
- ),
3928
- /* @__PURE__ */ jsx(
3929
- MaskedInput,
3930
- {
3931
- mask: "HH:MM:SS",
3932
- value: toParsed ? formatTimeOnly(toParsed) : "",
3933
- onCommit: commitToTime,
3934
- digitFilter: timeDigitFilter,
3935
- validate: isValidTimeInput,
3936
- segments: TIME_SEGMENTS,
3937
- className: "is-time",
3938
- ariaLabel: "End time"
3939
- }
3940
- )
3941
- ] })
3942
- ] })
3943
- ] })
3944
- ] });
3945
- }
3946
- function RelativePane({ draft, setDraft }) {
3947
- const m = draft.kind === "relative" && draft.duration ? DURATION_RX.exec(draft.duration) : null;
3948
- const draftN = m ? parseInt(m[1], 10) : 60;
3949
- const draftUnit = m ? m[2].toLowerCase() : "m";
3950
- const draftDuration = draft.kind === "relative" ? draft.duration : "";
3951
- const [customN, setCustomN] = useState(String(draftN));
3952
- const [customUnit, setCustomUnit] = useState(draftUnit);
3953
- useEffect(() => {
3954
- if (m) {
3955
- setCustomN(String(parseInt(m[1], 10)));
3956
- setCustomUnit(m[2].toLowerCase());
3957
- }
3958
- }, [draftDuration]);
3959
- const pickChip = (n, unit) => {
3960
- setDraft({ kind: "relative", duration: `${n}${unit}` });
3961
- };
3962
- const commitCustom = (n, unit) => {
3963
- const parsedN = parseInt(n, 10);
3964
- if (!Number.isFinite(parsedN) || parsedN <= 0) return;
3965
- setDraft({ kind: "relative", duration: `${parsedN}${unit}` });
3966
- };
3967
- return /* @__PURE__ */ jsxs("div", { className: "crewlet-time-window__relative", children: [
3968
- CHIP_GROUPS.map((group) => /* @__PURE__ */ jsxs("div", { className: "crewlet-time-window__chip-row", children: [
3969
- /* @__PURE__ */ jsx("span", { className: "crewlet-time-window__chip-label", children: group.label }),
3970
- /* @__PURE__ */ jsx("div", { className: "crewlet-time-window__chips", children: group.values.map((n) => {
3971
- const dur = `${n}${group.unit}`;
3972
- const active = draftDuration === dur;
3973
- return /* @__PURE__ */ jsx(
3974
- "button",
3975
- {
3976
- type: "button",
3977
- className: `crewlet-time-window__chip${active ? " is-active" : ""}`,
3978
- onClick: () => pickChip(n, group.unit),
3979
- children: n
3980
- },
3981
- n
3982
- );
3983
- }) })
3984
- ] }, group.unit)),
3985
- /* @__PURE__ */ jsxs("div", { className: "crewlet-time-window__custom-row", children: [
3986
- /* @__PURE__ */ jsx(
3987
- "input",
3988
- {
3989
- type: "text",
3990
- inputMode: "numeric",
3991
- pattern: "[0-9]*",
3992
- className: "crewlet-time-window__custom-input",
3993
- value: customN,
3994
- onChange: (e) => {
3995
- const next = e.target.value.replace(/[^0-9]/g, "");
3996
- setCustomN(next);
3997
- commitCustom(next, customUnit);
3998
- },
3999
- "aria-label": "Custom amount"
4000
- }
4001
- ),
4002
- /* @__PURE__ */ jsx(
4003
- Select,
4004
- {
4005
- value: customUnit,
4006
- onChange: (v) => {
4007
- const unit = String(v);
4008
- setCustomUnit(unit);
4009
- commitCustom(customN, unit);
4010
- },
4011
- options: [
4012
- { value: "m", label: "Minutes" },
4013
- { value: "h", label: "Hours" },
4014
- { value: "d", label: "Days" },
4015
- { value: "w", label: "Weeks" }
4016
- ],
4017
- size: "sm",
4018
- ariaLabel: "Custom unit"
4019
- }
4020
- )
4021
- ] })
4022
- ] });
4023
- }
4024
- function MaskedInput({
4025
- mask,
4026
- value,
4027
- onCommit,
4028
- digitFilter,
4029
- validate,
4030
- segments,
4031
- className,
4032
- ariaLabel
4033
- }) {
4034
- const inputRef = useRef(null);
4035
- const isSepAt = (pos) => {
4036
- const c = mask[pos];
4037
- return !!c && !/[A-Za-z]/.test(c);
4038
- };
4039
- const projectValueToDisplay = (v) => {
4040
- let out = "";
4041
- let vi = 0;
4042
- for (let i = 0; i < mask.length; i += 1) {
4043
- if (isSepAt(i)) {
4044
- out += mask[i];
4045
- if (vi < v.length && v[vi] === mask[i]) vi += 1;
4046
- continue;
4047
- }
4048
- while (vi < v.length && !/\d/.test(v[vi])) vi += 1;
4049
- if (vi < v.length) {
4050
- out += v[vi];
4051
- vi += 1;
4052
- } else {
4053
- out += mask[i];
4054
- }
4055
- }
4056
- return out;
4057
- };
4058
- const [display, setDisplay] = useState(() => projectValueToDisplay(value));
4059
- useEffect(() => {
4060
- setDisplay(projectValueToDisplay(value));
4061
- }, [value]);
4062
- const hasAnyDigit = /\d/.test(display);
4063
- const nextDigitSlot = (from) => {
4064
- let p = from;
4065
- while (p < mask.length && isSepAt(p)) p += 1;
4066
- return p < mask.length ? p : -1;
4067
- };
4068
- const prevDigitSlot = (from) => {
4069
- let p = from - 1;
4070
- while (p >= 0 && isSepAt(p)) p -= 1;
4071
- return p;
4072
- };
4073
- const setCursor = (pos) => {
4074
- requestAnimationFrame(() => {
4075
- const el = inputRef.current;
4076
- if (el) el.setSelectionRange(pos, pos);
4077
- });
4078
- };
4079
- const handleKeyDown = (e) => {
4080
- const el = e.currentTarget;
4081
- const pos = el.selectionStart ?? 0;
4082
- if (/^\d$/.test(e.key)) {
4083
- e.preventDefault();
4084
- const target = nextDigitSlot(pos);
4085
- if (target < 0) return;
4086
- if (digitFilter && !digitFilter(e.key, target, display)) {
4087
- return;
4088
- }
4089
- const next = display.substring(0, target) + e.key + display.substring(target + 1);
4090
- setDisplay(next);
4091
- let cursor = target + 1;
4092
- while (cursor < mask.length && isSepAt(cursor)) cursor += 1;
4093
- setCursor(cursor);
4094
- return;
4095
- }
4096
- if (e.key === "Backspace") {
4097
- e.preventDefault();
4098
- const target = prevDigitSlot(pos);
4099
- if (target < 0) return;
4100
- const next = display.substring(0, target) + mask[target] + display.substring(target + 1);
4101
- setDisplay(next);
4102
- setCursor(target);
4103
- return;
4104
- }
4105
- if (e.key === "Delete") {
4106
- e.preventDefault();
4107
- if (pos < mask.length && !isSepAt(pos)) {
4108
- const next = display.substring(0, pos) + mask[pos] + display.substring(pos + 1);
4109
- setDisplay(next);
4110
- }
4111
- return;
4112
- }
4113
- if ((e.key === "ArrowUp" || e.key === "ArrowDown") && segments && segments.length > 0) {
4114
- const seg = segments.find((s) => pos >= s.start && pos <= s.end + 1);
4115
- if (!seg) return;
4116
- e.preventDefault();
4117
- const segText = display.substring(seg.start, seg.end + 1);
4118
- const hasDigits = /\d/.test(segText);
4119
- const width = seg.end - seg.start + 1;
4120
- let nextVal;
4121
- if (!hasDigits) {
4122
- nextVal = seg.min;
4123
- } else {
4124
- const numeric = segText.replace(/[^0-9]/g, "0");
4125
- const current = parseInt(numeric, 10);
4126
- nextVal = e.key === "ArrowUp" ? current + 1 : current - 1;
4127
- if (nextVal > seg.max) nextVal = seg.max;
4128
- if (nextVal < seg.min) nextVal = seg.min;
4129
- }
4130
- const padded = String(nextVal).padStart(width, "0");
4131
- const next = display.substring(0, seg.start) + padded + display.substring(seg.end + 1);
4132
- setDisplay(next);
4133
- setCursor(seg.end + 1);
4134
- return;
4135
- }
4136
- if (e.key === "ArrowLeft") {
4137
- e.preventDefault();
4138
- const target = prevDigitSlot(pos);
4139
- setCursor(target < 0 ? 0 : target);
4140
- return;
4141
- }
4142
- if (e.key === "ArrowRight") {
4143
- e.preventDefault();
4144
- const target = nextDigitSlot(pos + 1);
4145
- setCursor(target < 0 ? mask.length : target);
4146
- return;
4147
- }
4148
- if (e.key === "Home") {
4149
- e.preventDefault();
4150
- const target = nextDigitSlot(0);
4151
- setCursor(target < 0 ? 0 : target);
4152
- return;
4153
- }
4154
- if (e.key === "End") {
4155
- e.preventDefault();
4156
- let target = mask.length;
4157
- while (target > 0 && isSepAt(target - 1)) target -= 1;
4158
- setCursor(target);
4159
- return;
4160
- }
4161
- if (e.key === "Tab" || e.key === "Enter" || e.key === "Escape") return;
4162
- e.preventDefault();
4163
- };
4164
- const snapToLeftmost = (el) => {
4165
- const target = nextDigitSlot(0);
4166
- const pos = target < 0 ? 0 : target;
4167
- el.setSelectionRange(pos, pos);
4168
- };
4169
- const handleFocus = (e) => {
4170
- const el = e.currentTarget;
4171
- if (!hasAnyDigit) {
4172
- requestAnimationFrame(() => snapToLeftmost(el));
4173
- }
4174
- };
4175
- const handleClick = (e) => {
4176
- const el = e.currentTarget;
4177
- requestAnimationFrame(() => {
4178
- if (!hasAnyDigit) {
4179
- snapToLeftmost(el);
4180
- return;
4181
- }
4182
- const pos = el.selectionStart ?? 0;
4183
- if (isSepAt(pos)) {
4184
- const target = nextDigitSlot(pos);
4185
- if (target >= 0) el.setSelectionRange(target, target);
4186
- }
4187
- });
4188
- };
4189
- const handlePaste = (e) => {
4190
- e.preventDefault();
4191
- const pasted = e.clipboardData.getData("text");
4192
- const digits = pasted.replace(/\D/g, "");
4193
- if (!digits) return;
4194
- const el = e.currentTarget;
4195
- const startPos = nextDigitSlot(el.selectionStart ?? 0);
4196
- if (startPos < 0) return;
4197
- let out = display;
4198
- let cursor = startPos;
4199
- let di = 0;
4200
- while (cursor < mask.length && di < digits.length) {
4201
- if (!isSepAt(cursor)) {
4202
- out = out.substring(0, cursor) + digits[di] + out.substring(cursor + 1);
4203
- di += 1;
4204
- }
4205
- cursor += 1;
4206
- }
4207
- setDisplay(out);
4208
- setCursor(cursor);
4209
- };
4210
- const handleBlur = () => {
4211
- if (validate && !validate(display)) {
4212
- setDisplay(projectValueToDisplay(value));
4213
- return;
4214
- }
4215
- onCommit(display);
4216
- };
4217
- const isPrecedingSegmentFilled = (sepPos) => {
4218
- for (let p = sepPos - 1; p >= 0; p -= 1) {
4219
- if (isSepAt(p)) break;
4220
- if (!/\d/.test(display[p] || "")) return false;
4221
- }
4222
- return true;
4223
- };
4224
- const overlay = display.split("").map((ch, i) => {
4225
- const sep = isSepAt(i);
4226
- const bright = sep ? isPrecedingSegmentFilled(i) : /\d/.test(ch);
4227
- return /* @__PURE__ */ jsx(
4228
- "span",
4229
- {
4230
- className: `crewlet-time-window__masked-char ${bright ? "is-digit" : "is-mask"}`,
4231
- children: ch
4232
- },
4233
- i
4234
- );
4235
- });
4236
- const wrapClass = [
4237
- "crewlet-time-window__masked-wrap",
4238
- className || "",
4239
- hasAnyDigit ? "" : "is-empty"
4240
- ].filter(Boolean).join(" ");
4241
- return /* @__PURE__ */ jsxs("div", { className: wrapClass, children: [
4242
- /* @__PURE__ */ jsx(
4243
- "input",
4244
- {
4245
- ref: inputRef,
4246
- type: "text",
4247
- className: "crewlet-time-window__text-input",
4248
- value: display,
4249
- onKeyDown: handleKeyDown,
4250
- onFocus: handleFocus,
4251
- onClick: handleClick,
4252
- onPaste: handlePaste,
4253
- onChange: () => {
4254
- },
4255
- onBlur: handleBlur,
4256
- "aria-label": ariaLabel
4257
- }
4258
- ),
4259
- /* @__PURE__ */ jsx("div", { className: "crewlet-time-window__masked-overlay", "aria-hidden": true, children: overlay })
4260
- ] });
4261
- }
4262
- var Tag2 = ({
4263
- variant = "neutral",
4264
- size = "md",
4265
- leadingIcon,
4266
- onRemove,
4267
- removeAriaLabel = "Remove tag",
4268
- monospace = false,
4269
- className = "",
4270
- children,
4271
- ...rest
4272
- }) => {
4273
- const classes = [
4274
- "crewlet-tag",
4275
- `crewlet-tag--${variant}`,
4276
- `crewlet-tag--${size}`,
4277
- monospace ? "crewlet-tag--monospace" : "",
4278
- onRemove ? "is-removable" : "",
4279
- className
4280
- ].filter(Boolean).join(" ");
4281
- return /* @__PURE__ */ jsxs("span", { ...rest, className: classes, children: [
4282
- leadingIcon ? /* @__PURE__ */ jsx("span", { className: "crewlet-tag__icon", "aria-hidden": true, children: leadingIcon }) : null,
4283
- /* @__PURE__ */ jsx("span", { className: "crewlet-tag__label", children }),
4284
- onRemove ? /* @__PURE__ */ jsx(
4285
- "button",
4286
- {
4287
- type: "button",
4288
- className: "crewlet-tag__remove",
4289
- onClick: (event) => {
4290
- event.stopPropagation();
4291
- onRemove();
4292
- },
4293
- "aria-label": removeAriaLabel,
4294
- children: /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "\xD7" })
4295
- }
4296
- ) : null
4297
- ] });
4298
- };
4299
- var TagsInput = forwardRef(function TagsInput2({
4300
- value,
4301
- onChange,
4302
- placeholder = "Add a tag, press Enter or comma",
4303
- tagVariant = "neutral",
4304
- monospace = false,
4305
- submitKeys = ["Enter", ","],
4306
- disabled = false,
4307
- className = "",
4308
- id,
4309
- dedupe = true
4310
- }, ref) {
4311
- const [draft, setDraft] = useState("");
4312
- const inputRef = useRef(null);
4313
- useImperativeHandle(ref, () => ({
4314
- focus: () => inputRef.current?.focus()
4315
- }));
4316
- const commit = (raw) => {
4317
- const next = raw.trim();
4318
- if (!next) return;
4319
- if (dedupe && value.some((v) => v.toLowerCase() === next.toLowerCase())) {
4320
- setDraft("");
4321
- return;
4322
- }
4323
- onChange([...value, next]);
4324
- setDraft("");
4325
- };
4326
- const onKeyDown = (event) => {
4327
- if (submitKeys.includes(event.key)) {
4328
- event.preventDefault();
4329
- commit(draft);
4330
- return;
4331
- }
4332
- if (event.key === "Backspace" && draft === "" && value.length > 0) {
4333
- event.preventDefault();
4334
- onChange(value.slice(0, -1));
4335
- }
4336
- };
4337
- const onPaste = (event) => {
4338
- const text = event.clipboardData.getData("text");
4339
- if (!text.includes(",") && !text.includes("\n")) return;
4340
- event.preventDefault();
4341
- const parts = text.split(/[,\n]+/).map((p) => p.trim()).filter(Boolean);
4342
- if (!parts.length) return;
4343
- const set = dedupe ? new Set(value.map((v) => v.toLowerCase())) : null;
4344
- const added = [];
4345
- for (const p of parts) {
4346
- if (set && set.has(p.toLowerCase())) continue;
4347
- added.push(p);
4348
- set?.add(p.toLowerCase());
4349
- }
4350
- if (added.length) onChange([...value, ...added]);
4351
- };
4352
- const onInputChange = (event) => {
4353
- setDraft(event.target.value);
4354
- };
4355
- const onContainerClick = () => {
4356
- inputRef.current?.focus();
4357
- };
4358
- const removeAt = (index) => {
4359
- onChange(value.filter((_, i) => i !== index));
4360
- };
4361
- const classes = ["crewlet-tags-input", disabled ? "is-disabled" : "", className].filter(Boolean).join(" ");
4362
- return /* @__PURE__ */ jsxs("div", { className: classes, onClick: onContainerClick, children: [
4363
- value.map((tag, index) => /* @__PURE__ */ jsx(
4364
- Tag2,
4365
- {
4366
- variant: tagVariant,
4367
- monospace,
4368
- onRemove: disabled ? void 0 : () => removeAt(index),
4369
- removeAriaLabel: `Remove ${tag}`,
4370
- children: tag
4371
- },
4372
- `${index}-${tag}`
4373
- )),
4374
- /* @__PURE__ */ jsx(
4375
- "input",
4376
- {
4377
- ref: inputRef,
4378
- id,
4379
- className: "crewlet-tags-input__field",
4380
- type: "text",
4381
- value: draft,
4382
- placeholder,
4383
- disabled,
4384
- onChange: onInputChange,
4385
- onKeyDown,
4386
- onPaste,
4387
- onBlur: () => commit(draft)
4388
- }
4389
- )
4390
- ] });
4391
- });
4392
- var ICONS = {
4393
- info: "info",
4394
- success: "check_circle",
4395
- warn: "warning",
4396
- error: "error"
4397
- };
4398
- var TITLES = {
4399
- info: null,
4400
- success: "Success",
4401
- warn: "Heads up",
4402
- error: "Something went wrong"
4403
- };
4404
- var isToastAction = (value) => {
4405
- if (!value || typeof value !== "object") return false;
4406
- const v = value;
4407
- return "label" in v && ("onClick" in v || "href" in v);
4408
- };
4409
- var ToastItem = ({ toast, onDismiss, renderAction }) => {
4410
- const variant = toast.variant || "info";
4411
- const knownVariant = ["info", "success", "warn", "error"].includes(variant);
4412
- const classes = [
4413
- "crewlet-toast",
4414
- `crewlet-toast--${toast.variant ?? "info"}`
4415
- ].filter(Boolean).join(" ");
4416
- const role = variant === "error" || toast.variant === "quota" ? "alert" : "status";
4417
- const icon = toast.icon ?? (knownVariant ? /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: ICONS[variant] }) : null);
4418
- const fallbackTitle = knownVariant ? TITLES[variant] : null;
4419
- const renderStandardAction = () => {
4420
- if (toast.action === void 0 || toast.action === null) return null;
4421
- if (!isToastAction(toast.action)) {
4422
- return /* @__PURE__ */ jsx("div", { className: "crewlet-toast__actions", children: toast.action });
4423
- }
4424
- const action = toast.action;
4425
- if (renderAction) {
4426
- const node = renderAction(action, { onDismiss });
4427
- return /* @__PURE__ */ jsx("div", { className: "crewlet-toast__actions", children: node });
4428
- }
4429
- if (action.href) {
4430
- const isExternal = action.external ?? /^https?:\/\//.test(action.href);
4431
- return /* @__PURE__ */ jsx("div", { className: "crewlet-toast__actions", children: /* @__PURE__ */ jsx(
4432
- "a",
4433
- {
4434
- className: "crewlet-toast__action-link",
4435
- href: action.href,
4436
- target: isExternal ? "_blank" : void 0,
4437
- rel: isExternal ? "noreferrer" : void 0,
4438
- onClick: onDismiss,
4439
- children: action.label
4440
- }
4441
- ) });
4442
- }
4443
- return /* @__PURE__ */ jsx("div", { className: "crewlet-toast__actions", children: /* @__PURE__ */ jsx(
4444
- "button",
4445
- {
4446
- type: "button",
4447
- className: "crewlet-toast__action-btn",
4448
- onClick: () => {
4449
- action.onClick?.();
4450
- onDismiss();
4451
- },
4452
- children: action.label
4453
- }
4454
- ) });
4455
- };
4456
- return /* @__PURE__ */ jsxs("div", { className: classes, role, children: [
4457
- toast.renderBody ? toast.renderBody({ onDismiss, toast }) : /* @__PURE__ */ jsxs(Fragment, { children: [
4458
- icon ? /* @__PURE__ */ jsx("span", { className: "crewlet-toast__icon", children: icon }) : null,
4459
- /* @__PURE__ */ jsxs("div", { className: "crewlet-toast__body", children: [
4460
- toast.title || fallbackTitle ? /* @__PURE__ */ jsx("div", { className: "crewlet-toast__title", children: toast.title || fallbackTitle }) : null,
4461
- toast.message ? /* @__PURE__ */ jsx("div", { className: "crewlet-toast__message", children: toast.message }) : null,
4462
- renderStandardAction()
4463
- ] })
4464
- ] }),
4465
- /* @__PURE__ */ jsx(
4466
- "button",
4467
- {
4468
- type: "button",
4469
- className: "crewlet-toast__dismiss",
4470
- onClick: onDismiss,
4471
- "aria-label": "Dismiss notification",
4472
- children: /* @__PURE__ */ jsx("span", { className: "material-symbols-outlined", "aria-hidden": true, children: "close" })
4473
- }
4474
- ),
4475
- toast.duration && toast.duration > 0 ? /* @__PURE__ */ jsx(
4476
- "div",
4477
- {
4478
- className: "crewlet-toast__progress",
4479
- style: { animationDuration: `${toast.duration}ms` },
4480
- onAnimationEnd: onDismiss,
4481
- "aria-hidden": true
4482
- },
4483
- toast.version ?? 0
4484
- ) : null
4485
- ] });
4486
- };
4487
- var Toaster = ({
4488
- toasts,
4489
- onDismiss,
4490
- renderAction,
4491
- position = "bottom-right",
4492
- className = ""
4493
- }) => {
4494
- if (!toasts?.length) return null;
4495
- const style = void 0;
4496
- const classes = ["crewlet-toaster", `crewlet-toaster--${position}`, className].filter(Boolean).join(" ");
4497
- return /* @__PURE__ */ jsx(
4498
- "div",
4499
- {
4500
- className: classes,
4501
- role: "region",
4502
- "aria-live": "polite",
4503
- "aria-label": "Notifications",
4504
- style,
4505
- children: toasts.map((toast) => /* @__PURE__ */ jsx(
4506
- ToastItem,
4507
- {
4508
- toast,
4509
- onDismiss: () => onDismiss(toast.id),
4510
- renderAction
4511
- },
4512
- toast.id
4513
- ))
4514
- }
4515
- );
4516
- };
4517
-
4518
- export { Accordion, AccordionItem, AppShell, Avatar, Button, Callout, Card, Checkbox, CodeBlock, ConfirmModal, Container, Copyable, CopyableCell, DataTable, DateTimePicker, Eyebrow, FormField, FormRow, IconButton, ImageUpload, Input, Kbd, Label, Modal, Popover, PricingCard, Section, Select, Skeleton, StatCard, Table, Tabs, Tag2 as Tag, TagsInput, Textarea, TimeWindowPicker, Toaster, getInitials, parseTimeWindowDuration, resolveTimeWindow };
1
+ import './chunk-ZCR6YGQ4.js';
2
+ export { TreeCanvas } from './chunk-7HPRF5XN.js';
3
+ import './chunk-IJ7QXNIJ.js';
4
+ export { Text } from './chunk-23DHIMJA.js';
5
+ export { DensitySwitcher, ThemeSwitcher, applyDensity, applyStoredPreferences, applyTheme, useDensityPreference, useThemePreference } from './chunk-2HQR4U4D.js';
6
+ export { DEFAULT_TIME_WINDOW_LABELS, TIME_WINDOW_SPANS, TimeWindowPicker, describeTimeWindow, isTimeWindowSet, parseTimeWindowDuration, resolveTimeWindow, timeWindowMatchesPreset } from './chunk-WOYYD2YR.js';
7
+ export { Timeline, TimelineItem } from './chunk-O67VF5MS.js';
8
+ export { ToastProvider, Toaster, useToast } from './chunk-NPZY6YYL.js';
9
+ export { Tooltip } from './chunk-XV6WRMBC.js';
10
+ export { StatCard } from './chunk-22O7LAW2.js';
11
+ export { Switch } from './chunk-EV6IMYJ6.js';
12
+ export { Table } from './chunk-FEFTGVMF.js';
13
+ export { TagGroup } from './chunk-V2XMNLMS.js';
14
+ export { TagsInput } from './chunk-RDGD7Z7I.js';
15
+ export { SearchTrigger } from './chunk-Q4ZPPART.js';
16
+ export { Section } from './chunk-A2UXBCLO.js';
17
+ export { SegmentedControl } from './chunk-6ZFVFA56.js';
18
+ export { TabPanel, Tabs, tabId } from './chunk-QEUY3A5P.js';
19
+ export { Select } from './chunk-OFHBK7RI.js';
20
+ export { matchesShortcut, useShortcut } from './chunk-F6OE5FH3.js';
21
+ export { NavGroup, NavItem, SidebarNav } from './chunk-3EYF6HKB.js';
22
+ export { Skeleton } from './chunk-W5J7R5EB.js';
23
+ export { StatGroup } from './chunk-QNCVHRZL.js';
24
+ export { OrgNodeDisclosure, OrgNodeLabel, OrgNodeLead } from './chunk-IAJLHVUA.js';
25
+ export { OrgTable, OrgTableActions, OrgTableAdd, OrgTableName } from './chunk-H4FBTAYS.js';
26
+ export { TreeGrid } from './chunk-5SDBMKUO.js';
27
+ export { SIZE_EPSILON, TYPE_AHEAD_RESET_MS, createTreeModel, isTypeAheadKey, layoutConnectors, layoutForest, treeAllExpandable, treeAncestors, treeCollapseOrAscend, treeExpandOrDescend, treeExpandTo, treeExpandable, treeFirst, treeFirstChild, treeItemAction, treeLast, treeLevel, treeNext, treeParent, treePosInSet, treePrevious, treeSetSize, treeStep, treeTypeAhead, treeVisible, typeAheadBuffer, useLayoutAnchor, useMeasuredSizes, useTreeState } from './chunk-NTRZGNEX.js';
28
+ export { OrgLabel } from './chunk-5C5YMZQU.js';
29
+ export { PricingCard } from './chunk-KLJCCMF3.js';
30
+ export { Tag } from './chunk-S2XHHEW4.js';
31
+ export { StatusDot } from './chunk-PZSWF74H.js';
32
+ export { Prose } from './chunk-JCSA3XVH.js';
33
+ export { Link } from './chunk-2ZQMA2WB.js';
34
+ export { List, ListItem } from './chunk-HZ5XZNPS.js';
35
+ export { ListInput } from './chunk-EN6K2IFA.js';
36
+ export { Meter, meterTone, progressTone } from './chunk-YODNNSZL.js';
37
+ export { NewItemsNotice } from './chunk-YLYOWZ4V.js';
38
+ export { useFullscreen } from './chunk-XDTYBF2F.js';
39
+ export { ImageUpload } from './chunk-5WS6L6FA.js';
40
+ export { InlineCode } from './chunk-NN62JQLR.js';
41
+ export { AutoGrid, Inline, Spacer, Stack } from './chunk-W4ENKCXJ.js';
42
+ export { DIFF_LIST_LABELS, DiffList } from './chunk-HQNHV7D6.js';
43
+ export { Disclosure } from './chunk-LWKJQTOM.js';
44
+ export { EmptyState } from './chunk-RA2ULG6H.js';
45
+ export { EntityChip } from './chunk-QYOFACSP.js';
46
+ export { ErrorBoundary } from './chunk-7EM5T4ME.js';
47
+ export { Eyebrow } from './chunk-RK4XPHVG.js';
48
+ export { FILTER_CHIP_ATTRIBUTE, FilterChip, FilterChipGroup } from './chunk-ZFATEERP.js';
49
+ export { CommandPalette } from './chunk-ESCO4XYW.js';
50
+ export { Container } from './chunk-QVE4GJAT.js';
51
+ export { DATA_VIEW_TOOLBAR_LABELS, DataView, DataViewToolbar, FILTER_AXIS_LABELS, FilterAxisBar, FilterAxisChip, applyColumnFilters, applyFilters, blankFilterValue, defaultOperator, filterDefsFromColumns, filterHasValue, filterPredicate, formatFilterValue, operatorOf, tableColumns } from './chunk-34VGEDVV.js';
52
+ export { Toolbar, ToolbarOverflow, ToolbarWide } from './chunk-5EKIWVOR.js';
53
+ export { PageHeader } from './chunk-HOXGBLEY.js';
54
+ export { RelativeTime, currentNow, formatElapsed, formatExact, formatRelative, formatUntil, toISO, toInstant, useNow } from './chunk-3CZ7XAVY.js';
55
+ export { Kbd, isApplePlatform, keyGlyph } from './chunk-SFZOKW2G.js';
56
+ export { ALL_ITEMS, CopyableCell, DATA_TABLE_LABELS, DataTable, RowActionsMenu } from './chunk-WQX73TWF.js';
57
+ export { Menu } from './chunk-FBZAKNSX.js';
58
+ export { EMPTY_VALUE, EmptyValue } from './chunk-EUCC37WY.js';
59
+ export { ConfirmModal, Modal } from './chunk-2EOKFKHX.js';
60
+ export { DEFAULT_DATE_TIME_PICKER_LABELS, DateTimePicker } from './chunk-UG46QBLT.js';
61
+ export { Popover } from './chunk-DD5BBGBE.js';
62
+ export { DescriptionList, DescriptionListItem } from './chunk-XN4JBAN3.js';
63
+ export { CalendarGrid, compareDays, daysInMonth, sameDay, shiftDay, shiftMonth, today } from './chunk-GXQ3B46I.js';
64
+ export { Callout } from './chunk-3ZP55QZE.js';
65
+ export { CANVAS_COMPOSE_CONTEXT, CANVAS_FIT_MAX_ZOOM, CANVAS_FIT_PADDING, CANVAS_FOCUS_CONTEXT, CANVAS_MAX_ZOOM, CANVAS_MIN_ZOOM, CANVAS_PAN_MARGIN, CANVAS_PAN_STEP, CANVAS_REVEAL_PADDING, CANVAS_TAP_SLOP, CANVAS_WHEEL_LINE_PX, CANVAS_WHEEL_ZOOM_PER_PIXEL, CANVAS_ZOOM_CEILING, CANVAS_ZOOM_LIMITS, CANVAS_ZOOM_STEP, Canvas, IDENTITY_VIEW, anchorView, beyondSlop, clampPan, clampZoom, fitView, panBy, pinchView, regionView, revealView, screenRect, toScreen, toWorld, wheelPixels, wheelZoomFactor, zoomAt, zoomLimits } from './chunk-CMEE37QJ.js';
66
+ import './chunk-53DVJCVW.js';
67
+ export { Card } from './chunk-NDCATSW7.js';
68
+ export { useCardHeaderSlot } from './chunk-AV2DXAEL.js';
69
+ export { ActivityStrip, BarList, DATA_COLORS, DATA_COLOR_OTHER, Legend, Sparkline, StackedBar, TimeSeries, dataColor } from './chunk-F4PJZ4ED.js';
70
+ export { Checkbox } from './chunk-2PRH365R.js';
71
+ export { CodeBlock } from './chunk-D35VTV2Z.js';
72
+ export { CopyButton, Copyable } from './chunk-3QRYCPXQ.js';
73
+ export { useClipboard, writeClipboard } from './chunk-5VNUYO76.js';
74
+ export { Combobox } from './chunk-UOGSZKLT.js';
75
+ export { TYPE_AHEAD_WINDOW, useListbox, useOptionKeys } from './chunk-GTOUGQQK.js';
76
+ export { FormField, FormRow, FormSection, Input, InputAffix, Label, ReadOnlyField, Textarea } from './chunk-O6232G37.js';
77
+ export { Accordion, AccordionItem } from './chunk-7FZ53WDJ.js';
78
+ export { DisclosureAnatomy } from './chunk-5KPQ6D7N.js';
79
+ export { Count } from './chunk-BHHIIK3M.js';
80
+ export { HeadingLevelProvider, headingTag, nextHeadingLevel, useHeadingLevel } from './chunk-PQADV6P6.js';
81
+ export { ADD_PILL_MS, AddPill } from './chunk-S4GZWSKR.js';
82
+ export { AppShell, AppShellRail, AppShellRailRow, AppShellToggle, AppShellTopbar, useAppShell } from './chunk-MNAXYY3R.js';
83
+ export { LAYER_GAP, LAYER_REPOSITION_EVENT, LayerHost, outsideBounds, placePopup, useLayerContainer, viewportBounds } from './chunk-7RPGY3JE.js';
84
+ export { focusables, isComposing, isModalLayerOpen, layerCount, useBodyScrollLock, useModalLayer, usePopupLayer } from './chunk-ITHXJHBB.js';
85
+ export { IconButton } from './chunk-F27EPQ7D.js';
86
+ export { Announcer, announce, useAnnouncer } from './chunk-RSAQOKH3.js';
87
+ export { AVATAR_SIZES, Avatar, avatarPixels, avatarSquareCorner, avatarTint, getInitials } from './chunk-PNFASD6F.js';
88
+ export { BrandLockup } from './chunk-WNI2WSJF.js';
89
+ export { Button, ButtonLink } from './chunk-BZYDZBAN.js';
90
+ export { VisuallyHidden } from './chunk-BZFWDIV6.js';
91
+ export { cx } from './chunk-ODEQTPFM.js';