@marktiderman/genesis-ui 1.3.0 → 2.1.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 (297) hide show
  1. package/LICENSE +21 -0
  2. package/dist/AppShell-sQT0UbXg.d.ts +198 -0
  3. package/dist/{chunk-NJMO3VPA.js → chunk-22XB3Q6H.js} +8 -8
  4. package/dist/chunk-22XB3Q6H.js.map +1 -0
  5. package/dist/{chunk-2MXMWBDV.js → chunk-27ELYTGE.js} +6 -6
  6. package/dist/chunk-27ELYTGE.js.map +1 -0
  7. package/dist/chunk-46TZGMFW.js +159 -0
  8. package/dist/chunk-46TZGMFW.js.map +1 -0
  9. package/dist/chunk-4CQHPWJD.js +117 -0
  10. package/dist/chunk-4CQHPWJD.js.map +1 -0
  11. package/dist/{chunk-ZDAT5JDH.js → chunk-65SPT7EA.js} +24 -6
  12. package/dist/chunk-65SPT7EA.js.map +1 -0
  13. package/dist/chunk-6KHY647C.js +67 -0
  14. package/dist/chunk-6KHY647C.js.map +1 -0
  15. package/dist/chunk-6M4NHHKG.js +51 -0
  16. package/dist/chunk-6M4NHHKG.js.map +1 -0
  17. package/dist/chunk-6Q6CLLJ4.js +9 -0
  18. package/dist/chunk-6Q6CLLJ4.js.map +1 -0
  19. package/dist/{chunk-UGHQFQOE.js → chunk-6TVMQH6M.js} +28 -28
  20. package/dist/chunk-6TVMQH6M.js.map +1 -0
  21. package/dist/{chunk-6VVVGYKS.js → chunk-6XPMU65K.js} +5 -5
  22. package/dist/{chunk-6VVVGYKS.js.map → chunk-6XPMU65K.js.map} +1 -1
  23. package/dist/{chunk-YZEA5TGK.js → chunk-7XMZVISM.js} +16 -16
  24. package/dist/chunk-7XMZVISM.js.map +1 -0
  25. package/dist/chunk-A7AHVDG7.js +52 -0
  26. package/dist/chunk-A7AHVDG7.js.map +1 -0
  27. package/dist/chunk-AFZ76Q26.js +84 -0
  28. package/dist/chunk-AFZ76Q26.js.map +1 -0
  29. package/dist/{chunk-4V4H2ZM7.js → chunk-AL5TMEZQ.js} +31 -31
  30. package/dist/chunk-AL5TMEZQ.js.map +1 -0
  31. package/dist/{chunk-AUECDWP2.js → chunk-AOZGQM6W.js} +3 -3
  32. package/dist/chunk-AOZGQM6W.js.map +1 -0
  33. package/dist/chunk-ARJI2JUV.js +122 -0
  34. package/dist/chunk-ARJI2JUV.js.map +1 -0
  35. package/dist/chunk-AVFZ4P4B.js +25 -0
  36. package/dist/chunk-AVFZ4P4B.js.map +1 -0
  37. package/dist/chunk-B2GIBUQD.js +11 -0
  38. package/dist/chunk-B2GIBUQD.js.map +1 -0
  39. package/dist/{chunk-JXI2MYL3.js → chunk-C46ITXE4.js} +5 -5
  40. package/dist/chunk-C46ITXE4.js.map +1 -0
  41. package/dist/{chunk-A2OXCNNY.js → chunk-CNTWQSUT.js} +3 -3
  42. package/dist/{chunk-A2OXCNNY.js.map → chunk-CNTWQSUT.js.map} +1 -1
  43. package/dist/chunk-D2LIXA2Y.js +65 -0
  44. package/dist/chunk-D2LIXA2Y.js.map +1 -0
  45. package/dist/{chunk-T5VX5DYO.js → chunk-F47U5KNW.js} +5 -5
  46. package/dist/chunk-F47U5KNW.js.map +1 -0
  47. package/dist/chunk-FEJAZFP3.js +1100 -0
  48. package/dist/chunk-FEJAZFP3.js.map +1 -0
  49. package/dist/{chunk-2B3MFHBQ.js → chunk-FK42ZGGV.js} +21 -21
  50. package/dist/chunk-FK42ZGGV.js.map +1 -0
  51. package/dist/{chunk-XISSLIMH.js → chunk-FTTNGGLA.js} +2 -2
  52. package/dist/{chunk-XISSLIMH.js.map → chunk-FTTNGGLA.js.map} +1 -1
  53. package/dist/chunk-G5YKBPBF.js +74 -0
  54. package/dist/chunk-G5YKBPBF.js.map +1 -0
  55. package/dist/{chunk-QLCDAFS7.js → chunk-GDFNAHI4.js} +10 -6
  56. package/dist/chunk-GDFNAHI4.js.map +1 -0
  57. package/dist/{chunk-KB5T5LWM.js → chunk-IJCT5IJD.js} +3 -3
  58. package/dist/{chunk-KB5T5LWM.js.map → chunk-IJCT5IJD.js.map} +1 -1
  59. package/dist/{chunk-44TULYGG.js → chunk-J6FBA7PH.js} +3 -3
  60. package/dist/{chunk-44TULYGG.js.map → chunk-J6FBA7PH.js.map} +1 -1
  61. package/dist/chunk-JAMLFMDS.js +12 -0
  62. package/dist/chunk-JAMLFMDS.js.map +1 -0
  63. package/dist/{chunk-C6H7AMO4.js → chunk-LNFF2RBD.js} +9 -9
  64. package/dist/chunk-LNFF2RBD.js.map +1 -0
  65. package/dist/{chunk-QBZMY2AF.js → chunk-M62CHO3N.js} +18 -18
  66. package/dist/chunk-M62CHO3N.js.map +1 -0
  67. package/dist/{chunk-5Q4FABZQ.js → chunk-NUXW6NPP.js} +8 -8
  68. package/dist/chunk-NUXW6NPP.js.map +1 -0
  69. package/dist/{chunk-ZJFSZFPI.js → chunk-NWSCYLPT.js} +77 -18
  70. package/dist/chunk-NWSCYLPT.js.map +1 -0
  71. package/dist/{chunk-63FW5ZUA.js → chunk-P3ZL6FGR.js} +7 -7
  72. package/dist/chunk-P3ZL6FGR.js.map +1 -0
  73. package/dist/chunk-P64MFQXZ.js +191 -0
  74. package/dist/chunk-P64MFQXZ.js.map +1 -0
  75. package/dist/chunk-PLIYF6KI.js +52 -0
  76. package/dist/chunk-PLIYF6KI.js.map +1 -0
  77. package/dist/chunk-PSR47JRA.js +26 -0
  78. package/dist/chunk-PSR47JRA.js.map +1 -0
  79. package/dist/{chunk-SBJV3K4G.js → chunk-QJ6P2DW6.js} +28 -28
  80. package/dist/chunk-QJ6P2DW6.js.map +1 -0
  81. package/dist/{chunk-IP6TMT3D.js → chunk-R7QFP6EL.js} +3 -3
  82. package/dist/chunk-R7QFP6EL.js.map +1 -0
  83. package/dist/{chunk-WZWR2ELB.js → chunk-SEBU6JCK.js} +6 -6
  84. package/dist/chunk-SEBU6JCK.js.map +1 -0
  85. package/dist/{chunk-7OBL3H5P.js → chunk-SEFK4NZV.js} +3 -3
  86. package/dist/chunk-SEFK4NZV.js.map +1 -0
  87. package/dist/chunk-SRIT4NFQ.js +67 -0
  88. package/dist/chunk-SRIT4NFQ.js.map +1 -0
  89. package/dist/chunk-SY62XJFA.js +19 -0
  90. package/dist/chunk-SY62XJFA.js.map +1 -0
  91. package/dist/{chunk-SVXDWPLO.js → chunk-THD3YOOZ.js} +17 -17
  92. package/dist/chunk-THD3YOOZ.js.map +1 -0
  93. package/dist/chunk-VXFYETRK.js +58 -0
  94. package/dist/chunk-VXFYETRK.js.map +1 -0
  95. package/dist/chunk-XBX6QMR5.js +58 -0
  96. package/dist/chunk-XBX6QMR5.js.map +1 -0
  97. package/dist/{chunk-SCUT2ZCD.js → chunk-XCGZPFT7.js} +17 -17
  98. package/dist/chunk-XCGZPFT7.js.map +1 -0
  99. package/dist/chunk-XMK3T4TA.js +73 -0
  100. package/dist/chunk-XMK3T4TA.js.map +1 -0
  101. package/dist/chunk-YJ6MPS6U.js +226 -0
  102. package/dist/chunk-YJ6MPS6U.js.map +1 -0
  103. package/dist/chunk-YRYQXU3G.js +38 -0
  104. package/dist/chunk-YRYQXU3G.js.map +1 -0
  105. package/dist/{chunk-5J7BP5DI.js → chunk-YWFPMND7.js} +10 -10
  106. package/dist/chunk-YWFPMND7.js.map +1 -0
  107. package/dist/chunk-YYEGAFUE.js +94 -0
  108. package/dist/chunk-YYEGAFUE.js.map +1 -0
  109. package/dist/{chunk-TKA4LOMT.js → chunk-ZZ7H7VVX.js} +8 -8
  110. package/dist/chunk-ZZ7H7VVX.js.map +1 -0
  111. package/dist/components/data/index.d.ts +318 -228
  112. package/dist/components/data/index.js +591 -1394
  113. package/dist/components/data/index.js.map +1 -1
  114. package/dist/components/layout/dashboard-page.d.ts +4 -0
  115. package/dist/components/layout/dashboard-page.js +5 -0
  116. package/dist/components/layout/dashboard-page.js.map +1 -0
  117. package/dist/components/layout/detail-page.d.ts +91 -0
  118. package/dist/components/layout/detail-page.js +6 -0
  119. package/dist/components/layout/detail-page.js.map +1 -0
  120. package/dist/components/layout/form-page.d.ts +82 -0
  121. package/dist/components/layout/form-page.js +5 -0
  122. package/dist/components/layout/form-page.js.map +1 -0
  123. package/dist/components/layout/index.d.ts +14 -0
  124. package/dist/components/layout/index.js +21 -0
  125. package/dist/components/layout/index.js.map +1 -0
  126. package/dist/components/layout/settings-page.d.ts +114 -0
  127. package/dist/components/layout/settings-page.js +6 -0
  128. package/dist/components/layout/settings-page.js.map +1 -0
  129. package/dist/components/patterns/empty-state.d.ts +73 -0
  130. package/dist/components/{ui → patterns}/empty-state.js +2 -1
  131. package/dist/components/{ui → patterns}/form-field.d.ts +5 -6
  132. package/dist/components/{ui → patterns}/form-field.js +2 -2
  133. package/dist/components/patterns/index.d.ts +17 -0
  134. package/dist/components/patterns/index.js +18 -0
  135. package/dist/components/patterns/index.js.map +1 -0
  136. package/dist/components/patterns/page-loading.d.ts +25 -0
  137. package/dist/components/patterns/page-loading.js +5 -0
  138. package/dist/components/patterns/page-loading.js.map +1 -0
  139. package/dist/components/patterns/settings-row.d.ts +41 -0
  140. package/dist/components/patterns/settings-row.js +6 -0
  141. package/dist/components/patterns/settings-row.js.map +1 -0
  142. package/dist/components/patterns/status-badge.d.ts +37 -0
  143. package/dist/components/patterns/status-badge.js +4 -0
  144. package/dist/components/patterns/status-badge.js.map +1 -0
  145. package/dist/components/patterns/toggle-row.d.ts +35 -0
  146. package/dist/components/patterns/toggle-row.js +8 -0
  147. package/dist/components/patterns/toggle-row.js.map +1 -0
  148. package/dist/components/patterns/user-avatar.d.ts +34 -0
  149. package/dist/components/patterns/user-avatar.js +5 -0
  150. package/dist/components/patterns/user-avatar.js.map +1 -0
  151. package/dist/components/ui/accessible-icon.d.ts +47 -0
  152. package/dist/components/ui/accessible-icon.js +3 -0
  153. package/dist/components/ui/accessible-icon.js.map +1 -0
  154. package/dist/components/ui/accordion.d.ts +5 -5
  155. package/dist/components/ui/accordion.js +1 -1
  156. package/dist/components/ui/alert-dialog.d.ts +12 -13
  157. package/dist/components/ui/alert-dialog.js +1 -1
  158. package/dist/components/ui/aspect-ratio.d.ts +2 -2
  159. package/dist/components/ui/aspect-ratio.js +1 -1
  160. package/dist/components/ui/avatar.d.ts +4 -4
  161. package/dist/components/ui/avatar.js +1 -1
  162. package/dist/components/ui/badge.d.ts +1 -2
  163. package/dist/components/ui/breadcrumb.d.ts +2 -3
  164. package/dist/components/ui/button-group.d.ts +1 -2
  165. package/dist/components/ui/button.d.ts +73 -8
  166. package/dist/components/ui/button.js +1 -1
  167. package/dist/components/ui/calendar.d.ts +2 -3
  168. package/dist/components/ui/calendar.js +2 -2
  169. package/dist/components/ui/card.d.ts +6 -7
  170. package/dist/components/ui/carousel.d.ts +4 -4
  171. package/dist/components/ui/carousel.js +2 -2
  172. package/dist/components/ui/chart.d.ts +4 -5
  173. package/dist/components/ui/checkbox.d.ts +2 -2
  174. package/dist/components/ui/checkbox.js +1 -1
  175. package/dist/components/ui/collapsible.d.ts +4 -4
  176. package/dist/components/ui/collapsible.js +1 -1
  177. package/dist/components/ui/command-palette.d.ts +1 -2
  178. package/dist/components/ui/command-palette.js +3 -3
  179. package/dist/components/ui/command.d.ts +11 -12
  180. package/dist/components/ui/command.js +2 -2
  181. package/dist/components/ui/context-menu.d.ts +16 -17
  182. package/dist/components/ui/context-menu.js +1 -1
  183. package/dist/components/ui/date-picker.d.ts +2 -2
  184. package/dist/components/ui/date-picker.js +4 -4
  185. package/dist/components/ui/dialog.d.ts +11 -12
  186. package/dist/components/ui/dialog.js +1 -1
  187. package/dist/components/ui/drawer.d.ts +10 -11
  188. package/dist/components/ui/dropdown-menu.d.ts +16 -17
  189. package/dist/components/ui/dropdown-menu.js +1 -1
  190. package/dist/components/ui/hover-card.d.ts +4 -4
  191. package/dist/components/ui/hover-card.js +1 -1
  192. package/dist/components/ui/input-otp.d.ts +2 -2
  193. package/dist/components/ui/kbd.d.ts +1 -2
  194. package/dist/components/ui/label.d.ts +2 -2
  195. package/dist/components/ui/label.js +1 -1
  196. package/dist/components/ui/menubar.d.ts +17 -18
  197. package/dist/components/ui/menubar.js +1 -1
  198. package/dist/components/ui/navigation-menu.d.ts +9 -9
  199. package/dist/components/ui/navigation-menu.js +1 -1
  200. package/dist/components/ui/pagination.d.ts +5 -6
  201. package/dist/components/ui/pagination.js +2 -2
  202. package/dist/components/ui/password-toggle-field.d.ts +120 -0
  203. package/dist/components/ui/password-toggle-field.js +4 -0
  204. package/dist/components/ui/password-toggle-field.js.map +1 -0
  205. package/dist/components/ui/popover.d.ts +4 -4
  206. package/dist/components/ui/popover.js +1 -1
  207. package/dist/components/ui/progress.d.ts +1 -2
  208. package/dist/components/ui/radio-group.d.ts +3 -3
  209. package/dist/components/ui/radio-group.js +1 -1
  210. package/dist/components/ui/resizable.d.ts +3 -4
  211. package/dist/components/ui/scroll-area.d.ts +60 -3
  212. package/dist/components/ui/scroll-area.js +1 -1
  213. package/dist/components/ui/select.d.ts +9 -9
  214. package/dist/components/ui/select.js +1 -1
  215. package/dist/components/ui/separator.d.ts +1 -2
  216. package/dist/components/ui/sheet.d.ts +13 -12
  217. package/dist/components/ui/sheet.js +1 -1
  218. package/dist/components/ui/skeleton.d.ts +1 -2
  219. package/dist/components/ui/slider.d.ts +2 -2
  220. package/dist/components/ui/slider.js +1 -1
  221. package/dist/components/ui/sonner.d.ts +1 -2
  222. package/dist/components/ui/spinner.d.ts +1 -2
  223. package/dist/components/ui/switch.d.ts +2 -2
  224. package/dist/components/ui/switch.js +1 -1
  225. package/dist/components/ui/tabs.d.ts +29 -14
  226. package/dist/components/ui/tabs.js +1 -1
  227. package/dist/components/ui/toggle-group.d.ts +3 -3
  228. package/dist/components/ui/toggle-group.js +2 -2
  229. package/dist/components/ui/toggle.d.ts +2 -2
  230. package/dist/components/ui/toggle.js +1 -1
  231. package/dist/components/ui/toolbar.d.ts +104 -0
  232. package/dist/components/ui/toolbar.js +6 -0
  233. package/dist/components/ui/toolbar.js.map +1 -0
  234. package/dist/components/ui/tooltip.d.ts +46 -5
  235. package/dist/components/ui/tooltip.js +1 -1
  236. package/dist/components/ui/typography.d.ts +9 -10
  237. package/dist/components/ui/visually-hidden.d.ts +48 -0
  238. package/dist/components/ui/visually-hidden.js +4 -0
  239. package/dist/components/ui/visually-hidden.js.map +1 -0
  240. package/dist/dashboard-page-C0TihRl5.d.ts +107 -0
  241. package/dist/hooks/index.d.ts +2 -3
  242. package/dist/hooks/index.js +6 -6
  243. package/dist/hooks/use-keyboard-navigation.d.ts +58 -2
  244. package/dist/hooks/use-keyboard-navigation.js +1 -1
  245. package/dist/hooks/use-resource-page.d.ts +2 -3
  246. package/dist/hooks/use-resource-page.js +2 -2
  247. package/dist/hooks/use-view-preference.d.ts +2 -2
  248. package/dist/hooks/use-view-preference.js +1 -1
  249. package/dist/index.d.ts +22 -25
  250. package/dist/index.js +54 -38
  251. package/dist/spacing-BS6qECD4.d.ts +191 -0
  252. package/dist/split-Dk9f4Mi0.d.ts +57 -0
  253. package/dist/{use-resource-page-BXBv_Xah.d.ts → use-resource-page-DTQS011Q.d.ts} +18 -6
  254. package/dist/view-settings-kHKCHMjj.d.ts +12 -0
  255. package/dist/{ViewToggle-DDe_Luj2.d.ts → view-toggle-CahQ87PW.d.ts} +2 -2
  256. package/package.json +80 -39
  257. package/dist/chunk-2B3MFHBQ.js.map +0 -1
  258. package/dist/chunk-2MXMWBDV.js.map +0 -1
  259. package/dist/chunk-4V4H2ZM7.js.map +0 -1
  260. package/dist/chunk-5J7BP5DI.js.map +0 -1
  261. package/dist/chunk-5Q4FABZQ.js.map +0 -1
  262. package/dist/chunk-63FW5ZUA.js.map +0 -1
  263. package/dist/chunk-7OBL3H5P.js.map +0 -1
  264. package/dist/chunk-AUECDWP2.js.map +0 -1
  265. package/dist/chunk-C6H7AMO4.js.map +0 -1
  266. package/dist/chunk-EI5EGKHL.js +0 -32
  267. package/dist/chunk-EI5EGKHL.js.map +0 -1
  268. package/dist/chunk-IP6TMT3D.js.map +0 -1
  269. package/dist/chunk-JJ4NOMMI.js +0 -73
  270. package/dist/chunk-JJ4NOMMI.js.map +0 -1
  271. package/dist/chunk-JXI2MYL3.js.map +0 -1
  272. package/dist/chunk-KBS4BHME.js +0 -11
  273. package/dist/chunk-KBS4BHME.js.map +0 -1
  274. package/dist/chunk-LRXDZBV2.js +0 -26
  275. package/dist/chunk-LRXDZBV2.js.map +0 -1
  276. package/dist/chunk-NJMO3VPA.js.map +0 -1
  277. package/dist/chunk-OCTV3S4I.js +0 -29
  278. package/dist/chunk-OCTV3S4I.js.map +0 -1
  279. package/dist/chunk-QBZMY2AF.js.map +0 -1
  280. package/dist/chunk-QLCDAFS7.js.map +0 -1
  281. package/dist/chunk-SBJV3K4G.js.map +0 -1
  282. package/dist/chunk-SCUT2ZCD.js.map +0 -1
  283. package/dist/chunk-SVXDWPLO.js.map +0 -1
  284. package/dist/chunk-T465NJRL.js +0 -134
  285. package/dist/chunk-T465NJRL.js.map +0 -1
  286. package/dist/chunk-T5VX5DYO.js.map +0 -1
  287. package/dist/chunk-TKA4LOMT.js.map +0 -1
  288. package/dist/chunk-UGHQFQOE.js.map +0 -1
  289. package/dist/chunk-WALXVR6V.js +0 -9
  290. package/dist/chunk-WALXVR6V.js.map +0 -1
  291. package/dist/chunk-WZWR2ELB.js.map +0 -1
  292. package/dist/chunk-YZEA5TGK.js.map +0 -1
  293. package/dist/chunk-ZDAT5JDH.js.map +0 -1
  294. package/dist/chunk-ZJFSZFPI.js.map +0 -1
  295. package/dist/components/ui/empty-state.d.ts +0 -22
  296. /package/dist/components/{ui → patterns}/empty-state.js.map +0 -0
  297. /package/dist/components/{ui → patterns}/form-field.js.map +0 -0
@@ -1,473 +1,52 @@
1
1
  "use client";
2
- import { Spinner } from '../../chunk-OVU3Z2MT.js';
3
- import { Switch } from '../../chunk-2MXMWBDV.js';
2
+ import { AppShell, PageHeader } from '../../chunk-FEJAZFP3.js';
3
+ export { ViewSettings, ViewToggle } from '../../chunk-46TZGMFW.js';
4
4
  import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from '../../chunk-N2ZFNOOT.js';
5
5
  import { Textarea } from '../../chunk-YETMISCE.js';
6
- import { Progress } from '../../chunk-W5Y3LG5K.js';
7
- import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../../chunk-2B3MFHBQ.js';
8
6
  import { Separator } from '../../chunk-SXSCHWZU.js';
9
- import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription } from '../../chunk-SCUT2ZCD.js';
7
+ import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription } from '../../chunk-XCGZPFT7.js';
10
8
  import { Skeleton } from '../../chunk-QFM4Z2SS.js';
9
+ import { Progress } from '../../chunk-W5Y3LG5K.js';
10
+ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuCheckboxItem, DropdownMenuSeparator, DropdownMenuItem } from '../../chunk-QJ6P2DW6.js';
11
11
  import { Input } from '../../chunk-QSXVL5UB.js';
12
- import { Popover, PopoverTrigger, PopoverContent } from '../../chunk-NJMO3VPA.js';
13
- import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuCheckboxItem, DropdownMenuSeparator, DropdownMenuItem } from '../../chunk-SBJV3K4G.js';
14
- import { FormField, FormLabel, FormError, FormDescription } from '../../chunk-IP6TMT3D.js';
15
- import { Label } from '../../chunk-T5VX5DYO.js';
12
+ import { Checkbox } from '../../chunk-SEBU6JCK.js';
16
13
  import { Card, CardContent, CardHeader, CardTitle } from '../../chunk-FN3FZVUR.js';
17
- import { Checkbox } from '../../chunk-WZWR2ELB.js';
18
- import { Collapsible, CollapsibleContent } from '../../chunk-KBS4BHME.js';
14
+ import '../../chunk-B2GIBUQD.js';
19
15
  import { Badge } from '../../chunk-NPNADZHM.js';
20
- import { Button } from '../../chunk-ZJFSZFPI.js';
21
- import { titleCase, useResourcePage } from '../../chunk-QLCDAFS7.js';
16
+ import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../../chunk-FK42ZGGV.js';
17
+ import { Popover, PopoverTrigger, PopoverContent } from '../../chunk-22XB3Q6H.js';
18
+ import { Switch } from '../../chunk-27ELYTGE.js';
19
+ import { EmptyState } from '../../chunk-65SPT7EA.js';
20
+ export { EmptyState } from '../../chunk-65SPT7EA.js';
21
+ import { Button } from '../../chunk-NWSCYLPT.js';
22
+ import { PageLoading } from '../../chunk-YRYQXU3G.js';
23
+ import { Spinner } from '../../chunk-OVU3Z2MT.js';
24
+ import { FormField, FormLabel, FormError, FormDescription } from '../../chunk-R7QFP6EL.js';
25
+ import '../../chunk-F47U5KNW.js';
26
+ import { DetailPage } from '../../chunk-AFZ76Q26.js';
27
+ import '../../chunk-D2LIXA2Y.js';
28
+ import '../../chunk-AVFZ4P4B.js';
29
+ import { titleCase, useResourcePage } from '../../chunk-GDFNAHI4.js';
22
30
  export { useSavedViews } from '../../chunk-GCQFVMPI.js';
23
- import '../../chunk-XISSLIMH.js';
31
+ import '../../chunk-FTTNGGLA.js';
24
32
  import { useViewSettings } from '../../chunk-OZ7ZAYCS.js';
25
- export { CommandPalette, useCommandPaletteHotkey } from '../../chunk-A2OXCNNY.js';
26
- import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator } from '../../chunk-7OBL3H5P.js';
27
- import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from '../../chunk-YZEA5TGK.js';
33
+ export { CommandPalette, useCommandPaletteHotkey } from '../../chunk-CNTWQSUT.js';
34
+ import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator } from '../../chunk-SEFK4NZV.js';
35
+ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from '../../chunk-7XMZVISM.js';
28
36
  import { cn } from '../../chunk-NZ5WBA4R.js';
29
- import { DefaultLink, defaultNavigate, isPathActive } from '../../chunk-6NPEMLBA.js';
37
+ import { DefaultLink, defaultNavigate } from '../../chunk-6NPEMLBA.js';
30
38
  export { DefaultLink, defaultNavigate, isPathActive } from '../../chunk-6NPEMLBA.js';
39
+ import { useKeyboardNavigation } from '../../chunk-YJ6MPS6U.js';
31
40
  import { useIsMobile } from '../../chunk-M7AU5DDM.js';
32
- import { Search, LayoutGrid, Table2, List, Settings2, ListFilter, SlidersHorizontal, AlertTriangle, RefreshCw, ChevronDown, Check, X, ArrowUpDown, Bookmark, Trash2, BookmarkPlus, Loader2, Columns3, ArrowUp, ArrowDown, ChevronLeft, ChevronRight, ExternalLink, Pencil, ChevronsUpDown, ImageIcon, Upload, AlertCircle, FileIcon, Menu, PanelLeft, PanelLeftClose, Sun, Moon, Plus } from 'lucide-react';
41
+ import { AlertTriangle, RefreshCw, ChevronDown, Check, Search, X, ArrowUpDown, Bookmark, Trash2, BookmarkPlus, Loader2, Columns3, ArrowUp, ArrowDown, ChevronLeft, ChevronRight, ExternalLink, Pencil, ChevronsUpDown, ImageIcon, Upload, AlertCircle, FileIcon, Plus } from 'lucide-react';
33
42
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
34
- import { useId, useState, useEffect, useMemo, useCallback, useRef } from 'react';
43
+ import { useState, useRef, useEffect, useCallback, useMemo, Fragment as Fragment$1 } from 'react';
35
44
  import { formatDistanceToNow, format } from 'date-fns';
36
45
  import { useFieldArray, Controller, useWatch } from 'react-hook-form';
37
- import { useResource, useWizard, useResourceForm, buildArrayItemDefaults } from '@marktiderman/genesis-core/hooks';
46
+ import { useResource, useWizard, useResourceForm, useOne, buildArrayItemDefaults } from '@marktiderman/genesis-core/hooks';
38
47
  import { useDataProvider } from '@marktiderman/genesis-core/provider';
39
48
  import { useDropzone } from 'react-dropzone';
40
49
 
41
- function EmptyState({
42
- icon: Icon = Search,
43
- message = "No items found",
44
- action,
45
- hasFilters = false,
46
- onClearFilters
47
- }) {
48
- return /* @__PURE__ */ jsxs("div", { className: "text-center py-16 animate-fade-in", children: [
49
- /* @__PURE__ */ jsx(Icon, { className: "h-10 w-10 text-muted-foreground/30 mx-auto mb-3" }),
50
- /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: hasFilters ? "No results match your filters" : message }),
51
- hasFilters && onClearFilters ? /* @__PURE__ */ jsx(Button, { variant: "link", size: "sm", onClick: onClearFilters, className: "mt-2", children: "Clear all filters" }) : action && /* @__PURE__ */ jsx("div", { className: "mt-4", children: action })
52
- ] });
53
- }
54
- function ViewToggle({ viewMode, onViewModeChange }) {
55
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center border rounded-lg overflow-hidden", children: [
56
- /* @__PURE__ */ jsx(
57
- Button,
58
- {
59
- variant: viewMode === "grid" ? "default" : "ghost",
60
- size: "icon",
61
- className: "h-8 w-8 rounded-none",
62
- onClick: () => onViewModeChange("grid"),
63
- title: "Grid view",
64
- children: /* @__PURE__ */ jsx(LayoutGrid, { className: "h-4 w-4" })
65
- }
66
- ),
67
- /* @__PURE__ */ jsx(
68
- Button,
69
- {
70
- variant: viewMode === "table" ? "default" : "ghost",
71
- size: "icon",
72
- className: "h-8 w-8 rounded-none",
73
- onClick: () => onViewModeChange("table"),
74
- title: "Table view",
75
- children: /* @__PURE__ */ jsx(Table2, { className: "h-4 w-4" })
76
- }
77
- ),
78
- /* @__PURE__ */ jsx(
79
- Button,
80
- {
81
- variant: viewMode === "list" ? "default" : "ghost",
82
- size: "icon",
83
- className: "h-8 w-8 rounded-none",
84
- onClick: () => onViewModeChange("list"),
85
- title: "List view",
86
- children: /* @__PURE__ */ jsx(List, { className: "h-4 w-4" })
87
- }
88
- )
89
- ] });
90
- }
91
- var PAGE_SIZE_OPTIONS = [10, 25, 50, 100];
92
- var DENSITY_OPTIONS = [
93
- { value: "compact", label: "Compact" },
94
- { value: "comfortable", label: "Comfortable" },
95
- { value: "spacious", label: "Spacious" }
96
- ];
97
- function ViewSettings({
98
- pageSize,
99
- onPageSizeChange,
100
- density,
101
- onDensityChange
102
- }) {
103
- return /* @__PURE__ */ jsxs(Popover, { children: [
104
- /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
105
- Button,
106
- {
107
- variant: "ghost",
108
- size: "icon",
109
- className: "h-8 w-8",
110
- title: "View settings",
111
- children: /* @__PURE__ */ jsx(Settings2, { className: "h-4 w-4" })
112
- }
113
- ) }),
114
- /* @__PURE__ */ jsxs(PopoverContent, { align: "end", className: "w-56 space-y-4", children: [
115
- /* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold", children: "Display Settings" }),
116
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
117
- /* @__PURE__ */ jsx("label", { className: "text-xs text-muted-foreground", children: "Page size" }),
118
- /* @__PURE__ */ jsxs(
119
- Select,
120
- {
121
- value: String(pageSize),
122
- onValueChange: (v) => onPageSizeChange(Number(v)),
123
- children: [
124
- /* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
125
- /* @__PURE__ */ jsx(SelectContent, { className: "bg-popover z-50", children: PAGE_SIZE_OPTIONS.map((n) => /* @__PURE__ */ jsxs(SelectItem, { value: String(n), children: [
126
- n,
127
- " items"
128
- ] }, n)) })
129
- ]
130
- }
131
- )
132
- ] }),
133
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
134
- /* @__PURE__ */ jsx("label", { className: "text-xs text-muted-foreground", children: "Density" }),
135
- /* @__PURE__ */ jsxs(
136
- Select,
137
- {
138
- value: density,
139
- onValueChange: (v) => onDensityChange(v),
140
- children: [
141
- /* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
142
- /* @__PURE__ */ jsx(SelectContent, { className: "bg-popover z-50", children: DENSITY_OPTIONS.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value)) })
143
- ]
144
- }
145
- )
146
- ] })
147
- ] })
148
- ] });
149
- }
150
-
151
- // src/option-storage.ts
152
- function readStoredOptions(key) {
153
- if (typeof window === "undefined") return {};
154
- try {
155
- const raw = localStorage.getItem(key);
156
- if (!raw) return {};
157
- const parsed = JSON.parse(raw);
158
- if (!parsed || typeof parsed !== "object") return {};
159
- return parsed;
160
- } catch {
161
- return {};
162
- }
163
- }
164
- function writeStoredOptions(key, value) {
165
- if (typeof window === "undefined") return;
166
- try {
167
- localStorage.setItem(key, JSON.stringify(value));
168
- } catch {
169
- }
170
- }
171
- function pluralizeEntity(entity, count) {
172
- if (count === 1) return entity;
173
- return entity.endsWith("s") ? entity : `${entity}s`;
174
- }
175
- var OPTION_DEFAULTS = {
176
- showIcon: true,
177
- showSubtitle: true,
178
- showCount: true,
179
- showViewToggle: true,
180
- showViewSettings: true,
181
- showCreate: true,
182
- showDetailsToggle: true,
183
- showDetailsHint: true,
184
- subtitleInDetails: void 0,
185
- defaultDetailsOpen: false,
186
- detailsLabel: "Details",
187
- configurable: void 0
188
- };
189
- var BOOLEAN_OPTION_KEYS = [
190
- "showIcon",
191
- "showSubtitle",
192
- "showCount",
193
- "showViewToggle",
194
- "showViewSettings",
195
- "showCreate",
196
- "showDetailsToggle",
197
- "showDetailsHint",
198
- "subtitleInDetails",
199
- "defaultDetailsOpen",
200
- "configurable"
201
- ];
202
- var STRING_OPTION_KEYS = ["detailsLabel"];
203
- function readStoredOptions2(key) {
204
- const raw = readStoredOptions(key);
205
- const sanitized = {};
206
- for (const optKey of BOOLEAN_OPTION_KEYS) {
207
- const value = raw[optKey];
208
- if (typeof value === "boolean") {
209
- sanitized[optKey] = value;
210
- }
211
- }
212
- for (const optKey of STRING_OPTION_KEYS) {
213
- const value = raw[optKey];
214
- if (typeof value === "string") {
215
- sanitized[optKey] = value;
216
- }
217
- }
218
- return sanitized;
219
- }
220
- function PageHeader({
221
- title,
222
- subtitle,
223
- icon: Icon,
224
- options: optionsProp,
225
- optionsStorageKey,
226
- actions,
227
- details,
228
- detailsOpen,
229
- onDetailsOpenChange,
230
- detailsActive = false,
231
- detailsHint,
232
- count,
233
- totalCount,
234
- entityName,
235
- countActive = false,
236
- viewMode,
237
- onViewModeChange,
238
- pageSize,
239
- onPageSizeChange,
240
- density,
241
- onDensityChange,
242
- createAction
243
- }) {
244
- const detailsRegionId = useId();
245
- const hasDetails = details != null;
246
- const hasCount = count !== void 0 && totalCount !== void 0;
247
- const hasViewToggle = Boolean(viewMode && onViewModeChange);
248
- const hasViewSettings = Boolean(onPageSizeChange && onDensityChange);
249
- const hasCreate = createAction != null;
250
- const [userOverrides, setUserOverrides] = useState(
251
- {}
252
- );
253
- const [hydratedKey, setHydratedKey] = useState(
254
- optionsStorageKey ? void 0 : ""
255
- );
256
- useEffect(() => {
257
- if (!optionsStorageKey) {
258
- setUserOverrides({});
259
- setHydratedKey("");
260
- return;
261
- }
262
- setUserOverrides(readStoredOptions2(optionsStorageKey));
263
- setHydratedKey(optionsStorageKey);
264
- }, [optionsStorageKey]);
265
- useEffect(() => {
266
- if (!optionsStorageKey || hydratedKey !== optionsStorageKey) return;
267
- writeStoredOptions(optionsStorageKey, userOverrides);
268
- }, [optionsStorageKey, userOverrides, hydratedKey]);
269
- const resolved = useMemo(() => {
270
- const merged = {
271
- ...OPTION_DEFAULTS,
272
- ...optionsProp,
273
- ...userOverrides
274
- };
275
- if (merged.subtitleInDetails === void 0) {
276
- merged.subtitleInDetails = hasDetails;
277
- }
278
- if (merged.configurable === void 0) {
279
- merged.configurable = hasDetails || hasCount || hasViewSettings || hasViewToggle;
280
- }
281
- return merged;
282
- }, [
283
- optionsProp,
284
- userOverrides,
285
- hasDetails,
286
- hasCount,
287
- hasViewSettings,
288
- hasViewToggle
289
- ]);
290
- const patchOption = useCallback((key, value) => {
291
- setUserOverrides((prev) => ({ ...prev, [key]: value }));
292
- }, []);
293
- const moveSubtitle = Boolean(resolved.subtitleInDetails) && hasDetails;
294
- const showTitleSubtitle = Boolean(subtitle) && Boolean(resolved.showSubtitle) && !moveSubtitle;
295
- const [uncontrolledOpen, setUncontrolledOpen] = useState(
296
- () => optionsProp?.defaultDetailsOpen ?? OPTION_DEFAULTS.defaultDetailsOpen
297
- );
298
- const isControlled = detailsOpen !== void 0;
299
- const open = isControlled ? detailsOpen : uncontrolledOpen;
300
- const setOpen = (next) => {
301
- if (!isControlled) setUncontrolledOpen(next);
302
- onDetailsOpenChange?.(next);
303
- };
304
- const detailsLabel = resolved.detailsLabel ?? "Details";
305
- const entity = entityName || "item";
306
- const toggles = [
307
- { key: "showIcon", label: "Icon", available: Boolean(Icon) },
308
- {
309
- key: "showSubtitle",
310
- label: "Subtitle",
311
- available: Boolean(subtitle) && !moveSubtitle
312
- },
313
- { key: "showCount", label: "Count", available: hasCount },
314
- { key: "showViewToggle", label: "View toggle", available: hasViewToggle },
315
- {
316
- key: "showViewSettings",
317
- label: "Display settings",
318
- available: hasViewSettings
319
- },
320
- { key: "showCreate", label: "Create action", available: hasCreate },
321
- {
322
- key: "showDetailsToggle",
323
- label: "Filters / details",
324
- available: hasDetails
325
- },
326
- {
327
- key: "showDetailsHint",
328
- label: "Collapsed hint",
329
- available: hasDetails && detailsHint != null
330
- }
331
- ].filter((t) => t.available);
332
- const showDetailsToggle = hasDetails && Boolean(resolved.showDetailsToggle);
333
- const showHint = hasDetails && !open && Boolean(resolved.showDetailsHint) && detailsHint != null;
334
- const actionsCluster = /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 shrink-0", children: [
335
- showHint ? /* @__PURE__ */ jsx("span", { className: "hidden max-w-[14rem] truncate text-xs text-muted-foreground sm:inline", children: detailsHint }) : null,
336
- hasCount && resolved.showCount !== false ? /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: countActive ? `${count} of ${totalCount}` : `${totalCount} ${pluralizeEntity(entity, totalCount)}` }) : null,
337
- hasViewToggle && resolved.showViewToggle !== false ? /* @__PURE__ */ jsx(ViewToggle, { viewMode, onViewModeChange }) : null,
338
- showDetailsToggle ? /* @__PURE__ */ jsxs(
339
- Button,
340
- {
341
- type: "button",
342
- variant: "ghost",
343
- size: "icon",
344
- className: cn(
345
- "relative h-8 w-8",
346
- (open || detailsActive) && "bg-accent text-accent-foreground"
347
- ),
348
- onClick: () => setOpen(!open),
349
- "data-testid": "page-header-details-toggle",
350
- title: open ? `Hide ${detailsLabel}` : `Show ${detailsLabel}`,
351
- "aria-label": open ? `Hide ${detailsLabel}` : `Show ${detailsLabel}`,
352
- "aria-expanded": open,
353
- "aria-controls": detailsRegionId,
354
- testID: "page-header-details-toggle",
355
- children: [
356
- /* @__PURE__ */ jsx(ListFilter, { className: "h-4 w-4" }),
357
- detailsActive ? /* @__PURE__ */ jsx(
358
- "span",
359
- {
360
- className: "absolute right-1.5 top-1.5 size-1.5 rounded-full bg-primary",
361
- "aria-hidden": true
362
- }
363
- ) : null
364
- ]
365
- }
366
- ) : null,
367
- hasViewSettings && resolved.showViewSettings !== false ? /* @__PURE__ */ jsx(
368
- ViewSettings,
369
- {
370
- pageSize: pageSize ?? 25,
371
- onPageSizeChange,
372
- density: density ?? "comfortable",
373
- onDensityChange
374
- }
375
- ) : null,
376
- hasCreate && resolved.showCreate !== false ? createAction : null,
377
- actions,
378
- resolved.configurable && toggles.length > 0 ? /* @__PURE__ */ jsxs(Popover, { children: [
379
- /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
380
- Button,
381
- {
382
- type: "button",
383
- variant: "ghost",
384
- size: "icon",
385
- className: "h-8 w-8",
386
- title: "Header options",
387
- "aria-label": "Header options",
388
- "data-testid": "page-header-options",
389
- testID: "page-header-options",
390
- children: /* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-4 w-4" })
391
- }
392
- ) }),
393
- /* @__PURE__ */ jsxs(PopoverContent, { align: "end", className: "w-64 space-y-3", children: [
394
- /* @__PURE__ */ jsxs("div", { children: [
395
- /* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold", children: "Header options" }),
396
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Customize this page header. Saved on this device when a storage key is set." })
397
- ] }),
398
- /* @__PURE__ */ jsx("ul", { className: "space-y-3", children: toggles.map((t) => {
399
- const checked = Boolean(resolved[t.key] ?? true);
400
- return /* @__PURE__ */ jsxs(
401
- "li",
402
- {
403
- className: "flex items-center justify-between gap-3",
404
- children: [
405
- /* @__PURE__ */ jsx(
406
- Label,
407
- {
408
- htmlFor: `page-header-opt-${t.key}`,
409
- className: "text-sm font-normal",
410
- children: t.label
411
- }
412
- ),
413
- /* @__PURE__ */ jsx(
414
- Switch,
415
- {
416
- id: `page-header-opt-${t.key}`,
417
- checked,
418
- onCheckedChange: (v) => patchOption(t.key, v)
419
- }
420
- )
421
- ]
422
- },
423
- t.key
424
- );
425
- }) }),
426
- optionsStorageKey && Object.keys(userOverrides).length > 0 ? /* @__PURE__ */ jsx(
427
- Button,
428
- {
429
- type: "button",
430
- variant: "ghost",
431
- size: "sm",
432
- className: "h-8 w-full text-xs",
433
- onClick: () => setUserOverrides({}),
434
- "data-testid": "page-header-options-reset",
435
- "aria-label": "Reset header options",
436
- testID: "page-header-options-reset",
437
- children: "Reset to defaults"
438
- }
439
- ) : null
440
- ] })
441
- ] }) : null
442
- ] });
443
- const titleRow = /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [
444
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
445
- Icon && resolved.showIcon !== false ? /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center overflow-hidden rounded-xl bg-primary/10", children: /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5 text-primary" }) }) : null,
446
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
447
- /* @__PURE__ */ jsx(
448
- "h1",
449
- {
450
- "data-testid": "page-title",
451
- className: "truncate font-display text-xl font-bold tracking-tight sm:text-2xl",
452
- children: title
453
- }
454
- ),
455
- showTitleSubtitle ? /* @__PURE__ */ jsx("p", { className: "truncate text-sm text-muted-foreground", children: subtitle }) : null
456
- ] })
457
- ] }),
458
- actionsCluster
459
- ] });
460
- if (!hasDetails) {
461
- return titleRow;
462
- }
463
- return /* @__PURE__ */ jsxs(Collapsible, { open, onOpenChange: setOpen, children: [
464
- titleRow,
465
- /* @__PURE__ */ jsx(CollapsibleContent, { id: detailsRegionId, className: "mt-3", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 rounded-lg border border-border bg-card/40 p-3 sm:p-4", children: [
466
- moveSubtitle && subtitle && resolved.showSubtitle !== false ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: subtitle }) : null,
467
- details
468
- ] }) })
469
- ] });
470
- }
471
50
  function DataPageShell({
472
51
  title,
473
52
  icon,
@@ -570,7 +149,7 @@ function DataPageShell({
570
149
  EmptyState,
571
150
  {
572
151
  icon: emptyIcon,
573
- message: emptyMessage,
152
+ title: emptyMessage,
574
153
  action: emptyAction,
575
154
  hasFilters: hasActiveFilters,
576
155
  onClearFilters
@@ -947,14 +526,25 @@ function DataGrid({
947
526
  }
948
527
  if (viewMode === "list" && renderListItem) {
949
528
  const keyFnList = getKey || ((item) => item.id);
950
- return /* @__PURE__ */ jsx("div", { className: "divide-y divide-border rounded-lg border", children: items.map((item, i) => /* @__PURE__ */ jsx(
529
+ return /* @__PURE__ */ jsx(
951
530
  "div",
952
531
  {
953
- className: "px-4 py-3 transition-colors hover:bg-accent/50",
954
- children: renderListItem(item, i)
955
- },
956
- keyFnList(item)
957
- )) });
532
+ ref: keyboardContainerRef,
533
+ className: "divide-y divide-border rounded-lg border",
534
+ children: items.map((item, i) => /* @__PURE__ */ jsx(
535
+ "div",
536
+ {
537
+ "data-nav-item": true,
538
+ className: cn(
539
+ "px-4 py-3 transition-colors hover:bg-accent/50",
540
+ focusedIndex === i && "ring-2 ring-inset ring-primary bg-accent/50"
541
+ ),
542
+ children: renderListItem(item, i)
543
+ },
544
+ keyFnList(item)
545
+ ))
546
+ }
547
+ );
958
548
  }
959
549
  const visible = items.slice(0, visibleCount);
960
550
  const keyFn = getKey || ((item) => item.id);
@@ -1731,6 +1321,11 @@ var widthClasses = {
1731
1321
  md: "w-96",
1732
1322
  lg: "w-[480px]"
1733
1323
  };
1324
+ var dialogMaxWidthClasses = {
1325
+ sm: "sm:max-w-sm",
1326
+ md: "sm:max-w-lg",
1327
+ lg: "sm:max-w-2xl"
1328
+ };
1734
1329
  function keyToLabel(key) {
1735
1330
  return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase());
1736
1331
  }
@@ -1741,6 +1336,37 @@ function formatValue(value) {
1741
1336
  if (typeof value === "object") return JSON.stringify(value);
1742
1337
  return String(value);
1743
1338
  }
1339
+ function renderTitleBlock(layout, title, subtitle) {
1340
+ const titleText = title || "Detail panel";
1341
+ if (layout === "dialog") {
1342
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1343
+ /* @__PURE__ */ jsx(DialogTitle, { className: cn("text-sm font-semibold truncate", !title && "sr-only"), children: titleText }),
1344
+ /* @__PURE__ */ jsx(
1345
+ DialogDescription,
1346
+ {
1347
+ className: cn("text-xs text-muted-foreground truncate", !subtitle && "sr-only"),
1348
+ children: subtitle || titleText
1349
+ }
1350
+ )
1351
+ ] });
1352
+ }
1353
+ if (layout === "sheet") {
1354
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1355
+ /* @__PURE__ */ jsx(SheetTitle, { className: cn("text-sm font-semibold truncate", !title && "sr-only"), children: titleText }),
1356
+ /* @__PURE__ */ jsx(
1357
+ SheetDescription,
1358
+ {
1359
+ className: cn("text-xs text-muted-foreground truncate", !subtitle && "sr-only"),
1360
+ children: subtitle || titleText
1361
+ }
1362
+ )
1363
+ ] });
1364
+ }
1365
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1366
+ title && /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold truncate", children: title }),
1367
+ subtitle && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground truncate", children: subtitle })
1368
+ ] });
1369
+ }
1744
1370
  function DetailPanel({
1745
1371
  item,
1746
1372
  open,
@@ -1751,7 +1377,8 @@ function DetailPanel({
1751
1377
  onDelete,
1752
1378
  children,
1753
1379
  fields,
1754
- width = "md"
1380
+ width = "md",
1381
+ layout = "sheet"
1755
1382
  }) {
1756
1383
  if (!open || !item) return null;
1757
1384
  const renderContent = () => {
@@ -1776,78 +1403,114 @@ function DetailPanel({
1776
1403
  ] })
1777
1404
  ] }, key)) });
1778
1405
  };
1779
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1780
- /* @__PURE__ */ jsx(
1781
- "div",
1406
+ const header = /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-border px-4 py-3", children: [
1407
+ /* @__PURE__ */ jsx("div", { className: "min-w-0", children: renderTitleBlock(layout, title, subtitle) }),
1408
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [
1409
+ onEdit && /* @__PURE__ */ jsx(
1410
+ Button,
1411
+ {
1412
+ testID: "detail-panel-edit",
1413
+ variant: "ghost",
1414
+ size: "icon",
1415
+ className: "h-8 w-8",
1416
+ onClick: () => onEdit(item),
1417
+ "aria-label": "Edit",
1418
+ children: /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4" })
1419
+ }
1420
+ ),
1421
+ onDelete && /* @__PURE__ */ jsx(
1422
+ Button,
1423
+ {
1424
+ testID: "detail-panel-delete",
1425
+ variant: "ghost",
1426
+ size: "icon",
1427
+ className: "h-8 w-8 text-destructive hover:text-destructive",
1428
+ onClick: () => onDelete(item),
1429
+ "aria-label": "Delete",
1430
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
1431
+ }
1432
+ ),
1433
+ /* @__PURE__ */ jsx(
1434
+ Button,
1435
+ {
1436
+ testID: "detail-panel-close",
1437
+ variant: "ghost",
1438
+ size: "icon",
1439
+ className: "h-8 w-8",
1440
+ onClick: onClose,
1441
+ "aria-label": "Close",
1442
+ children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
1443
+ }
1444
+ )
1445
+ ] })
1446
+ ] });
1447
+ const content = /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto px-4 py-3", children: renderContent() });
1448
+ if (layout === "dialog") {
1449
+ return /* @__PURE__ */ jsx(
1450
+ Dialog,
1782
1451
  {
1783
- className: "fixed inset-0 z-40 bg-black/50 sm:hidden",
1784
- onClick: onClose,
1785
- "aria-hidden": "true"
1452
+ open,
1453
+ onOpenChange: (next) => {
1454
+ if (!next) onClose();
1455
+ },
1456
+ children: /* @__PURE__ */ jsxs(
1457
+ DialogContent,
1458
+ {
1459
+ showCloseButton: false,
1460
+ className: cn(
1461
+ "flex max-h-[85vh] flex-col gap-0 overflow-hidden p-0",
1462
+ dialogMaxWidthClasses[width]
1463
+ ),
1464
+ children: [
1465
+ header,
1466
+ content
1467
+ ]
1468
+ }
1469
+ )
1786
1470
  }
1787
- ),
1788
- /* @__PURE__ */ jsxs(
1789
- "div",
1471
+ );
1472
+ }
1473
+ if (layout === "sheet") {
1474
+ return /* @__PURE__ */ jsx(
1475
+ Sheet,
1790
1476
  {
1791
- role: "dialog",
1792
- "aria-label": title ?? "Detail panel",
1793
- onKeyDown: (e) => {
1794
- if (e.key === "Escape") onClose();
1477
+ open,
1478
+ onOpenChange: (next) => {
1479
+ if (!next) onClose();
1795
1480
  },
1796
- tabIndex: -1,
1797
- className: cn(
1798
- "fixed inset-y-0 right-0 z-50 flex flex-col border-l border-border bg-card shadow-xl",
1799
- "max-w-[90vw]",
1800
- widthClasses[width],
1801
- "transform transition-transform duration-200 ease-in-out",
1802
- open ? "translate-x-0" : "translate-x-full"
1803
- ),
1804
- children: [
1805
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-border px-4 py-3", children: [
1806
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
1807
- title && /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold truncate", children: title }),
1808
- subtitle && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground truncate", children: subtitle })
1809
- ] }),
1810
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [
1811
- onEdit && /* @__PURE__ */ jsx(
1812
- Button,
1813
- {
1814
- variant: "ghost",
1815
- size: "icon",
1816
- className: "h-8 w-8",
1817
- onClick: () => onEdit(item),
1818
- "aria-label": "Edit",
1819
- children: /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4" })
1820
- }
1821
- ),
1822
- onDelete && /* @__PURE__ */ jsx(
1823
- Button,
1824
- {
1825
- variant: "ghost",
1826
- size: "icon",
1827
- className: "h-8 w-8 text-destructive hover:text-destructive",
1828
- onClick: () => onDelete(item),
1829
- "aria-label": "Delete",
1830
- children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
1831
- }
1832
- ),
1833
- /* @__PURE__ */ jsx(
1834
- Button,
1835
- {
1836
- variant: "ghost",
1837
- size: "icon",
1838
- className: "h-8 w-8",
1839
- onClick: onClose,
1840
- "aria-label": "Close",
1841
- children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
1842
- }
1843
- )
1844
- ] })
1845
- ] }),
1846
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto px-4 py-3", children: renderContent() })
1847
- ]
1481
+ children: /* @__PURE__ */ jsxs(
1482
+ SheetContent,
1483
+ {
1484
+ side: "right",
1485
+ showCloseButton: false,
1486
+ className: cn(
1487
+ "flex flex-col gap-0 overflow-hidden p-0 max-w-[90vw] sm:max-w-[90vw]",
1488
+ widthClasses[width]
1489
+ ),
1490
+ children: [
1491
+ header,
1492
+ content
1493
+ ]
1494
+ }
1495
+ )
1848
1496
  }
1849
- )
1850
- ] });
1497
+ );
1498
+ }
1499
+ return /* @__PURE__ */ jsxs(
1500
+ "div",
1501
+ {
1502
+ role: "region",
1503
+ "aria-label": title || "Detail panel",
1504
+ className: cn(
1505
+ "flex flex-col rounded-lg border border-border bg-card",
1506
+ widthClasses[width]
1507
+ ),
1508
+ children: [
1509
+ header,
1510
+ content
1511
+ ]
1512
+ }
1513
+ );
1851
1514
  }
1852
1515
  function RelationField({
1853
1516
  field,
@@ -2761,6 +2424,24 @@ function ResourcePage(props) {
2761
2424
  searchPlaceholder,
2762
2425
  initialFilters,
2763
2426
  onFiltersChange,
2427
+ page: pageProp,
2428
+ perPage: perPageProp,
2429
+ onPageChange,
2430
+ onPerPageChange,
2431
+ onSortChange,
2432
+ onSearchChange,
2433
+ savedViews,
2434
+ onLoadView,
2435
+ onSaveView,
2436
+ onDeleteView,
2437
+ canSaveViews,
2438
+ columnVisibility,
2439
+ columnVisibilityKey,
2440
+ resizableColumns,
2441
+ resizeKey,
2442
+ stickyHeader,
2443
+ stickyFirstColumn,
2444
+ pageSizeOptions,
2764
2445
  onCreate,
2765
2446
  onEdit,
2766
2447
  onDelete,
@@ -2773,6 +2454,9 @@ function ResourcePage(props) {
2773
2454
  formSteps,
2774
2455
  formLayout: formLayoutProp,
2775
2456
  bulkActions,
2457
+ renderCard: renderCardProp,
2458
+ gridCols: gridColsProp,
2459
+ keyboardNavigation = true,
2776
2460
  toolbar,
2777
2461
  emptyState,
2778
2462
  resourceOptions,
@@ -2781,8 +2465,10 @@ function ResourcePage(props) {
2781
2465
  viewSettingsKey: viewSettingsKeyProp
2782
2466
  } = props;
2783
2467
  const isClientMode = !!resourceName;
2468
+ const isServerControlled = pageProp !== void 0 || perPageProp !== void 0 || onPageChange !== void 0 || onPerPageChange !== void 0 || onSortChange !== void 0 || onSearchChange !== void 0;
2784
2469
  const viewSettingsKey = viewSettingsKeyProp ?? resourceName ?? title.toLowerCase().replace(/\s+/g, "-");
2785
2470
  const { settings, updateSetting } = useViewSettings(viewSettingsKey);
2471
+ const effectivePageSize = perPageProp ?? settings.pageSize;
2786
2472
  const resourceHook = useResource(
2787
2473
  resourceName ?? "__noop__",
2788
2474
  isClientMode ? resourceOptions : { enabled: false }
@@ -2803,7 +2489,8 @@ function ResourcePage(props) {
2803
2489
  defaultView,
2804
2490
  detail,
2805
2491
  titleField: titleFieldProp,
2806
- subtitleField: subtitleFieldProp
2492
+ subtitleField: subtitleFieldProp,
2493
+ controlled: isServerControlled
2807
2494
  });
2808
2495
  const actions = useMemo(() => {
2809
2496
  if (isClientMode) {
@@ -2815,15 +2502,24 @@ function ResourcePage(props) {
2815
2502
  }
2816
2503
  return {
2817
2504
  update: async () => {
2818
- throw new Error("ResourceActions.update not available in server-data mode");
2505
+ throw new Error(
2506
+ "ResourceActions.update not available in server-data mode"
2507
+ );
2819
2508
  },
2820
2509
  remove: async () => {
2821
- throw new Error("ResourceActions.remove not available in server-data mode");
2510
+ throw new Error(
2511
+ "ResourceActions.remove not available in server-data mode"
2512
+ );
2822
2513
  },
2823
2514
  refetch: () => {
2824
2515
  }
2825
2516
  };
2826
- }, [isClientMode, resourceHook.update, resourceHook.remove, resourceHook.list]);
2517
+ }, [
2518
+ isClientMode,
2519
+ resourceHook.update,
2520
+ resourceHook.remove,
2521
+ resourceHook.list
2522
+ ]);
2827
2523
  const [selectedIds, setSelectedIds] = useState(/* @__PURE__ */ new Set());
2828
2524
  const handleToggleSelect = useCallback((id) => {
2829
2525
  setSelectedIds((prev) => {
@@ -2873,15 +2569,101 @@ function ResourcePage(props) {
2873
2569
  [useBuiltInEdit, onEdit]
2874
2570
  );
2875
2571
  const tableColumns = useMemo(() => {
2876
- if (!isClientMode) return page.tableColumns;
2877
- return page.columns.map((col) => ({
2572
+ const base = isClientMode ? page.columns.map((col) => ({
2878
2573
  key: col.key,
2879
2574
  header: col.header ?? col.key,
2880
2575
  sortable: col.sortable ?? true,
2881
2576
  hideBelow: col.hideBelow,
2882
2577
  render: col.render ? (item, value) => col.render(item, value, actions) : void 0
2883
- }));
2884
- }, [isClientMode, page.columns, page.tableColumns, actions]);
2578
+ })) : page.tableColumns;
2579
+ if (!isServerControlled) return base;
2580
+ return base.map((col) => ({ ...col, sortable: false }));
2581
+ }, [
2582
+ isClientMode,
2583
+ isServerControlled,
2584
+ page.columns,
2585
+ page.tableColumns,
2586
+ actions
2587
+ ]);
2588
+ const currentPage = pageProp ?? 1;
2589
+ const handleSearchChange = useCallback(
2590
+ (value) => {
2591
+ page.filterConfig.onSearchChange(value);
2592
+ if (!isServerControlled) return;
2593
+ onSearchChange?.(value);
2594
+ if (currentPage !== 1) onPageChange?.(1);
2595
+ },
2596
+ [
2597
+ page.filterConfig,
2598
+ isServerControlled,
2599
+ onSearchChange,
2600
+ onPageChange,
2601
+ currentPage
2602
+ ]
2603
+ );
2604
+ const handleSortSelect = useCallback(
2605
+ (value) => {
2606
+ page.filterConfig.onSortChange(value);
2607
+ if (!isServerControlled) return;
2608
+ const [field, order] = value.split(":");
2609
+ onSortChange?.(
2610
+ field && (order === "asc" || order === "desc") ? { field, order } : null
2611
+ );
2612
+ onPageChange?.(1);
2613
+ },
2614
+ [page.filterConfig, isServerControlled, onSortChange, onPageChange]
2615
+ );
2616
+ const handleClearFilters = useCallback(() => {
2617
+ page.clearFilters();
2618
+ if (!isServerControlled) return;
2619
+ onSearchChange?.("");
2620
+ onSortChange?.(null);
2621
+ onPageChange?.(1);
2622
+ }, [page, isServerControlled, onSearchChange, onSortChange, onPageChange]);
2623
+ const handleStatusChange = useCallback(
2624
+ (values) => {
2625
+ page.filterConfig.statusChips?.onChange(values);
2626
+ if (!isServerControlled) return;
2627
+ if (currentPage !== 1) onPageChange?.(1);
2628
+ },
2629
+ [page.filterConfig, isServerControlled, onPageChange, currentPage]
2630
+ );
2631
+ const statusChipsConfig = useMemo(() => {
2632
+ const base = page.filterConfig.statusChips;
2633
+ if (!base) return void 0;
2634
+ return { ...base, onChange: handleStatusChange };
2635
+ }, [page.filterConfig.statusChips, handleStatusChange]);
2636
+ const handlePageSizeChange = useCallback(
2637
+ (size) => {
2638
+ updateSetting("pageSize", size);
2639
+ onPerPageChange?.(size);
2640
+ if (isServerControlled) onPageChange?.(1);
2641
+ },
2642
+ [updateSetting, onPerPageChange, isServerControlled, onPageChange]
2643
+ );
2644
+ const missingPerPage = isServerControlled && !!onPageChange && perPageProp === void 0;
2645
+ const missingTotal = isServerControlled && !!onPageChange && totalProp === void 0;
2646
+ const pagerMisconfigured = missingPerPage || missingTotal;
2647
+ const missingPagerProps = [
2648
+ ...missingPerPage ? ["perPage"] : [],
2649
+ ...missingTotal ? ["total"] : []
2650
+ ];
2651
+ const loggedMissingPerPageRef = useRef(false);
2652
+ useEffect(() => {
2653
+ if (!pagerMisconfigured) return;
2654
+ if (loggedMissingPerPageRef.current) return;
2655
+ loggedMissingPerPageRef.current = true;
2656
+ console.error(
2657
+ `ResourcePage: ${missingPagerProps.map((name) => `\`${name}\``).join(" and ")} ${missingPagerProps.length > 1 ? "are" : "is"} required when \`onPageChange\` is supplied.`
2658
+ );
2659
+ }, [pagerMisconfigured]);
2660
+ const pagerPageSize = perPageProp ?? effectivePageSize;
2661
+ const totalPages = Math.max(
2662
+ 1,
2663
+ Math.ceil(displayTotal / Math.max(1, pagerPageSize))
2664
+ );
2665
+ const rangeFrom = page.data.length === 0 ? 0 : (currentPage - 1) * pagerPageSize + 1;
2666
+ const rangeTo = rangeFrom === 0 ? 0 : rangeFrom + page.data.length - 1;
2885
2667
  const detailTitle = page.selectedItem ? String(
2886
2668
  page.selectedItem[page.titleField] ?? ""
2887
2669
  ) : "";
@@ -2898,6 +2680,35 @@ function ResourcePage(props) {
2898
2680
  }
2899
2681
  page.handleRowClick(item);
2900
2682
  };
2683
+ const hasBulkSelect = !!bulkActions && bulkActions.length > 0;
2684
+ const overlayOpen = page.detailOpen || formOpen;
2685
+ const keyboardNav = useKeyboardNavigation({
2686
+ itemCount: page.data.length,
2687
+ enabled: keyboardNavigation && page.viewMode !== "table",
2688
+ overlayOpen,
2689
+ onOpen: (index) => {
2690
+ const item = page.data[index];
2691
+ if (item) handleRowClick(item);
2692
+ },
2693
+ onToggleSelect: hasBulkSelect ? (index) => {
2694
+ const item = page.data[index];
2695
+ if (item) {
2696
+ handleToggleSelect(
2697
+ String(item.id ?? "")
2698
+ );
2699
+ }
2700
+ } : void 0,
2701
+ // Supplied only while there is an open detail to close — the hook
2702
+ // suppresses Backspace's default whenever this is present, so leaving it
2703
+ // wired up permanently would swallow the browser/app's Backspace on
2704
+ // every grid/list page (including detail="none" and detail="route",
2705
+ // which never open one). `detailOpen` is only ever true for the panel
2706
+ // and modal modes, so it is the precise precondition here.
2707
+ onBack: page.detailOpen ? () => {
2708
+ page.setDetailOpen(false);
2709
+ page.setSelectedItem(null);
2710
+ } : void 0
2711
+ });
2901
2712
  const showCreateButton = onCreate || useBuiltInCreate;
2902
2713
  const createButton = showCreateButton ? /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: handleCreate, children: [
2903
2714
  /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4 mr-1" }),
@@ -2908,15 +2719,83 @@ function ResourcePage(props) {
2908
2719
  createButton
2909
2720
  ] }) : void 0;
2910
2721
  const isEmpty = !isLoading && !hasError && page.data.length === 0;
2911
- const modalDetailContent = page.selectedItem ? renderDetailProp ? renderDetailProp(page.selectedItem) : detailFields ? /* @__PURE__ */ jsx("dl", { className: "space-y-3", children: detailFields.map((field) => /* @__PURE__ */ jsxs("div", { children: [
2912
- /* @__PURE__ */ jsx("dt", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider", children: field.label }),
2913
- /* @__PURE__ */ jsx("dd", { className: "mt-1 text-sm text-foreground", children: field.render ? field.render(
2914
- page.selectedItem[field.key],
2915
- page.selectedItem
2916
- ) : String(
2917
- page.selectedItem[field.key] ?? "\u2014"
2918
- ) })
2919
- ] }, field.key)) }) : null : null;
2722
+ const cardActionsFor = (item) => ({
2723
+ ...actions,
2724
+ open: () => handleRowClick(item)
2725
+ });
2726
+ const rowActivationProps = (item, label, testId) => ({
2727
+ role: "button",
2728
+ tabIndex: 0,
2729
+ "aria-label": label || void 0,
2730
+ "data-testid": testId,
2731
+ onClick: () => handleRowClick(item),
2732
+ onKeyDown: (e) => {
2733
+ if (e.key === "Enter" || e.key === " ") {
2734
+ e.preventDefault();
2735
+ handleRowClick(item);
2736
+ }
2737
+ }
2738
+ });
2739
+ const renderDefaultCard = (item) => {
2740
+ const cols = page.columns;
2741
+ const titleCol = cols[0];
2742
+ const titleVal = titleCol ? String(item[titleCol.key] ?? "") : "";
2743
+ const badgeCol = cols[1];
2744
+ const badgeVal = badgeCol ? item[badgeCol.key] : null;
2745
+ const priceCol = cols.find(
2746
+ (c, i) => i > 1 && /price|cost|amount|total/i.test(c.key)
2747
+ );
2748
+ const priceVal = priceCol ? item[priceCol.key] : null;
2749
+ const metaCols = cols.filter(
2750
+ (c) => c !== titleCol && c !== badgeCol && c !== priceCol
2751
+ );
2752
+ return /* @__PURE__ */ jsxs(
2753
+ Card,
2754
+ {
2755
+ ...rowActivationProps(item, titleVal, "resource-card"),
2756
+ className: "h-full cursor-pointer hover:shadow-md motion-safe:transition-shadow focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background",
2757
+ children: [
2758
+ /* @__PURE__ */ jsx(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle, { className: "text-base truncate", children: titleVal }) }),
2759
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2760
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
2761
+ badgeCol && /* @__PURE__ */ jsx("span", { children: badgeCol.render ? badgeCol.render(item, badgeVal, actions) : badgeVal != null ? String(badgeVal) : "\u2014" }),
2762
+ priceVal != null && /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: String(priceVal) })
2763
+ ] }),
2764
+ metaCols.map((col) => {
2765
+ const val = item[col.key];
2766
+ return /* @__PURE__ */ jsx(
2767
+ "p",
2768
+ {
2769
+ className: "text-xs text-muted-foreground truncate",
2770
+ children: col.render ? col.render(item, val, actions) : val != null ? String(val) : "\u2014"
2771
+ },
2772
+ col.key
2773
+ );
2774
+ })
2775
+ ] }) })
2776
+ ]
2777
+ }
2778
+ );
2779
+ };
2780
+ const renderDefaultListItem = (item) => {
2781
+ const cols = page.columns;
2782
+ const titleCol = cols[0];
2783
+ const titleVal = titleCol ? String(item[titleCol.key] ?? "") : "";
2784
+ return /* @__PURE__ */ jsxs(
2785
+ "div",
2786
+ {
2787
+ ...rowActivationProps(item, titleVal, "resource-list-row"),
2788
+ className: "flex items-center justify-between px-3 py-2 border-b cursor-pointer hover:bg-muted/50 motion-safe:transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring",
2789
+ children: [
2790
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-sm truncate", children: titleVal }),
2791
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: cols.slice(1, 4).map((col) => {
2792
+ const val = item[col.key];
2793
+ return /* @__PURE__ */ jsx("span", { children: col.render ? col.render(item, val, actions) : val != null ? String(val) : "\u2014" }, col.key);
2794
+ }) })
2795
+ ]
2796
+ }
2797
+ );
2798
+ };
2920
2799
  return /* @__PURE__ */ jsxs(
2921
2800
  DataPageShell,
2922
2801
  {
@@ -2931,25 +2810,30 @@ function ResourcePage(props) {
2931
2810
  onRetry: isClientMode ? () => resourceHook.list.refetch() : void 0,
2932
2811
  viewMode: page.viewMode,
2933
2812
  onViewModeChange: page.setViewMode,
2934
- pageSize: settings.pageSize,
2935
- onPageSizeChange: (size) => updateSetting("pageSize", size),
2813
+ pageSize: effectivePageSize,
2814
+ onPageSizeChange: handlePageSizeChange,
2936
2815
  density: settings.density,
2937
2816
  onDensityChange: (d) => updateSetting("density", d),
2938
2817
  createAction,
2939
2818
  hasActiveFilters: page.hasActiveFilters,
2940
- onClearFilters: page.clearFilters,
2819
+ onClearFilters: handleClearFilters,
2941
2820
  emptyAction: emptyState,
2942
2821
  filters: /* @__PURE__ */ jsx(
2943
2822
  DataFilters,
2944
2823
  {
2945
2824
  search: page.filterConfig.search,
2946
- onSearchChange: page.filterConfig.onSearchChange,
2825
+ onSearchChange: handleSearchChange,
2947
2826
  searchPlaceholder,
2948
2827
  sort: page.filterConfig.sort,
2949
- onSortChange: page.filterConfig.onSortChange,
2828
+ onSortChange: handleSortSelect,
2950
2829
  sortOptions: page.filterConfig.sortOptions,
2951
- statusChips: page.filterConfig.statusChips,
2952
- onClearAll: page.hasActiveFilters ? page.clearFilters : void 0
2830
+ statusChips: statusChipsConfig,
2831
+ savedViews,
2832
+ onLoadView,
2833
+ onSaveView,
2834
+ onDeleteView,
2835
+ canSaveViews,
2836
+ onClearAll: page.hasActiveFilters ? handleClearFilters : void 0
2953
2837
  }
2954
2838
  ),
2955
2839
  children: [
@@ -2975,7 +2859,10 @@ function ResourcePage(props) {
2975
2859
  {
2976
2860
  items: page.data,
2977
2861
  viewMode: page.viewMode,
2862
+ gridCols: gridColsProp,
2978
2863
  getKey: (item) => String(item.id ?? ""),
2864
+ focusedIndex: keyboardNav.focusedIndex,
2865
+ keyboardContainerRef: keyboardNav.containerRef,
2979
2866
  renderTable: (items) => /* @__PURE__ */ jsx(
2980
2867
  DataTable,
2981
2868
  {
@@ -2988,72 +2875,109 @@ function ResourcePage(props) {
2988
2875
  onToggleSelect: handleToggleSelect,
2989
2876
  onToggleAll: handleToggleAll,
2990
2877
  density: settings.density,
2991
- pageSize: settings.pageSize
2878
+ pageSize: effectivePageSize,
2879
+ pageSizeOptions,
2880
+ columnVisibility,
2881
+ columnVisibilityKey,
2882
+ resizableColumns,
2883
+ resizeKey,
2884
+ stickyHeader,
2885
+ stickyFirstColumn,
2886
+ pagination: isServerControlled ? false : void 0
2992
2887
  }
2993
2888
  ),
2994
- renderCard: (item) => {
2995
- const cols = page.columns;
2996
- const titleCol = cols[0];
2997
- const titleVal = titleCol ? String(item[titleCol.key] ?? "") : "";
2998
- const badgeCol = cols[1];
2999
- const badgeVal = badgeCol ? item[badgeCol.key] : null;
3000
- const priceCol = cols.find(
3001
- (c, i) => i > 1 && /price|cost|amount|total/i.test(c.key)
3002
- );
3003
- const priceVal = priceCol ? item[priceCol.key] : null;
3004
- const metaCols = cols.filter(
3005
- (c) => c !== titleCol && c !== badgeCol && c !== priceCol
3006
- );
3007
- return /* @__PURE__ */ jsxs(
3008
- Card,
3009
- {
3010
- className: "h-full cursor-pointer hover:shadow-md transition-shadow",
3011
- onClick: () => handleRowClick(item),
3012
- children: [
3013
- /* @__PURE__ */ jsx(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle, { className: "text-base truncate", children: titleVal }) }),
3014
- /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
3015
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
3016
- badgeCol && /* @__PURE__ */ jsx("span", { children: badgeCol.render ? badgeCol.render(item, badgeVal, actions) : badgeVal != null ? String(badgeVal) : "\u2014" }),
3017
- priceVal != null && /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: String(priceVal) })
3018
- ] }),
3019
- metaCols.map((col) => {
3020
- const val = item[col.key];
3021
- return /* @__PURE__ */ jsx(
3022
- "p",
3023
- {
3024
- className: "text-xs text-muted-foreground truncate",
3025
- children: col.render ? col.render(item, val, actions) : val != null ? String(val) : "\u2014"
3026
- },
3027
- col.key
3028
- );
3029
- })
3030
- ] }) })
3031
- ]
3032
- }
3033
- );
3034
- },
3035
- renderListItem: (item) => {
3036
- const cols = page.columns;
3037
- const titleCol = cols[0];
3038
- const titleVal = titleCol ? String(item[titleCol.key] ?? "") : "";
3039
- return /* @__PURE__ */ jsxs(
3040
- "div",
2889
+ renderCard: (item, index) => renderCardProp ? renderCardProp(item, index, cardActionsFor(item)) : renderDefaultCard(item),
2890
+ renderListItem: (item, index) => renderCardProp ? renderCardProp(item, index, cardActionsFor(item)) : renderDefaultListItem(item)
2891
+ }
2892
+ ),
2893
+ isServerControlled && onPageChange && pagerMisconfigured && /* @__PURE__ */ jsxs(
2894
+ "div",
2895
+ {
2896
+ className: "flex items-center gap-2 rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive",
2897
+ "data-testid": "resource-page-pagination-error",
2898
+ role: "alert",
2899
+ children: [
2900
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
2901
+ /* @__PURE__ */ jsxs("span", { children: [
2902
+ "Pagination unavailable:",
2903
+ " ",
2904
+ missingPagerProps.map((name, index) => /* @__PURE__ */ jsxs(Fragment$1, { children: [
2905
+ index > 0 ? " and " : null,
2906
+ /* @__PURE__ */ jsx("code", { children: name })
2907
+ ] }, name)),
2908
+ " ",
2909
+ missingPagerProps.length > 1 ? "are" : "is",
2910
+ " required when",
2911
+ " ",
2912
+ /* @__PURE__ */ jsx("code", { children: "onPageChange" }),
2913
+ " is supplied."
2914
+ ] })
2915
+ ]
2916
+ }
2917
+ ),
2918
+ isServerControlled && onPageChange && !pagerMisconfigured && /* @__PURE__ */ jsxs(
2919
+ "nav",
2920
+ {
2921
+ "aria-label": "Pagination",
2922
+ className: "flex flex-col sm:flex-row items-center justify-between gap-2 px-3 py-2 text-sm",
2923
+ "data-testid": "resource-page-pagination",
2924
+ children: [
2925
+ /* @__PURE__ */ jsx(
2926
+ "span",
3041
2927
  {
3042
- className: "flex items-center justify-between px-3 py-2 border-b cursor-pointer hover:bg-muted/50 transition-colors",
3043
- onClick: () => handleRowClick(item),
3044
- children: [
3045
- /* @__PURE__ */ jsx("span", { className: "font-medium text-sm truncate", children: titleVal }),
3046
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: cols.slice(1, 4).map((col) => {
3047
- const val = item[col.key];
3048
- return /* @__PURE__ */ jsx("span", { children: col.render ? col.render(item, val, actions) : val != null ? String(val) : "\u2014" }, col.key);
3049
- }) })
3050
- ]
2928
+ className: "text-muted-foreground",
2929
+ "data-testid": "resource-page-range",
2930
+ "aria-live": "polite",
2931
+ children: displayTotal === 0 ? "No records" : `Showing ${rangeFrom}-${rangeTo} of ${displayTotal}`
3051
2932
  }
3052
- );
3053
- }
2933
+ ),
2934
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2935
+ /* @__PURE__ */ jsxs(
2936
+ Button,
2937
+ {
2938
+ variant: "outline",
2939
+ size: "sm",
2940
+ disabled: currentPage <= 1,
2941
+ onClick: () => onPageChange(currentPage - 1),
2942
+ testID: "resource-page-prev",
2943
+ children: [
2944
+ /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4 mr-1", "aria-hidden": "true" }),
2945
+ "Previous"
2946
+ ]
2947
+ }
2948
+ ),
2949
+ /* @__PURE__ */ jsxs(
2950
+ "span",
2951
+ {
2952
+ className: "text-muted-foreground",
2953
+ "data-testid": "resource-page-indicator",
2954
+ children: [
2955
+ "Page ",
2956
+ currentPage,
2957
+ " of ",
2958
+ totalPages
2959
+ ]
2960
+ }
2961
+ ),
2962
+ /* @__PURE__ */ jsxs(
2963
+ Button,
2964
+ {
2965
+ variant: "outline",
2966
+ size: "sm",
2967
+ disabled: currentPage >= totalPages,
2968
+ onClick: () => onPageChange(currentPage + 1),
2969
+ testID: "resource-page-next",
2970
+ children: [
2971
+ "Next",
2972
+ /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4 ml-1", "aria-hidden": "true" })
2973
+ ]
2974
+ }
2975
+ )
2976
+ ] })
2977
+ ]
3054
2978
  }
3055
2979
  ),
3056
- detail === "panel" && /* @__PURE__ */ jsx(
2980
+ (detail === "panel" || detail === "modal") && /* @__PURE__ */ jsx(
3057
2981
  DetailPanel,
3058
2982
  {
3059
2983
  item: page.selectedItem,
@@ -3068,48 +2992,10 @@ function ResourcePage(props) {
3068
2992
  onDelete,
3069
2993
  fields: detailFields,
3070
2994
  width: detailWidth,
2995
+ layout: detail === "modal" ? "dialog" : "sheet",
3071
2996
  children: renderDetailProp ? (item) => renderDetailProp(item) : void 0
3072
2997
  }
3073
2998
  ),
3074
- detail === "modal" && /* @__PURE__ */ jsx(
3075
- Dialog,
3076
- {
3077
- open: page.detailOpen,
3078
- onOpenChange: (open) => {
3079
- page.setDetailOpen(open);
3080
- if (!open) page.setSelectedItem(null);
3081
- },
3082
- children: /* @__PURE__ */ jsxs(DialogContent, { children: [
3083
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
3084
- /* @__PURE__ */ jsx(DialogTitle, { children: detailTitle }),
3085
- detailSubtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: detailSubtitle })
3086
- ] }),
3087
- /* @__PURE__ */ jsxs("div", { className: "py-4", children: [
3088
- modalDetailContent,
3089
- (onEdit || useBuiltInEdit || onDelete) && page.selectedItem && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-4 pt-4 border-t", children: [
3090
- (onEdit || useBuiltInEdit) && /* @__PURE__ */ jsx(
3091
- Button,
3092
- {
3093
- variant: "outline",
3094
- size: "sm",
3095
- onClick: () => handleEdit(page.selectedItem),
3096
- children: "Edit"
3097
- }
3098
- ),
3099
- onDelete && /* @__PURE__ */ jsx(
3100
- Button,
3101
- {
3102
- variant: "destructive",
3103
- size: "sm",
3104
- onClick: () => onDelete(page.selectedItem),
3105
- children: "Delete"
3106
- }
3107
- )
3108
- ] })
3109
- ] })
3110
- ] })
3111
- }
3112
- ),
3113
2999
  (useBuiltInCreate || useBuiltInEdit) && resourceName && /* @__PURE__ */ jsx(
3114
3000
  ResourceForm,
3115
3001
  {
@@ -3136,769 +3022,80 @@ function ResourcePage(props) {
3136
3022
  }
3137
3023
  );
3138
3024
  }
3139
- var OPTION_DEFAULTS2 = {
3140
- showLogo: true,
3141
- showAppName: true,
3142
- showThemeToggle: true,
3143
- showCollapse: true,
3144
- showUserSlot: true,
3145
- showFooter: true,
3146
- showSecondaryNav: true,
3147
- showMobileBottomNav: true,
3148
- defaultCollapsed: false,
3149
- configurable: void 0,
3150
- density: "comfortable",
3151
- railWidth: "narrow",
3152
- scrollRoot: "container"
3153
- };
3154
- function readStoredCollapsed(key) {
3155
- if (typeof window === "undefined") return void 0;
3156
- try {
3157
- const raw = localStorage.getItem(`${key}:collapsed`);
3158
- if (raw === "1") return true;
3159
- if (raw === "0") return false;
3160
- return void 0;
3161
- } catch {
3162
- return void 0;
3163
- }
3025
+ function pick(record, field) {
3026
+ const value = field ? record[field] : record.name ?? record.title ?? record.id;
3027
+ return value == null ? "" : String(value);
3164
3028
  }
3165
- function writeStoredCollapsed(key, collapsed) {
3166
- if (typeof window === "undefined") return;
3167
- try {
3168
- localStorage.setItem(`${key}:collapsed`, collapsed ? "1" : "0");
3169
- } catch {
3170
- }
3171
- }
3172
- function ThemeToggle({ testId }) {
3173
- const [dark, setDark] = useState(false);
3174
- useEffect(() => {
3175
- const stored = localStorage.getItem("theme");
3176
- const prefersDark = window.matchMedia(
3177
- "(prefers-color-scheme: dark)"
3178
- ).matches;
3179
- const isDark = stored === "dark" || !stored && prefersDark;
3180
- setDark(isDark);
3181
- document.documentElement.classList.toggle("dark", isDark);
3182
- }, []);
3183
- function toggle() {
3184
- const next = !dark;
3185
- setDark(next);
3186
- document.documentElement.classList.toggle("dark", next);
3187
- localStorage.setItem("theme", next ? "dark" : "light");
3188
- }
3029
+ function NotFound() {
3189
3030
  return /* @__PURE__ */ jsx(
3190
- Button,
3031
+ EmptyState,
3191
3032
  {
3192
- variant: "ghost",
3193
- size: "icon",
3194
- onClick: toggle,
3195
- className: "h-8 w-8",
3196
- title: "Toggle theme",
3197
- "aria-label": "Toggle theme",
3198
- "data-testid": testId,
3199
- testID: testId,
3200
- children: dark ? /* @__PURE__ */ jsx(Sun, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Moon, { className: "h-4 w-4" })
3033
+ title: "Not found",
3034
+ description: "This record no longer exists, or you don't have access to it."
3201
3035
  }
3202
3036
  );
3203
3037
  }
3204
- function itemIsActive(item, activePath, isActive) {
3205
- if (item.end) {
3206
- return Boolean(activePath && activePath === item.to);
3207
- }
3208
- if (isActive(item.to, activePath)) return true;
3209
- return Boolean(
3210
- item.children?.some((child) => itemIsActive(child, activePath, isActive))
3211
- );
3038
+ function isNotFoundError(error) {
3039
+ if (!(error instanceof Error)) return false;
3040
+ if (error.code === "PGRST116") return true;
3041
+ return /\[MockProvider\] Record not found/i.test(error.message) || /switchboard-data-provider: no ".*" row with id/i.test(error.message);
3212
3042
  }
3213
- function NavLinkRow({
3214
- item,
3215
- collapsed,
3216
- density,
3217
- onNavClick,
3218
- linkComponent: LinkComp,
3219
- activePath,
3220
- isActive
3221
- }) {
3222
- const active = itemIsActive(item, activePath, isActive);
3223
- const pad = density === "compact" ? "px-2.5 py-1.5" : "px-3 py-2";
3224
- if (item.disabled) {
3225
- return /* @__PURE__ */ jsxs(
3226
- "span",
3227
- {
3228
- title: item.label,
3229
- className: cn(
3230
- "flex items-center gap-3 rounded-lg text-sm font-medium opacity-40",
3231
- pad,
3232
- collapsed && "justify-center px-2"
3233
- ),
3234
- "aria-disabled": true,
3235
- children: [
3236
- /* @__PURE__ */ jsx(item.icon, { className: "h-4 w-4 shrink-0" }),
3237
- !collapsed ? /* @__PURE__ */ jsx("span", { className: "truncate", children: item.label }) : null
3238
- ]
3239
- }
3240
- );
3241
- }
3242
- return /* @__PURE__ */ jsxs(
3243
- LinkComp,
3244
- {
3245
- href: item.to,
3246
- onClick: onNavClick,
3247
- title: collapsed ? item.label : void 0,
3248
- "aria-label": collapsed ? item.label : void 0,
3249
- "aria-current": active ? "page" : void 0,
3250
- className: cn(
3251
- "flex items-center gap-3 rounded-lg text-sm font-medium transition-colors",
3252
- pad,
3253
- collapsed && "justify-center px-2",
3254
- active ? "bg-sidebar-accent text-sidebar-primary" : "text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
3255
- ),
3256
- children: [
3257
- /* @__PURE__ */ jsx(item.icon, { className: "h-4 w-4 shrink-0" }),
3258
- !collapsed ? /* @__PURE__ */ jsxs(Fragment, { children: [
3259
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: item.label }),
3260
- item.badge != null ? /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "h-5 shrink-0 px-1.5 text-[10px]", children: item.badge }) : null
3261
- ] }) : null
3262
- ]
3263
- }
3264
- );
3265
- }
3266
- function NavGroup({
3267
- item,
3268
- collapsed,
3269
- density,
3270
- onNavClick,
3271
- linkComponent,
3272
- activePath,
3273
- isActive
3043
+ function ResourceDetailPage({
3044
+ resource,
3045
+ id,
3046
+ titleField,
3047
+ subtitleField,
3048
+ select,
3049
+ breadcrumb,
3050
+ actions,
3051
+ sidebar,
3052
+ children,
3053
+ className,
3054
+ testID
3274
3055
  }) {
3275
- const children = item.children ?? [];
3276
- const childActive = children.some(
3277
- (c) => itemIsActive(c, activePath, isActive)
3278
- );
3279
- const [open, setOpen] = useState(childActive);
3280
- useEffect(() => {
3281
- if (childActive) setOpen(true);
3282
- }, [childActive]);
3283
- if (children.length === 0) {
3284
- return /* @__PURE__ */ jsx(
3285
- NavLinkRow,
3286
- {
3287
- item,
3288
- collapsed,
3289
- density,
3290
- onNavClick,
3291
- linkComponent,
3292
- activePath,
3293
- isActive
3294
- }
3295
- );
3056
+ const { data, isLoading, isError, error } = useOne(resource, id, {
3057
+ select,
3058
+ // `useOne` defaults to `enabled: !!id`, which is wrong for a numeric
3059
+ // primary key: id `0` is falsy, so the query would never run and the page
3060
+ // would report a record that exists as missing, without ever asking the
3061
+ // provider. `Identifier` is `string | number`, so this is reachable.
3062
+ // Presence is the question, not truthiness.
3063
+ enabled: id != null
3064
+ });
3065
+ if (id == null || isLoading) {
3066
+ return /* @__PURE__ */ jsx(PageLoading, { testID: testID ? `${testID}-loading` : void 0 });
3296
3067
  }
3297
- if (collapsed) {
3298
- const target = item.to ? { ...item, children: void 0 } : { ...children[0], children: void 0 };
3068
+ if (isError) {
3069
+ if (isNotFoundError(error)) return /* @__PURE__ */ jsx(NotFound, {});
3299
3070
  return /* @__PURE__ */ jsx(
3300
- NavLinkRow,
3071
+ EmptyState,
3301
3072
  {
3302
- item: target,
3303
- collapsed: true,
3304
- density,
3305
- onNavClick,
3306
- linkComponent,
3307
- activePath,
3308
- isActive
3073
+ icon: AlertTriangle,
3074
+ title: "Couldn't load this record",
3075
+ description: error instanceof Error ? error.message : void 0
3309
3076
  }
3310
3077
  );
3311
3078
  }
3312
- const selfExact = Boolean(item.to && activePath && activePath === item.to);
3313
- const selfActive = item.to ? item.end ? selfExact : isActive(item.to, activePath) : false;
3314
- const groupHighlight = selfActive || childActive;
3315
- const pad = density === "compact" ? "px-2.5 py-1.5" : "px-3 py-2";
3316
- const LinkComp = linkComponent;
3317
- const parentClass = cn(
3318
- "flex min-w-0 flex-1 items-center gap-3 rounded-lg text-sm font-medium transition-colors",
3319
- pad,
3320
- groupHighlight ? "bg-sidebar-accent/60 text-sidebar-primary" : "text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
3321
- );
3322
- return /* @__PURE__ */ jsx(Collapsible, { open, onOpenChange: setOpen, children: /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
3323
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", "data-testid": `nav-group-${item.label}`, children: [
3324
- item.to && !item.disabled ? /* @__PURE__ */ jsxs(
3325
- LinkComp,
3326
- {
3327
- href: item.to,
3328
- onClick: onNavClick,
3329
- "aria-current": selfExact ? "page" : void 0,
3330
- className: parentClass,
3331
- children: [
3332
- /* @__PURE__ */ jsx(item.icon, { className: "h-4 w-4 shrink-0" }),
3333
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: item.label })
3334
- ]
3335
- }
3336
- ) : /* @__PURE__ */ jsxs(
3337
- "button",
3338
- {
3339
- type: "button",
3340
- className: parentClass,
3341
- onClick: () => setOpen((o) => !o),
3342
- "aria-expanded": open,
3343
- "aria-disabled": item.disabled || void 0,
3344
- disabled: item.disabled,
3345
- children: [
3346
- /* @__PURE__ */ jsx(item.icon, { className: "h-4 w-4 shrink-0" }),
3347
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: item.label })
3348
- ]
3349
- }
3350
- ),
3351
- /* @__PURE__ */ jsx(
3352
- "button",
3353
- {
3354
- type: "button",
3355
- className: cn(
3356
- "shrink-0 rounded-lg p-1.5 text-sidebar-foreground transition-colors hover:bg-sidebar-accent",
3357
- groupHighlight && "text-sidebar-primary",
3358
- item.disabled && "pointer-events-none opacity-40"
3359
- ),
3360
- onClick: () => setOpen((o) => !o),
3361
- "aria-expanded": open,
3362
- "aria-label": open ? `Collapse ${item.label}` : `Expand ${item.label}`,
3363
- "aria-disabled": item.disabled || void 0,
3364
- disabled: item.disabled,
3365
- "data-testid": `nav-group-toggle-${item.label}`,
3366
- children: /* @__PURE__ */ jsx(
3367
- ChevronDown,
3368
- {
3369
- className: cn(
3370
- "h-3.5 w-3.5 opacity-60 transition-transform",
3371
- open && "rotate-180"
3372
- )
3373
- }
3374
- )
3375
- }
3376
- )
3377
- ] }),
3378
- /* @__PURE__ */ jsx(CollapsibleContent, { children: /* @__PURE__ */ jsx("div", { className: "ml-3 space-y-0.5 border-l border-sidebar-border pl-2", children: children.map((child) => /* @__PURE__ */ jsx(
3379
- NavLinkRow,
3380
- {
3381
- item: child,
3382
- collapsed: false,
3383
- density,
3384
- onNavClick,
3385
- linkComponent,
3386
- activePath,
3387
- isActive
3388
- },
3389
- child.to + child.label
3390
- )) }) })
3391
- ] }) });
3392
- }
3393
- function SidebarNav({
3394
- navItems,
3395
- secondaryNavItems,
3396
- secondaryNavLabel,
3397
- showSecondary,
3398
- collapsed,
3399
- density,
3400
- onNavClick,
3401
- linkComponent,
3402
- activePath,
3403
- isActive
3404
- }) {
3405
- return /* @__PURE__ */ jsxs(
3406
- "nav",
3407
- {
3408
- className: cn(
3409
- "flex-1 space-y-1 overflow-y-auto",
3410
- collapsed ? "p-2" : "p-3"
3411
- ),
3412
- "aria-label": "Main",
3413
- children: [
3414
- navItems.map((item) => /* @__PURE__ */ jsx(
3415
- NavGroup,
3416
- {
3417
- item,
3418
- collapsed,
3419
- density,
3420
- onNavClick,
3421
- linkComponent,
3422
- activePath,
3423
- isActive
3424
- },
3425
- item.to + item.label
3426
- )),
3427
- showSecondary && secondaryNavItems && secondaryNavItems.length > 0 ? /* @__PURE__ */ jsxs(
3428
- "div",
3429
- {
3430
- className: cn(
3431
- "mt-4 space-y-1 border-t border-sidebar-border pt-4",
3432
- collapsed && "mt-2 pt-2"
3433
- ),
3434
- children: [
3435
- !collapsed ? /* @__PURE__ */ jsx("p", { className: "mb-2 px-3 text-xs font-medium uppercase tracking-wider text-sidebar-foreground/40", children: secondaryNavLabel }) : null,
3436
- secondaryNavItems.map((item) => /* @__PURE__ */ jsx(
3437
- NavGroup,
3438
- {
3439
- item,
3440
- collapsed,
3441
- density,
3442
- onNavClick,
3443
- linkComponent,
3444
- activePath,
3445
- isActive
3446
- },
3447
- item.to + item.label
3448
- ))
3449
- ]
3450
- }
3451
- ) : null
3452
- ]
3453
- }
3454
- );
3455
- }
3456
- function SidebarBrand({
3457
- appName,
3458
- appLogo,
3459
- logo,
3460
- showLogo,
3461
- showAppName,
3462
- collapsed
3463
- }) {
3464
- if (!showLogo && !showAppName) return null;
3465
- return /* @__PURE__ */ jsxs(
3466
- "div",
3467
- {
3468
- className: cn(
3469
- "flex min-w-0 items-center gap-2",
3470
- collapsed && "justify-center"
3471
- ),
3472
- children: [
3473
- showLogo ? logo ? /* @__PURE__ */ jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center overflow-hidden", children: logo }) : appLogo ? /* @__PURE__ */ jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-primary", children: /* @__PURE__ */ jsx("span", { className: "text-sm font-bold text-primary-foreground", children: appLogo }) }) : null : null,
3474
- showAppName && !collapsed ? /* @__PURE__ */ jsx("span", { className: "truncate font-semibold text-sidebar-foreground", children: appName }) : null
3475
- ]
3476
- }
3477
- );
3478
- }
3479
- function AppShell({
3480
- appName,
3481
- appLogo,
3482
- logo,
3483
- navItems,
3484
- secondaryNavItems,
3485
- secondaryNavLabel = "More",
3486
- mobileNavItems,
3487
- headerSlot,
3488
- collapsedHeaderSlot,
3489
- footer,
3490
- userSlot,
3491
- children,
3492
- showThemeToggle: showThemeToggleProp,
3493
- options: optionsProp,
3494
- optionsStorageKey,
3495
- collapsed: collapsedProp,
3496
- onCollapsedChange,
3497
- linkComponent = DefaultLink,
3498
- activePath,
3499
- isActive = isPathActive
3500
- }) {
3501
- const [mobileOpen, setMobileOpen] = useState(false);
3502
- const [userOverrides, setUserOverrides] = useState(
3503
- {}
3504
- );
3505
- const [hydratedKey, setHydratedKey] = useState(
3506
- optionsStorageKey ? void 0 : ""
3507
- );
3508
- useEffect(() => {
3509
- setMobileOpen(false);
3510
- }, [activePath]);
3511
- const hasSecondary = Boolean(secondaryNavItems && secondaryNavItems.length);
3512
- const hasUser = userSlot != null;
3513
- const hasFooter = footer != null;
3514
- const hasLogo = Boolean(logo || appLogo);
3515
- useEffect(() => {
3516
- if (!optionsStorageKey) {
3517
- setUserOverrides({});
3518
- setHydratedKey("");
3519
- return;
3520
- }
3521
- setUserOverrides(readStoredOptions(optionsStorageKey));
3522
- setHydratedKey(optionsStorageKey);
3523
- }, [optionsStorageKey]);
3524
- useEffect(() => {
3525
- if (!optionsStorageKey || hydratedKey !== optionsStorageKey) return;
3526
- writeStoredOptions(optionsStorageKey, userOverrides);
3527
- }, [optionsStorageKey, userOverrides, hydratedKey]);
3528
- const lockedOptions = optionsProp?.lockedOptions;
3529
- const resolved = useMemo(() => {
3530
- const merged = {
3531
- ...OPTION_DEFAULTS2,
3532
- ...showThemeToggleProp !== void 0 ? { showThemeToggle: showThemeToggleProp } : {},
3533
- ...optionsProp,
3534
- ...userOverrides
3535
- };
3536
- if (merged.configurable === void 0) {
3537
- merged.configurable = hasSecondary || hasUser || hasFooter || Boolean(merged.showCollapse) || Boolean(merged.showThemeToggle);
3538
- }
3539
- if (lockedOptions?.length) {
3540
- const defaults = OPTION_DEFAULTS2;
3541
- const devOptions = optionsProp;
3542
- const target = merged;
3543
- for (const key of lockedOptions) {
3544
- target[key] = devOptions?.[key] ?? defaults[key];
3545
- }
3546
- }
3547
- return merged;
3548
- }, [
3549
- optionsProp,
3550
- userOverrides,
3551
- showThemeToggleProp,
3552
- hasSecondary,
3553
- hasUser,
3554
- hasFooter,
3555
- lockedOptions
3556
- ]);
3557
- const patchOption = useCallback(
3558
- (key, value) => {
3559
- if (lockedOptions?.includes(key)) return;
3560
- setUserOverrides((prev) => ({ ...prev, [key]: value }));
3561
- },
3562
- [lockedOptions]
3563
- );
3564
- const [uncontrolledCollapsed, setUncontrolledCollapsed] = useState(
3565
- () => optionsProp?.defaultCollapsed ?? OPTION_DEFAULTS2.defaultCollapsed
3566
- );
3567
- useEffect(() => {
3568
- if (!optionsStorageKey || collapsedProp !== void 0) return;
3569
- const stored = readStoredCollapsed(optionsStorageKey);
3570
- if (stored !== void 0) setUncontrolledCollapsed(stored);
3571
- }, [optionsStorageKey, collapsedProp]);
3572
- const isCollapseControlled = collapsedProp !== void 0;
3573
- const collapsed = isCollapseControlled ? collapsedProp : uncontrolledCollapsed;
3574
- const setCollapsed = (next) => {
3575
- if (!isCollapseControlled) {
3576
- setUncontrolledCollapsed(next);
3577
- if (optionsStorageKey) writeStoredCollapsed(optionsStorageKey, next);
3578
- }
3579
- onCollapsedChange?.(next);
3580
- };
3581
- const density = resolved.density ?? "comfortable";
3582
- const scrollWindow = resolved.scrollRoot === "window";
3583
- const railExpanded = resolved.railWidth === "wide" ? "md:w-64" : "md:w-52";
3584
- const railClass = collapsed ? "md:w-14" : railExpanded;
3585
- const bottomItems = (mobileNavItems ?? navItems.filter((i) => !i.children || i.children.length === 0)).filter((i) => !i.disabled);
3586
- const toggles = [
3587
- { key: "showLogo", label: "Logo", available: hasLogo },
3588
- { key: "showAppName", label: "App name", available: true },
3589
- {
3590
- key: "showThemeToggle",
3591
- label: "Theme toggle",
3592
- available: true
3593
- },
3594
- { key: "showCollapse", label: "Collapse control", available: true },
3595
- {
3596
- key: "showSecondaryNav",
3597
- label: "Secondary nav",
3598
- available: hasSecondary
3599
- },
3600
- { key: "showFooter", label: "Footer", available: hasFooter },
3601
- { key: "showUserSlot", label: "User block", available: hasUser },
3602
- {
3603
- key: "showMobileBottomNav",
3604
- label: "Mobile bottom nav",
3605
- available: bottomItems.length > 0
3606
- }
3607
- ].filter((t) => t.available && !lockedOptions?.includes(t.key));
3608
- const showTheme = resolved.showThemeToggle !== false;
3609
- const showCollapseCtrl = resolved.showCollapse !== false;
3610
- const showSecondary = hasSecondary && resolved.showSecondaryNav !== false;
3611
- const showFooterSlot = hasFooter && resolved.showFooter !== false;
3612
- const showUser = hasUser && resolved.showUserSlot !== false;
3613
- const showMobileBottom = bottomItems.length > 0 && resolved.showMobileBottomNav !== false;
3614
- const optionsControl = resolved.configurable && toggles.length > 0 ? /* @__PURE__ */ jsxs(Popover, { children: [
3615
- /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
3616
- Button,
3617
- {
3618
- type: "button",
3619
- variant: "ghost",
3620
- size: "icon",
3621
- className: "h-8 w-8",
3622
- title: "Sidebar options",
3623
- "aria-label": "Sidebar options",
3624
- "data-testid": "app-shell-options",
3625
- testID: "app-shell-options",
3626
- children: /* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-4 w-4" })
3627
- }
3628
- ) }),
3629
- /* @__PURE__ */ jsxs(PopoverContent, { align: "start", side: "top", className: "w-64 space-y-3", children: [
3630
- /* @__PURE__ */ jsxs("div", { children: [
3631
- /* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold", children: "Sidebar options" }),
3632
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Customize this sidebar. Saved on this device when a storage key is set." })
3633
- ] }),
3634
- /* @__PURE__ */ jsx("ul", { className: "space-y-3", children: toggles.map((t) => {
3635
- const checked = Boolean(resolved[t.key] ?? true);
3636
- return /* @__PURE__ */ jsxs(
3637
- "li",
3638
- {
3639
- className: "flex items-center justify-between gap-3",
3640
- children: [
3641
- /* @__PURE__ */ jsx(
3642
- Label,
3643
- {
3644
- htmlFor: `app-shell-opt-${t.key}`,
3645
- className: "text-sm font-normal",
3646
- children: t.label
3647
- }
3648
- ),
3649
- /* @__PURE__ */ jsx(
3650
- Switch,
3651
- {
3652
- id: `app-shell-opt-${t.key}`,
3653
- checked,
3654
- onCheckedChange: (v) => patchOption(t.key, v)
3655
- }
3656
- )
3657
- ]
3658
- },
3659
- t.key
3660
- );
3661
- }) }),
3662
- optionsStorageKey && Object.keys(userOverrides).length > 0 ? /* @__PURE__ */ jsx(
3663
- Button,
3664
- {
3665
- type: "button",
3666
- variant: "ghost",
3667
- size: "sm",
3668
- className: "h-8 w-full text-xs",
3669
- onClick: () => setUserOverrides({}),
3670
- "data-testid": "app-shell-options-reset",
3671
- "aria-label": "Reset sidebar options",
3672
- testID: "app-shell-options-reset",
3673
- children: "Reset to defaults"
3674
- }
3675
- ) : null
3676
- ] })
3677
- ] }) : null;
3678
- const sidebarChrome = (opts) => /* @__PURE__ */ jsxs(Fragment, { children: [
3679
- /* @__PURE__ */ jsxs(
3680
- "div",
3681
- {
3682
- className: cn(
3683
- "flex h-14 shrink-0 items-center gap-2 border-b border-sidebar-border",
3684
- collapsed && !opts.mobile ? "justify-center px-2" : "px-3"
3685
- ),
3686
- children: [
3687
- /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
3688
- SidebarBrand,
3689
- {
3690
- appName,
3691
- appLogo,
3692
- logo,
3693
- showLogo: resolved.showLogo !== false,
3694
- showAppName: resolved.showAppName !== false,
3695
- collapsed: collapsed && !opts.mobile
3696
- }
3697
- ) }),
3698
- opts.mobile ? /* @__PURE__ */ jsx(
3699
- Button,
3700
- {
3701
- variant: "ghost",
3702
- size: "icon",
3703
- onClick: () => setMobileOpen(false),
3704
- "aria-label": "Close navigation",
3705
- "data-testid": "app-shell-mobile-close",
3706
- testID: "app-shell-mobile-close",
3707
- children: /* @__PURE__ */ jsx(X, { className: "h-5 w-5" })
3708
- }
3709
- ) : /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-0.5", children: [
3710
- showCollapseCtrl ? /* @__PURE__ */ jsx(
3711
- Button,
3712
- {
3713
- variant: "ghost",
3714
- size: "icon",
3715
- className: "h-8 w-8",
3716
- onClick: () => setCollapsed(!collapsed),
3717
- title: collapsed ? "Expand sidebar" : "Collapse sidebar",
3718
- "aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
3719
- "aria-pressed": collapsed,
3720
- "data-testid": "app-shell-collapse",
3721
- testID: "app-shell-collapse",
3722
- children: collapsed ? /* @__PURE__ */ jsx(PanelLeft, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(PanelLeftClose, { className: "h-4 w-4" })
3723
- }
3724
- ) : null,
3725
- !collapsed ? optionsControl : null
3726
- ] })
3727
- ]
3728
- }
3729
- ),
3730
- collapsed && !opts.mobile ? collapsedHeaderSlot ? /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b border-sidebar-border p-2", children: collapsedHeaderSlot }) : null : headerSlot ? /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b border-sidebar-border", children: headerSlot }) : null,
3731
- /* @__PURE__ */ jsx(
3732
- SidebarNav,
3733
- {
3734
- navItems,
3735
- secondaryNavItems,
3736
- secondaryNavLabel,
3737
- showSecondary,
3738
- collapsed: collapsed && !opts.mobile,
3739
- density,
3740
- onNavClick: opts.mobile ? () => setMobileOpen(false) : void 0,
3741
- linkComponent,
3742
- activePath,
3743
- isActive
3744
- }
3745
- ),
3746
- (showFooterSlot || showUser || showTheme || collapsed && optionsControl) && !opts.mobile ? /* @__PURE__ */ jsxs("div", { className: "mt-auto shrink-0 space-y-2 border-t border-sidebar-border p-2", children: [
3747
- showFooterSlot && !collapsed ? footer : null,
3748
- showUser ? /* @__PURE__ */ jsx(
3749
- "div",
3750
- {
3751
- className: cn(
3752
- collapsed && "flex justify-center [&_[data-shell-user-detail]]:hidden"
3753
- ),
3754
- children: userSlot
3755
- }
3756
- ) : null,
3757
- /* @__PURE__ */ jsxs(
3758
- "div",
3759
- {
3760
- className: cn(
3761
- "flex items-center gap-1",
3762
- collapsed ? "flex-col" : "justify-end"
3763
- ),
3764
- children: [
3765
- collapsed ? optionsControl : null,
3766
- showTheme ? /* @__PURE__ */ jsx(ThemeToggle, { testId: "app-shell-theme-toggle-desktop" }) : null
3767
- ]
3768
- }
3769
- )
3770
- ] }) : null,
3771
- opts.mobile && (showFooterSlot || showUser || showTheme) ? /* @__PURE__ */ jsxs("div", { className: "mt-auto shrink-0 space-y-2 border-t border-sidebar-border p-3", children: [
3772
- showFooterSlot ? footer : null,
3773
- showUser ? userSlot : null,
3774
- showTheme ? /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(ThemeToggle, { testId: "app-shell-theme-toggle-mobile-drawer" }) }) : null
3775
- ] }) : null
3776
- ] });
3777
- return /* @__PURE__ */ jsxs(
3778
- "div",
3079
+ if (!data) return /* @__PURE__ */ jsx(NotFound, {});
3080
+ return /* @__PURE__ */ jsx(
3081
+ DetailPage,
3779
3082
  {
3780
- className: cn(
3781
- "flex",
3782
- scrollWindow ? "min-h-screen" : "h-screen overflow-hidden"
3783
- ),
3784
- children: [
3785
- /* @__PURE__ */ jsx(
3786
- "aside",
3787
- {
3788
- className: cn(
3789
- "hidden flex-col border-r border-sidebar-border bg-sidebar transition-[width] duration-200 md:flex",
3790
- scrollWindow && "sticky top-0 h-screen",
3791
- railClass
3792
- ),
3793
- "data-collapsed": collapsed || void 0,
3794
- "data-testid": "app-shell-sidebar",
3795
- children: sidebarChrome({})
3796
- }
3797
- ),
3798
- mobileOpen ? /* @__PURE__ */ jsx(
3799
- "div",
3800
- {
3801
- className: "fixed inset-0 z-40 bg-black/50 md:hidden",
3802
- onClick: () => setMobileOpen(false),
3803
- "aria-hidden": true
3804
- }
3805
- ) : null,
3806
- /* @__PURE__ */ jsx(
3807
- "aside",
3808
- {
3809
- className: cn(
3810
- "fixed inset-y-0 left-0 z-50 flex w-60 flex-col border-r border-sidebar-border bg-sidebar transition-transform md:hidden",
3811
- mobileOpen ? "translate-x-0" : "-translate-x-full pointer-events-none"
3812
- ),
3813
- id: "app-shell-mobile-nav",
3814
- "aria-hidden": !mobileOpen,
3815
- inert: !mobileOpen || void 0,
3816
- children: sidebarChrome({ mobile: true })
3817
- }
3818
- ),
3819
- /* @__PURE__ */ jsxs(
3820
- "div",
3821
- {
3822
- className: cn(
3823
- "flex min-w-0 flex-1 flex-col",
3824
- !scrollWindow && "overflow-hidden"
3825
- ),
3826
- children: [
3827
- /* @__PURE__ */ jsxs(
3828
- "header",
3829
- {
3830
- className: cn(
3831
- "flex h-14 items-center gap-3 border-b px-4 md:hidden",
3832
- scrollWindow && "sticky top-0 z-20 bg-background"
3833
- ),
3834
- children: [
3835
- /* @__PURE__ */ jsx(
3836
- Button,
3837
- {
3838
- variant: "ghost",
3839
- size: "icon",
3840
- onClick: () => setMobileOpen(true),
3841
- "aria-label": "Open navigation",
3842
- "aria-expanded": mobileOpen,
3843
- "aria-controls": "app-shell-mobile-nav",
3844
- "data-testid": "app-shell-mobile-open",
3845
- testID: "app-shell-mobile-open",
3846
- children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
3847
- }
3848
- ),
3849
- /* @__PURE__ */ jsx("span", { className: "flex-1 truncate font-semibold", children: appName }),
3850
- showTheme ? /* @__PURE__ */ jsx(ThemeToggle, { testId: "app-shell-theme-toggle-mobile-header" }) : null
3851
- ]
3852
- }
3853
- ),
3854
- /* @__PURE__ */ jsx(
3855
- "main",
3856
- {
3857
- className: cn(
3858
- "flex-1",
3859
- !scrollWindow && "overflow-y-auto",
3860
- showMobileBottom ? "pb-16 md:pb-0" : "pb-0"
3861
- ),
3862
- children: /* @__PURE__ */ jsx("div", { className: "min-w-0 p-4 sm:p-6 lg:p-8", children })
3863
- }
3864
- ),
3865
- showMobileBottom ? /* @__PURE__ */ jsx(
3866
- "nav",
3867
- {
3868
- className: "fixed bottom-0 left-0 right-0 z-30 flex border-t bg-background md:hidden",
3869
- "aria-label": "Primary",
3870
- children: bottomItems.map((item) => {
3871
- const LinkComp = linkComponent;
3872
- const active = itemIsActive(item, activePath, isActive);
3873
- return /* @__PURE__ */ jsxs(
3874
- LinkComp,
3875
- {
3876
- href: item.to,
3877
- title: item.label,
3878
- "aria-label": item.label,
3879
- "aria-current": active ? "page" : void 0,
3880
- className: cn(
3881
- "flex flex-1 flex-col items-center gap-1 py-2 text-xs font-medium transition-colors",
3882
- active ? "text-primary" : "text-muted-foreground"
3883
- ),
3884
- children: [
3885
- /* @__PURE__ */ jsx(item.icon, { className: "h-5 w-5" }),
3886
- /* @__PURE__ */ jsx("span", { className: "max-w-full truncate px-0.5", children: item.label })
3887
- ]
3888
- },
3889
- item.to + item.label
3890
- );
3891
- })
3892
- }
3893
- ) : null
3894
- ]
3895
- }
3896
- )
3897
- ]
3083
+ className,
3084
+ testID,
3085
+ breadcrumb,
3086
+ title: pick(data, titleField),
3087
+ subtitle: subtitleField ? pick(data, subtitleField) : void 0,
3088
+ actions: actions?.(data),
3089
+ sidebar: sidebar?.(data),
3090
+ children: children(data)
3898
3091
  }
3899
3092
  );
3900
3093
  }
3901
3094
 
3902
- export { AppShell, BadgeCell, CurrencyCell, DataBulkBar, DataFilters, DataGrid, DataList, DataPageShell, DataTable, DateCell, DetailPanel, EmptyState, FieldArrayRenderer, FileUploadField, FilterCombobox, KanbanBoard, LinkCell, NumberCell, PageHeader, RelationField, ResourceForm, ResourcePage, StatCard, ThumbnailCell, ViewSettings, ViewToggle, WizardForm };
3095
+ // src/components/data/index.ts
3096
+ var AppShell2 = AppShell;
3097
+ var PageHeader2 = PageHeader;
3098
+
3099
+ export { AppShell2 as AppShell, BadgeCell, CurrencyCell, DataBulkBar, DataFilters, DataGrid, DataList, DataPageShell, DataTable, DateCell, DetailPanel, FieldArrayRenderer, FileUploadField, FilterCombobox, KanbanBoard, LinkCell, NumberCell, PageHeader2 as PageHeader, RelationField, ResourceDetailPage, ResourceForm, ResourcePage, StatCard, ThumbnailCell, WizardForm };
3903
3100
  //# sourceMappingURL=index.js.map
3904
3101
  //# sourceMappingURL=index.js.map