@pilotiq/pilotiq 0.23.1 → 0.24.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +91 -0
- package/boost/guidelines.md +566 -0
- package/boost/skills/pilotiq-fields/SKILL.md +47 -0
- package/boost/skills/pilotiq-fields/rules/field-catalog.md +288 -0
- package/boost/skills/pilotiq-fields/rules/reactive-fields.md +199 -0
- package/boost/skills/pilotiq-fields/rules/validation.md +198 -0
- package/boost/skills/pilotiq-relations/SKILL.md +47 -0
- package/boost/skills/pilotiq-relations/rules/relation-managers.md +256 -0
- package/boost/skills/pilotiq-relations/rules/repeater-relationship.md +177 -0
- package/boost/skills/pilotiq-resource/SKILL.md +61 -0
- package/boost/skills/pilotiq-resource/rules/authorization.md +242 -0
- package/boost/skills/pilotiq-resource/rules/defining-resources.md +228 -0
- package/boost/skills/pilotiq-resource/rules/page-overrides.md +296 -0
- package/dist/actions/exportFactory.d.ts +10 -0
- package/dist/actions/exportFactory.d.ts.map +1 -1
- package/dist/actions/exportFactory.js +10 -0
- package/dist/actions/exportFactory.js.map +1 -1
- package/dist/react/CollabRoomContext.d.ts +5 -5
- package/dist/react/index.d.ts +0 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +0 -1
- package/dist/react/index.js.map +1 -1
- package/dist/routes/helpers.d.ts.map +1 -1
- package/dist/routes/helpers.js +6 -2
- package/dist/routes/helpers.js.map +1 -1
- package/dist/routes/relations.d.ts.map +1 -1
- package/dist/routes/relations.js +12 -0
- package/dist/routes/relations.js.map +1 -1
- package/package.json +6 -1
- package/.turbo/turbo-build.log +0 -8
- package/CLAUDE.md +0 -265
- package/dist/react/useCollabSeed.d.ts +0 -23
- package/dist/react/useCollabSeed.d.ts.map +0 -1
- package/dist/react/useCollabSeed.js +0 -82
- package/dist/react/useCollabSeed.js.map +0 -1
- package/src/Cluster.test.ts +0 -283
- package/src/Cluster.ts +0 -83
- package/src/Column.test.ts +0 -199
- package/src/Column.ts +0 -710
- package/src/Global.test.ts +0 -367
- package/src/Global.ts +0 -169
- package/src/Page.test.ts +0 -114
- package/src/Page.ts +0 -208
- package/src/Pilotiq.perf.test.ts +0 -252
- package/src/Pilotiq.test.ts +0 -129
- package/src/Pilotiq.ts +0 -1158
- package/src/PilotiqRegistry.ts +0 -36
- package/src/PilotiqServiceProvider.ts +0 -121
- package/src/RelationManager.test.ts +0 -400
- package/src/RelationManager.ts +0 -527
- package/src/RenderHook.test.ts +0 -252
- package/src/RenderHook.ts +0 -242
- package/src/Resource.test.ts +0 -284
- package/src/Resource.ts +0 -526
- package/src/RightPanel.test.ts +0 -202
- package/src/RightPanel.ts +0 -132
- package/src/Tab.test.ts +0 -91
- package/src/Tab.ts +0 -156
- package/src/UserMenuItem.ts +0 -145
- package/src/actions/Action.test.ts +0 -2526
- package/src/actions/Action.ts +0 -1515
- package/src/actions/ActionGroup.test.ts +0 -112
- package/src/actions/ActionGroup.ts +0 -173
- package/src/actions/attachFactory.ts +0 -172
- package/src/actions/bulkFactories.ts +0 -168
- package/src/actions/crudFactories.ts +0 -220
- package/src/actions/exportFactory.ts +0 -215
- package/src/actions/factoryHelpers.ts +0 -177
- package/src/actions/importFactory.ts +0 -243
- package/src/actions/index.ts +0 -17
- package/src/actions/m2mFactories.ts +0 -193
- package/src/actions/relationFactories.ts +0 -372
- package/src/applyPageHooks.test.ts +0 -463
- package/src/applyPageHooks.ts +0 -330
- package/src/authorization.test.ts +0 -483
- package/src/breadcrumbs.test.ts +0 -238
- package/src/cells/coerce.test.ts +0 -85
- package/src/cells/coerce.ts +0 -84
- package/src/clusterPaths.ts +0 -35
- package/src/columns/BadgeColumn.test.ts +0 -54
- package/src/columns/BadgeColumn.ts +0 -32
- package/src/columns/BooleanColumn.test.ts +0 -41
- package/src/columns/BooleanColumn.ts +0 -18
- package/src/columns/ColorColumn.test.ts +0 -37
- package/src/columns/ColorColumn.ts +0 -38
- package/src/columns/IconColumn.test.ts +0 -54
- package/src/columns/IconColumn.ts +0 -37
- package/src/columns/ImageColumn.test.ts +0 -41
- package/src/columns/ImageColumn.ts +0 -28
- package/src/columns/SelectColumn.ts +0 -98
- package/src/columns/TextColumn.test.ts +0 -190
- package/src/columns/TextColumn.ts +0 -20
- package/src/columns/TextInputColumn.ts +0 -68
- package/src/columns/ToggleColumn.ts +0 -46
- package/src/columns/editableColumns.test.ts +0 -238
- package/src/columns/index.ts +0 -9
- package/src/defaultGlobalPages.ts +0 -95
- package/src/defaultPages.test.ts +0 -634
- package/src/defaultPages.ts +0 -617
- package/src/defaultViewPage.test.ts +0 -147
- package/src/elements/Form.test.ts +0 -223
- package/src/elements/Form.ts +0 -416
- package/src/elements/ListTabs.ts +0 -28
- package/src/elements/Table.test.ts +0 -422
- package/src/elements/Table.ts +0 -850
- package/src/elements/TableGroup.test.ts +0 -260
- package/src/elements/TableGroup.ts +0 -334
- package/src/elements/dispatchAction.test.ts +0 -463
- package/src/elements/dispatchAction.ts +0 -355
- package/src/elements/dispatchForm.test.ts +0 -477
- package/src/elements/dispatchForm.ts +0 -1993
- package/src/elements/dispatchTable.test.ts +0 -1514
- package/src/elements/dispatchTable.ts +0 -745
- package/src/elements/index.ts +0 -21
- package/src/entries/BadgeEntry.ts +0 -39
- package/src/entries/CodeEntry.test.ts +0 -40
- package/src/entries/CodeEntry.ts +0 -52
- package/src/entries/ColorEntry.ts +0 -63
- package/src/entries/ComponentEntry.test.ts +0 -173
- package/src/entries/ComponentEntry.ts +0 -95
- package/src/entries/Entry.ts +0 -304
- package/src/entries/IconEntry.ts +0 -49
- package/src/entries/ImageEntry.ts +0 -61
- package/src/entries/KeyValueEntry.ts +0 -47
- package/src/entries/RepeatableEntry.test.ts +0 -239
- package/src/entries/RepeatableEntry.ts +0 -173
- package/src/entries/TextEntry.test.ts +0 -394
- package/src/entries/TextEntry.ts +0 -60
- package/src/entries/index.ts +0 -12
- package/src/entries/leaves.test.ts +0 -306
- package/src/entries/registry.ts +0 -54
- package/src/fields/BuilderField.test.ts +0 -1188
- package/src/fields/BuilderField.ts +0 -605
- package/src/fields/BuilderRelationship.test.ts +0 -811
- package/src/fields/CheckboxField.test.ts +0 -44
- package/src/fields/CheckboxField.ts +0 -27
- package/src/fields/CheckboxListField.test.ts +0 -99
- package/src/fields/CheckboxListField.ts +0 -66
- package/src/fields/ColorPickerField.test.ts +0 -33
- package/src/fields/ColorPickerField.ts +0 -25
- package/src/fields/DateField.ts +0 -54
- package/src/fields/DateTimeField.test.ts +0 -55
- package/src/fields/EmailField.ts +0 -16
- package/src/fields/Field.test.ts +0 -654
- package/src/fields/Field.ts +0 -817
- package/src/fields/FileUploadField.test.ts +0 -143
- package/src/fields/FileUploadField.ts +0 -159
- package/src/fields/HiddenField.test.ts +0 -27
- package/src/fields/HiddenField.ts +0 -28
- package/src/fields/KeyValueField.test.ts +0 -105
- package/src/fields/KeyValueField.ts +0 -55
- package/src/fields/MarkdownField.test.ts +0 -167
- package/src/fields/MarkdownField.ts +0 -162
- package/src/fields/NumberField.ts +0 -33
- package/src/fields/RadioField.test.ts +0 -94
- package/src/fields/RadioField.ts +0 -67
- package/src/fields/RepeaterField.test.ts +0 -1806
- package/src/fields/RepeaterField.ts +0 -939
- package/src/fields/RepeaterRelationship.test.ts +0 -1923
- package/src/fields/RepeaterSimple.test.ts +0 -248
- package/src/fields/RowButton.test.ts +0 -219
- package/src/fields/RowButton.ts +0 -135
- package/src/fields/SelectField.test.ts +0 -192
- package/src/fields/SelectField.ts +0 -235
- package/src/fields/SliderField.test.ts +0 -50
- package/src/fields/SliderField.ts +0 -53
- package/src/fields/SlugField.ts +0 -24
- package/src/fields/TagsInputField.test.ts +0 -154
- package/src/fields/TagsInputField.ts +0 -133
- package/src/fields/TextField.test.ts +0 -213
- package/src/fields/TextField.ts +0 -177
- package/src/fields/TextareaField.test.ts +0 -58
- package/src/fields/TextareaField.ts +0 -59
- package/src/fields/ToggleButtonsField.test.ts +0 -106
- package/src/fields/ToggleButtonsField.ts +0 -59
- package/src/fields/ToggleField.ts +0 -16
- package/src/fields/disableOptionsWhenSelectedInSiblingRepeaterItems.test.ts +0 -319
- package/src/fields/optionsResolver.ts +0 -95
- package/src/fields/resolveField.ts +0 -28
- package/src/filters/BooleanFilter.ts +0 -35
- package/src/filters/DateRangeFilter.test.ts +0 -194
- package/src/filters/DateRangeFilter.ts +0 -148
- package/src/filters/Filter.test.ts +0 -268
- package/src/filters/Filter.ts +0 -184
- package/src/filters/FormFilter.test.ts +0 -238
- package/src/filters/FormFilter.ts +0 -215
- package/src/filters/MultiSelectFilter.test.ts +0 -119
- package/src/filters/MultiSelectFilter.ts +0 -78
- package/src/filters/QueryBuilderFilter.test.ts +0 -662
- package/src/filters/QueryBuilderFilter.ts +0 -398
- package/src/filters/SelectFilter.ts +0 -46
- package/src/filters/TernaryFilter.test.ts +0 -160
- package/src/filters/TernaryFilter.ts +0 -72
- package/src/filters/TrashedFilter.test.ts +0 -149
- package/src/filters/TrashedFilter.ts +0 -55
- package/src/filters/queryBuilder/BooleanConstraint.ts +0 -31
- package/src/filters/queryBuilder/Constraint.ts +0 -115
- package/src/filters/queryBuilder/DateConstraint.ts +0 -69
- package/src/filters/queryBuilder/NumberConstraint.ts +0 -66
- package/src/filters/queryBuilder/SelectConstraint.ts +0 -72
- package/src/filters/queryBuilder/TextConstraint.ts +0 -64
- package/src/filters/queryBuilder/index.ts +0 -12
- package/src/icons/index.ts +0 -2
- package/src/icons/lucide.ts +0 -204
- package/src/icons/registry.test.ts +0 -56
- package/src/icons/registry.ts +0 -41
- package/src/icons/types.ts +0 -47
- package/src/index.ts +0 -525
- package/src/io/csv.test.ts +0 -142
- package/src/io/csv.ts +0 -170
- package/src/nestedRelationManagerData.test.ts +0 -547
- package/src/notifications/Notification.test.ts +0 -210
- package/src/notifications/Notification.ts +0 -354
- package/src/notifications/broadcast.test.ts +0 -110
- package/src/notifications/broadcast.ts +0 -95
- package/src/notifications/database.test.ts +0 -383
- package/src/notifications/database.ts +0 -398
- package/src/notifications/databaseNotifications.test.ts +0 -187
- package/src/notifications/dispatchNotificationAction.test.ts +0 -341
- package/src/notifications/dispatchNotificationAction.ts +0 -142
- package/src/notifications/flash.test.ts +0 -89
- package/src/notifications/flash.ts +0 -71
- package/src/notifications/index.ts +0 -45
- package/src/notifications/registerBroadcastAuth.test.ts +0 -134
- package/src/notifications/registerBroadcastAuth.ts +0 -100
- package/src/notifications/resolveSavedNotification.test.ts +0 -82
- package/src/notifications/resolveSavedNotification.ts +0 -59
- package/src/notifications/types.ts +0 -93
- package/src/orm/m2mAccessor.ts +0 -66
- package/src/orm/modelDefaults.test.ts +0 -633
- package/src/orm/modelDefaults.ts +0 -666
- package/src/pageData/breadcrumbs.ts +0 -288
- package/src/pageData/forms.ts +0 -578
- package/src/pageData/helpers.ts +0 -857
- package/src/pageData/misc.ts +0 -347
- package/src/pageData/navigation.ts +0 -842
- package/src/pageData/relationPages.ts +0 -1248
- package/src/pageData/relationTabs.ts +0 -286
- package/src/pageData/resourcePages.ts +0 -609
- package/src/pageData.test.ts +0 -1545
- package/src/pageData.ts +0 -341
- package/src/plugins/index.ts +0 -8
- package/src/plugins/themeEditor.test.ts +0 -36
- package/src/plugins/themeEditor.ts +0 -45
- package/src/react/AppShell.tsx +0 -251
- package/src/react/CollabExtensionFactoryRegistry.ts +0 -55
- package/src/react/CollabRoomContext.ts +0 -98
- package/src/react/CollabTextRendererRegistry.ts +0 -102
- package/src/react/CommandPalette.tsx +0 -375
- package/src/react/CurrentUserContext.tsx +0 -50
- package/src/react/CustomPageWrapperGate.tsx +0 -69
- package/src/react/CustomPageWrapperRegistry.ts +0 -45
- package/src/react/FieldFocusReporterRegistry.ts +0 -37
- package/src/react/FieldLabelSlotRegistry.ts +0 -30
- package/src/react/FieldPresenceRegistry.ts +0 -46
- package/src/react/FormCollabBindingRegistry.ts +0 -242
- package/src/react/FormStateContext.tsx +0 -591
- package/src/react/HeadHooks.tsx +0 -126
- package/src/react/MarkdownEditorRegistry.test.ts +0 -38
- package/src/react/MarkdownEditorRegistry.ts +0 -107
- package/src/react/NotificationActionStrip.tsx +0 -263
- package/src/react/NotificationBell.tsx +0 -426
- package/src/react/PendingSuggestionApplierRegistry.test.ts +0 -97
- package/src/react/PendingSuggestionApplierRegistry.ts +0 -98
- package/src/react/PendingSuggestionOverlayRegistry.ts +0 -54
- package/src/react/PendingSuggestionsContext.tsx +0 -172
- package/src/react/RecordWrapperGate.tsx +0 -58
- package/src/react/RecordWrapperRegistry.ts +0 -39
- package/src/react/RenderHookSlot.tsx +0 -32
- package/src/react/RightSidebar.tsx +0 -257
- package/src/react/RightSidebarContext.tsx +0 -234
- package/src/react/RightSidebarTrigger.tsx +0 -53
- package/src/react/RowCoordsContext.tsx +0 -23
- package/src/react/SchemaRenderer.tsx +0 -549
- package/src/react/SearchTrigger.tsx +0 -46
- package/src/react/ThemeProvider.tsx +0 -93
- package/src/react/ThemeSettingsPage.tsx +0 -579
- package/src/react/ThemeToggle.tsx +0 -20
- package/src/react/Toaster.tsx +0 -158
- package/src/react/UserMenu.tsx +0 -196
- package/src/react/WidgetDataContext.tsx +0 -157
- package/src/react/cells/EditableCell.tsx +0 -389
- package/src/react/component-slots.test.ts +0 -103
- package/src/react/component-slots.ts +0 -116
- package/src/react/fieldJsHandler.test.ts +0 -166
- package/src/react/fieldJsHandler.ts +0 -79
- package/src/react/fields/BuilderInput.tsx +0 -1078
- package/src/react/fields/CheckboxInput.tsx +0 -39
- package/src/react/fields/CheckboxListInput.tsx +0 -102
- package/src/react/fields/ColorInput.tsx +0 -71
- package/src/react/fields/DateFieldInput.tsx +0 -70
- package/src/react/fields/DateTimeInput.tsx +0 -62
- package/src/react/fields/FieldShell.tsx +0 -348
- package/src/react/fields/FileUploadInput.tsx +0 -639
- package/src/react/fields/HiddenInput.tsx +0 -17
- package/src/react/fields/KeyValueInput.tsx +0 -230
- package/src/react/fields/MarkdownInput.tsx +0 -560
- package/src/react/fields/RadioInput.tsx +0 -81
- package/src/react/fields/RepeaterInput.test.ts +0 -116
- package/src/react/fields/RepeaterInput.tsx +0 -1420
- package/src/react/fields/SelectFieldInput.tsx +0 -280
- package/src/react/fields/SliderInput.tsx +0 -81
- package/src/react/fields/TagsInput.tsx +0 -283
- package/src/react/fields/TextLikeInput.tsx +0 -256
- package/src/react/fields/ToggleButtonsInput.tsx +0 -60
- package/src/react/fields/ToggleFieldInput.tsx +0 -56
- package/src/react/fields/relationshipRenameDispatch.test.ts +0 -106
- package/src/react/fields/relationshipRenameDispatch.ts +0 -97
- package/src/react/fields/repeaterReconcile.test.ts +0 -114
- package/src/react/fields/repeaterReconcile.ts +0 -104
- package/src/react/fields/rowChromeButton.tsx +0 -336
- package/src/react/fields/rowState.ts +0 -106
- package/src/react/fields/syncRowGates.test.ts +0 -202
- package/src/react/fields/syncRowGates.ts +0 -66
- package/src/react/fields/textInputControls.tsx +0 -238
- package/src/react/fields/useRowReorderDnd.ts +0 -78
- package/src/react/formStateHelpers.test.ts +0 -508
- package/src/react/formStateHelpers.ts +0 -381
- package/src/react/hooks/use-mobile.ts +0 -19
- package/src/react/icon-context.tsx +0 -60
- package/src/react/index.ts +0 -195
- package/src/react/layouts/SidebarLayout.tsx +0 -250
- package/src/react/layouts/TopbarLayout.tsx +0 -258
- package/src/react/navigate.tsx +0 -37
- package/src/react/onProviderSynced.test.ts +0 -90
- package/src/react/parseRecordEditUrl.test.ts +0 -122
- package/src/react/parseRecordEditUrl.ts +0 -94
- package/src/react/persistedState.ts +0 -40
- package/src/react/registry.ts +0 -48
- package/src/react/right-panel-registry.tsx +0 -47
- package/src/react/schemaRenderer/AlertRenderer.tsx +0 -112
- package/src/react/schemaRenderer/EntryRenderer.tsx +0 -501
- package/src/react/schemaRenderer/SectionRenderer.tsx +0 -120
- package/src/react/schemaRenderer/SimpleElements.tsx +0 -306
- package/src/react/schemaRenderer/TabsRenderer.tsx +0 -62
- package/src/react/schemaRenderer/WizardRenderer.tsx +0 -338
- package/src/react/schemaRenderer/action/ActionGroupTrigger.tsx +0 -177
- package/src/react/schemaRenderer/action/ActionModalDialog.tsx +0 -273
- package/src/react/schemaRenderer/action/ConfirmActionDialog.tsx +0 -61
- package/src/react/schemaRenderer/action/HandlerActionButton.tsx +0 -43
- package/src/react/schemaRenderer/action/MethodActionButton.tsx +0 -64
- package/src/react/schemaRenderer/action/buttons.tsx +0 -99
- package/src/react/schemaRenderer/action/helpers.ts +0 -140
- package/src/react/schemaRenderer/action/renderAction.tsx +0 -245
- package/src/react/schemaRenderer/columnFormat.ts +0 -65
- package/src/react/schemaRenderer/constants.ts +0 -50
- package/src/react/schemaRenderer/form/FormRenderer.tsx +0 -274
- package/src/react/schemaRenderer/form/renderField.tsx +0 -511
- package/src/react/schemaRenderer/helpers.tsx +0 -81
- package/src/react/schemaRenderer/table/CardsLayoutBody.tsx +0 -308
- package/src/react/schemaRenderer/table/TableRenderer.tsx +0 -123
- package/src/react/schemaRenderer/table/TableRendererBody.tsx +0 -974
- package/src/react/schemaRenderer/table/filters.tsx +0 -1233
- package/src/react/schemaRenderer/table/formatCell.tsx +0 -264
- package/src/react/schemaRenderer/table/links.tsx +0 -112
- package/src/react/schemaRenderer/table/renderRowActions.tsx +0 -52
- package/src/react/schemaRenderer/table/url.tsx +0 -143
- package/src/react/theme-preview/apply.ts +0 -99
- package/src/react/theme-preview/build-html.ts +0 -436
- package/src/react/ui/button.tsx +0 -51
- package/src/react/ui/calendar.tsx +0 -67
- package/src/react/ui/checkbox.tsx +0 -29
- package/src/react/ui/dialog.tsx +0 -108
- package/src/react/ui/dropdown-menu.tsx +0 -97
- package/src/react/ui/input.tsx +0 -20
- package/src/react/ui/label.tsx +0 -21
- package/src/react/ui/popover.tsx +0 -50
- package/src/react/ui/select.tsx +0 -169
- package/src/react/ui/separator.tsx +0 -25
- package/src/react/ui/sheet.tsx +0 -136
- package/src/react/ui/sidebar.tsx +0 -723
- package/src/react/ui/skeleton.tsx +0 -13
- package/src/react/ui/slider.tsx +0 -34
- package/src/react/ui/switch.tsx +0 -28
- package/src/react/ui/table.tsx +0 -105
- package/src/react/ui/tabs.tsx +0 -63
- package/src/react/ui/textarea.tsx +0 -18
- package/src/react/ui/tooltip.tsx +0 -64
- package/src/react/useCollabSeed.ts +0 -86
- package/src/react/useResizableWidth.ts +0 -139
- package/src/react/utils.ts +0 -6
- package/src/react/widgetRegistry.test.ts +0 -43
- package/src/react/widgetRegistry.ts +0 -50
- package/src/react/widgets/StatsOverviewRenderer.tsx +0 -232
- package/src/react/widgets/TableWidgetRenderer.tsx +0 -231
- package/src/react/widgets/ViewRenderer.tsx +0 -71
- package/src/relationManagerData.test.ts +0 -1595
- package/src/richtext/index.ts +0 -8
- package/src/richtext/registry.ts +0 -89
- package/src/routes/globals.ts +0 -148
- package/src/routes/guard.test.ts +0 -325
- package/src/routes/helpers.ts +0 -700
- package/src/routes/pages.ts +0 -175
- package/src/routes/panel.ts +0 -204
- package/src/routes/relations.ts +0 -1227
- package/src/routes/resources.ts +0 -781
- package/src/routes/theme.ts +0 -91
- package/src/routes-nested-relations.test.ts +0 -676
- package/src/routes-relations.test.ts +0 -972
- package/src/routes.test.ts +0 -2027
- package/src/routes.ts +0 -303
- package/src/schema/Alert.test.ts +0 -109
- package/src/schema/Alert.ts +0 -131
- package/src/schema/Block.ts +0 -169
- package/src/schema/Breadcrumbs.ts +0 -40
- package/src/schema/Card.ts +0 -35
- package/src/schema/Divider.ts +0 -20
- package/src/schema/Element.ts +0 -219
- package/src/schema/EmptyState.test.ts +0 -37
- package/src/schema/EmptyState.ts +0 -63
- package/src/schema/Fieldset.ts +0 -43
- package/src/schema/Grid.ts +0 -43
- package/src/schema/Group.ts +0 -30
- package/src/schema/Heading.ts +0 -39
- package/src/schema/Html.ts +0 -67
- package/src/schema/Icon.ts +0 -54
- package/src/schema/Image.ts +0 -57
- package/src/schema/LinkTag.ts +0 -41
- package/src/schema/Markdown.ts +0 -85
- package/src/schema/MetaTag.ts +0 -41
- package/src/schema/RelationTabs.ts +0 -71
- package/src/schema/ScriptTag.ts +0 -55
- package/src/schema/Section.ts +0 -160
- package/src/schema/ServerDataElement.test.ts +0 -140
- package/src/schema/ServerDataElement.ts +0 -156
- package/src/schema/SlotComponent.test.ts +0 -77
- package/src/schema/SlotComponent.ts +0 -71
- package/src/schema/Split.ts +0 -50
- package/src/schema/Stat.test.ts +0 -118
- package/src/schema/Stat.ts +0 -154
- package/src/schema/StatsOverview.test.ts +0 -141
- package/src/schema/StatsOverview.ts +0 -119
- package/src/schema/StyleTag.ts +0 -35
- package/src/schema/TableWidget.test.ts +0 -297
- package/src/schema/TableWidget.ts +0 -289
- package/src/schema/Tabs.ts +0 -79
- package/src/schema/Text.ts +0 -58
- package/src/schema/UnorderedList.ts +0 -49
- package/src/schema/View.test.ts +0 -111
- package/src/schema/View.ts +0 -127
- package/src/schema/Wizard.ts +0 -220
- package/src/schema/containers.test.ts +0 -564
- package/src/schema/headTags.test.ts +0 -134
- package/src/schema/index.ts +0 -40
- package/src/schema/primes.test.ts +0 -269
- package/src/schema/resolveSchema.test.ts +0 -379
- package/src/schema/resolveSchema.ts +0 -917
- package/src/schema/sanitize.ts +0 -58
- package/src/search.test.ts +0 -446
- package/src/search.ts +0 -178
- package/src/sessionFilters.test.ts +0 -375
- package/src/sessionFilters.ts +0 -143
- package/src/slot-components/index.ts +0 -10
- package/src/slot-components/registry.ts +0 -56
- package/src/styles/file-upload.css +0 -13
- package/src/summarizers/Summarizer.test.ts +0 -84
- package/src/summarizers/Summarizer.ts +0 -123
- package/src/summarizers/index.ts +0 -11
- package/src/theme/base-colors.ts +0 -68
- package/src/theme/chart-colors.ts +0 -50
- package/src/theme/colors.ts +0 -447
- package/src/theme/generate-css.test.ts +0 -139
- package/src/theme/generate-css.ts +0 -44
- package/src/theme/generate-scale.test.ts +0 -106
- package/src/theme/generate-scale.ts +0 -97
- package/src/theme/icon-map.ts +0 -42
- package/src/theme/index.ts +0 -34
- package/src/theme/migrate.test.ts +0 -178
- package/src/theme/migrate.ts +0 -81
- package/src/theme/presets.ts +0 -135
- package/src/theme/radius.ts +0 -18
- package/src/theme/resolve.test.ts +0 -238
- package/src/theme/resolve.ts +0 -96
- package/src/theme/spacing.ts +0 -18
- package/src/theme/storage.test.ts +0 -126
- package/src/theme/storage.ts +0 -106
- package/src/theme/theme-colors.ts +0 -88
- package/src/theme/types.ts +0 -125
- package/src/uploads/UploadAdapter.ts +0 -35
- package/src/uploads/index.ts +0 -2
- package/src/uploads/localUpload.test.ts +0 -70
- package/src/uploads/localUpload.ts +0 -84
- package/src/validation/Validator.ts +0 -49
- package/src/validation/index.ts +0 -28
- package/src/validation/rules.ts +0 -78
- package/src/validation/runValidators.ts +0 -435
- package/src/validation/uniqueValidator.test.ts +0 -196
- package/src/validation/uniqueValidator.ts +0 -133
- package/src/validation/validators.test.ts +0 -268
- package/src/vite.test.ts +0 -184
- package/src/vite.ts +0 -787
- package/src/widgets/index.ts +0 -10
- package/src/widgets/registry.ts +0 -45
- package/src/widgets.test.ts +0 -592
- package/tsconfig.build.json +0 -11
- package/tsconfig.json +0 -4
- package/tsconfig.test.json +0 -10
- package/views/react/Dashboard.tsx +0 -27
- package/views/react/Resources/Form.tsx +0 -102
- package/views/react/Resources/Index.tsx +0 -49
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react'
|
|
2
|
-
import { CheckIcon, CircleIcon, CopyIcon } from 'lucide-react'
|
|
3
|
-
import type { ElementMeta } from '../../../schema/Element.js'
|
|
4
|
-
import {
|
|
5
|
-
BADGE_COLOR_CLASSES,
|
|
6
|
-
COLUMN_COLOR_CLASSES,
|
|
7
|
-
COLUMN_WEIGHT_CLASSES,
|
|
8
|
-
} from '../constants.js'
|
|
9
|
-
import { resolveIcon } from '../helpers.js'
|
|
10
|
-
import { applyColumnFormat } from '../columnFormat.js'
|
|
11
|
-
|
|
12
|
-
// ─── Table cell rendering ───────────────────────────────────
|
|
13
|
-
//
|
|
14
|
-
// `formatCell` is the column-type dispatch (text / badge / icon /
|
|
15
|
-
// image / color + array variants). `wrapCell` and `wrapCellList`
|
|
16
|
-
// apply the shared text chrome (color / weight / tooltip / line-clamp /
|
|
17
|
-
// wrap / copy-to-clipboard) so every column-type path stays consistent.
|
|
18
|
-
|
|
19
|
-
/** Render a cell. Honors the column's `columnType` (badge/icon/boolean/
|
|
20
|
-
* image), built-in `format` spec, and per-row `_formatted[name]`
|
|
21
|
-
* overrides from server-side `formatStateUsing` callbacks. */
|
|
22
|
-
export function formatCell(
|
|
23
|
-
value: unknown,
|
|
24
|
-
col?: ElementMeta,
|
|
25
|
-
row?: Record<string, unknown>,
|
|
26
|
-
): React.ReactNode {
|
|
27
|
-
if (col === undefined) {
|
|
28
|
-
// Legacy raw-value fallback for non-column callsites.
|
|
29
|
-
if (value === null || value === undefined) return <span className="text-muted-foreground">—</span>
|
|
30
|
-
if (value instanceof Date) return value.toLocaleString(undefined, { dateStyle: 'medium', timeStyle: 'short' })
|
|
31
|
-
if (typeof value === 'boolean') return value ? 'Yes' : 'No'
|
|
32
|
-
if (typeof value === 'object') return JSON.stringify(value)
|
|
33
|
-
return String(value)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const columnType = String(col['columnType'] ?? 'text')
|
|
37
|
-
const fallback = (col['default'] as string | undefined)
|
|
38
|
-
|
|
39
|
-
// Per-row server-eval result wins over everything.
|
|
40
|
-
const colName = String(col['name'] ?? '')
|
|
41
|
-
const formatted = (row?.['_formatted'] as Record<string, string> | undefined)?.[colName]
|
|
42
|
-
const richtext = (row?.['_richtextCells'] as Record<string, true> | undefined)?.[colName] === true
|
|
43
|
-
const isBlank = value === null || value === undefined || value === ''
|
|
44
|
-
|
|
45
|
-
if (formatted !== undefined && formatted !== '') {
|
|
46
|
-
return wrapCell(formatted, col, richtext)
|
|
47
|
-
}
|
|
48
|
-
if (isBlank) {
|
|
49
|
-
return <span className="text-muted-foreground">{fallback ?? '—'}</span>
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
switch (columnType) {
|
|
53
|
-
case 'badge': {
|
|
54
|
-
const map = (col['badgeColors'] as Record<string, string> | undefined) ?? {}
|
|
55
|
-
const color = map[String(value)] ?? 'gray'
|
|
56
|
-
const cls = BADGE_COLOR_CLASSES[color] ?? BADGE_COLOR_CLASSES['gray']
|
|
57
|
-
return (
|
|
58
|
-
<span className={`inline-flex items-center rounded-full px-2 py-0.5 text-xs font-medium ${cls}`}>
|
|
59
|
-
{String(value)}
|
|
60
|
-
</span>
|
|
61
|
-
)
|
|
62
|
-
}
|
|
63
|
-
case 'icon':
|
|
64
|
-
case 'boolean': {
|
|
65
|
-
const map = (col['iconOptions'] as Record<string, { icon: string; color?: string }> | undefined) ?? {}
|
|
66
|
-
const opt = map[String(value)]
|
|
67
|
-
if (!opt) return <span className="text-muted-foreground">—</span>
|
|
68
|
-
const Icon = resolveIcon(opt.icon) ?? CircleIcon
|
|
69
|
-
const colorClass = opt.color ? (COLUMN_COLOR_CLASSES[opt.color] ?? '') : ''
|
|
70
|
-
return <Icon className={`size-4 inline ${colorClass}`} aria-label={String(value)} />
|
|
71
|
-
}
|
|
72
|
-
case 'image': {
|
|
73
|
-
const url = String(value)
|
|
74
|
-
const size = (col['imageSize'] as number | undefined) ?? 32
|
|
75
|
-
const shape = col['imageShape'] === 'circle' ? 'rounded-full' : 'rounded-md'
|
|
76
|
-
return (
|
|
77
|
-
<img
|
|
78
|
-
src={url}
|
|
79
|
-
alt=""
|
|
80
|
-
width={size}
|
|
81
|
-
height={size}
|
|
82
|
-
className={`${shape} object-cover`}
|
|
83
|
-
/>
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
case 'color': {
|
|
87
|
-
const css = String(value)
|
|
88
|
-
const shape = col['colorShape'] as 'rounded' | 'square' | 'circle' | undefined
|
|
89
|
-
const shapeClass =
|
|
90
|
-
shape === 'circle' ? 'rounded-full' :
|
|
91
|
-
shape === 'square' ? 'rounded-none' : 'rounded'
|
|
92
|
-
const hideValue = col['colorHideValue'] === true
|
|
93
|
-
return (
|
|
94
|
-
<span className="inline-flex items-center gap-2">
|
|
95
|
-
<span
|
|
96
|
-
className={`size-4 border border-border ${shapeClass}`}
|
|
97
|
-
style={{ backgroundColor: css }}
|
|
98
|
-
aria-hidden="true"
|
|
99
|
-
/>
|
|
100
|
-
{!hideValue && <span className="text-sm">{css}</span>}
|
|
101
|
-
</span>
|
|
102
|
-
)
|
|
103
|
-
}
|
|
104
|
-
default: {
|
|
105
|
-
// Array-valued cells — `bulleted()` wins over `listWithLineBreaks()`
|
|
106
|
-
// when both are set. Falls through to the standard string path for
|
|
107
|
-
// non-array values so the per-cell formatters keep working.
|
|
108
|
-
if (Array.isArray(value)) {
|
|
109
|
-
const items = value.map(v => String(v))
|
|
110
|
-
if (col['bulleted'] === true) {
|
|
111
|
-
return wrapCellList(items, col, 'bulleted')
|
|
112
|
-
}
|
|
113
|
-
if (col['listWithLineBreaks'] === true) {
|
|
114
|
-
return wrapCellList(items, col, 'lines')
|
|
115
|
-
}
|
|
116
|
-
// Bare array — comma-join (matches the existing legacy fallback).
|
|
117
|
-
return wrapCell(items.join(', '), col)
|
|
118
|
-
}
|
|
119
|
-
// Text column — apply built-in format, then wrapper.
|
|
120
|
-
const fmt = col['format'] as { kind: string; [k: string]: unknown } | undefined
|
|
121
|
-
const display = fmt ? applyColumnFormat(value, fmt) : String(value)
|
|
122
|
-
return wrapCell(display, col)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/** Apply text-rendering chrome (color, weight, line-clamp, wrap, tooltip)
|
|
128
|
-
* to a stringified cell value. Used by the text and per-row formatter
|
|
129
|
-
* paths so styling stays consistent. When `asHtml` is true the content
|
|
130
|
-
* is server-rendered HTML (e.g. from the registered richtext renderer)
|
|
131
|
-
* and gets injected via `dangerouslySetInnerHTML`. */
|
|
132
|
-
function wrapCell(content: string, col: ElementMeta, asHtml = false): React.ReactNode {
|
|
133
|
-
const color = col['color'] as string | undefined
|
|
134
|
-
const weight = col['weight'] as string | undefined
|
|
135
|
-
const tooltip = col['tooltip'] as string | undefined
|
|
136
|
-
const wrapping = Boolean(col['wrap'])
|
|
137
|
-
const clamp = col['lineClamp'] as number | undefined
|
|
138
|
-
const copyMsg = col['copyMessage'] as string | undefined
|
|
139
|
-
|
|
140
|
-
const colorCls = color ? (COLUMN_COLOR_CLASSES[color] ?? '') : ''
|
|
141
|
-
const weightCls = weight ? (COLUMN_WEIGHT_CLASSES[weight] ?? '') : ''
|
|
142
|
-
const wrapCls = wrapping ? 'whitespace-normal' : ''
|
|
143
|
-
const clampStyle = clamp !== undefined
|
|
144
|
-
? { display: '-webkit-box', WebkitLineClamp: String(clamp), WebkitBoxOrient: 'vertical' as const, overflow: 'hidden' }
|
|
145
|
-
: undefined
|
|
146
|
-
|
|
147
|
-
const valueNode = asHtml
|
|
148
|
-
? (
|
|
149
|
-
<span
|
|
150
|
-
className={`prose prose-sm max-w-none dark:prose-invert ${colorCls} ${weightCls} ${wrapCls}`.trim()}
|
|
151
|
-
title={tooltip}
|
|
152
|
-
style={clampStyle}
|
|
153
|
-
dangerouslySetInnerHTML={{ __html: content }}
|
|
154
|
-
/>
|
|
155
|
-
)
|
|
156
|
-
: (
|
|
157
|
-
<span
|
|
158
|
-
className={`${colorCls} ${weightCls} ${wrapCls}`.trim()}
|
|
159
|
-
title={tooltip}
|
|
160
|
-
style={clampStyle}
|
|
161
|
-
>
|
|
162
|
-
{content}
|
|
163
|
-
</span>
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
if (copyMsg === undefined) return valueNode
|
|
167
|
-
|
|
168
|
-
// Copy-to-clipboard trigger — copies the rendered text. For richtext
|
|
169
|
-
// cells the underlying source isn't separately stamped on the wire
|
|
170
|
-
// (would double the row payload), so the rendered HTML is what gets
|
|
171
|
-
// copied; admins comfortable with HTML still get something usable.
|
|
172
|
-
return (
|
|
173
|
-
<span className="inline-flex items-center gap-1.5">
|
|
174
|
-
{valueNode}
|
|
175
|
-
<CellCopyButton text={content} label={copyMsg} />
|
|
176
|
-
</span>
|
|
177
|
-
)
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/** Tabular-list rendering used by `Column.bulleted()` /
|
|
181
|
-
* `Column.listWithLineBreaks()`. `mode='bulleted'` mounts a `<ul>` with
|
|
182
|
-
* bullet markers; `mode='lines'` separates entries with `<br>`. Both
|
|
183
|
-
* inherit the same color / weight / wrap / tooltip / clamp chrome as
|
|
184
|
-
* the text path. Empty arrays fall through to the muted dash. */
|
|
185
|
-
function wrapCellList(
|
|
186
|
-
items: string[],
|
|
187
|
-
col: ElementMeta,
|
|
188
|
-
mode: 'bulleted' | 'lines',
|
|
189
|
-
): React.ReactNode {
|
|
190
|
-
if (items.length === 0) {
|
|
191
|
-
const fallback = (col['default'] as string | undefined) ?? '—'
|
|
192
|
-
return <span className="text-muted-foreground">{fallback}</span>
|
|
193
|
-
}
|
|
194
|
-
const color = col['color'] as string | undefined
|
|
195
|
-
const weight = col['weight'] as string | undefined
|
|
196
|
-
const tooltip = col['tooltip'] as string | undefined
|
|
197
|
-
|
|
198
|
-
const colorCls = color ? (COLUMN_COLOR_CLASSES[color] ?? '') : ''
|
|
199
|
-
const weightCls = weight ? (COLUMN_WEIGHT_CLASSES[weight] ?? '') : ''
|
|
200
|
-
|
|
201
|
-
if (mode === 'bulleted') {
|
|
202
|
-
return (
|
|
203
|
-
<ul
|
|
204
|
-
className={`list-disc pl-4 space-y-0.5 ${colorCls} ${weightCls}`.trim()}
|
|
205
|
-
title={tooltip}
|
|
206
|
-
>
|
|
207
|
-
{items.map((s, i) => <li key={i}>{s}</li>)}
|
|
208
|
-
</ul>
|
|
209
|
-
)
|
|
210
|
-
}
|
|
211
|
-
return (
|
|
212
|
-
<span
|
|
213
|
-
className={`${colorCls} ${weightCls}`.trim()}
|
|
214
|
-
title={tooltip}
|
|
215
|
-
>
|
|
216
|
-
{items.map((s, i) => (
|
|
217
|
-
<React.Fragment key={i}>
|
|
218
|
-
{i > 0 && <br />}
|
|
219
|
-
{s}
|
|
220
|
-
</React.Fragment>
|
|
221
|
-
))}
|
|
222
|
-
</span>
|
|
223
|
-
)
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/** Slim copy-to-clipboard button used by `Column.copyMessage()`. The
|
|
227
|
-
* label doubles as the toast text. Mirrors `EntryCopyButton`'s shape
|
|
228
|
-
* but compact enough to live inline next to a cell value. */
|
|
229
|
-
function CellCopyButton({ text, label }: { text: string; label: string }): React.ReactNode {
|
|
230
|
-
const [copied, setCopied] = useState(false)
|
|
231
|
-
const handleClick = (e: React.MouseEvent) => {
|
|
232
|
-
e.stopPropagation()
|
|
233
|
-
e.preventDefault()
|
|
234
|
-
if (typeof navigator !== 'undefined' && navigator.clipboard) {
|
|
235
|
-
navigator.clipboard.writeText(text).then(() => {
|
|
236
|
-
setCopied(true)
|
|
237
|
-
setTimeout(() => setCopied(false), 1500)
|
|
238
|
-
}).catch(() => { /* ignore — older browser / permission denied */ })
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return (
|
|
242
|
-
<button
|
|
243
|
-
type="button"
|
|
244
|
-
onClick={handleClick}
|
|
245
|
-
aria-label={copied ? label : 'Copy'}
|
|
246
|
-
title={copied ? label : 'Copy'}
|
|
247
|
-
data-no-row-nav
|
|
248
|
-
className="inline-flex h-5 w-5 items-center justify-center rounded text-muted-foreground hover:text-foreground hover:bg-muted"
|
|
249
|
-
>
|
|
250
|
-
{copied ? <CheckIcon className="size-3" /> : <CopyIcon className="size-3" />}
|
|
251
|
-
</button>
|
|
252
|
-
)
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
/** Resolve a stable row identifier — prefers the row's `id` field, falls
|
|
256
|
-
* back to the row index for rows missing one. Used as the React key
|
|
257
|
-
* in table bodies and as the `:id` substitution for row actions. */
|
|
258
|
-
export function rowId(row: unknown, index: number): string {
|
|
259
|
-
if (row && typeof row === 'object' && 'id' in row) {
|
|
260
|
-
const id = (row as { id?: unknown }).id
|
|
261
|
-
if (id !== undefined && id !== null) return String(id)
|
|
262
|
-
}
|
|
263
|
-
return String(index)
|
|
264
|
-
}
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import type { NavigateFn } from '../../navigate.js'
|
|
3
|
-
|
|
4
|
-
// ─── Inline link chrome ─────────────────────────────────────
|
|
5
|
-
//
|
|
6
|
-
// Three small chrome components used by the table body:
|
|
7
|
-
// - RecordCellLink wraps each row-data cell in a real <a href> so
|
|
8
|
-
// cmd-click / right-click "open in new tab" works; plain clicks
|
|
9
|
-
// SPA-nav via useNavigate.
|
|
10
|
-
// - ActiveGroupKeyChip surfaces the current `?groupKey=` drill-in
|
|
11
|
-
// state above the table with an × that clears it.
|
|
12
|
-
// - GroupHeadingLink turns a banded group's heading into a clickable
|
|
13
|
-
// drill-in trigger when the group is scopable.
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Modifier-aware SPA-nav click handler. Plain left-click intercepts to
|
|
17
|
-
* `navigate(href)`; cmd / ctrl / shift / alt-click and middle-click
|
|
18
|
-
* fall through to the browser so "open in new tab" / "save link as"
|
|
19
|
-
* semantics keep working on every real `<a href>` in the table body.
|
|
20
|
-
*/
|
|
21
|
-
export function useSpaNavClick(
|
|
22
|
-
href: string,
|
|
23
|
-
navigate: NavigateFn,
|
|
24
|
-
): (e: React.MouseEvent<HTMLAnchorElement>) => void {
|
|
25
|
-
return (e) => {
|
|
26
|
-
if (e.button !== 0) return
|
|
27
|
-
if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return
|
|
28
|
-
e.preventDefault()
|
|
29
|
-
void navigate(href)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export function RecordCellLink({
|
|
34
|
-
href, navigate, children,
|
|
35
|
-
}: {
|
|
36
|
-
href: string
|
|
37
|
-
navigate: NavigateFn
|
|
38
|
-
children: React.ReactNode
|
|
39
|
-
}) {
|
|
40
|
-
const onClick = useSpaNavClick(href, navigate)
|
|
41
|
-
return (
|
|
42
|
-
<a
|
|
43
|
-
href={href}
|
|
44
|
-
onClick={onClick}
|
|
45
|
-
className="block px-2 py-2 text-inherit no-underline hover:text-inherit focus:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded"
|
|
46
|
-
>
|
|
47
|
-
{children}
|
|
48
|
-
</a>
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* "Drilled into <Label>: <Value>" chip above the table when a group
|
|
54
|
-
* heading has been clicked. The × clears `?<prefix>groupKey=`, returning
|
|
55
|
-
* the table to its banded view. Real `<a href>` with `useNavigate()`
|
|
56
|
-
* intercept on plain left-click so cmd-click / middle-click open a
|
|
57
|
-
* fresh tab (rare but valid for sharing the banded view URL).
|
|
58
|
-
*/
|
|
59
|
-
export function ActiveGroupKeyChip({
|
|
60
|
-
label, value, displayValue, clearHref, navigate,
|
|
61
|
-
}: {
|
|
62
|
-
label: string
|
|
63
|
-
value: string
|
|
64
|
-
displayValue: string
|
|
65
|
-
clearHref: string
|
|
66
|
-
navigate: NavigateFn
|
|
67
|
-
}) {
|
|
68
|
-
const onClick = useSpaNavClick(clearHref, navigate)
|
|
69
|
-
return (
|
|
70
|
-
<div className="flex items-center gap-2 rounded-md border bg-muted/40 px-3 py-2 text-sm">
|
|
71
|
-
<span className="text-muted-foreground">Drilled into</span>
|
|
72
|
-
<span className="font-medium text-foreground">
|
|
73
|
-
{label ? `${label}: ` : ''}{displayValue || value}
|
|
74
|
-
</span>
|
|
75
|
-
<a
|
|
76
|
-
href={clearHref}
|
|
77
|
-
onClick={onClick}
|
|
78
|
-
aria-label="Clear drill-in"
|
|
79
|
-
className="ms-auto text-muted-foreground hover:text-foreground"
|
|
80
|
-
>
|
|
81
|
-
×
|
|
82
|
-
</a>
|
|
83
|
-
</div>
|
|
84
|
-
)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Group-heading text wrapped in a real `<a href>` that SPA-navs into the
|
|
89
|
-
* drilled-in URL. Plain left-click intercepts for `useNavigate()`;
|
|
90
|
-
* cmd/ctrl/shift-click + middle-click fall through to the browser so
|
|
91
|
-
* "open in new tab" semantics work. Visually inherits the heading
|
|
92
|
-
* styling — the link adds underline-on-hover affordance without
|
|
93
|
-
* disturbing the surrounding text-transform / size.
|
|
94
|
-
*/
|
|
95
|
-
export function GroupHeadingLink({
|
|
96
|
-
href, navigate, children,
|
|
97
|
-
}: {
|
|
98
|
-
href: string
|
|
99
|
-
navigate: NavigateFn
|
|
100
|
-
children: React.ReactNode
|
|
101
|
-
}) {
|
|
102
|
-
const onClick = useSpaNavClick(href, navigate)
|
|
103
|
-
return (
|
|
104
|
-
<a
|
|
105
|
-
href={href}
|
|
106
|
-
onClick={onClick}
|
|
107
|
-
className="inline-flex items-center gap-1 text-inherit no-underline hover:underline focus:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded"
|
|
108
|
-
>
|
|
109
|
-
{children}
|
|
110
|
-
</a>
|
|
111
|
-
)
|
|
112
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import type { ElementMeta } from '../../../schema/Element.js'
|
|
3
|
-
import type { RenderActionOptions } from '../action/buttons.js'
|
|
4
|
-
|
|
5
|
-
// ─── Row-action chrome ──────────────────────────────────────
|
|
6
|
-
//
|
|
7
|
-
// Inline action strip mounted in the trailing cell of each table row.
|
|
8
|
-
// Per-row visibility and disabled state come from the server-side eval
|
|
9
|
-
// inside `dispatchTable` (`_visibleActions` / `_disabledActions` keys on
|
|
10
|
-
// the row); we just consume the stamped allow/disallow sets here.
|
|
11
|
-
//
|
|
12
|
-
// The dispatch (link / fetch+JSON / modal / confirm) lives on the
|
|
13
|
-
// action layer — `renderActionLike` is injected to keep the cycle
|
|
14
|
-
// between this module and `SchemaRenderer.tsx`'s top-level dispatch
|
|
15
|
-
// clean.
|
|
16
|
-
|
|
17
|
-
type RenderActionLike = (el: ElementMeta, index: number, opts?: RenderActionOptions) => React.ReactNode
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Render row actions inline. Each Action becomes a small button next to
|
|
21
|
-
* the others; an `ActionGroup` placed in row position keeps its dropdown
|
|
22
|
-
* via `ActionGroupTrigger` (the dropdown UX is opt-in via grouping, not
|
|
23
|
-
* a default). The `:id` substitution comes from `opts.ids = [rowId]`.
|
|
24
|
-
*/
|
|
25
|
-
export function renderRowActions(
|
|
26
|
-
rowId: string,
|
|
27
|
-
rowRecord: Record<string, unknown> | undefined,
|
|
28
|
-
actions: ElementMeta[],
|
|
29
|
-
renderActionLike: RenderActionLike,
|
|
30
|
-
): React.ReactNode {
|
|
31
|
-
const rowVisibleSet = new Set((rowRecord?.['_visibleActions'] as string[] | undefined) ?? [])
|
|
32
|
-
const rowDisabledSet = new Set((rowRecord?.['_disabledActions'] as string[] | undefined) ?? [])
|
|
33
|
-
|
|
34
|
-
const visible = actions.filter(a => {
|
|
35
|
-
if (!a['conditional']) return true
|
|
36
|
-
return rowVisibleSet.has(String(a['name'] ?? ''))
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
const decorate = (a: ElementMeta): ElementMeta => {
|
|
40
|
-
const name = String(a['name'] ?? '')
|
|
41
|
-
if (rowDisabledSet.has(name)) {
|
|
42
|
-
return { ...a, disabled: true }
|
|
43
|
-
}
|
|
44
|
-
return a
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<div className="flex items-center justify-end gap-1">
|
|
49
|
-
{visible.map((a, i) => renderActionLike(decorate(a), i, { ids: [rowId], size: 'sm' }))}
|
|
50
|
-
</div>
|
|
51
|
-
)
|
|
52
|
-
}
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import type { NavigateFn } from '../../navigate.js'
|
|
3
|
-
|
|
4
|
-
// ─── Table URL helpers ──────────────────────────────────────
|
|
5
|
-
//
|
|
6
|
-
// The table renderer mirrors its current sort / search / page / group
|
|
7
|
-
// state to the URL query string. These helpers build / parse / dedupe
|
|
8
|
-
// that slice without dragging the server-side dispatcher into the
|
|
9
|
-
// client bundle.
|
|
10
|
-
|
|
11
|
-
export interface TableUrlState {
|
|
12
|
-
search?: string
|
|
13
|
-
sort?: { column: string; direction: 'asc' | 'desc' }
|
|
14
|
-
page?: number
|
|
15
|
-
/** Active group column for `?group=`. Empty string means an explicit
|
|
16
|
-
* "no grouping" override (set on the URL when the user picks "None"
|
|
17
|
-
* in the dropdown to override `defaultGroup`); `undefined` omits the
|
|
18
|
-
* key entirely so the configured default takes over. */
|
|
19
|
-
group?: string
|
|
20
|
-
/** Drilled-in group key for `?groupKey=`. `undefined` omits — the
|
|
21
|
-
* heading is banded (or no group at all); empty string explicitly
|
|
22
|
-
* clears (used by the chip's × so a stale URL value doesn't return
|
|
23
|
-
* via foreign-param round-trip). */
|
|
24
|
-
groupKey?: string
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Mirror of `prefixedKey` in `elements/dispatchTable.ts`. Kept inline so
|
|
28
|
-
// SchemaRenderer doesn't drag the server-side dispatcher into the client
|
|
29
|
-
// bundle.
|
|
30
|
-
export function prefixK(prefix: string | undefined, key: string): string {
|
|
31
|
-
return prefix === undefined || prefix === '' ? key : `${prefix}_${key}`
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
let cachedSearchString: string | null = null
|
|
35
|
-
let cachedSearchParams: URLSearchParams | null = null
|
|
36
|
-
|
|
37
|
-
export function getCurrentSearchParams(): URLSearchParams | null {
|
|
38
|
-
if (typeof window === 'undefined') return null
|
|
39
|
-
const s = window.location.search
|
|
40
|
-
if (s === cachedSearchString && cachedSearchParams) return cachedSearchParams
|
|
41
|
-
cachedSearchString = s
|
|
42
|
-
cachedSearchParams = new URLSearchParams(s)
|
|
43
|
-
return cachedSearchParams
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function SearchFormHiddenInputs({ prefix }: { prefix: string | undefined }): React.ReactElement {
|
|
47
|
-
const sp = getCurrentSearchParams()
|
|
48
|
-
if (!sp) return <></>
|
|
49
|
-
const searchKey = prefixK(prefix, 'search')
|
|
50
|
-
const pageKey = prefixK(prefix, 'page')
|
|
51
|
-
const inputs: React.ReactElement[] = []
|
|
52
|
-
let i = 0
|
|
53
|
-
for (const [k, v] of sp) {
|
|
54
|
-
if (k === searchKey || k === pageKey) continue
|
|
55
|
-
inputs.push(<input key={i++} type="hidden" name={k} value={v} />)
|
|
56
|
-
}
|
|
57
|
-
return <>{inputs}</>
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export function buildTableQuery(
|
|
61
|
-
state: TableUrlState,
|
|
62
|
-
override: TableUrlState,
|
|
63
|
-
pathname: string,
|
|
64
|
-
filterValues: Record<string, string> = {},
|
|
65
|
-
prefix?: string,
|
|
66
|
-
): string {
|
|
67
|
-
const merged: TableUrlState = { ...state, ...override }
|
|
68
|
-
const params = new URLSearchParams()
|
|
69
|
-
// Foreign URL params (other tables' state, app-level params) round-trip
|
|
70
|
-
// verbatim so this builder only ever rewrites its own slice.
|
|
71
|
-
const currentParams = getCurrentSearchParams()
|
|
72
|
-
if (currentParams) {
|
|
73
|
-
const ours = new Set([
|
|
74
|
-
prefixK(prefix, 'search'),
|
|
75
|
-
prefixK(prefix, 'sort'),
|
|
76
|
-
prefixK(prefix, 'page'),
|
|
77
|
-
prefixK(prefix, 'perPage'),
|
|
78
|
-
prefixK(prefix, 'group'),
|
|
79
|
-
prefixK(prefix, 'groupKey'),
|
|
80
|
-
...Object.keys(filterValues).map(n => prefixK(prefix, n)),
|
|
81
|
-
])
|
|
82
|
-
for (const [k, v] of currentParams) {
|
|
83
|
-
if (ours.has(k)) continue
|
|
84
|
-
params.set(k, v)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
// Carry forward active filter values so sort/pagination links don't
|
|
88
|
-
// accidentally clear them. Filter names can't collide with reserved
|
|
89
|
-
// keys (search/sort/page/perPage/group) — that's enforced upstream.
|
|
90
|
-
for (const [name, val] of Object.entries(filterValues)) {
|
|
91
|
-
if (val) params.set(prefixK(prefix, name), val)
|
|
92
|
-
}
|
|
93
|
-
if (merged.search) params.set(prefixK(prefix, 'search'), merged.search)
|
|
94
|
-
if (merged.sort) params.set(prefixK(prefix, 'sort'), `${merged.sort.column}:${merged.sort.direction}`)
|
|
95
|
-
if (merged.page && merged.page > 1) params.set(prefixK(prefix, 'page'), String(merged.page))
|
|
96
|
-
if (merged.group !== undefined) params.set(prefixK(prefix, 'group'), merged.group)
|
|
97
|
-
// groupKey is sparse — only writes when the override sets a non-empty
|
|
98
|
-
// value. Drill-out (chip ×) passes `''` to clear; the foreign-param
|
|
99
|
-
// dedupe set above already filtered the stale value out, so an empty
|
|
100
|
-
// override produces a URL without the key.
|
|
101
|
-
if (merged.groupKey) params.set(prefixK(prefix, 'groupKey'), merged.groupKey)
|
|
102
|
-
const qs = params.toString()
|
|
103
|
-
// Always anchor to a real pathname — Vike's client-side router treats
|
|
104
|
-
// a bare `?qs` href as a fresh URL with empty pathname, which routes
|
|
105
|
-
// to the dashboard and blanks the page during SPA navigation.
|
|
106
|
-
const base = pathname || (typeof window !== 'undefined' ? window.location.pathname : '')
|
|
107
|
-
return qs ? `${base}?${qs}` : (base || '#')
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* SPA-navigate to the current URL with the filter slice patched in
|
|
112
|
-
* place. `null` or empty-string values delete the key; non-empty values
|
|
113
|
-
* set it. The accompanying `?page` is always cleared so users land on
|
|
114
|
-
* the first page of the relaxed / tightened set. No-op on SSR.
|
|
115
|
-
*
|
|
116
|
-
* Used by every filter widget's "apply" / "clear" path (FilterSelect /
|
|
117
|
-
* MultiSelect / DateRange / Form / QueryBuilder + ActiveFiltersBar).
|
|
118
|
-
*/
|
|
119
|
-
export function patchFilterUrl(
|
|
120
|
-
navigate: NavigateFn,
|
|
121
|
-
prefix: string | undefined,
|
|
122
|
-
patches: Record<string, string | null>,
|
|
123
|
-
): void {
|
|
124
|
-
if (typeof window === 'undefined') return
|
|
125
|
-
const url = new URL(window.location.href)
|
|
126
|
-
for (const [name, value] of Object.entries(patches)) {
|
|
127
|
-
const k = prefixK(prefix, name)
|
|
128
|
-
if (value === null || value === '') url.searchParams.delete(k)
|
|
129
|
-
else url.searchParams.set(k, value)
|
|
130
|
-
}
|
|
131
|
-
url.searchParams.delete(prefixK(prefix, 'page'))
|
|
132
|
-
void navigate(url.pathname + url.search)
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export function nextSortDir(
|
|
136
|
-
current: TableUrlState['sort'],
|
|
137
|
-
column: string,
|
|
138
|
-
): { column: string; direction: 'asc' | 'desc' } {
|
|
139
|
-
if (current?.column === column) {
|
|
140
|
-
return { column, direction: current.direction === 'asc' ? 'desc' : 'asc' }
|
|
141
|
-
}
|
|
142
|
-
return { column, direction: 'asc' }
|
|
143
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { resolveTheme } from '../../theme/resolve.js'
|
|
2
|
-
import { generateThemeCSS } from '../../theme/generate-css.js'
|
|
3
|
-
import type { ThemeConfig } from '../../theme/types.js'
|
|
4
|
-
|
|
5
|
-
const cap = (s: string) => s.charAt(0).toUpperCase() + s.slice(1)
|
|
6
|
-
|
|
7
|
-
// Fontshare overrides for fonts not on Google Fonts.
|
|
8
|
-
const FONTSHARE_URLS: Record<string, string> = {
|
|
9
|
-
Satoshi: 'https://api.fontshare.com/v2/css?f[]=satoshi@300,500,700&display=swap',
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function fontStylesheetUrl(family: string): string {
|
|
13
|
-
return FONTSHARE_URLS[family]
|
|
14
|
-
?? `https://fonts.googleapis.com/css2?family=${family.replace(/ /g, '+')}:wght@400;500;600;700&display=swap`
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** Apply theme to parent page immediately — updates the <style> tag AND inline styles. */
|
|
18
|
-
export function applyToParent(config: Partial<ThemeConfig>) {
|
|
19
|
-
const merged: ThemeConfig = { preset: 'vega', ...config }
|
|
20
|
-
const resolved = resolveTheme(merged)
|
|
21
|
-
const css = generateThemeCSS(resolved)
|
|
22
|
-
|
|
23
|
-
// Update the ThemeProvider's <style> tag so both light and dark mode vars are current
|
|
24
|
-
const id = 'pilotiq-theme'
|
|
25
|
-
let style = document.getElementById(id) as HTMLStyleElement | null
|
|
26
|
-
if (!style) {
|
|
27
|
-
style = document.createElement('style')
|
|
28
|
-
style.id = id
|
|
29
|
-
document.head.appendChild(style)
|
|
30
|
-
}
|
|
31
|
-
style.textContent = css
|
|
32
|
-
|
|
33
|
-
// Also set inline styles for immediate visual feedback (overrides @layer)
|
|
34
|
-
const root = document.documentElement
|
|
35
|
-
const isDark = root.classList.contains('dark')
|
|
36
|
-
const vars = isDark ? resolved.dark : resolved.light
|
|
37
|
-
|
|
38
|
-
for (const [key, value] of Object.entries(vars)) {
|
|
39
|
-
root.style.setProperty(key, value)
|
|
40
|
-
}
|
|
41
|
-
root.style.setProperty('--radius', resolved.radius)
|
|
42
|
-
root.style.setProperty('--spacing', resolved.spacing)
|
|
43
|
-
if (resolved.fontFamily?.body) {
|
|
44
|
-
root.style.setProperty('--font-sans', resolved.fontFamily.body)
|
|
45
|
-
root.style.setProperty('--default-font-family', resolved.fontFamily.body)
|
|
46
|
-
}
|
|
47
|
-
if (resolved.fontFamily?.heading) {
|
|
48
|
-
root.style.setProperty('--font-heading', resolved.fontFamily.heading)
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Apply config changes to a live preview document — patches CSS vars, font
|
|
54
|
-
* links, mode class, and preset fingerprint text WITHOUT reloading the iframe.
|
|
55
|
-
* Called on every config/mode change so the user's scroll position survives.
|
|
56
|
-
*/
|
|
57
|
-
export function applyConfigToDoc(doc: Document, config: Partial<ThemeConfig>, mode: 'light' | 'dark') {
|
|
58
|
-
const merged: ThemeConfig = { preset: 'vega', ...config }
|
|
59
|
-
const resolved = resolveTheme(merged)
|
|
60
|
-
const themeCSS = generateThemeCSS(resolved)
|
|
61
|
-
|
|
62
|
-
// Light/dark switch — generateThemeCSS emits `.dark { ... }`, so toggling
|
|
63
|
-
// the class on <html> picks up the right var set without a re-render.
|
|
64
|
-
doc.documentElement.className = mode
|
|
65
|
-
|
|
66
|
-
// Theme CSS — single <style id="pilotiq-theme"> we keep refilling.
|
|
67
|
-
let style = doc.getElementById('pilotiq-theme') as HTMLStyleElement | null
|
|
68
|
-
if (!style) {
|
|
69
|
-
style = doc.createElement('style')
|
|
70
|
-
style.id = 'pilotiq-theme'
|
|
71
|
-
doc.head.appendChild(style)
|
|
72
|
-
}
|
|
73
|
-
style.textContent = themeCSS
|
|
74
|
-
|
|
75
|
-
// Font links — diff against current set so the browser doesn't re-fetch
|
|
76
|
-
// already-loaded stylesheets (avoids the brief FOIT on every config change).
|
|
77
|
-
const wanted = new Set<string>()
|
|
78
|
-
if (resolved.fonts?.body) wanted.add(resolved.fonts.body)
|
|
79
|
-
if (resolved.fonts?.heading) wanted.add(resolved.fonts.heading)
|
|
80
|
-
const existing = doc.querySelectorAll<HTMLLinkElement>('link[data-pilotiq-font]')
|
|
81
|
-
const haveSet = new Set<string>()
|
|
82
|
-
existing.forEach(link => {
|
|
83
|
-
const family = link.dataset.pilotiqFont ?? ''
|
|
84
|
-
if (!wanted.has(family)) link.remove()
|
|
85
|
-
else haveSet.add(family)
|
|
86
|
-
})
|
|
87
|
-
for (const family of wanted) {
|
|
88
|
-
if (haveSet.has(family)) continue
|
|
89
|
-
const link = doc.createElement('link')
|
|
90
|
-
link.rel = 'stylesheet'
|
|
91
|
-
link.href = fontStylesheetUrl(family)
|
|
92
|
-
link.dataset.pilotiqFont = family
|
|
93
|
-
doc.head.appendChild(link)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Preset fingerprint heading — only piece of body text that depends on config.
|
|
97
|
-
const fp = doc.getElementById('preset-fingerprint')
|
|
98
|
-
if (fp) fp.textContent = `${cap(config.preset ?? 'vega')} - ${resolved.fonts?.heading ?? 'System'}`
|
|
99
|
-
}
|