@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,501 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react'
|
|
2
|
-
import type { ElementMeta } from '../../schema/Element.js'
|
|
3
|
-
import { CheckIcon, CircleIcon, CopyIcon } from 'lucide-react'
|
|
4
|
-
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/tooltip.js'
|
|
5
|
-
import { getEntryComponent } from '../../entries/registry.js'
|
|
6
|
-
import {
|
|
7
|
-
BADGE_COLOR_CLASSES,
|
|
8
|
-
COLUMN_COLOR_CLASSES,
|
|
9
|
-
TEXT_COLOR_CLASSES,
|
|
10
|
-
TEXT_SIZE_CLASSES,
|
|
11
|
-
TEXT_WEIGHT_CLASSES,
|
|
12
|
-
} from './constants.js'
|
|
13
|
-
import { resolveIcon } from './helpers.js'
|
|
14
|
-
import { applyColumnFormat } from './columnFormat.js'
|
|
15
|
-
|
|
16
|
-
// ─── Entry rendering (Plan #16 — read-only label/value pairs) ───
|
|
17
|
-
|
|
18
|
-
/** Coerce a `KeyValueEntry` state value (object | JSON string | …) into a
|
|
19
|
-
* flat record. Returns `null` when the value is empty or non-decodable. */
|
|
20
|
-
function normalizeKeyValueValue(value: unknown): Record<string, unknown> | null {
|
|
21
|
-
if (value === null || value === undefined || value === '') return null
|
|
22
|
-
if (typeof value === 'string') {
|
|
23
|
-
try {
|
|
24
|
-
const parsed = JSON.parse(value) as unknown
|
|
25
|
-
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
26
|
-
return parsed as Record<string, unknown>
|
|
27
|
-
}
|
|
28
|
-
} catch {
|
|
29
|
-
// Non-JSON string — fall through to null so the renderer shows the
|
|
30
|
-
// fallback rather than misrepresenting it as a one-row map.
|
|
31
|
-
}
|
|
32
|
-
return null
|
|
33
|
-
}
|
|
34
|
-
if (Array.isArray(value)) return null
|
|
35
|
-
if (typeof value === 'object') return value as Record<string, unknown>
|
|
36
|
-
return null
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/** Render a single kv cell value — primitives become their string form;
|
|
40
|
-
* nested objects/arrays JSON-stringify for compactness. */
|
|
41
|
-
function formatKeyValueCell(value: unknown): string {
|
|
42
|
-
if (value === null || value === undefined) return ''
|
|
43
|
-
if (typeof value === 'object') return JSON.stringify(value)
|
|
44
|
-
return String(value)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Plan #16 — read-only label-value pair for `Resource.detail()` schemas.
|
|
49
|
-
* Dispatches on `meta.entryType` (`'text' | 'badge' | 'icon' | 'image' | 'keyValue' | 'color'`).
|
|
50
|
-
* Wraps the rendered value in `<EntryShell>` for the shared chrome
|
|
51
|
-
* (label / helperText / tooltip / copyable trigger).
|
|
52
|
-
*
|
|
53
|
-
* `renderElement` is injected so the `repeatable` branch can recurse into
|
|
54
|
-
* row children without re-importing the main switch.
|
|
55
|
-
*/
|
|
56
|
-
export function renderEntry(
|
|
57
|
-
el: ElementMeta,
|
|
58
|
-
index: number,
|
|
59
|
-
renderElement: (el: ElementMeta, index: number) => React.ReactNode,
|
|
60
|
-
): React.ReactNode {
|
|
61
|
-
const entryType = String(el['entryType'] ?? 'text')
|
|
62
|
-
const value = el['value']
|
|
63
|
-
const fallback = el['default'] ? String(el['default']) : '—'
|
|
64
|
-
|
|
65
|
-
let body: React.ReactNode
|
|
66
|
-
switch (entryType) {
|
|
67
|
-
case 'text': {
|
|
68
|
-
const formatted = el['_formatted'] !== undefined
|
|
69
|
-
? String(el['_formatted'])
|
|
70
|
-
: (el['format']
|
|
71
|
-
? applyColumnFormat(value, el['format'] as { kind: string; [k: string]: unknown })
|
|
72
|
-
: (value === null || value === undefined || value === '' ? '' : String(value)))
|
|
73
|
-
|
|
74
|
-
const display = formatted === '' ? fallback : formatted
|
|
75
|
-
const isFallback = formatted === ''
|
|
76
|
-
const isRichText = el['richtext'] === true && !isFallback
|
|
77
|
-
const sizeKey = el['size'] ? String(el['size']) : 'sm'
|
|
78
|
-
const colorKey = el['color'] ? String(el['color']) : (isFallback ? 'muted' : 'default')
|
|
79
|
-
const weightKey = el['weight'] ? String(el['weight']) : 'normal'
|
|
80
|
-
const sizeCls = TEXT_SIZE_CLASSES[sizeKey] ?? 'text-sm'
|
|
81
|
-
const colorCls = TEXT_COLOR_CLASSES[colorKey] ?? ''
|
|
82
|
-
const weightCls = TEXT_WEIGHT_CLASSES[weightKey] ?? ''
|
|
83
|
-
const lineClamp = el['lineClamp'] as number | undefined
|
|
84
|
-
const wrap = el['wrap'] === true
|
|
85
|
-
|
|
86
|
-
const style: React.CSSProperties = {}
|
|
87
|
-
if (lineClamp !== undefined) {
|
|
88
|
-
style.display = '-webkit-box'
|
|
89
|
-
style.WebkitLineClamp = lineClamp
|
|
90
|
-
;(style as { WebkitBoxOrient?: string }).WebkitBoxOrient = 'vertical'
|
|
91
|
-
style.overflow = 'hidden'
|
|
92
|
-
}
|
|
93
|
-
const wrapCls = wrap ? 'whitespace-pre-wrap' : (lineClamp !== undefined ? '' : 'whitespace-nowrap')
|
|
94
|
-
|
|
95
|
-
if (isRichText) {
|
|
96
|
-
// Server-rendered HTML from a registered richtext renderer (e.g.
|
|
97
|
-
// `@pilotiq/tiptap`). Wrap in `prose` for sensible default
|
|
98
|
-
// styling — matches the read-only `Markdown` / `Html` primes.
|
|
99
|
-
const proseSize = sizeKey === 'lg' || sizeKey === 'xl'
|
|
100
|
-
? 'prose-lg'
|
|
101
|
-
: sizeKey === 'sm' || sizeKey === 'xs'
|
|
102
|
-
? 'prose-sm'
|
|
103
|
-
: ''
|
|
104
|
-
body = (
|
|
105
|
-
<div
|
|
106
|
-
className={`prose max-w-none dark:prose-invert ${proseSize} ${colorCls} ${weightCls}`.trim()}
|
|
107
|
-
style={style}
|
|
108
|
-
dangerouslySetInnerHTML={{ __html: display }}
|
|
109
|
-
/>
|
|
110
|
-
)
|
|
111
|
-
break
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
body = (
|
|
115
|
-
<span className={`${sizeCls} ${colorCls} ${weightCls} ${wrapCls}`.trim()} style={style}>
|
|
116
|
-
{display}
|
|
117
|
-
</span>
|
|
118
|
-
)
|
|
119
|
-
break
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
case 'badge': {
|
|
123
|
-
const isBlank = value === null || value === undefined || value === ''
|
|
124
|
-
if (isBlank) {
|
|
125
|
-
body = <span className="text-sm text-muted-foreground">{fallback}</span>
|
|
126
|
-
break
|
|
127
|
-
}
|
|
128
|
-
const map = (el['colors'] as Record<string, string> | undefined) ?? {}
|
|
129
|
-
const colorKey = map[String(value)] ?? 'gray'
|
|
130
|
-
const cls = BADGE_COLOR_CLASSES[colorKey] ?? BADGE_COLOR_CLASSES['gray']
|
|
131
|
-
body = (
|
|
132
|
-
<span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${cls}`}>
|
|
133
|
-
{String(value)}
|
|
134
|
-
</span>
|
|
135
|
-
)
|
|
136
|
-
break
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
case 'icon': {
|
|
140
|
-
const isBlank = value === null || value === undefined || value === ''
|
|
141
|
-
const map = (el['options'] as Record<string, { icon: string; color?: string; label?: string }> | undefined) ?? {}
|
|
142
|
-
const opt = isBlank ? undefined : map[String(value)]
|
|
143
|
-
if (!opt) {
|
|
144
|
-
body = <span className="text-sm text-muted-foreground">{fallback}</span>
|
|
145
|
-
break
|
|
146
|
-
}
|
|
147
|
-
const Icon = resolveIcon(opt.icon) ?? CircleIcon
|
|
148
|
-
const colorClass = opt.color ? (COLUMN_COLOR_CLASSES[opt.color] ?? '') : ''
|
|
149
|
-
const ariaLabel = opt.label ?? String(value)
|
|
150
|
-
body = <Icon className={`inline size-5 ${colorClass}`.trim()} aria-label={ariaLabel} />
|
|
151
|
-
break
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
case 'image': {
|
|
155
|
-
const isBlank = value === null || value === undefined || value === ''
|
|
156
|
-
if (isBlank) {
|
|
157
|
-
body = <span className="text-sm text-muted-foreground">{fallback}</span>
|
|
158
|
-
break
|
|
159
|
-
}
|
|
160
|
-
const url = String(value)
|
|
161
|
-
const width = (el['imageWidth'] as number | undefined) ?? (el['imageSize'] as number | undefined) ?? 64
|
|
162
|
-
const height = (el['imageHeight'] as number | undefined) ?? (el['imageSize'] as number | undefined) ?? 64
|
|
163
|
-
const shape = String(el['imageShape'] ?? 'rounded')
|
|
164
|
-
const shapeCls = shape === 'circle' ? 'rounded-full' : shape === 'square' ? '' : 'rounded-md'
|
|
165
|
-
body = (
|
|
166
|
-
<img
|
|
167
|
-
src={url}
|
|
168
|
-
alt=""
|
|
169
|
-
width={width}
|
|
170
|
-
height={height}
|
|
171
|
-
className={`inline-block object-cover ${shapeCls}`.trim()}
|
|
172
|
-
/>
|
|
173
|
-
)
|
|
174
|
-
break
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
case 'keyValue': {
|
|
178
|
-
const parsed = normalizeKeyValueValue(value)
|
|
179
|
-
const keys = parsed ? Object.keys(parsed) : []
|
|
180
|
-
if (!parsed || keys.length === 0) {
|
|
181
|
-
body = <span className="text-sm text-muted-foreground">{fallback}</span>
|
|
182
|
-
break
|
|
183
|
-
}
|
|
184
|
-
const keyLabel = el['keyLabel'] ? String(el['keyLabel']) : 'Key'
|
|
185
|
-
const valueLabel = el['valueLabel'] ? String(el['valueLabel']) : 'Value'
|
|
186
|
-
body = (
|
|
187
|
-
<table className="w-full border border-border text-sm">
|
|
188
|
-
<thead>
|
|
189
|
-
<tr className="bg-muted/50 text-left text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
190
|
-
<th className="border-b border-border px-2 py-1">{keyLabel}</th>
|
|
191
|
-
<th className="border-b border-border px-2 py-1">{valueLabel}</th>
|
|
192
|
-
</tr>
|
|
193
|
-
</thead>
|
|
194
|
-
<tbody>
|
|
195
|
-
{keys.map(k => (
|
|
196
|
-
<tr key={k} className="border-t border-border first:border-t-0">
|
|
197
|
-
<td className="px-2 py-1 align-top font-mono text-xs">{k}</td>
|
|
198
|
-
<td className="px-2 py-1 align-top font-mono text-xs break-all">
|
|
199
|
-
{formatKeyValueCell(parsed[k])}
|
|
200
|
-
</td>
|
|
201
|
-
</tr>
|
|
202
|
-
))}
|
|
203
|
-
</tbody>
|
|
204
|
-
</table>
|
|
205
|
-
)
|
|
206
|
-
break
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
case 'color': {
|
|
210
|
-
const isBlank = value === null || value === undefined || value === ''
|
|
211
|
-
if (isBlank) {
|
|
212
|
-
body = <span className="text-sm text-muted-foreground">{fallback}</span>
|
|
213
|
-
break
|
|
214
|
-
}
|
|
215
|
-
const hex = String(value)
|
|
216
|
-
const width = (el['colorWidth'] as number | undefined) ?? (el['colorSize'] as number | undefined) ?? 24
|
|
217
|
-
const height = (el['colorHeight'] as number | undefined) ?? (el['colorSize'] as number | undefined) ?? 24
|
|
218
|
-
const shape = String(el['colorShape'] ?? 'rounded')
|
|
219
|
-
const shapeCls = shape === 'circle' ? 'rounded-full' : shape === 'square' ? '' : 'rounded-md'
|
|
220
|
-
const showValue = el['showValue'] !== false
|
|
221
|
-
body = (
|
|
222
|
-
<span className="inline-flex items-center gap-2">
|
|
223
|
-
<span
|
|
224
|
-
className={`inline-block border border-border ${shapeCls}`.trim()}
|
|
225
|
-
style={{ width, height, backgroundColor: hex }}
|
|
226
|
-
aria-label={hex}
|
|
227
|
-
/>
|
|
228
|
-
{showValue && (
|
|
229
|
-
<span className="font-mono text-xs text-muted-foreground">{hex}</span>
|
|
230
|
-
)}
|
|
231
|
-
</span>
|
|
232
|
-
)
|
|
233
|
-
break
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
case 'code': {
|
|
237
|
-
const isBlank = value === null || value === undefined || value === ''
|
|
238
|
-
if (isBlank) {
|
|
239
|
-
body = <span className="text-sm text-muted-foreground">{fallback}</span>
|
|
240
|
-
break
|
|
241
|
-
}
|
|
242
|
-
const text = typeof value === 'string' ? value : String(value)
|
|
243
|
-
const lang = el['language'] ? String(el['language']) : undefined
|
|
244
|
-
body = (
|
|
245
|
-
<pre
|
|
246
|
-
className="rounded-md border border-border bg-muted/40 p-3 text-xs overflow-x-auto"
|
|
247
|
-
data-language={lang}
|
|
248
|
-
>
|
|
249
|
-
<code className="font-mono">{text}</code>
|
|
250
|
-
</pre>
|
|
251
|
-
)
|
|
252
|
-
break
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
case 'component': {
|
|
256
|
-
const componentName = String(el['component'] ?? '')
|
|
257
|
-
if (!componentName) {
|
|
258
|
-
body = (
|
|
259
|
-
<EntryComponentError>
|
|
260
|
-
ComponentEntry is missing its <code className="font-mono">component</code> name —
|
|
261
|
-
set <code className="font-mono">static componentName = '...'</code> on the
|
|
262
|
-
subclass or call <code className="font-mono">.component('...')</code> in the
|
|
263
|
-
fluent form.
|
|
264
|
-
</EntryComponentError>
|
|
265
|
-
)
|
|
266
|
-
break
|
|
267
|
-
}
|
|
268
|
-
const Component = getEntryComponent(componentName)
|
|
269
|
-
if (!Component) {
|
|
270
|
-
body = (
|
|
271
|
-
<EntryComponentError>
|
|
272
|
-
No component registered under name <code className="font-mono">{componentName}</code>.
|
|
273
|
-
Register it at app boot:
|
|
274
|
-
<pre className="mt-2 overflow-x-auto rounded bg-amber-100/60 p-2 text-xs dark:bg-amber-900/30">{`import { registerEntryComponents } from '@pilotiq/pilotiq/entries'\nregisterEntryComponents({ ${componentName}: ${componentName} })`}</pre>
|
|
275
|
-
</EntryComponentError>
|
|
276
|
-
)
|
|
277
|
-
break
|
|
278
|
-
}
|
|
279
|
-
// Render-time errors propagate to React's nearest error boundary —
|
|
280
|
-
// surfacing them inline here would require wrapping every entry in
|
|
281
|
-
// its own boundary, which v1 doesn't ship. The two pre-render
|
|
282
|
-
// sentinels above (missing name / missing registration) cover the
|
|
283
|
-
// typical wiring mistakes.
|
|
284
|
-
body = <Component value={value} />
|
|
285
|
-
break
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
case 'repeatable': {
|
|
289
|
-
// Read-only sibling of `Repeater`. Reads `meta.rows` (resolved by
|
|
290
|
-
// `resolveRepeatableRows`) and dispatches on the chosen layout —
|
|
291
|
-
// `table > grid > stack`. Empty / non-array state falls through to
|
|
292
|
-
// the inherited `default()` placeholder, same as every other entry.
|
|
293
|
-
const rows = (el['rows'] as Array<{ id: string; children: ElementMeta[] }> | undefined) ?? []
|
|
294
|
-
if (rows.length === 0) {
|
|
295
|
-
body = <span className="text-sm text-muted-foreground">{fallback}</span>
|
|
296
|
-
break
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
const tableCfg = el['table'] as { columns: Array<{ label: string; alignment?: 'left' | 'center' | 'right'; width?: string }> } | undefined
|
|
300
|
-
const gridN = el['grid'] as number | undefined
|
|
301
|
-
const innerCols = el['columns'] as number | undefined
|
|
302
|
-
const contained = el['contained'] !== false
|
|
303
|
-
|
|
304
|
-
if (tableCfg && tableCfg.columns.length > 0) {
|
|
305
|
-
const cols = tableCfg.columns
|
|
306
|
-
body = (
|
|
307
|
-
<table className="w-full border border-border text-sm">
|
|
308
|
-
{cols.some(c => c.width) && (
|
|
309
|
-
<colgroup>
|
|
310
|
-
{cols.map((c, i) => (
|
|
311
|
-
<col key={i} style={c.width ? { width: c.width } : undefined} />
|
|
312
|
-
))}
|
|
313
|
-
</colgroup>
|
|
314
|
-
)}
|
|
315
|
-
<thead>
|
|
316
|
-
<tr className="bg-muted/50 text-left text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
317
|
-
{cols.map((c, i) => (
|
|
318
|
-
<th
|
|
319
|
-
key={i}
|
|
320
|
-
className={`border-b border-border px-2 py-1 ${c.alignment === 'right' ? 'text-right' : c.alignment === 'center' ? 'text-center' : ''}`.trim()}
|
|
321
|
-
>
|
|
322
|
-
{c.label}
|
|
323
|
-
</th>
|
|
324
|
-
))}
|
|
325
|
-
</tr>
|
|
326
|
-
</thead>
|
|
327
|
-
<tbody>
|
|
328
|
-
{rows.map(row => (
|
|
329
|
-
<tr key={row.id} className="border-t border-border first:border-t-0 align-top">
|
|
330
|
-
{row.children.map((child, i) => {
|
|
331
|
-
const align = cols[i]?.alignment
|
|
332
|
-
const alignCls = align === 'right' ? 'text-right' : align === 'center' ? 'text-center' : ''
|
|
333
|
-
return (
|
|
334
|
-
<td key={i} className={`px-2 py-1 ${alignCls}`.trim()}>
|
|
335
|
-
{renderElement(child, i)}
|
|
336
|
-
</td>
|
|
337
|
-
)
|
|
338
|
-
})}
|
|
339
|
-
</tr>
|
|
340
|
-
))}
|
|
341
|
-
</tbody>
|
|
342
|
-
</table>
|
|
343
|
-
)
|
|
344
|
-
break
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
const cardCls = contained
|
|
348
|
-
? 'rounded-md border border-border p-3 bg-background'
|
|
349
|
-
: ''
|
|
350
|
-
const innerColsCls = innerCols && innerCols >= 2
|
|
351
|
-
? `grid gap-3 grid-cols-1 md:grid-cols-${Math.min(innerCols, 6)}`
|
|
352
|
-
: 'space-y-2'
|
|
353
|
-
|
|
354
|
-
const cards = rows.map(row => (
|
|
355
|
-
<div key={row.id} className={`${cardCls} ${innerColsCls}`.trim()}>
|
|
356
|
-
{row.children.map((child, i) => renderElement(child, i))}
|
|
357
|
-
</div>
|
|
358
|
-
))
|
|
359
|
-
|
|
360
|
-
if (gridN && gridN >= 2) {
|
|
361
|
-
const cap = Math.min(gridN, 6)
|
|
362
|
-
body = (
|
|
363
|
-
<div className={`w-full grid gap-3 grid-cols-1 md:grid-cols-${cap}`}>
|
|
364
|
-
{cards}
|
|
365
|
-
</div>
|
|
366
|
-
)
|
|
367
|
-
break
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
body = <div className="w-full space-y-3">{cards}</div>
|
|
371
|
-
break
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
default:
|
|
375
|
-
body = <span className="text-sm text-muted-foreground">{fallback}</span>
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
const copyable = el['copyable'] as { label?: string } | undefined
|
|
379
|
-
const copyValue = el['_formatted'] !== undefined
|
|
380
|
-
? String(el['_formatted'])
|
|
381
|
-
: value === null || value === undefined
|
|
382
|
-
? ''
|
|
383
|
-
: typeof value === 'object'
|
|
384
|
-
? JSON.stringify(value)
|
|
385
|
-
: String(value)
|
|
386
|
-
|
|
387
|
-
return (
|
|
388
|
-
<EntryShell
|
|
389
|
-
key={index}
|
|
390
|
-
el={el}
|
|
391
|
-
copyValue={copyable !== undefined ? copyValue : undefined}
|
|
392
|
-
copyableLabel={copyable?.label}
|
|
393
|
-
>
|
|
394
|
-
{body}
|
|
395
|
-
</EntryShell>
|
|
396
|
-
)
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
interface EntryShellProps {
|
|
400
|
-
el: ElementMeta
|
|
401
|
-
copyValue?: string | undefined
|
|
402
|
-
copyableLabel?: string | undefined
|
|
403
|
-
children: React.ReactNode
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
function EntryShell({ el, copyValue, copyableLabel, children }: EntryShellProps): React.ReactNode {
|
|
407
|
-
const label = String(el['label'] ?? '')
|
|
408
|
-
const helperText = el['helperText'] ? String(el['helperText']) : undefined
|
|
409
|
-
const tooltipText = el['tooltip'] ? String(el['tooltip']) : undefined
|
|
410
|
-
const inline = el['inlineLabel'] === true
|
|
411
|
-
|
|
412
|
-
const labelNode = label ? (
|
|
413
|
-
<div className="flex items-center gap-1.5 text-sm font-medium text-muted-foreground">
|
|
414
|
-
<span>{label}</span>
|
|
415
|
-
{tooltipText && <EntryTooltip text={tooltipText} />}
|
|
416
|
-
</div>
|
|
417
|
-
) : null
|
|
418
|
-
|
|
419
|
-
const valueRow = (
|
|
420
|
-
<div className="flex items-center gap-2">
|
|
421
|
-
{children}
|
|
422
|
-
{copyValue !== undefined && (
|
|
423
|
-
<EntryCopyButton text={copyValue} label={copyableLabel ?? 'Copy'} />
|
|
424
|
-
)}
|
|
425
|
-
</div>
|
|
426
|
-
)
|
|
427
|
-
|
|
428
|
-
if (inline) {
|
|
429
|
-
return (
|
|
430
|
-
<div className="flex items-baseline gap-3">
|
|
431
|
-
{labelNode && <div className="min-w-32">{labelNode}</div>}
|
|
432
|
-
<div className="min-w-0 flex-1">
|
|
433
|
-
{valueRow}
|
|
434
|
-
{helperText && <p className="mt-1 text-xs text-muted-foreground">{helperText}</p>}
|
|
435
|
-
</div>
|
|
436
|
-
</div>
|
|
437
|
-
)
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
return (
|
|
441
|
-
<div className="space-y-1">
|
|
442
|
-
{labelNode}
|
|
443
|
-
{valueRow}
|
|
444
|
-
{helperText && <p className="text-xs text-muted-foreground">{helperText}</p>}
|
|
445
|
-
</div>
|
|
446
|
-
)
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
function EntryComponentError({ children }: { children: React.ReactNode }): React.ReactNode {
|
|
450
|
-
return (
|
|
451
|
-
<div
|
|
452
|
-
role="alert"
|
|
453
|
-
className="rounded-md border border-amber-500/40 bg-amber-50 p-3 text-sm text-amber-800 dark:bg-amber-950/30 dark:text-amber-200"
|
|
454
|
-
>
|
|
455
|
-
{children}
|
|
456
|
-
</div>
|
|
457
|
-
)
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
function EntryTooltip({ text }: { text: string }): React.ReactNode {
|
|
461
|
-
const trigger = (
|
|
462
|
-
<button
|
|
463
|
-
type="button"
|
|
464
|
-
className="inline-flex h-3.5 w-3.5 items-center justify-center rounded-full border text-[10px] text-muted-foreground"
|
|
465
|
-
aria-label={text}
|
|
466
|
-
>
|
|
467
|
-
?
|
|
468
|
-
</button>
|
|
469
|
-
)
|
|
470
|
-
return (
|
|
471
|
-
<TooltipProvider>
|
|
472
|
-
<Tooltip>
|
|
473
|
-
<TooltipTrigger render={() => trigger} />
|
|
474
|
-
<TooltipContent>{text}</TooltipContent>
|
|
475
|
-
</Tooltip>
|
|
476
|
-
</TooltipProvider>
|
|
477
|
-
)
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
function EntryCopyButton({ text, label }: { text: string; label: string }): React.ReactNode {
|
|
481
|
-
const [copied, setCopied] = useState(false)
|
|
482
|
-
const handleClick = () => {
|
|
483
|
-
if (typeof navigator !== 'undefined' && navigator.clipboard) {
|
|
484
|
-
navigator.clipboard.writeText(text).then(() => {
|
|
485
|
-
setCopied(true)
|
|
486
|
-
setTimeout(() => setCopied(false), 1500)
|
|
487
|
-
}).catch(() => { /* ignore — older browser / permission denied */ })
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
return (
|
|
491
|
-
<button
|
|
492
|
-
type="button"
|
|
493
|
-
onClick={handleClick}
|
|
494
|
-
aria-label={label}
|
|
495
|
-
title={label}
|
|
496
|
-
className="inline-flex h-6 w-6 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-muted"
|
|
497
|
-
>
|
|
498
|
-
{copied ? <CheckIcon className="size-3.5" /> : <CopyIcon className="size-3.5" />}
|
|
499
|
-
</button>
|
|
500
|
-
)
|
|
501
|
-
}
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import React, { useEffect, useState } from 'react'
|
|
2
|
-
import type { ElementMeta } from '../../schema/Element.js'
|
|
3
|
-
import { readStoredFlag, writeStoredFlag } from '../persistedState.js'
|
|
4
|
-
import { layoutClasses, resolveIcon } from './helpers.js'
|
|
5
|
-
|
|
6
|
-
// ─── Section (stateful when collapsible) ────────────────────
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* `Section` chrome with optional collapse + persisted open/closed state.
|
|
10
|
-
* Renders a card-style border, header (title / description / icon / badge
|
|
11
|
-
* / after-header actions), body, and the collapse toggle when enabled.
|
|
12
|
-
*
|
|
13
|
-
* `renderElement` is injected so the body can re-enter the main switch
|
|
14
|
-
* without creating a SchemaRenderer ↔ SectionRenderer import cycle.
|
|
15
|
-
*/
|
|
16
|
-
export function SectionRenderer({
|
|
17
|
-
el,
|
|
18
|
-
index,
|
|
19
|
-
renderElement,
|
|
20
|
-
}: {
|
|
21
|
-
el: ElementMeta
|
|
22
|
-
index: number
|
|
23
|
-
renderElement: (el: ElementMeta, index: number) => React.ReactNode
|
|
24
|
-
}) {
|
|
25
|
-
const title = el['title'] ? String(el['title']) : undefined
|
|
26
|
-
const description = el['description'] ? String(el['description']) : undefined
|
|
27
|
-
const iconName = el['icon'] ? String(el['icon']) : undefined
|
|
28
|
-
const badge = el['badge'] ? String(el['badge']) : undefined
|
|
29
|
-
const columns = Number(el['columns'] ?? 1)
|
|
30
|
-
const collapsible = Boolean(el['collapsible'])
|
|
31
|
-
const compact = Boolean(el['compact'])
|
|
32
|
-
const dense = Boolean(el['dense'])
|
|
33
|
-
const secondary = Boolean(el['secondary'])
|
|
34
|
-
const afterHeader = (el['afterHeader'] as ElementMeta[] | undefined) ?? []
|
|
35
|
-
const persist = Boolean(el['persistCollapsed'])
|
|
36
|
-
const persistKey = el['persistKey']
|
|
37
|
-
? `pilotiq.section.${String(el['persistKey'])}`
|
|
38
|
-
: title
|
|
39
|
-
? `pilotiq.section.${title.toLowerCase().replace(/\s+/g, '-')}`
|
|
40
|
-
: undefined
|
|
41
|
-
|
|
42
|
-
const defaultCollapsed = Boolean(el['defaultCollapsed'])
|
|
43
|
-
const [collapsed, setCollapsed] = useState(defaultCollapsed)
|
|
44
|
-
|
|
45
|
-
// Plan #8 — persist open/closed state to localStorage. Hydration-safe:
|
|
46
|
-
// initial render uses `defaultCollapsed`; effect overrides from storage
|
|
47
|
-
// after mount so server + client first paint agree.
|
|
48
|
-
useEffect(() => {
|
|
49
|
-
if (!persist || !persistKey) return
|
|
50
|
-
setCollapsed(readStoredFlag(persistKey, defaultCollapsed))
|
|
51
|
-
}, [persist, persistKey, defaultCollapsed])
|
|
52
|
-
|
|
53
|
-
useEffect(() => {
|
|
54
|
-
if (!persist || !persistKey) return
|
|
55
|
-
writeStoredFlag(persistKey, collapsed)
|
|
56
|
-
}, [persist, persistKey, collapsed])
|
|
57
|
-
|
|
58
|
-
// `dense` tightens the inner spacing between the section's children
|
|
59
|
-
// (orthogonal to `compact`, which trims the section's outer padding /
|
|
60
|
-
// heading). gap-2 ≈ 8px vs gap-4 ≈ 16px.
|
|
61
|
-
const innerGap = dense ? 'gap-2' : 'gap-4'
|
|
62
|
-
const gridClass = columns === 2 ? `grid grid-cols-2 ${innerGap}` : columns === 3 ? `grid grid-cols-3 ${innerGap}` : `flex flex-col ${innerGap}`
|
|
63
|
-
const padding = compact ? 'p-3' : 'p-4'
|
|
64
|
-
const titleSize = compact ? 'text-sm' : 'text-base'
|
|
65
|
-
const Icon = resolveIcon(iconName)
|
|
66
|
-
|
|
67
|
-
// `secondary()` flips the section background to the muted token so it
|
|
68
|
-
// visually recedes beneath a primary section. The border thins to the
|
|
69
|
-
// same muted tone for the same reason — a sharp `border-input` line
|
|
70
|
-
// around a muted block looks like a typographic ledger rather than a
|
|
71
|
-
// grouping container.
|
|
72
|
-
const surfaceClass = secondary ? 'bg-muted/40 border-muted' : 'bg-card'
|
|
73
|
-
|
|
74
|
-
return (
|
|
75
|
-
<section
|
|
76
|
-
key={index}
|
|
77
|
-
className={`flex flex-col ${compact ? 'gap-2' : 'gap-3'} rounded-lg border ${surfaceClass} ${padding} ${layoutClasses(el)}`.trim()}
|
|
78
|
-
>
|
|
79
|
-
{(title || description || collapsible || badge || afterHeader.length > 0) && (
|
|
80
|
-
<header className="flex items-start justify-between gap-2">
|
|
81
|
-
<div className="flex items-start gap-2">
|
|
82
|
-
{Icon && <Icon className="size-4 mt-0.5 text-muted-foreground" aria-hidden="true" />}
|
|
83
|
-
<div>
|
|
84
|
-
<div className="flex items-center gap-2">
|
|
85
|
-
{title && <h3 className={`${titleSize} font-semibold`}>{title}</h3>}
|
|
86
|
-
{badge && (
|
|
87
|
-
<span className="rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium uppercase tracking-wide text-muted-foreground">
|
|
88
|
-
{badge}
|
|
89
|
-
</span>
|
|
90
|
-
)}
|
|
91
|
-
</div>
|
|
92
|
-
{description && <p className="text-xs text-muted-foreground mt-0.5">{description}</p>}
|
|
93
|
-
</div>
|
|
94
|
-
</div>
|
|
95
|
-
<div className="flex items-center gap-2">
|
|
96
|
-
{afterHeader.length > 0 && (
|
|
97
|
-
<div className="flex items-center gap-1">
|
|
98
|
-
{afterHeader.map((a, i) => renderElement(a, i))}
|
|
99
|
-
</div>
|
|
100
|
-
)}
|
|
101
|
-
{collapsible && (
|
|
102
|
-
<button
|
|
103
|
-
type="button"
|
|
104
|
-
onClick={() => setCollapsed(c => !c)}
|
|
105
|
-
className="text-xs text-muted-foreground hover:text-foreground"
|
|
106
|
-
>
|
|
107
|
-
{collapsed ? 'Expand' : 'Collapse'}
|
|
108
|
-
</button>
|
|
109
|
-
)}
|
|
110
|
-
</div>
|
|
111
|
-
</header>
|
|
112
|
-
)}
|
|
113
|
-
{!collapsed && el.children && el.children.length > 0 && (
|
|
114
|
-
<div className={gridClass}>
|
|
115
|
-
{el.children.map((c, i) => renderElement(c, i))}
|
|
116
|
-
</div>
|
|
117
|
-
)}
|
|
118
|
-
</section>
|
|
119
|
-
)
|
|
120
|
-
}
|