@marktiderman/genesis-ui 1.2.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 (295) 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-AI6CCNWK.js → chunk-6NPEMLBA.js} +2 -2
  17. package/dist/chunk-6NPEMLBA.js.map +1 -0
  18. package/dist/chunk-6Q6CLLJ4.js +9 -0
  19. package/dist/chunk-6Q6CLLJ4.js.map +1 -0
  20. package/dist/{chunk-UGHQFQOE.js → chunk-6TVMQH6M.js} +28 -28
  21. package/dist/chunk-6TVMQH6M.js.map +1 -0
  22. package/dist/{chunk-UKSBLFEK.js → chunk-6XPMU65K.js} +5 -5
  23. package/dist/{chunk-UKSBLFEK.js.map → chunk-6XPMU65K.js.map} +1 -1
  24. package/dist/{chunk-YZEA5TGK.js → chunk-7XMZVISM.js} +16 -16
  25. package/dist/chunk-7XMZVISM.js.map +1 -0
  26. package/dist/chunk-A7AHVDG7.js +52 -0
  27. package/dist/chunk-A7AHVDG7.js.map +1 -0
  28. package/dist/chunk-AFZ76Q26.js +84 -0
  29. package/dist/chunk-AFZ76Q26.js.map +1 -0
  30. package/dist/{chunk-4V4H2ZM7.js → chunk-AL5TMEZQ.js} +31 -31
  31. package/dist/chunk-AL5TMEZQ.js.map +1 -0
  32. package/dist/{chunk-O4B5ALO7.js → chunk-AOZGQM6W.js} +3 -3
  33. package/dist/chunk-AOZGQM6W.js.map +1 -0
  34. package/dist/chunk-ARJI2JUV.js +122 -0
  35. package/dist/chunk-ARJI2JUV.js.map +1 -0
  36. package/dist/chunk-AVFZ4P4B.js +25 -0
  37. package/dist/chunk-AVFZ4P4B.js.map +1 -0
  38. package/dist/chunk-B2GIBUQD.js +11 -0
  39. package/dist/chunk-B2GIBUQD.js.map +1 -0
  40. package/dist/{chunk-JXI2MYL3.js → chunk-C46ITXE4.js} +5 -5
  41. package/dist/chunk-C46ITXE4.js.map +1 -0
  42. package/dist/chunk-CNTWQSUT.js +65 -0
  43. package/dist/chunk-CNTWQSUT.js.map +1 -0
  44. package/dist/chunk-D2LIXA2Y.js +65 -0
  45. package/dist/chunk-D2LIXA2Y.js.map +1 -0
  46. package/dist/{chunk-FCGFG4RV.js → chunk-D4JTQYP5.js} +3 -3
  47. package/dist/{chunk-FCGFG4RV.js.map → chunk-D4JTQYP5.js.map} +1 -1
  48. package/dist/{chunk-T5VX5DYO.js → chunk-F47U5KNW.js} +5 -5
  49. package/dist/chunk-F47U5KNW.js.map +1 -0
  50. package/dist/chunk-FEJAZFP3.js +1100 -0
  51. package/dist/chunk-FEJAZFP3.js.map +1 -0
  52. package/dist/{chunk-2B3MFHBQ.js → chunk-FK42ZGGV.js} +21 -21
  53. package/dist/chunk-FK42ZGGV.js.map +1 -0
  54. package/dist/{chunk-XISSLIMH.js → chunk-FTTNGGLA.js} +2 -2
  55. package/dist/{chunk-XISSLIMH.js.map → chunk-FTTNGGLA.js.map} +1 -1
  56. package/dist/chunk-G5YKBPBF.js +74 -0
  57. package/dist/chunk-G5YKBPBF.js.map +1 -0
  58. package/dist/chunk-GCQFVMPI.js +56 -0
  59. package/dist/chunk-GCQFVMPI.js.map +1 -0
  60. package/dist/{chunk-LDPPIBIJ.js → chunk-IJCT5IJD.js} +3 -3
  61. package/dist/{chunk-LDPPIBIJ.js.map → chunk-IJCT5IJD.js.map} +1 -1
  62. package/dist/{chunk-DGRHPJ3M.js → chunk-J6FBA7PH.js} +3 -3
  63. package/dist/{chunk-DGRHPJ3M.js.map → chunk-J6FBA7PH.js.map} +1 -1
  64. package/dist/chunk-JAMLFMDS.js +12 -0
  65. package/dist/chunk-JAMLFMDS.js.map +1 -0
  66. package/dist/{chunk-C6H7AMO4.js → chunk-LNFF2RBD.js} +9 -9
  67. package/dist/chunk-LNFF2RBD.js.map +1 -0
  68. package/dist/{chunk-QBZMY2AF.js → chunk-M62CHO3N.js} +18 -18
  69. package/dist/chunk-M62CHO3N.js.map +1 -0
  70. package/dist/{chunk-5Q4FABZQ.js → chunk-NUXW6NPP.js} +8 -8
  71. package/dist/chunk-NUXW6NPP.js.map +1 -0
  72. package/dist/{chunk-I6JQHP6D.js → chunk-NWSCYLPT.js} +78 -17
  73. package/dist/chunk-NWSCYLPT.js.map +1 -0
  74. package/dist/{chunk-63FW5ZUA.js → chunk-P3ZL6FGR.js} +7 -7
  75. package/dist/chunk-P3ZL6FGR.js.map +1 -0
  76. package/dist/chunk-P64MFQXZ.js +191 -0
  77. package/dist/chunk-P64MFQXZ.js.map +1 -0
  78. package/dist/chunk-PLIYF6KI.js +52 -0
  79. package/dist/chunk-PLIYF6KI.js.map +1 -0
  80. package/dist/chunk-PSR47JRA.js +26 -0
  81. package/dist/chunk-PSR47JRA.js.map +1 -0
  82. package/dist/{chunk-QLCDAFS7.js → chunk-Q4I4SFBD.js} +3 -3
  83. package/dist/chunk-Q4I4SFBD.js.map +1 -0
  84. package/dist/{chunk-SBJV3K4G.js → chunk-QJ6P2DW6.js} +28 -28
  85. package/dist/chunk-QJ6P2DW6.js.map +1 -0
  86. package/dist/{chunk-IP6TMT3D.js → chunk-R7QFP6EL.js} +3 -3
  87. package/dist/chunk-R7QFP6EL.js.map +1 -0
  88. package/dist/{chunk-WZWR2ELB.js → chunk-SEBU6JCK.js} +6 -6
  89. package/dist/chunk-SEBU6JCK.js.map +1 -0
  90. package/dist/{chunk-7OBL3H5P.js → chunk-SEFK4NZV.js} +3 -3
  91. package/dist/chunk-SEFK4NZV.js.map +1 -0
  92. package/dist/chunk-SRIT4NFQ.js +67 -0
  93. package/dist/chunk-SRIT4NFQ.js.map +1 -0
  94. package/dist/chunk-SY62XJFA.js +19 -0
  95. package/dist/chunk-SY62XJFA.js.map +1 -0
  96. package/dist/{chunk-SVXDWPLO.js → chunk-THD3YOOZ.js} +17 -17
  97. package/dist/chunk-THD3YOOZ.js.map +1 -0
  98. package/dist/chunk-VXFYETRK.js +58 -0
  99. package/dist/chunk-VXFYETRK.js.map +1 -0
  100. package/dist/chunk-XBX6QMR5.js +58 -0
  101. package/dist/chunk-XBX6QMR5.js.map +1 -0
  102. package/dist/{chunk-SCUT2ZCD.js → chunk-XCGZPFT7.js} +17 -17
  103. package/dist/chunk-XCGZPFT7.js.map +1 -0
  104. package/dist/chunk-XMK3T4TA.js +73 -0
  105. package/dist/chunk-XMK3T4TA.js.map +1 -0
  106. package/dist/chunk-YJ6MPS6U.js +226 -0
  107. package/dist/chunk-YJ6MPS6U.js.map +1 -0
  108. package/dist/chunk-YRYQXU3G.js +38 -0
  109. package/dist/chunk-YRYQXU3G.js.map +1 -0
  110. package/dist/{chunk-5J7BP5DI.js → chunk-YWFPMND7.js} +10 -10
  111. package/dist/chunk-YWFPMND7.js.map +1 -0
  112. package/dist/chunk-YYEGAFUE.js +94 -0
  113. package/dist/chunk-YYEGAFUE.js.map +1 -0
  114. package/dist/{chunk-TKA4LOMT.js → chunk-ZZ7H7VVX.js} +8 -8
  115. package/dist/chunk-ZZ7H7VVX.js.map +1 -0
  116. package/dist/components/data/index.d.ts +223 -74
  117. package/dist/components/data/index.js +504 -539
  118. package/dist/components/data/index.js.map +1 -1
  119. package/dist/components/layout/dashboard-page.d.ts +4 -0
  120. package/dist/components/layout/dashboard-page.js +5 -0
  121. package/dist/components/layout/dashboard-page.js.map +1 -0
  122. package/dist/components/layout/detail-page.d.ts +91 -0
  123. package/dist/components/layout/detail-page.js +6 -0
  124. package/dist/components/layout/detail-page.js.map +1 -0
  125. package/dist/components/layout/form-page.d.ts +82 -0
  126. package/dist/components/layout/form-page.js +5 -0
  127. package/dist/components/layout/form-page.js.map +1 -0
  128. package/dist/components/layout/index.d.ts +15 -0
  129. package/dist/components/layout/index.js +21 -0
  130. package/dist/components/layout/index.js.map +1 -0
  131. package/dist/components/layout/settings-page.d.ts +114 -0
  132. package/dist/components/layout/settings-page.js +6 -0
  133. package/dist/components/layout/settings-page.js.map +1 -0
  134. package/dist/components/patterns/empty-state.d.ts +74 -0
  135. package/dist/components/{ui → patterns}/empty-state.js +2 -1
  136. package/dist/components/{ui → patterns}/form-field.d.ts +2 -2
  137. package/dist/components/{ui → patterns}/form-field.js +2 -2
  138. package/dist/components/patterns/index.d.ts +18 -0
  139. package/dist/components/patterns/index.js +18 -0
  140. package/dist/components/patterns/index.js.map +1 -0
  141. package/dist/components/patterns/page-loading.d.ts +25 -0
  142. package/dist/components/patterns/page-loading.js +5 -0
  143. package/dist/components/patterns/page-loading.js.map +1 -0
  144. package/dist/components/patterns/settings-row.d.ts +42 -0
  145. package/dist/components/patterns/settings-row.js +6 -0
  146. package/dist/components/patterns/settings-row.js.map +1 -0
  147. package/dist/components/patterns/status-badge.d.ts +38 -0
  148. package/dist/components/patterns/status-badge.js +4 -0
  149. package/dist/components/patterns/status-badge.js.map +1 -0
  150. package/dist/components/patterns/toggle-row.d.ts +36 -0
  151. package/dist/components/patterns/toggle-row.js +8 -0
  152. package/dist/components/patterns/toggle-row.js.map +1 -0
  153. package/dist/components/patterns/user-avatar.d.ts +34 -0
  154. package/dist/components/patterns/user-avatar.js +5 -0
  155. package/dist/components/patterns/user-avatar.js.map +1 -0
  156. package/dist/components/ui/accessible-icon.d.ts +48 -0
  157. package/dist/components/ui/accessible-icon.js +3 -0
  158. package/dist/components/ui/accessible-icon.js.map +1 -0
  159. package/dist/components/ui/accordion.d.ts +5 -5
  160. package/dist/components/ui/accordion.js +1 -1
  161. package/dist/components/ui/alert-dialog.d.ts +10 -10
  162. package/dist/components/ui/alert-dialog.js +1 -1
  163. package/dist/components/ui/aspect-ratio.d.ts +2 -2
  164. package/dist/components/ui/aspect-ratio.js +1 -1
  165. package/dist/components/ui/avatar.d.ts +4 -4
  166. package/dist/components/ui/avatar.js +1 -1
  167. package/dist/components/ui/button.d.ts +75 -8
  168. package/dist/components/ui/button.js +1 -1
  169. package/dist/components/ui/calendar.js +2 -2
  170. package/dist/components/ui/carousel.d.ts +4 -3
  171. package/dist/components/ui/carousel.js +2 -2
  172. package/dist/components/ui/checkbox.d.ts +2 -2
  173. package/dist/components/ui/checkbox.js +1 -1
  174. package/dist/components/ui/collapsible.d.ts +4 -4
  175. package/dist/components/ui/collapsible.js +1 -1
  176. package/dist/components/ui/command-palette.d.ts +30 -0
  177. package/dist/components/ui/command-palette.js +6 -0
  178. package/dist/components/ui/command-palette.js.map +1 -0
  179. package/dist/components/ui/command.d.ts +9 -9
  180. package/dist/components/ui/command.js +2 -2
  181. package/dist/components/ui/context-menu.d.ts +15 -15
  182. package/dist/components/ui/context-menu.js +1 -1
  183. package/dist/components/ui/date-picker.js +4 -4
  184. package/dist/components/ui/dialog.d.ts +9 -9
  185. package/dist/components/ui/dialog.js +1 -1
  186. package/dist/components/ui/drawer.d.ts +7 -7
  187. package/dist/components/ui/dropdown-menu.d.ts +15 -15
  188. package/dist/components/ui/dropdown-menu.js +1 -1
  189. package/dist/components/ui/hover-card.d.ts +4 -4
  190. package/dist/components/ui/hover-card.js +1 -1
  191. package/dist/components/ui/input-otp.d.ts +2 -2
  192. package/dist/components/ui/label.d.ts +2 -2
  193. package/dist/components/ui/label.js +1 -1
  194. package/dist/components/ui/menubar.d.ts +16 -16
  195. package/dist/components/ui/menubar.js +1 -1
  196. package/dist/components/ui/navigation-menu.d.ts +9 -9
  197. package/dist/components/ui/navigation-menu.js +1 -1
  198. package/dist/components/ui/pagination.js +2 -2
  199. package/dist/components/ui/password-toggle-field.d.ts +121 -0
  200. package/dist/components/ui/password-toggle-field.js +4 -0
  201. package/dist/components/ui/password-toggle-field.js.map +1 -0
  202. package/dist/components/ui/popover.d.ts +4 -4
  203. package/dist/components/ui/popover.js +1 -1
  204. package/dist/components/ui/radio-group.d.ts +3 -3
  205. package/dist/components/ui/radio-group.js +1 -1
  206. package/dist/components/ui/resizable.d.ts +1 -1
  207. package/dist/components/ui/scroll-area.d.ts +60 -3
  208. package/dist/components/ui/scroll-area.js +1 -1
  209. package/dist/components/ui/select.d.ts +9 -9
  210. package/dist/components/ui/select.js +1 -1
  211. package/dist/components/ui/sheet.d.ts +11 -9
  212. package/dist/components/ui/sheet.js +1 -1
  213. package/dist/components/ui/slider.d.ts +2 -2
  214. package/dist/components/ui/slider.js +1 -1
  215. package/dist/components/ui/switch.d.ts +2 -2
  216. package/dist/components/ui/switch.js +1 -1
  217. package/dist/components/ui/tabs.d.ts +29 -14
  218. package/dist/components/ui/tabs.js +1 -1
  219. package/dist/components/ui/toggle-group.d.ts +3 -3
  220. package/dist/components/ui/toggle-group.js +2 -2
  221. package/dist/components/ui/toggle.d.ts +2 -2
  222. package/dist/components/ui/toggle.js +1 -1
  223. package/dist/components/ui/toolbar.d.ts +104 -0
  224. package/dist/components/ui/toolbar.js +6 -0
  225. package/dist/components/ui/toolbar.js.map +1 -0
  226. package/dist/components/ui/tooltip.d.ts +46 -4
  227. package/dist/components/ui/tooltip.js +1 -1
  228. package/dist/components/ui/visually-hidden.d.ts +48 -0
  229. package/dist/components/ui/visually-hidden.js +4 -0
  230. package/dist/components/ui/visually-hidden.js.map +1 -0
  231. package/dist/dashboard-page-C0TihRl5.d.ts +107 -0
  232. package/dist/hooks/index.d.ts +5 -3
  233. package/dist/hooks/index.js +10 -5
  234. package/dist/hooks/use-data-filters.d.ts +1 -1
  235. package/dist/hooks/use-data-filters.js +2 -2
  236. package/dist/hooks/use-keyboard-navigation.d.ts +58 -2
  237. package/dist/hooks/use-keyboard-navigation.js +1 -1
  238. package/dist/hooks/use-resource-page.d.ts +3 -2
  239. package/dist/hooks/use-resource-page.js +2 -2
  240. package/dist/hooks/use-saved-views.d.ts +28 -0
  241. package/dist/hooks/use-saved-views.js +3 -0
  242. package/dist/hooks/use-saved-views.js.map +1 -0
  243. package/dist/hooks/use-view-preference.d.ts +1 -1
  244. package/dist/hooks/use-view-preference.js +1 -1
  245. package/dist/index.d.ts +25 -26
  246. package/dist/index.js +55 -38
  247. package/dist/{navigation-qI86Ldug.d.ts → navigation-CpOEyUSb.d.ts} +4 -0
  248. package/dist/spacing-BS6qECD4.d.ts +191 -0
  249. package/dist/split-Dk9f4Mi0.d.ts +57 -0
  250. package/dist/{use-resource-page-B-CFcikK.d.ts → use-resource-page-CPs6wjKt.d.ts} +10 -2
  251. package/dist/view-settings--KXNT_jE.d.ts +12 -0
  252. package/package.json +77 -32
  253. package/dist/chunk-2B3MFHBQ.js.map +0 -1
  254. package/dist/chunk-2MXMWBDV.js.map +0 -1
  255. package/dist/chunk-4V4H2ZM7.js.map +0 -1
  256. package/dist/chunk-5J7BP5DI.js.map +0 -1
  257. package/dist/chunk-5Q4FABZQ.js.map +0 -1
  258. package/dist/chunk-63FW5ZUA.js.map +0 -1
  259. package/dist/chunk-7OBL3H5P.js.map +0 -1
  260. package/dist/chunk-AI6CCNWK.js.map +0 -1
  261. package/dist/chunk-C6H7AMO4.js.map +0 -1
  262. package/dist/chunk-EI5EGKHL.js +0 -32
  263. package/dist/chunk-EI5EGKHL.js.map +0 -1
  264. package/dist/chunk-I6JQHP6D.js.map +0 -1
  265. package/dist/chunk-IP6TMT3D.js.map +0 -1
  266. package/dist/chunk-JJ4NOMMI.js +0 -73
  267. package/dist/chunk-JJ4NOMMI.js.map +0 -1
  268. package/dist/chunk-JXI2MYL3.js.map +0 -1
  269. package/dist/chunk-KBS4BHME.js +0 -11
  270. package/dist/chunk-KBS4BHME.js.map +0 -1
  271. package/dist/chunk-LRXDZBV2.js +0 -26
  272. package/dist/chunk-LRXDZBV2.js.map +0 -1
  273. package/dist/chunk-NJMO3VPA.js.map +0 -1
  274. package/dist/chunk-O4B5ALO7.js.map +0 -1
  275. package/dist/chunk-OCTV3S4I.js +0 -29
  276. package/dist/chunk-OCTV3S4I.js.map +0 -1
  277. package/dist/chunk-QBZMY2AF.js.map +0 -1
  278. package/dist/chunk-QLCDAFS7.js.map +0 -1
  279. package/dist/chunk-SBJV3K4G.js.map +0 -1
  280. package/dist/chunk-SCUT2ZCD.js.map +0 -1
  281. package/dist/chunk-SVXDWPLO.js.map +0 -1
  282. package/dist/chunk-T465NJRL.js +0 -134
  283. package/dist/chunk-T465NJRL.js.map +0 -1
  284. package/dist/chunk-T5VX5DYO.js.map +0 -1
  285. package/dist/chunk-TKA4LOMT.js.map +0 -1
  286. package/dist/chunk-UGHQFQOE.js.map +0 -1
  287. package/dist/chunk-WALXVR6V.js +0 -9
  288. package/dist/chunk-WALXVR6V.js.map +0 -1
  289. package/dist/chunk-WZWR2ELB.js.map +0 -1
  290. package/dist/chunk-YZEA5TGK.js.map +0 -1
  291. package/dist/chunk-ZDAT5JDH.js.map +0 -1
  292. package/dist/components/ui/empty-state.d.ts +0 -22
  293. /package/dist/components/{ui → patterns}/empty-state.js.map +0 -0
  294. /package/dist/components/{ui → patterns}/form-field.js.map +0 -0
  295. /package/dist/{ViewToggle-DDe_Luj2.d.ts → view-toggle-DDe_Luj2.d.ts} +0 -0
@@ -1,151 +1,55 @@
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 { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '../../chunk-2B3MFHBQ.js';
7
6
  import { Separator } from '../../chunk-SXSCHWZU.js';
8
- import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription } from '../../chunk-SCUT2ZCD.js';
7
+ import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetDescription } from '../../chunk-XCGZPFT7.js';
9
8
  import { Skeleton } from '../../chunk-QFM4Z2SS.js';
10
9
  import { Progress } from '../../chunk-W5Y3LG5K.js';
11
- import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuCheckboxItem, DropdownMenuSeparator, DropdownMenuItem } from '../../chunk-SBJV3K4G.js';
12
- import { FormField, FormLabel, FormError, FormDescription } from '../../chunk-IP6TMT3D.js';
13
- import '../../chunk-T5VX5DYO.js';
10
+ import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuCheckboxItem, DropdownMenuSeparator, DropdownMenuItem } from '../../chunk-QJ6P2DW6.js';
14
11
  import { Input } from '../../chunk-QSXVL5UB.js';
15
- import { Checkbox } from '../../chunk-WZWR2ELB.js';
16
- import { Command, CommandInput, CommandList, CommandEmpty, CommandGroup, CommandItem, CommandSeparator } from '../../chunk-7OBL3H5P.js';
17
- import { Popover, PopoverTrigger, PopoverContent } from '../../chunk-NJMO3VPA.js';
18
- import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from '../../chunk-YZEA5TGK.js';
19
- import { Badge } from '../../chunk-NPNADZHM.js';
20
- import { Button } from '../../chunk-I6JQHP6D.js';
12
+ import { Checkbox } from '../../chunk-SEBU6JCK.js';
21
13
  import { Card, CardContent, CardHeader, CardTitle } from '../../chunk-FN3FZVUR.js';
14
+ import '../../chunk-B2GIBUQD.js';
15
+ import { Badge } from '../../chunk-NPNADZHM.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';
30
+ export { useSavedViews } from '../../chunk-GCQFVMPI.js';
31
+ import '../../chunk-FTTNGGLA.js';
32
+ import { useViewSettings } from '../../chunk-OZ7ZAYCS.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';
22
36
  import { cn } from '../../chunk-NZ5WBA4R.js';
23
- import { titleCase, useResourcePage } from '../../chunk-QLCDAFS7.js';
24
- import '../../chunk-XISSLIMH.js';
25
- import { DefaultLink, defaultNavigate, isPathActive } from '../../chunk-AI6CCNWK.js';
26
- export { DefaultLink, defaultNavigate, isPathActive } from '../../chunk-AI6CCNWK.js';
37
+ import { DefaultLink, defaultNavigate } from '../../chunk-6NPEMLBA.js';
38
+ export { DefaultLink, defaultNavigate, isPathActive } from '../../chunk-6NPEMLBA.js';
39
+ import { useKeyboardNavigation } from '../../chunk-YJ6MPS6U.js';
27
40
  import { useIsMobile } from '../../chunk-M7AU5DDM.js';
28
- import { Search, LayoutGrid, Table2, List, Settings2, AlertTriangle, RefreshCw, ChevronDown, Check, X, ArrowUpDown, Bookmark, Trash2, BookmarkPlus, Loader2, Columns3, ArrowUp, ArrowDown, ChevronLeft, ChevronRight, ExternalLink, Pencil, ChevronsUpDown, ImageIcon, Upload, AlertCircle, FileIcon, Menu, 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';
29
42
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
30
43
  import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
31
44
  import { formatDistanceToNow, format } from 'date-fns';
32
45
  import { useFieldArray, Controller, useWatch } from 'react-hook-form';
33
- import { useResource, useWizard, useResourceForm, buildArrayItemDefaults } from '@marktiderman/genesis-core/hooks';
46
+ import { useResource, useWizard, useResourceForm, useOne, buildArrayItemDefaults } from '@marktiderman/genesis-core/hooks';
34
47
  import { useDataProvider } from '@marktiderman/genesis-core/provider';
35
48
  import { useDropzone } from 'react-dropzone';
36
49
 
37
- function EmptyState({
38
- icon: Icon = Search,
39
- message = "No items found",
40
- action,
41
- hasFilters = false,
42
- onClearFilters
43
- }) {
44
- return /* @__PURE__ */ jsxs("div", { className: "text-center py-16 animate-fade-in", children: [
45
- /* @__PURE__ */ jsx(Icon, { className: "h-10 w-10 text-muted-foreground/30 mx-auto mb-3" }),
46
- /* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: hasFilters ? "No results match your filters" : message }),
47
- 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 })
48
- ] });
49
- }
50
- function ViewToggle({ viewMode, onViewModeChange }) {
51
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center border rounded-lg overflow-hidden", children: [
52
- /* @__PURE__ */ jsx(
53
- Button,
54
- {
55
- variant: viewMode === "grid" ? "default" : "ghost",
56
- size: "icon",
57
- className: "h-8 w-8 rounded-none",
58
- onClick: () => onViewModeChange("grid"),
59
- title: "Grid view",
60
- children: /* @__PURE__ */ jsx(LayoutGrid, { className: "h-4 w-4" })
61
- }
62
- ),
63
- /* @__PURE__ */ jsx(
64
- Button,
65
- {
66
- variant: viewMode === "table" ? "default" : "ghost",
67
- size: "icon",
68
- className: "h-8 w-8 rounded-none",
69
- onClick: () => onViewModeChange("table"),
70
- title: "Table view",
71
- children: /* @__PURE__ */ jsx(Table2, { className: "h-4 w-4" })
72
- }
73
- ),
74
- /* @__PURE__ */ jsx(
75
- Button,
76
- {
77
- variant: viewMode === "list" ? "default" : "ghost",
78
- size: "icon",
79
- className: "h-8 w-8 rounded-none",
80
- onClick: () => onViewModeChange("list"),
81
- title: "List view",
82
- children: /* @__PURE__ */ jsx(List, { className: "h-4 w-4" })
83
- }
84
- )
85
- ] });
86
- }
87
- var PAGE_SIZE_OPTIONS = [10, 25, 50, 100];
88
- var DENSITY_OPTIONS = [
89
- { value: "compact", label: "Compact" },
90
- { value: "comfortable", label: "Comfortable" },
91
- { value: "spacious", label: "Spacious" }
92
- ];
93
- function ViewSettings({
94
- pageSize,
95
- onPageSizeChange,
96
- density,
97
- onDensityChange
98
- }) {
99
- return /* @__PURE__ */ jsxs(Popover, { children: [
100
- /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
101
- Button,
102
- {
103
- variant: "ghost",
104
- size: "icon",
105
- className: "h-8 w-8",
106
- title: "View settings",
107
- children: /* @__PURE__ */ jsx(Settings2, { className: "h-4 w-4" })
108
- }
109
- ) }),
110
- /* @__PURE__ */ jsxs(PopoverContent, { align: "end", className: "w-56 space-y-4", children: [
111
- /* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold", children: "Display Settings" }),
112
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
113
- /* @__PURE__ */ jsx("label", { className: "text-xs text-muted-foreground", children: "Page size" }),
114
- /* @__PURE__ */ jsxs(
115
- Select,
116
- {
117
- value: String(pageSize),
118
- onValueChange: (v) => onPageSizeChange(Number(v)),
119
- children: [
120
- /* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
121
- /* @__PURE__ */ jsx(SelectContent, { className: "bg-popover z-50", children: PAGE_SIZE_OPTIONS.map((n) => /* @__PURE__ */ jsxs(SelectItem, { value: String(n), children: [
122
- n,
123
- " items"
124
- ] }, n)) })
125
- ]
126
- }
127
- )
128
- ] }),
129
- /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
130
- /* @__PURE__ */ jsx("label", { className: "text-xs text-muted-foreground", children: "Density" }),
131
- /* @__PURE__ */ jsxs(
132
- Select,
133
- {
134
- value: density,
135
- onValueChange: (v) => onDensityChange(v),
136
- children: [
137
- /* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
138
- /* @__PURE__ */ jsx(SelectContent, { className: "bg-popover z-50", children: DENSITY_OPTIONS.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value)) })
139
- ]
140
- }
141
- )
142
- ] })
143
- ] })
144
- ] });
145
- }
146
50
  function DataPageShell({
147
51
  title,
148
- icon: Icon,
52
+ icon,
149
53
  entityName,
150
54
  subtitle,
151
55
  createAction,
@@ -168,44 +72,57 @@ function DataPageShell({
168
72
  filters,
169
73
  hasActiveFilters = false,
170
74
  onClearFilters,
75
+ filtersCollapsible = false,
76
+ defaultFiltersOpen = false,
77
+ filtersOpen,
78
+ onFiltersOpenChange,
79
+ filtersLabel,
80
+ filtersHint,
81
+ headerOptions,
82
+ headerOptionsStorageKey,
171
83
  children
172
84
  }) {
173
85
  const entity = entityName || title.toLowerCase();
174
- const hasViewToggle = viewMode && onViewModeChange;
175
- const hasViewSettings = onPageSizeChange && onDensityChange;
176
- const hasCount = count !== void 0 && totalCount !== void 0;
86
+ const collapseFilters = filtersCollapsible && filters != null;
87
+ const mergedOptions = {
88
+ defaultDetailsOpen: defaultFiltersOpen,
89
+ detailsLabel: filtersLabel,
90
+ subtitleInDetails: collapseFilters ? true : void 0,
91
+ ...headerOptions
92
+ };
93
+ const sharedHeaderProps = {
94
+ title,
95
+ subtitle,
96
+ icon,
97
+ entityName: entity,
98
+ count,
99
+ totalCount,
100
+ countActive: hasActiveFilters,
101
+ viewMode,
102
+ onViewModeChange,
103
+ pageSize,
104
+ onPageSizeChange,
105
+ density,
106
+ onDensityChange,
107
+ createAction,
108
+ optionsStorageKey: headerOptionsStorageKey
109
+ };
177
110
  return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
178
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [
179
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
180
- Icon && /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-primary/10 overflow-hidden", children: /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5 text-primary" }) }),
181
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
182
- /* @__PURE__ */ jsx(
183
- "h1",
184
- {
185
- "data-testid": "page-title",
186
- className: "text-xl sm:text-2xl font-bold font-display tracking-tight truncate",
187
- children: title
188
- }
189
- ),
190
- subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground truncate", children: subtitle })
191
- ] })
192
- ] }),
193
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
194
- hasCount && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs", children: hasActiveFilters ? `${count} of ${totalCount}` : `${totalCount} ${entity.endsWith("s") ? entity : `${entity}s`}` }),
195
- hasViewToggle && /* @__PURE__ */ jsx(ViewToggle, { viewMode, onViewModeChange }),
196
- hasViewSettings && /* @__PURE__ */ jsx(
197
- ViewSettings,
198
- {
199
- pageSize: pageSize ?? 25,
200
- onPageSizeChange,
201
- density: density ?? "comfortable",
202
- onDensityChange
203
- }
204
- ),
205
- createAction
206
- ] })
111
+ collapseFilters ? /* @__PURE__ */ jsx(
112
+ PageHeader,
113
+ {
114
+ ...sharedHeaderProps,
115
+ details: filters,
116
+ detailsOpen: filtersOpen,
117
+ onDetailsOpenChange: onFiltersOpenChange,
118
+ detailsActive: hasActiveFilters,
119
+ detailsHint: filtersHint,
120
+ options: mergedOptions
121
+ }
122
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
123
+ /* @__PURE__ */ jsx(PageHeader, { ...sharedHeaderProps, options: headerOptions }),
124
+ filters
207
125
  ] }),
208
- filters,
209
126
  isLoading ? /* @__PURE__ */ jsx(
210
127
  "div",
211
128
  {
@@ -222,17 +139,17 @@ function DataPageShell({
222
139
  }
223
140
  ) : hasError ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-16 text-center", children: [
224
141
  /* @__PURE__ */ jsx(AlertTriangle, { className: "h-10 w-10 text-destructive mb-3" }),
225
- /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-foreground mb-1", children: "Something went wrong" }),
226
- /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mb-4", children: errorMessage || `Failed to load ${entity}s. Please try again.` }),
142
+ /* @__PURE__ */ jsx("p", { className: "mb-1 text-sm font-medium text-foreground", children: "Something went wrong" }),
143
+ /* @__PURE__ */ jsx("p", { className: "mb-4 text-sm text-muted-foreground", children: errorMessage || `Failed to load ${entity}s. Please try again.` }),
227
144
  onRetry && /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", onClick: onRetry, children: [
228
- /* @__PURE__ */ jsx(RefreshCw, { className: "h-4 w-4 mr-1" }),
145
+ /* @__PURE__ */ jsx(RefreshCw, { className: "mr-1 h-4 w-4" }),
229
146
  " Retry"
230
147
  ] })
231
148
  ] }) : isEmpty ? /* @__PURE__ */ jsx(
232
149
  EmptyState,
233
150
  {
234
151
  icon: emptyIcon,
235
- message: emptyMessage,
152
+ title: emptyMessage,
236
153
  action: emptyAction,
237
154
  hasFilters: hasActiveFilters,
238
155
  onClearFilters
@@ -609,14 +526,25 @@ function DataGrid({
609
526
  }
610
527
  if (viewMode === "list" && renderListItem) {
611
528
  const keyFnList = getKey || ((item) => item.id);
612
- return /* @__PURE__ */ jsx("div", { className: "divide-y divide-border rounded-lg border", children: items.map((item, i) => /* @__PURE__ */ jsx(
529
+ return /* @__PURE__ */ jsx(
613
530
  "div",
614
531
  {
615
- className: "px-4 py-3 transition-colors hover:bg-accent/50",
616
- children: renderListItem(item, i)
617
- },
618
- keyFnList(item)
619
- )) });
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
+ );
620
548
  }
621
549
  const visible = items.slice(0, visibleCount);
622
550
  const keyFn = getKey || ((item) => item.id);
@@ -647,6 +575,16 @@ function DataGrid({
647
575
  )
648
576
  ] });
649
577
  }
578
+ var DENSITY_CELL = {
579
+ compact: "py-1 h-8",
580
+ comfortable: "py-2",
581
+ spacious: "py-3"
582
+ };
583
+ var DENSITY_HEAD = {
584
+ compact: "h-8 py-1",
585
+ comfortable: "h-10",
586
+ spacious: "h-12 py-3"
587
+ };
650
588
  function getCellClasses(col) {
651
589
  const hide = col.hideBelow ? col.hideBelow === "sm" ? "hidden sm:table-cell" : col.hideBelow === "md" ? "hidden md:table-cell" : col.hideBelow === "lg" ? "hidden lg:table-cell" : "hidden xl:table-cell" : "";
652
590
  return cn(col.width, hide);
@@ -764,8 +702,11 @@ function DataTable({
764
702
  pageSizeOptions = [10, 25, 50, 100],
765
703
  stickyHeader = true,
766
704
  resizeKey,
767
- stickyFirstColumn
705
+ stickyFirstColumn,
706
+ density = "comfortable"
768
707
  }) {
708
+ const cellDensity = DENSITY_CELL[density];
709
+ const headDensity = DENSITY_HEAD[density];
769
710
  const [sorts, setSorts] = useState([]);
770
711
  const handleSort = useCallback(
771
712
  (key, shiftKey) => {
@@ -788,15 +729,6 @@ function DataTable({
788
729
  );
789
730
  const [visibleCols, setVisibleCols] = useState(
790
731
  () => {
791
- if (columnVisibilityKey) {
792
- try {
793
- const saved = localStorage.getItem(
794
- `dt-vis-${columnVisibilityKey}`
795
- );
796
- if (saved) return JSON.parse(saved);
797
- } catch {
798
- }
799
- }
800
732
  const initial = {};
801
733
  columns.forEach((c) => {
802
734
  initial[c.key] = c.defaultVisible !== false;
@@ -804,14 +736,40 @@ function DataTable({
804
736
  return initial;
805
737
  }
806
738
  );
739
+ const [visibilityHydrated, setVisibilityHydrated] = useState(false);
807
740
  useEffect(() => {
808
- if (columnVisibilityKey) {
809
- localStorage.setItem(
810
- `dt-vis-${columnVisibilityKey}`,
811
- JSON.stringify(visibleCols)
812
- );
741
+ if (!columnVisibilityKey) {
742
+ setVisibilityHydrated(true);
743
+ return;
744
+ }
745
+ try {
746
+ const saved = localStorage.getItem(`dt-vis-${columnVisibilityKey}`);
747
+ if (saved) {
748
+ const parsed = JSON.parse(saved);
749
+ setVisibleCols((prev) => {
750
+ const next = { ...prev };
751
+ for (const [k, v] of Object.entries(parsed)) {
752
+ if (k in next || columns.some((c) => c.key === k)) {
753
+ next[k] = v;
754
+ }
755
+ }
756
+ columns.forEach((c) => {
757
+ if (!(c.key in next)) next[c.key] = c.defaultVisible !== false;
758
+ });
759
+ return next;
760
+ });
761
+ }
762
+ } catch {
813
763
  }
814
- }, [visibleCols, columnVisibilityKey]);
764
+ setVisibilityHydrated(true);
765
+ }, [columnVisibilityKey]);
766
+ useEffect(() => {
767
+ if (!visibilityHydrated || !columnVisibilityKey) return;
768
+ localStorage.setItem(
769
+ `dt-vis-${columnVisibilityKey}`,
770
+ JSON.stringify(visibleCols)
771
+ );
772
+ }, [visibleCols, columnVisibilityKey, visibilityHydrated]);
815
773
  const toggleColumnVisibility = useCallback((key) => {
816
774
  setVisibleCols((prev) => ({ ...prev, [key]: !prev[key] }));
817
775
  }, []);
@@ -950,7 +908,7 @@ function DataTable({
950
908
  {
951
909
  className: stickyHeader ? "sticky top-0 z-10 bg-background" : "",
952
910
  children: /* @__PURE__ */ jsxs(TableRow, { children: [
953
- selectable && /* @__PURE__ */ jsx(TableHead, { className: "w-10", children: /* @__PURE__ */ jsx(
911
+ selectable && /* @__PURE__ */ jsx(TableHead, { className: cn("w-10", headDensity), children: /* @__PURE__ */ jsx(
954
912
  Checkbox,
955
913
  {
956
914
  checked: items.length > 0 && selected?.size === items.length,
@@ -967,6 +925,7 @@ function DataTable({
967
925
  {
968
926
  className: cn(
969
927
  getCellClasses(col),
928
+ headDensity,
970
929
  col.sortable && "cursor-pointer select-none",
971
930
  resizableColumns && "relative",
972
931
  isFirstCol && "sticky left-0 bg-background",
@@ -998,7 +957,7 @@ function DataTable({
998
957
  col.key
999
958
  );
1000
959
  }),
1001
- renderActions && /* @__PURE__ */ jsx(TableHead, { className: "w-10" })
960
+ renderActions && /* @__PURE__ */ jsx(TableHead, { className: cn("w-10", headDensity) })
1002
961
  ] })
1003
962
  }
1004
963
  ),
@@ -1018,13 +977,20 @@ function DataTable({
1018
977
  "data-state": selected?.has(id) ? "selected" : void 0,
1019
978
  onClick: onRowClick ? () => onRowClick(item) : void 0,
1020
979
  children: [
1021
- selectable && /* @__PURE__ */ jsx(TableCell, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsx(
1022
- Checkbox,
980
+ selectable && /* @__PURE__ */ jsx(
981
+ TableCell,
1023
982
  {
1024
- checked: selected?.has(id),
1025
- onCheckedChange: () => onToggleSelect?.(id)
983
+ className: cellDensity,
984
+ onClick: (e) => e.stopPropagation(),
985
+ children: /* @__PURE__ */ jsx(
986
+ Checkbox,
987
+ {
988
+ checked: selected?.has(id),
989
+ onCheckedChange: () => onToggleSelect?.(id)
990
+ }
991
+ )
1026
992
  }
1027
- ) }),
993
+ ),
1028
994
  activeColumns.map((col, colIndex) => {
1029
995
  const acc = col.accessor || ((it) => it[col.key]);
1030
996
  const value = acc(item);
@@ -1036,6 +1002,7 @@ function DataTable({
1036
1002
  getCellClasses(
1037
1003
  col
1038
1004
  ),
1005
+ cellDensity,
1039
1006
  isFirstCol && "sticky left-0 z-[1] bg-background"
1040
1007
  ),
1041
1008
  children: col.editable ? /* @__PURE__ */ jsx(
@@ -1050,7 +1017,14 @@ function DataTable({
1050
1017
  col.key
1051
1018
  );
1052
1019
  }),
1053
- renderActions && /* @__PURE__ */ jsx(TableCell, { onClick: (e) => e.stopPropagation(), children: renderActions(item) })
1020
+ renderActions && /* @__PURE__ */ jsx(
1021
+ TableCell,
1022
+ {
1023
+ className: cellDensity,
1024
+ onClick: (e) => e.stopPropagation(),
1025
+ children: renderActions(item)
1026
+ }
1027
+ )
1054
1028
  ]
1055
1029
  },
1056
1030
  id
@@ -1347,6 +1321,11 @@ var widthClasses = {
1347
1321
  md: "w-96",
1348
1322
  lg: "w-[480px]"
1349
1323
  };
1324
+ var dialogMaxWidthClasses = {
1325
+ sm: "sm:max-w-sm",
1326
+ md: "sm:max-w-lg",
1327
+ lg: "sm:max-w-2xl"
1328
+ };
1350
1329
  function keyToLabel(key) {
1351
1330
  return key.replace(/_/g, " ").replace(/([a-z])([A-Z])/g, "$1 $2").replace(/\b\w/g, (c) => c.toUpperCase());
1352
1331
  }
@@ -1357,6 +1336,37 @@ function formatValue(value) {
1357
1336
  if (typeof value === "object") return JSON.stringify(value);
1358
1337
  return String(value);
1359
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
+ }
1360
1370
  function DetailPanel({
1361
1371
  item,
1362
1372
  open,
@@ -1367,7 +1377,8 @@ function DetailPanel({
1367
1377
  onDelete,
1368
1378
  children,
1369
1379
  fields,
1370
- width = "md"
1380
+ width = "md",
1381
+ layout = "sheet"
1371
1382
  }) {
1372
1383
  if (!open || !item) return null;
1373
1384
  const renderContent = () => {
@@ -1392,78 +1403,114 @@ function DetailPanel({
1392
1403
  ] })
1393
1404
  ] }, key)) });
1394
1405
  };
1395
- return /* @__PURE__ */ jsxs(Fragment, { children: [
1396
- /* @__PURE__ */ jsx(
1397
- "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,
1398
1451
  {
1399
- className: "fixed inset-0 z-40 bg-black/50 sm:hidden",
1400
- onClick: onClose,
1401
- "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
+ )
1402
1470
  }
1403
- ),
1404
- /* @__PURE__ */ jsxs(
1405
- "div",
1471
+ );
1472
+ }
1473
+ if (layout === "sheet") {
1474
+ return /* @__PURE__ */ jsx(
1475
+ Sheet,
1406
1476
  {
1407
- role: "dialog",
1408
- "aria-label": title ?? "Detail panel",
1409
- onKeyDown: (e) => {
1410
- if (e.key === "Escape") onClose();
1477
+ open,
1478
+ onOpenChange: (next) => {
1479
+ if (!next) onClose();
1411
1480
  },
1412
- tabIndex: -1,
1413
- className: cn(
1414
- "fixed inset-y-0 right-0 z-50 flex flex-col border-l border-border bg-card shadow-xl",
1415
- "max-w-[90vw]",
1416
- widthClasses[width],
1417
- "transform transition-transform duration-200 ease-in-out",
1418
- open ? "translate-x-0" : "translate-x-full"
1419
- ),
1420
- children: [
1421
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-border px-4 py-3", children: [
1422
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
1423
- title && /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold truncate", children: title }),
1424
- subtitle && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground truncate", children: subtitle })
1425
- ] }),
1426
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [
1427
- onEdit && /* @__PURE__ */ jsx(
1428
- Button,
1429
- {
1430
- variant: "ghost",
1431
- size: "icon",
1432
- className: "h-8 w-8",
1433
- onClick: () => onEdit(item),
1434
- "aria-label": "Edit",
1435
- children: /* @__PURE__ */ jsx(Pencil, { className: "h-4 w-4" })
1436
- }
1437
- ),
1438
- onDelete && /* @__PURE__ */ jsx(
1439
- Button,
1440
- {
1441
- variant: "ghost",
1442
- size: "icon",
1443
- className: "h-8 w-8 text-destructive hover:text-destructive",
1444
- onClick: () => onDelete(item),
1445
- "aria-label": "Delete",
1446
- children: /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4" })
1447
- }
1448
- ),
1449
- /* @__PURE__ */ jsx(
1450
- Button,
1451
- {
1452
- variant: "ghost",
1453
- size: "icon",
1454
- className: "h-8 w-8",
1455
- onClick: onClose,
1456
- "aria-label": "Close",
1457
- children: /* @__PURE__ */ jsx(X, { className: "h-4 w-4" })
1458
- }
1459
- )
1460
- ] })
1461
- ] }),
1462
- /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto px-4 py-3", children: renderContent() })
1463
- ]
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
+ )
1464
1496
  }
1465
- )
1466
- ] });
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
+ );
1467
1514
  }
1468
1515
  function RelationField({
1469
1516
  field,
@@ -2389,13 +2436,19 @@ function ResourcePage(props) {
2389
2436
  formSteps,
2390
2437
  formLayout: formLayoutProp,
2391
2438
  bulkActions,
2439
+ renderCard: renderCardProp,
2440
+ gridCols: gridColsProp,
2441
+ keyboardNavigation = true,
2392
2442
  toolbar,
2393
2443
  emptyState,
2394
2444
  resourceOptions,
2395
2445
  defaultView = "table",
2396
- onNavigate = defaultNavigate
2446
+ onNavigate = defaultNavigate,
2447
+ viewSettingsKey: viewSettingsKeyProp
2397
2448
  } = props;
2398
2449
  const isClientMode = !!resourceName;
2450
+ const viewSettingsKey = viewSettingsKeyProp ?? resourceName ?? title.toLowerCase().replace(/\s+/g, "-");
2451
+ const { settings, updateSetting } = useViewSettings(viewSettingsKey);
2399
2452
  const resourceHook = useResource(
2400
2453
  resourceName ?? "__noop__",
2401
2454
  isClientMode ? resourceOptions : { enabled: false }
@@ -2511,6 +2564,35 @@ function ResourcePage(props) {
2511
2564
  }
2512
2565
  page.handleRowClick(item);
2513
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
+ });
2514
2596
  const showCreateButton = onCreate || useBuiltInCreate;
2515
2597
  const createButton = showCreateButton ? /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: handleCreate, children: [
2516
2598
  /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4 mr-1" }),
@@ -2521,15 +2603,83 @@ function ResourcePage(props) {
2521
2603
  createButton
2522
2604
  ] }) : void 0;
2523
2605
  const isEmpty = !isLoading && !hasError && page.data.length === 0;
2524
- const modalDetailContent = page.selectedItem ? renderDetailProp ? renderDetailProp(page.selectedItem) : detailFields ? /* @__PURE__ */ jsx("dl", { className: "space-y-3", children: detailFields.map((field) => /* @__PURE__ */ jsxs("div", { children: [
2525
- /* @__PURE__ */ jsx("dt", { className: "text-xs font-medium text-muted-foreground uppercase tracking-wider", children: field.label }),
2526
- /* @__PURE__ */ jsx("dd", { className: "mt-1 text-sm text-foreground", children: field.render ? field.render(
2527
- page.selectedItem[field.key],
2528
- page.selectedItem
2529
- ) : String(
2530
- page.selectedItem[field.key] ?? "\u2014"
2531
- ) })
2532
- ] }, field.key)) }) : null : null;
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
+ };
2533
2683
  return /* @__PURE__ */ jsxs(
2534
2684
  DataPageShell,
2535
2685
  {
@@ -2544,6 +2694,10 @@ function ResourcePage(props) {
2544
2694
  onRetry: isClientMode ? () => resourceHook.list.refetch() : void 0,
2545
2695
  viewMode: page.viewMode,
2546
2696
  onViewModeChange: page.setViewMode,
2697
+ pageSize: settings.pageSize,
2698
+ onPageSizeChange: (size) => updateSetting("pageSize", size),
2699
+ density: settings.density,
2700
+ onDensityChange: (d) => updateSetting("density", d),
2547
2701
  createAction,
2548
2702
  hasActiveFilters: page.hasActiveFilters,
2549
2703
  onClearFilters: page.clearFilters,
@@ -2584,7 +2738,10 @@ function ResourcePage(props) {
2584
2738
  {
2585
2739
  items: page.data,
2586
2740
  viewMode: page.viewMode,
2741
+ gridCols: gridColsProp,
2587
2742
  getKey: (item) => String(item.id ?? ""),
2743
+ focusedIndex: keyboardNav.focusedIndex,
2744
+ keyboardContainerRef: keyboardNav.containerRef,
2588
2745
  renderTable: (items) => /* @__PURE__ */ jsx(
2589
2746
  DataTable,
2590
2747
  {
@@ -2595,72 +2752,16 @@ function ResourcePage(props) {
2595
2752
  selectable: !!bulkActions && bulkActions.length > 0,
2596
2753
  selected: selectedIds,
2597
2754
  onToggleSelect: handleToggleSelect,
2598
- onToggleAll: handleToggleAll
2755
+ onToggleAll: handleToggleAll,
2756
+ density: settings.density,
2757
+ pageSize: settings.pageSize
2599
2758
  }
2600
2759
  ),
2601
- renderCard: (item) => {
2602
- const cols = page.columns;
2603
- const titleCol = cols[0];
2604
- const titleVal = titleCol ? String(item[titleCol.key] ?? "") : "";
2605
- const badgeCol = cols[1];
2606
- const badgeVal = badgeCol ? item[badgeCol.key] : null;
2607
- const priceCol = cols.find(
2608
- (c, i) => i > 1 && /price|cost|amount|total/i.test(c.key)
2609
- );
2610
- const priceVal = priceCol ? item[priceCol.key] : null;
2611
- const metaCols = cols.filter(
2612
- (c) => c !== titleCol && c !== badgeCol && c !== priceCol
2613
- );
2614
- return /* @__PURE__ */ jsxs(
2615
- Card,
2616
- {
2617
- className: "h-full cursor-pointer hover:shadow-md transition-shadow",
2618
- onClick: () => handleRowClick(item),
2619
- children: [
2620
- /* @__PURE__ */ jsx(CardHeader, { className: "pb-2", children: /* @__PURE__ */ jsx(CardTitle, { className: "text-base truncate", children: titleVal }) }),
2621
- /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
2622
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
2623
- badgeCol && /* @__PURE__ */ jsx("span", { children: badgeCol.render ? badgeCol.render(item, badgeVal, actions) : badgeVal != null ? String(badgeVal) : "\u2014" }),
2624
- priceVal != null && /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: String(priceVal) })
2625
- ] }),
2626
- metaCols.map((col) => {
2627
- const val = item[col.key];
2628
- return /* @__PURE__ */ jsx(
2629
- "p",
2630
- {
2631
- className: "text-xs text-muted-foreground truncate",
2632
- children: col.render ? col.render(item, val, actions) : val != null ? String(val) : "\u2014"
2633
- },
2634
- col.key
2635
- );
2636
- })
2637
- ] }) })
2638
- ]
2639
- }
2640
- );
2641
- },
2642
- renderListItem: (item) => {
2643
- const cols = page.columns;
2644
- const titleCol = cols[0];
2645
- const titleVal = titleCol ? String(item[titleCol.key] ?? "") : "";
2646
- return /* @__PURE__ */ jsxs(
2647
- "div",
2648
- {
2649
- className: "flex items-center justify-between px-3 py-2 border-b cursor-pointer hover:bg-muted/50 transition-colors",
2650
- onClick: () => handleRowClick(item),
2651
- children: [
2652
- /* @__PURE__ */ jsx("span", { className: "font-medium text-sm truncate", children: titleVal }),
2653
- /* @__PURE__ */ jsx("div", { className: "flex items-center gap-3 text-xs text-muted-foreground", children: cols.slice(1, 4).map((col) => {
2654
- const val = item[col.key];
2655
- return /* @__PURE__ */ jsx("span", { children: col.render ? col.render(item, val, actions) : val != null ? String(val) : "\u2014" }, col.key);
2656
- }) })
2657
- ]
2658
- }
2659
- );
2660
- }
2760
+ renderCard: (item, index) => renderCardProp ? renderCardProp(item, index, cardActionsFor(item)) : renderDefaultCard(item),
2761
+ renderListItem: (item, index) => renderCardProp ? renderCardProp(item, index, cardActionsFor(item)) : renderDefaultListItem(item)
2661
2762
  }
2662
2763
  ),
2663
- detail === "panel" && /* @__PURE__ */ jsx(
2764
+ (detail === "panel" || detail === "modal") && /* @__PURE__ */ jsx(
2664
2765
  DetailPanel,
2665
2766
  {
2666
2767
  item: page.selectedItem,
@@ -2675,48 +2776,10 @@ function ResourcePage(props) {
2675
2776
  onDelete,
2676
2777
  fields: detailFields,
2677
2778
  width: detailWidth,
2779
+ layout: detail === "modal" ? "dialog" : "sheet",
2678
2780
  children: renderDetailProp ? (item) => renderDetailProp(item) : void 0
2679
2781
  }
2680
2782
  ),
2681
- detail === "modal" && /* @__PURE__ */ jsx(
2682
- Dialog,
2683
- {
2684
- open: page.detailOpen,
2685
- onOpenChange: (open) => {
2686
- page.setDetailOpen(open);
2687
- if (!open) page.setSelectedItem(null);
2688
- },
2689
- children: /* @__PURE__ */ jsxs(DialogContent, { children: [
2690
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
2691
- /* @__PURE__ */ jsx(DialogTitle, { children: detailTitle }),
2692
- detailSubtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: detailSubtitle })
2693
- ] }),
2694
- /* @__PURE__ */ jsxs("div", { className: "py-4", children: [
2695
- modalDetailContent,
2696
- (onEdit || useBuiltInEdit || onDelete) && page.selectedItem && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-4 pt-4 border-t", children: [
2697
- (onEdit || useBuiltInEdit) && /* @__PURE__ */ jsx(
2698
- Button,
2699
- {
2700
- variant: "outline",
2701
- size: "sm",
2702
- onClick: () => handleEdit(page.selectedItem),
2703
- children: "Edit"
2704
- }
2705
- ),
2706
- onDelete && /* @__PURE__ */ jsx(
2707
- Button,
2708
- {
2709
- variant: "destructive",
2710
- size: "sm",
2711
- onClick: () => onDelete(page.selectedItem),
2712
- children: "Delete"
2713
- }
2714
- )
2715
- ] })
2716
- ] })
2717
- ] })
2718
- }
2719
- ),
2720
2783
  (useBuiltInCreate || useBuiltInEdit) && resourceName && /* @__PURE__ */ jsx(
2721
2784
  ResourceForm,
2722
2785
  {
@@ -2743,178 +2806,80 @@ function ResourcePage(props) {
2743
2806
  }
2744
2807
  );
2745
2808
  }
2746
- function ThemeToggle() {
2747
- const [dark, setDark] = useState(false);
2748
- useEffect(() => {
2749
- const stored = localStorage.getItem("theme");
2750
- const prefersDark = window.matchMedia(
2751
- "(prefers-color-scheme: dark)"
2752
- ).matches;
2753
- const isDark = stored === "dark" || !stored && prefersDark;
2754
- setDark(isDark);
2755
- document.documentElement.classList.toggle("dark", isDark);
2756
- }, []);
2757
- function toggle() {
2758
- const next = !dark;
2759
- setDark(next);
2760
- document.documentElement.classList.toggle("dark", next);
2761
- localStorage.setItem("theme", next ? "dark" : "light");
2762
- }
2763
- return /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", onClick: toggle, className: "h-8 w-8", children: dark ? /* @__PURE__ */ jsx(Sun, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Moon, { className: "h-4 w-4" }) });
2809
+ function pick(record, field) {
2810
+ const value = field ? record[field] : record.name ?? record.title ?? record.id;
2811
+ return value == null ? "" : String(value);
2764
2812
  }
2765
- function SidebarNav({
2766
- navItems,
2767
- onNavClick,
2768
- linkComponent: LinkComp,
2769
- activePath,
2770
- isActive
2771
- }) {
2772
- return /* @__PURE__ */ jsx("nav", { className: "flex-1 p-3 space-y-1", children: navItems.map((item) => {
2773
- const active = isActive(item.to, activePath);
2774
- return /* @__PURE__ */ jsxs(
2775
- LinkComp,
2776
- {
2777
- href: item.to,
2778
- onClick: onNavClick,
2779
- "aria-current": active ? "page" : void 0,
2780
- className: cn(
2781
- "flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-colors",
2782
- active ? "bg-sidebar-accent text-sidebar-primary" : "text-sidebar-foreground hover:bg-sidebar-accent hover:text-sidebar-accent-foreground"
2783
- ),
2784
- children: [
2785
- /* @__PURE__ */ jsx(item.icon, { className: "h-4 w-4" }),
2786
- item.label
2787
- ]
2788
- },
2789
- item.to
2790
- );
2791
- }) });
2813
+ function NotFound() {
2814
+ return /* @__PURE__ */ jsx(
2815
+ EmptyState,
2816
+ {
2817
+ title: "Not found",
2818
+ description: "This record no longer exists, or you don't have access to it."
2819
+ }
2820
+ );
2792
2821
  }
2793
- function SidebarLogo({
2794
- appName,
2795
- appLogo
2796
- }) {
2797
- return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2798
- appLogo && /* @__PURE__ */ jsx("div", { className: "h-7 w-7 rounded-lg bg-primary flex items-center justify-center", children: /* @__PURE__ */ jsx("span", { className: "text-primary-foreground text-sm font-bold", children: appLogo }) }),
2799
- /* @__PURE__ */ jsx("span", { className: "font-semibold text-sidebar-foreground", children: appName })
2800
- ] });
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);
2801
2826
  }
2802
- function AppShell({
2803
- appName,
2804
- appLogo,
2805
- navItems,
2827
+ function ResourceDetailPage({
2828
+ resource,
2829
+ id,
2830
+ titleField,
2831
+ subtitleField,
2832
+ select,
2833
+ breadcrumb,
2834
+ actions,
2835
+ sidebar,
2806
2836
  children,
2807
- showThemeToggle = true,
2808
- linkComponent = DefaultLink,
2809
- activePath,
2810
- isActive = isPathActive
2837
+ className,
2838
+ testID
2811
2839
  }) {
2812
- const [mobileOpen, setMobileOpen] = useState(false);
2813
- return /* @__PURE__ */ jsxs("div", { className: "flex h-screen overflow-hidden", children: [
2814
- /* @__PURE__ */ jsxs("aside", { className: "hidden md:flex md:w-60 flex-col border-r border-sidebar-border bg-sidebar", children: [
2815
- /* @__PURE__ */ jsx("div", { className: "flex h-14 items-center gap-2 px-4 border-b border-sidebar-border", children: /* @__PURE__ */ jsx(SidebarLogo, { appName, appLogo }) }),
2816
- /* @__PURE__ */ jsx(
2817
- SidebarNav,
2818
- {
2819
- navItems,
2820
- linkComponent,
2821
- activePath,
2822
- isActive
2823
- }
2824
- ),
2825
- showThemeToggle && /* @__PURE__ */ jsx("div", { className: "border-t border-sidebar-border p-3 flex items-center justify-end", children: /* @__PURE__ */ jsx(ThemeToggle, {}) })
2826
- ] }),
2827
- mobileOpen && /* @__PURE__ */ jsx(
2828
- "div",
2829
- {
2830
- className: "fixed inset-0 z-40 bg-black/50 md:hidden",
2831
- onClick: () => setMobileOpen(false)
2832
- }
2833
- ),
2834
- /* @__PURE__ */ jsxs(
2835
- "aside",
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 });
2851
+ }
2852
+ if (isError) {
2853
+ if (isNotFoundError(error)) return /* @__PURE__ */ jsx(NotFound, {});
2854
+ return /* @__PURE__ */ jsx(
2855
+ EmptyState,
2836
2856
  {
2837
- className: cn(
2838
- "fixed inset-y-0 left-0 z-50 w-60 flex flex-col border-r border-sidebar-border bg-sidebar transition-transform md:hidden",
2839
- mobileOpen ? "translate-x-0" : "-translate-x-full"
2840
- ),
2841
- children: [
2842
- /* @__PURE__ */ jsxs("div", { className: "flex h-14 items-center justify-between px-4 border-b border-sidebar-border", children: [
2843
- /* @__PURE__ */ jsx(SidebarLogo, { appName, appLogo }),
2844
- /* @__PURE__ */ jsx(
2845
- Button,
2846
- {
2847
- variant: "ghost",
2848
- size: "icon",
2849
- onClick: () => setMobileOpen(false),
2850
- children: /* @__PURE__ */ jsx(X, { className: "h-5 w-5" })
2851
- }
2852
- )
2853
- ] }),
2854
- /* @__PURE__ */ jsx(
2855
- SidebarNav,
2856
- {
2857
- navItems,
2858
- onNavClick: () => setMobileOpen(false),
2859
- linkComponent,
2860
- activePath,
2861
- isActive
2862
- }
2863
- )
2864
- ]
2857
+ icon: AlertTriangle,
2858
+ title: "Couldn't load this record",
2859
+ description: error instanceof Error ? error.message : void 0
2865
2860
  }
2866
- ),
2867
- /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
2868
- /* @__PURE__ */ jsxs("header", { className: "flex h-14 items-center gap-3 border-b px-4 md:hidden", children: [
2869
- /* @__PURE__ */ jsx(
2870
- Button,
2871
- {
2872
- variant: "ghost",
2873
- size: "icon",
2874
- onClick: () => setMobileOpen(true),
2875
- children: /* @__PURE__ */ jsx(Menu, { className: "h-5 w-5" })
2876
- }
2877
- ),
2878
- /* @__PURE__ */ jsx("span", { className: "flex-1 font-semibold", children: appName }),
2879
- showThemeToggle && /* @__PURE__ */ jsx(ThemeToggle, {})
2880
- ] }),
2881
- /* @__PURE__ */ jsx("main", { className: "flex-1 overflow-y-auto pb-16 md:pb-0", children: /* @__PURE__ */ jsx("div", { className: "p-4 sm:p-6 lg:p-8", children }) }),
2882
- /* @__PURE__ */ jsx("nav", { className: "fixed bottom-0 left-0 right-0 z-30 flex border-t bg-background md:hidden", children: navItems.map((item) => {
2883
- const LinkComp = linkComponent;
2884
- const active = isActive(item.to, activePath);
2885
- return /* @__PURE__ */ jsxs(
2886
- LinkComp,
2887
- {
2888
- href: item.to,
2889
- "aria-current": active ? "page" : void 0,
2890
- className: cn(
2891
- "flex flex-1 flex-col items-center gap-1 py-2 text-xs font-medium transition-colors",
2892
- active ? "text-primary" : "text-muted-foreground"
2893
- ),
2894
- children: [
2895
- /* @__PURE__ */ jsx(item.icon, { className: "h-5 w-5" }),
2896
- item.label
2897
- ]
2898
- },
2899
- item.to
2900
- );
2901
- }) })
2902
- ] })
2903
- ] });
2904
- }
2905
- function PageHeader({ title, subtitle, icon: Icon, actions }) {
2906
- return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [
2907
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
2908
- Icon && /* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-primary/10 overflow-hidden", children: /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5 text-primary" }) }),
2909
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
2910
- /* @__PURE__ */ jsx("h1", { className: "text-xl sm:text-2xl font-bold font-display tracking-tight truncate", children: title }),
2911
- subtitle && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground truncate", children: subtitle })
2912
- ] })
2913
- ] }),
2914
- actions && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 shrink-0", children: actions })
2915
- ] });
2861
+ );
2862
+ }
2863
+ if (!data) return /* @__PURE__ */ jsx(NotFound, {});
2864
+ return /* @__PURE__ */ jsx(
2865
+ DetailPage,
2866
+ {
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)
2875
+ }
2876
+ );
2916
2877
  }
2917
2878
 
2918
- 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 };
2919
2884
  //# sourceMappingURL=index.js.map
2920
2885
  //# sourceMappingURL=index.js.map