@marktiderman/genesis-ui 1.3.0 → 2.0.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 (280) hide show
  1. package/dist/AppShell-BJarYZzz.d.ts +198 -0
  2. package/dist/{chunk-NJMO3VPA.js → chunk-22XB3Q6H.js} +8 -8
  3. package/dist/chunk-22XB3Q6H.js.map +1 -0
  4. package/dist/{chunk-2MXMWBDV.js → chunk-27ELYTGE.js} +6 -6
  5. package/dist/chunk-27ELYTGE.js.map +1 -0
  6. package/dist/chunk-46TZGMFW.js +159 -0
  7. package/dist/chunk-46TZGMFW.js.map +1 -0
  8. package/dist/chunk-4CQHPWJD.js +117 -0
  9. package/dist/chunk-4CQHPWJD.js.map +1 -0
  10. package/dist/{chunk-ZDAT5JDH.js → chunk-65SPT7EA.js} +24 -6
  11. package/dist/chunk-65SPT7EA.js.map +1 -0
  12. package/dist/chunk-6KHY647C.js +67 -0
  13. package/dist/chunk-6KHY647C.js.map +1 -0
  14. package/dist/chunk-6M4NHHKG.js +51 -0
  15. package/dist/chunk-6M4NHHKG.js.map +1 -0
  16. package/dist/chunk-6Q6CLLJ4.js +9 -0
  17. package/dist/chunk-6Q6CLLJ4.js.map +1 -0
  18. package/dist/{chunk-UGHQFQOE.js → chunk-6TVMQH6M.js} +28 -28
  19. package/dist/chunk-6TVMQH6M.js.map +1 -0
  20. package/dist/{chunk-6VVVGYKS.js → chunk-6XPMU65K.js} +5 -5
  21. package/dist/{chunk-6VVVGYKS.js.map → chunk-6XPMU65K.js.map} +1 -1
  22. package/dist/{chunk-YZEA5TGK.js → chunk-7XMZVISM.js} +16 -16
  23. package/dist/chunk-7XMZVISM.js.map +1 -0
  24. package/dist/chunk-A7AHVDG7.js +52 -0
  25. package/dist/chunk-A7AHVDG7.js.map +1 -0
  26. package/dist/chunk-AFZ76Q26.js +84 -0
  27. package/dist/chunk-AFZ76Q26.js.map +1 -0
  28. package/dist/{chunk-4V4H2ZM7.js → chunk-AL5TMEZQ.js} +31 -31
  29. package/dist/chunk-AL5TMEZQ.js.map +1 -0
  30. package/dist/{chunk-AUECDWP2.js → chunk-AOZGQM6W.js} +3 -3
  31. package/dist/chunk-AOZGQM6W.js.map +1 -0
  32. package/dist/chunk-ARJI2JUV.js +122 -0
  33. package/dist/chunk-ARJI2JUV.js.map +1 -0
  34. package/dist/chunk-AVFZ4P4B.js +25 -0
  35. package/dist/chunk-AVFZ4P4B.js.map +1 -0
  36. package/dist/chunk-B2GIBUQD.js +11 -0
  37. package/dist/chunk-B2GIBUQD.js.map +1 -0
  38. package/dist/{chunk-JXI2MYL3.js → chunk-C46ITXE4.js} +5 -5
  39. package/dist/chunk-C46ITXE4.js.map +1 -0
  40. package/dist/{chunk-A2OXCNNY.js → chunk-CNTWQSUT.js} +3 -3
  41. package/dist/{chunk-A2OXCNNY.js.map → chunk-CNTWQSUT.js.map} +1 -1
  42. package/dist/chunk-D2LIXA2Y.js +65 -0
  43. package/dist/chunk-D2LIXA2Y.js.map +1 -0
  44. package/dist/{chunk-T5VX5DYO.js → chunk-F47U5KNW.js} +5 -5
  45. package/dist/chunk-F47U5KNW.js.map +1 -0
  46. package/dist/chunk-FEJAZFP3.js +1100 -0
  47. package/dist/chunk-FEJAZFP3.js.map +1 -0
  48. package/dist/{chunk-2B3MFHBQ.js → chunk-FK42ZGGV.js} +21 -21
  49. package/dist/chunk-FK42ZGGV.js.map +1 -0
  50. package/dist/{chunk-XISSLIMH.js → chunk-FTTNGGLA.js} +2 -2
  51. package/dist/{chunk-XISSLIMH.js.map → chunk-FTTNGGLA.js.map} +1 -1
  52. package/dist/chunk-G5YKBPBF.js +74 -0
  53. package/dist/chunk-G5YKBPBF.js.map +1 -0
  54. package/dist/{chunk-KB5T5LWM.js → chunk-IJCT5IJD.js} +3 -3
  55. package/dist/{chunk-KB5T5LWM.js.map → chunk-IJCT5IJD.js.map} +1 -1
  56. package/dist/{chunk-44TULYGG.js → chunk-J6FBA7PH.js} +3 -3
  57. package/dist/{chunk-44TULYGG.js.map → chunk-J6FBA7PH.js.map} +1 -1
  58. package/dist/chunk-JAMLFMDS.js +12 -0
  59. package/dist/chunk-JAMLFMDS.js.map +1 -0
  60. package/dist/{chunk-C6H7AMO4.js → chunk-LNFF2RBD.js} +9 -9
  61. package/dist/chunk-LNFF2RBD.js.map +1 -0
  62. package/dist/{chunk-QBZMY2AF.js → chunk-M62CHO3N.js} +18 -18
  63. package/dist/chunk-M62CHO3N.js.map +1 -0
  64. package/dist/{chunk-5Q4FABZQ.js → chunk-NUXW6NPP.js} +8 -8
  65. package/dist/chunk-NUXW6NPP.js.map +1 -0
  66. package/dist/{chunk-ZJFSZFPI.js → chunk-NWSCYLPT.js} +77 -18
  67. package/dist/chunk-NWSCYLPT.js.map +1 -0
  68. package/dist/{chunk-63FW5ZUA.js → chunk-P3ZL6FGR.js} +7 -7
  69. package/dist/chunk-P3ZL6FGR.js.map +1 -0
  70. package/dist/chunk-P64MFQXZ.js +191 -0
  71. package/dist/chunk-P64MFQXZ.js.map +1 -0
  72. package/dist/chunk-PLIYF6KI.js +52 -0
  73. package/dist/chunk-PLIYF6KI.js.map +1 -0
  74. package/dist/chunk-PSR47JRA.js +26 -0
  75. package/dist/chunk-PSR47JRA.js.map +1 -0
  76. package/dist/{chunk-QLCDAFS7.js → chunk-Q4I4SFBD.js} +3 -3
  77. package/dist/chunk-Q4I4SFBD.js.map +1 -0
  78. package/dist/{chunk-SBJV3K4G.js → chunk-QJ6P2DW6.js} +28 -28
  79. package/dist/chunk-QJ6P2DW6.js.map +1 -0
  80. package/dist/{chunk-IP6TMT3D.js → chunk-R7QFP6EL.js} +3 -3
  81. package/dist/chunk-R7QFP6EL.js.map +1 -0
  82. package/dist/{chunk-WZWR2ELB.js → chunk-SEBU6JCK.js} +6 -6
  83. package/dist/chunk-SEBU6JCK.js.map +1 -0
  84. package/dist/{chunk-7OBL3H5P.js → chunk-SEFK4NZV.js} +3 -3
  85. package/dist/chunk-SEFK4NZV.js.map +1 -0
  86. package/dist/chunk-SRIT4NFQ.js +67 -0
  87. package/dist/chunk-SRIT4NFQ.js.map +1 -0
  88. package/dist/chunk-SY62XJFA.js +19 -0
  89. package/dist/chunk-SY62XJFA.js.map +1 -0
  90. package/dist/{chunk-SVXDWPLO.js → chunk-THD3YOOZ.js} +17 -17
  91. package/dist/chunk-THD3YOOZ.js.map +1 -0
  92. package/dist/chunk-VXFYETRK.js +58 -0
  93. package/dist/chunk-VXFYETRK.js.map +1 -0
  94. package/dist/chunk-XBX6QMR5.js +58 -0
  95. package/dist/chunk-XBX6QMR5.js.map +1 -0
  96. package/dist/{chunk-SCUT2ZCD.js → chunk-XCGZPFT7.js} +17 -17
  97. package/dist/chunk-XCGZPFT7.js.map +1 -0
  98. package/dist/chunk-XMK3T4TA.js +73 -0
  99. package/dist/chunk-XMK3T4TA.js.map +1 -0
  100. package/dist/chunk-YJ6MPS6U.js +226 -0
  101. package/dist/chunk-YJ6MPS6U.js.map +1 -0
  102. package/dist/chunk-YRYQXU3G.js +38 -0
  103. package/dist/chunk-YRYQXU3G.js.map +1 -0
  104. package/dist/{chunk-5J7BP5DI.js → chunk-YWFPMND7.js} +10 -10
  105. package/dist/chunk-YWFPMND7.js.map +1 -0
  106. package/dist/chunk-YYEGAFUE.js +94 -0
  107. package/dist/chunk-YYEGAFUE.js.map +1 -0
  108. package/dist/{chunk-TKA4LOMT.js → chunk-ZZ7H7VVX.js} +8 -8
  109. package/dist/chunk-ZZ7H7VVX.js.map +1 -0
  110. package/dist/components/data/index.d.ts +194 -211
  111. package/dist/components/data/index.js +362 -1381
  112. package/dist/components/data/index.js.map +1 -1
  113. package/dist/components/layout/dashboard-page.d.ts +4 -0
  114. package/dist/components/layout/dashboard-page.js +5 -0
  115. package/dist/components/layout/dashboard-page.js.map +1 -0
  116. package/dist/components/layout/detail-page.d.ts +91 -0
  117. package/dist/components/layout/detail-page.js +6 -0
  118. package/dist/components/layout/detail-page.js.map +1 -0
  119. package/dist/components/layout/form-page.d.ts +82 -0
  120. package/dist/components/layout/form-page.js +5 -0
  121. package/dist/components/layout/form-page.js.map +1 -0
  122. package/dist/components/layout/index.d.ts +15 -0
  123. package/dist/components/layout/index.js +21 -0
  124. package/dist/components/layout/index.js.map +1 -0
  125. package/dist/components/layout/settings-page.d.ts +114 -0
  126. package/dist/components/layout/settings-page.js +6 -0
  127. package/dist/components/layout/settings-page.js.map +1 -0
  128. package/dist/components/patterns/empty-state.d.ts +74 -0
  129. package/dist/components/{ui → patterns}/empty-state.js +2 -1
  130. package/dist/components/{ui → patterns}/form-field.d.ts +2 -2
  131. package/dist/components/{ui → patterns}/form-field.js +2 -2
  132. package/dist/components/patterns/index.d.ts +18 -0
  133. package/dist/components/patterns/index.js +18 -0
  134. package/dist/components/patterns/index.js.map +1 -0
  135. package/dist/components/patterns/page-loading.d.ts +25 -0
  136. package/dist/components/patterns/page-loading.js +5 -0
  137. package/dist/components/patterns/page-loading.js.map +1 -0
  138. package/dist/components/patterns/settings-row.d.ts +42 -0
  139. package/dist/components/patterns/settings-row.js +6 -0
  140. package/dist/components/patterns/settings-row.js.map +1 -0
  141. package/dist/components/patterns/status-badge.d.ts +38 -0
  142. package/dist/components/patterns/status-badge.js +4 -0
  143. package/dist/components/patterns/status-badge.js.map +1 -0
  144. package/dist/components/patterns/toggle-row.d.ts +36 -0
  145. package/dist/components/patterns/toggle-row.js +8 -0
  146. package/dist/components/patterns/toggle-row.js.map +1 -0
  147. package/dist/components/patterns/user-avatar.d.ts +34 -0
  148. package/dist/components/patterns/user-avatar.js +5 -0
  149. package/dist/components/patterns/user-avatar.js.map +1 -0
  150. package/dist/components/ui/accessible-icon.d.ts +48 -0
  151. package/dist/components/ui/accessible-icon.js +3 -0
  152. package/dist/components/ui/accessible-icon.js.map +1 -0
  153. package/dist/components/ui/accordion.d.ts +5 -5
  154. package/dist/components/ui/accordion.js +1 -1
  155. package/dist/components/ui/alert-dialog.d.ts +10 -10
  156. package/dist/components/ui/alert-dialog.js +1 -1
  157. package/dist/components/ui/aspect-ratio.d.ts +2 -2
  158. package/dist/components/ui/aspect-ratio.js +1 -1
  159. package/dist/components/ui/avatar.d.ts +4 -4
  160. package/dist/components/ui/avatar.js +1 -1
  161. package/dist/components/ui/button.d.ts +73 -8
  162. package/dist/components/ui/button.js +1 -1
  163. package/dist/components/ui/calendar.js +2 -2
  164. package/dist/components/ui/carousel.d.ts +4 -3
  165. package/dist/components/ui/carousel.js +2 -2
  166. package/dist/components/ui/checkbox.d.ts +2 -2
  167. package/dist/components/ui/checkbox.js +1 -1
  168. package/dist/components/ui/collapsible.d.ts +4 -4
  169. package/dist/components/ui/collapsible.js +1 -1
  170. package/dist/components/ui/command-palette.js +3 -3
  171. package/dist/components/ui/command.d.ts +9 -9
  172. package/dist/components/ui/command.js +2 -2
  173. package/dist/components/ui/context-menu.d.ts +15 -15
  174. package/dist/components/ui/context-menu.js +1 -1
  175. package/dist/components/ui/date-picker.js +4 -4
  176. package/dist/components/ui/dialog.d.ts +9 -9
  177. package/dist/components/ui/dialog.js +1 -1
  178. package/dist/components/ui/drawer.d.ts +7 -7
  179. package/dist/components/ui/dropdown-menu.d.ts +15 -15
  180. package/dist/components/ui/dropdown-menu.js +1 -1
  181. package/dist/components/ui/hover-card.d.ts +4 -4
  182. package/dist/components/ui/hover-card.js +1 -1
  183. package/dist/components/ui/input-otp.d.ts +2 -2
  184. package/dist/components/ui/label.d.ts +2 -2
  185. package/dist/components/ui/label.js +1 -1
  186. package/dist/components/ui/menubar.d.ts +16 -16
  187. package/dist/components/ui/menubar.js +1 -1
  188. package/dist/components/ui/navigation-menu.d.ts +9 -9
  189. package/dist/components/ui/navigation-menu.js +1 -1
  190. package/dist/components/ui/pagination.js +2 -2
  191. package/dist/components/ui/password-toggle-field.d.ts +121 -0
  192. package/dist/components/ui/password-toggle-field.js +4 -0
  193. package/dist/components/ui/password-toggle-field.js.map +1 -0
  194. package/dist/components/ui/popover.d.ts +4 -4
  195. package/dist/components/ui/popover.js +1 -1
  196. package/dist/components/ui/radio-group.d.ts +3 -3
  197. package/dist/components/ui/radio-group.js +1 -1
  198. package/dist/components/ui/resizable.d.ts +1 -1
  199. package/dist/components/ui/scroll-area.d.ts +60 -3
  200. package/dist/components/ui/scroll-area.js +1 -1
  201. package/dist/components/ui/select.d.ts +9 -9
  202. package/dist/components/ui/select.js +1 -1
  203. package/dist/components/ui/sheet.d.ts +11 -9
  204. package/dist/components/ui/sheet.js +1 -1
  205. package/dist/components/ui/slider.d.ts +2 -2
  206. package/dist/components/ui/slider.js +1 -1
  207. package/dist/components/ui/switch.d.ts +2 -2
  208. package/dist/components/ui/switch.js +1 -1
  209. package/dist/components/ui/tabs.d.ts +29 -14
  210. package/dist/components/ui/tabs.js +1 -1
  211. package/dist/components/ui/toggle-group.d.ts +3 -3
  212. package/dist/components/ui/toggle-group.js +2 -2
  213. package/dist/components/ui/toggle.d.ts +2 -2
  214. package/dist/components/ui/toggle.js +1 -1
  215. package/dist/components/ui/toolbar.d.ts +104 -0
  216. package/dist/components/ui/toolbar.js +6 -0
  217. package/dist/components/ui/toolbar.js.map +1 -0
  218. package/dist/components/ui/tooltip.d.ts +46 -4
  219. package/dist/components/ui/tooltip.js +1 -1
  220. package/dist/components/ui/visually-hidden.d.ts +48 -0
  221. package/dist/components/ui/visually-hidden.js +4 -0
  222. package/dist/components/ui/visually-hidden.js.map +1 -0
  223. package/dist/dashboard-page-C0TihRl5.d.ts +107 -0
  224. package/dist/hooks/index.d.ts +2 -2
  225. package/dist/hooks/index.js +6 -6
  226. package/dist/hooks/use-keyboard-navigation.d.ts +58 -2
  227. package/dist/hooks/use-keyboard-navigation.js +1 -1
  228. package/dist/hooks/use-resource-page.d.ts +2 -2
  229. package/dist/hooks/use-resource-page.js +2 -2
  230. package/dist/hooks/use-view-preference.d.ts +1 -1
  231. package/dist/hooks/use-view-preference.js +1 -1
  232. package/dist/index.d.ts +23 -25
  233. package/dist/index.js +54 -38
  234. package/dist/spacing-BS6qECD4.d.ts +191 -0
  235. package/dist/split-Dk9f4Mi0.d.ts +57 -0
  236. package/dist/{use-resource-page-BXBv_Xah.d.ts → use-resource-page-CPs6wjKt.d.ts} +1 -1
  237. package/dist/view-settings--KXNT_jE.d.ts +12 -0
  238. package/package.json +73 -32
  239. package/dist/chunk-2B3MFHBQ.js.map +0 -1
  240. package/dist/chunk-2MXMWBDV.js.map +0 -1
  241. package/dist/chunk-4V4H2ZM7.js.map +0 -1
  242. package/dist/chunk-5J7BP5DI.js.map +0 -1
  243. package/dist/chunk-5Q4FABZQ.js.map +0 -1
  244. package/dist/chunk-63FW5ZUA.js.map +0 -1
  245. package/dist/chunk-7OBL3H5P.js.map +0 -1
  246. package/dist/chunk-AUECDWP2.js.map +0 -1
  247. package/dist/chunk-C6H7AMO4.js.map +0 -1
  248. package/dist/chunk-EI5EGKHL.js +0 -32
  249. package/dist/chunk-EI5EGKHL.js.map +0 -1
  250. package/dist/chunk-IP6TMT3D.js.map +0 -1
  251. package/dist/chunk-JJ4NOMMI.js +0 -73
  252. package/dist/chunk-JJ4NOMMI.js.map +0 -1
  253. package/dist/chunk-JXI2MYL3.js.map +0 -1
  254. package/dist/chunk-KBS4BHME.js +0 -11
  255. package/dist/chunk-KBS4BHME.js.map +0 -1
  256. package/dist/chunk-LRXDZBV2.js +0 -26
  257. package/dist/chunk-LRXDZBV2.js.map +0 -1
  258. package/dist/chunk-NJMO3VPA.js.map +0 -1
  259. package/dist/chunk-OCTV3S4I.js +0 -29
  260. package/dist/chunk-OCTV3S4I.js.map +0 -1
  261. package/dist/chunk-QBZMY2AF.js.map +0 -1
  262. package/dist/chunk-QLCDAFS7.js.map +0 -1
  263. package/dist/chunk-SBJV3K4G.js.map +0 -1
  264. package/dist/chunk-SCUT2ZCD.js.map +0 -1
  265. package/dist/chunk-SVXDWPLO.js.map +0 -1
  266. package/dist/chunk-T465NJRL.js +0 -134
  267. package/dist/chunk-T465NJRL.js.map +0 -1
  268. package/dist/chunk-T5VX5DYO.js.map +0 -1
  269. package/dist/chunk-TKA4LOMT.js.map +0 -1
  270. package/dist/chunk-UGHQFQOE.js.map +0 -1
  271. package/dist/chunk-WALXVR6V.js +0 -9
  272. package/dist/chunk-WALXVR6V.js.map +0 -1
  273. package/dist/chunk-WZWR2ELB.js.map +0 -1
  274. package/dist/chunk-YZEA5TGK.js.map +0 -1
  275. package/dist/chunk-ZDAT5JDH.js.map +0 -1
  276. package/dist/chunk-ZJFSZFPI.js.map +0 -1
  277. package/dist/components/ui/empty-state.d.ts +0 -22
  278. /package/dist/components/{ui → patterns}/empty-state.js.map +0 -0
  279. /package/dist/components/{ui → patterns}/form-field.js.map +0 -0
  280. /package/dist/{ViewToggle-DDe_Luj2.d.ts → view-toggle-DDe_Luj2.d.ts} +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-Q4I4SFBD.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 } 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,
@@ -2773,6 +2436,9 @@ function ResourcePage(props) {
2773
2436
  formSteps,
2774
2437
  formLayout: formLayoutProp,
2775
2438
  bulkActions,
2439
+ renderCard: renderCardProp,
2440
+ gridCols: gridColsProp,
2441
+ keyboardNavigation = true,
2776
2442
  toolbar,
2777
2443
  emptyState,
2778
2444
  resourceOptions,
@@ -2898,6 +2564,35 @@ function ResourcePage(props) {
2898
2564
  }
2899
2565
  page.handleRowClick(item);
2900
2566
  };
2567
+ const hasBulkSelect = !!bulkActions && bulkActions.length > 0;
2568
+ const overlayOpen = page.detailOpen || formOpen;
2569
+ const keyboardNav = useKeyboardNavigation({
2570
+ itemCount: page.data.length,
2571
+ enabled: keyboardNavigation && page.viewMode !== "table",
2572
+ overlayOpen,
2573
+ onOpen: (index) => {
2574
+ const item = page.data[index];
2575
+ if (item) handleRowClick(item);
2576
+ },
2577
+ onToggleSelect: hasBulkSelect ? (index) => {
2578
+ const item = page.data[index];
2579
+ if (item) {
2580
+ handleToggleSelect(
2581
+ String(item.id ?? "")
2582
+ );
2583
+ }
2584
+ } : void 0,
2585
+ // Supplied only while there is an open detail to close — the hook
2586
+ // suppresses Backspace's default whenever this is present, so leaving it
2587
+ // wired up permanently would swallow the browser/app's Backspace on
2588
+ // every grid/list page (including detail="none" and detail="route",
2589
+ // which never open one). `detailOpen` is only ever true for the panel
2590
+ // and modal modes, so it is the precise precondition here.
2591
+ onBack: page.detailOpen ? () => {
2592
+ page.setDetailOpen(false);
2593
+ page.setSelectedItem(null);
2594
+ } : void 0
2595
+ });
2901
2596
  const showCreateButton = onCreate || useBuiltInCreate;
2902
2597
  const createButton = showCreateButton ? /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: handleCreate, children: [
2903
2598
  /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4 mr-1" }),
@@ -2908,16 +2603,84 @@ function ResourcePage(props) {
2908
2603
  createButton
2909
2604
  ] }) : void 0;
2910
2605
  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;
2920
- return /* @__PURE__ */ jsxs(
2606
+ const cardActionsFor = (item) => ({
2607
+ ...actions,
2608
+ open: () => handleRowClick(item)
2609
+ });
2610
+ const rowActivationProps = (item, label, testId) => ({
2611
+ role: "button",
2612
+ tabIndex: 0,
2613
+ "aria-label": label || void 0,
2614
+ "data-testid": testId,
2615
+ onClick: () => handleRowClick(item),
2616
+ onKeyDown: (e) => {
2617
+ if (e.key === "Enter" || e.key === " ") {
2618
+ e.preventDefault();
2619
+ handleRowClick(item);
2620
+ }
2621
+ }
2622
+ });
2623
+ const renderDefaultCard = (item) => {
2624
+ const cols = page.columns;
2625
+ const titleCol = cols[0];
2626
+ const titleVal = titleCol ? String(item[titleCol.key] ?? "") : "";
2627
+ const badgeCol = cols[1];
2628
+ const badgeVal = badgeCol ? item[badgeCol.key] : null;
2629
+ const priceCol = cols.find(
2630
+ (c, i) => i > 1 && /price|cost|amount|total/i.test(c.key)
2631
+ );
2632
+ const priceVal = priceCol ? item[priceCol.key] : null;
2633
+ const metaCols = cols.filter(
2634
+ (c) => c !== titleCol && c !== badgeCol && c !== priceCol
2635
+ );
2636
+ return /* @__PURE__ */ jsxs(
2637
+ Card,
2638
+ {
2639
+ ...rowActivationProps(item, titleVal, "resource-card"),
2640
+ 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",
2641
+ children: [
2642
+ /* @__PURE__ */ jsx(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle, { className: "text-base truncate", children: titleVal }) }),
2643
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2644
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
2645
+ badgeCol && /* @__PURE__ */ jsx("span", { children: badgeCol.render ? badgeCol.render(item, badgeVal, actions) : badgeVal != null ? String(badgeVal) : "\u2014" }),
2646
+ priceVal != null && /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: String(priceVal) })
2647
+ ] }),
2648
+ metaCols.map((col) => {
2649
+ const val = item[col.key];
2650
+ return /* @__PURE__ */ jsx(
2651
+ "p",
2652
+ {
2653
+ className: "text-xs text-muted-foreground truncate",
2654
+ children: col.render ? col.render(item, val, actions) : val != null ? String(val) : "\u2014"
2655
+ },
2656
+ col.key
2657
+ );
2658
+ })
2659
+ ] }) })
2660
+ ]
2661
+ }
2662
+ );
2663
+ };
2664
+ const renderDefaultListItem = (item) => {
2665
+ const cols = page.columns;
2666
+ const titleCol = cols[0];
2667
+ const titleVal = titleCol ? String(item[titleCol.key] ?? "") : "";
2668
+ return /* @__PURE__ */ jsxs(
2669
+ "div",
2670
+ {
2671
+ ...rowActivationProps(item, titleVal, "resource-list-row"),
2672
+ 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",
2673
+ children: [
2674
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-sm truncate", children: titleVal }),
2675
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: cols.slice(1, 4).map((col) => {
2676
+ const val = item[col.key];
2677
+ return /* @__PURE__ */ jsx("span", { children: col.render ? col.render(item, val, actions) : val != null ? String(val) : "\u2014" }, col.key);
2678
+ }) })
2679
+ ]
2680
+ }
2681
+ );
2682
+ };
2683
+ return /* @__PURE__ */ jsxs(
2921
2684
  DataPageShell,
2922
2685
  {
2923
2686
  title,
@@ -2975,7 +2738,10 @@ function ResourcePage(props) {
2975
2738
  {
2976
2739
  items: page.data,
2977
2740
  viewMode: page.viewMode,
2741
+ gridCols: gridColsProp,
2978
2742
  getKey: (item) => String(item.id ?? ""),
2743
+ focusedIndex: keyboardNav.focusedIndex,
2744
+ keyboardContainerRef: keyboardNav.containerRef,
2979
2745
  renderTable: (items) => /* @__PURE__ */ jsx(
2980
2746
  DataTable,
2981
2747
  {
@@ -2991,69 +2757,11 @@ function ResourcePage(props) {
2991
2757
  pageSize: settings.pageSize
2992
2758
  }
2993
2759
  ),
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",
3041
- {
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
- ]
3051
- }
3052
- );
3053
- }
2760
+ renderCard: (item, index) => renderCardProp ? renderCardProp(item, index, cardActionsFor(item)) : renderDefaultCard(item),
2761
+ renderListItem: (item, index) => renderCardProp ? renderCardProp(item, index, cardActionsFor(item)) : renderDefaultListItem(item)
3054
2762
  }
3055
2763
  ),
3056
- detail === "panel" && /* @__PURE__ */ jsx(
2764
+ (detail === "panel" || detail === "modal") && /* @__PURE__ */ jsx(
3057
2765
  DetailPanel,
3058
2766
  {
3059
2767
  item: page.selectedItem,
@@ -3068,48 +2776,10 @@ function ResourcePage(props) {
3068
2776
  onDelete,
3069
2777
  fields: detailFields,
3070
2778
  width: detailWidth,
2779
+ layout: detail === "modal" ? "dialog" : "sheet",
3071
2780
  children: renderDetailProp ? (item) => renderDetailProp(item) : void 0
3072
2781
  }
3073
2782
  ),
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
2783
  (useBuiltInCreate || useBuiltInEdit) && resourceName && /* @__PURE__ */ jsx(
3114
2784
  ResourceForm,
3115
2785
  {
@@ -3136,769 +2806,80 @@ function ResourcePage(props) {
3136
2806
  }
3137
2807
  );
3138
2808
  }
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
- }
2809
+ function pick(record, field) {
2810
+ const value = field ? record[field] : record.name ?? record.title ?? record.id;
2811
+ return value == null ? "" : String(value);
3164
2812
  }
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
- }
2813
+ function NotFound() {
3189
2814
  return /* @__PURE__ */ jsx(
3190
- Button,
2815
+ EmptyState,
3191
2816
  {
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" })
2817
+ title: "Not found",
2818
+ description: "This record no longer exists, or you don't have access to it."
3201
2819
  }
3202
2820
  );
3203
2821
  }
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
- );
3212
- }
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
- );
2822
+ function isNotFoundError(error) {
2823
+ if (!(error instanceof Error)) return false;
2824
+ if (error.code === "PGRST116") return true;
2825
+ return /\[MockProvider\] Record not found/i.test(error.message) || /switchboard-data-provider: no ".*" row with id/i.test(error.message);
3265
2826
  }
3266
- function NavGroup({
3267
- item,
3268
- collapsed,
3269
- density,
3270
- onNavClick,
3271
- linkComponent,
3272
- activePath,
3273
- isActive
2827
+ function ResourceDetailPage({
2828
+ resource,
2829
+ id,
2830
+ titleField,
2831
+ subtitleField,
2832
+ select,
2833
+ breadcrumb,
2834
+ actions,
2835
+ sidebar,
2836
+ children,
2837
+ className,
2838
+ testID
3274
2839
  }) {
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
- );
2840
+ const { data, isLoading, isError, error } = useOne(resource, id, {
2841
+ select,
2842
+ // `useOne` defaults to `enabled: !!id`, which is wrong for a numeric
2843
+ // primary key: id `0` is falsy, so the query would never run and the page
2844
+ // would report a record that exists as missing, without ever asking the
2845
+ // provider. `Identifier` is `string | number`, so this is reachable.
2846
+ // Presence is the question, not truthiness.
2847
+ enabled: id != null
2848
+ });
2849
+ if (id == null || isLoading) {
2850
+ return /* @__PURE__ */ jsx(PageLoading, { testID: testID ? `${testID}-loading` : void 0 });
3296
2851
  }
3297
- if (collapsed) {
3298
- const target = item.to ? { ...item, children: void 0 } : { ...children[0], children: void 0 };
2852
+ if (isError) {
2853
+ if (isNotFoundError(error)) return /* @__PURE__ */ jsx(NotFound, {});
3299
2854
  return /* @__PURE__ */ jsx(
3300
- NavLinkRow,
2855
+ EmptyState,
3301
2856
  {
3302
- item: target,
3303
- collapsed: true,
3304
- density,
3305
- onNavClick,
3306
- linkComponent,
3307
- activePath,
3308
- isActive
2857
+ icon: AlertTriangle,
2858
+ title: "Couldn't load this record",
2859
+ description: error instanceof Error ? error.message : void 0
3309
2860
  }
3310
2861
  );
3311
2862
  }
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",
2863
+ if (!data) return /* @__PURE__ */ jsx(NotFound, {});
2864
+ return /* @__PURE__ */ jsx(
2865
+ DetailPage,
3779
2866
  {
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
- ]
2867
+ className,
2868
+ testID,
2869
+ breadcrumb,
2870
+ title: pick(data, titleField),
2871
+ subtitle: subtitleField ? pick(data, subtitleField) : void 0,
2872
+ actions: actions?.(data),
2873
+ sidebar: sidebar?.(data),
2874
+ children: children(data)
3898
2875
  }
3899
2876
  );
3900
2877
  }
3901
2878
 
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 };
2879
+ // src/components/data/index.ts
2880
+ var AppShell2 = AppShell;
2881
+ var PageHeader2 = PageHeader;
2882
+
2883
+ 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
2884
  //# sourceMappingURL=index.js.map
3904
2885
  //# sourceMappingURL=index.js.map