@pilotiq/pilotiq 0.24.1 → 0.24.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -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/package.json +6 -1
- 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
|
@@ -1,389 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useRef, useState } from 'react'
|
|
2
|
-
|
|
3
|
-
import { Input } from '../ui/input.js'
|
|
4
|
-
import { Switch } from '../ui/switch.js'
|
|
5
|
-
import {
|
|
6
|
-
Select,
|
|
7
|
-
SelectContent,
|
|
8
|
-
SelectItem,
|
|
9
|
-
SelectTrigger,
|
|
10
|
-
SelectValue,
|
|
11
|
-
} from '../ui/select.js'
|
|
12
|
-
import { useToast } from '../Toaster.js'
|
|
13
|
-
import type { NotificationMeta } from '../../notifications/Notification.js'
|
|
14
|
-
import {
|
|
15
|
-
Dialog,
|
|
16
|
-
DialogContent,
|
|
17
|
-
DialogDescription,
|
|
18
|
-
DialogFooter,
|
|
19
|
-
DialogHeader,
|
|
20
|
-
DialogTitle,
|
|
21
|
-
} from '../ui/dialog.js'
|
|
22
|
-
import { Button } from '../ui/button.js'
|
|
23
|
-
|
|
24
|
-
const DEFAULT_DEBOUNCE_MS = 500
|
|
25
|
-
|
|
26
|
-
/** Shared shape consumed by all three editable-cell renderers. */
|
|
27
|
-
export interface EditableCellProps {
|
|
28
|
-
/** PATCH endpoint stamped per row by `tagCellEditUrls`. */
|
|
29
|
-
url: string
|
|
30
|
-
/** Column meta as serialized to the client. */
|
|
31
|
-
col: Record<string, unknown>
|
|
32
|
-
/** Raw value from the row (pre-coerce, post-server). */
|
|
33
|
-
value: unknown
|
|
34
|
-
/** Disabled = static `disabled()` OR per-row `_cellDisabled[col]`. */
|
|
35
|
-
disabled: boolean
|
|
36
|
-
/** Row-scoped option override stamped by `loadTableRecords` when the
|
|
37
|
-
* column is a `SelectColumn` with a per-row `.options(record => …)`
|
|
38
|
-
* resolver. Wins over `col.selectOptions`; absent when the resolver
|
|
39
|
-
* threw or the column has only static options. */
|
|
40
|
-
rowOptions?: Array<{ value: string; label: string }>
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
interface PatchResultOk {
|
|
44
|
-
ok: true
|
|
45
|
-
value: unknown
|
|
46
|
-
notifications: NotificationMeta[] | undefined
|
|
47
|
-
}
|
|
48
|
-
interface PatchResultErrors {
|
|
49
|
-
ok: false
|
|
50
|
-
errors: Record<string, string[]>
|
|
51
|
-
}
|
|
52
|
-
interface PatchResultError {
|
|
53
|
-
ok: false
|
|
54
|
-
error: string
|
|
55
|
-
}
|
|
56
|
-
type PatchResult = PatchResultOk | PatchResultErrors | PatchResultError
|
|
57
|
-
|
|
58
|
-
async function patchCell(url: string, value: unknown): Promise<PatchResult> {
|
|
59
|
-
const res = await fetch(url, {
|
|
60
|
-
method: 'POST',
|
|
61
|
-
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
|
|
62
|
-
body: JSON.stringify({ value }),
|
|
63
|
-
})
|
|
64
|
-
let body: unknown = null
|
|
65
|
-
try { body = await res.json() } catch { /* fall through */ }
|
|
66
|
-
if (res.ok && body && typeof body === 'object') {
|
|
67
|
-
const b = body as Record<string, unknown>
|
|
68
|
-
return {
|
|
69
|
-
ok: true,
|
|
70
|
-
value: b['value'],
|
|
71
|
-
notifications: Array.isArray(b['notifications'])
|
|
72
|
-
? b['notifications'] as NotificationMeta[]
|
|
73
|
-
: undefined,
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (body && typeof body === 'object') {
|
|
77
|
-
const b = body as Record<string, unknown>
|
|
78
|
-
if (b['errors']) {
|
|
79
|
-
return { ok: false, errors: b['errors'] as Record<string, string[]> }
|
|
80
|
-
}
|
|
81
|
-
if (typeof b['error'] === 'string') {
|
|
82
|
-
return { ok: false, error: b['error'] }
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return { ok: false, error: `Request failed (${res.status})` }
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/** Pull the first error message out of a `{ errors: { value: [...] } }`
|
|
89
|
-
* response. Also reads the reserved `_cell` key surfaced by
|
|
90
|
-
* `Column.beforeStateUpdated / afterStateUpdated` halts. Falls back to
|
|
91
|
-
* a generic "Couldn't save" string. */
|
|
92
|
-
function firstErrorMessage(result: PatchResultErrors | PatchResultError): string {
|
|
93
|
-
if ('errors' in result) {
|
|
94
|
-
const fieldErrs = result.errors['value']
|
|
95
|
-
if (fieldErrs && fieldErrs.length > 0) return fieldErrs[0]!
|
|
96
|
-
const cellErrs = result.errors['_cell']
|
|
97
|
-
if (cellErrs && cellErrs.length > 0) return cellErrs[0]!
|
|
98
|
-
}
|
|
99
|
-
if ('error' in result) return result.error
|
|
100
|
-
return "Couldn't save"
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/** Confirm-gated wrapper. Renders a Dialog with the `confirm` message
|
|
104
|
-
* before invoking `onConfirm`; cancel calls `onCancel` so the caller can
|
|
105
|
-
* roll back the optimistic state. Mounts only when `open=true` so its
|
|
106
|
-
* children aren't kept alive between confirm cycles. */
|
|
107
|
-
function ConfirmDialog({
|
|
108
|
-
open, message, onConfirm, onCancel,
|
|
109
|
-
}: {
|
|
110
|
-
open: boolean
|
|
111
|
-
message: string
|
|
112
|
-
onConfirm: () => void
|
|
113
|
-
onCancel: () => void
|
|
114
|
-
}): React.ReactElement | null {
|
|
115
|
-
if (!open) return null
|
|
116
|
-
return (
|
|
117
|
-
<Dialog open={open} onOpenChange={(v) => { if (!v) onCancel() }}>
|
|
118
|
-
<DialogContent className="sm:max-w-sm">
|
|
119
|
-
<DialogHeader>
|
|
120
|
-
<DialogTitle>Confirm change</DialogTitle>
|
|
121
|
-
<DialogDescription>{message}</DialogDescription>
|
|
122
|
-
</DialogHeader>
|
|
123
|
-
<DialogFooter>
|
|
124
|
-
<Button variant="ghost" onClick={onCancel}>Cancel</Button>
|
|
125
|
-
<Button onClick={onConfirm}>Confirm</Button>
|
|
126
|
-
</DialogFooter>
|
|
127
|
-
</DialogContent>
|
|
128
|
-
</Dialog>
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// ─── TextInput cell ────────────────────────────────────
|
|
133
|
-
|
|
134
|
-
export function CellTextInput(props: EditableCellProps): React.ReactElement {
|
|
135
|
-
const { url, col, value, disabled } = props
|
|
136
|
-
const inputType = (col['inputType'] as string | undefined) ?? 'text'
|
|
137
|
-
const placeholder = col['inputPlaceholder'] as string | undefined
|
|
138
|
-
const step = col['inputStep'] as number | undefined
|
|
139
|
-
const minVal = col['inputMin'] as number | undefined
|
|
140
|
-
const maxVal = col['inputMax'] as number | undefined
|
|
141
|
-
const debounceMs = (col['debounceMs'] as number | undefined) ?? DEFAULT_DEBOUNCE_MS
|
|
142
|
-
const confirmMsg = col['confirm'] as string | undefined
|
|
143
|
-
|
|
144
|
-
const [localValue, setLocalValue] = useState<string>(() =>
|
|
145
|
-
value === null || value === undefined ? '' : String(value),
|
|
146
|
-
)
|
|
147
|
-
const committedRef = useRef<string>(localValue)
|
|
148
|
-
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
149
|
-
const seqRef = useRef(0)
|
|
150
|
-
const [pendingConfirm, setPendingConfirm] = useState<string | null>(null)
|
|
151
|
-
const { notify } = useToast()
|
|
152
|
-
|
|
153
|
-
useEffect(() => {
|
|
154
|
-
return () => { if (debounceRef.current) clearTimeout(debounceRef.current) }
|
|
155
|
-
}, [])
|
|
156
|
-
|
|
157
|
-
const commit = async (next: string): Promise<void> => {
|
|
158
|
-
if (next === committedRef.current) return
|
|
159
|
-
const prev = committedRef.current
|
|
160
|
-
committedRef.current = next
|
|
161
|
-
const seq = ++seqRef.current
|
|
162
|
-
const result = await patchCell(url, next)
|
|
163
|
-
if (seq !== seqRef.current) return // a newer commit has already fired
|
|
164
|
-
if (result.ok) {
|
|
165
|
-
// Success: keep the local + committed in sync. No success toast — saving
|
|
166
|
-
// every keystroke would be noise.
|
|
167
|
-
result.notifications?.forEach(n => notify(n))
|
|
168
|
-
} else {
|
|
169
|
-
committedRef.current = prev
|
|
170
|
-
setLocalValue(prev)
|
|
171
|
-
notify({
|
|
172
|
-
type: 'error',
|
|
173
|
-
title: "Couldn't save",
|
|
174
|
-
body: firstErrorMessage(result),
|
|
175
|
-
})
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const queueCommit = (next: string): void => {
|
|
180
|
-
if (debounceRef.current) clearTimeout(debounceRef.current)
|
|
181
|
-
if (confirmMsg) {
|
|
182
|
-
// Confirm-gated: don't send anything until the user accepts.
|
|
183
|
-
setPendingConfirm(next)
|
|
184
|
-
return
|
|
185
|
-
}
|
|
186
|
-
debounceRef.current = setTimeout(() => { void commit(next) }, debounceMs)
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const flushOnBlur = (): void => {
|
|
190
|
-
if (debounceRef.current) { clearTimeout(debounceRef.current); debounceRef.current = null }
|
|
191
|
-
if (confirmMsg) return // wait for the dialog
|
|
192
|
-
void commit(localValue)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
return (
|
|
196
|
-
<div className="px-2 py-1" data-no-row-nav>
|
|
197
|
-
<Input
|
|
198
|
-
type={inputType}
|
|
199
|
-
value={localValue}
|
|
200
|
-
placeholder={placeholder}
|
|
201
|
-
step={step}
|
|
202
|
-
min={minVal}
|
|
203
|
-
max={maxVal}
|
|
204
|
-
disabled={disabled}
|
|
205
|
-
onChange={(e) => { setLocalValue(e.target.value); queueCommit(e.target.value) }}
|
|
206
|
-
onBlur={flushOnBlur}
|
|
207
|
-
className="h-8"
|
|
208
|
-
/>
|
|
209
|
-
<ConfirmDialog
|
|
210
|
-
open={pendingConfirm !== null}
|
|
211
|
-
message={confirmMsg ?? ''}
|
|
212
|
-
onConfirm={() => {
|
|
213
|
-
const next = pendingConfirm!
|
|
214
|
-
setPendingConfirm(null)
|
|
215
|
-
void commit(next)
|
|
216
|
-
}}
|
|
217
|
-
onCancel={() => {
|
|
218
|
-
// Roll back the local state to the committed value.
|
|
219
|
-
setLocalValue(committedRef.current)
|
|
220
|
-
setPendingConfirm(null)
|
|
221
|
-
}}
|
|
222
|
-
/>
|
|
223
|
-
</div>
|
|
224
|
-
)
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// ─── Toggle cell ───────────────────────────────────────
|
|
228
|
-
|
|
229
|
-
export function CellToggle(props: EditableCellProps): React.ReactElement {
|
|
230
|
-
const { url, value, disabled, col } = props
|
|
231
|
-
const confirmMsg = col['confirm'] as string | undefined
|
|
232
|
-
const [localValue, setLocalValue] = useState<boolean>(() => Boolean(value))
|
|
233
|
-
const committedRef = useRef<boolean>(localValue)
|
|
234
|
-
const seqRef = useRef(0)
|
|
235
|
-
const [pendingConfirm, setPendingConfirm] = useState<boolean | null>(null)
|
|
236
|
-
const { notify } = useToast()
|
|
237
|
-
|
|
238
|
-
const commit = async (next: boolean): Promise<void> => {
|
|
239
|
-
const prev = committedRef.current
|
|
240
|
-
committedRef.current = next
|
|
241
|
-
const seq = ++seqRef.current
|
|
242
|
-
const result = await patchCell(url, next)
|
|
243
|
-
if (seq !== seqRef.current) return
|
|
244
|
-
if (result.ok) {
|
|
245
|
-
result.notifications?.forEach(n => notify(n))
|
|
246
|
-
} else {
|
|
247
|
-
committedRef.current = prev
|
|
248
|
-
setLocalValue(prev)
|
|
249
|
-
notify({
|
|
250
|
-
type: 'error',
|
|
251
|
-
title: "Couldn't save",
|
|
252
|
-
body: firstErrorMessage(result),
|
|
253
|
-
})
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const onChange = (next: boolean): void => {
|
|
258
|
-
setLocalValue(next)
|
|
259
|
-
if (confirmMsg) { setPendingConfirm(next); return }
|
|
260
|
-
void commit(next)
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return (
|
|
264
|
-
<div className="px-2 py-2" data-no-row-nav>
|
|
265
|
-
<Switch
|
|
266
|
-
checked={localValue}
|
|
267
|
-
disabled={disabled}
|
|
268
|
-
onCheckedChange={onChange}
|
|
269
|
-
/>
|
|
270
|
-
<ConfirmDialog
|
|
271
|
-
open={pendingConfirm !== null}
|
|
272
|
-
message={confirmMsg ?? ''}
|
|
273
|
-
onConfirm={() => {
|
|
274
|
-
const next = pendingConfirm!
|
|
275
|
-
setPendingConfirm(null)
|
|
276
|
-
void commit(next)
|
|
277
|
-
}}
|
|
278
|
-
onCancel={() => {
|
|
279
|
-
// Roll back to the committed value — flips the switch back.
|
|
280
|
-
setLocalValue(committedRef.current)
|
|
281
|
-
setPendingConfirm(null)
|
|
282
|
-
}}
|
|
283
|
-
/>
|
|
284
|
-
</div>
|
|
285
|
-
)
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// ─── Select cell ───────────────────────────────────────
|
|
289
|
-
|
|
290
|
-
export function CellSelect(props: EditableCellProps): React.ReactElement {
|
|
291
|
-
const { url, value, disabled, col, rowOptions } = props
|
|
292
|
-
// Per-row resolver wins over the column's static options. Both can be
|
|
293
|
-
// present: a column may set static options as a fallback that the
|
|
294
|
-
// resolver overrides per row.
|
|
295
|
-
const opts: Array<{ value: string; label: string }> =
|
|
296
|
-
rowOptions ??
|
|
297
|
-
(Array.isArray(col['selectOptions']) ? col['selectOptions'] as Array<{ value: string; label: string }> : [])
|
|
298
|
-
const nullable = col['selectNullable'] === true
|
|
299
|
-
const showPlaceholderOnce = col['selectablePlaceholder'] !== false // default true (keep showing)
|
|
300
|
-
const confirmMsg = col['confirm'] as string | undefined
|
|
301
|
-
|
|
302
|
-
const valueAsString = (v: unknown): string =>
|
|
303
|
-
v === null || v === undefined ? '' : String(v)
|
|
304
|
-
|
|
305
|
-
const [localValue, setLocalValue] = useState<string>(() => valueAsString(value))
|
|
306
|
-
const committedRef = useRef<string>(localValue)
|
|
307
|
-
const seqRef = useRef(0)
|
|
308
|
-
const [pendingConfirm, setPendingConfirm] = useState<string | null>(null)
|
|
309
|
-
const { notify } = useToast()
|
|
310
|
-
|
|
311
|
-
const commit = async (next: string): Promise<void> => {
|
|
312
|
-
const prev = committedRef.current
|
|
313
|
-
committedRef.current = next
|
|
314
|
-
const seq = ++seqRef.current
|
|
315
|
-
// Empty-string value with `nullable()` becomes `null` on the wire so
|
|
316
|
-
// the server-side coerce path treats it as a clear, not an option key.
|
|
317
|
-
const wire: string | null = next === '' && nullable ? null : next
|
|
318
|
-
const result = await patchCell(url, wire)
|
|
319
|
-
if (seq !== seqRef.current) return
|
|
320
|
-
if (result.ok) {
|
|
321
|
-
result.notifications?.forEach(n => notify(n))
|
|
322
|
-
} else {
|
|
323
|
-
committedRef.current = prev
|
|
324
|
-
setLocalValue(prev)
|
|
325
|
-
notify({
|
|
326
|
-
type: 'error',
|
|
327
|
-
title: "Couldn't save",
|
|
328
|
-
body: firstErrorMessage(result),
|
|
329
|
-
})
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
const onChange = (next: string): void => {
|
|
334
|
-
setLocalValue(next)
|
|
335
|
-
if (confirmMsg) { setPendingConfirm(next); return }
|
|
336
|
-
void commit(next)
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Hide the placeholder option once a value is set when the user opted out.
|
|
340
|
-
const showPlaceholder = showPlaceholderOnce || localValue === ''
|
|
341
|
-
const placeholderText = nullable ? '—' : 'Select…'
|
|
342
|
-
|
|
343
|
-
return (
|
|
344
|
-
<div className="px-2 py-1" data-no-row-nav>
|
|
345
|
-
<Select
|
|
346
|
-
value={localValue}
|
|
347
|
-
onValueChange={(v) => onChange(typeof v === 'string' ? v : '')}
|
|
348
|
-
disabled={disabled}
|
|
349
|
-
>
|
|
350
|
-
<SelectTrigger className="h-8 w-full">
|
|
351
|
-
<SelectValue placeholder={placeholderText} />
|
|
352
|
-
</SelectTrigger>
|
|
353
|
-
<SelectContent>
|
|
354
|
-
{nullable && showPlaceholder && (
|
|
355
|
-
<SelectItem value="">{placeholderText}</SelectItem>
|
|
356
|
-
)}
|
|
357
|
-
{opts.map(o => (
|
|
358
|
-
<SelectItem key={o.value} value={o.value}>{o.label}</SelectItem>
|
|
359
|
-
))}
|
|
360
|
-
</SelectContent>
|
|
361
|
-
</Select>
|
|
362
|
-
<ConfirmDialog
|
|
363
|
-
open={pendingConfirm !== null}
|
|
364
|
-
message={confirmMsg ?? ''}
|
|
365
|
-
onConfirm={() => {
|
|
366
|
-
const next = pendingConfirm!
|
|
367
|
-
setPendingConfirm(null)
|
|
368
|
-
void commit(next)
|
|
369
|
-
}}
|
|
370
|
-
onCancel={() => {
|
|
371
|
-
setLocalValue(committedRef.current)
|
|
372
|
-
setPendingConfirm(null)
|
|
373
|
-
}}
|
|
374
|
-
/>
|
|
375
|
-
</div>
|
|
376
|
-
)
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/** Discriminator for the SchemaRenderer cell switch — pick the right
|
|
380
|
-
* editable-cell renderer based on `columnType`. Returns `null` when the
|
|
381
|
-
* column is read-only so the caller falls through to `formatCell`. */
|
|
382
|
-
export function pickEditableCell(columnType: string): React.FC<EditableCellProps> | null {
|
|
383
|
-
switch (columnType) {
|
|
384
|
-
case 'textInput': return CellTextInput
|
|
385
|
-
case 'toggle': return CellToggle
|
|
386
|
-
case 'select': return CellSelect
|
|
387
|
-
default: return null
|
|
388
|
-
}
|
|
389
|
-
}
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import { describe, it } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
import { Pilotiq } from '../Pilotiq.js'
|
|
4
|
-
import { isNavItemActive } from './component-slots.js'
|
|
5
|
-
|
|
6
|
-
describe('Pilotiq.components({ nav, header, footer })', () => {
|
|
7
|
-
it('starts empty', () => {
|
|
8
|
-
const panel = Pilotiq.make('Admin')
|
|
9
|
-
assert.deepEqual(panel.getComponentSlots(), {})
|
|
10
|
-
})
|
|
11
|
-
|
|
12
|
-
it('stores a registered nav slot', () => {
|
|
13
|
-
const Nav = () => null
|
|
14
|
-
const panel = Pilotiq.make('Admin').components({ nav: Nav })
|
|
15
|
-
const slots = panel.getComponentSlots()
|
|
16
|
-
assert.equal(slots.nav, Nav)
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
it('stores a registered header slot', () => {
|
|
20
|
-
const Header = () => null
|
|
21
|
-
const panel = Pilotiq.make('Admin').components({ header: Header })
|
|
22
|
-
assert.equal(panel.getComponentSlots().header, Header)
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it('stores a registered footer slot', () => {
|
|
26
|
-
const Footer = () => null
|
|
27
|
-
const panel = Pilotiq.make('Admin').components({ footer: Footer })
|
|
28
|
-
assert.equal(panel.getComponentSlots().footer, Footer)
|
|
29
|
-
})
|
|
30
|
-
|
|
31
|
-
it('merges across multiple calls — later wins per slot, unset keys preserved', () => {
|
|
32
|
-
const A = () => null
|
|
33
|
-
const B = () => null
|
|
34
|
-
const panel = Pilotiq.make('Admin')
|
|
35
|
-
.components({ nav: A })
|
|
36
|
-
.components({}) // empty object keeps existing
|
|
37
|
-
.components({ nav: B }) // overrides
|
|
38
|
-
assert.equal(panel.getComponentSlots().nav, B)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('preserves unset slots when overriding only one slot', () => {
|
|
42
|
-
const Nav = () => null
|
|
43
|
-
const Header = () => null
|
|
44
|
-
const Footer = () => null
|
|
45
|
-
const Nav2 = () => null
|
|
46
|
-
const panel = Pilotiq.make('Admin')
|
|
47
|
-
.components({ nav: Nav, header: Header, footer: Footer })
|
|
48
|
-
.components({ nav: Nav2 }) // only nav changes
|
|
49
|
-
const slots = panel.getComponentSlots()
|
|
50
|
-
assert.equal(slots.nav, Nav2)
|
|
51
|
-
assert.equal(slots.header, Header)
|
|
52
|
-
assert.equal(slots.footer, Footer)
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it('allows registering all three slots together', () => {
|
|
56
|
-
const Nav = () => null
|
|
57
|
-
const Header = () => null
|
|
58
|
-
const Footer = () => null
|
|
59
|
-
const panel = Pilotiq.make('Admin')
|
|
60
|
-
.components({ nav: Nav, header: Header, footer: Footer })
|
|
61
|
-
const slots = panel.getComponentSlots()
|
|
62
|
-
assert.equal(slots.nav, Nav)
|
|
63
|
-
assert.equal(slots.header, Header)
|
|
64
|
-
assert.equal(slots.footer, Footer)
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('survives plugin registration without interference', () => {
|
|
68
|
-
const Nav = () => null
|
|
69
|
-
const panel = Pilotiq.make('Admin')
|
|
70
|
-
.components({ nav: Nav })
|
|
71
|
-
.use({ name: 'noop', register() { /* no-op */ } })
|
|
72
|
-
assert.equal(panel.getComponentSlots().nav, Nav)
|
|
73
|
-
})
|
|
74
|
-
})
|
|
75
|
-
|
|
76
|
-
describe('isNavItemActive', () => {
|
|
77
|
-
const basePath = '/admin'
|
|
78
|
-
|
|
79
|
-
it('returns false when currentPath is undefined', () => {
|
|
80
|
-
assert.equal(isNavItemActive('/admin/users', undefined, basePath), false)
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
it('matches the dashboard URL only on exact equality', () => {
|
|
84
|
-
assert.equal(isNavItemActive(basePath, basePath, basePath), true)
|
|
85
|
-
// Dashboard URL is a prefix of every panel URL; it must NOT light up
|
|
86
|
-
// for non-root pages — otherwise it'd always read as active.
|
|
87
|
-
assert.equal(isNavItemActive(basePath, '/admin/users', basePath), false)
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it('matches non-dashboard URLs on exact equality', () => {
|
|
91
|
-
assert.equal(isNavItemActive('/admin/users', '/admin/users', basePath), true)
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it('matches non-dashboard URLs as a prefix followed by `/`', () => {
|
|
95
|
-
assert.equal(isNavItemActive('/admin/users', '/admin/users/42', basePath), true)
|
|
96
|
-
assert.equal(isNavItemActive('/admin/users', '/admin/users/42/edit', basePath), true)
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
it('does NOT match a sibling URL that shares a prefix without `/`', () => {
|
|
100
|
-
// `/admin/users` must not activate when on `/admin/user` (singular).
|
|
101
|
-
assert.equal(isNavItemActive('/admin/user', '/admin/users', basePath), false)
|
|
102
|
-
})
|
|
103
|
-
})
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import type * as React from 'react'
|
|
2
|
-
import type { NavItem } from '../pageData.js'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Props passed to a component registered at `Pilotiq.components({ nav })`.
|
|
6
|
-
*
|
|
7
|
-
* The component owns the entire nav region — for `SidebarLayout` that
|
|
8
|
-
* means the `<SidebarContent>` body (the panel chrome's header / footer
|
|
9
|
-
* still render); for `TopbarLayout` that means the `<nav>` element
|
|
10
|
-
* between the brand cluster and the right-side controls.
|
|
11
|
-
*
|
|
12
|
-
* `navigation` is the pre-grouped, pre-sorted tree built by
|
|
13
|
-
* `panelInfo()` — same shape the default renderers consume — so a
|
|
14
|
-
* custom nav can opt into the framework's `navigationGroup` / `sort` /
|
|
15
|
-
* badge metadata for free, or ignore it and walk a custom tree.
|
|
16
|
-
*/
|
|
17
|
-
export interface NavComponentProps {
|
|
18
|
-
/**
|
|
19
|
-
* Pre-grouped navigation items. May be empty when nothing the user
|
|
20
|
-
* can access is registered. Items with `children` represent nested
|
|
21
|
-
* sub-navigation (the default sidebar renders these under
|
|
22
|
-
* `SidebarMenuSub`).
|
|
23
|
-
*/
|
|
24
|
-
navigation: NavItem[]
|
|
25
|
-
/** Panel base path (e.g. `/admin`). */
|
|
26
|
-
basePath: string
|
|
27
|
-
/** Current request pathname — undefined when not in a request scope
|
|
28
|
-
* (e.g. unit-testing the layout standalone). */
|
|
29
|
-
currentPath?: string
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Props passed to a component registered at `Pilotiq.components({ header })`.
|
|
34
|
-
*
|
|
35
|
-
* The component owns the entire `<header>` chrome bar — in `SidebarLayout`
|
|
36
|
-
* that's the top bar above the main content (sidebar trigger, search,
|
|
37
|
-
* theme toggle, bell, user menu); in `TopbarLayout` that's the *whole*
|
|
38
|
-
* topbar including the brand cluster and the nav region. Setting `header`
|
|
39
|
-
* on `TopbarLayout` makes the `nav` slot irrelevant for that layout —
|
|
40
|
-
* the consumer's header owns everything between page edges.
|
|
41
|
-
*
|
|
42
|
-
* Render hooks that splice INSIDE the default header
|
|
43
|
-
* (`panels::topbar.start`, `panels::topbar.end`, `panels::user-menu.before`,
|
|
44
|
-
* `panels::user-menu.after`) don't fire when the header is replaced —
|
|
45
|
-
* the surrounding container is gone, so there's nowhere to splice into.
|
|
46
|
-
* Hooks rooted outside the header (`panels::sidebar.start` / `.footer`,
|
|
47
|
-
* `panels::sidebar.nav.start` / `.end`, `panels::footer`) keep firing.
|
|
48
|
-
*
|
|
49
|
-
* Shape mirrors `NavComponentProps` so a header that wants to render
|
|
50
|
-
* the nav inline (the TopbarLayout case) can do so without juggling a
|
|
51
|
-
* second slot.
|
|
52
|
-
*/
|
|
53
|
-
export interface HeaderComponentProps {
|
|
54
|
-
/**
|
|
55
|
-
* Pre-grouped navigation items — same tree the `nav` slot receives.
|
|
56
|
-
* Header consumers that want to mount the topbar nav inline read
|
|
57
|
-
* this; sidebar-layout consumers can ignore it.
|
|
58
|
-
*/
|
|
59
|
-
navigation: NavItem[]
|
|
60
|
-
/** Panel base path (e.g. `/admin`). */
|
|
61
|
-
basePath: string
|
|
62
|
-
/** Current request pathname — undefined when not in a request scope. */
|
|
63
|
-
currentPath?: string
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Props passed to a component registered at `Pilotiq.components({ footer })`.
|
|
68
|
-
*
|
|
69
|
-
* The component mounts as a `<footer>` element BELOW the main content
|
|
70
|
-
* area — outside the scrolling region in both layouts. It is a separate
|
|
71
|
-
* surface from the `panels::footer` render hook, which continues to
|
|
72
|
-
* fire INSIDE the main content area (use the hook to append per-page
|
|
73
|
-
* trailing chrome; use this slot for site-chrome that frames every
|
|
74
|
-
* page like a status bar or copyright row).
|
|
75
|
-
*/
|
|
76
|
-
export interface FooterComponentProps {
|
|
77
|
-
/** Panel base path (e.g. `/admin`). */
|
|
78
|
-
basePath: string
|
|
79
|
-
/** Current request pathname — undefined when not in a request scope. */
|
|
80
|
-
currentPath?: string
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Registry of build-time component overrides registered through
|
|
85
|
-
* `Pilotiq.components({ nav, header, footer, … })`. The Vite plugin
|
|
86
|
-
* harvests the actual React refs into `pages/(pilotiq)/_components.ts`
|
|
87
|
-
* and forwards them to `<AppShell>`; component refs never travel over
|
|
88
|
-
* the wire.
|
|
89
|
-
*
|
|
90
|
-
* The shape is open-ended so additional slots can land without a
|
|
91
|
-
* breaking change when a concrete consumer asks for them.
|
|
92
|
-
*/
|
|
93
|
-
export interface ComponentSlotRegistry {
|
|
94
|
-
nav?: React.ComponentType<NavComponentProps>
|
|
95
|
-
header?: React.ComponentType<HeaderComponentProps>
|
|
96
|
-
footer?: React.ComponentType<FooterComponentProps>
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Active-link match identical to the default sidebar's. Exported so
|
|
101
|
-
* custom nav components can reuse the framework's longest-prefix
|
|
102
|
-
* semantics: the dashboard URL (`basePath`) only matches on exact
|
|
103
|
-
* equality — otherwise it would light up on every panel page — and
|
|
104
|
-
* non-dashboard URLs match as a prefix followed by `/` or end-of-string
|
|
105
|
-
* so `/admin/users` doesn't activate `/admin/user`.
|
|
106
|
-
*/
|
|
107
|
-
export function isNavItemActive(
|
|
108
|
-
url: string,
|
|
109
|
-
currentPath: string | undefined,
|
|
110
|
-
basePath: string,
|
|
111
|
-
): boolean {
|
|
112
|
-
if (!currentPath) return false
|
|
113
|
-
if (url === basePath) return currentPath === basePath
|
|
114
|
-
if (url === currentPath) return true
|
|
115
|
-
return currentPath.startsWith(url + '/')
|
|
116
|
-
}
|