@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
package/src/elements/Table.ts
DELETED
|
@@ -1,850 +0,0 @@
|
|
|
1
|
-
import { Element, type ElementMeta } from '../schema/Element.js'
|
|
2
|
-
import { Column } from '../Column.js'
|
|
3
|
-
import { Action } from '../actions/Action.js'
|
|
4
|
-
import { ActionGroup } from '../actions/ActionGroup.js'
|
|
5
|
-
import { Filter } from '../filters/Filter.js'
|
|
6
|
-
import type { SummaryResult } from '../summarizers/Summarizer.js'
|
|
7
|
-
import { TableGroup, type TableGroupMeta } from './TableGroup.js'
|
|
8
|
-
|
|
9
|
-
/** Either a plain `Action` or an `ActionGroup` (a labelled dropdown of
|
|
10
|
-
* actions). Both can sit in any of the table action slots — the slot
|
|
11
|
-
* stamps the placement automatically on whichever shape arrives. */
|
|
12
|
-
type ActionOrGroup = Action | ActionGroup
|
|
13
|
-
|
|
14
|
-
export type SortDirection = 'asc' | 'desc'
|
|
15
|
-
|
|
16
|
-
export interface TableContext<R = unknown> {
|
|
17
|
-
request?: unknown
|
|
18
|
-
search?: string
|
|
19
|
-
sort?: { column: string; direction: SortDirection }
|
|
20
|
-
page?: number
|
|
21
|
-
perPage?: number
|
|
22
|
-
records?: R[]
|
|
23
|
-
/** Active filter values keyed by filter name (e.g. `{ status: 'published' }`).
|
|
24
|
-
* Empty / unsupplied filters are absent. */
|
|
25
|
-
filters?: Record<string, string>
|
|
26
|
-
/** Active list-page tab name (e.g. `'drafts'`). Set by
|
|
27
|
-
* `pageData.resourceIndexData` from `?tab=` before records run. User-
|
|
28
|
-
* supplied `Table.records(fn)` handlers can branch on this for custom
|
|
29
|
-
* narrowing; the model adapter consults `tabQuery` instead. */
|
|
30
|
-
tab?: string
|
|
31
|
-
/** Active list-page tab's `modifyQuery` chain — applied alongside
|
|
32
|
-
* filter `where` clauses in `modelTableRecords`. Set by the framework;
|
|
33
|
-
* users configure it via `ListTab.modifyQuery(fn)`. */
|
|
34
|
-
tabQuery?: (q: import('../orm/modelDefaults.js').ModelQuery) => import('../orm/modelDefaults.js').ModelQuery
|
|
35
|
-
/** Drill-in scope when the user clicked a group heading. Carries the
|
|
36
|
-
* resolved `TableGroup` instance + the bucket key (the same value
|
|
37
|
-
* `getKeyFromRecordUsing` would produce). Set by `loadTableRecords`
|
|
38
|
-
* after reconciling `?<prefix>groupKey=`. The model adapter calls
|
|
39
|
-
* `group.resolveScoper()(q, key)` after filters but before pagination;
|
|
40
|
-
* user-supplied `Table.records(fn)` handlers can branch on this for
|
|
41
|
-
* cross-table joins or non-default narrowing. */
|
|
42
|
-
groupScope?: {
|
|
43
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
44
|
-
group: import('./TableGroup.js').TableGroup<any>
|
|
45
|
-
key: string
|
|
46
|
-
}
|
|
47
|
-
/** Whatever `Pilotiq.user(req => …)` returned for the current request.
|
|
48
|
-
* Forwarded into `Resource.query(ctx)` by `modelTableRecords` so user-
|
|
49
|
-
* installed scopes (tenant filters, etc.) see the same opaque user
|
|
50
|
-
* shape that authorization predicates do. Absent on the dispatcher's
|
|
51
|
-
* input ctx when no user resolver is configured. */
|
|
52
|
-
user?: unknown
|
|
53
|
-
[key: string]: unknown
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export type TableQueryHandler<Q = unknown> = (
|
|
57
|
-
query: Q,
|
|
58
|
-
ctx: TableContext,
|
|
59
|
-
) => Q | Promise<Q>
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* User-supplied row loader. Returns the records to render plus an optional
|
|
63
|
-
* `total` for pagination. When `total` is omitted the framework treats
|
|
64
|
-
* `rows.length` as the total.
|
|
65
|
-
*/
|
|
66
|
-
export interface TableRecordsResult<R = unknown> {
|
|
67
|
-
rows: R[]
|
|
68
|
-
total?: number
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export type TableRecordsHandler<R = unknown> = (
|
|
72
|
-
ctx: TableContext<R>,
|
|
73
|
-
) => TableRecordsResult<R> | R[] | Promise<TableRecordsResult<R> | R[]>
|
|
74
|
-
|
|
75
|
-
export interface TableEmptyState {
|
|
76
|
-
heading?: string
|
|
77
|
-
description?: string
|
|
78
|
-
icon?: string
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Per-row URL function. Returns the destination URL for clicks on a
|
|
83
|
-
* row's data cells — each data column wraps its content in a real
|
|
84
|
-
* `<a href>` (Filament-style), so right-click / cmd-click / middle-click
|
|
85
|
-
* "open in new tab" all work natively. Plain left-clicks are intercepted
|
|
86
|
-
* for SPA navigation. Return `undefined` for rows that shouldn't be
|
|
87
|
-
* clickable. Action and bulk-select cells are never wrapped.
|
|
88
|
-
*
|
|
89
|
-
* Per-column overrides: `Column.recordUrl(fn)` swaps in a different URL
|
|
90
|
-
* for that column's cell, and `Column.recordUrl(false)` opts a column
|
|
91
|
-
* out entirely.
|
|
92
|
-
*/
|
|
93
|
-
export type RecordUrlHandler<R = unknown> = (record: R) => string | undefined
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Per-row CSS class function. Returns extra Tailwind / CSS class names
|
|
97
|
-
* appended to that row's `<tr>`. Useful for status-driven row tinting
|
|
98
|
-
* ("destructive" when overdue, "warning" when stale). Result is appended
|
|
99
|
-
* after the framework's own row classes (striped, cursor-pointer); user
|
|
100
|
-
* classes win on equal specificity. Throwing or returning falsy stays
|
|
101
|
-
* silent — the row just renders without extras.
|
|
102
|
-
*/
|
|
103
|
-
export type RecordClassesHandler<R = unknown> = (record: R) => string | undefined
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Per-row card content function. Receives the record + the current
|
|
107
|
-
* `TableContext` and returns an `Element[]` rendered inside a card in
|
|
108
|
-
* `contentLayout('cards')` mode. Resolved via `resolveSchema` per-row in
|
|
109
|
-
* `loadTableRecords`; the result is stamped onto `row._cardChildren`.
|
|
110
|
-
*
|
|
111
|
-
* Typical content: `Image`, `Heading`, `Text`, `Icon`, `Badge`-style
|
|
112
|
-
* `Entry` primitives, plus layout primitives like `Group / Split / Grid`.
|
|
113
|
-
* Display-only — `Form` / `Field` / `Filter` / `Action` inside the card
|
|
114
|
-
* schema is unsupported in v1.
|
|
115
|
-
*/
|
|
116
|
-
export type CardSchemaHandler<R = unknown> = (
|
|
117
|
-
record: R,
|
|
118
|
-
ctx: TableContext<R>,
|
|
119
|
-
) => Element[] | Promise<Element[]>
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Responsive grid column counts for `contentLayout('cards')`. Each
|
|
123
|
-
* breakpoint maps to its Tailwind container query (`@sm` = ≥40rem etc.);
|
|
124
|
-
* `default` is the base (no media query). Unspecified breakpoints inherit
|
|
125
|
-
* the next-smaller. v1 caps each value at 12 to match the grid's column
|
|
126
|
-
* limits — values outside `[1, 12]` clamp.
|
|
127
|
-
*/
|
|
128
|
-
export interface CardsPerRow {
|
|
129
|
-
default?: number
|
|
130
|
-
sm?: number
|
|
131
|
-
md?: number
|
|
132
|
-
lg?: number
|
|
133
|
-
xl?: number
|
|
134
|
-
'2xl'?: number
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Table content-layout mode. `'table'` (default) renders the classic
|
|
139
|
-
* `<thead>` + `<tbody>` HTML table. `'cards'` hides the header row and
|
|
140
|
-
* renders each row as a card in a CSS grid. Columns still drive search /
|
|
141
|
-
* sort / filter / group / summarize semantics in cards mode; only the
|
|
142
|
-
* row-level rendering differs.
|
|
143
|
-
*/
|
|
144
|
-
export type ContentLayout = 'table' | 'cards'
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Where filters render relative to the table.
|
|
148
|
-
* - `'modal'` (default) — chrome-only popover above the table, opened by
|
|
149
|
-
* the toolbar Filters button.
|
|
150
|
-
* - `'above-content'` — inline strip rendered between the header bar and
|
|
151
|
-
* the table; every filter widget is always visible.
|
|
152
|
-
* - `'above-content-collapsible'` — same strip, hidden behind the toolbar
|
|
153
|
-
* Filters button. Open / closed state persists per table path.
|
|
154
|
-
* - `'below-content'` — inline strip rendered after the table (under the
|
|
155
|
-
* pagination row).
|
|
156
|
-
*
|
|
157
|
-
* Filament v5's sidebar positions (`BeforeContent` / `AfterContent`)
|
|
158
|
-
* reshape the page rather than the table chrome and are deferred until a
|
|
159
|
-
* consumer asks.
|
|
160
|
-
*/
|
|
161
|
-
export type FiltersLayout =
|
|
162
|
-
| 'modal'
|
|
163
|
-
| 'above-content'
|
|
164
|
-
| 'above-content-collapsible'
|
|
165
|
-
| 'below-content'
|
|
166
|
-
|
|
167
|
-
export interface TableMeta extends ElementMeta {
|
|
168
|
-
type: 'table'
|
|
169
|
-
defaultSort?: { column: string; direction: SortDirection }
|
|
170
|
-
perPage?: number
|
|
171
|
-
searchable: boolean
|
|
172
|
-
|
|
173
|
-
// Top-bar chrome
|
|
174
|
-
heading?: string
|
|
175
|
-
description?: string
|
|
176
|
-
striped?: boolean
|
|
177
|
-
emptyState?: TableEmptyState
|
|
178
|
-
/**
|
|
179
|
-
* Distinct empty-state for the "filter/search active but no rows match"
|
|
180
|
-
* case. When set AND a search query or one or more URL filter keys are
|
|
181
|
-
* present, the renderer picks this shape over `emptyState`. When unset,
|
|
182
|
-
* the renderer falls back to `emptyState` (or the framework defaults
|
|
183
|
-
* — "No matching records" with a generic clear-filters hint).
|
|
184
|
-
*/
|
|
185
|
-
filteredEmptyState?: TableEmptyState
|
|
186
|
-
|
|
187
|
-
/**
|
|
188
|
-
* Per-row URL stamped onto each row's data under the reserved
|
|
189
|
-
* `_recordUrl` key (alongside the existing `_visibleActions` /
|
|
190
|
-
* `_disabledActions` / `_formatted` keys). The renderer reads from
|
|
191
|
-
* the row, not the table meta — `RecordUrlHandler` is server-side only.
|
|
192
|
-
*/
|
|
193
|
-
recordUrl?: true
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Server-side per-row CSS marker — same convention as `recordUrl`.
|
|
197
|
-
* Each row's `_recordClasses` carries the resolved string; this flag
|
|
198
|
-
* is just a hint for the renderer to look for it.
|
|
199
|
-
*/
|
|
200
|
-
recordClasses?: true
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* Auto-refresh interval in seconds. The client renderer kicks off a
|
|
204
|
-
* `setInterval` that re-fetches the current URL via the SPA navigator
|
|
205
|
-
* — pagination / sort / filter state is preserved because we re-visit
|
|
206
|
-
* the same `pathname + search`. Hidden tabs pause to avoid hammering
|
|
207
|
-
* the server in the background; resume on visibility change. Unset =
|
|
208
|
-
* no polling.
|
|
209
|
-
*/
|
|
210
|
-
pollInterval?: number
|
|
211
|
-
|
|
212
|
-
/** The currently active group — the column rows are banded by. Set
|
|
213
|
-
* each request from either `?group=` or `defaultGroup(...)`. The
|
|
214
|
-
* renderer emits a heading row whenever `_groupValue` changes between
|
|
215
|
-
* adjacent rows. Always a column name; the rich metadata (label /
|
|
216
|
-
* collapsibility / etc.) lives on `groups` below. */
|
|
217
|
-
defaultGroup?: string
|
|
218
|
-
|
|
219
|
-
/** Group selector options. When 2+ groups are registered the renderer
|
|
220
|
-
* mounts a "Group by" dropdown above the table; the user's selection
|
|
221
|
-
* round-trips via `?group=`. Empty / absent means the bare-column form
|
|
222
|
-
* (`Table.defaultGroup('col')` only). */
|
|
223
|
-
groups?: TableGroupMeta[]
|
|
224
|
-
|
|
225
|
-
/** The drilled-in group key for this request. When set, the renderer
|
|
226
|
-
* suppresses group banding (no heading rows, no per-group summaries),
|
|
227
|
-
* shows a "Drilled into <Label>: <Key>" chip above the table with an
|
|
228
|
-
* × to clear, and the records have already been narrowed to that
|
|
229
|
-
* bucket server-side via `TableGroup.scopeQueryByKey`. Sparse —
|
|
230
|
-
* omitted unless `?<prefix>groupKey=<value>` is present AND the named
|
|
231
|
-
* group exists AND is `scopable`. */
|
|
232
|
-
activeGroupKey?: string
|
|
233
|
-
|
|
234
|
-
/** Per-column summary results — keyed by column name, each entry is the
|
|
235
|
-
* computed `SummaryResult[]` for that column's `summarize([…])`. Filled
|
|
236
|
-
* in by `loadTableRecords` after `records()` runs. Renderer emits a
|
|
237
|
-
* `<tfoot>` row when this is present. */
|
|
238
|
-
summaries?: Record<string, SummaryResult[]>
|
|
239
|
-
|
|
240
|
-
/** Per-group summaries — outer key = `_groupValue`, inner = column
|
|
241
|
-
* name, value = `SummaryResult[]`. Computed only when an active group
|
|
242
|
-
* is set AND at least one column has summarizers. Renderer emits an
|
|
243
|
-
* inline summary row at the END of each group band, aligned to the
|
|
244
|
-
* same columns as the global footer. */
|
|
245
|
-
groupSummaries?: Record<string, Record<string, SummaryResult[]>>
|
|
246
|
-
|
|
247
|
-
/** Drag-to-reorder is enabled on this table. The renderer adds a grip
|
|
248
|
-
* handle column and binds HTML5 DnD on each `<tr>`. The actual sort
|
|
249
|
-
* column the order is written back to lives on `reorderableColumn`. */
|
|
250
|
-
reorderable?: true
|
|
251
|
-
|
|
252
|
-
/** Name of the column the persisted order is written to. Defaults to
|
|
253
|
-
* `'sort'` when `Table.reorderable()` is called without an argument.
|
|
254
|
-
* Pilotiq's default `Table.records()` adapter uses this as the default
|
|
255
|
-
* sort column when the URL doesn't override `?sort=…`. */
|
|
256
|
-
reorderableColumn?: string
|
|
257
|
-
|
|
258
|
-
/** Stamped server-side at render time. The renderer POSTs the new id
|
|
259
|
-
* order here when a row is dropped. Absent when the route handler
|
|
260
|
-
* hasn't tagged the table (panel boot will throw before that point if
|
|
261
|
-
* `reorderable()` is set without a corresponding `model.reorder`). */
|
|
262
|
-
reorderUrl?: string
|
|
263
|
-
|
|
264
|
-
/** Tier-3 — set when `Resource.deferLoading = true`. The SSR pass
|
|
265
|
-
* skips `Table.records()` so the client paints a skeleton on first
|
|
266
|
-
* frame and fetches rows from `tableUrl` after mount. URL chrome
|
|
267
|
-
* (current sort / search / page / group) still mirrors so the
|
|
268
|
-
* skeleton frame doesn't reset user-visible state. */
|
|
269
|
-
deferred?: true
|
|
270
|
-
|
|
271
|
-
/** Stamped server-side at render time alongside `deferred`. The
|
|
272
|
-
* renderer GETs this URL with the page's current query string to
|
|
273
|
-
* fetch rows after mount. Empty when `deferLoading` is off. */
|
|
274
|
-
tableUrl?: string
|
|
275
|
-
|
|
276
|
-
/** Tier-3 — when set, the table's URL state (search / sort / page /
|
|
277
|
-
* perPage / group / filter values) is namespaced under the identifier
|
|
278
|
-
* so multiple tables on one page don't fight over `?search=` etc.
|
|
279
|
-
* `?orders_search=foo&invoices_sort=date:desc`. Off by default — bare
|
|
280
|
-
* keys are still used when no identifier is set. Custom-page-only:
|
|
281
|
-
* resource list pages have one table by default and don't need it. */
|
|
282
|
-
queryStringIdentifier?: string
|
|
283
|
-
|
|
284
|
-
/** Content-layout mode. Absent = `'table'` (classic HTML table); when
|
|
285
|
-
* `'cards'` the renderer hides the column header row and arranges rows
|
|
286
|
-
* as cards in a CSS grid. Per-row card content is stamped on each row
|
|
287
|
-
* under `_cardChildren: ElementMeta[]` by `loadTableRecords`. Columns
|
|
288
|
-
* still drive search / sort / filter / group / summarize semantics. */
|
|
289
|
-
contentLayout?: 'cards'
|
|
290
|
-
|
|
291
|
-
/** Responsive card column counts for `contentLayout: 'cards'`. Renderer
|
|
292
|
-
* maps each breakpoint to a `@container`-scoped Tailwind grid class. */
|
|
293
|
-
cardsPerRow?: CardsPerRow
|
|
294
|
-
|
|
295
|
-
/** Filter layout position. Absent = `'modal'` (current popover behavior).
|
|
296
|
-
* The renderer swaps the toolbar Filters button for an inline strip in
|
|
297
|
-
* the matching slot when set. See `FiltersLayout` for the full enum. */
|
|
298
|
-
filtersLayout?: Exclude<FiltersLayout, 'modal'>
|
|
299
|
-
|
|
300
|
-
// Render-time state — populated by the framework after `records()` runs.
|
|
301
|
-
rows?: unknown[]
|
|
302
|
-
total?: number
|
|
303
|
-
currentSort?: { column: string; direction: SortDirection }
|
|
304
|
-
search?: string
|
|
305
|
-
currentPage?: number
|
|
306
|
-
/** Absolute pathname the table lives at (e.g. `/admin/articles`). The
|
|
307
|
-
* renderer prefixes sort/pagination/search hrefs with this so SPA
|
|
308
|
-
* navigation resolves against the right route — Vike's client-side
|
|
309
|
-
* router doesn't follow `?qs`-only relative links. */
|
|
310
|
-
currentPath?: string
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Table container. Children are typically `Column[]` plus header / row /
|
|
315
|
-
* bulk Actions. The query hook stays server-side; toMeta emits the
|
|
316
|
-
* configured sort/pagination state, the searchable flag, and (after the
|
|
317
|
-
* framework runs `records()`) the resolved rows + pagination state.
|
|
318
|
-
*/
|
|
319
|
-
export class Table<R = unknown, Q = unknown> extends Element {
|
|
320
|
-
private _query?: TableQueryHandler<Q>
|
|
321
|
-
private _records?: TableRecordsHandler<R>
|
|
322
|
-
private _defaultSort?: { column: string; direction: SortDirection }
|
|
323
|
-
private _perPage?: number
|
|
324
|
-
|
|
325
|
-
// Top-bar chrome
|
|
326
|
-
private _heading?: string
|
|
327
|
-
private _description?: string
|
|
328
|
-
private _striped = false
|
|
329
|
-
private _emptyState?: TableEmptyState
|
|
330
|
-
private _filteredEmptyState?: TableEmptyState
|
|
331
|
-
|
|
332
|
-
// Render-time state
|
|
333
|
-
private _rows?: R[]
|
|
334
|
-
private _total?: number
|
|
335
|
-
private _currentSort?: { column: string; direction: SortDirection }
|
|
336
|
-
private _currentSearch?: string
|
|
337
|
-
private _currentPage?: number
|
|
338
|
-
private _currentPath?: string
|
|
339
|
-
private _recordUrl?: RecordUrlHandler<R>
|
|
340
|
-
private _recordClasses?: RecordClassesHandler<R>
|
|
341
|
-
private _pollInterval?: number
|
|
342
|
-
private _defaultGroup?: string
|
|
343
|
-
// Variance-relaxed — covariant `TableGroup<R>[]` ergonomics
|
|
344
|
-
// matter more than tight invariance against the table's `R` parameter.
|
|
345
|
-
private _groups: TableGroup<any>[] = [] // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
346
|
-
private _activeGroup?: string
|
|
347
|
-
private _activeGroupKey?: string
|
|
348
|
-
private _summaries?: Record<string, SummaryResult[]>
|
|
349
|
-
private _groupSummaries?: Record<string, Record<string, SummaryResult[]>>
|
|
350
|
-
private _reorderableColumn?: string
|
|
351
|
-
private _reorderUrl?: string
|
|
352
|
-
private _deferred = false
|
|
353
|
-
private _tableUrl?: string
|
|
354
|
-
private _queryStringIdentifier?: string
|
|
355
|
-
private _contentLayout: ContentLayout = 'table'
|
|
356
|
-
private _cardSchema?: CardSchemaHandler<R>
|
|
357
|
-
private _cardsPerRow?: CardsPerRow
|
|
358
|
-
private _filtersLayout: FiltersLayout = 'modal'
|
|
359
|
-
|
|
360
|
-
private constructor() { super() }
|
|
361
|
-
|
|
362
|
-
static make<R = unknown, Q = unknown>(): Table<R, Q> {
|
|
363
|
-
return new Table<R, Q>()
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// ─── Children ─────────────────────────────────────────
|
|
367
|
-
|
|
368
|
-
/** Set children directly — typically a mix of Columns and Actions. */
|
|
369
|
-
schema(elements: Element[]): this {
|
|
370
|
-
this._children = elements
|
|
371
|
-
return this
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/** Shorthand: replace the column children. Existing actions are preserved. */
|
|
375
|
-
columns(cols: Column[]): this {
|
|
376
|
-
const existing = this._children ?? []
|
|
377
|
-
const nonColumns = existing.filter(el => !(el instanceof Column))
|
|
378
|
-
this._children = [...cols, ...nonColumns]
|
|
379
|
-
return this
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
/** Shorthand: append actions to the children. Placement on each action /
|
|
383
|
-
* group is preserved as-is; use the slot variants below
|
|
384
|
-
* (`recordActions`, `headerActions`, `bulkActions`) when you want the
|
|
385
|
-
* table to assign placement automatically. Both `Action` and
|
|
386
|
-
* `ActionGroup` are accepted — groups render as dropdown triggers. */
|
|
387
|
-
actions(acts: ActionOrGroup[]): this {
|
|
388
|
-
const existing = this._children ?? []
|
|
389
|
-
this._children = [...existing, ...acts]
|
|
390
|
-
return this
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/** Per-row actions slot — rendered in a DropdownMenu on each row.
|
|
394
|
-
* Stamps `placement: 'row'` on each action so callers don't need
|
|
395
|
-
* `Action.make(...).row()` boilerplate. */
|
|
396
|
-
recordActions(acts: ActionOrGroup[]): this {
|
|
397
|
-
return this.actions(acts.map(a => a.placement('row')))
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/** Header actions slot — rendered top-right of the table. */
|
|
401
|
-
headerActions(acts: ActionOrGroup[]): this {
|
|
402
|
-
return this.actions(acts.map(a => a.placement('header')))
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
/** Bulk actions slot — shown in a toolbar when rows are selected. */
|
|
406
|
-
bulkActions(acts: ActionOrGroup[]): this {
|
|
407
|
-
return this.actions(acts.map(a => a.placement('bulk')))
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/** Shorthand: replace the filter children. Existing columns/actions stay. */
|
|
411
|
-
filters(filters: Filter[]): this {
|
|
412
|
-
const existing = this._children ?? []
|
|
413
|
-
const nonFilters = existing.filter(el => !(el instanceof Filter))
|
|
414
|
-
this._children = [...nonFilters, ...filters]
|
|
415
|
-
return this
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// ─── Lifecycle config ────────────────────────────────
|
|
419
|
-
|
|
420
|
-
/** Adapter-flavored query builder hook. Reserved for ORM adapters in Phase 3+. */
|
|
421
|
-
query(fn: TableQueryHandler<Q>): this { this._query = fn; return this }
|
|
422
|
-
|
|
423
|
-
/** Row loader — receives a `TableContext` and returns rows (and optional total). */
|
|
424
|
-
records(fn: TableRecordsHandler<R>): this { this._records = fn; return this }
|
|
425
|
-
|
|
426
|
-
defaultSort(column: string, direction: SortDirection = 'asc'): this {
|
|
427
|
-
this._defaultSort = { column, direction }
|
|
428
|
-
return this
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
paginate(perPage: number): this { this._perPage = perPage; return this }
|
|
432
|
-
|
|
433
|
-
// ─── Top-bar chrome ───────────────────────────────────
|
|
434
|
-
|
|
435
|
-
/** Title rendered above the table (left of the header bar). */
|
|
436
|
-
heading(s: string): this { this._heading = s; return this }
|
|
437
|
-
|
|
438
|
-
/** Subtitle rendered under the heading. */
|
|
439
|
-
description(s: string): this { this._description = s; return this }
|
|
440
|
-
|
|
441
|
-
/** Alternating row backgrounds for visual scanning. */
|
|
442
|
-
striped(v = true): this { this._striped = v; return this }
|
|
443
|
-
|
|
444
|
-
/** Customize the "no records" placeholder. */
|
|
445
|
-
emptyState(state: TableEmptyState): this {
|
|
446
|
-
this._emptyState = state
|
|
447
|
-
return this
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* Customize the "no matching records" placeholder shown when a search
|
|
452
|
-
* query or filter is active but the result set is empty. Distinct from
|
|
453
|
-
* `emptyState(...)` so the two cases can read differently — empty
|
|
454
|
-
* tables typically want a "create your first one" call to action,
|
|
455
|
-
* while filtered-empty tables want a "clear filters / adjust search"
|
|
456
|
-
* hint.
|
|
457
|
-
*
|
|
458
|
-
* Falls back to `emptyState(...)` (or the framework defaults) when
|
|
459
|
-
* unset, so opting into the distinction is purely additive.
|
|
460
|
-
*/
|
|
461
|
-
filteredEmptyState(state: TableEmptyState): this {
|
|
462
|
-
this._filteredEmptyState = state
|
|
463
|
-
return this
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
/**
|
|
467
|
-
* Set a per-row URL — each data cell renders as a real `<a href>` so
|
|
468
|
-
* "open in new tab" works natively (right-click / cmd-click / middle-
|
|
469
|
-
* click). Plain left-clicks SPA-navigate via `useNavigate()`. Action
|
|
470
|
-
* and bulk-select cells stay unwrapped, so clicking a row action only
|
|
471
|
-
* fires the action — there's no overlapping row-level click handler.
|
|
472
|
-
*
|
|
473
|
-
* The URL is stamped onto each row under the reserved `_recordUrl`
|
|
474
|
-
* key during `loadTableRecords` — same convention as
|
|
475
|
-
* `_visibleActions` / `_formatted`.
|
|
476
|
-
*
|
|
477
|
-
* Per-column overrides: pair with `Column.recordUrl(fn)` to swap a
|
|
478
|
-
* column-specific URL, or `Column.recordUrl(false)` to opt a column
|
|
479
|
-
* out (e.g. a column whose cell content has its own click affordance).
|
|
480
|
-
*/
|
|
481
|
-
recordUrl(fn: RecordUrlHandler<R>): this {
|
|
482
|
-
this._recordUrl = fn
|
|
483
|
-
return this
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
/**
|
|
487
|
-
* Per-row CSS class hook. The handler runs server-side once per row,
|
|
488
|
-
* after `records()` resolves; the result is stamped under the reserved
|
|
489
|
-
* `_recordClasses` key on the row and appended to the rendered `<tr>`'s
|
|
490
|
-
* className. Pair with semantic Tailwind tokens (`bg-destructive/10`,
|
|
491
|
-
* `text-warning`) so theming stays consistent.
|
|
492
|
-
*/
|
|
493
|
-
recordClasses(fn: RecordClassesHandler<R>): this {
|
|
494
|
-
this._recordClasses = fn
|
|
495
|
-
return this
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* Auto-refresh the table at a regular interval. `seconds` is positive;
|
|
500
|
-
* non-positive values silently disable polling. SPA-friendly — the
|
|
501
|
-
* client navigates to `pathname + search` (the current URL) so sort /
|
|
502
|
-
* filter / pagination state survive the refresh, and AppShell stays
|
|
503
|
-
* mounted. Polling pauses while the document is hidden.
|
|
504
|
-
*/
|
|
505
|
-
poll(seconds: number): this {
|
|
506
|
-
if (seconds > 0) this._pollInterval = seconds
|
|
507
|
-
return this
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
/**
|
|
511
|
-
* Band rows by a column's value. Stable-sorts the rendered rows so
|
|
512
|
-
* shared values cluster together, then stamps each row's `_groupValue`
|
|
513
|
-
* — the renderer inserts a heading row whenever the value changes.
|
|
514
|
-
*
|
|
515
|
-
* Accepts either a bare column name or a `TableGroup` instance. When
|
|
516
|
-
* passed a `TableGroup` it's auto-added to `groups([…])` if it isn't
|
|
517
|
-
* registered already, so `defaultGroup(TableGroup.make('status').label('Status'))`
|
|
518
|
-
* doesn't require repeating the registration.
|
|
519
|
-
*/
|
|
520
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
521
|
-
defaultGroup(group: string | TableGroup<any>): this {
|
|
522
|
-
if (typeof group === 'string') {
|
|
523
|
-
this._defaultGroup = group
|
|
524
|
-
} else {
|
|
525
|
-
this._defaultGroup = group.getColumn()
|
|
526
|
-
const already = this._groups.some(g => g.getColumn() === group.getColumn())
|
|
527
|
-
if (!already) this._groups.push(group)
|
|
528
|
-
}
|
|
529
|
-
return this
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
/**
|
|
533
|
-
* Register the available group options. The renderer mounts a "Group
|
|
534
|
-
* by" dropdown above the table when 2+ groups are registered (or 1
|
|
535
|
-
* group with rich metadata — label / collapsible / record-derived
|
|
536
|
-
* title). The active selection round-trips through the URL via the
|
|
537
|
-
* reserved `?group=` key.
|
|
538
|
-
*/
|
|
539
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
540
|
-
groups(items: TableGroup<any>[]): this {
|
|
541
|
-
this._groups = items
|
|
542
|
-
return this
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
/**
|
|
546
|
-
* Enable drag-to-reorder. `column` names the model attribute the new
|
|
547
|
-
* order is written back to (defaults to `'sort'`, matching Filament).
|
|
548
|
-
* The route registers `POST {base}/{slug}/_reorder` — the renderer
|
|
549
|
-
* POSTs `{ ids }` there on every drop. Boot panics when this is set
|
|
550
|
-
* but `Resource.model.reorder` is missing.
|
|
551
|
-
*
|
|
552
|
-
* The reorder column also doubles as the default sort: when no
|
|
553
|
-
* `defaultSort()` is configured and reorder is on, rows render
|
|
554
|
-
* `(reorderColumn, asc)` so the visible order matches the persisted
|
|
555
|
-
* order. URL `?sort=…` still wins.
|
|
556
|
-
*/
|
|
557
|
-
reorderable(column: string = 'sort'): this {
|
|
558
|
-
this._reorderableColumn = column
|
|
559
|
-
return this
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
// ─── Render-time state ────────────────────────────────
|
|
563
|
-
|
|
564
|
-
/** Attach loaded rows + total. Called by the framework after `records()` runs. */
|
|
565
|
-
withRows(rows: R[], total?: number): this {
|
|
566
|
-
this._rows = rows
|
|
567
|
-
if (total !== undefined) this._total = total
|
|
568
|
-
return this
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
withSort(column: string, direction: SortDirection): this {
|
|
572
|
-
this._currentSort = { column, direction }
|
|
573
|
-
return this
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
withSearch(query: string): this { this._currentSearch = query; return this }
|
|
577
|
-
withPage(page: number): this { this._currentPage = page; return this }
|
|
578
|
-
withCurrentPath(path: string): this { this._currentPath = path; return this }
|
|
579
|
-
withSummaries(summaries: Record<string, SummaryResult[]>): this {
|
|
580
|
-
this._summaries = summaries
|
|
581
|
-
return this
|
|
582
|
-
}
|
|
583
|
-
/** Stamp per-group summary results. Outer key = `_groupValue`,
|
|
584
|
-
* inner = column name. Set by `loadTableRecords` only when an active
|
|
585
|
-
* group is set AND at least one column has summarizers. */
|
|
586
|
-
withGroupSummaries(
|
|
587
|
-
groupSummaries: Record<string, Record<string, SummaryResult[]>>,
|
|
588
|
-
): this {
|
|
589
|
-
this._groupSummaries = groupSummaries
|
|
590
|
-
return this
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
/** Stamp the reorder POST URL. Called by `tagTableReorderUrls` during
|
|
594
|
-
* `resourceIndexData` so the renderer knows where to send drops. */
|
|
595
|
-
withReorderUrl(url: string): this {
|
|
596
|
-
this._reorderUrl = url
|
|
597
|
-
return this
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
/** Mark this table as deferred — the SSR pass skips `records()` and
|
|
601
|
-
* the client fetches rows from `tableUrl` after mount. Set by
|
|
602
|
-
* `resourceIndexData` when `Resource.deferLoading = true`; user code
|
|
603
|
-
* doesn't typically call this directly. */
|
|
604
|
-
withDeferred(v: boolean = true): this {
|
|
605
|
-
this._deferred = v
|
|
606
|
-
return this
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
/** Stamp the deferred-load fetch URL. Set alongside `withDeferred`
|
|
610
|
-
* by `tagTableUrls` during `resourceIndexData`. */
|
|
611
|
-
withTableUrl(url: string): this {
|
|
612
|
-
this._tableUrl = url
|
|
613
|
-
return this
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
/**
|
|
617
|
-
* Namespace this table's URL state under an identifier so multiple
|
|
618
|
-
* tables on the same page don't collide on `?search=` / `?sort=` /
|
|
619
|
-
* `?page=` / filter keys. With `queryStringIdentifier('orders')`, every
|
|
620
|
-
* key is read and written as `orders_<key>` (e.g. `?orders_search=foo`,
|
|
621
|
-
* `?orders_sort=date:desc`, `?orders_status=open`). Off by default —
|
|
622
|
-
* resource list pages have one Table and don't need a prefix.
|
|
623
|
-
*
|
|
624
|
-
* The empty string is rejected (would silently disable namespacing
|
|
625
|
-
* while implying it's on); identifiers must match `[A-Za-z0-9_-]+`
|
|
626
|
-
* so they don't smuggle reserved characters into URL keys.
|
|
627
|
-
*/
|
|
628
|
-
queryStringIdentifier(id: string): this {
|
|
629
|
-
if (!/^[A-Za-z0-9_-]+$/.test(id)) {
|
|
630
|
-
throw new Error(
|
|
631
|
-
`Table.queryStringIdentifier: invalid id ${JSON.stringify(id)} — must match /^[A-Za-z0-9_-]+$/`,
|
|
632
|
-
)
|
|
633
|
-
}
|
|
634
|
-
this._queryStringIdentifier = id
|
|
635
|
-
return this
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
/**
|
|
639
|
-
* Pick the content layout for this table. `'table'` (default) renders
|
|
640
|
-
* the classic `<thead>` + `<tbody>` HTML table. `'cards'` hides the
|
|
641
|
-
* column header row and renders each row as a card in a CSS grid; the
|
|
642
|
-
* per-row content comes from `cardSchema(...)`.
|
|
643
|
-
*
|
|
644
|
-
* Columns still drive search / sort / filter / group / summarize
|
|
645
|
-
* semantics in cards mode — they're just not painted as table headers.
|
|
646
|
-
* The top-bar gains a "Sort by" dropdown built from `Column.sortable()`
|
|
647
|
-
* columns since column headers (the usual sort affordance) are hidden.
|
|
648
|
-
*/
|
|
649
|
-
contentLayout(layout: ContentLayout): this {
|
|
650
|
-
this._contentLayout = layout
|
|
651
|
-
return this
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
/** Sugar for `contentLayout('cards')`. */
|
|
655
|
-
cards(): this { return this.contentLayout('cards') }
|
|
656
|
-
|
|
657
|
-
/**
|
|
658
|
-
* Per-row card content. Returns an `Element[]` rendered inside a card
|
|
659
|
-
* for the given record + ctx. Resolved server-side per row in
|
|
660
|
-
* `loadTableRecords` and stamped on `row._cardChildren`. Required when
|
|
661
|
-
* `contentLayout === 'cards'`; `toMeta()` throws otherwise.
|
|
662
|
-
*
|
|
663
|
-
* Display-only — `Form / Field / Filter / Action` inside the card
|
|
664
|
-
* schema is unsupported in v1. Reuse `Heading`, `Text`, `Image`, `Icon`,
|
|
665
|
-
* `Group / Split / Grid`, and the read-only `Entry` family
|
|
666
|
-
* (`TextEntry / BadgeEntry / IconEntry / ImageEntry / KeyValueEntry /
|
|
667
|
-
* ColorEntry / ComponentEntry`) for content.
|
|
668
|
-
*/
|
|
669
|
-
cardSchema(fn: CardSchemaHandler<R>): this {
|
|
670
|
-
this._cardSchema = fn
|
|
671
|
-
return this
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
/**
|
|
675
|
-
* Responsive grid column counts in cards mode. Each entry maps to a
|
|
676
|
-
* Tailwind container query (`@sm` = ≥40rem, etc.). Unspecified
|
|
677
|
-
* breakpoints inherit the next-smaller; `default` is the base.
|
|
678
|
-
* Values clamp to `[1, 12]`. Default `{ default: 1, sm: 2, lg: 3 }`.
|
|
679
|
-
*/
|
|
680
|
-
cardsPerRow(opts: CardsPerRow): this {
|
|
681
|
-
this._cardsPerRow = opts
|
|
682
|
-
return this
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
/**
|
|
686
|
-
* Where filters render relative to the table. Default `'modal'` keeps
|
|
687
|
-
* the toolbar Filters button + popover. The three inline modes
|
|
688
|
-
* (`'above-content'` / `'above-content-collapsible'` / `'below-content'`)
|
|
689
|
-
* lay every filter widget out as a horizontal strip in place of the
|
|
690
|
-
* popover.
|
|
691
|
-
*/
|
|
692
|
-
filtersLayout(layout: FiltersLayout): this {
|
|
693
|
-
this._filtersLayout = layout
|
|
694
|
-
return this
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
/** Render-time setter — the column rows are actually banded by for
|
|
698
|
-
* this request, after `?group=` and `defaultGroup(...)` are reconciled.
|
|
699
|
-
* Set by `loadTableRecords`. Empty string explicitly clears (URL
|
|
700
|
-
* `?group=` overrode `defaultGroup`). */
|
|
701
|
-
withActiveGroup(column: string | undefined): this {
|
|
702
|
-
if (column === undefined) delete this._activeGroup
|
|
703
|
-
else this._activeGroup = column
|
|
704
|
-
return this
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
/** Render-time setter — the drilled-in group key for this request,
|
|
708
|
-
* after `?<prefix>groupKey=` was reconciled against a `scopable`
|
|
709
|
-
* group. Set by `loadTableRecords`. Empty string / undefined both
|
|
710
|
-
* clear, since drill-in needs an actual key to scope against. */
|
|
711
|
-
withActiveGroupKey(key: string | undefined): this {
|
|
712
|
-
if (key === undefined || key === '') delete this._activeGroupKey
|
|
713
|
-
else this._activeGroupKey = key
|
|
714
|
-
return this
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
// ─── Getters ──────────────────────────────────────────
|
|
718
|
-
|
|
719
|
-
getQuery(): TableQueryHandler<Q> | undefined { return this._query }
|
|
720
|
-
getRecords(): TableRecordsHandler<R> | undefined { return this._records }
|
|
721
|
-
getDefaultSort(): { column: string; direction: SortDirection } | undefined { return this._defaultSort }
|
|
722
|
-
getPerPage(): number | undefined { return this._perPage }
|
|
723
|
-
getRows(): R[] | undefined { return this._rows }
|
|
724
|
-
getTotal(): number | undefined { return this._total }
|
|
725
|
-
getCurrentSort(): { column: string; direction: SortDirection } | undefined { return this._currentSort }
|
|
726
|
-
getCurrentSearch(): string | undefined { return this._currentSearch }
|
|
727
|
-
getCurrentPage(): number | undefined { return this._currentPage }
|
|
728
|
-
getCurrentPath(): string | undefined { return this._currentPath }
|
|
729
|
-
getRecordUrl(): RecordUrlHandler<R> | undefined { return this._recordUrl }
|
|
730
|
-
getRecordClasses(): RecordClassesHandler<R> | undefined { return this._recordClasses }
|
|
731
|
-
getPollInterval(): number | undefined { return this._pollInterval }
|
|
732
|
-
getDefaultGroup(): string | undefined { return this._defaultGroup }
|
|
733
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
734
|
-
getGroups(): TableGroup<any>[] { return this._groups }
|
|
735
|
-
getActiveGroup(): string | undefined { return this._activeGroup }
|
|
736
|
-
getActiveGroupKey(): string | undefined { return this._activeGroupKey }
|
|
737
|
-
/** Resolve the active column to a `TableGroup` instance. Returns the
|
|
738
|
-
* matching registered group, or — when the active column is set but
|
|
739
|
-
* not registered (bare-column form) — synthesizes a no-metadata group
|
|
740
|
-
* so the dispatcher has a uniform shape to work with. */
|
|
741
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
742
|
-
getActiveGroupInstance(): TableGroup<any> | undefined {
|
|
743
|
-
const col = this._activeGroup
|
|
744
|
-
if (!col) return undefined
|
|
745
|
-
const found = this._groups.find(g => g.getColumn() === col)
|
|
746
|
-
return found ?? TableGroup.make(col)
|
|
747
|
-
}
|
|
748
|
-
getSummaries(): Record<string, SummaryResult[]> | undefined { return this._summaries }
|
|
749
|
-
getGroupSummaries(): Record<string, Record<string, SummaryResult[]>> | undefined { return this._groupSummaries }
|
|
750
|
-
getReorderableColumn(): string | undefined { return this._reorderableColumn }
|
|
751
|
-
isReorderable(): boolean { return this._reorderableColumn !== undefined }
|
|
752
|
-
getReorderUrl(): string | undefined { return this._reorderUrl }
|
|
753
|
-
isDeferred(): boolean { return this._deferred }
|
|
754
|
-
getTableUrl(): string | undefined { return this._tableUrl }
|
|
755
|
-
getQueryStringIdentifier(): string | undefined { return this._queryStringIdentifier }
|
|
756
|
-
getContentLayout(): ContentLayout { return this._contentLayout }
|
|
757
|
-
isCardsLayout(): boolean { return this._contentLayout === 'cards' }
|
|
758
|
-
getCardSchema(): CardSchemaHandler<R> | undefined { return this._cardSchema }
|
|
759
|
-
getCardsPerRow(): CardsPerRow | undefined { return this._cardsPerRow }
|
|
760
|
-
getFiltersLayout(): FiltersLayout { return this._filtersLayout }
|
|
761
|
-
|
|
762
|
-
/** Convenience: the `Column` children only. */
|
|
763
|
-
getColumns(): Column[] {
|
|
764
|
-
return (this._children ?? []).filter((el): el is Column => el instanceof Column)
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
/** Convenience: the `Filter` children only. */
|
|
768
|
-
getFilters(): Filter[] {
|
|
769
|
-
return (this._children ?? []).filter((el): el is Filter => el instanceof Filter)
|
|
770
|
-
}
|
|
771
|
-
|
|
772
|
-
// ─── Serialization ────────────────────────────────────
|
|
773
|
-
|
|
774
|
-
getType(): string { return 'table' }
|
|
775
|
-
|
|
776
|
-
override toMeta(): TableMeta {
|
|
777
|
-
const searchable = this.getColumns().some(c => c.isSearchable())
|
|
778
|
-
if (this._contentLayout === 'cards' && this._cardSchema === undefined) {
|
|
779
|
-
throw new Error(
|
|
780
|
-
'Table.contentLayout("cards") requires .cardSchema((record, ctx) => Element[]). ' +
|
|
781
|
-
'Cards mode renders each row from a per-row schema; configure one before rendering.',
|
|
782
|
-
)
|
|
783
|
-
}
|
|
784
|
-
const cardsPerRow = this._cardsPerRow !== undefined
|
|
785
|
-
? clampCardsPerRow(this._cardsPerRow)
|
|
786
|
-
: undefined
|
|
787
|
-
return {
|
|
788
|
-
type: 'table',
|
|
789
|
-
searchable,
|
|
790
|
-
...(this._defaultSort ? { defaultSort: this._defaultSort } : {}),
|
|
791
|
-
...(this._perPage !== undefined ? { perPage: this._perPage } : {}),
|
|
792
|
-
...(this._heading !== undefined ? { heading: this._heading } : {}),
|
|
793
|
-
...(this._description !== undefined ? { description: this._description } : {}),
|
|
794
|
-
...(this._striped ? { striped: true } : {}),
|
|
795
|
-
...(this._emptyState !== undefined ? { emptyState: this._emptyState } : {}),
|
|
796
|
-
...(this._filteredEmptyState !== undefined ? { filteredEmptyState: this._filteredEmptyState } : {}),
|
|
797
|
-
...(this._recordUrl !== undefined ? { recordUrl: true as const } : {}),
|
|
798
|
-
...(this._recordClasses !== undefined ? { recordClasses: true as const } : {}),
|
|
799
|
-
...(this._pollInterval !== undefined ? { pollInterval: this._pollInterval } : {}),
|
|
800
|
-
// `defaultGroup` on the meta means "the column rows are actually
|
|
801
|
-
// grouped by for this render". Prefer the render-time activeGroup
|
|
802
|
-
// (set by `loadTableRecords` after reconciling `?group=` and the
|
|
803
|
-
// configured default); fall back to the configured default when
|
|
804
|
-
// no request-side reconciliation has happened (e.g. tests calling
|
|
805
|
-
// `toMeta()` directly).
|
|
806
|
-
...(this._activeGroup !== undefined
|
|
807
|
-
? (this._activeGroup === ''
|
|
808
|
-
? {}
|
|
809
|
-
: { defaultGroup: this._activeGroup })
|
|
810
|
-
: (this._defaultGroup !== undefined
|
|
811
|
-
? { defaultGroup: this._defaultGroup }
|
|
812
|
-
: {})),
|
|
813
|
-
...(this._groups.length > 0
|
|
814
|
-
? { groups: this._groups.map(g => g.toMeta()) }
|
|
815
|
-
: {}),
|
|
816
|
-
...(this._summaries !== undefined ? { summaries: this._summaries } : {}),
|
|
817
|
-
...(this._groupSummaries !== undefined ? { groupSummaries: this._groupSummaries } : {}),
|
|
818
|
-
...(this._activeGroupKey !== undefined ? { activeGroupKey: this._activeGroupKey } : {}),
|
|
819
|
-
...(this._reorderableColumn !== undefined ? { reorderable: true as const, reorderableColumn: this._reorderableColumn } : {}),
|
|
820
|
-
...(this._reorderUrl !== undefined ? { reorderUrl: this._reorderUrl } : {}),
|
|
821
|
-
...(this._deferred ? { deferred: true as const } : {}),
|
|
822
|
-
...(this._tableUrl !== undefined ? { tableUrl: this._tableUrl } : {}),
|
|
823
|
-
...(this._queryStringIdentifier !== undefined
|
|
824
|
-
? { queryStringIdentifier: this._queryStringIdentifier } : {}),
|
|
825
|
-
...(this._contentLayout === 'cards' ? { contentLayout: 'cards' as const } : {}),
|
|
826
|
-
...(cardsPerRow !== undefined ? { cardsPerRow } : {}),
|
|
827
|
-
...(this._filtersLayout !== 'modal' ? { filtersLayout: this._filtersLayout } : {}),
|
|
828
|
-
...(this._rows !== undefined ? { rows: this._rows } : {}),
|
|
829
|
-
...(this._total !== undefined ? { total: this._total } : {}),
|
|
830
|
-
...(this._currentSort !== undefined ? { currentSort: this._currentSort } : {}),
|
|
831
|
-
...(this._currentSearch !== undefined ? { search: this._currentSearch } : {}),
|
|
832
|
-
...(this._currentPage !== undefined ? { currentPage: this._currentPage } : {}),
|
|
833
|
-
...(this._currentPath !== undefined ? { currentPath: this._currentPath } : {}),
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
const CARDS_PER_ROW_KEYS = ['default', 'sm', 'md', 'lg', 'xl', '2xl'] as const
|
|
839
|
-
|
|
840
|
-
function clampCardsPerRow(opts: CardsPerRow): CardsPerRow {
|
|
841
|
-
const out: CardsPerRow = {}
|
|
842
|
-
for (const key of CARDS_PER_ROW_KEYS) {
|
|
843
|
-
const v = opts[key]
|
|
844
|
-
if (v === undefined) continue
|
|
845
|
-
const n = Math.floor(Number(v))
|
|
846
|
-
if (!Number.isFinite(n)) continue
|
|
847
|
-
out[key] = Math.min(12, Math.max(1, n))
|
|
848
|
-
}
|
|
849
|
-
return out
|
|
850
|
-
}
|