@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.
- package/CHANGELOG.md +91 -0
- package/boost/guidelines.md +566 -0
- package/boost/skills/pilotiq-fields/SKILL.md +47 -0
- package/boost/skills/pilotiq-fields/rules/field-catalog.md +288 -0
- package/boost/skills/pilotiq-fields/rules/reactive-fields.md +199 -0
- package/boost/skills/pilotiq-fields/rules/validation.md +198 -0
- package/boost/skills/pilotiq-relations/SKILL.md +47 -0
- package/boost/skills/pilotiq-relations/rules/relation-managers.md +256 -0
- package/boost/skills/pilotiq-relations/rules/repeater-relationship.md +177 -0
- package/boost/skills/pilotiq-resource/SKILL.md +61 -0
- package/boost/skills/pilotiq-resource/rules/authorization.md +242 -0
- package/boost/skills/pilotiq-resource/rules/defining-resources.md +228 -0
- package/boost/skills/pilotiq-resource/rules/page-overrides.md +296 -0
- package/dist/actions/exportFactory.d.ts +10 -0
- package/dist/actions/exportFactory.d.ts.map +1 -1
- package/dist/actions/exportFactory.js +10 -0
- package/dist/actions/exportFactory.js.map +1 -1
- package/dist/react/CollabRoomContext.d.ts +5 -5
- package/dist/react/index.d.ts +0 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +0 -1
- package/dist/react/index.js.map +1 -1
- package/dist/routes/helpers.d.ts.map +1 -1
- package/dist/routes/helpers.js +6 -2
- package/dist/routes/helpers.js.map +1 -1
- package/dist/routes/relations.d.ts.map +1 -1
- package/dist/routes/relations.js +12 -0
- package/dist/routes/relations.js.map +1 -1
- package/package.json +6 -1
- package/.turbo/turbo-build.log +0 -8
- package/CLAUDE.md +0 -265
- package/dist/react/useCollabSeed.d.ts +0 -23
- package/dist/react/useCollabSeed.d.ts.map +0 -1
- package/dist/react/useCollabSeed.js +0 -82
- package/dist/react/useCollabSeed.js.map +0 -1
- package/src/Cluster.test.ts +0 -283
- package/src/Cluster.ts +0 -83
- package/src/Column.test.ts +0 -199
- package/src/Column.ts +0 -710
- package/src/Global.test.ts +0 -367
- package/src/Global.ts +0 -169
- package/src/Page.test.ts +0 -114
- package/src/Page.ts +0 -208
- package/src/Pilotiq.perf.test.ts +0 -252
- package/src/Pilotiq.test.ts +0 -129
- package/src/Pilotiq.ts +0 -1158
- package/src/PilotiqRegistry.ts +0 -36
- package/src/PilotiqServiceProvider.ts +0 -121
- package/src/RelationManager.test.ts +0 -400
- package/src/RelationManager.ts +0 -527
- package/src/RenderHook.test.ts +0 -252
- package/src/RenderHook.ts +0 -242
- package/src/Resource.test.ts +0 -284
- package/src/Resource.ts +0 -526
- package/src/RightPanel.test.ts +0 -202
- package/src/RightPanel.ts +0 -132
- package/src/Tab.test.ts +0 -91
- package/src/Tab.ts +0 -156
- package/src/UserMenuItem.ts +0 -145
- package/src/actions/Action.test.ts +0 -2526
- package/src/actions/Action.ts +0 -1515
- package/src/actions/ActionGroup.test.ts +0 -112
- package/src/actions/ActionGroup.ts +0 -173
- package/src/actions/attachFactory.ts +0 -172
- package/src/actions/bulkFactories.ts +0 -168
- package/src/actions/crudFactories.ts +0 -220
- package/src/actions/exportFactory.ts +0 -215
- package/src/actions/factoryHelpers.ts +0 -177
- package/src/actions/importFactory.ts +0 -243
- package/src/actions/index.ts +0 -17
- package/src/actions/m2mFactories.ts +0 -193
- package/src/actions/relationFactories.ts +0 -372
- package/src/applyPageHooks.test.ts +0 -463
- package/src/applyPageHooks.ts +0 -330
- package/src/authorization.test.ts +0 -483
- package/src/breadcrumbs.test.ts +0 -238
- package/src/cells/coerce.test.ts +0 -85
- package/src/cells/coerce.ts +0 -84
- package/src/clusterPaths.ts +0 -35
- package/src/columns/BadgeColumn.test.ts +0 -54
- package/src/columns/BadgeColumn.ts +0 -32
- package/src/columns/BooleanColumn.test.ts +0 -41
- package/src/columns/BooleanColumn.ts +0 -18
- package/src/columns/ColorColumn.test.ts +0 -37
- package/src/columns/ColorColumn.ts +0 -38
- package/src/columns/IconColumn.test.ts +0 -54
- package/src/columns/IconColumn.ts +0 -37
- package/src/columns/ImageColumn.test.ts +0 -41
- package/src/columns/ImageColumn.ts +0 -28
- package/src/columns/SelectColumn.ts +0 -98
- package/src/columns/TextColumn.test.ts +0 -190
- package/src/columns/TextColumn.ts +0 -20
- package/src/columns/TextInputColumn.ts +0 -68
- package/src/columns/ToggleColumn.ts +0 -46
- package/src/columns/editableColumns.test.ts +0 -238
- package/src/columns/index.ts +0 -9
- package/src/defaultGlobalPages.ts +0 -95
- package/src/defaultPages.test.ts +0 -634
- package/src/defaultPages.ts +0 -617
- package/src/defaultViewPage.test.ts +0 -147
- package/src/elements/Form.test.ts +0 -223
- package/src/elements/Form.ts +0 -416
- package/src/elements/ListTabs.ts +0 -28
- package/src/elements/Table.test.ts +0 -422
- package/src/elements/Table.ts +0 -850
- package/src/elements/TableGroup.test.ts +0 -260
- package/src/elements/TableGroup.ts +0 -334
- package/src/elements/dispatchAction.test.ts +0 -463
- package/src/elements/dispatchAction.ts +0 -355
- package/src/elements/dispatchForm.test.ts +0 -477
- package/src/elements/dispatchForm.ts +0 -1993
- package/src/elements/dispatchTable.test.ts +0 -1514
- package/src/elements/dispatchTable.ts +0 -745
- package/src/elements/index.ts +0 -21
- package/src/entries/BadgeEntry.ts +0 -39
- package/src/entries/CodeEntry.test.ts +0 -40
- package/src/entries/CodeEntry.ts +0 -52
- package/src/entries/ColorEntry.ts +0 -63
- package/src/entries/ComponentEntry.test.ts +0 -173
- package/src/entries/ComponentEntry.ts +0 -95
- package/src/entries/Entry.ts +0 -304
- package/src/entries/IconEntry.ts +0 -49
- package/src/entries/ImageEntry.ts +0 -61
- package/src/entries/KeyValueEntry.ts +0 -47
- package/src/entries/RepeatableEntry.test.ts +0 -239
- package/src/entries/RepeatableEntry.ts +0 -173
- package/src/entries/TextEntry.test.ts +0 -394
- package/src/entries/TextEntry.ts +0 -60
- package/src/entries/index.ts +0 -12
- package/src/entries/leaves.test.ts +0 -306
- package/src/entries/registry.ts +0 -54
- package/src/fields/BuilderField.test.ts +0 -1188
- package/src/fields/BuilderField.ts +0 -605
- package/src/fields/BuilderRelationship.test.ts +0 -811
- package/src/fields/CheckboxField.test.ts +0 -44
- package/src/fields/CheckboxField.ts +0 -27
- package/src/fields/CheckboxListField.test.ts +0 -99
- package/src/fields/CheckboxListField.ts +0 -66
- package/src/fields/ColorPickerField.test.ts +0 -33
- package/src/fields/ColorPickerField.ts +0 -25
- package/src/fields/DateField.ts +0 -54
- package/src/fields/DateTimeField.test.ts +0 -55
- package/src/fields/EmailField.ts +0 -16
- package/src/fields/Field.test.ts +0 -654
- package/src/fields/Field.ts +0 -817
- package/src/fields/FileUploadField.test.ts +0 -143
- package/src/fields/FileUploadField.ts +0 -159
- package/src/fields/HiddenField.test.ts +0 -27
- package/src/fields/HiddenField.ts +0 -28
- package/src/fields/KeyValueField.test.ts +0 -105
- package/src/fields/KeyValueField.ts +0 -55
- package/src/fields/MarkdownField.test.ts +0 -167
- package/src/fields/MarkdownField.ts +0 -162
- package/src/fields/NumberField.ts +0 -33
- package/src/fields/RadioField.test.ts +0 -94
- package/src/fields/RadioField.ts +0 -67
- package/src/fields/RepeaterField.test.ts +0 -1806
- package/src/fields/RepeaterField.ts +0 -939
- package/src/fields/RepeaterRelationship.test.ts +0 -1923
- package/src/fields/RepeaterSimple.test.ts +0 -248
- package/src/fields/RowButton.test.ts +0 -219
- package/src/fields/RowButton.ts +0 -135
- package/src/fields/SelectField.test.ts +0 -192
- package/src/fields/SelectField.ts +0 -235
- package/src/fields/SliderField.test.ts +0 -50
- package/src/fields/SliderField.ts +0 -53
- package/src/fields/SlugField.ts +0 -24
- package/src/fields/TagsInputField.test.ts +0 -154
- package/src/fields/TagsInputField.ts +0 -133
- package/src/fields/TextField.test.ts +0 -213
- package/src/fields/TextField.ts +0 -177
- package/src/fields/TextareaField.test.ts +0 -58
- package/src/fields/TextareaField.ts +0 -59
- package/src/fields/ToggleButtonsField.test.ts +0 -106
- package/src/fields/ToggleButtonsField.ts +0 -59
- package/src/fields/ToggleField.ts +0 -16
- package/src/fields/disableOptionsWhenSelectedInSiblingRepeaterItems.test.ts +0 -319
- package/src/fields/optionsResolver.ts +0 -95
- package/src/fields/resolveField.ts +0 -28
- package/src/filters/BooleanFilter.ts +0 -35
- package/src/filters/DateRangeFilter.test.ts +0 -194
- package/src/filters/DateRangeFilter.ts +0 -148
- package/src/filters/Filter.test.ts +0 -268
- package/src/filters/Filter.ts +0 -184
- package/src/filters/FormFilter.test.ts +0 -238
- package/src/filters/FormFilter.ts +0 -215
- package/src/filters/MultiSelectFilter.test.ts +0 -119
- package/src/filters/MultiSelectFilter.ts +0 -78
- package/src/filters/QueryBuilderFilter.test.ts +0 -662
- package/src/filters/QueryBuilderFilter.ts +0 -398
- package/src/filters/SelectFilter.ts +0 -46
- package/src/filters/TernaryFilter.test.ts +0 -160
- package/src/filters/TernaryFilter.ts +0 -72
- package/src/filters/TrashedFilter.test.ts +0 -149
- package/src/filters/TrashedFilter.ts +0 -55
- package/src/filters/queryBuilder/BooleanConstraint.ts +0 -31
- package/src/filters/queryBuilder/Constraint.ts +0 -115
- package/src/filters/queryBuilder/DateConstraint.ts +0 -69
- package/src/filters/queryBuilder/NumberConstraint.ts +0 -66
- package/src/filters/queryBuilder/SelectConstraint.ts +0 -72
- package/src/filters/queryBuilder/TextConstraint.ts +0 -64
- package/src/filters/queryBuilder/index.ts +0 -12
- package/src/icons/index.ts +0 -2
- package/src/icons/lucide.ts +0 -204
- package/src/icons/registry.test.ts +0 -56
- package/src/icons/registry.ts +0 -41
- package/src/icons/types.ts +0 -47
- package/src/index.ts +0 -525
- package/src/io/csv.test.ts +0 -142
- package/src/io/csv.ts +0 -170
- package/src/nestedRelationManagerData.test.ts +0 -547
- package/src/notifications/Notification.test.ts +0 -210
- package/src/notifications/Notification.ts +0 -354
- package/src/notifications/broadcast.test.ts +0 -110
- package/src/notifications/broadcast.ts +0 -95
- package/src/notifications/database.test.ts +0 -383
- package/src/notifications/database.ts +0 -398
- package/src/notifications/databaseNotifications.test.ts +0 -187
- package/src/notifications/dispatchNotificationAction.test.ts +0 -341
- package/src/notifications/dispatchNotificationAction.ts +0 -142
- package/src/notifications/flash.test.ts +0 -89
- package/src/notifications/flash.ts +0 -71
- package/src/notifications/index.ts +0 -45
- package/src/notifications/registerBroadcastAuth.test.ts +0 -134
- package/src/notifications/registerBroadcastAuth.ts +0 -100
- package/src/notifications/resolveSavedNotification.test.ts +0 -82
- package/src/notifications/resolveSavedNotification.ts +0 -59
- package/src/notifications/types.ts +0 -93
- package/src/orm/m2mAccessor.ts +0 -66
- package/src/orm/modelDefaults.test.ts +0 -633
- package/src/orm/modelDefaults.ts +0 -666
- package/src/pageData/breadcrumbs.ts +0 -288
- package/src/pageData/forms.ts +0 -578
- package/src/pageData/helpers.ts +0 -857
- package/src/pageData/misc.ts +0 -347
- package/src/pageData/navigation.ts +0 -842
- package/src/pageData/relationPages.ts +0 -1248
- package/src/pageData/relationTabs.ts +0 -286
- package/src/pageData/resourcePages.ts +0 -609
- package/src/pageData.test.ts +0 -1545
- package/src/pageData.ts +0 -341
- package/src/plugins/index.ts +0 -8
- package/src/plugins/themeEditor.test.ts +0 -36
- package/src/plugins/themeEditor.ts +0 -45
- package/src/react/AppShell.tsx +0 -251
- package/src/react/CollabExtensionFactoryRegistry.ts +0 -55
- package/src/react/CollabRoomContext.ts +0 -98
- package/src/react/CollabTextRendererRegistry.ts +0 -102
- package/src/react/CommandPalette.tsx +0 -375
- package/src/react/CurrentUserContext.tsx +0 -50
- package/src/react/CustomPageWrapperGate.tsx +0 -69
- package/src/react/CustomPageWrapperRegistry.ts +0 -45
- package/src/react/FieldFocusReporterRegistry.ts +0 -37
- package/src/react/FieldLabelSlotRegistry.ts +0 -30
- package/src/react/FieldPresenceRegistry.ts +0 -46
- package/src/react/FormCollabBindingRegistry.ts +0 -242
- package/src/react/FormStateContext.tsx +0 -591
- package/src/react/HeadHooks.tsx +0 -126
- package/src/react/MarkdownEditorRegistry.test.ts +0 -38
- package/src/react/MarkdownEditorRegistry.ts +0 -107
- package/src/react/NotificationActionStrip.tsx +0 -263
- package/src/react/NotificationBell.tsx +0 -426
- package/src/react/PendingSuggestionApplierRegistry.test.ts +0 -97
- package/src/react/PendingSuggestionApplierRegistry.ts +0 -98
- package/src/react/PendingSuggestionOverlayRegistry.ts +0 -54
- package/src/react/PendingSuggestionsContext.tsx +0 -172
- package/src/react/RecordWrapperGate.tsx +0 -58
- package/src/react/RecordWrapperRegistry.ts +0 -39
- package/src/react/RenderHookSlot.tsx +0 -32
- package/src/react/RightSidebar.tsx +0 -257
- package/src/react/RightSidebarContext.tsx +0 -234
- package/src/react/RightSidebarTrigger.tsx +0 -53
- package/src/react/RowCoordsContext.tsx +0 -23
- package/src/react/SchemaRenderer.tsx +0 -549
- package/src/react/SearchTrigger.tsx +0 -46
- package/src/react/ThemeProvider.tsx +0 -93
- package/src/react/ThemeSettingsPage.tsx +0 -579
- package/src/react/ThemeToggle.tsx +0 -20
- package/src/react/Toaster.tsx +0 -158
- package/src/react/UserMenu.tsx +0 -196
- package/src/react/WidgetDataContext.tsx +0 -157
- package/src/react/cells/EditableCell.tsx +0 -389
- package/src/react/component-slots.test.ts +0 -103
- package/src/react/component-slots.ts +0 -116
- package/src/react/fieldJsHandler.test.ts +0 -166
- package/src/react/fieldJsHandler.ts +0 -79
- package/src/react/fields/BuilderInput.tsx +0 -1078
- package/src/react/fields/CheckboxInput.tsx +0 -39
- package/src/react/fields/CheckboxListInput.tsx +0 -102
- package/src/react/fields/ColorInput.tsx +0 -71
- package/src/react/fields/DateFieldInput.tsx +0 -70
- package/src/react/fields/DateTimeInput.tsx +0 -62
- package/src/react/fields/FieldShell.tsx +0 -348
- package/src/react/fields/FileUploadInput.tsx +0 -639
- package/src/react/fields/HiddenInput.tsx +0 -17
- package/src/react/fields/KeyValueInput.tsx +0 -230
- package/src/react/fields/MarkdownInput.tsx +0 -560
- package/src/react/fields/RadioInput.tsx +0 -81
- package/src/react/fields/RepeaterInput.test.ts +0 -116
- package/src/react/fields/RepeaterInput.tsx +0 -1420
- package/src/react/fields/SelectFieldInput.tsx +0 -280
- package/src/react/fields/SliderInput.tsx +0 -81
- package/src/react/fields/TagsInput.tsx +0 -283
- package/src/react/fields/TextLikeInput.tsx +0 -256
- package/src/react/fields/ToggleButtonsInput.tsx +0 -60
- package/src/react/fields/ToggleFieldInput.tsx +0 -56
- package/src/react/fields/relationshipRenameDispatch.test.ts +0 -106
- package/src/react/fields/relationshipRenameDispatch.ts +0 -97
- package/src/react/fields/repeaterReconcile.test.ts +0 -114
- package/src/react/fields/repeaterReconcile.ts +0 -104
- package/src/react/fields/rowChromeButton.tsx +0 -336
- package/src/react/fields/rowState.ts +0 -106
- package/src/react/fields/syncRowGates.test.ts +0 -202
- package/src/react/fields/syncRowGates.ts +0 -66
- package/src/react/fields/textInputControls.tsx +0 -238
- package/src/react/fields/useRowReorderDnd.ts +0 -78
- package/src/react/formStateHelpers.test.ts +0 -508
- package/src/react/formStateHelpers.ts +0 -381
- package/src/react/hooks/use-mobile.ts +0 -19
- package/src/react/icon-context.tsx +0 -60
- package/src/react/index.ts +0 -195
- package/src/react/layouts/SidebarLayout.tsx +0 -250
- package/src/react/layouts/TopbarLayout.tsx +0 -258
- package/src/react/navigate.tsx +0 -37
- package/src/react/onProviderSynced.test.ts +0 -90
- package/src/react/parseRecordEditUrl.test.ts +0 -122
- package/src/react/parseRecordEditUrl.ts +0 -94
- package/src/react/persistedState.ts +0 -40
- package/src/react/registry.ts +0 -48
- package/src/react/right-panel-registry.tsx +0 -47
- package/src/react/schemaRenderer/AlertRenderer.tsx +0 -112
- package/src/react/schemaRenderer/EntryRenderer.tsx +0 -501
- package/src/react/schemaRenderer/SectionRenderer.tsx +0 -120
- package/src/react/schemaRenderer/SimpleElements.tsx +0 -306
- package/src/react/schemaRenderer/TabsRenderer.tsx +0 -62
- package/src/react/schemaRenderer/WizardRenderer.tsx +0 -338
- package/src/react/schemaRenderer/action/ActionGroupTrigger.tsx +0 -177
- package/src/react/schemaRenderer/action/ActionModalDialog.tsx +0 -273
- package/src/react/schemaRenderer/action/ConfirmActionDialog.tsx +0 -61
- package/src/react/schemaRenderer/action/HandlerActionButton.tsx +0 -43
- package/src/react/schemaRenderer/action/MethodActionButton.tsx +0 -64
- package/src/react/schemaRenderer/action/buttons.tsx +0 -99
- package/src/react/schemaRenderer/action/helpers.ts +0 -140
- package/src/react/schemaRenderer/action/renderAction.tsx +0 -245
- package/src/react/schemaRenderer/columnFormat.ts +0 -65
- package/src/react/schemaRenderer/constants.ts +0 -50
- package/src/react/schemaRenderer/form/FormRenderer.tsx +0 -274
- package/src/react/schemaRenderer/form/renderField.tsx +0 -511
- package/src/react/schemaRenderer/helpers.tsx +0 -81
- package/src/react/schemaRenderer/table/CardsLayoutBody.tsx +0 -308
- package/src/react/schemaRenderer/table/TableRenderer.tsx +0 -123
- package/src/react/schemaRenderer/table/TableRendererBody.tsx +0 -974
- package/src/react/schemaRenderer/table/filters.tsx +0 -1233
- package/src/react/schemaRenderer/table/formatCell.tsx +0 -264
- package/src/react/schemaRenderer/table/links.tsx +0 -112
- package/src/react/schemaRenderer/table/renderRowActions.tsx +0 -52
- package/src/react/schemaRenderer/table/url.tsx +0 -143
- package/src/react/theme-preview/apply.ts +0 -99
- package/src/react/theme-preview/build-html.ts +0 -436
- package/src/react/ui/button.tsx +0 -51
- package/src/react/ui/calendar.tsx +0 -67
- package/src/react/ui/checkbox.tsx +0 -29
- package/src/react/ui/dialog.tsx +0 -108
- package/src/react/ui/dropdown-menu.tsx +0 -97
- package/src/react/ui/input.tsx +0 -20
- package/src/react/ui/label.tsx +0 -21
- package/src/react/ui/popover.tsx +0 -50
- package/src/react/ui/select.tsx +0 -169
- package/src/react/ui/separator.tsx +0 -25
- package/src/react/ui/sheet.tsx +0 -136
- package/src/react/ui/sidebar.tsx +0 -723
- package/src/react/ui/skeleton.tsx +0 -13
- package/src/react/ui/slider.tsx +0 -34
- package/src/react/ui/switch.tsx +0 -28
- package/src/react/ui/table.tsx +0 -105
- package/src/react/ui/tabs.tsx +0 -63
- package/src/react/ui/textarea.tsx +0 -18
- package/src/react/ui/tooltip.tsx +0 -64
- package/src/react/useCollabSeed.ts +0 -86
- package/src/react/useResizableWidth.ts +0 -139
- package/src/react/utils.ts +0 -6
- package/src/react/widgetRegistry.test.ts +0 -43
- package/src/react/widgetRegistry.ts +0 -50
- package/src/react/widgets/StatsOverviewRenderer.tsx +0 -232
- package/src/react/widgets/TableWidgetRenderer.tsx +0 -231
- package/src/react/widgets/ViewRenderer.tsx +0 -71
- package/src/relationManagerData.test.ts +0 -1595
- package/src/richtext/index.ts +0 -8
- package/src/richtext/registry.ts +0 -89
- package/src/routes/globals.ts +0 -148
- package/src/routes/guard.test.ts +0 -325
- package/src/routes/helpers.ts +0 -700
- package/src/routes/pages.ts +0 -175
- package/src/routes/panel.ts +0 -204
- package/src/routes/relations.ts +0 -1227
- package/src/routes/resources.ts +0 -781
- package/src/routes/theme.ts +0 -91
- package/src/routes-nested-relations.test.ts +0 -676
- package/src/routes-relations.test.ts +0 -972
- package/src/routes.test.ts +0 -2027
- package/src/routes.ts +0 -303
- package/src/schema/Alert.test.ts +0 -109
- package/src/schema/Alert.ts +0 -131
- package/src/schema/Block.ts +0 -169
- package/src/schema/Breadcrumbs.ts +0 -40
- package/src/schema/Card.ts +0 -35
- package/src/schema/Divider.ts +0 -20
- package/src/schema/Element.ts +0 -219
- package/src/schema/EmptyState.test.ts +0 -37
- package/src/schema/EmptyState.ts +0 -63
- package/src/schema/Fieldset.ts +0 -43
- package/src/schema/Grid.ts +0 -43
- package/src/schema/Group.ts +0 -30
- package/src/schema/Heading.ts +0 -39
- package/src/schema/Html.ts +0 -67
- package/src/schema/Icon.ts +0 -54
- package/src/schema/Image.ts +0 -57
- package/src/schema/LinkTag.ts +0 -41
- package/src/schema/Markdown.ts +0 -85
- package/src/schema/MetaTag.ts +0 -41
- package/src/schema/RelationTabs.ts +0 -71
- package/src/schema/ScriptTag.ts +0 -55
- package/src/schema/Section.ts +0 -160
- package/src/schema/ServerDataElement.test.ts +0 -140
- package/src/schema/ServerDataElement.ts +0 -156
- package/src/schema/SlotComponent.test.ts +0 -77
- package/src/schema/SlotComponent.ts +0 -71
- package/src/schema/Split.ts +0 -50
- package/src/schema/Stat.test.ts +0 -118
- package/src/schema/Stat.ts +0 -154
- package/src/schema/StatsOverview.test.ts +0 -141
- package/src/schema/StatsOverview.ts +0 -119
- package/src/schema/StyleTag.ts +0 -35
- package/src/schema/TableWidget.test.ts +0 -297
- package/src/schema/TableWidget.ts +0 -289
- package/src/schema/Tabs.ts +0 -79
- package/src/schema/Text.ts +0 -58
- package/src/schema/UnorderedList.ts +0 -49
- package/src/schema/View.test.ts +0 -111
- package/src/schema/View.ts +0 -127
- package/src/schema/Wizard.ts +0 -220
- package/src/schema/containers.test.ts +0 -564
- package/src/schema/headTags.test.ts +0 -134
- package/src/schema/index.ts +0 -40
- package/src/schema/primes.test.ts +0 -269
- package/src/schema/resolveSchema.test.ts +0 -379
- package/src/schema/resolveSchema.ts +0 -917
- package/src/schema/sanitize.ts +0 -58
- package/src/search.test.ts +0 -446
- package/src/search.ts +0 -178
- package/src/sessionFilters.test.ts +0 -375
- package/src/sessionFilters.ts +0 -143
- package/src/slot-components/index.ts +0 -10
- package/src/slot-components/registry.ts +0 -56
- package/src/styles/file-upload.css +0 -13
- package/src/summarizers/Summarizer.test.ts +0 -84
- package/src/summarizers/Summarizer.ts +0 -123
- package/src/summarizers/index.ts +0 -11
- package/src/theme/base-colors.ts +0 -68
- package/src/theme/chart-colors.ts +0 -50
- package/src/theme/colors.ts +0 -447
- package/src/theme/generate-css.test.ts +0 -139
- package/src/theme/generate-css.ts +0 -44
- package/src/theme/generate-scale.test.ts +0 -106
- package/src/theme/generate-scale.ts +0 -97
- package/src/theme/icon-map.ts +0 -42
- package/src/theme/index.ts +0 -34
- package/src/theme/migrate.test.ts +0 -178
- package/src/theme/migrate.ts +0 -81
- package/src/theme/presets.ts +0 -135
- package/src/theme/radius.ts +0 -18
- package/src/theme/resolve.test.ts +0 -238
- package/src/theme/resolve.ts +0 -96
- package/src/theme/spacing.ts +0 -18
- package/src/theme/storage.test.ts +0 -126
- package/src/theme/storage.ts +0 -106
- package/src/theme/theme-colors.ts +0 -88
- package/src/theme/types.ts +0 -125
- package/src/uploads/UploadAdapter.ts +0 -35
- package/src/uploads/index.ts +0 -2
- package/src/uploads/localUpload.test.ts +0 -70
- package/src/uploads/localUpload.ts +0 -84
- package/src/validation/Validator.ts +0 -49
- package/src/validation/index.ts +0 -28
- package/src/validation/rules.ts +0 -78
- package/src/validation/runValidators.ts +0 -435
- package/src/validation/uniqueValidator.test.ts +0 -196
- package/src/validation/uniqueValidator.ts +0 -133
- package/src/validation/validators.test.ts +0 -268
- package/src/vite.test.ts +0 -184
- package/src/vite.ts +0 -787
- package/src/widgets/index.ts +0 -10
- package/src/widgets/registry.ts +0 -45
- package/src/widgets.test.ts +0 -592
- package/tsconfig.build.json +0 -11
- package/tsconfig.json +0 -4
- package/tsconfig.test.json +0 -10
- package/views/react/Dashboard.tsx +0 -27
- package/views/react/Resources/Form.tsx +0 -102
- 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
|
-
}
|