@pilotiq/pilotiq 0.24.1 → 0.24.3

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 (518) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/boost/guidelines.md +571 -0
  3. package/boost/skills/pilotiq-actions/SKILL.md +49 -0
  4. package/boost/skills/pilotiq-actions/rules/dispatch-modes.md +177 -0
  5. package/boost/skills/pilotiq-actions/rules/factories.md +130 -0
  6. package/boost/skills/pilotiq-actions/rules/visibility-and-authorization.md +125 -0
  7. package/boost/skills/pilotiq-fields/SKILL.md +47 -0
  8. package/boost/skills/pilotiq-fields/rules/field-catalog.md +288 -0
  9. package/boost/skills/pilotiq-fields/rules/reactive-fields.md +199 -0
  10. package/boost/skills/pilotiq-fields/rules/validation.md +198 -0
  11. package/boost/skills/pilotiq-relations/SKILL.md +47 -0
  12. package/boost/skills/pilotiq-relations/rules/relation-managers.md +256 -0
  13. package/boost/skills/pilotiq-relations/rules/repeater-relationship.md +177 -0
  14. package/boost/skills/pilotiq-resource/SKILL.md +61 -0
  15. package/boost/skills/pilotiq-resource/rules/authorization.md +242 -0
  16. package/boost/skills/pilotiq-resource/rules/defining-resources.md +228 -0
  17. package/boost/skills/pilotiq-resource/rules/page-overrides.md +296 -0
  18. package/dist/Pilotiq.d.ts +31 -0
  19. package/dist/Pilotiq.d.ts.map +1 -1
  20. package/dist/Pilotiq.js +3 -1
  21. package/dist/Pilotiq.js.map +1 -1
  22. package/dist/PilotiqRegistry.d.ts +13 -0
  23. package/dist/PilotiqRegistry.d.ts.map +1 -1
  24. package/dist/PilotiqRegistry.js +15 -0
  25. package/dist/PilotiqRegistry.js.map +1 -1
  26. package/dist/pageData/misc.d.ts.map +1 -1
  27. package/dist/pageData/misc.js +6 -0
  28. package/dist/pageData/misc.js.map +1 -1
  29. package/dist/pageData/navigation.d.ts +1 -0
  30. package/dist/pageData/navigation.d.ts.map +1 -1
  31. package/dist/pageData/navigation.js +3 -0
  32. package/dist/pageData/navigation.js.map +1 -1
  33. package/dist/pageData/relationPages.d.ts.map +1 -1
  34. package/dist/pageData/relationPages.js +3 -0
  35. package/dist/pageData/relationPages.js.map +1 -1
  36. package/dist/pageData/resourcePages.d.ts.map +1 -1
  37. package/dist/pageData/resourcePages.js +8 -0
  38. package/dist/pageData/resourcePages.js.map +1 -1
  39. package/dist/react/AppShell.d.ts +8 -0
  40. package/dist/react/AppShell.d.ts.map +1 -1
  41. package/dist/react/AppShell.js.map +1 -1
  42. package/dist/react/layouts/SidebarLayout.d.ts.map +1 -1
  43. package/dist/react/layouts/SidebarLayout.js +10 -2
  44. package/dist/react/layouts/SidebarLayout.js.map +1 -1
  45. package/dist/react/widgets/StatsOverviewRenderer.d.ts.map +1 -1
  46. package/dist/react/widgets/StatsOverviewRenderer.js +32 -18
  47. package/dist/react/widgets/StatsOverviewRenderer.js.map +1 -1
  48. package/dist/routes/relations.d.ts.map +1 -1
  49. package/dist/routes/relations.js +25 -18
  50. package/dist/routes/relations.js.map +1 -1
  51. package/dist/routes/resources.js.map +1 -1
  52. package/package.json +10 -5
  53. package/.turbo/turbo-build.log +0 -8
  54. package/CLAUDE.md +0 -265
  55. package/src/Cluster.test.ts +0 -283
  56. package/src/Cluster.ts +0 -83
  57. package/src/Column.test.ts +0 -199
  58. package/src/Column.ts +0 -710
  59. package/src/Global.test.ts +0 -367
  60. package/src/Global.ts +0 -169
  61. package/src/Page.test.ts +0 -114
  62. package/src/Page.ts +0 -208
  63. package/src/Pilotiq.perf.test.ts +0 -252
  64. package/src/Pilotiq.test.ts +0 -129
  65. package/src/Pilotiq.ts +0 -1158
  66. package/src/PilotiqRegistry.ts +0 -36
  67. package/src/PilotiqServiceProvider.ts +0 -121
  68. package/src/RelationManager.test.ts +0 -400
  69. package/src/RelationManager.ts +0 -527
  70. package/src/RenderHook.test.ts +0 -252
  71. package/src/RenderHook.ts +0 -242
  72. package/src/Resource.test.ts +0 -284
  73. package/src/Resource.ts +0 -526
  74. package/src/RightPanel.test.ts +0 -202
  75. package/src/RightPanel.ts +0 -132
  76. package/src/Tab.test.ts +0 -91
  77. package/src/Tab.ts +0 -156
  78. package/src/UserMenuItem.ts +0 -145
  79. package/src/actions/Action.test.ts +0 -2526
  80. package/src/actions/Action.ts +0 -1515
  81. package/src/actions/ActionGroup.test.ts +0 -112
  82. package/src/actions/ActionGroup.ts +0 -173
  83. package/src/actions/attachFactory.ts +0 -172
  84. package/src/actions/bulkFactories.ts +0 -168
  85. package/src/actions/crudFactories.ts +0 -220
  86. package/src/actions/exportFactory.ts +0 -225
  87. package/src/actions/factoryHelpers.ts +0 -177
  88. package/src/actions/importFactory.ts +0 -243
  89. package/src/actions/index.ts +0 -17
  90. package/src/actions/m2mFactories.ts +0 -193
  91. package/src/actions/relationFactories.ts +0 -372
  92. package/src/applyPageHooks.test.ts +0 -463
  93. package/src/applyPageHooks.ts +0 -330
  94. package/src/authorization.test.ts +0 -483
  95. package/src/breadcrumbs.test.ts +0 -238
  96. package/src/cells/coerce.test.ts +0 -85
  97. package/src/cells/coerce.ts +0 -84
  98. package/src/clusterPaths.ts +0 -35
  99. package/src/columns/BadgeColumn.test.ts +0 -54
  100. package/src/columns/BadgeColumn.ts +0 -32
  101. package/src/columns/BooleanColumn.test.ts +0 -41
  102. package/src/columns/BooleanColumn.ts +0 -18
  103. package/src/columns/ColorColumn.test.ts +0 -37
  104. package/src/columns/ColorColumn.ts +0 -38
  105. package/src/columns/IconColumn.test.ts +0 -54
  106. package/src/columns/IconColumn.ts +0 -37
  107. package/src/columns/ImageColumn.test.ts +0 -41
  108. package/src/columns/ImageColumn.ts +0 -28
  109. package/src/columns/SelectColumn.ts +0 -98
  110. package/src/columns/TextColumn.test.ts +0 -190
  111. package/src/columns/TextColumn.ts +0 -20
  112. package/src/columns/TextInputColumn.ts +0 -68
  113. package/src/columns/ToggleColumn.ts +0 -46
  114. package/src/columns/editableColumns.test.ts +0 -238
  115. package/src/columns/index.ts +0 -9
  116. package/src/defaultGlobalPages.ts +0 -95
  117. package/src/defaultPages.test.ts +0 -634
  118. package/src/defaultPages.ts +0 -617
  119. package/src/defaultViewPage.test.ts +0 -147
  120. package/src/elements/Form.test.ts +0 -223
  121. package/src/elements/Form.ts +0 -416
  122. package/src/elements/ListTabs.ts +0 -28
  123. package/src/elements/Table.test.ts +0 -422
  124. package/src/elements/Table.ts +0 -850
  125. package/src/elements/TableGroup.test.ts +0 -260
  126. package/src/elements/TableGroup.ts +0 -334
  127. package/src/elements/dispatchAction.test.ts +0 -463
  128. package/src/elements/dispatchAction.ts +0 -355
  129. package/src/elements/dispatchForm.test.ts +0 -477
  130. package/src/elements/dispatchForm.ts +0 -1993
  131. package/src/elements/dispatchTable.test.ts +0 -1514
  132. package/src/elements/dispatchTable.ts +0 -745
  133. package/src/elements/index.ts +0 -21
  134. package/src/entries/BadgeEntry.ts +0 -39
  135. package/src/entries/CodeEntry.test.ts +0 -40
  136. package/src/entries/CodeEntry.ts +0 -52
  137. package/src/entries/ColorEntry.ts +0 -63
  138. package/src/entries/ComponentEntry.test.ts +0 -173
  139. package/src/entries/ComponentEntry.ts +0 -95
  140. package/src/entries/Entry.ts +0 -304
  141. package/src/entries/IconEntry.ts +0 -49
  142. package/src/entries/ImageEntry.ts +0 -61
  143. package/src/entries/KeyValueEntry.ts +0 -47
  144. package/src/entries/RepeatableEntry.test.ts +0 -239
  145. package/src/entries/RepeatableEntry.ts +0 -173
  146. package/src/entries/TextEntry.test.ts +0 -394
  147. package/src/entries/TextEntry.ts +0 -60
  148. package/src/entries/index.ts +0 -12
  149. package/src/entries/leaves.test.ts +0 -306
  150. package/src/entries/registry.ts +0 -54
  151. package/src/fields/BuilderField.test.ts +0 -1188
  152. package/src/fields/BuilderField.ts +0 -605
  153. package/src/fields/BuilderRelationship.test.ts +0 -811
  154. package/src/fields/CheckboxField.test.ts +0 -44
  155. package/src/fields/CheckboxField.ts +0 -27
  156. package/src/fields/CheckboxListField.test.ts +0 -99
  157. package/src/fields/CheckboxListField.ts +0 -66
  158. package/src/fields/ColorPickerField.test.ts +0 -33
  159. package/src/fields/ColorPickerField.ts +0 -25
  160. package/src/fields/DateField.ts +0 -54
  161. package/src/fields/DateTimeField.test.ts +0 -55
  162. package/src/fields/EmailField.ts +0 -16
  163. package/src/fields/Field.test.ts +0 -654
  164. package/src/fields/Field.ts +0 -817
  165. package/src/fields/FileUploadField.test.ts +0 -143
  166. package/src/fields/FileUploadField.ts +0 -159
  167. package/src/fields/HiddenField.test.ts +0 -27
  168. package/src/fields/HiddenField.ts +0 -28
  169. package/src/fields/KeyValueField.test.ts +0 -105
  170. package/src/fields/KeyValueField.ts +0 -55
  171. package/src/fields/MarkdownField.test.ts +0 -167
  172. package/src/fields/MarkdownField.ts +0 -162
  173. package/src/fields/NumberField.ts +0 -33
  174. package/src/fields/RadioField.test.ts +0 -94
  175. package/src/fields/RadioField.ts +0 -67
  176. package/src/fields/RepeaterField.test.ts +0 -1806
  177. package/src/fields/RepeaterField.ts +0 -939
  178. package/src/fields/RepeaterRelationship.test.ts +0 -1923
  179. package/src/fields/RepeaterSimple.test.ts +0 -248
  180. package/src/fields/RowButton.test.ts +0 -219
  181. package/src/fields/RowButton.ts +0 -135
  182. package/src/fields/SelectField.test.ts +0 -192
  183. package/src/fields/SelectField.ts +0 -235
  184. package/src/fields/SliderField.test.ts +0 -50
  185. package/src/fields/SliderField.ts +0 -53
  186. package/src/fields/SlugField.ts +0 -24
  187. package/src/fields/TagsInputField.test.ts +0 -154
  188. package/src/fields/TagsInputField.ts +0 -133
  189. package/src/fields/TextField.test.ts +0 -213
  190. package/src/fields/TextField.ts +0 -177
  191. package/src/fields/TextareaField.test.ts +0 -58
  192. package/src/fields/TextareaField.ts +0 -59
  193. package/src/fields/ToggleButtonsField.test.ts +0 -106
  194. package/src/fields/ToggleButtonsField.ts +0 -59
  195. package/src/fields/ToggleField.ts +0 -16
  196. package/src/fields/disableOptionsWhenSelectedInSiblingRepeaterItems.test.ts +0 -319
  197. package/src/fields/optionsResolver.ts +0 -95
  198. package/src/fields/resolveField.ts +0 -28
  199. package/src/filters/BooleanFilter.ts +0 -35
  200. package/src/filters/DateRangeFilter.test.ts +0 -194
  201. package/src/filters/DateRangeFilter.ts +0 -148
  202. package/src/filters/Filter.test.ts +0 -268
  203. package/src/filters/Filter.ts +0 -184
  204. package/src/filters/FormFilter.test.ts +0 -238
  205. package/src/filters/FormFilter.ts +0 -215
  206. package/src/filters/MultiSelectFilter.test.ts +0 -119
  207. package/src/filters/MultiSelectFilter.ts +0 -78
  208. package/src/filters/QueryBuilderFilter.test.ts +0 -662
  209. package/src/filters/QueryBuilderFilter.ts +0 -398
  210. package/src/filters/SelectFilter.ts +0 -46
  211. package/src/filters/TernaryFilter.test.ts +0 -160
  212. package/src/filters/TernaryFilter.ts +0 -72
  213. package/src/filters/TrashedFilter.test.ts +0 -149
  214. package/src/filters/TrashedFilter.ts +0 -55
  215. package/src/filters/queryBuilder/BooleanConstraint.ts +0 -31
  216. package/src/filters/queryBuilder/Constraint.ts +0 -115
  217. package/src/filters/queryBuilder/DateConstraint.ts +0 -69
  218. package/src/filters/queryBuilder/NumberConstraint.ts +0 -66
  219. package/src/filters/queryBuilder/SelectConstraint.ts +0 -72
  220. package/src/filters/queryBuilder/TextConstraint.ts +0 -64
  221. package/src/filters/queryBuilder/index.ts +0 -12
  222. package/src/icons/index.ts +0 -2
  223. package/src/icons/lucide.ts +0 -204
  224. package/src/icons/registry.test.ts +0 -56
  225. package/src/icons/registry.ts +0 -41
  226. package/src/icons/types.ts +0 -47
  227. package/src/index.ts +0 -525
  228. package/src/io/csv.test.ts +0 -142
  229. package/src/io/csv.ts +0 -170
  230. package/src/nestedRelationManagerData.test.ts +0 -547
  231. package/src/notifications/Notification.test.ts +0 -210
  232. package/src/notifications/Notification.ts +0 -354
  233. package/src/notifications/broadcast.test.ts +0 -110
  234. package/src/notifications/broadcast.ts +0 -95
  235. package/src/notifications/database.test.ts +0 -383
  236. package/src/notifications/database.ts +0 -398
  237. package/src/notifications/databaseNotifications.test.ts +0 -187
  238. package/src/notifications/dispatchNotificationAction.test.ts +0 -341
  239. package/src/notifications/dispatchNotificationAction.ts +0 -142
  240. package/src/notifications/flash.test.ts +0 -89
  241. package/src/notifications/flash.ts +0 -71
  242. package/src/notifications/index.ts +0 -45
  243. package/src/notifications/registerBroadcastAuth.test.ts +0 -134
  244. package/src/notifications/registerBroadcastAuth.ts +0 -100
  245. package/src/notifications/resolveSavedNotification.test.ts +0 -82
  246. package/src/notifications/resolveSavedNotification.ts +0 -59
  247. package/src/notifications/types.ts +0 -93
  248. package/src/orm/m2mAccessor.ts +0 -66
  249. package/src/orm/modelDefaults.test.ts +0 -633
  250. package/src/orm/modelDefaults.ts +0 -666
  251. package/src/pageData/breadcrumbs.ts +0 -288
  252. package/src/pageData/forms.ts +0 -578
  253. package/src/pageData/helpers.ts +0 -857
  254. package/src/pageData/misc.ts +0 -347
  255. package/src/pageData/navigation.ts +0 -842
  256. package/src/pageData/relationPages.ts +0 -1248
  257. package/src/pageData/relationTabs.ts +0 -286
  258. package/src/pageData/resourcePages.ts +0 -609
  259. package/src/pageData.test.ts +0 -1545
  260. package/src/pageData.ts +0 -341
  261. package/src/plugins/index.ts +0 -8
  262. package/src/plugins/themeEditor.test.ts +0 -36
  263. package/src/plugins/themeEditor.ts +0 -45
  264. package/src/react/AppShell.tsx +0 -251
  265. package/src/react/CollabExtensionFactoryRegistry.ts +0 -55
  266. package/src/react/CollabRoomContext.ts +0 -98
  267. package/src/react/CollabTextRendererRegistry.ts +0 -102
  268. package/src/react/CommandPalette.tsx +0 -375
  269. package/src/react/CurrentUserContext.tsx +0 -50
  270. package/src/react/CustomPageWrapperGate.tsx +0 -69
  271. package/src/react/CustomPageWrapperRegistry.ts +0 -45
  272. package/src/react/FieldFocusReporterRegistry.ts +0 -37
  273. package/src/react/FieldLabelSlotRegistry.ts +0 -30
  274. package/src/react/FieldPresenceRegistry.ts +0 -46
  275. package/src/react/FormCollabBindingRegistry.ts +0 -242
  276. package/src/react/FormStateContext.tsx +0 -591
  277. package/src/react/HeadHooks.tsx +0 -126
  278. package/src/react/MarkdownEditorRegistry.test.ts +0 -38
  279. package/src/react/MarkdownEditorRegistry.ts +0 -107
  280. package/src/react/NotificationActionStrip.tsx +0 -263
  281. package/src/react/NotificationBell.tsx +0 -426
  282. package/src/react/PendingSuggestionApplierRegistry.test.ts +0 -97
  283. package/src/react/PendingSuggestionApplierRegistry.ts +0 -98
  284. package/src/react/PendingSuggestionOverlayRegistry.ts +0 -54
  285. package/src/react/PendingSuggestionsContext.tsx +0 -172
  286. package/src/react/RecordWrapperGate.tsx +0 -58
  287. package/src/react/RecordWrapperRegistry.ts +0 -39
  288. package/src/react/RenderHookSlot.tsx +0 -32
  289. package/src/react/RightSidebar.tsx +0 -257
  290. package/src/react/RightSidebarContext.tsx +0 -234
  291. package/src/react/RightSidebarTrigger.tsx +0 -53
  292. package/src/react/RowCoordsContext.tsx +0 -23
  293. package/src/react/SchemaRenderer.tsx +0 -549
  294. package/src/react/SearchTrigger.tsx +0 -46
  295. package/src/react/ThemeProvider.tsx +0 -93
  296. package/src/react/ThemeSettingsPage.tsx +0 -579
  297. package/src/react/ThemeToggle.tsx +0 -20
  298. package/src/react/Toaster.tsx +0 -158
  299. package/src/react/UserMenu.tsx +0 -196
  300. package/src/react/WidgetDataContext.tsx +0 -157
  301. package/src/react/cells/EditableCell.tsx +0 -389
  302. package/src/react/component-slots.test.ts +0 -103
  303. package/src/react/component-slots.ts +0 -116
  304. package/src/react/fieldJsHandler.test.ts +0 -166
  305. package/src/react/fieldJsHandler.ts +0 -79
  306. package/src/react/fields/BuilderInput.tsx +0 -1078
  307. package/src/react/fields/CheckboxInput.tsx +0 -39
  308. package/src/react/fields/CheckboxListInput.tsx +0 -102
  309. package/src/react/fields/ColorInput.tsx +0 -71
  310. package/src/react/fields/DateFieldInput.tsx +0 -70
  311. package/src/react/fields/DateTimeInput.tsx +0 -62
  312. package/src/react/fields/FieldShell.tsx +0 -348
  313. package/src/react/fields/FileUploadInput.tsx +0 -639
  314. package/src/react/fields/HiddenInput.tsx +0 -17
  315. package/src/react/fields/KeyValueInput.tsx +0 -230
  316. package/src/react/fields/MarkdownInput.tsx +0 -560
  317. package/src/react/fields/RadioInput.tsx +0 -81
  318. package/src/react/fields/RepeaterInput.test.ts +0 -116
  319. package/src/react/fields/RepeaterInput.tsx +0 -1420
  320. package/src/react/fields/SelectFieldInput.tsx +0 -280
  321. package/src/react/fields/SliderInput.tsx +0 -81
  322. package/src/react/fields/TagsInput.tsx +0 -283
  323. package/src/react/fields/TextLikeInput.tsx +0 -256
  324. package/src/react/fields/ToggleButtonsInput.tsx +0 -60
  325. package/src/react/fields/ToggleFieldInput.tsx +0 -56
  326. package/src/react/fields/relationshipRenameDispatch.test.ts +0 -106
  327. package/src/react/fields/relationshipRenameDispatch.ts +0 -97
  328. package/src/react/fields/repeaterReconcile.test.ts +0 -114
  329. package/src/react/fields/repeaterReconcile.ts +0 -104
  330. package/src/react/fields/rowChromeButton.tsx +0 -336
  331. package/src/react/fields/rowState.ts +0 -106
  332. package/src/react/fields/syncRowGates.test.ts +0 -202
  333. package/src/react/fields/syncRowGates.ts +0 -66
  334. package/src/react/fields/textInputControls.tsx +0 -238
  335. package/src/react/fields/useRowReorderDnd.ts +0 -78
  336. package/src/react/formStateHelpers.test.ts +0 -508
  337. package/src/react/formStateHelpers.ts +0 -381
  338. package/src/react/hooks/use-mobile.ts +0 -19
  339. package/src/react/icon-context.tsx +0 -60
  340. package/src/react/index.ts +0 -194
  341. package/src/react/layouts/SidebarLayout.tsx +0 -250
  342. package/src/react/layouts/TopbarLayout.tsx +0 -258
  343. package/src/react/navigate.tsx +0 -37
  344. package/src/react/onProviderSynced.test.ts +0 -90
  345. package/src/react/parseRecordEditUrl.test.ts +0 -122
  346. package/src/react/parseRecordEditUrl.ts +0 -94
  347. package/src/react/persistedState.ts +0 -40
  348. package/src/react/registry.ts +0 -48
  349. package/src/react/right-panel-registry.tsx +0 -47
  350. package/src/react/schemaRenderer/AlertRenderer.tsx +0 -112
  351. package/src/react/schemaRenderer/EntryRenderer.tsx +0 -501
  352. package/src/react/schemaRenderer/SectionRenderer.tsx +0 -120
  353. package/src/react/schemaRenderer/SimpleElements.tsx +0 -306
  354. package/src/react/schemaRenderer/TabsRenderer.tsx +0 -62
  355. package/src/react/schemaRenderer/WizardRenderer.tsx +0 -338
  356. package/src/react/schemaRenderer/action/ActionGroupTrigger.tsx +0 -177
  357. package/src/react/schemaRenderer/action/ActionModalDialog.tsx +0 -273
  358. package/src/react/schemaRenderer/action/ConfirmActionDialog.tsx +0 -61
  359. package/src/react/schemaRenderer/action/HandlerActionButton.tsx +0 -43
  360. package/src/react/schemaRenderer/action/MethodActionButton.tsx +0 -64
  361. package/src/react/schemaRenderer/action/buttons.tsx +0 -99
  362. package/src/react/schemaRenderer/action/helpers.ts +0 -140
  363. package/src/react/schemaRenderer/action/renderAction.tsx +0 -245
  364. package/src/react/schemaRenderer/columnFormat.ts +0 -65
  365. package/src/react/schemaRenderer/constants.ts +0 -50
  366. package/src/react/schemaRenderer/form/FormRenderer.tsx +0 -274
  367. package/src/react/schemaRenderer/form/renderField.tsx +0 -511
  368. package/src/react/schemaRenderer/helpers.tsx +0 -81
  369. package/src/react/schemaRenderer/table/CardsLayoutBody.tsx +0 -308
  370. package/src/react/schemaRenderer/table/TableRenderer.tsx +0 -123
  371. package/src/react/schemaRenderer/table/TableRendererBody.tsx +0 -974
  372. package/src/react/schemaRenderer/table/filters.tsx +0 -1233
  373. package/src/react/schemaRenderer/table/formatCell.tsx +0 -264
  374. package/src/react/schemaRenderer/table/links.tsx +0 -112
  375. package/src/react/schemaRenderer/table/renderRowActions.tsx +0 -52
  376. package/src/react/schemaRenderer/table/url.tsx +0 -143
  377. package/src/react/theme-preview/apply.ts +0 -99
  378. package/src/react/theme-preview/build-html.ts +0 -436
  379. package/src/react/ui/button.tsx +0 -51
  380. package/src/react/ui/calendar.tsx +0 -67
  381. package/src/react/ui/checkbox.tsx +0 -29
  382. package/src/react/ui/dialog.tsx +0 -108
  383. package/src/react/ui/dropdown-menu.tsx +0 -97
  384. package/src/react/ui/input.tsx +0 -20
  385. package/src/react/ui/label.tsx +0 -21
  386. package/src/react/ui/popover.tsx +0 -50
  387. package/src/react/ui/select.tsx +0 -169
  388. package/src/react/ui/separator.tsx +0 -25
  389. package/src/react/ui/sheet.tsx +0 -136
  390. package/src/react/ui/sidebar.tsx +0 -723
  391. package/src/react/ui/skeleton.tsx +0 -13
  392. package/src/react/ui/slider.tsx +0 -34
  393. package/src/react/ui/switch.tsx +0 -28
  394. package/src/react/ui/table.tsx +0 -105
  395. package/src/react/ui/tabs.tsx +0 -63
  396. package/src/react/ui/textarea.tsx +0 -18
  397. package/src/react/ui/tooltip.tsx +0 -64
  398. package/src/react/useResizableWidth.ts +0 -139
  399. package/src/react/utils.ts +0 -6
  400. package/src/react/widgetRegistry.test.ts +0 -43
  401. package/src/react/widgetRegistry.ts +0 -50
  402. package/src/react/widgets/StatsOverviewRenderer.tsx +0 -232
  403. package/src/react/widgets/TableWidgetRenderer.tsx +0 -231
  404. package/src/react/widgets/ViewRenderer.tsx +0 -71
  405. package/src/relationManagerData.test.ts +0 -1595
  406. package/src/richtext/index.ts +0 -8
  407. package/src/richtext/registry.ts +0 -89
  408. package/src/routes/globals.ts +0 -148
  409. package/src/routes/guard.test.ts +0 -325
  410. package/src/routes/helpers.ts +0 -704
  411. package/src/routes/pages.ts +0 -175
  412. package/src/routes/panel.ts +0 -204
  413. package/src/routes/relations.ts +0 -1243
  414. package/src/routes/resources.ts +0 -781
  415. package/src/routes/theme.ts +0 -91
  416. package/src/routes-nested-relations.test.ts +0 -676
  417. package/src/routes-relations.test.ts +0 -972
  418. package/src/routes.test.ts +0 -2027
  419. package/src/routes.ts +0 -303
  420. package/src/schema/Alert.test.ts +0 -109
  421. package/src/schema/Alert.ts +0 -131
  422. package/src/schema/Block.ts +0 -169
  423. package/src/schema/Breadcrumbs.ts +0 -40
  424. package/src/schema/Card.ts +0 -35
  425. package/src/schema/Divider.ts +0 -20
  426. package/src/schema/Element.ts +0 -219
  427. package/src/schema/EmptyState.test.ts +0 -37
  428. package/src/schema/EmptyState.ts +0 -63
  429. package/src/schema/Fieldset.ts +0 -43
  430. package/src/schema/Grid.ts +0 -43
  431. package/src/schema/Group.ts +0 -30
  432. package/src/schema/Heading.ts +0 -39
  433. package/src/schema/Html.ts +0 -67
  434. package/src/schema/Icon.ts +0 -54
  435. package/src/schema/Image.ts +0 -57
  436. package/src/schema/LinkTag.ts +0 -41
  437. package/src/schema/Markdown.ts +0 -85
  438. package/src/schema/MetaTag.ts +0 -41
  439. package/src/schema/RelationTabs.ts +0 -71
  440. package/src/schema/ScriptTag.ts +0 -55
  441. package/src/schema/Section.ts +0 -160
  442. package/src/schema/ServerDataElement.test.ts +0 -140
  443. package/src/schema/ServerDataElement.ts +0 -156
  444. package/src/schema/SlotComponent.test.ts +0 -77
  445. package/src/schema/SlotComponent.ts +0 -71
  446. package/src/schema/Split.ts +0 -50
  447. package/src/schema/Stat.test.ts +0 -118
  448. package/src/schema/Stat.ts +0 -154
  449. package/src/schema/StatsOverview.test.ts +0 -141
  450. package/src/schema/StatsOverview.ts +0 -119
  451. package/src/schema/StyleTag.ts +0 -35
  452. package/src/schema/TableWidget.test.ts +0 -297
  453. package/src/schema/TableWidget.ts +0 -289
  454. package/src/schema/Tabs.ts +0 -79
  455. package/src/schema/Text.ts +0 -58
  456. package/src/schema/UnorderedList.ts +0 -49
  457. package/src/schema/View.test.ts +0 -111
  458. package/src/schema/View.ts +0 -127
  459. package/src/schema/Wizard.ts +0 -220
  460. package/src/schema/containers.test.ts +0 -564
  461. package/src/schema/headTags.test.ts +0 -134
  462. package/src/schema/index.ts +0 -40
  463. package/src/schema/primes.test.ts +0 -269
  464. package/src/schema/resolveSchema.test.ts +0 -379
  465. package/src/schema/resolveSchema.ts +0 -917
  466. package/src/schema/sanitize.ts +0 -58
  467. package/src/search.test.ts +0 -446
  468. package/src/search.ts +0 -178
  469. package/src/sessionFilters.test.ts +0 -375
  470. package/src/sessionFilters.ts +0 -143
  471. package/src/slot-components/index.ts +0 -10
  472. package/src/slot-components/registry.ts +0 -56
  473. package/src/styles/file-upload.css +0 -13
  474. package/src/summarizers/Summarizer.test.ts +0 -84
  475. package/src/summarizers/Summarizer.ts +0 -123
  476. package/src/summarizers/index.ts +0 -11
  477. package/src/theme/base-colors.ts +0 -68
  478. package/src/theme/chart-colors.ts +0 -50
  479. package/src/theme/colors.ts +0 -447
  480. package/src/theme/generate-css.test.ts +0 -139
  481. package/src/theme/generate-css.ts +0 -44
  482. package/src/theme/generate-scale.test.ts +0 -106
  483. package/src/theme/generate-scale.ts +0 -97
  484. package/src/theme/icon-map.ts +0 -42
  485. package/src/theme/index.ts +0 -34
  486. package/src/theme/migrate.test.ts +0 -178
  487. package/src/theme/migrate.ts +0 -81
  488. package/src/theme/presets.ts +0 -135
  489. package/src/theme/radius.ts +0 -18
  490. package/src/theme/resolve.test.ts +0 -238
  491. package/src/theme/resolve.ts +0 -96
  492. package/src/theme/spacing.ts +0 -18
  493. package/src/theme/storage.test.ts +0 -126
  494. package/src/theme/storage.ts +0 -106
  495. package/src/theme/theme-colors.ts +0 -88
  496. package/src/theme/types.ts +0 -125
  497. package/src/uploads/UploadAdapter.ts +0 -35
  498. package/src/uploads/index.ts +0 -2
  499. package/src/uploads/localUpload.test.ts +0 -70
  500. package/src/uploads/localUpload.ts +0 -84
  501. package/src/validation/Validator.ts +0 -49
  502. package/src/validation/index.ts +0 -28
  503. package/src/validation/rules.ts +0 -78
  504. package/src/validation/runValidators.ts +0 -435
  505. package/src/validation/uniqueValidator.test.ts +0 -196
  506. package/src/validation/uniqueValidator.ts +0 -133
  507. package/src/validation/validators.test.ts +0 -268
  508. package/src/vite.test.ts +0 -184
  509. package/src/vite.ts +0 -787
  510. package/src/widgets/index.ts +0 -10
  511. package/src/widgets/registry.ts +0 -45
  512. package/src/widgets.test.ts +0 -592
  513. package/tsconfig.build.json +0 -11
  514. package/tsconfig.json +0 -4
  515. package/tsconfig.test.json +0 -10
  516. package/views/react/Dashboard.tsx +0 -27
  517. package/views/react/Resources/Form.tsx +0 -102
  518. package/views/react/Resources/Index.tsx +0 -49
@@ -1,850 +0,0 @@
1
- import { Element, type ElementMeta } from '../schema/Element.js'
2
- import { Column } from '../Column.js'
3
- import { Action } from '../actions/Action.js'
4
- import { ActionGroup } from '../actions/ActionGroup.js'
5
- import { Filter } from '../filters/Filter.js'
6
- import type { SummaryResult } from '../summarizers/Summarizer.js'
7
- import { TableGroup, type TableGroupMeta } from './TableGroup.js'
8
-
9
- /** Either a plain `Action` or an `ActionGroup` (a labelled dropdown of
10
- * actions). Both can sit in any of the table action slots — the slot
11
- * stamps the placement automatically on whichever shape arrives. */
12
- type ActionOrGroup = Action | ActionGroup
13
-
14
- export type SortDirection = 'asc' | 'desc'
15
-
16
- export interface TableContext<R = unknown> {
17
- request?: unknown
18
- search?: string
19
- sort?: { column: string; direction: SortDirection }
20
- page?: number
21
- perPage?: number
22
- records?: R[]
23
- /** Active filter values keyed by filter name (e.g. `{ status: 'published' }`).
24
- * Empty / unsupplied filters are absent. */
25
- filters?: Record<string, string>
26
- /** Active list-page tab name (e.g. `'drafts'`). Set by
27
- * `pageData.resourceIndexData` from `?tab=` before records run. User-
28
- * supplied `Table.records(fn)` handlers can branch on this for custom
29
- * narrowing; the model adapter consults `tabQuery` instead. */
30
- tab?: string
31
- /** Active list-page tab's `modifyQuery` chain — applied alongside
32
- * filter `where` clauses in `modelTableRecords`. Set by the framework;
33
- * users configure it via `ListTab.modifyQuery(fn)`. */
34
- tabQuery?: (q: import('../orm/modelDefaults.js').ModelQuery) => import('../orm/modelDefaults.js').ModelQuery
35
- /** Drill-in scope when the user clicked a group heading. Carries the
36
- * resolved `TableGroup` instance + the bucket key (the same value
37
- * `getKeyFromRecordUsing` would produce). Set by `loadTableRecords`
38
- * after reconciling `?<prefix>groupKey=`. The model adapter calls
39
- * `group.resolveScoper()(q, key)` after filters but before pagination;
40
- * user-supplied `Table.records(fn)` handlers can branch on this for
41
- * cross-table joins or non-default narrowing. */
42
- groupScope?: {
43
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
- group: import('./TableGroup.js').TableGroup<any>
45
- key: string
46
- }
47
- /** Whatever `Pilotiq.user(req => …)` returned for the current request.
48
- * Forwarded into `Resource.query(ctx)` by `modelTableRecords` so user-
49
- * installed scopes (tenant filters, etc.) see the same opaque user
50
- * shape that authorization predicates do. Absent on the dispatcher's
51
- * input ctx when no user resolver is configured. */
52
- user?: unknown
53
- [key: string]: unknown
54
- }
55
-
56
- export type TableQueryHandler<Q = unknown> = (
57
- query: Q,
58
- ctx: TableContext,
59
- ) => Q | Promise<Q>
60
-
61
- /**
62
- * User-supplied row loader. Returns the records to render plus an optional
63
- * `total` for pagination. When `total` is omitted the framework treats
64
- * `rows.length` as the total.
65
- */
66
- export interface TableRecordsResult<R = unknown> {
67
- rows: R[]
68
- total?: number
69
- }
70
-
71
- export type TableRecordsHandler<R = unknown> = (
72
- ctx: TableContext<R>,
73
- ) => TableRecordsResult<R> | R[] | Promise<TableRecordsResult<R> | R[]>
74
-
75
- export interface TableEmptyState {
76
- heading?: string
77
- description?: string
78
- icon?: string
79
- }
80
-
81
- /**
82
- * Per-row URL function. Returns the destination URL for clicks on a
83
- * row's data cells — each data column wraps its content in a real
84
- * `<a href>` (Filament-style), so right-click / cmd-click / middle-click
85
- * "open in new tab" all work natively. Plain left-clicks are intercepted
86
- * for SPA navigation. Return `undefined` for rows that shouldn't be
87
- * clickable. Action and bulk-select cells are never wrapped.
88
- *
89
- * Per-column overrides: `Column.recordUrl(fn)` swaps in a different URL
90
- * for that column's cell, and `Column.recordUrl(false)` opts a column
91
- * out entirely.
92
- */
93
- export type RecordUrlHandler<R = unknown> = (record: R) => string | undefined
94
-
95
- /**
96
- * Per-row CSS class function. Returns extra Tailwind / CSS class names
97
- * appended to that row's `<tr>`. Useful for status-driven row tinting
98
- * ("destructive" when overdue, "warning" when stale). Result is appended
99
- * after the framework's own row classes (striped, cursor-pointer); user
100
- * classes win on equal specificity. Throwing or returning falsy stays
101
- * silent — the row just renders without extras.
102
- */
103
- export type RecordClassesHandler<R = unknown> = (record: R) => string | undefined
104
-
105
- /**
106
- * Per-row card content function. Receives the record + the current
107
- * `TableContext` and returns an `Element[]` rendered inside a card in
108
- * `contentLayout('cards')` mode. Resolved via `resolveSchema` per-row in
109
- * `loadTableRecords`; the result is stamped onto `row._cardChildren`.
110
- *
111
- * Typical content: `Image`, `Heading`, `Text`, `Icon`, `Badge`-style
112
- * `Entry` primitives, plus layout primitives like `Group / Split / Grid`.
113
- * Display-only — `Form` / `Field` / `Filter` / `Action` inside the card
114
- * schema is unsupported in v1.
115
- */
116
- export type CardSchemaHandler<R = unknown> = (
117
- record: R,
118
- ctx: TableContext<R>,
119
- ) => Element[] | Promise<Element[]>
120
-
121
- /**
122
- * Responsive grid column counts for `contentLayout('cards')`. Each
123
- * breakpoint maps to its Tailwind container query (`@sm` = ≥40rem etc.);
124
- * `default` is the base (no media query). Unspecified breakpoints inherit
125
- * the next-smaller. v1 caps each value at 12 to match the grid's column
126
- * limits — values outside `[1, 12]` clamp.
127
- */
128
- export interface CardsPerRow {
129
- default?: number
130
- sm?: number
131
- md?: number
132
- lg?: number
133
- xl?: number
134
- '2xl'?: number
135
- }
136
-
137
- /**
138
- * Table content-layout mode. `'table'` (default) renders the classic
139
- * `<thead>` + `<tbody>` HTML table. `'cards'` hides the header row and
140
- * renders each row as a card in a CSS grid. Columns still drive search /
141
- * sort / filter / group / summarize semantics in cards mode; only the
142
- * row-level rendering differs.
143
- */
144
- export type ContentLayout = 'table' | 'cards'
145
-
146
- /**
147
- * Where filters render relative to the table.
148
- * - `'modal'` (default) — chrome-only popover above the table, opened by
149
- * the toolbar Filters button.
150
- * - `'above-content'` — inline strip rendered between the header bar and
151
- * the table; every filter widget is always visible.
152
- * - `'above-content-collapsible'` — same strip, hidden behind the toolbar
153
- * Filters button. Open / closed state persists per table path.
154
- * - `'below-content'` — inline strip rendered after the table (under the
155
- * pagination row).
156
- *
157
- * Filament v5's sidebar positions (`BeforeContent` / `AfterContent`)
158
- * reshape the page rather than the table chrome and are deferred until a
159
- * consumer asks.
160
- */
161
- export type FiltersLayout =
162
- | 'modal'
163
- | 'above-content'
164
- | 'above-content-collapsible'
165
- | 'below-content'
166
-
167
- export interface TableMeta extends ElementMeta {
168
- type: 'table'
169
- defaultSort?: { column: string; direction: SortDirection }
170
- perPage?: number
171
- searchable: boolean
172
-
173
- // Top-bar chrome
174
- heading?: string
175
- description?: string
176
- striped?: boolean
177
- emptyState?: TableEmptyState
178
- /**
179
- * Distinct empty-state for the "filter/search active but no rows match"
180
- * case. When set AND a search query or one or more URL filter keys are
181
- * present, the renderer picks this shape over `emptyState`. When unset,
182
- * the renderer falls back to `emptyState` (or the framework defaults
183
- * — "No matching records" with a generic clear-filters hint).
184
- */
185
- filteredEmptyState?: TableEmptyState
186
-
187
- /**
188
- * Per-row URL stamped onto each row's data under the reserved
189
- * `_recordUrl` key (alongside the existing `_visibleActions` /
190
- * `_disabledActions` / `_formatted` keys). The renderer reads from
191
- * the row, not the table meta — `RecordUrlHandler` is server-side only.
192
- */
193
- recordUrl?: true
194
-
195
- /**
196
- * Server-side per-row CSS marker — same convention as `recordUrl`.
197
- * Each row's `_recordClasses` carries the resolved string; this flag
198
- * is just a hint for the renderer to look for it.
199
- */
200
- recordClasses?: true
201
-
202
- /**
203
- * Auto-refresh interval in seconds. The client renderer kicks off a
204
- * `setInterval` that re-fetches the current URL via the SPA navigator
205
- * — pagination / sort / filter state is preserved because we re-visit
206
- * the same `pathname + search`. Hidden tabs pause to avoid hammering
207
- * the server in the background; resume on visibility change. Unset =
208
- * no polling.
209
- */
210
- pollInterval?: number
211
-
212
- /** The currently active group — the column rows are banded by. Set
213
- * each request from either `?group=` or `defaultGroup(...)`. The
214
- * renderer emits a heading row whenever `_groupValue` changes between
215
- * adjacent rows. Always a column name; the rich metadata (label /
216
- * collapsibility / etc.) lives on `groups` below. */
217
- defaultGroup?: string
218
-
219
- /** Group selector options. When 2+ groups are registered the renderer
220
- * mounts a "Group by" dropdown above the table; the user's selection
221
- * round-trips via `?group=`. Empty / absent means the bare-column form
222
- * (`Table.defaultGroup('col')` only). */
223
- groups?: TableGroupMeta[]
224
-
225
- /** The drilled-in group key for this request. When set, the renderer
226
- * suppresses group banding (no heading rows, no per-group summaries),
227
- * shows a "Drilled into <Label>: <Key>" chip above the table with an
228
- * × to clear, and the records have already been narrowed to that
229
- * bucket server-side via `TableGroup.scopeQueryByKey`. Sparse —
230
- * omitted unless `?<prefix>groupKey=<value>` is present AND the named
231
- * group exists AND is `scopable`. */
232
- activeGroupKey?: string
233
-
234
- /** Per-column summary results — keyed by column name, each entry is the
235
- * computed `SummaryResult[]` for that column's `summarize([…])`. Filled
236
- * in by `loadTableRecords` after `records()` runs. Renderer emits a
237
- * `<tfoot>` row when this is present. */
238
- summaries?: Record<string, SummaryResult[]>
239
-
240
- /** Per-group summaries — outer key = `_groupValue`, inner = column
241
- * name, value = `SummaryResult[]`. Computed only when an active group
242
- * is set AND at least one column has summarizers. Renderer emits an
243
- * inline summary row at the END of each group band, aligned to the
244
- * same columns as the global footer. */
245
- groupSummaries?: Record<string, Record<string, SummaryResult[]>>
246
-
247
- /** Drag-to-reorder is enabled on this table. The renderer adds a grip
248
- * handle column and binds HTML5 DnD on each `<tr>`. The actual sort
249
- * column the order is written back to lives on `reorderableColumn`. */
250
- reorderable?: true
251
-
252
- /** Name of the column the persisted order is written to. Defaults to
253
- * `'sort'` when `Table.reorderable()` is called without an argument.
254
- * Pilotiq's default `Table.records()` adapter uses this as the default
255
- * sort column when the URL doesn't override `?sort=…`. */
256
- reorderableColumn?: string
257
-
258
- /** Stamped server-side at render time. The renderer POSTs the new id
259
- * order here when a row is dropped. Absent when the route handler
260
- * hasn't tagged the table (panel boot will throw before that point if
261
- * `reorderable()` is set without a corresponding `model.reorder`). */
262
- reorderUrl?: string
263
-
264
- /** Tier-3 — set when `Resource.deferLoading = true`. The SSR pass
265
- * skips `Table.records()` so the client paints a skeleton on first
266
- * frame and fetches rows from `tableUrl` after mount. URL chrome
267
- * (current sort / search / page / group) still mirrors so the
268
- * skeleton frame doesn't reset user-visible state. */
269
- deferred?: true
270
-
271
- /** Stamped server-side at render time alongside `deferred`. The
272
- * renderer GETs this URL with the page's current query string to
273
- * fetch rows after mount. Empty when `deferLoading` is off. */
274
- tableUrl?: string
275
-
276
- /** Tier-3 — when set, the table's URL state (search / sort / page /
277
- * perPage / group / filter values) is namespaced under the identifier
278
- * so multiple tables on one page don't fight over `?search=` etc.
279
- * `?orders_search=foo&invoices_sort=date:desc`. Off by default — bare
280
- * keys are still used when no identifier is set. Custom-page-only:
281
- * resource list pages have one table by default and don't need it. */
282
- queryStringIdentifier?: string
283
-
284
- /** Content-layout mode. Absent = `'table'` (classic HTML table); when
285
- * `'cards'` the renderer hides the column header row and arranges rows
286
- * as cards in a CSS grid. Per-row card content is stamped on each row
287
- * under `_cardChildren: ElementMeta[]` by `loadTableRecords`. Columns
288
- * still drive search / sort / filter / group / summarize semantics. */
289
- contentLayout?: 'cards'
290
-
291
- /** Responsive card column counts for `contentLayout: 'cards'`. Renderer
292
- * maps each breakpoint to a `@container`-scoped Tailwind grid class. */
293
- cardsPerRow?: CardsPerRow
294
-
295
- /** Filter layout position. Absent = `'modal'` (current popover behavior).
296
- * The renderer swaps the toolbar Filters button for an inline strip in
297
- * the matching slot when set. See `FiltersLayout` for the full enum. */
298
- filtersLayout?: Exclude<FiltersLayout, 'modal'>
299
-
300
- // Render-time state — populated by the framework after `records()` runs.
301
- rows?: unknown[]
302
- total?: number
303
- currentSort?: { column: string; direction: SortDirection }
304
- search?: string
305
- currentPage?: number
306
- /** Absolute pathname the table lives at (e.g. `/admin/articles`). The
307
- * renderer prefixes sort/pagination/search hrefs with this so SPA
308
- * navigation resolves against the right route — Vike's client-side
309
- * router doesn't follow `?qs`-only relative links. */
310
- currentPath?: string
311
- }
312
-
313
- /**
314
- * Table container. Children are typically `Column[]` plus header / row /
315
- * bulk Actions. The query hook stays server-side; toMeta emits the
316
- * configured sort/pagination state, the searchable flag, and (after the
317
- * framework runs `records()`) the resolved rows + pagination state.
318
- */
319
- export class Table<R = unknown, Q = unknown> extends Element {
320
- private _query?: TableQueryHandler<Q>
321
- private _records?: TableRecordsHandler<R>
322
- private _defaultSort?: { column: string; direction: SortDirection }
323
- private _perPage?: number
324
-
325
- // Top-bar chrome
326
- private _heading?: string
327
- private _description?: string
328
- private _striped = false
329
- private _emptyState?: TableEmptyState
330
- private _filteredEmptyState?: TableEmptyState
331
-
332
- // Render-time state
333
- private _rows?: R[]
334
- private _total?: number
335
- private _currentSort?: { column: string; direction: SortDirection }
336
- private _currentSearch?: string
337
- private _currentPage?: number
338
- private _currentPath?: string
339
- private _recordUrl?: RecordUrlHandler<R>
340
- private _recordClasses?: RecordClassesHandler<R>
341
- private _pollInterval?: number
342
- private _defaultGroup?: string
343
- // Variance-relaxed — covariant `TableGroup<R>[]` ergonomics
344
- // matter more than tight invariance against the table's `R` parameter.
345
- private _groups: TableGroup<any>[] = [] // eslint-disable-line @typescript-eslint/no-explicit-any
346
- private _activeGroup?: string
347
- private _activeGroupKey?: string
348
- private _summaries?: Record<string, SummaryResult[]>
349
- private _groupSummaries?: Record<string, Record<string, SummaryResult[]>>
350
- private _reorderableColumn?: string
351
- private _reorderUrl?: string
352
- private _deferred = false
353
- private _tableUrl?: string
354
- private _queryStringIdentifier?: string
355
- private _contentLayout: ContentLayout = 'table'
356
- private _cardSchema?: CardSchemaHandler<R>
357
- private _cardsPerRow?: CardsPerRow
358
- private _filtersLayout: FiltersLayout = 'modal'
359
-
360
- private constructor() { super() }
361
-
362
- static make<R = unknown, Q = unknown>(): Table<R, Q> {
363
- return new Table<R, Q>()
364
- }
365
-
366
- // ─── Children ─────────────────────────────────────────
367
-
368
- /** Set children directly — typically a mix of Columns and Actions. */
369
- schema(elements: Element[]): this {
370
- this._children = elements
371
- return this
372
- }
373
-
374
- /** Shorthand: replace the column children. Existing actions are preserved. */
375
- columns(cols: Column[]): this {
376
- const existing = this._children ?? []
377
- const nonColumns = existing.filter(el => !(el instanceof Column))
378
- this._children = [...cols, ...nonColumns]
379
- return this
380
- }
381
-
382
- /** Shorthand: append actions to the children. Placement on each action /
383
- * group is preserved as-is; use the slot variants below
384
- * (`recordActions`, `headerActions`, `bulkActions`) when you want the
385
- * table to assign placement automatically. Both `Action` and
386
- * `ActionGroup` are accepted — groups render as dropdown triggers. */
387
- actions(acts: ActionOrGroup[]): this {
388
- const existing = this._children ?? []
389
- this._children = [...existing, ...acts]
390
- return this
391
- }
392
-
393
- /** Per-row actions slot — rendered in a DropdownMenu on each row.
394
- * Stamps `placement: 'row'` on each action so callers don't need
395
- * `Action.make(...).row()` boilerplate. */
396
- recordActions(acts: ActionOrGroup[]): this {
397
- return this.actions(acts.map(a => a.placement('row')))
398
- }
399
-
400
- /** Header actions slot — rendered top-right of the table. */
401
- headerActions(acts: ActionOrGroup[]): this {
402
- return this.actions(acts.map(a => a.placement('header')))
403
- }
404
-
405
- /** Bulk actions slot — shown in a toolbar when rows are selected. */
406
- bulkActions(acts: ActionOrGroup[]): this {
407
- return this.actions(acts.map(a => a.placement('bulk')))
408
- }
409
-
410
- /** Shorthand: replace the filter children. Existing columns/actions stay. */
411
- filters(filters: Filter[]): this {
412
- const existing = this._children ?? []
413
- const nonFilters = existing.filter(el => !(el instanceof Filter))
414
- this._children = [...nonFilters, ...filters]
415
- return this
416
- }
417
-
418
- // ─── Lifecycle config ────────────────────────────────
419
-
420
- /** Adapter-flavored query builder hook. Reserved for ORM adapters in Phase 3+. */
421
- query(fn: TableQueryHandler<Q>): this { this._query = fn; return this }
422
-
423
- /** Row loader — receives a `TableContext` and returns rows (and optional total). */
424
- records(fn: TableRecordsHandler<R>): this { this._records = fn; return this }
425
-
426
- defaultSort(column: string, direction: SortDirection = 'asc'): this {
427
- this._defaultSort = { column, direction }
428
- return this
429
- }
430
-
431
- paginate(perPage: number): this { this._perPage = perPage; return this }
432
-
433
- // ─── Top-bar chrome ───────────────────────────────────
434
-
435
- /** Title rendered above the table (left of the header bar). */
436
- heading(s: string): this { this._heading = s; return this }
437
-
438
- /** Subtitle rendered under the heading. */
439
- description(s: string): this { this._description = s; return this }
440
-
441
- /** Alternating row backgrounds for visual scanning. */
442
- striped(v = true): this { this._striped = v; return this }
443
-
444
- /** Customize the "no records" placeholder. */
445
- emptyState(state: TableEmptyState): this {
446
- this._emptyState = state
447
- return this
448
- }
449
-
450
- /**
451
- * Customize the "no matching records" placeholder shown when a search
452
- * query or filter is active but the result set is empty. Distinct from
453
- * `emptyState(...)` so the two cases can read differently — empty
454
- * tables typically want a "create your first one" call to action,
455
- * while filtered-empty tables want a "clear filters / adjust search"
456
- * hint.
457
- *
458
- * Falls back to `emptyState(...)` (or the framework defaults) when
459
- * unset, so opting into the distinction is purely additive.
460
- */
461
- filteredEmptyState(state: TableEmptyState): this {
462
- this._filteredEmptyState = state
463
- return this
464
- }
465
-
466
- /**
467
- * Set a per-row URL — each data cell renders as a real `<a href>` so
468
- * "open in new tab" works natively (right-click / cmd-click / middle-
469
- * click). Plain left-clicks SPA-navigate via `useNavigate()`. Action
470
- * and bulk-select cells stay unwrapped, so clicking a row action only
471
- * fires the action — there's no overlapping row-level click handler.
472
- *
473
- * The URL is stamped onto each row under the reserved `_recordUrl`
474
- * key during `loadTableRecords` — same convention as
475
- * `_visibleActions` / `_formatted`.
476
- *
477
- * Per-column overrides: pair with `Column.recordUrl(fn)` to swap a
478
- * column-specific URL, or `Column.recordUrl(false)` to opt a column
479
- * out (e.g. a column whose cell content has its own click affordance).
480
- */
481
- recordUrl(fn: RecordUrlHandler<R>): this {
482
- this._recordUrl = fn
483
- return this
484
- }
485
-
486
- /**
487
- * Per-row CSS class hook. The handler runs server-side once per row,
488
- * after `records()` resolves; the result is stamped under the reserved
489
- * `_recordClasses` key on the row and appended to the rendered `<tr>`'s
490
- * className. Pair with semantic Tailwind tokens (`bg-destructive/10`,
491
- * `text-warning`) so theming stays consistent.
492
- */
493
- recordClasses(fn: RecordClassesHandler<R>): this {
494
- this._recordClasses = fn
495
- return this
496
- }
497
-
498
- /**
499
- * Auto-refresh the table at a regular interval. `seconds` is positive;
500
- * non-positive values silently disable polling. SPA-friendly — the
501
- * client navigates to `pathname + search` (the current URL) so sort /
502
- * filter / pagination state survive the refresh, and AppShell stays
503
- * mounted. Polling pauses while the document is hidden.
504
- */
505
- poll(seconds: number): this {
506
- if (seconds > 0) this._pollInterval = seconds
507
- return this
508
- }
509
-
510
- /**
511
- * Band rows by a column's value. Stable-sorts the rendered rows so
512
- * shared values cluster together, then stamps each row's `_groupValue`
513
- * — the renderer inserts a heading row whenever the value changes.
514
- *
515
- * Accepts either a bare column name or a `TableGroup` instance. When
516
- * passed a `TableGroup` it's auto-added to `groups([…])` if it isn't
517
- * registered already, so `defaultGroup(TableGroup.make('status').label('Status'))`
518
- * doesn't require repeating the registration.
519
- */
520
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
521
- defaultGroup(group: string | TableGroup<any>): this {
522
- if (typeof group === 'string') {
523
- this._defaultGroup = group
524
- } else {
525
- this._defaultGroup = group.getColumn()
526
- const already = this._groups.some(g => g.getColumn() === group.getColumn())
527
- if (!already) this._groups.push(group)
528
- }
529
- return this
530
- }
531
-
532
- /**
533
- * Register the available group options. The renderer mounts a "Group
534
- * by" dropdown above the table when 2+ groups are registered (or 1
535
- * group with rich metadata — label / collapsible / record-derived
536
- * title). The active selection round-trips through the URL via the
537
- * reserved `?group=` key.
538
- */
539
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
540
- groups(items: TableGroup<any>[]): this {
541
- this._groups = items
542
- return this
543
- }
544
-
545
- /**
546
- * Enable drag-to-reorder. `column` names the model attribute the new
547
- * order is written back to (defaults to `'sort'`, matching Filament).
548
- * The route registers `POST {base}/{slug}/_reorder` — the renderer
549
- * POSTs `{ ids }` there on every drop. Boot panics when this is set
550
- * but `Resource.model.reorder` is missing.
551
- *
552
- * The reorder column also doubles as the default sort: when no
553
- * `defaultSort()` is configured and reorder is on, rows render
554
- * `(reorderColumn, asc)` so the visible order matches the persisted
555
- * order. URL `?sort=…` still wins.
556
- */
557
- reorderable(column: string = 'sort'): this {
558
- this._reorderableColumn = column
559
- return this
560
- }
561
-
562
- // ─── Render-time state ────────────────────────────────
563
-
564
- /** Attach loaded rows + total. Called by the framework after `records()` runs. */
565
- withRows(rows: R[], total?: number): this {
566
- this._rows = rows
567
- if (total !== undefined) this._total = total
568
- return this
569
- }
570
-
571
- withSort(column: string, direction: SortDirection): this {
572
- this._currentSort = { column, direction }
573
- return this
574
- }
575
-
576
- withSearch(query: string): this { this._currentSearch = query; return this }
577
- withPage(page: number): this { this._currentPage = page; return this }
578
- withCurrentPath(path: string): this { this._currentPath = path; return this }
579
- withSummaries(summaries: Record<string, SummaryResult[]>): this {
580
- this._summaries = summaries
581
- return this
582
- }
583
- /** Stamp per-group summary results. Outer key = `_groupValue`,
584
- * inner = column name. Set by `loadTableRecords` only when an active
585
- * group is set AND at least one column has summarizers. */
586
- withGroupSummaries(
587
- groupSummaries: Record<string, Record<string, SummaryResult[]>>,
588
- ): this {
589
- this._groupSummaries = groupSummaries
590
- return this
591
- }
592
-
593
- /** Stamp the reorder POST URL. Called by `tagTableReorderUrls` during
594
- * `resourceIndexData` so the renderer knows where to send drops. */
595
- withReorderUrl(url: string): this {
596
- this._reorderUrl = url
597
- return this
598
- }
599
-
600
- /** Mark this table as deferred — the SSR pass skips `records()` and
601
- * the client fetches rows from `tableUrl` after mount. Set by
602
- * `resourceIndexData` when `Resource.deferLoading = true`; user code
603
- * doesn't typically call this directly. */
604
- withDeferred(v: boolean = true): this {
605
- this._deferred = v
606
- return this
607
- }
608
-
609
- /** Stamp the deferred-load fetch URL. Set alongside `withDeferred`
610
- * by `tagTableUrls` during `resourceIndexData`. */
611
- withTableUrl(url: string): this {
612
- this._tableUrl = url
613
- return this
614
- }
615
-
616
- /**
617
- * Namespace this table's URL state under an identifier so multiple
618
- * tables on the same page don't collide on `?search=` / `?sort=` /
619
- * `?page=` / filter keys. With `queryStringIdentifier('orders')`, every
620
- * key is read and written as `orders_<key>` (e.g. `?orders_search=foo`,
621
- * `?orders_sort=date:desc`, `?orders_status=open`). Off by default —
622
- * resource list pages have one Table and don't need a prefix.
623
- *
624
- * The empty string is rejected (would silently disable namespacing
625
- * while implying it's on); identifiers must match `[A-Za-z0-9_-]+`
626
- * so they don't smuggle reserved characters into URL keys.
627
- */
628
- queryStringIdentifier(id: string): this {
629
- if (!/^[A-Za-z0-9_-]+$/.test(id)) {
630
- throw new Error(
631
- `Table.queryStringIdentifier: invalid id ${JSON.stringify(id)} — must match /^[A-Za-z0-9_-]+$/`,
632
- )
633
- }
634
- this._queryStringIdentifier = id
635
- return this
636
- }
637
-
638
- /**
639
- * Pick the content layout for this table. `'table'` (default) renders
640
- * the classic `<thead>` + `<tbody>` HTML table. `'cards'` hides the
641
- * column header row and renders each row as a card in a CSS grid; the
642
- * per-row content comes from `cardSchema(...)`.
643
- *
644
- * Columns still drive search / sort / filter / group / summarize
645
- * semantics in cards mode — they're just not painted as table headers.
646
- * The top-bar gains a "Sort by" dropdown built from `Column.sortable()`
647
- * columns since column headers (the usual sort affordance) are hidden.
648
- */
649
- contentLayout(layout: ContentLayout): this {
650
- this._contentLayout = layout
651
- return this
652
- }
653
-
654
- /** Sugar for `contentLayout('cards')`. */
655
- cards(): this { return this.contentLayout('cards') }
656
-
657
- /**
658
- * Per-row card content. Returns an `Element[]` rendered inside a card
659
- * for the given record + ctx. Resolved server-side per row in
660
- * `loadTableRecords` and stamped on `row._cardChildren`. Required when
661
- * `contentLayout === 'cards'`; `toMeta()` throws otherwise.
662
- *
663
- * Display-only — `Form / Field / Filter / Action` inside the card
664
- * schema is unsupported in v1. Reuse `Heading`, `Text`, `Image`, `Icon`,
665
- * `Group / Split / Grid`, and the read-only `Entry` family
666
- * (`TextEntry / BadgeEntry / IconEntry / ImageEntry / KeyValueEntry /
667
- * ColorEntry / ComponentEntry`) for content.
668
- */
669
- cardSchema(fn: CardSchemaHandler<R>): this {
670
- this._cardSchema = fn
671
- return this
672
- }
673
-
674
- /**
675
- * Responsive grid column counts in cards mode. Each entry maps to a
676
- * Tailwind container query (`@sm` = ≥40rem, etc.). Unspecified
677
- * breakpoints inherit the next-smaller; `default` is the base.
678
- * Values clamp to `[1, 12]`. Default `{ default: 1, sm: 2, lg: 3 }`.
679
- */
680
- cardsPerRow(opts: CardsPerRow): this {
681
- this._cardsPerRow = opts
682
- return this
683
- }
684
-
685
- /**
686
- * Where filters render relative to the table. Default `'modal'` keeps
687
- * the toolbar Filters button + popover. The three inline modes
688
- * (`'above-content'` / `'above-content-collapsible'` / `'below-content'`)
689
- * lay every filter widget out as a horizontal strip in place of the
690
- * popover.
691
- */
692
- filtersLayout(layout: FiltersLayout): this {
693
- this._filtersLayout = layout
694
- return this
695
- }
696
-
697
- /** Render-time setter — the column rows are actually banded by for
698
- * this request, after `?group=` and `defaultGroup(...)` are reconciled.
699
- * Set by `loadTableRecords`. Empty string explicitly clears (URL
700
- * `?group=` overrode `defaultGroup`). */
701
- withActiveGroup(column: string | undefined): this {
702
- if (column === undefined) delete this._activeGroup
703
- else this._activeGroup = column
704
- return this
705
- }
706
-
707
- /** Render-time setter — the drilled-in group key for this request,
708
- * after `?<prefix>groupKey=` was reconciled against a `scopable`
709
- * group. Set by `loadTableRecords`. Empty string / undefined both
710
- * clear, since drill-in needs an actual key to scope against. */
711
- withActiveGroupKey(key: string | undefined): this {
712
- if (key === undefined || key === '') delete this._activeGroupKey
713
- else this._activeGroupKey = key
714
- return this
715
- }
716
-
717
- // ─── Getters ──────────────────────────────────────────
718
-
719
- getQuery(): TableQueryHandler<Q> | undefined { return this._query }
720
- getRecords(): TableRecordsHandler<R> | undefined { return this._records }
721
- getDefaultSort(): { column: string; direction: SortDirection } | undefined { return this._defaultSort }
722
- getPerPage(): number | undefined { return this._perPage }
723
- getRows(): R[] | undefined { return this._rows }
724
- getTotal(): number | undefined { return this._total }
725
- getCurrentSort(): { column: string; direction: SortDirection } | undefined { return this._currentSort }
726
- getCurrentSearch(): string | undefined { return this._currentSearch }
727
- getCurrentPage(): number | undefined { return this._currentPage }
728
- getCurrentPath(): string | undefined { return this._currentPath }
729
- getRecordUrl(): RecordUrlHandler<R> | undefined { return this._recordUrl }
730
- getRecordClasses(): RecordClassesHandler<R> | undefined { return this._recordClasses }
731
- getPollInterval(): number | undefined { return this._pollInterval }
732
- getDefaultGroup(): string | undefined { return this._defaultGroup }
733
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
734
- getGroups(): TableGroup<any>[] { return this._groups }
735
- getActiveGroup(): string | undefined { return this._activeGroup }
736
- getActiveGroupKey(): string | undefined { return this._activeGroupKey }
737
- /** Resolve the active column to a `TableGroup` instance. Returns the
738
- * matching registered group, or — when the active column is set but
739
- * not registered (bare-column form) — synthesizes a no-metadata group
740
- * so the dispatcher has a uniform shape to work with. */
741
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
742
- getActiveGroupInstance(): TableGroup<any> | undefined {
743
- const col = this._activeGroup
744
- if (!col) return undefined
745
- const found = this._groups.find(g => g.getColumn() === col)
746
- return found ?? TableGroup.make(col)
747
- }
748
- getSummaries(): Record<string, SummaryResult[]> | undefined { return this._summaries }
749
- getGroupSummaries(): Record<string, Record<string, SummaryResult[]>> | undefined { return this._groupSummaries }
750
- getReorderableColumn(): string | undefined { return this._reorderableColumn }
751
- isReorderable(): boolean { return this._reorderableColumn !== undefined }
752
- getReorderUrl(): string | undefined { return this._reorderUrl }
753
- isDeferred(): boolean { return this._deferred }
754
- getTableUrl(): string | undefined { return this._tableUrl }
755
- getQueryStringIdentifier(): string | undefined { return this._queryStringIdentifier }
756
- getContentLayout(): ContentLayout { return this._contentLayout }
757
- isCardsLayout(): boolean { return this._contentLayout === 'cards' }
758
- getCardSchema(): CardSchemaHandler<R> | undefined { return this._cardSchema }
759
- getCardsPerRow(): CardsPerRow | undefined { return this._cardsPerRow }
760
- getFiltersLayout(): FiltersLayout { return this._filtersLayout }
761
-
762
- /** Convenience: the `Column` children only. */
763
- getColumns(): Column[] {
764
- return (this._children ?? []).filter((el): el is Column => el instanceof Column)
765
- }
766
-
767
- /** Convenience: the `Filter` children only. */
768
- getFilters(): Filter[] {
769
- return (this._children ?? []).filter((el): el is Filter => el instanceof Filter)
770
- }
771
-
772
- // ─── Serialization ────────────────────────────────────
773
-
774
- getType(): string { return 'table' }
775
-
776
- override toMeta(): TableMeta {
777
- const searchable = this.getColumns().some(c => c.isSearchable())
778
- if (this._contentLayout === 'cards' && this._cardSchema === undefined) {
779
- throw new Error(
780
- 'Table.contentLayout("cards") requires .cardSchema((record, ctx) => Element[]). ' +
781
- 'Cards mode renders each row from a per-row schema; configure one before rendering.',
782
- )
783
- }
784
- const cardsPerRow = this._cardsPerRow !== undefined
785
- ? clampCardsPerRow(this._cardsPerRow)
786
- : undefined
787
- return {
788
- type: 'table',
789
- searchable,
790
- ...(this._defaultSort ? { defaultSort: this._defaultSort } : {}),
791
- ...(this._perPage !== undefined ? { perPage: this._perPage } : {}),
792
- ...(this._heading !== undefined ? { heading: this._heading } : {}),
793
- ...(this._description !== undefined ? { description: this._description } : {}),
794
- ...(this._striped ? { striped: true } : {}),
795
- ...(this._emptyState !== undefined ? { emptyState: this._emptyState } : {}),
796
- ...(this._filteredEmptyState !== undefined ? { filteredEmptyState: this._filteredEmptyState } : {}),
797
- ...(this._recordUrl !== undefined ? { recordUrl: true as const } : {}),
798
- ...(this._recordClasses !== undefined ? { recordClasses: true as const } : {}),
799
- ...(this._pollInterval !== undefined ? { pollInterval: this._pollInterval } : {}),
800
- // `defaultGroup` on the meta means "the column rows are actually
801
- // grouped by for this render". Prefer the render-time activeGroup
802
- // (set by `loadTableRecords` after reconciling `?group=` and the
803
- // configured default); fall back to the configured default when
804
- // no request-side reconciliation has happened (e.g. tests calling
805
- // `toMeta()` directly).
806
- ...(this._activeGroup !== undefined
807
- ? (this._activeGroup === ''
808
- ? {}
809
- : { defaultGroup: this._activeGroup })
810
- : (this._defaultGroup !== undefined
811
- ? { defaultGroup: this._defaultGroup }
812
- : {})),
813
- ...(this._groups.length > 0
814
- ? { groups: this._groups.map(g => g.toMeta()) }
815
- : {}),
816
- ...(this._summaries !== undefined ? { summaries: this._summaries } : {}),
817
- ...(this._groupSummaries !== undefined ? { groupSummaries: this._groupSummaries } : {}),
818
- ...(this._activeGroupKey !== undefined ? { activeGroupKey: this._activeGroupKey } : {}),
819
- ...(this._reorderableColumn !== undefined ? { reorderable: true as const, reorderableColumn: this._reorderableColumn } : {}),
820
- ...(this._reorderUrl !== undefined ? { reorderUrl: this._reorderUrl } : {}),
821
- ...(this._deferred ? { deferred: true as const } : {}),
822
- ...(this._tableUrl !== undefined ? { tableUrl: this._tableUrl } : {}),
823
- ...(this._queryStringIdentifier !== undefined
824
- ? { queryStringIdentifier: this._queryStringIdentifier } : {}),
825
- ...(this._contentLayout === 'cards' ? { contentLayout: 'cards' as const } : {}),
826
- ...(cardsPerRow !== undefined ? { cardsPerRow } : {}),
827
- ...(this._filtersLayout !== 'modal' ? { filtersLayout: this._filtersLayout } : {}),
828
- ...(this._rows !== undefined ? { rows: this._rows } : {}),
829
- ...(this._total !== undefined ? { total: this._total } : {}),
830
- ...(this._currentSort !== undefined ? { currentSort: this._currentSort } : {}),
831
- ...(this._currentSearch !== undefined ? { search: this._currentSearch } : {}),
832
- ...(this._currentPage !== undefined ? { currentPage: this._currentPage } : {}),
833
- ...(this._currentPath !== undefined ? { currentPath: this._currentPath } : {}),
834
- }
835
- }
836
- }
837
-
838
- const CARDS_PER_ROW_KEYS = ['default', 'sm', 'md', 'lg', 'xl', '2xl'] as const
839
-
840
- function clampCardsPerRow(opts: CardsPerRow): CardsPerRow {
841
- const out: CardsPerRow = {}
842
- for (const key of CARDS_PER_ROW_KEYS) {
843
- const v = opts[key]
844
- if (v === undefined) continue
845
- const n = Math.floor(Number(v))
846
- if (!Number.isFinite(n)) continue
847
- out[key] = Math.min(12, Math.max(1, n))
848
- }
849
- return out
850
- }