@pilotiq/pilotiq 0.24.1 → 0.24.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +57 -0
- package/boost/guidelines.md +571 -0
- package/boost/skills/pilotiq-actions/SKILL.md +49 -0
- package/boost/skills/pilotiq-actions/rules/dispatch-modes.md +177 -0
- package/boost/skills/pilotiq-actions/rules/factories.md +130 -0
- package/boost/skills/pilotiq-actions/rules/visibility-and-authorization.md +125 -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/Pilotiq.d.ts +31 -0
- package/dist/Pilotiq.d.ts.map +1 -1
- package/dist/Pilotiq.js +3 -1
- package/dist/Pilotiq.js.map +1 -1
- package/dist/PilotiqRegistry.d.ts +13 -0
- package/dist/PilotiqRegistry.d.ts.map +1 -1
- package/dist/PilotiqRegistry.js +15 -0
- package/dist/PilotiqRegistry.js.map +1 -1
- package/dist/pageData/misc.d.ts.map +1 -1
- package/dist/pageData/misc.js +6 -0
- package/dist/pageData/misc.js.map +1 -1
- package/dist/pageData/navigation.d.ts +1 -0
- package/dist/pageData/navigation.d.ts.map +1 -1
- package/dist/pageData/navigation.js +3 -0
- package/dist/pageData/navigation.js.map +1 -1
- package/dist/pageData/relationPages.d.ts.map +1 -1
- package/dist/pageData/relationPages.js +3 -0
- package/dist/pageData/relationPages.js.map +1 -1
- package/dist/pageData/resourcePages.d.ts.map +1 -1
- package/dist/pageData/resourcePages.js +8 -0
- package/dist/pageData/resourcePages.js.map +1 -1
- package/dist/react/AppShell.d.ts +8 -0
- package/dist/react/AppShell.d.ts.map +1 -1
- package/dist/react/AppShell.js.map +1 -1
- package/dist/react/layouts/SidebarLayout.d.ts.map +1 -1
- package/dist/react/layouts/SidebarLayout.js +10 -2
- package/dist/react/layouts/SidebarLayout.js.map +1 -1
- package/dist/react/widgets/StatsOverviewRenderer.d.ts.map +1 -1
- package/dist/react/widgets/StatsOverviewRenderer.js +32 -18
- package/dist/react/widgets/StatsOverviewRenderer.js.map +1 -1
- package/dist/routes/relations.d.ts.map +1 -1
- package/dist/routes/relations.js +25 -18
- package/dist/routes/relations.js.map +1 -1
- package/dist/routes/resources.js.map +1 -1
- package/package.json +10 -5
- package/.turbo/turbo-build.log +0 -8
- package/CLAUDE.md +0 -265
- 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 -225
- 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 -194
- 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/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 -704
- package/src/routes/pages.ts +0 -175
- package/src/routes/panel.ts +0 -204
- package/src/routes/relations.ts +0 -1243
- 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
package/src/fields/Field.ts
DELETED
|
@@ -1,817 +0,0 @@
|
|
|
1
|
-
import { Element, type ElementMeta } from '../schema/Element.js'
|
|
2
|
-
import type { RenderContext } from '../schema/resolveSchema.js'
|
|
3
|
-
import type { SerializedRule, Validator, ValidatorContext } from '../validation/Validator.js'
|
|
4
|
-
|
|
5
|
-
export type FieldType =
|
|
6
|
-
| 'text'
|
|
7
|
-
| 'textarea'
|
|
8
|
-
| 'email'
|
|
9
|
-
| 'number'
|
|
10
|
-
| 'select'
|
|
11
|
-
| 'toggle'
|
|
12
|
-
| 'date'
|
|
13
|
-
| 'slug'
|
|
14
|
-
// Permits external packages (`@pilotiq/tiptap` etc.) to declare their own
|
|
15
|
-
// fieldType strings without having to widen this union. The `& {}` trick
|
|
16
|
-
// keeps autocomplete on the literal members for built-ins.
|
|
17
|
-
| (string & {})
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* JSON-serializable field metadata sent to the client.
|
|
21
|
-
*
|
|
22
|
-
* Extends `ElementMeta` so Fields are first-class members of the resolved
|
|
23
|
-
* schema tree. Top-level `type` is always `'field'`; the `fieldType`
|
|
24
|
-
* sub-discriminator tells the client which input to render. Avoiding
|
|
25
|
-
* `type: 'text'` for TextField keeps it from clashing with the `Text`
|
|
26
|
-
* display element.
|
|
27
|
-
*/
|
|
28
|
-
/**
|
|
29
|
-
* Live-update options. `onBlur:true` defers the trigger until the input
|
|
30
|
-
* loses focus; `debounce:N` waits `N`ms of idle after the last change.
|
|
31
|
-
* They compose — `{ onBlur: true, debounce: 500 }` waits 500ms after blur.
|
|
32
|
-
*
|
|
33
|
-
* Serialized verbatim onto `FieldMeta.live`; the client uses it to decide
|
|
34
|
-
* how to wire each field's `onChange` / `onBlur` handler.
|
|
35
|
-
*/
|
|
36
|
-
export interface LiveOptions {
|
|
37
|
-
onBlur?: boolean
|
|
38
|
-
debounce?: number
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Decoration content for `Field.prefix()` / `Field.suffix()`. Either a
|
|
43
|
-
* literal string (e.g. `"$"`, `".com"`) or an icon descriptor mirroring
|
|
44
|
-
* the icon system (`{ icon: 'name' }` looks up the registry; `{ icon:
|
|
45
|
-
* Component }` ships the class identity for the renderer to resolve at
|
|
46
|
-
* render time).
|
|
47
|
-
*/
|
|
48
|
-
export type FieldDecoration = string | { icon: string }
|
|
49
|
-
|
|
50
|
-
export interface FieldMeta extends ElementMeta {
|
|
51
|
-
type: 'field'
|
|
52
|
-
fieldType: FieldType
|
|
53
|
-
name: string
|
|
54
|
-
label: string
|
|
55
|
-
required: boolean
|
|
56
|
-
disabled: boolean
|
|
57
|
-
placeholder?: string
|
|
58
|
-
rules?: SerializedRule[]
|
|
59
|
-
/** When true, the rendered input gets the HTML `autofocus` attribute. */
|
|
60
|
-
autofocus?: boolean
|
|
61
|
-
/**
|
|
62
|
-
* Render the label as `sr-only` — visually hidden but kept in the DOM
|
|
63
|
-
* for screen readers. Distinct from omitting the label entirely (which
|
|
64
|
-
* loses the accessibility hook).
|
|
65
|
-
*/
|
|
66
|
-
hiddenLabel?: boolean
|
|
67
|
-
/**
|
|
68
|
-
* Pass-through HTML attrs for the field's outer wrapper (the `flex
|
|
69
|
-
* flex-col` container in `FieldShell` — the same node that hosts the
|
|
70
|
-
* label + input + helper).
|
|
71
|
-
*/
|
|
72
|
-
extraAttributes?: Record<string, string | number | boolean>
|
|
73
|
-
/** Pass-through HTML attrs for the underlying `<input>` / `<select>` / etc. */
|
|
74
|
-
extraInputAttributes?: Record<string, string | number | boolean>
|
|
75
|
-
/** Alias for `extraAttributes`. Filament-parity name kept for clarity. */
|
|
76
|
-
extraFieldWrapperAttributes?: Record<string, string | number | boolean>
|
|
77
|
-
/**
|
|
78
|
-
* Plan #5 reactive flag. When set, the client wires up a roundtrip
|
|
79
|
-
* to the form's `stateUrl` on change/blur. Bare `true` means "fire
|
|
80
|
-
* immediately on every change"; the object form carries debounce /
|
|
81
|
-
* onBlur sub-options.
|
|
82
|
-
*/
|
|
83
|
-
live?: true | LiveOptions
|
|
84
|
-
/**
|
|
85
|
-
* Client-side reactivity hook. String body of a function bound with
|
|
86
|
-
* `$state` (the changed field's new value), `$get(name)`, and
|
|
87
|
-
* `$set(name, value)`. Compiled and run on the client on every change
|
|
88
|
-
* — independent of `live()`. Treated as admin-trusted code; CSP
|
|
89
|
-
* `unsafe-eval` is required. See `docs/plans/after-state-updated-js.md`.
|
|
90
|
-
*/
|
|
91
|
-
afterStateUpdatedJs?: string
|
|
92
|
-
/** Plan #6 cross-field plumbing. */
|
|
93
|
-
prefix?: FieldDecoration
|
|
94
|
-
suffix?: FieldDecoration
|
|
95
|
-
helperText?: string
|
|
96
|
-
/**
|
|
97
|
-
* Render the label to the left of the input rather than above it.
|
|
98
|
-
* Mirrors `Entry.inlineLabel()` for cross-surface symmetry — same flag
|
|
99
|
-
* shape, same default (label-above) when omitted.
|
|
100
|
-
*/
|
|
101
|
-
inlineLabel?: boolean
|
|
102
|
-
/**
|
|
103
|
-
* Default value for create-mode (no record). Display-time wins over
|
|
104
|
-
* this when a record / values map is present (see `renderFormChild`
|
|
105
|
-
* — values from the form state override meta defaults).
|
|
106
|
-
*/
|
|
107
|
-
defaultValue?: unknown
|
|
108
|
-
/**
|
|
109
|
-
* Result of `formatStateUsing(fn)` evaluated at meta-build. Renderers
|
|
110
|
-
* prefer this over the raw value when present (mirrors
|
|
111
|
-
* `Column.formatStateUsing` from Plan #2).
|
|
112
|
-
*/
|
|
113
|
-
formattedValue?: string
|
|
114
|
-
/**
|
|
115
|
-
* Per-field collab override. Absent = inherit the panel-wide default
|
|
116
|
-
* (collab on every record-edit page when `@pilotiq-pro/collab` is
|
|
117
|
-
* registered). Explicit `false` opts THIS field out of the collab
|
|
118
|
-
* layer entirely — no value sync AND no presence chip — so the field
|
|
119
|
-
* stays device-local inside an otherwise collab-on form. Useful for
|
|
120
|
-
* sensitive scratch space or fields whose LWW semantics would
|
|
121
|
-
* surprise users (rapid typing in plain-text inputs). Forward-compat
|
|
122
|
-
* for `true` (opt in even when panel default is off) once
|
|
123
|
-
* panel-level disable lands.
|
|
124
|
-
*/
|
|
125
|
-
collab?: boolean
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Context passed to `showWhen` / `hideWhen` / `disabledWhen` callbacks.
|
|
130
|
-
* Plan #5 widened these from `(record) => bool` to `(ctx) => bool` so
|
|
131
|
-
* conditions can read sibling values via `$get`. Existing code that
|
|
132
|
-
* destructures `record` keeps working: `({ record }) => …`. Plain
|
|
133
|
-
* `record => record.foo` callers must migrate to the destructure form.
|
|
134
|
-
*/
|
|
135
|
-
export interface ConditionContext {
|
|
136
|
-
record?: unknown
|
|
137
|
-
values?: Record<string, unknown>
|
|
138
|
-
$get?: (name: string) => unknown
|
|
139
|
-
$set?: (name: string, value: unknown) => void
|
|
140
|
-
user?: unknown
|
|
141
|
-
/**
|
|
142
|
-
* Plan #14 row-scoped sugar inside a Repeater. Mirrors `RenderContext.row`
|
|
143
|
-
* — present only when this field is being resolved as part of a Repeater
|
|
144
|
-
* row's inner schema. `row.$get / $set` read the row's local values map
|
|
145
|
-
* (same as `ctx.$get / $set` in this scope); `row.index` is the row's
|
|
146
|
-
* position. Use it for clarity at call sites that want to be explicit
|
|
147
|
-
* about row-scoping.
|
|
148
|
-
*/
|
|
149
|
-
row?: {
|
|
150
|
-
index: number
|
|
151
|
-
$get: (name: string) => unknown
|
|
152
|
-
$set: (name: string, value: unknown) => void
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export type FieldCondition = (ctx: ConditionContext) => boolean
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Server-side hook fired when a `live()` field's value changes. Receives
|
|
160
|
-
* the new value and a context bag with helpers for reading/writing
|
|
161
|
-
* sibling fields. Async — pilotiq awaits it before re-resolving the
|
|
162
|
-
* form schema for the response.
|
|
163
|
-
*/
|
|
164
|
-
export type AfterStateUpdatedContext = {
|
|
165
|
-
$get: (name: string) => unknown
|
|
166
|
-
$set: (name: string, value: unknown) => void
|
|
167
|
-
record?: unknown
|
|
168
|
-
user?: unknown
|
|
169
|
-
request?: unknown
|
|
170
|
-
values: Record<string, unknown>
|
|
171
|
-
/**
|
|
172
|
-
* Plan #14 — present only when the field is inside a Repeater or
|
|
173
|
-
* Builder row. `$get` / `$set` are row-scoped (mirroring the
|
|
174
|
-
* resolve-time `$get` inside a row); cross-row reads / writes go
|
|
175
|
-
* through the parent `$get / $set` with a dotted path
|
|
176
|
-
* (`items.0.quantity` or `content.0.data.heading`).
|
|
177
|
-
*
|
|
178
|
-
* `blockType` is set only for Builder rows — the discriminator picks
|
|
179
|
-
* which block schema is active. Undefined inside Repeater rows
|
|
180
|
-
* (homogeneous schema, single inner type).
|
|
181
|
-
*/
|
|
182
|
-
row?: {
|
|
183
|
-
index: number
|
|
184
|
-
blockType?: string
|
|
185
|
-
$get: (name: string) => unknown
|
|
186
|
-
$set: (name: string, value: unknown) => void
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
export type AfterStateUpdatedHandler = (
|
|
191
|
-
value: unknown,
|
|
192
|
-
ctx: AfterStateUpdatedContext,
|
|
193
|
-
) => void | Promise<void>
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Display-time transform passed to `Field.formatStateUsing(fn)`. Receives
|
|
197
|
-
* the resolved value plus the record context, returns the string that
|
|
198
|
-
* the renderer should display. Parallel to `Column.formatStateUsing` so
|
|
199
|
-
* the same shape applies in tables and forms.
|
|
200
|
-
*/
|
|
201
|
-
export type FormatStateUsingHandler = (
|
|
202
|
-
value: unknown,
|
|
203
|
-
ctx: { record?: unknown },
|
|
204
|
-
) => string
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Configuration for `Field.distinct()` — cross-row uniqueness inside a
|
|
208
|
-
* Repeater or Builder. Outside an array-row context the flag is a no-op
|
|
209
|
-
* (validators run per row's local values map; cross-row comparisons are
|
|
210
|
-
* the array-field validator's job).
|
|
211
|
-
*
|
|
212
|
-
* - `caseInsensitive` (default `false`): case-fold strings before
|
|
213
|
-
* comparing. Non-string values are compared as-is.
|
|
214
|
-
* - `ignoreNulls` (default `true`): treat `null / undefined / ''` as
|
|
215
|
-
* "not yet set" and skip them — two empty rows aren't a conflict by
|
|
216
|
-
* default. Pass `false` to forbid duplicate empties too.
|
|
217
|
-
* - `message` (default `'Must be unique'`): override the rejection text.
|
|
218
|
-
*/
|
|
219
|
-
export interface DistinctOptions {
|
|
220
|
-
caseInsensitive?: boolean
|
|
221
|
-
ignoreNulls?: boolean
|
|
222
|
-
message?: string
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
export abstract class Field extends Element {
|
|
226
|
-
readonly name: string
|
|
227
|
-
readonly fieldType: FieldType
|
|
228
|
-
|
|
229
|
-
protected _label: string
|
|
230
|
-
protected _required = false
|
|
231
|
-
protected _readonly = false
|
|
232
|
-
protected _placeholder?: string
|
|
233
|
-
|
|
234
|
-
// Visibility flags — exclude this field from a specific render mode.
|
|
235
|
-
// Evaluated by the field resolver against `RenderContext.mode`.
|
|
236
|
-
protected _hideFromTable = false
|
|
237
|
-
protected _hideFromCreate = false
|
|
238
|
-
protected _hideFromEdit = false
|
|
239
|
-
protected _hideFromView = false
|
|
240
|
-
|
|
241
|
-
// Condition callbacks — evaluated server-side against `RenderContext.record`.
|
|
242
|
-
// No-op when no record is present (e.g. create mode).
|
|
243
|
-
protected _showWhen?: FieldCondition
|
|
244
|
-
protected _hideWhen?: FieldCondition
|
|
245
|
-
protected _disabledWhen?: FieldCondition
|
|
246
|
-
|
|
247
|
-
// Validators run server-side on submit. Each may carry a serialized
|
|
248
|
-
// descriptor mirrored to the client via `toMeta().rules` for live UX.
|
|
249
|
-
protected _validators: Validator[] = []
|
|
250
|
-
|
|
251
|
-
// Plan #5 reactive plumbing. `_live` undefined → field doesn't trigger
|
|
252
|
-
// re-resolves; `true` → fire on every change; object → onBlur/debounce.
|
|
253
|
-
// `_afterStateUpdated` runs server-side after the changed field's value
|
|
254
|
-
// is applied but before the schema is re-resolved.
|
|
255
|
-
protected _live?: true | LiveOptions
|
|
256
|
-
protected _afterStateUpdated?: AfterStateUpdatedHandler
|
|
257
|
-
// Client-side counterpart to `_afterStateUpdated`. Raw JS string;
|
|
258
|
-
// compiled + executed on the client on every change. Empty string clears.
|
|
259
|
-
protected _afterStateUpdatedJs?: string
|
|
260
|
-
|
|
261
|
-
// Plan #6 cross-field plumbing. All optional, all serialized only when set.
|
|
262
|
-
protected _prefix?: FieldDecoration
|
|
263
|
-
protected _suffix?: FieldDecoration
|
|
264
|
-
protected _helperText?: string
|
|
265
|
-
protected _inlineLabel?: boolean
|
|
266
|
-
protected _default?: unknown
|
|
267
|
-
protected _dehydrated = true
|
|
268
|
-
protected _formatStateUsing?: FormatStateUsingHandler
|
|
269
|
-
// Cross-row uniqueness flag. Only consulted by `validateRepeater` and
|
|
270
|
-
// `validateBuilder`; a no-op outside an array-row context.
|
|
271
|
-
protected _distinct?: DistinctOptions
|
|
272
|
-
|
|
273
|
-
// Disable options already picked in sibling Repeater/Builder rows. Only
|
|
274
|
-
// consulted by option-bearing subclasses (Select / Radio / CheckboxList /
|
|
275
|
-
// ToggleButtons) inside their `toMeta` — a no-op everywhere else.
|
|
276
|
-
protected _disableOptionsWhenSelectedInSiblings = false
|
|
277
|
-
|
|
278
|
-
// Filament-parity micro-additions. All optional, all serialized only when set.
|
|
279
|
-
protected _autofocus = false
|
|
280
|
-
protected _hiddenLabel = false
|
|
281
|
-
protected _validationAttribute?: string
|
|
282
|
-
protected _extraAttributes?: Record<string, string | number | boolean>
|
|
283
|
-
protected _extraInputAttributes?: Record<string, string | number | boolean>
|
|
284
|
-
protected _extraFieldWrapperAttributes?: Record<string, string | number | boolean>
|
|
285
|
-
|
|
286
|
-
// Operation-aware shortcuts. Each entry is `'table' | 'create' | 'edit' | 'view'`;
|
|
287
|
-
// resolved against `RenderContext.mode` by `isHiddenIn / isDisabledIn`.
|
|
288
|
-
// `disabledOn(['edit'])` reads as "disabled when the page mode is edit".
|
|
289
|
-
protected _disabledOn?: ReadonlyArray<'table' | 'create' | 'edit' | 'view'>
|
|
290
|
-
protected _hiddenOn?: ReadonlyArray<'table' | 'create' | 'edit' | 'view'>
|
|
291
|
-
protected _visibleOn?: ReadonlyArray<'table' | 'create' | 'edit' | 'view'>
|
|
292
|
-
|
|
293
|
-
// Per-field collab override. `undefined` = inherit panel default;
|
|
294
|
-
// explicit `false` = opt out of value sync AND presence entirely.
|
|
295
|
-
protected _collab?: boolean
|
|
296
|
-
|
|
297
|
-
constructor(name: string, type: FieldType) {
|
|
298
|
-
super()
|
|
299
|
-
this.name = name
|
|
300
|
-
this.fieldType = type
|
|
301
|
-
this._label = name.charAt(0).toUpperCase() + name.slice(1)
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
/** All fields share the `'field'` type discriminator; client switches on `fieldType`. */
|
|
305
|
-
getType(): string { return 'field' }
|
|
306
|
-
|
|
307
|
-
// ─── Static config ────────────────────────────────────
|
|
308
|
-
|
|
309
|
-
label(l: string): this { this._label = l; return this }
|
|
310
|
-
required(v = true): this { this._required = v; return this }
|
|
311
|
-
readonly(v = true): this { this._readonly = v; return this }
|
|
312
|
-
placeholder(p: string): this { this._placeholder = p; return this }
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Render the input with the HTML `autofocus` attribute. The browser
|
|
316
|
-
* focuses the first matching control on initial paint; on SPA-nav the
|
|
317
|
-
* renderer uses a `useEffect` fallback so the focus still lands.
|
|
318
|
-
*/
|
|
319
|
-
autofocus(v = true): this { this._autofocus = v; return this }
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Render the label as `sr-only` — visually hidden but kept for screen
|
|
323
|
-
* readers. Use for inputs whose context already names them (e.g. a
|
|
324
|
-
* search bar in a table header). Distinct from omitting the label.
|
|
325
|
-
*/
|
|
326
|
-
hiddenLabel(v = true): this { this._hiddenLabel = v; return this }
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
* Override the name used in default validation messages. Filament
|
|
330
|
-
* parity: `validationAttribute('email address')` makes the auto-required
|
|
331
|
-
* message read "The email address is required" instead of "This field
|
|
332
|
-
* is required". Only consulted by the implicit-required check; explicit
|
|
333
|
-
* validators with a `message` argument keep their text.
|
|
334
|
-
*/
|
|
335
|
-
validationAttribute(label: string): this { this._validationAttribute = label; return this }
|
|
336
|
-
|
|
337
|
-
/** Pass-through HTML attrs spread on the field's outer wrapper. */
|
|
338
|
-
extraAttributes(attrs: Record<string, string | number | boolean>): this {
|
|
339
|
-
this._extraAttributes = attrs
|
|
340
|
-
return this
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/** Pass-through HTML attrs spread on the underlying `<input>` / `<select>` / etc. */
|
|
344
|
-
extraInputAttributes(attrs: Record<string, string | number | boolean>): this {
|
|
345
|
-
this._extraInputAttributes = attrs
|
|
346
|
-
return this
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
/** Filament-parity alias for `extraAttributes` — same outer-wrapper target. */
|
|
350
|
-
extraFieldWrapperAttributes(attrs: Record<string, string | number | boolean>): this {
|
|
351
|
-
this._extraFieldWrapperAttributes = attrs
|
|
352
|
-
return this
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// ─── Visibility flags ─────────────────────────────────
|
|
356
|
-
|
|
357
|
-
hideFromTable(v = true): this { this._hideFromTable = v; return this }
|
|
358
|
-
hideFromCreate(v = true): this { this._hideFromCreate = v; return this }
|
|
359
|
-
hideFromEdit(v = true): this { this._hideFromEdit = v; return this }
|
|
360
|
-
hideFromView(v = true): this { this._hideFromView = v; return this }
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* Disable the input only on the listed page modes. Sugar over
|
|
364
|
-
* `disabledWhen(({ ctx }) => …)` for the common case. Composes with
|
|
365
|
-
* `readonly()` and `disabledWhen()` — any path returning true wins.
|
|
366
|
-
*/
|
|
367
|
-
disabledOn(modes: ReadonlyArray<'table' | 'create' | 'edit' | 'view'>): this {
|
|
368
|
-
this._disabledOn = modes
|
|
369
|
-
return this
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Hide the field only on the listed page modes. Distinct from the
|
|
374
|
-
* existing `hideFromCreate / hideFromEdit / …` flags — accepts a list
|
|
375
|
-
* so `hiddenOn(['create', 'view'])` reads in one line.
|
|
376
|
-
*/
|
|
377
|
-
hiddenOn(modes: ReadonlyArray<'table' | 'create' | 'edit' | 'view'>): this {
|
|
378
|
-
this._hiddenOn = modes
|
|
379
|
-
return this
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Inverse of `hiddenOn` — show only on the listed page modes. The
|
|
384
|
-
* `mode === undefined` case (custom Pages, schema-only routes) keeps
|
|
385
|
-
* the field visible to match `hideFromX`'s no-op posture there.
|
|
386
|
-
*/
|
|
387
|
-
visibleOn(modes: ReadonlyArray<'table' | 'create' | 'edit' | 'view'>): this {
|
|
388
|
-
this._visibleOn = modes
|
|
389
|
-
return this
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
// ─── Conditions ───────────────────────────────────────
|
|
393
|
-
|
|
394
|
-
showWhen(fn: FieldCondition): this { this._showWhen = fn; return this }
|
|
395
|
-
hideWhen(fn: FieldCondition): this { this._hideWhen = fn; return this }
|
|
396
|
-
disabledWhen(fn: FieldCondition): this { this._disabledWhen = fn; return this }
|
|
397
|
-
|
|
398
|
-
// ─── Reactivity (Plan #5) ─────────────────────────────
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* Mark this field as a re-resolve trigger. With no opts, the form
|
|
402
|
-
* roundtrips on every change. Pass `{ onBlur: true }` to wait for
|
|
403
|
-
* blur, `{ debounce: 500 }` to wait 500ms of idle, or both. Calling
|
|
404
|
-
* `live(false)` clears the flag.
|
|
405
|
-
*/
|
|
406
|
-
live(opts?: boolean | LiveOptions): this {
|
|
407
|
-
if (opts === false) { delete this._live; return this }
|
|
408
|
-
if (opts === undefined || opts === true) { this._live = true; return this }
|
|
409
|
-
this._live = opts
|
|
410
|
-
return this
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
/**
|
|
414
|
-
* Server-side hook called when this field's value changes during a
|
|
415
|
-
* partial-resolve roundtrip. Receives the new value and `{ $get, $set,
|
|
416
|
-
* record, user, request, values }`. Use `$set` to populate dependent
|
|
417
|
-
* fields (e.g. slug from title). Async; thrown errors fail the
|
|
418
|
-
* roundtrip and surface a toast on the client.
|
|
419
|
-
*/
|
|
420
|
-
afterStateUpdated(fn: AfterStateUpdatedHandler): this {
|
|
421
|
-
this._afterStateUpdated = fn
|
|
422
|
-
return this
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* Client-side reactivity hook. The string is compiled into a function
|
|
427
|
-
* `(($state, $get, $set) => { …body… })` on the client and run
|
|
428
|
-
* synchronously on every change — independent of `live()` (no server
|
|
429
|
-
* roundtrip required). Use `$set('other', value)` to populate
|
|
430
|
-
* dependent fields instantly.
|
|
431
|
-
*
|
|
432
|
-
* Treated as admin-trusted code: written at schema-definition time,
|
|
433
|
-
* never derived from request input. CSP `unsafe-eval` is required;
|
|
434
|
-
* apps with strict CSPs see the eval fail at runtime (caught + logged,
|
|
435
|
-
* field stays usable).
|
|
436
|
-
*
|
|
437
|
-
* Pass `''` (empty string) to clear. Composes with the server hook
|
|
438
|
-
* `afterStateUpdated()`: JS runs first synchronously; the server's
|
|
439
|
-
* response on the next `live()` roundtrip overlays it.
|
|
440
|
-
*/
|
|
441
|
-
afterStateUpdatedJs(body: string): this {
|
|
442
|
-
if (body === '') { delete this._afterStateUpdatedJs; return this }
|
|
443
|
-
this._afterStateUpdatedJs = body
|
|
444
|
-
return this
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
/** Whether this field is configured to trigger live re-resolves. */
|
|
448
|
-
isLive(): boolean { return this._live !== undefined }
|
|
449
|
-
getLiveOptions(): true | LiveOptions | undefined { return this._live }
|
|
450
|
-
getAfterStateUpdated(): AfterStateUpdatedHandler | undefined { return this._afterStateUpdated }
|
|
451
|
-
getAfterStateUpdatedJs(): string | undefined { return this._afterStateUpdatedJs }
|
|
452
|
-
|
|
453
|
-
// ─── Cross-field plumbing (Plan #6) ───────────────────
|
|
454
|
-
|
|
455
|
-
/**
|
|
456
|
-
* Decoration before the input — currency mark, protocol, etc. Pass a
|
|
457
|
-
* plain string for text or `{ icon: 'name' }` to use the icon registry.
|
|
458
|
-
* Serialized verbatim onto `FieldMeta.prefix`.
|
|
459
|
-
*/
|
|
460
|
-
prefix(content: FieldDecoration): this { this._prefix = content; return this }
|
|
461
|
-
|
|
462
|
-
/** Decoration after the input — domain suffix, unit, etc. */
|
|
463
|
-
suffix(content: FieldDecoration): this { this._suffix = content; return this }
|
|
464
|
-
|
|
465
|
-
/** Helper text rendered below the input — typically a constraint hint. */
|
|
466
|
-
helperText(text: string): this { this._helperText = text; return this }
|
|
467
|
-
|
|
468
|
-
/**
|
|
469
|
-
* Render the label to the left of the input rather than above it.
|
|
470
|
-
* Mirrors `Entry.inlineLabel()`. Default is label-above; pass `false`
|
|
471
|
-
* to clear the flag — including overriding a cascading default set
|
|
472
|
-
* via `Form.inlineLabel()` / `Section.inlineLabel()`.
|
|
473
|
-
*
|
|
474
|
-
* Resolution at meta-build time: explicit field setting wins; when
|
|
475
|
-
* unset, the nearest ancestor `Form` / `Section` cascade kicks in
|
|
476
|
-
* (`RenderContext.inlineLabelDefault`); when neither is set, label-above.
|
|
477
|
-
*/
|
|
478
|
-
inlineLabel(v = true): this { this._inlineLabel = v; return this }
|
|
479
|
-
|
|
480
|
-
/**
|
|
481
|
-
* Default value for create-mode (no record). On edit, the loaded
|
|
482
|
-
* record's value wins. Stored opaquely; the renderer reads it via
|
|
483
|
-
* `FieldMeta.defaultValue`.
|
|
484
|
-
*/
|
|
485
|
-
default(value: unknown): this { this._default = value; return this }
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
* Toggle whether this field round-trips its value on submit. Default
|
|
489
|
-
* `true` (dehydrated — value is included in the POST body). Pass
|
|
490
|
-
* `false` for purely-display fields, computed values, or wizard
|
|
491
|
-
* scratch state. Dehydrated-false fields are filtered out by
|
|
492
|
-
* `coerceFormValues` before validation runs.
|
|
493
|
-
*/
|
|
494
|
-
dehydrated(value: boolean = true): this { this._dehydrated = value; return this }
|
|
495
|
-
|
|
496
|
-
/**
|
|
497
|
-
* Per-field realtime-collab override.
|
|
498
|
-
*
|
|
499
|
-
* - `.collab(false)` — opts THIS field out of the collab layer
|
|
500
|
-
* entirely (no value sync via the form-level CRDT, no presence
|
|
501
|
-
* chip rendered next to the label). The field stays device-local
|
|
502
|
-
* inside an otherwise collab-on form. Useful for private scratch
|
|
503
|
-
* space or fields whose LWW semantics would surprise users
|
|
504
|
-
* (rapid typing in plain-text inputs hit the v1 last-writer-wins
|
|
505
|
-
* footgun — `.collab(false)` is the v1 escape hatch).
|
|
506
|
-
* - `.collab(true)` (default of the bare call) — explicit opt-in.
|
|
507
|
-
* Forward-compat for a future panel-level disable; today it
|
|
508
|
-
* behaves the same as not calling the setter when collab is
|
|
509
|
-
* already on at the panel level.
|
|
510
|
-
* - Not calling the setter — inherits the panel-wide default
|
|
511
|
-
* (collab on whenever `.plugins([collab()])` is registered).
|
|
512
|
-
*/
|
|
513
|
-
collab(enabled = true): this {
|
|
514
|
-
this._collab = enabled
|
|
515
|
-
return this
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
/**
|
|
519
|
-
* Display-time transform — receives `(value, { record })` and returns
|
|
520
|
-
* a string. Result lands on `FieldMeta.formattedValue`; renderers
|
|
521
|
-
* prefer it over the raw value when present. Parallels
|
|
522
|
-
* `Column.formatStateUsing` from Plan #2.
|
|
523
|
-
*/
|
|
524
|
-
formatStateUsing(fn: FormatStateUsingHandler): this {
|
|
525
|
-
this._formatStateUsing = fn
|
|
526
|
-
return this
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
/**
|
|
530
|
-
* Cross-row uniqueness inside a Repeater / Builder. When the field is
|
|
531
|
-
* resolved in a row context, every row's value is compared against
|
|
532
|
-
* earlier rows; the second + subsequent occurrences fail validation.
|
|
533
|
-
*
|
|
534
|
-
* Pass `false` (or `distinct(false)`) to clear the flag. Pass an options
|
|
535
|
-
* object for `caseInsensitive / ignoreNulls / message` (see
|
|
536
|
-
* `DistinctOptions`). Bare `distinct()` is the common case — exact
|
|
537
|
-
* comparison, empty values ignored, default message.
|
|
538
|
-
*
|
|
539
|
-
* Outside an array-row context this flag is a no-op (`validateSchema`
|
|
540
|
-
* never reads it directly — only `validateRepeater / validateBuilder`
|
|
541
|
-
* do, and those have access to the full row array). Pair with
|
|
542
|
-
* `unique({ model })` if you also need DB-level uniqueness across all
|
|
543
|
-
* records.
|
|
544
|
-
*/
|
|
545
|
-
distinct(opts?: boolean | DistinctOptions): this {
|
|
546
|
-
if (opts === false) { delete this._distinct; return this }
|
|
547
|
-
if (opts === undefined || opts === true) { this._distinct = {}; return this }
|
|
548
|
-
this._distinct = opts
|
|
549
|
-
return this
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
getDistinct(): DistinctOptions | undefined { return this._distinct }
|
|
553
|
-
|
|
554
|
-
/**
|
|
555
|
-
* Inside a Repeater / Builder, grey out option choices that another row
|
|
556
|
-
* has already picked. Auto-enables `distinct()` (server-side cross-row
|
|
557
|
-
* uniqueness as a last-line guarantee) and `live()` (so picking a value
|
|
558
|
-
* in one row immediately re-resolves the others).
|
|
559
|
-
*
|
|
560
|
-
* Pass `false` to clear the flag — calling `distinct(false) / live(false)`
|
|
561
|
-
* separately afterwards is up to you, this method only re-arms them when
|
|
562
|
-
* enabling. Outside an array-row context the flag is a no-op (the
|
|
563
|
-
* resolver only stamps `ctx.row.siblings` inside Repeater/Builder rows).
|
|
564
|
-
*
|
|
565
|
-
* Builder scoping is per-block-type: a `Select`'s "taken" values come
|
|
566
|
-
* only from sibling rows whose `type` matches the current row's block
|
|
567
|
-
* (otherwise picks across heterogeneous blocks would falsely conflict).
|
|
568
|
-
*
|
|
569
|
-
* Implemented on `Field` so the flag carries through to every subclass,
|
|
570
|
-
* but only `SelectField / RadioField / CheckboxListField /
|
|
571
|
-
* ToggleButtonsField` consume it inside their `toMeta` (other field
|
|
572
|
-
* types have no option list to disable).
|
|
573
|
-
*/
|
|
574
|
-
disableOptionsWhenSelectedInSiblingRepeaterItems(value: boolean = true): this {
|
|
575
|
-
if (value === false) {
|
|
576
|
-
this._disableOptionsWhenSelectedInSiblings = false
|
|
577
|
-
return this
|
|
578
|
-
}
|
|
579
|
-
this._disableOptionsWhenSelectedInSiblings = true
|
|
580
|
-
this.distinct()
|
|
581
|
-
this.live()
|
|
582
|
-
return this
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
shouldDisableOptionsTakenInSiblings(): boolean {
|
|
586
|
-
return this._disableOptionsWhenSelectedInSiblings
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
isDehydrated(): boolean { return this._dehydrated }
|
|
590
|
-
getDefault(): unknown { return this._default }
|
|
591
|
-
getPrefix(): FieldDecoration | undefined { return this._prefix }
|
|
592
|
-
getSuffix(): FieldDecoration | undefined { return this._suffix }
|
|
593
|
-
getHelperText(): string | undefined { return this._helperText }
|
|
594
|
-
getFormatStateUsing(): FormatStateUsingHandler | undefined { return this._formatStateUsing }
|
|
595
|
-
|
|
596
|
-
// ─── Validation ───────────────────────────────────────
|
|
597
|
-
|
|
598
|
-
/**
|
|
599
|
-
* Append one or more validators. Multiple calls accumulate; pass an array
|
|
600
|
-
* to add several at once. Order is preserved — `runValidators()` collects
|
|
601
|
-
* every error in order so the user sees all problems at once.
|
|
602
|
-
*/
|
|
603
|
-
validate(v: Validator | Validator[]): this {
|
|
604
|
-
if (Array.isArray(v)) this._validators.push(...v)
|
|
605
|
-
else this._validators.push(v)
|
|
606
|
-
return this
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
getValidators(): Validator[] { return this._validators }
|
|
610
|
-
|
|
611
|
-
// ─── Getters (read-only access for resolver/tests) ───
|
|
612
|
-
|
|
613
|
-
getLabel(): string { return this._label }
|
|
614
|
-
isRequired(): boolean { return this._required }
|
|
615
|
-
isReadonly(): boolean { return this._readonly }
|
|
616
|
-
getPlaceholder(): string | undefined { return this._placeholder }
|
|
617
|
-
|
|
618
|
-
// ─── Resolution ───────────────────────────────────────
|
|
619
|
-
|
|
620
|
-
/**
|
|
621
|
-
* Whether this field should be omitted from the rendered output for the
|
|
622
|
-
* current context. Combines the `_hideFromMode` flags (when `ctx.mode` is
|
|
623
|
-
* set) and the `showWhen` / `hideWhen` callbacks (when `ctx.record` or
|
|
624
|
-
* `ctx.values` is present).
|
|
625
|
-
*
|
|
626
|
-
* Plan #5: condition callbacks receive a `ConditionContext` carrying
|
|
627
|
-
* record, values, $get, $set, user. They still fire only when there's
|
|
628
|
-
* something to look at — record in non-create modes, or values when
|
|
629
|
-
* the form has been edited.
|
|
630
|
-
*/
|
|
631
|
-
isHiddenIn(ctx?: RenderContext): boolean {
|
|
632
|
-
const mode = ctx?.mode
|
|
633
|
-
if (mode === 'table' && this._hideFromTable) return true
|
|
634
|
-
if (mode === 'create' && this._hideFromCreate) return true
|
|
635
|
-
if (mode === 'edit' && this._hideFromEdit) return true
|
|
636
|
-
if (mode === 'view' && this._hideFromView) return true
|
|
637
|
-
if (mode !== undefined) {
|
|
638
|
-
if (this._hiddenOn && this._hiddenOn.includes(mode)) return true
|
|
639
|
-
if (this._visibleOn && !this._visibleOn.includes(mode)) return true
|
|
640
|
-
}
|
|
641
|
-
if (this._showWhen || this._hideWhen) {
|
|
642
|
-
const condCtx = this.buildConditionContext(ctx)
|
|
643
|
-
if (condCtx) {
|
|
644
|
-
if (this._showWhen && !this._showWhen(condCtx)) return true
|
|
645
|
-
if (this._hideWhen && this._hideWhen(condCtx)) return true
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
return false
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
/**
|
|
652
|
-
* Resolved disabled state — `true` if `readonly()` is set OR
|
|
653
|
-
* `disabledWhen()` returns true for the current record/values.
|
|
654
|
-
*/
|
|
655
|
-
isDisabledIn(ctx?: RenderContext): boolean {
|
|
656
|
-
if (this._readonly) return true
|
|
657
|
-
const mode = ctx?.mode
|
|
658
|
-
if (mode !== undefined && this._disabledOn && this._disabledOn.includes(mode)) return true
|
|
659
|
-
if (this._disabledWhen) {
|
|
660
|
-
const condCtx = this.buildConditionContext(ctx)
|
|
661
|
-
if (condCtx) return this._disabledWhen(condCtx)
|
|
662
|
-
}
|
|
663
|
-
return false
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
/**
|
|
667
|
-
* Build the condition-callback ctx from the render ctx. Returns
|
|
668
|
-
* undefined when there's no useful data to evaluate against (no
|
|
669
|
-
* record AND no values) — saves callbacks from being asked
|
|
670
|
-
* "should I show?" with nothing to look at, matching today's
|
|
671
|
-
* "no record → skip" semantics. The resolver and partial-resolve
|
|
672
|
-
* endpoint always pass either record or values, so user code never
|
|
673
|
-
* needs to defensively check.
|
|
674
|
-
*/
|
|
675
|
-
private buildConditionContext(ctx?: RenderContext): ConditionContext | undefined {
|
|
676
|
-
if (!ctx) return undefined
|
|
677
|
-
if (ctx.record === undefined && ctx.values === undefined) return undefined
|
|
678
|
-
const condCtx: ConditionContext = {}
|
|
679
|
-
if (ctx.record !== undefined) condCtx.record = ctx.record
|
|
680
|
-
if (ctx.values !== undefined) condCtx.values = ctx.values
|
|
681
|
-
if (ctx.$get) condCtx.$get = ctx.$get
|
|
682
|
-
if (ctx.$set) condCtx.$set = ctx.$set
|
|
683
|
-
if (ctx.user !== undefined) condCtx.user = ctx.user
|
|
684
|
-
if (ctx.row !== undefined) condCtx.row = ctx.row
|
|
685
|
-
return condCtx
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
/**
|
|
689
|
-
* Run every validator against `value`, collecting all error messages in
|
|
690
|
-
* declaration order. The `_required` flag implicitly contributes a
|
|
691
|
-
* required check unless the validator list already includes one (matched
|
|
692
|
-
* by `serialized.rule === 'required'`) — keeps `.required()` and
|
|
693
|
-
* `.validate(required())` from double-firing.
|
|
694
|
-
*
|
|
695
|
-
* Async-aware: validators returning a `Promise<string | null>` (e.g.
|
|
696
|
-
* `unique()` probing the DB) are awaited in declaration order. Errors
|
|
697
|
-
* thrown by an async validator propagate to the caller — the pipeline
|
|
698
|
-
* does NOT swallow them, since DB failures should surface as 500s
|
|
699
|
-
* rather than silently invalidating the field.
|
|
700
|
-
*/
|
|
701
|
-
async runValidators(value: unknown, ctx?: ValidatorContext): Promise<string[]> {
|
|
702
|
-
const errors: string[] = []
|
|
703
|
-
|
|
704
|
-
if (this._required && !this.hasRequiredValidator()) {
|
|
705
|
-
if (value === undefined || value === null || value === '') {
|
|
706
|
-
errors.push(this.requiredMessage())
|
|
707
|
-
}
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
for (const v of this._validators) {
|
|
711
|
-
const result = await v(value, ctx)
|
|
712
|
-
if (result) errors.push(result)
|
|
713
|
-
}
|
|
714
|
-
return errors
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
/**
|
|
718
|
-
* Default-required error text. When `validationAttribute()` is set,
|
|
719
|
-
* substitutes the attribute into a human message — "The email address
|
|
720
|
-
* is required" — otherwise falls back to the legacy generic phrasing
|
|
721
|
-
* so existing tests + UI strings keep matching.
|
|
722
|
-
*/
|
|
723
|
-
private requiredMessage(): string {
|
|
724
|
-
return this._validationAttribute
|
|
725
|
-
? `The ${this._validationAttribute} is required`
|
|
726
|
-
: 'This field is required'
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
private hasRequiredValidator(): boolean {
|
|
730
|
-
return this._validators.some(v => v.serialized?.rule === 'required')
|
|
731
|
-
}
|
|
732
|
-
|
|
733
|
-
/** Serialized rule descriptors mirrored to the client. */
|
|
734
|
-
protected getSerializedRules(): SerializedRule[] {
|
|
735
|
-
const rules: SerializedRule[] = []
|
|
736
|
-
if (this._required && !this.hasRequiredValidator()) {
|
|
737
|
-
rules.push({ rule: 'required', message: this.requiredMessage() })
|
|
738
|
-
}
|
|
739
|
-
for (const v of this._validators) {
|
|
740
|
-
if (v.serialized) rules.push(v.serialized)
|
|
741
|
-
}
|
|
742
|
-
return rules
|
|
743
|
-
}
|
|
744
|
-
|
|
745
|
-
/**
|
|
746
|
-
* Serialize this field's state for the client. Subclasses spread this and
|
|
747
|
-
* add their own fields (e.g. `maxLength`, `options`).
|
|
748
|
-
*
|
|
749
|
-
* Disabled state is computed via `isDisabledIn(ctx)` — pass the current
|
|
750
|
-
* `RenderContext` so `disabledWhen` evaluates correctly and reactive
|
|
751
|
-
* subclasses (Plan #5) can read `values / $get`. Omit the ctx for
|
|
752
|
-
* meta-only contexts (e.g. table cell rendering of an unbound field).
|
|
753
|
-
*/
|
|
754
|
-
/**
|
|
755
|
-
* Build the synchronous base meta object — type, fieldType, name,
|
|
756
|
-
* label, required, disabled, placeholder, rules, live. Subclasses spread
|
|
757
|
-
* the result to add their own keys. Kept sync (a separate protected
|
|
758
|
-
* helper rather than `super.toMeta`) so async subclasses (Plan #5
|
|
759
|
-
* `SelectField` with resolver-style options) can `const base =
|
|
760
|
-
* this.buildMeta(ctx)` instead of having to handle a `FieldMeta |
|
|
761
|
-
* Promise<FieldMeta>` union from `super.toMeta`.
|
|
762
|
-
*/
|
|
763
|
-
protected buildMeta(ctx?: RenderContext): FieldMeta {
|
|
764
|
-
const rules = this.getSerializedRules()
|
|
765
|
-
|
|
766
|
-
// formatStateUsing: prefer the record-mapped value when available,
|
|
767
|
-
// otherwise the seeded default. Skip entirely when no source value
|
|
768
|
-
// exists — calling the formatter with `undefined` is rarely useful
|
|
769
|
-
// and would force every formatter to defensively guard.
|
|
770
|
-
let formattedValue: string | undefined
|
|
771
|
-
if (this._formatStateUsing) {
|
|
772
|
-
const recordValue = ctx?.record !== undefined && ctx.record !== null && typeof ctx.record === 'object'
|
|
773
|
-
? (ctx.record as Record<string, unknown>)[this.name]
|
|
774
|
-
: undefined
|
|
775
|
-
const valuesValue = ctx?.values?.[this.name]
|
|
776
|
-
const sourceValue = valuesValue !== undefined ? valuesValue
|
|
777
|
-
: recordValue !== undefined ? recordValue
|
|
778
|
-
: this._default
|
|
779
|
-
if (sourceValue !== undefined) {
|
|
780
|
-
try {
|
|
781
|
-
formattedValue = this._formatStateUsing(sourceValue, { record: ctx?.record })
|
|
782
|
-
} catch (err) {
|
|
783
|
-
console.warn(`[pilotiq] formatStateUsing for "${this.name}" threw:`, err)
|
|
784
|
-
}
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
return {
|
|
789
|
-
type: 'field',
|
|
790
|
-
fieldType: this.fieldType,
|
|
791
|
-
name: this.name,
|
|
792
|
-
label: this._label,
|
|
793
|
-
required: this._required,
|
|
794
|
-
disabled: this.isDisabledIn(ctx),
|
|
795
|
-
...(this._placeholder ? { placeholder: this._placeholder } : {}),
|
|
796
|
-
...(rules.length > 0 ? { rules } : {}),
|
|
797
|
-
...(this._live !== undefined ? { live: this._live } : {}),
|
|
798
|
-
...(this._afterStateUpdatedJs !== undefined ? { afterStateUpdatedJs: this._afterStateUpdatedJs } : {}),
|
|
799
|
-
...(this._prefix !== undefined ? { prefix: this._prefix } : {}),
|
|
800
|
-
...(this._suffix !== undefined ? { suffix: this._suffix } : {}),
|
|
801
|
-
...(this._helperText !== undefined ? { helperText: this._helperText } : {}),
|
|
802
|
-
...(this._inlineLabel === true || (this._inlineLabel === undefined && ctx?.inlineLabelDefault === true) ? { inlineLabel: true } : {}),
|
|
803
|
-
...(this._default !== undefined ? { defaultValue: this._default } : {}),
|
|
804
|
-
...(formattedValue !== undefined ? { formattedValue } : {}),
|
|
805
|
-
...(this._autofocus ? { autofocus: true } : {}),
|
|
806
|
-
...(this._hiddenLabel ? { hiddenLabel: true } : {}),
|
|
807
|
-
...(this._extraAttributes !== undefined ? { extraAttributes: this._extraAttributes } : {}),
|
|
808
|
-
...(this._extraInputAttributes !== undefined ? { extraInputAttributes: this._extraInputAttributes } : {}),
|
|
809
|
-
...(this._extraFieldWrapperAttributes !== undefined ? { extraFieldWrapperAttributes: this._extraFieldWrapperAttributes } : {}),
|
|
810
|
-
...(this._collab !== undefined ? { collab: this._collab } : {}),
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
override toMeta(ctx?: RenderContext): FieldMeta | Promise<FieldMeta> {
|
|
815
|
-
return this.buildMeta(ctx)
|
|
816
|
-
}
|
|
817
|
-
}
|