@pilotiq/pilotiq 0.23.1 → 0.24.2

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 (500) hide show
  1. package/CHANGELOG.md +91 -0
  2. package/boost/guidelines.md +566 -0
  3. package/boost/skills/pilotiq-fields/SKILL.md +47 -0
  4. package/boost/skills/pilotiq-fields/rules/field-catalog.md +288 -0
  5. package/boost/skills/pilotiq-fields/rules/reactive-fields.md +199 -0
  6. package/boost/skills/pilotiq-fields/rules/validation.md +198 -0
  7. package/boost/skills/pilotiq-relations/SKILL.md +47 -0
  8. package/boost/skills/pilotiq-relations/rules/relation-managers.md +256 -0
  9. package/boost/skills/pilotiq-relations/rules/repeater-relationship.md +177 -0
  10. package/boost/skills/pilotiq-resource/SKILL.md +61 -0
  11. package/boost/skills/pilotiq-resource/rules/authorization.md +242 -0
  12. package/boost/skills/pilotiq-resource/rules/defining-resources.md +228 -0
  13. package/boost/skills/pilotiq-resource/rules/page-overrides.md +296 -0
  14. package/dist/actions/exportFactory.d.ts +10 -0
  15. package/dist/actions/exportFactory.d.ts.map +1 -1
  16. package/dist/actions/exportFactory.js +10 -0
  17. package/dist/actions/exportFactory.js.map +1 -1
  18. package/dist/react/CollabRoomContext.d.ts +5 -5
  19. package/dist/react/index.d.ts +0 -1
  20. package/dist/react/index.d.ts.map +1 -1
  21. package/dist/react/index.js +0 -1
  22. package/dist/react/index.js.map +1 -1
  23. package/dist/routes/helpers.d.ts.map +1 -1
  24. package/dist/routes/helpers.js +6 -2
  25. package/dist/routes/helpers.js.map +1 -1
  26. package/dist/routes/relations.d.ts.map +1 -1
  27. package/dist/routes/relations.js +12 -0
  28. package/dist/routes/relations.js.map +1 -1
  29. package/package.json +6 -1
  30. package/.turbo/turbo-build.log +0 -8
  31. package/CLAUDE.md +0 -265
  32. package/dist/react/useCollabSeed.d.ts +0 -23
  33. package/dist/react/useCollabSeed.d.ts.map +0 -1
  34. package/dist/react/useCollabSeed.js +0 -82
  35. package/dist/react/useCollabSeed.js.map +0 -1
  36. package/src/Cluster.test.ts +0 -283
  37. package/src/Cluster.ts +0 -83
  38. package/src/Column.test.ts +0 -199
  39. package/src/Column.ts +0 -710
  40. package/src/Global.test.ts +0 -367
  41. package/src/Global.ts +0 -169
  42. package/src/Page.test.ts +0 -114
  43. package/src/Page.ts +0 -208
  44. package/src/Pilotiq.perf.test.ts +0 -252
  45. package/src/Pilotiq.test.ts +0 -129
  46. package/src/Pilotiq.ts +0 -1158
  47. package/src/PilotiqRegistry.ts +0 -36
  48. package/src/PilotiqServiceProvider.ts +0 -121
  49. package/src/RelationManager.test.ts +0 -400
  50. package/src/RelationManager.ts +0 -527
  51. package/src/RenderHook.test.ts +0 -252
  52. package/src/RenderHook.ts +0 -242
  53. package/src/Resource.test.ts +0 -284
  54. package/src/Resource.ts +0 -526
  55. package/src/RightPanel.test.ts +0 -202
  56. package/src/RightPanel.ts +0 -132
  57. package/src/Tab.test.ts +0 -91
  58. package/src/Tab.ts +0 -156
  59. package/src/UserMenuItem.ts +0 -145
  60. package/src/actions/Action.test.ts +0 -2526
  61. package/src/actions/Action.ts +0 -1515
  62. package/src/actions/ActionGroup.test.ts +0 -112
  63. package/src/actions/ActionGroup.ts +0 -173
  64. package/src/actions/attachFactory.ts +0 -172
  65. package/src/actions/bulkFactories.ts +0 -168
  66. package/src/actions/crudFactories.ts +0 -220
  67. package/src/actions/exportFactory.ts +0 -215
  68. package/src/actions/factoryHelpers.ts +0 -177
  69. package/src/actions/importFactory.ts +0 -243
  70. package/src/actions/index.ts +0 -17
  71. package/src/actions/m2mFactories.ts +0 -193
  72. package/src/actions/relationFactories.ts +0 -372
  73. package/src/applyPageHooks.test.ts +0 -463
  74. package/src/applyPageHooks.ts +0 -330
  75. package/src/authorization.test.ts +0 -483
  76. package/src/breadcrumbs.test.ts +0 -238
  77. package/src/cells/coerce.test.ts +0 -85
  78. package/src/cells/coerce.ts +0 -84
  79. package/src/clusterPaths.ts +0 -35
  80. package/src/columns/BadgeColumn.test.ts +0 -54
  81. package/src/columns/BadgeColumn.ts +0 -32
  82. package/src/columns/BooleanColumn.test.ts +0 -41
  83. package/src/columns/BooleanColumn.ts +0 -18
  84. package/src/columns/ColorColumn.test.ts +0 -37
  85. package/src/columns/ColorColumn.ts +0 -38
  86. package/src/columns/IconColumn.test.ts +0 -54
  87. package/src/columns/IconColumn.ts +0 -37
  88. package/src/columns/ImageColumn.test.ts +0 -41
  89. package/src/columns/ImageColumn.ts +0 -28
  90. package/src/columns/SelectColumn.ts +0 -98
  91. package/src/columns/TextColumn.test.ts +0 -190
  92. package/src/columns/TextColumn.ts +0 -20
  93. package/src/columns/TextInputColumn.ts +0 -68
  94. package/src/columns/ToggleColumn.ts +0 -46
  95. package/src/columns/editableColumns.test.ts +0 -238
  96. package/src/columns/index.ts +0 -9
  97. package/src/defaultGlobalPages.ts +0 -95
  98. package/src/defaultPages.test.ts +0 -634
  99. package/src/defaultPages.ts +0 -617
  100. package/src/defaultViewPage.test.ts +0 -147
  101. package/src/elements/Form.test.ts +0 -223
  102. package/src/elements/Form.ts +0 -416
  103. package/src/elements/ListTabs.ts +0 -28
  104. package/src/elements/Table.test.ts +0 -422
  105. package/src/elements/Table.ts +0 -850
  106. package/src/elements/TableGroup.test.ts +0 -260
  107. package/src/elements/TableGroup.ts +0 -334
  108. package/src/elements/dispatchAction.test.ts +0 -463
  109. package/src/elements/dispatchAction.ts +0 -355
  110. package/src/elements/dispatchForm.test.ts +0 -477
  111. package/src/elements/dispatchForm.ts +0 -1993
  112. package/src/elements/dispatchTable.test.ts +0 -1514
  113. package/src/elements/dispatchTable.ts +0 -745
  114. package/src/elements/index.ts +0 -21
  115. package/src/entries/BadgeEntry.ts +0 -39
  116. package/src/entries/CodeEntry.test.ts +0 -40
  117. package/src/entries/CodeEntry.ts +0 -52
  118. package/src/entries/ColorEntry.ts +0 -63
  119. package/src/entries/ComponentEntry.test.ts +0 -173
  120. package/src/entries/ComponentEntry.ts +0 -95
  121. package/src/entries/Entry.ts +0 -304
  122. package/src/entries/IconEntry.ts +0 -49
  123. package/src/entries/ImageEntry.ts +0 -61
  124. package/src/entries/KeyValueEntry.ts +0 -47
  125. package/src/entries/RepeatableEntry.test.ts +0 -239
  126. package/src/entries/RepeatableEntry.ts +0 -173
  127. package/src/entries/TextEntry.test.ts +0 -394
  128. package/src/entries/TextEntry.ts +0 -60
  129. package/src/entries/index.ts +0 -12
  130. package/src/entries/leaves.test.ts +0 -306
  131. package/src/entries/registry.ts +0 -54
  132. package/src/fields/BuilderField.test.ts +0 -1188
  133. package/src/fields/BuilderField.ts +0 -605
  134. package/src/fields/BuilderRelationship.test.ts +0 -811
  135. package/src/fields/CheckboxField.test.ts +0 -44
  136. package/src/fields/CheckboxField.ts +0 -27
  137. package/src/fields/CheckboxListField.test.ts +0 -99
  138. package/src/fields/CheckboxListField.ts +0 -66
  139. package/src/fields/ColorPickerField.test.ts +0 -33
  140. package/src/fields/ColorPickerField.ts +0 -25
  141. package/src/fields/DateField.ts +0 -54
  142. package/src/fields/DateTimeField.test.ts +0 -55
  143. package/src/fields/EmailField.ts +0 -16
  144. package/src/fields/Field.test.ts +0 -654
  145. package/src/fields/Field.ts +0 -817
  146. package/src/fields/FileUploadField.test.ts +0 -143
  147. package/src/fields/FileUploadField.ts +0 -159
  148. package/src/fields/HiddenField.test.ts +0 -27
  149. package/src/fields/HiddenField.ts +0 -28
  150. package/src/fields/KeyValueField.test.ts +0 -105
  151. package/src/fields/KeyValueField.ts +0 -55
  152. package/src/fields/MarkdownField.test.ts +0 -167
  153. package/src/fields/MarkdownField.ts +0 -162
  154. package/src/fields/NumberField.ts +0 -33
  155. package/src/fields/RadioField.test.ts +0 -94
  156. package/src/fields/RadioField.ts +0 -67
  157. package/src/fields/RepeaterField.test.ts +0 -1806
  158. package/src/fields/RepeaterField.ts +0 -939
  159. package/src/fields/RepeaterRelationship.test.ts +0 -1923
  160. package/src/fields/RepeaterSimple.test.ts +0 -248
  161. package/src/fields/RowButton.test.ts +0 -219
  162. package/src/fields/RowButton.ts +0 -135
  163. package/src/fields/SelectField.test.ts +0 -192
  164. package/src/fields/SelectField.ts +0 -235
  165. package/src/fields/SliderField.test.ts +0 -50
  166. package/src/fields/SliderField.ts +0 -53
  167. package/src/fields/SlugField.ts +0 -24
  168. package/src/fields/TagsInputField.test.ts +0 -154
  169. package/src/fields/TagsInputField.ts +0 -133
  170. package/src/fields/TextField.test.ts +0 -213
  171. package/src/fields/TextField.ts +0 -177
  172. package/src/fields/TextareaField.test.ts +0 -58
  173. package/src/fields/TextareaField.ts +0 -59
  174. package/src/fields/ToggleButtonsField.test.ts +0 -106
  175. package/src/fields/ToggleButtonsField.ts +0 -59
  176. package/src/fields/ToggleField.ts +0 -16
  177. package/src/fields/disableOptionsWhenSelectedInSiblingRepeaterItems.test.ts +0 -319
  178. package/src/fields/optionsResolver.ts +0 -95
  179. package/src/fields/resolveField.ts +0 -28
  180. package/src/filters/BooleanFilter.ts +0 -35
  181. package/src/filters/DateRangeFilter.test.ts +0 -194
  182. package/src/filters/DateRangeFilter.ts +0 -148
  183. package/src/filters/Filter.test.ts +0 -268
  184. package/src/filters/Filter.ts +0 -184
  185. package/src/filters/FormFilter.test.ts +0 -238
  186. package/src/filters/FormFilter.ts +0 -215
  187. package/src/filters/MultiSelectFilter.test.ts +0 -119
  188. package/src/filters/MultiSelectFilter.ts +0 -78
  189. package/src/filters/QueryBuilderFilter.test.ts +0 -662
  190. package/src/filters/QueryBuilderFilter.ts +0 -398
  191. package/src/filters/SelectFilter.ts +0 -46
  192. package/src/filters/TernaryFilter.test.ts +0 -160
  193. package/src/filters/TernaryFilter.ts +0 -72
  194. package/src/filters/TrashedFilter.test.ts +0 -149
  195. package/src/filters/TrashedFilter.ts +0 -55
  196. package/src/filters/queryBuilder/BooleanConstraint.ts +0 -31
  197. package/src/filters/queryBuilder/Constraint.ts +0 -115
  198. package/src/filters/queryBuilder/DateConstraint.ts +0 -69
  199. package/src/filters/queryBuilder/NumberConstraint.ts +0 -66
  200. package/src/filters/queryBuilder/SelectConstraint.ts +0 -72
  201. package/src/filters/queryBuilder/TextConstraint.ts +0 -64
  202. package/src/filters/queryBuilder/index.ts +0 -12
  203. package/src/icons/index.ts +0 -2
  204. package/src/icons/lucide.ts +0 -204
  205. package/src/icons/registry.test.ts +0 -56
  206. package/src/icons/registry.ts +0 -41
  207. package/src/icons/types.ts +0 -47
  208. package/src/index.ts +0 -525
  209. package/src/io/csv.test.ts +0 -142
  210. package/src/io/csv.ts +0 -170
  211. package/src/nestedRelationManagerData.test.ts +0 -547
  212. package/src/notifications/Notification.test.ts +0 -210
  213. package/src/notifications/Notification.ts +0 -354
  214. package/src/notifications/broadcast.test.ts +0 -110
  215. package/src/notifications/broadcast.ts +0 -95
  216. package/src/notifications/database.test.ts +0 -383
  217. package/src/notifications/database.ts +0 -398
  218. package/src/notifications/databaseNotifications.test.ts +0 -187
  219. package/src/notifications/dispatchNotificationAction.test.ts +0 -341
  220. package/src/notifications/dispatchNotificationAction.ts +0 -142
  221. package/src/notifications/flash.test.ts +0 -89
  222. package/src/notifications/flash.ts +0 -71
  223. package/src/notifications/index.ts +0 -45
  224. package/src/notifications/registerBroadcastAuth.test.ts +0 -134
  225. package/src/notifications/registerBroadcastAuth.ts +0 -100
  226. package/src/notifications/resolveSavedNotification.test.ts +0 -82
  227. package/src/notifications/resolveSavedNotification.ts +0 -59
  228. package/src/notifications/types.ts +0 -93
  229. package/src/orm/m2mAccessor.ts +0 -66
  230. package/src/orm/modelDefaults.test.ts +0 -633
  231. package/src/orm/modelDefaults.ts +0 -666
  232. package/src/pageData/breadcrumbs.ts +0 -288
  233. package/src/pageData/forms.ts +0 -578
  234. package/src/pageData/helpers.ts +0 -857
  235. package/src/pageData/misc.ts +0 -347
  236. package/src/pageData/navigation.ts +0 -842
  237. package/src/pageData/relationPages.ts +0 -1248
  238. package/src/pageData/relationTabs.ts +0 -286
  239. package/src/pageData/resourcePages.ts +0 -609
  240. package/src/pageData.test.ts +0 -1545
  241. package/src/pageData.ts +0 -341
  242. package/src/plugins/index.ts +0 -8
  243. package/src/plugins/themeEditor.test.ts +0 -36
  244. package/src/plugins/themeEditor.ts +0 -45
  245. package/src/react/AppShell.tsx +0 -251
  246. package/src/react/CollabExtensionFactoryRegistry.ts +0 -55
  247. package/src/react/CollabRoomContext.ts +0 -98
  248. package/src/react/CollabTextRendererRegistry.ts +0 -102
  249. package/src/react/CommandPalette.tsx +0 -375
  250. package/src/react/CurrentUserContext.tsx +0 -50
  251. package/src/react/CustomPageWrapperGate.tsx +0 -69
  252. package/src/react/CustomPageWrapperRegistry.ts +0 -45
  253. package/src/react/FieldFocusReporterRegistry.ts +0 -37
  254. package/src/react/FieldLabelSlotRegistry.ts +0 -30
  255. package/src/react/FieldPresenceRegistry.ts +0 -46
  256. package/src/react/FormCollabBindingRegistry.ts +0 -242
  257. package/src/react/FormStateContext.tsx +0 -591
  258. package/src/react/HeadHooks.tsx +0 -126
  259. package/src/react/MarkdownEditorRegistry.test.ts +0 -38
  260. package/src/react/MarkdownEditorRegistry.ts +0 -107
  261. package/src/react/NotificationActionStrip.tsx +0 -263
  262. package/src/react/NotificationBell.tsx +0 -426
  263. package/src/react/PendingSuggestionApplierRegistry.test.ts +0 -97
  264. package/src/react/PendingSuggestionApplierRegistry.ts +0 -98
  265. package/src/react/PendingSuggestionOverlayRegistry.ts +0 -54
  266. package/src/react/PendingSuggestionsContext.tsx +0 -172
  267. package/src/react/RecordWrapperGate.tsx +0 -58
  268. package/src/react/RecordWrapperRegistry.ts +0 -39
  269. package/src/react/RenderHookSlot.tsx +0 -32
  270. package/src/react/RightSidebar.tsx +0 -257
  271. package/src/react/RightSidebarContext.tsx +0 -234
  272. package/src/react/RightSidebarTrigger.tsx +0 -53
  273. package/src/react/RowCoordsContext.tsx +0 -23
  274. package/src/react/SchemaRenderer.tsx +0 -549
  275. package/src/react/SearchTrigger.tsx +0 -46
  276. package/src/react/ThemeProvider.tsx +0 -93
  277. package/src/react/ThemeSettingsPage.tsx +0 -579
  278. package/src/react/ThemeToggle.tsx +0 -20
  279. package/src/react/Toaster.tsx +0 -158
  280. package/src/react/UserMenu.tsx +0 -196
  281. package/src/react/WidgetDataContext.tsx +0 -157
  282. package/src/react/cells/EditableCell.tsx +0 -389
  283. package/src/react/component-slots.test.ts +0 -103
  284. package/src/react/component-slots.ts +0 -116
  285. package/src/react/fieldJsHandler.test.ts +0 -166
  286. package/src/react/fieldJsHandler.ts +0 -79
  287. package/src/react/fields/BuilderInput.tsx +0 -1078
  288. package/src/react/fields/CheckboxInput.tsx +0 -39
  289. package/src/react/fields/CheckboxListInput.tsx +0 -102
  290. package/src/react/fields/ColorInput.tsx +0 -71
  291. package/src/react/fields/DateFieldInput.tsx +0 -70
  292. package/src/react/fields/DateTimeInput.tsx +0 -62
  293. package/src/react/fields/FieldShell.tsx +0 -348
  294. package/src/react/fields/FileUploadInput.tsx +0 -639
  295. package/src/react/fields/HiddenInput.tsx +0 -17
  296. package/src/react/fields/KeyValueInput.tsx +0 -230
  297. package/src/react/fields/MarkdownInput.tsx +0 -560
  298. package/src/react/fields/RadioInput.tsx +0 -81
  299. package/src/react/fields/RepeaterInput.test.ts +0 -116
  300. package/src/react/fields/RepeaterInput.tsx +0 -1420
  301. package/src/react/fields/SelectFieldInput.tsx +0 -280
  302. package/src/react/fields/SliderInput.tsx +0 -81
  303. package/src/react/fields/TagsInput.tsx +0 -283
  304. package/src/react/fields/TextLikeInput.tsx +0 -256
  305. package/src/react/fields/ToggleButtonsInput.tsx +0 -60
  306. package/src/react/fields/ToggleFieldInput.tsx +0 -56
  307. package/src/react/fields/relationshipRenameDispatch.test.ts +0 -106
  308. package/src/react/fields/relationshipRenameDispatch.ts +0 -97
  309. package/src/react/fields/repeaterReconcile.test.ts +0 -114
  310. package/src/react/fields/repeaterReconcile.ts +0 -104
  311. package/src/react/fields/rowChromeButton.tsx +0 -336
  312. package/src/react/fields/rowState.ts +0 -106
  313. package/src/react/fields/syncRowGates.test.ts +0 -202
  314. package/src/react/fields/syncRowGates.ts +0 -66
  315. package/src/react/fields/textInputControls.tsx +0 -238
  316. package/src/react/fields/useRowReorderDnd.ts +0 -78
  317. package/src/react/formStateHelpers.test.ts +0 -508
  318. package/src/react/formStateHelpers.ts +0 -381
  319. package/src/react/hooks/use-mobile.ts +0 -19
  320. package/src/react/icon-context.tsx +0 -60
  321. package/src/react/index.ts +0 -195
  322. package/src/react/layouts/SidebarLayout.tsx +0 -250
  323. package/src/react/layouts/TopbarLayout.tsx +0 -258
  324. package/src/react/navigate.tsx +0 -37
  325. package/src/react/onProviderSynced.test.ts +0 -90
  326. package/src/react/parseRecordEditUrl.test.ts +0 -122
  327. package/src/react/parseRecordEditUrl.ts +0 -94
  328. package/src/react/persistedState.ts +0 -40
  329. package/src/react/registry.ts +0 -48
  330. package/src/react/right-panel-registry.tsx +0 -47
  331. package/src/react/schemaRenderer/AlertRenderer.tsx +0 -112
  332. package/src/react/schemaRenderer/EntryRenderer.tsx +0 -501
  333. package/src/react/schemaRenderer/SectionRenderer.tsx +0 -120
  334. package/src/react/schemaRenderer/SimpleElements.tsx +0 -306
  335. package/src/react/schemaRenderer/TabsRenderer.tsx +0 -62
  336. package/src/react/schemaRenderer/WizardRenderer.tsx +0 -338
  337. package/src/react/schemaRenderer/action/ActionGroupTrigger.tsx +0 -177
  338. package/src/react/schemaRenderer/action/ActionModalDialog.tsx +0 -273
  339. package/src/react/schemaRenderer/action/ConfirmActionDialog.tsx +0 -61
  340. package/src/react/schemaRenderer/action/HandlerActionButton.tsx +0 -43
  341. package/src/react/schemaRenderer/action/MethodActionButton.tsx +0 -64
  342. package/src/react/schemaRenderer/action/buttons.tsx +0 -99
  343. package/src/react/schemaRenderer/action/helpers.ts +0 -140
  344. package/src/react/schemaRenderer/action/renderAction.tsx +0 -245
  345. package/src/react/schemaRenderer/columnFormat.ts +0 -65
  346. package/src/react/schemaRenderer/constants.ts +0 -50
  347. package/src/react/schemaRenderer/form/FormRenderer.tsx +0 -274
  348. package/src/react/schemaRenderer/form/renderField.tsx +0 -511
  349. package/src/react/schemaRenderer/helpers.tsx +0 -81
  350. package/src/react/schemaRenderer/table/CardsLayoutBody.tsx +0 -308
  351. package/src/react/schemaRenderer/table/TableRenderer.tsx +0 -123
  352. package/src/react/schemaRenderer/table/TableRendererBody.tsx +0 -974
  353. package/src/react/schemaRenderer/table/filters.tsx +0 -1233
  354. package/src/react/schemaRenderer/table/formatCell.tsx +0 -264
  355. package/src/react/schemaRenderer/table/links.tsx +0 -112
  356. package/src/react/schemaRenderer/table/renderRowActions.tsx +0 -52
  357. package/src/react/schemaRenderer/table/url.tsx +0 -143
  358. package/src/react/theme-preview/apply.ts +0 -99
  359. package/src/react/theme-preview/build-html.ts +0 -436
  360. package/src/react/ui/button.tsx +0 -51
  361. package/src/react/ui/calendar.tsx +0 -67
  362. package/src/react/ui/checkbox.tsx +0 -29
  363. package/src/react/ui/dialog.tsx +0 -108
  364. package/src/react/ui/dropdown-menu.tsx +0 -97
  365. package/src/react/ui/input.tsx +0 -20
  366. package/src/react/ui/label.tsx +0 -21
  367. package/src/react/ui/popover.tsx +0 -50
  368. package/src/react/ui/select.tsx +0 -169
  369. package/src/react/ui/separator.tsx +0 -25
  370. package/src/react/ui/sheet.tsx +0 -136
  371. package/src/react/ui/sidebar.tsx +0 -723
  372. package/src/react/ui/skeleton.tsx +0 -13
  373. package/src/react/ui/slider.tsx +0 -34
  374. package/src/react/ui/switch.tsx +0 -28
  375. package/src/react/ui/table.tsx +0 -105
  376. package/src/react/ui/tabs.tsx +0 -63
  377. package/src/react/ui/textarea.tsx +0 -18
  378. package/src/react/ui/tooltip.tsx +0 -64
  379. package/src/react/useCollabSeed.ts +0 -86
  380. package/src/react/useResizableWidth.ts +0 -139
  381. package/src/react/utils.ts +0 -6
  382. package/src/react/widgetRegistry.test.ts +0 -43
  383. package/src/react/widgetRegistry.ts +0 -50
  384. package/src/react/widgets/StatsOverviewRenderer.tsx +0 -232
  385. package/src/react/widgets/TableWidgetRenderer.tsx +0 -231
  386. package/src/react/widgets/ViewRenderer.tsx +0 -71
  387. package/src/relationManagerData.test.ts +0 -1595
  388. package/src/richtext/index.ts +0 -8
  389. package/src/richtext/registry.ts +0 -89
  390. package/src/routes/globals.ts +0 -148
  391. package/src/routes/guard.test.ts +0 -325
  392. package/src/routes/helpers.ts +0 -700
  393. package/src/routes/pages.ts +0 -175
  394. package/src/routes/panel.ts +0 -204
  395. package/src/routes/relations.ts +0 -1227
  396. package/src/routes/resources.ts +0 -781
  397. package/src/routes/theme.ts +0 -91
  398. package/src/routes-nested-relations.test.ts +0 -676
  399. package/src/routes-relations.test.ts +0 -972
  400. package/src/routes.test.ts +0 -2027
  401. package/src/routes.ts +0 -303
  402. package/src/schema/Alert.test.ts +0 -109
  403. package/src/schema/Alert.ts +0 -131
  404. package/src/schema/Block.ts +0 -169
  405. package/src/schema/Breadcrumbs.ts +0 -40
  406. package/src/schema/Card.ts +0 -35
  407. package/src/schema/Divider.ts +0 -20
  408. package/src/schema/Element.ts +0 -219
  409. package/src/schema/EmptyState.test.ts +0 -37
  410. package/src/schema/EmptyState.ts +0 -63
  411. package/src/schema/Fieldset.ts +0 -43
  412. package/src/schema/Grid.ts +0 -43
  413. package/src/schema/Group.ts +0 -30
  414. package/src/schema/Heading.ts +0 -39
  415. package/src/schema/Html.ts +0 -67
  416. package/src/schema/Icon.ts +0 -54
  417. package/src/schema/Image.ts +0 -57
  418. package/src/schema/LinkTag.ts +0 -41
  419. package/src/schema/Markdown.ts +0 -85
  420. package/src/schema/MetaTag.ts +0 -41
  421. package/src/schema/RelationTabs.ts +0 -71
  422. package/src/schema/ScriptTag.ts +0 -55
  423. package/src/schema/Section.ts +0 -160
  424. package/src/schema/ServerDataElement.test.ts +0 -140
  425. package/src/schema/ServerDataElement.ts +0 -156
  426. package/src/schema/SlotComponent.test.ts +0 -77
  427. package/src/schema/SlotComponent.ts +0 -71
  428. package/src/schema/Split.ts +0 -50
  429. package/src/schema/Stat.test.ts +0 -118
  430. package/src/schema/Stat.ts +0 -154
  431. package/src/schema/StatsOverview.test.ts +0 -141
  432. package/src/schema/StatsOverview.ts +0 -119
  433. package/src/schema/StyleTag.ts +0 -35
  434. package/src/schema/TableWidget.test.ts +0 -297
  435. package/src/schema/TableWidget.ts +0 -289
  436. package/src/schema/Tabs.ts +0 -79
  437. package/src/schema/Text.ts +0 -58
  438. package/src/schema/UnorderedList.ts +0 -49
  439. package/src/schema/View.test.ts +0 -111
  440. package/src/schema/View.ts +0 -127
  441. package/src/schema/Wizard.ts +0 -220
  442. package/src/schema/containers.test.ts +0 -564
  443. package/src/schema/headTags.test.ts +0 -134
  444. package/src/schema/index.ts +0 -40
  445. package/src/schema/primes.test.ts +0 -269
  446. package/src/schema/resolveSchema.test.ts +0 -379
  447. package/src/schema/resolveSchema.ts +0 -917
  448. package/src/schema/sanitize.ts +0 -58
  449. package/src/search.test.ts +0 -446
  450. package/src/search.ts +0 -178
  451. package/src/sessionFilters.test.ts +0 -375
  452. package/src/sessionFilters.ts +0 -143
  453. package/src/slot-components/index.ts +0 -10
  454. package/src/slot-components/registry.ts +0 -56
  455. package/src/styles/file-upload.css +0 -13
  456. package/src/summarizers/Summarizer.test.ts +0 -84
  457. package/src/summarizers/Summarizer.ts +0 -123
  458. package/src/summarizers/index.ts +0 -11
  459. package/src/theme/base-colors.ts +0 -68
  460. package/src/theme/chart-colors.ts +0 -50
  461. package/src/theme/colors.ts +0 -447
  462. package/src/theme/generate-css.test.ts +0 -139
  463. package/src/theme/generate-css.ts +0 -44
  464. package/src/theme/generate-scale.test.ts +0 -106
  465. package/src/theme/generate-scale.ts +0 -97
  466. package/src/theme/icon-map.ts +0 -42
  467. package/src/theme/index.ts +0 -34
  468. package/src/theme/migrate.test.ts +0 -178
  469. package/src/theme/migrate.ts +0 -81
  470. package/src/theme/presets.ts +0 -135
  471. package/src/theme/radius.ts +0 -18
  472. package/src/theme/resolve.test.ts +0 -238
  473. package/src/theme/resolve.ts +0 -96
  474. package/src/theme/spacing.ts +0 -18
  475. package/src/theme/storage.test.ts +0 -126
  476. package/src/theme/storage.ts +0 -106
  477. package/src/theme/theme-colors.ts +0 -88
  478. package/src/theme/types.ts +0 -125
  479. package/src/uploads/UploadAdapter.ts +0 -35
  480. package/src/uploads/index.ts +0 -2
  481. package/src/uploads/localUpload.test.ts +0 -70
  482. package/src/uploads/localUpload.ts +0 -84
  483. package/src/validation/Validator.ts +0 -49
  484. package/src/validation/index.ts +0 -28
  485. package/src/validation/rules.ts +0 -78
  486. package/src/validation/runValidators.ts +0 -435
  487. package/src/validation/uniqueValidator.test.ts +0 -196
  488. package/src/validation/uniqueValidator.ts +0 -133
  489. package/src/validation/validators.test.ts +0 -268
  490. package/src/vite.test.ts +0 -184
  491. package/src/vite.ts +0 -787
  492. package/src/widgets/index.ts +0 -10
  493. package/src/widgets/registry.ts +0 -45
  494. package/src/widgets.test.ts +0 -592
  495. package/tsconfig.build.json +0 -11
  496. package/tsconfig.json +0 -4
  497. package/tsconfig.test.json +0 -10
  498. package/views/react/Dashboard.tsx +0 -27
  499. package/views/react/Resources/Form.tsx +0 -102
  500. 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
- }