@pilotiq/pilotiq 0.24.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 (480) hide show
  1. package/CHANGELOG.md +33 -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/package.json +6 -1
  15. package/.turbo/turbo-build.log +0 -8
  16. package/CLAUDE.md +0 -265
  17. package/src/Cluster.test.ts +0 -283
  18. package/src/Cluster.ts +0 -83
  19. package/src/Column.test.ts +0 -199
  20. package/src/Column.ts +0 -710
  21. package/src/Global.test.ts +0 -367
  22. package/src/Global.ts +0 -169
  23. package/src/Page.test.ts +0 -114
  24. package/src/Page.ts +0 -208
  25. package/src/Pilotiq.perf.test.ts +0 -252
  26. package/src/Pilotiq.test.ts +0 -129
  27. package/src/Pilotiq.ts +0 -1158
  28. package/src/PilotiqRegistry.ts +0 -36
  29. package/src/PilotiqServiceProvider.ts +0 -121
  30. package/src/RelationManager.test.ts +0 -400
  31. package/src/RelationManager.ts +0 -527
  32. package/src/RenderHook.test.ts +0 -252
  33. package/src/RenderHook.ts +0 -242
  34. package/src/Resource.test.ts +0 -284
  35. package/src/Resource.ts +0 -526
  36. package/src/RightPanel.test.ts +0 -202
  37. package/src/RightPanel.ts +0 -132
  38. package/src/Tab.test.ts +0 -91
  39. package/src/Tab.ts +0 -156
  40. package/src/UserMenuItem.ts +0 -145
  41. package/src/actions/Action.test.ts +0 -2526
  42. package/src/actions/Action.ts +0 -1515
  43. package/src/actions/ActionGroup.test.ts +0 -112
  44. package/src/actions/ActionGroup.ts +0 -173
  45. package/src/actions/attachFactory.ts +0 -172
  46. package/src/actions/bulkFactories.ts +0 -168
  47. package/src/actions/crudFactories.ts +0 -220
  48. package/src/actions/exportFactory.ts +0 -225
  49. package/src/actions/factoryHelpers.ts +0 -177
  50. package/src/actions/importFactory.ts +0 -243
  51. package/src/actions/index.ts +0 -17
  52. package/src/actions/m2mFactories.ts +0 -193
  53. package/src/actions/relationFactories.ts +0 -372
  54. package/src/applyPageHooks.test.ts +0 -463
  55. package/src/applyPageHooks.ts +0 -330
  56. package/src/authorization.test.ts +0 -483
  57. package/src/breadcrumbs.test.ts +0 -238
  58. package/src/cells/coerce.test.ts +0 -85
  59. package/src/cells/coerce.ts +0 -84
  60. package/src/clusterPaths.ts +0 -35
  61. package/src/columns/BadgeColumn.test.ts +0 -54
  62. package/src/columns/BadgeColumn.ts +0 -32
  63. package/src/columns/BooleanColumn.test.ts +0 -41
  64. package/src/columns/BooleanColumn.ts +0 -18
  65. package/src/columns/ColorColumn.test.ts +0 -37
  66. package/src/columns/ColorColumn.ts +0 -38
  67. package/src/columns/IconColumn.test.ts +0 -54
  68. package/src/columns/IconColumn.ts +0 -37
  69. package/src/columns/ImageColumn.test.ts +0 -41
  70. package/src/columns/ImageColumn.ts +0 -28
  71. package/src/columns/SelectColumn.ts +0 -98
  72. package/src/columns/TextColumn.test.ts +0 -190
  73. package/src/columns/TextColumn.ts +0 -20
  74. package/src/columns/TextInputColumn.ts +0 -68
  75. package/src/columns/ToggleColumn.ts +0 -46
  76. package/src/columns/editableColumns.test.ts +0 -238
  77. package/src/columns/index.ts +0 -9
  78. package/src/defaultGlobalPages.ts +0 -95
  79. package/src/defaultPages.test.ts +0 -634
  80. package/src/defaultPages.ts +0 -617
  81. package/src/defaultViewPage.test.ts +0 -147
  82. package/src/elements/Form.test.ts +0 -223
  83. package/src/elements/Form.ts +0 -416
  84. package/src/elements/ListTabs.ts +0 -28
  85. package/src/elements/Table.test.ts +0 -422
  86. package/src/elements/Table.ts +0 -850
  87. package/src/elements/TableGroup.test.ts +0 -260
  88. package/src/elements/TableGroup.ts +0 -334
  89. package/src/elements/dispatchAction.test.ts +0 -463
  90. package/src/elements/dispatchAction.ts +0 -355
  91. package/src/elements/dispatchForm.test.ts +0 -477
  92. package/src/elements/dispatchForm.ts +0 -1993
  93. package/src/elements/dispatchTable.test.ts +0 -1514
  94. package/src/elements/dispatchTable.ts +0 -745
  95. package/src/elements/index.ts +0 -21
  96. package/src/entries/BadgeEntry.ts +0 -39
  97. package/src/entries/CodeEntry.test.ts +0 -40
  98. package/src/entries/CodeEntry.ts +0 -52
  99. package/src/entries/ColorEntry.ts +0 -63
  100. package/src/entries/ComponentEntry.test.ts +0 -173
  101. package/src/entries/ComponentEntry.ts +0 -95
  102. package/src/entries/Entry.ts +0 -304
  103. package/src/entries/IconEntry.ts +0 -49
  104. package/src/entries/ImageEntry.ts +0 -61
  105. package/src/entries/KeyValueEntry.ts +0 -47
  106. package/src/entries/RepeatableEntry.test.ts +0 -239
  107. package/src/entries/RepeatableEntry.ts +0 -173
  108. package/src/entries/TextEntry.test.ts +0 -394
  109. package/src/entries/TextEntry.ts +0 -60
  110. package/src/entries/index.ts +0 -12
  111. package/src/entries/leaves.test.ts +0 -306
  112. package/src/entries/registry.ts +0 -54
  113. package/src/fields/BuilderField.test.ts +0 -1188
  114. package/src/fields/BuilderField.ts +0 -605
  115. package/src/fields/BuilderRelationship.test.ts +0 -811
  116. package/src/fields/CheckboxField.test.ts +0 -44
  117. package/src/fields/CheckboxField.ts +0 -27
  118. package/src/fields/CheckboxListField.test.ts +0 -99
  119. package/src/fields/CheckboxListField.ts +0 -66
  120. package/src/fields/ColorPickerField.test.ts +0 -33
  121. package/src/fields/ColorPickerField.ts +0 -25
  122. package/src/fields/DateField.ts +0 -54
  123. package/src/fields/DateTimeField.test.ts +0 -55
  124. package/src/fields/EmailField.ts +0 -16
  125. package/src/fields/Field.test.ts +0 -654
  126. package/src/fields/Field.ts +0 -817
  127. package/src/fields/FileUploadField.test.ts +0 -143
  128. package/src/fields/FileUploadField.ts +0 -159
  129. package/src/fields/HiddenField.test.ts +0 -27
  130. package/src/fields/HiddenField.ts +0 -28
  131. package/src/fields/KeyValueField.test.ts +0 -105
  132. package/src/fields/KeyValueField.ts +0 -55
  133. package/src/fields/MarkdownField.test.ts +0 -167
  134. package/src/fields/MarkdownField.ts +0 -162
  135. package/src/fields/NumberField.ts +0 -33
  136. package/src/fields/RadioField.test.ts +0 -94
  137. package/src/fields/RadioField.ts +0 -67
  138. package/src/fields/RepeaterField.test.ts +0 -1806
  139. package/src/fields/RepeaterField.ts +0 -939
  140. package/src/fields/RepeaterRelationship.test.ts +0 -1923
  141. package/src/fields/RepeaterSimple.test.ts +0 -248
  142. package/src/fields/RowButton.test.ts +0 -219
  143. package/src/fields/RowButton.ts +0 -135
  144. package/src/fields/SelectField.test.ts +0 -192
  145. package/src/fields/SelectField.ts +0 -235
  146. package/src/fields/SliderField.test.ts +0 -50
  147. package/src/fields/SliderField.ts +0 -53
  148. package/src/fields/SlugField.ts +0 -24
  149. package/src/fields/TagsInputField.test.ts +0 -154
  150. package/src/fields/TagsInputField.ts +0 -133
  151. package/src/fields/TextField.test.ts +0 -213
  152. package/src/fields/TextField.ts +0 -177
  153. package/src/fields/TextareaField.test.ts +0 -58
  154. package/src/fields/TextareaField.ts +0 -59
  155. package/src/fields/ToggleButtonsField.test.ts +0 -106
  156. package/src/fields/ToggleButtonsField.ts +0 -59
  157. package/src/fields/ToggleField.ts +0 -16
  158. package/src/fields/disableOptionsWhenSelectedInSiblingRepeaterItems.test.ts +0 -319
  159. package/src/fields/optionsResolver.ts +0 -95
  160. package/src/fields/resolveField.ts +0 -28
  161. package/src/filters/BooleanFilter.ts +0 -35
  162. package/src/filters/DateRangeFilter.test.ts +0 -194
  163. package/src/filters/DateRangeFilter.ts +0 -148
  164. package/src/filters/Filter.test.ts +0 -268
  165. package/src/filters/Filter.ts +0 -184
  166. package/src/filters/FormFilter.test.ts +0 -238
  167. package/src/filters/FormFilter.ts +0 -215
  168. package/src/filters/MultiSelectFilter.test.ts +0 -119
  169. package/src/filters/MultiSelectFilter.ts +0 -78
  170. package/src/filters/QueryBuilderFilter.test.ts +0 -662
  171. package/src/filters/QueryBuilderFilter.ts +0 -398
  172. package/src/filters/SelectFilter.ts +0 -46
  173. package/src/filters/TernaryFilter.test.ts +0 -160
  174. package/src/filters/TernaryFilter.ts +0 -72
  175. package/src/filters/TrashedFilter.test.ts +0 -149
  176. package/src/filters/TrashedFilter.ts +0 -55
  177. package/src/filters/queryBuilder/BooleanConstraint.ts +0 -31
  178. package/src/filters/queryBuilder/Constraint.ts +0 -115
  179. package/src/filters/queryBuilder/DateConstraint.ts +0 -69
  180. package/src/filters/queryBuilder/NumberConstraint.ts +0 -66
  181. package/src/filters/queryBuilder/SelectConstraint.ts +0 -72
  182. package/src/filters/queryBuilder/TextConstraint.ts +0 -64
  183. package/src/filters/queryBuilder/index.ts +0 -12
  184. package/src/icons/index.ts +0 -2
  185. package/src/icons/lucide.ts +0 -204
  186. package/src/icons/registry.test.ts +0 -56
  187. package/src/icons/registry.ts +0 -41
  188. package/src/icons/types.ts +0 -47
  189. package/src/index.ts +0 -525
  190. package/src/io/csv.test.ts +0 -142
  191. package/src/io/csv.ts +0 -170
  192. package/src/nestedRelationManagerData.test.ts +0 -547
  193. package/src/notifications/Notification.test.ts +0 -210
  194. package/src/notifications/Notification.ts +0 -354
  195. package/src/notifications/broadcast.test.ts +0 -110
  196. package/src/notifications/broadcast.ts +0 -95
  197. package/src/notifications/database.test.ts +0 -383
  198. package/src/notifications/database.ts +0 -398
  199. package/src/notifications/databaseNotifications.test.ts +0 -187
  200. package/src/notifications/dispatchNotificationAction.test.ts +0 -341
  201. package/src/notifications/dispatchNotificationAction.ts +0 -142
  202. package/src/notifications/flash.test.ts +0 -89
  203. package/src/notifications/flash.ts +0 -71
  204. package/src/notifications/index.ts +0 -45
  205. package/src/notifications/registerBroadcastAuth.test.ts +0 -134
  206. package/src/notifications/registerBroadcastAuth.ts +0 -100
  207. package/src/notifications/resolveSavedNotification.test.ts +0 -82
  208. package/src/notifications/resolveSavedNotification.ts +0 -59
  209. package/src/notifications/types.ts +0 -93
  210. package/src/orm/m2mAccessor.ts +0 -66
  211. package/src/orm/modelDefaults.test.ts +0 -633
  212. package/src/orm/modelDefaults.ts +0 -666
  213. package/src/pageData/breadcrumbs.ts +0 -288
  214. package/src/pageData/forms.ts +0 -578
  215. package/src/pageData/helpers.ts +0 -857
  216. package/src/pageData/misc.ts +0 -347
  217. package/src/pageData/navigation.ts +0 -842
  218. package/src/pageData/relationPages.ts +0 -1248
  219. package/src/pageData/relationTabs.ts +0 -286
  220. package/src/pageData/resourcePages.ts +0 -609
  221. package/src/pageData.test.ts +0 -1545
  222. package/src/pageData.ts +0 -341
  223. package/src/plugins/index.ts +0 -8
  224. package/src/plugins/themeEditor.test.ts +0 -36
  225. package/src/plugins/themeEditor.ts +0 -45
  226. package/src/react/AppShell.tsx +0 -251
  227. package/src/react/CollabExtensionFactoryRegistry.ts +0 -55
  228. package/src/react/CollabRoomContext.ts +0 -98
  229. package/src/react/CollabTextRendererRegistry.ts +0 -102
  230. package/src/react/CommandPalette.tsx +0 -375
  231. package/src/react/CurrentUserContext.tsx +0 -50
  232. package/src/react/CustomPageWrapperGate.tsx +0 -69
  233. package/src/react/CustomPageWrapperRegistry.ts +0 -45
  234. package/src/react/FieldFocusReporterRegistry.ts +0 -37
  235. package/src/react/FieldLabelSlotRegistry.ts +0 -30
  236. package/src/react/FieldPresenceRegistry.ts +0 -46
  237. package/src/react/FormCollabBindingRegistry.ts +0 -242
  238. package/src/react/FormStateContext.tsx +0 -591
  239. package/src/react/HeadHooks.tsx +0 -126
  240. package/src/react/MarkdownEditorRegistry.test.ts +0 -38
  241. package/src/react/MarkdownEditorRegistry.ts +0 -107
  242. package/src/react/NotificationActionStrip.tsx +0 -263
  243. package/src/react/NotificationBell.tsx +0 -426
  244. package/src/react/PendingSuggestionApplierRegistry.test.ts +0 -97
  245. package/src/react/PendingSuggestionApplierRegistry.ts +0 -98
  246. package/src/react/PendingSuggestionOverlayRegistry.ts +0 -54
  247. package/src/react/PendingSuggestionsContext.tsx +0 -172
  248. package/src/react/RecordWrapperGate.tsx +0 -58
  249. package/src/react/RecordWrapperRegistry.ts +0 -39
  250. package/src/react/RenderHookSlot.tsx +0 -32
  251. package/src/react/RightSidebar.tsx +0 -257
  252. package/src/react/RightSidebarContext.tsx +0 -234
  253. package/src/react/RightSidebarTrigger.tsx +0 -53
  254. package/src/react/RowCoordsContext.tsx +0 -23
  255. package/src/react/SchemaRenderer.tsx +0 -549
  256. package/src/react/SearchTrigger.tsx +0 -46
  257. package/src/react/ThemeProvider.tsx +0 -93
  258. package/src/react/ThemeSettingsPage.tsx +0 -579
  259. package/src/react/ThemeToggle.tsx +0 -20
  260. package/src/react/Toaster.tsx +0 -158
  261. package/src/react/UserMenu.tsx +0 -196
  262. package/src/react/WidgetDataContext.tsx +0 -157
  263. package/src/react/cells/EditableCell.tsx +0 -389
  264. package/src/react/component-slots.test.ts +0 -103
  265. package/src/react/component-slots.ts +0 -116
  266. package/src/react/fieldJsHandler.test.ts +0 -166
  267. package/src/react/fieldJsHandler.ts +0 -79
  268. package/src/react/fields/BuilderInput.tsx +0 -1078
  269. package/src/react/fields/CheckboxInput.tsx +0 -39
  270. package/src/react/fields/CheckboxListInput.tsx +0 -102
  271. package/src/react/fields/ColorInput.tsx +0 -71
  272. package/src/react/fields/DateFieldInput.tsx +0 -70
  273. package/src/react/fields/DateTimeInput.tsx +0 -62
  274. package/src/react/fields/FieldShell.tsx +0 -348
  275. package/src/react/fields/FileUploadInput.tsx +0 -639
  276. package/src/react/fields/HiddenInput.tsx +0 -17
  277. package/src/react/fields/KeyValueInput.tsx +0 -230
  278. package/src/react/fields/MarkdownInput.tsx +0 -560
  279. package/src/react/fields/RadioInput.tsx +0 -81
  280. package/src/react/fields/RepeaterInput.test.ts +0 -116
  281. package/src/react/fields/RepeaterInput.tsx +0 -1420
  282. package/src/react/fields/SelectFieldInput.tsx +0 -280
  283. package/src/react/fields/SliderInput.tsx +0 -81
  284. package/src/react/fields/TagsInput.tsx +0 -283
  285. package/src/react/fields/TextLikeInput.tsx +0 -256
  286. package/src/react/fields/ToggleButtonsInput.tsx +0 -60
  287. package/src/react/fields/ToggleFieldInput.tsx +0 -56
  288. package/src/react/fields/relationshipRenameDispatch.test.ts +0 -106
  289. package/src/react/fields/relationshipRenameDispatch.ts +0 -97
  290. package/src/react/fields/repeaterReconcile.test.ts +0 -114
  291. package/src/react/fields/repeaterReconcile.ts +0 -104
  292. package/src/react/fields/rowChromeButton.tsx +0 -336
  293. package/src/react/fields/rowState.ts +0 -106
  294. package/src/react/fields/syncRowGates.test.ts +0 -202
  295. package/src/react/fields/syncRowGates.ts +0 -66
  296. package/src/react/fields/textInputControls.tsx +0 -238
  297. package/src/react/fields/useRowReorderDnd.ts +0 -78
  298. package/src/react/formStateHelpers.test.ts +0 -508
  299. package/src/react/formStateHelpers.ts +0 -381
  300. package/src/react/hooks/use-mobile.ts +0 -19
  301. package/src/react/icon-context.tsx +0 -60
  302. package/src/react/index.ts +0 -194
  303. package/src/react/layouts/SidebarLayout.tsx +0 -250
  304. package/src/react/layouts/TopbarLayout.tsx +0 -258
  305. package/src/react/navigate.tsx +0 -37
  306. package/src/react/onProviderSynced.test.ts +0 -90
  307. package/src/react/parseRecordEditUrl.test.ts +0 -122
  308. package/src/react/parseRecordEditUrl.ts +0 -94
  309. package/src/react/persistedState.ts +0 -40
  310. package/src/react/registry.ts +0 -48
  311. package/src/react/right-panel-registry.tsx +0 -47
  312. package/src/react/schemaRenderer/AlertRenderer.tsx +0 -112
  313. package/src/react/schemaRenderer/EntryRenderer.tsx +0 -501
  314. package/src/react/schemaRenderer/SectionRenderer.tsx +0 -120
  315. package/src/react/schemaRenderer/SimpleElements.tsx +0 -306
  316. package/src/react/schemaRenderer/TabsRenderer.tsx +0 -62
  317. package/src/react/schemaRenderer/WizardRenderer.tsx +0 -338
  318. package/src/react/schemaRenderer/action/ActionGroupTrigger.tsx +0 -177
  319. package/src/react/schemaRenderer/action/ActionModalDialog.tsx +0 -273
  320. package/src/react/schemaRenderer/action/ConfirmActionDialog.tsx +0 -61
  321. package/src/react/schemaRenderer/action/HandlerActionButton.tsx +0 -43
  322. package/src/react/schemaRenderer/action/MethodActionButton.tsx +0 -64
  323. package/src/react/schemaRenderer/action/buttons.tsx +0 -99
  324. package/src/react/schemaRenderer/action/helpers.ts +0 -140
  325. package/src/react/schemaRenderer/action/renderAction.tsx +0 -245
  326. package/src/react/schemaRenderer/columnFormat.ts +0 -65
  327. package/src/react/schemaRenderer/constants.ts +0 -50
  328. package/src/react/schemaRenderer/form/FormRenderer.tsx +0 -274
  329. package/src/react/schemaRenderer/form/renderField.tsx +0 -511
  330. package/src/react/schemaRenderer/helpers.tsx +0 -81
  331. package/src/react/schemaRenderer/table/CardsLayoutBody.tsx +0 -308
  332. package/src/react/schemaRenderer/table/TableRenderer.tsx +0 -123
  333. package/src/react/schemaRenderer/table/TableRendererBody.tsx +0 -974
  334. package/src/react/schemaRenderer/table/filters.tsx +0 -1233
  335. package/src/react/schemaRenderer/table/formatCell.tsx +0 -264
  336. package/src/react/schemaRenderer/table/links.tsx +0 -112
  337. package/src/react/schemaRenderer/table/renderRowActions.tsx +0 -52
  338. package/src/react/schemaRenderer/table/url.tsx +0 -143
  339. package/src/react/theme-preview/apply.ts +0 -99
  340. package/src/react/theme-preview/build-html.ts +0 -436
  341. package/src/react/ui/button.tsx +0 -51
  342. package/src/react/ui/calendar.tsx +0 -67
  343. package/src/react/ui/checkbox.tsx +0 -29
  344. package/src/react/ui/dialog.tsx +0 -108
  345. package/src/react/ui/dropdown-menu.tsx +0 -97
  346. package/src/react/ui/input.tsx +0 -20
  347. package/src/react/ui/label.tsx +0 -21
  348. package/src/react/ui/popover.tsx +0 -50
  349. package/src/react/ui/select.tsx +0 -169
  350. package/src/react/ui/separator.tsx +0 -25
  351. package/src/react/ui/sheet.tsx +0 -136
  352. package/src/react/ui/sidebar.tsx +0 -723
  353. package/src/react/ui/skeleton.tsx +0 -13
  354. package/src/react/ui/slider.tsx +0 -34
  355. package/src/react/ui/switch.tsx +0 -28
  356. package/src/react/ui/table.tsx +0 -105
  357. package/src/react/ui/tabs.tsx +0 -63
  358. package/src/react/ui/textarea.tsx +0 -18
  359. package/src/react/ui/tooltip.tsx +0 -64
  360. package/src/react/useResizableWidth.ts +0 -139
  361. package/src/react/utils.ts +0 -6
  362. package/src/react/widgetRegistry.test.ts +0 -43
  363. package/src/react/widgetRegistry.ts +0 -50
  364. package/src/react/widgets/StatsOverviewRenderer.tsx +0 -232
  365. package/src/react/widgets/TableWidgetRenderer.tsx +0 -231
  366. package/src/react/widgets/ViewRenderer.tsx +0 -71
  367. package/src/relationManagerData.test.ts +0 -1595
  368. package/src/richtext/index.ts +0 -8
  369. package/src/richtext/registry.ts +0 -89
  370. package/src/routes/globals.ts +0 -148
  371. package/src/routes/guard.test.ts +0 -325
  372. package/src/routes/helpers.ts +0 -704
  373. package/src/routes/pages.ts +0 -175
  374. package/src/routes/panel.ts +0 -204
  375. package/src/routes/relations.ts +0 -1243
  376. package/src/routes/resources.ts +0 -781
  377. package/src/routes/theme.ts +0 -91
  378. package/src/routes-nested-relations.test.ts +0 -676
  379. package/src/routes-relations.test.ts +0 -972
  380. package/src/routes.test.ts +0 -2027
  381. package/src/routes.ts +0 -303
  382. package/src/schema/Alert.test.ts +0 -109
  383. package/src/schema/Alert.ts +0 -131
  384. package/src/schema/Block.ts +0 -169
  385. package/src/schema/Breadcrumbs.ts +0 -40
  386. package/src/schema/Card.ts +0 -35
  387. package/src/schema/Divider.ts +0 -20
  388. package/src/schema/Element.ts +0 -219
  389. package/src/schema/EmptyState.test.ts +0 -37
  390. package/src/schema/EmptyState.ts +0 -63
  391. package/src/schema/Fieldset.ts +0 -43
  392. package/src/schema/Grid.ts +0 -43
  393. package/src/schema/Group.ts +0 -30
  394. package/src/schema/Heading.ts +0 -39
  395. package/src/schema/Html.ts +0 -67
  396. package/src/schema/Icon.ts +0 -54
  397. package/src/schema/Image.ts +0 -57
  398. package/src/schema/LinkTag.ts +0 -41
  399. package/src/schema/Markdown.ts +0 -85
  400. package/src/schema/MetaTag.ts +0 -41
  401. package/src/schema/RelationTabs.ts +0 -71
  402. package/src/schema/ScriptTag.ts +0 -55
  403. package/src/schema/Section.ts +0 -160
  404. package/src/schema/ServerDataElement.test.ts +0 -140
  405. package/src/schema/ServerDataElement.ts +0 -156
  406. package/src/schema/SlotComponent.test.ts +0 -77
  407. package/src/schema/SlotComponent.ts +0 -71
  408. package/src/schema/Split.ts +0 -50
  409. package/src/schema/Stat.test.ts +0 -118
  410. package/src/schema/Stat.ts +0 -154
  411. package/src/schema/StatsOverview.test.ts +0 -141
  412. package/src/schema/StatsOverview.ts +0 -119
  413. package/src/schema/StyleTag.ts +0 -35
  414. package/src/schema/TableWidget.test.ts +0 -297
  415. package/src/schema/TableWidget.ts +0 -289
  416. package/src/schema/Tabs.ts +0 -79
  417. package/src/schema/Text.ts +0 -58
  418. package/src/schema/UnorderedList.ts +0 -49
  419. package/src/schema/View.test.ts +0 -111
  420. package/src/schema/View.ts +0 -127
  421. package/src/schema/Wizard.ts +0 -220
  422. package/src/schema/containers.test.ts +0 -564
  423. package/src/schema/headTags.test.ts +0 -134
  424. package/src/schema/index.ts +0 -40
  425. package/src/schema/primes.test.ts +0 -269
  426. package/src/schema/resolveSchema.test.ts +0 -379
  427. package/src/schema/resolveSchema.ts +0 -917
  428. package/src/schema/sanitize.ts +0 -58
  429. package/src/search.test.ts +0 -446
  430. package/src/search.ts +0 -178
  431. package/src/sessionFilters.test.ts +0 -375
  432. package/src/sessionFilters.ts +0 -143
  433. package/src/slot-components/index.ts +0 -10
  434. package/src/slot-components/registry.ts +0 -56
  435. package/src/styles/file-upload.css +0 -13
  436. package/src/summarizers/Summarizer.test.ts +0 -84
  437. package/src/summarizers/Summarizer.ts +0 -123
  438. package/src/summarizers/index.ts +0 -11
  439. package/src/theme/base-colors.ts +0 -68
  440. package/src/theme/chart-colors.ts +0 -50
  441. package/src/theme/colors.ts +0 -447
  442. package/src/theme/generate-css.test.ts +0 -139
  443. package/src/theme/generate-css.ts +0 -44
  444. package/src/theme/generate-scale.test.ts +0 -106
  445. package/src/theme/generate-scale.ts +0 -97
  446. package/src/theme/icon-map.ts +0 -42
  447. package/src/theme/index.ts +0 -34
  448. package/src/theme/migrate.test.ts +0 -178
  449. package/src/theme/migrate.ts +0 -81
  450. package/src/theme/presets.ts +0 -135
  451. package/src/theme/radius.ts +0 -18
  452. package/src/theme/resolve.test.ts +0 -238
  453. package/src/theme/resolve.ts +0 -96
  454. package/src/theme/spacing.ts +0 -18
  455. package/src/theme/storage.test.ts +0 -126
  456. package/src/theme/storage.ts +0 -106
  457. package/src/theme/theme-colors.ts +0 -88
  458. package/src/theme/types.ts +0 -125
  459. package/src/uploads/UploadAdapter.ts +0 -35
  460. package/src/uploads/index.ts +0 -2
  461. package/src/uploads/localUpload.test.ts +0 -70
  462. package/src/uploads/localUpload.ts +0 -84
  463. package/src/validation/Validator.ts +0 -49
  464. package/src/validation/index.ts +0 -28
  465. package/src/validation/rules.ts +0 -78
  466. package/src/validation/runValidators.ts +0 -435
  467. package/src/validation/uniqueValidator.test.ts +0 -196
  468. package/src/validation/uniqueValidator.ts +0 -133
  469. package/src/validation/validators.test.ts +0 -268
  470. package/src/vite.test.ts +0 -184
  471. package/src/vite.ts +0 -787
  472. package/src/widgets/index.ts +0 -10
  473. package/src/widgets/registry.ts +0 -45
  474. package/src/widgets.test.ts +0 -592
  475. package/tsconfig.build.json +0 -11
  476. package/tsconfig.json +0 -4
  477. package/tsconfig.test.json +0 -10
  478. package/views/react/Dashboard.tsx +0 -27
  479. package/views/react/Resources/Form.tsx +0 -102
  480. package/views/react/Resources/Index.tsx +0 -49
@@ -1,939 +0,0 @@
1
- import { Element, type ElementMeta, type LayoutContext } from '../schema/Element.js'
2
- import { Field, type FieldMeta } from './Field.js'
3
- import type { RenderContext } from '../schema/resolveSchema.js'
4
- import type { Action, ActionMeta } from '../actions/Action.js'
5
- import type { ModelLike } from '../orm/modelDefaults.js'
6
- import {
7
- RowButton,
8
- type RowButtonKind,
9
- type RowButtonsMeta,
10
- } from './RowButton.js'
11
-
12
- /**
13
- * Configuration for `Repeater.relationship(...)`. Stores rows in a real
14
- * `HasMany` relation on the parent record instead of serializing them
15
- * to a JSON column.
16
- *
17
- * `name` is the only required field — it must match a key on the
18
- * parent's `static relations` map (rudder ORM convention). The
19
- * `model` + `foreignKey` overrides exist for ORMs that don't follow
20
- * that convention; both default to discovery via the parent's
21
- * relations map at submit time.
22
- *
23
- * `orderColumn` is the integer column on the child model that
24
- * receives the row's 0-based index after each save. Omit when the
25
- * order doesn't need to round-trip through the database.
26
- */
27
- export interface RepeaterRelationshipConfig {
28
- /** Relationship key on the parent (e.g. `'attachments'`). */
29
- name: string
30
- /** Override the child model. Defaults to `parent.relations[name].model()`. */
31
- model?: ModelLike
32
- /** Override the FK column on the child. Defaults to `parent.relations[name].foreignKey`. */
33
- foreignKey?: string
34
- /** Optional integer column on the child to receive the row index. */
35
- orderColumn?: string
36
- /**
37
- * M2M only — pivot-table extra columns to surface as editable per-row
38
- * fields. Each name must match an inner-schema field's `name`; the
39
- * row's values for these names round-trip through the pivot row
40
- * (read via `accessor.withPivot(...)`, written via
41
- * `accessor.attach({ id: { … } })` for new rows and
42
- * `accessor.updatePivot(id, { … })` for existing rows). No-op (and
43
- * a clear error at submit time) on `hasMany` / `morphMany` modes.
44
- */
45
- pivotColumns?: string[]
46
- }
47
-
48
- /** Public meta — `model` + `foreignKey` are server-only and stay private. */
49
- export interface RepeaterRelationshipMeta {
50
- name: string
51
- orderColumn?: string
52
- }
53
-
54
- /**
55
- * Mode of the underlying relation, derived from the parent's `static
56
- * relations` map at submit time. Surfaced on `RepeaterRowContext.mode`
57
- * so per-row hooks can branch when needed (e.g. an `afterDelete` that
58
- * runs cleanup only when the child record was actually removed, not
59
- * when a pivot row was detached).
60
- */
61
- export type RepeaterRelationMode =
62
- | 'hasMany'
63
- | 'morphMany'
64
- | 'belongsToMany'
65
- | 'morphToMany'
66
- | 'morphedByMany'
67
-
68
- /**
69
- * Context passed to `Repeater.afterCreate / afterUpdate / afterDelete`
70
- * hooks. One invocation per row; `parent` is the post-save parent record
71
- * (PK already set), `record` carries the persisted child.
72
- *
73
- * For M2M modes (`belongsToMany / morphToMany / morphedByMany`)
74
- * `afterDelete` fires after `accessor.detach(...)` — the child record
75
- * may still exist (other parents may link to it). Branch on `ctx.mode`
76
- * if your cleanup depends on physical deletion vs detach.
77
- */
78
- export interface RepeaterRowContext<P = unknown> {
79
- /** Post-save parent record (the same `record` the surrounding form's
80
- * `afterSave` would see). */
81
- parent: P
82
- /** Convenience — `parent[primaryKey]`. */
83
- parentId: string | number
84
- /** The Repeater field's `name`. */
85
- field: string
86
- /** 0-based index of the row in the submitted set; `-1` for `afterDelete`
87
- * since deleted rows aren't in the submitted set. */
88
- index: number
89
- /** Underlying relation mode — see above for `afterDelete` semantics
90
- * on M2M. */
91
- mode: RepeaterRelationMode
92
- }
93
-
94
- /** Handler signature shared by the three after-hooks. */
95
- export type RepeaterRowAfterHandler<C = unknown, P = unknown> = (
96
- record: C,
97
- ctx: RepeaterRowContext<P>,
98
- ) => void | Promise<void>
99
-
100
- /**
101
- * Function evaluated once per row at meta-build to derive a human-readable
102
- * label for the collapsed-row header. Called with the row's submitted values
103
- * (or `{}` on a fresh blank row); should return a short string. Errors are
104
- * swallowed and the renderer falls back to the row index.
105
- */
106
- export type RepeaterItemLabel = (row: Record<string, unknown>) => string
107
-
108
- /**
109
- * Responsive column-count config for `Repeater.grid()` and `Builder.grid()`.
110
- * Keys map to the standard Tailwind breakpoints (sm/md/lg/xl/2xl). `default`
111
- * sets the column count below the smallest declared breakpoint and falls back
112
- * to 1 when omitted. Pass values ≥ 2 — anything lower is dropped at the field
113
- * level so the renderer sees only meaningful entries.
114
- *
115
- * Example: `.grid({ default: 1, md: 2, xl: 3 })` renders one column on mobile,
116
- * two from `md` up, three from `xl` up.
117
- */
118
- export interface ResponsiveGridConfig {
119
- default?: number
120
- sm?: number
121
- md?: number
122
- lg?: number
123
- xl?: number
124
- '2xl'?: number
125
- }
126
-
127
- /** Single-number form for `grid(n)` (current API) OR responsive object form. */
128
- export type RepeaterGridConfig = number | ResponsiveGridConfig
129
-
130
- /**
131
- * Header descriptor for `Repeater.table([...])` mode. One entry per inner
132
- * schema field, in declaration order — column[i] is the header for
133
- * `schema[i]`. Object literal (not a class) to keep the surface lean;
134
- * promote to a builder class if we ever need chaining or async resolvers.
135
- *
136
- * `alignment` aligns header text + cell contents (cells use `text-*`).
137
- * `width` is a raw CSS width string passed to `<col style="width: …">`.
138
- * `required` adds a red asterisk after the header label — purely visual,
139
- * doesn't affect validation (the inner field's own `required()` does).
140
- */
141
- export interface RepeaterTableColumn {
142
- label: string
143
- alignment?: 'left' | 'center' | 'right'
144
- width?: string
145
- required?: boolean
146
- }
147
-
148
- /**
149
- * Per-row visibility rule. Either a literal `boolean` or a callback receiving
150
- * a row-scoped `LayoutContext`. The context's `values / $get / $set / row` are
151
- * row-local; `record / user` mirror the parent form's render context.
152
- *
153
- * Returning truthy hides the row. The renderer keeps hidden rows mounted with
154
- * `display: none` so their inputs (and `__id`) round-trip through FormData
155
- * unchanged — visibility is purely UX, never a data filter.
156
- *
157
- * Throwing predicates fail-closed-as-visible (i.e. row stays visible) and log
158
- * a warning. We choose the inverse posture from `Element.evaluateVisibility`
159
- * because a misbehaving `itemHidden` should not silently hide rows the user
160
- * thinks they're editing.
161
- */
162
- export type RepeaterItemHiddenRule =
163
- | boolean
164
- | ((ctx: LayoutContext) => boolean | Promise<boolean>)
165
-
166
- /**
167
- * Per-row capability gate. Evaluated against a row-scoped `LayoutContext`
168
- * (same shape as `itemHidden`'s rule). Resolving truthy keeps the
169
- * capability enabled (the matching row button stays mounted); resolving
170
- * falsy removes it. Throwing predicates fail-open — the capability stays
171
- * enabled and we log a warning, mirroring `itemHidden`'s posture so a
172
- * misbehaving rule doesn't silently lock the user out of editing data.
173
- *
174
- * Used by `itemCanDelete / itemCanClone / itemCanReorder`.
175
- */
176
- export type RepeaterItemCanRule =
177
- | boolean
178
- | ((ctx: LayoutContext) => boolean | Promise<boolean>)
179
-
180
- /**
181
- * Resolved metadata for a single Repeater row. `id` is a stable identifier
182
- * scoped to the form render — survives reorder + clone client-side and is
183
- * round-tripped through a hidden `__id` value on submit so the renderer
184
- * keeps stable React keys across SSR / SPA / partial-resolve cycles.
185
- *
186
- * `children` is the resolved inner schema (resolved with row-scoped values).
187
- * Renderers iterate `rows` and feed each `children` array to `SchemaRenderer`.
188
- *
189
- * `hidden` is set when `itemHidden(rule)` resolved truthy for this row; the
190
- * renderer keeps the row mounted but hides its chrome + body so values still
191
- * round-trip on submit.
192
- */
193
- export interface RepeaterRowMeta {
194
- id: string
195
- children: ElementMeta[]
196
- itemLabel?: string
197
- hidden?: boolean
198
- /**
199
- * Resolved per-row action metas for `extraItemActions(...)`. Empty or
200
- * absent when the field has no extra actions, OR when every action's
201
- * visibility rule resolved false for this row. The renderer mounts these
202
- * in the row header alongside clone/delete; clicking dispatches the
203
- * action with `_rowPath = "<fieldName>.<index>"` so the server can
204
- * reconstruct the row-scoped handler context.
205
- */
206
- extraActions?: ActionMeta[]
207
- /**
208
- * Per-row capability flags. Stamped only when the corresponding
209
- * `itemCan*(rule)` resolved falsy for this row — non-customized rows
210
- * pay zero serialization cost. The renderer reads these and skips the
211
- * matching button: `canDelete: false` removes the trash, `canClone: false`
212
- * removes the clone (no-op when `cloneable()` is off field-wide),
213
- * `canReorder: false` removes the drag grip and Up/Down arrows on this
214
- * row only (no-op when `reorderable()` is off).
215
- *
216
- * Capability gates are presentation, not authorization — they hide the
217
- * button but don't reject tampered POST bodies. For real authorization,
218
- * gate the parent form's lifecycle hooks.
219
- */
220
- canDelete?: false
221
- canClone?: false
222
- canReorder?: false
223
- }
224
-
225
- export interface RepeaterFieldMeta extends FieldMeta {
226
- fieldType: 'repeater'
227
- rows: RepeaterRowMeta[]
228
- /** Zero-row blueprint for the client's Add button. */
229
- template: ElementMeta[]
230
- columns?: number
231
- minItems?: number
232
- maxItems?: number
233
- defaultItems?: number
234
- reorderable?: boolean
235
- collapsible?: boolean
236
- defaultCollapsed?: boolean
237
- /**
238
- * Set when `Repeater.accordion()` is configured. The renderer replaces
239
- * the per-row collapsed map with a single "open row id" slot — picking
240
- * row N collapses every other row. Implies `collapsible: true` (the
241
- * accordion ergonomic only makes sense over a collapsible repeater).
242
- */
243
- accordion?: boolean
244
- cloneable?: boolean
245
- addActionLabel?: string
246
- /**
247
- * Set when `Repeater.simple(field)` is configured. Tells the renderer
248
- * to drop the per-row chrome (header, clone, collapse) and lay the
249
- * single inner field out flush with a trash button on each row. The
250
- * wire format is unchanged — `<name>.<i>.<innerName>` — only the
251
- * stored shape differs (`[v]` instead of `[{name: v}]`).
252
- */
253
- simple?: boolean
254
- /**
255
- * Set when `Repeater.grid(...)` is configured. Lays the ROWS themselves
256
- * in an n-column grid (different from `columns(n)` which grids the inner
257
- * schema *inside* a row). Useful for tile-style pickers / member cards /
258
- * icon palettes.
259
- *
260
- * Two shapes:
261
- * - `number` (≥ 2) — fixed grid at every viewport.
262
- * - `ResponsiveGridConfig` — `{ default?, sm?, md?, lg?, xl?, '2xl'? }`
263
- * keyed by Tailwind breakpoint. The renderer emits a scoped
264
- * `<style>` block with media queries per declared breakpoint.
265
- *
266
- * Renderer swaps the outer `flex flex-col` container for a CSS grid and
267
- * skips the drag-drop indicator in grid mode (the horizontal bar reads
268
- * wrong across grid cells); button reorder still works.
269
- */
270
- grid?: RepeaterGridConfig
271
- /**
272
- * Set when `Repeater.table([...])` is configured. Renders rows as
273
- * `<tr>` and inner fields as `<td>`, with the supplied column
274
- * headers in a `<thead>`. Useful for compact uniform-row repeaters.
275
- * Mutually exclusive with `simple` (single-field shape conflicts)
276
- * and with `grid` (different layout); collapsible/accordion are
277
- * meaningless on `<tr>` rows so the renderer ignores them. The
278
- * inner schema's field labels are suppressed via `[&_label]:sr-only`
279
- * so headers carry the labelling. `clone / delete / extraActions`
280
- * land in a final actions cell when configured.
281
- */
282
- table?: {
283
- columns: RepeaterTableColumn[]
284
- }
285
- /**
286
- * Set when `Repeater.relationship(...)` is configured. Tells diagnostics
287
- * (and any future client UI) that the row diff is persisted via a
288
- * `HasMany` relation rather than a JSON column on the parent. The wire
289
- * shape carries only `{ name, orderColumn? }` — `model` and
290
- * `foreignKey` stay server-only.
291
- */
292
- relationship?: RepeaterRelationshipMeta
293
- /**
294
- * Per-slot chrome overrides for the seven built-in row buttons (add /
295
- * clone / delete / moveUp / moveDown / reorder / collapse). Authors set
296
- * these via `.addAction(RowButton.make()…)` etc.; the renderer merges
297
- * each override on top of its hardcoded default (icon + tooltip + color
298
- * + aria-label). Absent slots fall through to defaults — non-customized
299
- * Repeaters pay zero serialization cost.
300
- */
301
- buttons?: RowButtonsMeta
302
- }
303
-
304
- /**
305
- * Array-of-subschema field. The author composes an inner schema once via
306
- * `.schema([...])`; the rendered form lets the end user add / remove /
307
- * reorder rows of that schema.
308
- *
309
- * Storage on the parent record is a plain array of objects:
310
- * `[{ field1, field2 }, …]`. No special wrapper, no `position` column,
311
- * no per-row identity persistence.
312
- *
313
- * `toMeta` resolves the inner schema once per submitted row, plus a
314
- * zero-row template for the client's Add button. `coerceFormValues` and
315
- * `validateSchema` recurse into the rows using `<name>.<i>.<childName>`
316
- * dotted keys for both flat form-encoded bodies and JSON bodies.
317
- *
318
- * Plan #14.
319
- */
320
- export class RepeaterField extends Field {
321
- protected override _children: Element[] = []
322
-
323
- private _columns?: number
324
- private _minItems?: number
325
- private _maxItems?: number
326
- private _defaultItems = 1
327
- private _reorderable = false
328
- private _collapsible = false
329
- private _defaultCollapsed = false
330
- private _accordion = false
331
- private _cloneable = false
332
- private _addActionLabel?: string
333
- private _itemLabel?: RepeaterItemLabel
334
- private _itemHidden?: RepeaterItemHiddenRule
335
- private _itemCanDelete?: RepeaterItemCanRule
336
- private _itemCanClone?: RepeaterItemCanRule
337
- private _itemCanReorder?: RepeaterItemCanRule
338
- private _extraItemActions: Action[] = []
339
- private _simple = false
340
- private _grid?: RepeaterGridConfig
341
- private _tableColumns?: RepeaterTableColumn[]
342
- private _buttons: { [K in RowButtonKind]?: RowButton } = {}
343
- private _relationship?: RepeaterRelationshipConfig
344
- private _afterCreate?: RepeaterRowAfterHandler
345
- private _afterUpdate?: RepeaterRowAfterHandler
346
- private _afterDelete?: RepeaterRowAfterHandler
347
-
348
- private constructor(name: string) {
349
- super(name, 'repeater')
350
- }
351
-
352
- static make(name: string): RepeaterField {
353
- return new RepeaterField(name)
354
- }
355
-
356
- /** Inner schema rendered per row. Each row resolves these elements. */
357
- schema(elements: Element[]): this {
358
- this._children = elements
359
- return this
360
- }
361
-
362
- /**
363
- * Single-field "flat array" Repeater. Storage shape changes from
364
- * `[{ <innerName>: value }]` to `[value, value, …]` — handy for
365
- * keyword/alias/alt-domain lists where the row is just one input.
366
- *
367
- * Wire format on the form stays the same `<name>.<i>.<innerName>` shape
368
- * (the inner field's name is opaque to the consumer); the flat shape
369
- * shows up only in the saved record (after coerce → unwrap) and in the
370
- * loaded record (re-wrapped on the way into `resolveRepeaterRows`).
371
- *
372
- * Validators run against the wrapped shape so per-field rules
373
- * (`required`, `unique`, custom validators) work the same as in a
374
- * regular Repeater. The chrome strips down: no per-row header, no
375
- * collapse, no clone — just an inline trash button on each row plus
376
- * the bottom Add button. Reorder still works when `reorderable()`
377
- * is set.
378
- *
379
- * Calling `simple()` replaces the inner schema with the single field —
380
- * pass any prior `schema(...)` you'd called as wasted; `simple()` is
381
- * the schema for these rows.
382
- */
383
- simple(field: Field): this {
384
- if (this._relationship) {
385
- throw new Error(
386
- `[Pilotiq] Repeater "${this.name}": simple() is incompatible with relationship() — flat-array storage can't round-trip through child records.`,
387
- )
388
- }
389
- this._simple = true
390
- this._children = [field]
391
- return this
392
- }
393
-
394
- /** Grid column count for the inner schema (passed through to client). */
395
- columns(n: number): this { this._columns = n; return this }
396
-
397
- /** Number of empty rows to render initially when no values exist. */
398
- defaultItems(n: number): this { this._defaultItems = n; return this }
399
-
400
- /** Validator + client gate: at least `n` rows on submit. */
401
- minItems(n: number): this { this._minItems = n; return this }
402
-
403
- /** Validator + client gate: at most `n` rows on submit. */
404
- maxItems(n: number): this { this._maxItems = n; return this }
405
-
406
- /** Show drag handle + keyboard reorder controls per row. */
407
- reorderable(value: boolean = true): this { this._reorderable = value; return this }
408
-
409
- /** Show collapse chevron per row. */
410
- collapsible(value: boolean = true): this { this._collapsible = value; return this }
411
-
412
- /** Render rows collapsed by default (requires `collapsible()`). */
413
- collapsed(value: boolean = true): this { this._defaultCollapsed = value; return this }
414
-
415
- /**
416
- * Accordion mode: only one row open at a time. Picking a different row
417
- * collapses the currently-open one. Pair with `collapsed()` to start
418
- * with every row collapsed (default is "first row open"). Auto-arms
419
- * `collapsible()` since the accordion ergonomic is meaningless on a
420
- * non-collapsible repeater.
421
- */
422
- accordion(value: boolean = true): this {
423
- this._accordion = value
424
- if (value) this._collapsible = true
425
- return this
426
- }
427
-
428
- /** Show duplicate-row button per row. */
429
- cloneable(value: boolean = true): this { this._cloneable = value; return this }
430
-
431
- /**
432
- * Function evaluated per row for the collapsed-row header. The result
433
- * lands on `RepeaterRowMeta.itemLabel`; renderers fall back to the row
434
- * index when missing or when the function throws.
435
- */
436
- itemLabel(fn: RepeaterItemLabel): this { this._itemLabel = fn; return this }
437
-
438
- /**
439
- * Per-row visibility predicate. Evaluated against a row-scoped
440
- * `LayoutContext` (`values / $get / $set / row` all row-local). Returning
441
- * truthy hides the row from the user; the renderer keeps inputs mounted
442
- * via `display: none` so values round-trip through FormData on submit.
443
- *
444
- * Throwing → row stays visible + warn (inverse of layout `visible()` —
445
- * a misbehaving rule shouldn't silently hide data the user is editing).
446
- */
447
- itemHidden(rule: RepeaterItemHiddenRule): this { this._itemHidden = rule; return this }
448
-
449
- /**
450
- * Per-row gate for the trash button. Resolving truthy keeps the trash
451
- * button visible (default); resolving falsy hides it on that row only.
452
- * Useful for "this row is finalized — only the others can be removed".
453
- *
454
- * Predicate sees the same row-scoped `LayoutContext` as `itemHidden`
455
- * (`values / $get / $set / row` are row-local, `record / user` mirror
456
- * the parent form). Throwing predicates fail-open — the button stays
457
- * visible and we log a warning, mirroring `itemHidden`'s posture.
458
- *
459
- * Presentation only. Tampered POST bodies that delete a gated row will
460
- * still go through; gate the parent form's lifecycle hooks for real
461
- * authorization.
462
- */
463
- itemCanDelete(rule: RepeaterItemCanRule): this { this._itemCanDelete = rule; return this }
464
-
465
- /**
466
- * Per-row gate for the clone (`Duplicate row`) button. Resolving truthy
467
- * keeps it visible (default); resolving falsy hides it on that row only.
468
- * No-op when `cloneable()` is off field-wide.
469
- *
470
- * Same predicate shape, same fail-open posture as `itemCanDelete`.
471
- */
472
- itemCanClone(rule: RepeaterItemCanRule): this { this._itemCanClone = rule; return this }
473
-
474
- /**
475
- * Per-row gate for the reorder controls (drag grip + Up / Down arrows).
476
- * Resolving truthy keeps them visible (default); resolving falsy hides
477
- * them on that row only — pinning the row to its current position while
478
- * its neighbours stay reorderable. No-op when `reorderable()` is off
479
- * field-wide.
480
- *
481
- * Same predicate shape, same fail-open posture as `itemCanDelete`. Drag
482
- * targeting is unaffected — a non-pinned row can still drop at the
483
- * pinned row's slot, which is the right semantics (the OTHER row is the
484
- * one being moved). Pin both sides if you need a pair to stay adjacent.
485
- */
486
- itemCanReorder(rule: RepeaterItemCanRule): this { this._itemCanReorder = rule; return this }
487
-
488
- /** Custom label for the "Add row" button. Default `'Add'`. */
489
- addActionLabel(label: string): this { this._addActionLabel = label; return this }
490
-
491
- /**
492
- * Lay the ROWS themselves in an n-column grid — different from
493
- * `columns(n)` which grids the inner schema *inside* a row.
494
- *
495
- * Two forms:
496
- * - `grid(2)` — fixed 2-column grid at every viewport.
497
- * - `grid({ default: 1, md: 2, xl: 3 })` — responsive: column count
498
- * changes at the named Tailwind breakpoint (sm 640px / md 768px /
499
- * lg 1024px / xl 1280px / 2xl 1536px). `default` (or `1` when
500
- * omitted) is the count below the smallest declared breakpoint.
501
- *
502
- * Pass `n >= 2` or an object with at least one breakpoint having a value
503
- * `>= 2`; otherwise the grid mode resets (vertical stack, the default).
504
- *
505
- * In grid mode the renderer keeps reorder buttons working but
506
- * suppresses the horizontal drop indicator (which doesn't read
507
- * across grid cells). Drag-and-drop itself still moves rows; the
508
- * cursor is the only feedback.
509
- */
510
- grid(config: RepeaterGridConfig): this {
511
- const normalized = normalizeGridConfig(config)
512
- if (normalized === undefined) delete this._grid
513
- else this._grid = normalized
514
- return this
515
- }
516
-
517
- /**
518
- * Render rows as a compact HTML table — one `<tr>` per row, one
519
- * `<td>` per inner field, with the supplied column headers above.
520
- * Columns map 1:1 to `schema()` fields in declaration order.
521
- *
522
- * Pass an empty array to turn table mode off (handy for toggling via
523
- * a config value). Mutually exclusive with `simple()` (single-field
524
- * shape conflicts) and `grid()` (different layout) — the field
525
- * applies whichever was set last; renderer ignores collapsible /
526
- * accordion in table mode (`<tr>` rows can't collapse). The inner
527
- * schema's field labels render `sr-only` so headers carry the
528
- * labelling; clone / delete / `extraItemActions` land in a final
529
- * actions cell when configured.
530
- */
531
- table(columns: RepeaterTableColumn[]): this {
532
- if (columns.length === 0) delete this._tableColumns
533
- else this._tableColumns = columns
534
- return this
535
- }
536
-
537
- /**
538
- * Persist rows to a `HasMany` (or `MorphMany` / `MorphOne`) relation on
539
- * the parent record instead of serializing them to a JSON column. Each
540
- * row maps to a real child record; submit creates / updates / deletes
541
- * children against the relation transparently.
542
- *
543
- * For `morphMany`, the child also carries `<morphName>Id` +
544
- * `<morphName>Type` columns; the framework stamps both on every create
545
- * (and refuses to overwrite them on update) so a tampered POST body
546
- * can't reassign a row to a different polymorphic parent. The morph
547
- * shape is read off the parent's `static relations[name]` descriptor —
548
- * no `foreignKey` override applies.
549
- *
550
- * Pass either the relationship name as a string (the common case —
551
- * `model` + `foreignKey` are auto-discovered from the parent's
552
- * `static relations` map) or an object form for explicit overrides.
553
- *
554
- * Mutually exclusive with `simple()` (the flat `[v, v, …]` storage
555
- * shape can't round-trip through child records that need named
556
- * columns) and with `dehydrated(false)` (the field's whole purpose
557
- * is to write data — silently dropping it would be confusing).
558
- * Validators run unchanged.
559
- */
560
- relationship(arg: string | RepeaterRelationshipConfig): this {
561
- if (this._simple) {
562
- throw new Error(
563
- `[Pilotiq] Repeater "${this.name}": relationship() is incompatible with simple() — relationship-backed rows need named columns on the child model.`,
564
- )
565
- }
566
- if (this.isDehydrated() === false) {
567
- throw new Error(
568
- `[Pilotiq] Repeater "${this.name}": relationship() is incompatible with dehydrated(false) — the field's purpose is to persist data.`,
569
- )
570
- }
571
- this._relationship = typeof arg === 'string' ? { name: arg } : { ...arg }
572
- return this
573
- }
574
-
575
- /**
576
- * Sugar over `.relationship({ ..., orderColumn: col })`. Sets the
577
- * order column on an already-configured relationship; throws when
578
- * `relationship()` hasn't been called first so misconfiguration
579
- * surfaces eagerly.
580
- */
581
- orderColumn(col: string): this {
582
- if (!this._relationship) {
583
- throw new Error(
584
- `[Pilotiq] Repeater "${this.name}": orderColumn() requires relationship() to be configured first.`,
585
- )
586
- }
587
- this._relationship.orderColumn = col
588
- return this
589
- }
590
-
591
- /**
592
- * M2M only — declare pivot-table extra columns to surface as editable
593
- * per-row fields. Each entry must match an inner-schema field's `name`;
594
- * row values for those names load via `accessor.withPivot(...)`,
595
- * persist via `accessor.attach({ id: pivotData })` for new rows and
596
- * `accessor.updatePivot(id, pivotData)` for existing rows.
597
- *
598
- * Sugar over `.relationship({ ..., pivotColumns: cols })`. Like
599
- * `orderColumn()`, throws when `relationship()` hasn't been called
600
- * first. Empty array clears the projection.
601
- */
602
- pivotColumns(cols: string[]): this {
603
- if (!this._relationship) {
604
- throw new Error(
605
- `[Pilotiq] Repeater "${this.name}": pivotColumns() requires relationship() to be configured first.`,
606
- )
607
- }
608
- this._relationship.pivotColumns = [...cols]
609
- return this
610
- }
611
-
612
- /**
613
- * Per-row hook that fires after each newly created child record is
614
- * persisted in `relationship()` mode. Receives the created record
615
- * (with its primary key set) and a `RepeaterRowContext` carrying
616
- * the parent record + row index + relation mode. Errors propagate
617
- * — a throwing handler aborts the rest of the persist diff (the
618
- * parent + any earlier rows have already saved; v1 is non-
619
- * transactional).
620
- *
621
- * No-op outside `relationship()` mode. Use `Form.afterCreate(...)`
622
- * for hooks that fire on the parent record's create.
623
- */
624
- afterCreate(fn: RepeaterRowAfterHandler): this {
625
- if (!this._relationship) {
626
- throw new Error(
627
- `[Pilotiq] Repeater "${this.name}": afterCreate() requires relationship() to be configured first.`,
628
- )
629
- }
630
- this._afterCreate = fn
631
- return this
632
- }
633
-
634
- /** Per-row hook that fires after each existing child record is
635
- * updated. Receives the updated record from the child model's
636
- * `update()` return (or the post-update reload when `update`
637
- * returns void). See `afterCreate` notes for error semantics. */
638
- afterUpdate(fn: RepeaterRowAfterHandler): this {
639
- if (!this._relationship) {
640
- throw new Error(
641
- `[Pilotiq] Repeater "${this.name}": afterUpdate() requires relationship() to be configured first.`,
642
- )
643
- }
644
- this._afterUpdate = fn
645
- return this
646
- }
647
-
648
- /** Per-row hook that fires after each removed row is processed.
649
- * For `hasMany / morphMany` modes the child record was physically
650
- * deleted via `model.delete()`; for M2M modes only the pivot row
651
- * was detached and the child may still exist. Branch on
652
- * `ctx.mode` when that distinction matters. */
653
- afterDelete(fn: RepeaterRowAfterHandler): this {
654
- if (!this._relationship) {
655
- throw new Error(
656
- `[Pilotiq] Repeater "${this.name}": afterDelete() requires relationship() to be configured first.`,
657
- )
658
- }
659
- this._afterDelete = fn
660
- return this
661
- }
662
-
663
- /**
664
- * Customize the bottom Add button's chrome (label / icon / color /
665
- * tooltip). Equivalent to `addActionLabel()` plus icon + color
666
- * overrides — when both are set, this customizer wins (it ships under
667
- * `meta.buttons.add` which the renderer reads after `addActionLabel`).
668
- */
669
- addAction(b: RowButton): this { this._buttons.add = b; return this }
670
-
671
- /** Customize the per-row clone (`Duplicate row`) button. */
672
- cloneAction(b: RowButton): this { this._buttons.clone = b; return this }
673
-
674
- /** Customize the per-row trash (`Remove row`) button. */
675
- deleteAction(b: RowButton): this { this._buttons.delete = b; return this }
676
-
677
- /** Customize the per-row Up arrow. */
678
- moveUpAction(b: RowButton): this { this._buttons.moveUp = b; return this }
679
-
680
- /** Customize the per-row Down arrow. */
681
- moveDownAction(b: RowButton): this { this._buttons.moveDown = b; return this }
682
-
683
- /**
684
- * Customize the drag-grip handle. `label` becomes the `aria-label`,
685
- * `tooltip` becomes the `title` attribute, `icon` swaps the grip glyph,
686
- * `color` re-tones the handle. The grip isn't a real `<button>` (it's a
687
- * draggable `<span>`) so click semantics don't apply.
688
- */
689
- reorderAction(b: RowButton): this { this._buttons.reorder = b; return this }
690
-
691
- /**
692
- * Customize the per-row collapse chevron. Applies to BOTH states by
693
- * default — the open chevron and the collapsed chevron share the
694
- * override unless `expandAction(...)` is also set, in which case
695
- * `collapseAction` covers only the open state and `expandAction`
696
- * covers the collapsed state.
697
- */
698
- collapseAction(b: RowButton): this { this._buttons.collapse = b; return this }
699
-
700
- /**
701
- * Customize the per-row chevron when the row is currently *collapsed*
702
- * (i.e. the "click me to expand" state). Sibling of `collapseAction`
703
- * for the closed-state glyph; without this, both states fall through
704
- * to `collapseAction` (back-compat) and ultimately to the default
705
- * chevron pair (right when collapsed, down when open).
706
- */
707
- expandAction(b: RowButton): this { this._buttons.expand = b; return this }
708
-
709
- /**
710
- * Mount an "Expand all" button in the field header — clicking it opens
711
- * every collapsed row. Opt-in: calling without args shows the button
712
- * with the default icon (chevron-down) + label ("Expand all"); pass a
713
- * `RowButton` to override icon / label / tooltip / color.
714
- *
715
- * Auto-arms `collapsible()` since the affordance is meaningless without
716
- * collapsible rows. In `accordion()` mode the button opens the first
717
- * visible row (accordion's "only one open" invariant survives).
718
- */
719
- expandAllAction(button?: RowButton): this {
720
- this._buttons.expandAll = button ?? RowButton.make()
721
- this._collapsible = true
722
- return this
723
- }
724
-
725
- /**
726
- * Mount a "Collapse all" button in the field header — clicking it
727
- * collapses every open row. Opt-in (calling enables; pass a
728
- * `RowButton` to customize). Auto-arms `collapsible()`. In `accordion()`
729
- * mode the button closes the currently-open row, leaving everything
730
- * collapsed.
731
- */
732
- collapseAllAction(button?: RowButton): this {
733
- this._buttons.collapseAll = button ?? RowButton.make()
734
- this._collapsible = true
735
- return this
736
- }
737
-
738
- /**
739
- * Per-row action buttons rendered in each row's header alongside the
740
- * built-in clone/delete strip. Useful for "Mark featured", "Send test",
741
- * "Run preview", etc. — handler-style only in v1 (no `.href(…)` or
742
- * `.method(…)`-style row actions; no modal-form actions either).
743
- *
744
- * Each action's handler receives a row-scoped `ActionContext` with
745
- * `ctx.row = { index, id, values }` (the row's submitted values, not
746
- * the parent record). Visibility rules (`visible / hidden / disabled`)
747
- * are evaluated per row at meta-build with `{ values, record, user }`
748
- * — `values` is the row's data, `record` mirrors the parent form's
749
- * record (same as inner-field condition callbacks).
750
- *
751
- * Actions register one per row; the dispatcher uses `_rowPath` from the
752
- * submit body to know which row was triggered. Naming collisions
753
- * between row actions and page-level actions are NOT allowed (the
754
- * server's `findActions` walker treats row-scoped actions separately
755
- * via `findRowExtraActions`, but listing the same name in both spots
756
- * is undefined behavior).
757
- */
758
- extraItemActions(actions: Action[]): this {
759
- this._extraItemActions = actions
760
- return this
761
- }
762
-
763
- // ─── Read-only access for resolver / coercion / validation ──
764
-
765
- override getChildren(): Element[] | undefined {
766
- return this._children.length > 0 ? this._children : undefined
767
- }
768
-
769
- /** Direct access to the inner schema — used by coercion + validation. */
770
- getInnerSchema(): Element[] { return this._children }
771
-
772
- getColumns(): number | undefined { return this._columns }
773
- getDefaultItems(): number { return this._defaultItems }
774
- getMinItems(): number | undefined { return this._minItems }
775
- getMaxItems(): number | undefined { return this._maxItems }
776
- isReorderable(): boolean { return this._reorderable }
777
- isCollapsible(): boolean { return this._collapsible }
778
- isDefaultCollapsed(): boolean { return this._defaultCollapsed }
779
- isAccordion(): boolean { return this._accordion }
780
- isCloneable(): boolean { return this._cloneable }
781
- getItemLabel(): RepeaterItemLabel | undefined { return this._itemLabel }
782
- getItemHidden(): RepeaterItemHiddenRule | undefined { return this._itemHidden }
783
- getItemCanDelete(): RepeaterItemCanRule | undefined { return this._itemCanDelete }
784
- getItemCanClone(): RepeaterItemCanRule | undefined { return this._itemCanClone }
785
- getItemCanReorder(): RepeaterItemCanRule | undefined { return this._itemCanReorder }
786
- getAddActionLabel(): string | undefined { return this._addActionLabel }
787
- getExtraItemActions(): Action[] { return this._extraItemActions }
788
- isSimple(): boolean { return this._simple }
789
- getGrid(): RepeaterGridConfig | undefined { return this._grid }
790
- getTableColumns(): RepeaterTableColumn[] | undefined { return this._tableColumns }
791
- isTable(): boolean { return this._tableColumns !== undefined }
792
- /** Resolved relationship config (`undefined` when not configured). */
793
- getRelationship(): RepeaterRelationshipConfig | undefined { return this._relationship }
794
- isRelationship(): boolean { return this._relationship !== undefined }
795
-
796
- getAfterCreate(): RepeaterRowAfterHandler | undefined { return this._afterCreate }
797
- getAfterUpdate(): RepeaterRowAfterHandler | undefined { return this._afterUpdate }
798
- getAfterDelete(): RepeaterRowAfterHandler | undefined { return this._afterDelete }
799
- /** The configured customizer for a given slot, or `undefined`. */
800
- getButton(kind: RowButtonKind): RowButton | undefined { return this._buttons[kind] }
801
- /**
802
- * The single inner field of a `simple()` repeater. Returns `undefined`
803
- * outside simple mode (or when the inner schema hasn't been set yet).
804
- * Used by the wrap/unwrap helpers in `dispatchForm` and `resolveSchema`
805
- * — internal contract is "the simple inner field's name is the wrapping
806
- * key for `[v]` ↔ `[{name: v}]` transforms".
807
- */
808
- getSimpleInnerField(): Field | undefined {
809
- if (!this._simple) return undefined
810
- const first = this._children[0]
811
- if (first instanceof Field) return first
812
- return undefined
813
- }
814
-
815
- // ─── Meta ──────────────────────────────────────────────
816
-
817
- /**
818
- * Build the bare Repeater meta — base FieldMeta + per-Repeater config
819
- * keys. Rows + template are populated by the resolver in step 2 (an
820
- * `ElementResolver` registered for `'field'` is the wrong shape since
821
- * Repeater needs ctx.values per-row; the resolver special-cases
822
- * Repeater inline). Until then this returns an empty rows/template
823
- * pair so the type stays stable.
824
- */
825
- override toMeta(ctx?: RenderContext): RepeaterFieldMeta {
826
- const base = this.buildMeta(ctx)
827
- const meta: RepeaterFieldMeta = {
828
- ...base,
829
- fieldType: 'repeater',
830
- rows: [],
831
- template: [],
832
- defaultItems: this._defaultItems,
833
- }
834
- if (this._columns !== undefined) meta.columns = this._columns
835
- if (this._minItems !== undefined) meta.minItems = this._minItems
836
- if (this._maxItems !== undefined) meta.maxItems = this._maxItems
837
- if (this._reorderable) meta.reorderable = true
838
- if (this._collapsible) meta.collapsible = true
839
- if (this._defaultCollapsed) meta.defaultCollapsed = true
840
- if (this._accordion) meta.accordion = true
841
- if (this._cloneable) meta.cloneable = true
842
- if (this._addActionLabel !== undefined) meta.addActionLabel = this._addActionLabel
843
- if (this._simple) meta.simple = true
844
- if (this._grid !== undefined) meta.grid = this._grid
845
- if (this._tableColumns !== undefined) meta.table = { columns: this._tableColumns }
846
- if (this._relationship !== undefined) {
847
- const r: RepeaterRelationshipMeta = { name: this._relationship.name }
848
- if (this._relationship.orderColumn !== undefined) r.orderColumn = this._relationship.orderColumn
849
- meta.relationship = r
850
- }
851
- const buttons = serializeRowButtons(this._buttons)
852
- if (buttons !== undefined) meta.buttons = buttons
853
- return meta
854
- }
855
- }
856
-
857
- export const Repeater = RepeaterField
858
-
859
- /**
860
- * Structural Repeater check — Vite's SSR module cache can load this
861
- * package via two paths during a single dev session, so `instanceof
862
- * RepeaterField` can silently return false across module copies. The
863
- * structural check uses the serialized type discriminator + the
864
- * `fieldType` property name, both of which are stable strings.
865
- *
866
- * Mirrors the pattern documented in
867
- * `feedback_vite_ssr_module_dup_instanceof.md`.
868
- */
869
- export function isRepeaterField(el: { getType(): string; fieldType?: string }): boolean {
870
- return el.getType() === 'field' && el['fieldType'] === 'repeater'
871
- }
872
-
873
- /**
874
- * Walk a sparse `{ [kind]: RowButton }` map and serialize only the slots
875
- * the user actually set. Returns `undefined` when nothing is configured so
876
- * `meta.buttons` stays absent for non-customized fields. Each slot's inner
877
- * meta is the result of `RowButton.toMeta()` — already JSON-safe and
878
- * already drops unset keys.
879
- *
880
- * Shared between Repeater + Builder so the wire shape stays identical
881
- * across both renderers.
882
- */
883
- export function serializeRowButtons(
884
- buttons: { [K in RowButtonKind]?: RowButton },
885
- ): RowButtonsMeta | undefined {
886
- const out: RowButtonsMeta = {}
887
- let any = false
888
- for (const k of Object.keys(buttons) as RowButtonKind[]) {
889
- const b = buttons[k]
890
- if (b === undefined) continue
891
- out[k] = b.toMeta()
892
- any = true
893
- }
894
- return any ? out : undefined
895
- }
896
-
897
- const RESPONSIVE_BREAKPOINTS = ['default', 'sm', 'md', 'lg', 'xl', '2xl'] as const
898
-
899
- /**
900
- * Coerce a user-supplied grid config into the shape stored on `_grid`.
901
- *
902
- * - `number` ≥ 2 → returned unchanged.
903
- * - `number` < 2 → `undefined` (resets the grid mode — mirrors the existing
904
- * "passing 1 turns it off" sentinel).
905
- * - `ResponsiveGridConfig` → object with each declared breakpoint floored
906
- * to an integer ≥ 2; entries below 2 are dropped. When no breakpoint
907
- * survives the filter, returns `undefined` (no grid mode at any size).
908
- * Single-entry results collapse to the bare number when the only
909
- * surviving entry is `default` — that's exactly the scalar form.
910
- *
911
- * Shared with `Builder.grid()` so the two fields validate identically.
912
- */
913
- export function normalizeGridConfig(
914
- config: RepeaterGridConfig,
915
- ): RepeaterGridConfig | undefined {
916
- if (typeof config === 'number') {
917
- return config >= 2 ? Math.floor(config) : undefined
918
- }
919
- const out: ResponsiveGridConfig = {}
920
- let breakpointCount = 0 // declared breakpoints other than `default`
921
- for (const k of RESPONSIVE_BREAKPOINTS) {
922
- const raw = config[k]
923
- if (typeof raw !== 'number') continue
924
- const n = Math.floor(raw)
925
- // `default` allows n=1 (explicit "vertical stack below the smallest
926
- // declared breakpoint"). Other breakpoints require n >= 2 — anything
927
- // smaller would just fall through to the previous rule.
928
- if (k === 'default' ? n < 1 : n < 2) continue
929
- out[k] = n
930
- if (k !== 'default') breakpointCount++
931
- }
932
- // No actual breakpoint overrides → no responsive behavior. Either
933
- // collapse to the scalar form (when default exists) or reset.
934
- if (breakpointCount === 0) {
935
- if (typeof out.default === 'number' && out.default >= 2) return out.default
936
- return undefined
937
- }
938
- return out
939
- }