@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,95 +0,0 @@
|
|
|
1
|
-
import type { RenderContext } from '../schema/resolveSchema.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Shape of an option in a SelectField / Radio / CheckboxList.
|
|
5
|
-
* `value` round-trips through the form body; `label` is display only.
|
|
6
|
-
*
|
|
7
|
-
* `disabled` greys the option out and blocks selection. Stamped server-side
|
|
8
|
-
* by `disableOptionsWhenSelectedInSiblingRepeaterItems()` for options taken
|
|
9
|
-
* in sibling Repeater/Builder rows; users may also set it themselves on the
|
|
10
|
-
* static option list to express "this choice is permanently unavailable".
|
|
11
|
-
*/
|
|
12
|
-
export type SelectOption = { value: string; label: string; disabled?: boolean }
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Reactive options resolver. Receives the same `{ $get, $set, record,
|
|
16
|
-
* user, values }` ctx that `afterStateUpdated` sees, runs every resolve
|
|
17
|
-
* cycle, and may be async (for DB-backed lookups). Throws are swallowed
|
|
18
|
-
* to an empty array + console.warn — the field still renders, just with
|
|
19
|
-
* no choices.
|
|
20
|
-
*
|
|
21
|
-
* Used by SelectField, Radio, CheckboxList. Each calls `resolveOptions`
|
|
22
|
-
* from inside its async `toMeta` so the resolver runs at the same point
|
|
23
|
-
* in the render cycle for every dependent-options field type.
|
|
24
|
-
*/
|
|
25
|
-
export type OptionsResolver = (ctx: {
|
|
26
|
-
$get?: (name: string) => unknown
|
|
27
|
-
$set?: (name: string, value: unknown) => void
|
|
28
|
-
record?: unknown
|
|
29
|
-
user?: unknown
|
|
30
|
-
values?: Record<string, unknown>
|
|
31
|
-
}) => SelectOption[] | Promise<SelectOption[]>
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Resolve a `SelectOption[] | OptionsResolver` source against the
|
|
35
|
-
* current render context. Returns the resolved option array, or `[]`
|
|
36
|
-
* with a `console.warn` when the resolver throws — failure should
|
|
37
|
-
* never crash a form render.
|
|
38
|
-
*/
|
|
39
|
-
export async function resolveOptions(
|
|
40
|
-
source: SelectOption[] | OptionsResolver,
|
|
41
|
-
ctx: RenderContext | undefined,
|
|
42
|
-
fieldName: string,
|
|
43
|
-
): Promise<SelectOption[]> {
|
|
44
|
-
if (Array.isArray(source)) return source
|
|
45
|
-
try {
|
|
46
|
-
return await source({
|
|
47
|
-
...(ctx?.$get ? { $get: ctx.$get } : {}),
|
|
48
|
-
...(ctx?.$set ? { $set: ctx.$set } : {}),
|
|
49
|
-
...(ctx?.record !== undefined ? { record: ctx.record } : {}),
|
|
50
|
-
...(ctx?.user !== undefined ? { user: ctx.user } : {}),
|
|
51
|
-
...(ctx?.values ? { values: ctx.values } : {}),
|
|
52
|
-
})
|
|
53
|
-
} catch (err) {
|
|
54
|
-
console.warn(`[pilotiq] options() resolver for "${fieldName}" threw:`, err)
|
|
55
|
-
return []
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Apply the "disable options taken in sibling Repeater/Builder rows" gate.
|
|
61
|
-
* No-op outside an array-row context (no `ctx.row.siblings`) or when the
|
|
62
|
-
* field hasn't opted in. Builder scoping (same-block-type only) is handled
|
|
63
|
-
* by the resolver — by the time we get here, `ctx.row.siblings` already
|
|
64
|
-
* reflects the right scope.
|
|
65
|
-
*
|
|
66
|
-
* Multi-value sibling rows (e.g. `CheckboxList` storing `string[]`) unfold
|
|
67
|
-
* into individual taken values; single-value rows contribute one taken
|
|
68
|
-
* entry. Empty / null / `''` sibling values are skipped (they aren't real
|
|
69
|
-
* picks yet — same posture as `Field.distinct()`'s default `ignoreNulls`).
|
|
70
|
-
*/
|
|
71
|
-
export function disableOptionsTakenInSiblings(
|
|
72
|
-
options: SelectOption[],
|
|
73
|
-
enabled: boolean,
|
|
74
|
-
fieldName: string,
|
|
75
|
-
ctx: RenderContext | undefined,
|
|
76
|
-
): SelectOption[] {
|
|
77
|
-
if (!enabled) return options
|
|
78
|
-
const siblings = ctx?.row?.siblings
|
|
79
|
-
if (!siblings || siblings.length === 0) return options
|
|
80
|
-
const taken = new Set<string>()
|
|
81
|
-
for (const sib of siblings) {
|
|
82
|
-
if (!sib || typeof sib !== 'object') continue
|
|
83
|
-
const v = (sib as Record<string, unknown>)[fieldName]
|
|
84
|
-
if (Array.isArray(v)) {
|
|
85
|
-
for (const x of v) {
|
|
86
|
-
if (x === undefined || x === null || x === '') continue
|
|
87
|
-
taken.add(String(x))
|
|
88
|
-
}
|
|
89
|
-
} else if (v !== undefined && v !== null && v !== '') {
|
|
90
|
-
taken.add(String(v))
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (taken.size === 0) return options
|
|
94
|
-
return options.map(o => (taken.has(o.value) && !o.disabled ? { ...o, disabled: true } : o))
|
|
95
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { Field, FieldMeta } from './Field.js'
|
|
2
|
-
import type { RenderContext } from '../schema/resolveSchema.js'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Resolve a single Field against a render context.
|
|
6
|
-
*
|
|
7
|
-
* Returns `null` if the field should be hidden — visibility flags
|
|
8
|
-
* (`hideFromTable` / `hideFromCreate` / etc.) and condition callbacks
|
|
9
|
-
* (`showWhen` / `hideWhen`) are evaluated server-side. The serialized meta
|
|
10
|
-
* never includes a hidden field.
|
|
11
|
-
*
|
|
12
|
-
* Async because reactive Field subclasses (Plan #5 — `SelectField` with
|
|
13
|
-
* a resolver-style `options(fn)`) may compute meta asynchronously.
|
|
14
|
-
*/
|
|
15
|
-
export async function resolveField(field: Field, ctx: RenderContext = {}): Promise<FieldMeta | null> {
|
|
16
|
-
if (field.isHiddenIn(ctx)) return null
|
|
17
|
-
return await field.toMeta(ctx)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Resolve an array of Fields, dropping any that are hidden for the current
|
|
22
|
-
* context. Used by `Resource.form()` / `Resource.table()` paths until 1.3
|
|
23
|
-
* unifies Fields under the schema-element resolver.
|
|
24
|
-
*/
|
|
25
|
-
export async function resolveFields(fields: Field[], ctx: RenderContext = {}): Promise<FieldMeta[]> {
|
|
26
|
-
const metas = await Promise.all(fields.map(f => resolveField(f, ctx)))
|
|
27
|
-
return metas.filter((m): m is FieldMeta => m !== null)
|
|
28
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { Filter, type FilterKind, type FilterMeta } from './Filter.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Three-state filter: yes / no / any. Renders as a dropdown. When a
|
|
5
|
-
* value is selected, contributes `where(name, true|false)` to the
|
|
6
|
-
* underlying ORM query — values `'1'` and `'true'` map to `true`,
|
|
7
|
-
* everything else to `false`. The empty value means "any" (no clause
|
|
8
|
-
* applied).
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* BooleanFilter.make('featured').label('Featured only')
|
|
12
|
-
*/
|
|
13
|
-
export class BooleanFilter extends Filter {
|
|
14
|
-
static make(name: string): BooleanFilter {
|
|
15
|
-
return new BooleanFilter(name)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
override getKind(): FilterKind { return 'boolean' }
|
|
19
|
-
|
|
20
|
-
protected override formatActiveValue(value: string): string {
|
|
21
|
-
return coerceBooleanFilterValue(value) ? 'Yes' : 'No'
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
override toMeta(): FilterMeta {
|
|
25
|
-
return {
|
|
26
|
-
...this.buildBaseMeta(),
|
|
27
|
-
placeholder: this.getPlaceholder() ?? 'Any',
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/** Coerce a URL-string filter value to a boolean. */
|
|
33
|
-
export function coerceBooleanFilterValue(value: string): boolean {
|
|
34
|
-
return value === '1' || value === 'true' || value === 'yes' || value === 'on'
|
|
35
|
-
}
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
import { describe, it } from 'node:test'
|
|
2
|
-
import assert from 'node:assert/strict'
|
|
3
|
-
|
|
4
|
-
import { Table } from '../elements/Table.js'
|
|
5
|
-
import { Column } from '../Column.js'
|
|
6
|
-
import {
|
|
7
|
-
DateRangeFilter,
|
|
8
|
-
parseDateRangeValue,
|
|
9
|
-
encodeDateRangeValue,
|
|
10
|
-
} from './DateRangeFilter.js'
|
|
11
|
-
import { modelTableRecords } from '../orm/modelDefaults.js'
|
|
12
|
-
import type { ModelLike, ModelQuery } from '../orm/modelDefaults.js'
|
|
13
|
-
|
|
14
|
-
interface FakeOp { op: string; args: unknown[] }
|
|
15
|
-
class FakeQuery implements ModelQuery {
|
|
16
|
-
ops: FakeOp[] = []
|
|
17
|
-
where(...args: unknown[]): ModelQuery { this.ops.push({ op: 'where', args }); return this }
|
|
18
|
-
orWhere(...args: unknown[]): ModelQuery { this.ops.push({ op: 'orWhere', args }); return this }
|
|
19
|
-
orderBy(c: string, d: 'ASC' | 'DESC' = 'ASC'): ModelQuery { this.ops.push({ op: 'orderBy', args: [c, d] }); return this }
|
|
20
|
-
async paginate() { this.ops.push({ op: 'paginate', args: [] }); return { data: [], total: 0 } }
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function fakeModel(): ModelLike & { name: string; model: ModelLike; lastQuery: FakeQuery | null } {
|
|
24
|
-
let lastQuery: FakeQuery | null = null
|
|
25
|
-
const M = {
|
|
26
|
-
name: 'TestResource',
|
|
27
|
-
async find() { return null },
|
|
28
|
-
async create(d: Record<string, unknown>) { return d },
|
|
29
|
-
async update(_id: string | number, d: Record<string, unknown>) { return d },
|
|
30
|
-
async delete() {},
|
|
31
|
-
query(): ModelQuery {
|
|
32
|
-
const q = new FakeQuery()
|
|
33
|
-
lastQuery = q
|
|
34
|
-
return q
|
|
35
|
-
},
|
|
36
|
-
get lastQuery() { return lastQuery },
|
|
37
|
-
} as unknown as ModelLike & { name: string; model: ModelLike; lastQuery: FakeQuery | null }
|
|
38
|
-
M.model = M
|
|
39
|
-
return M
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
describe('parseDateRangeValue', () => {
|
|
43
|
-
it('round-trips a closed range', () => {
|
|
44
|
-
assert.deepEqual(
|
|
45
|
-
parseDateRangeValue('2026-01-01..2026-12-31'),
|
|
46
|
-
{ from: '2026-01-01', to: '2026-12-31' },
|
|
47
|
-
)
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
it('round-trips an open-from range', () => {
|
|
51
|
-
assert.deepEqual(parseDateRangeValue('2026-01-01..'), { from: '2026-01-01' })
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('round-trips an open-to range', () => {
|
|
55
|
-
assert.deepEqual(parseDateRangeValue('..2026-12-31'), { to: '2026-12-31' })
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
it('treats empty / `..` as the empty range', () => {
|
|
59
|
-
assert.deepEqual(parseDateRangeValue(''), {})
|
|
60
|
-
assert.deepEqual(parseDateRangeValue('..'), {})
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
it('treats a missing separator as a single from bound', () => {
|
|
64
|
-
assert.deepEqual(parseDateRangeValue('2026-01-01'), { from: '2026-01-01' })
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('trims whitespace on both sides', () => {
|
|
68
|
-
assert.deepEqual(
|
|
69
|
-
parseDateRangeValue(' 2026-01-01 .. 2026-12-31 '),
|
|
70
|
-
{ from: '2026-01-01', to: '2026-12-31' },
|
|
71
|
-
)
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
describe('encodeDateRangeValue', () => {
|
|
76
|
-
it('encodes a closed range', () => {
|
|
77
|
-
assert.equal(encodeDateRangeValue({ from: '2026-01-01', to: '2026-12-31' }), '2026-01-01..2026-12-31')
|
|
78
|
-
})
|
|
79
|
-
it('encodes open-from / open-to', () => {
|
|
80
|
-
assert.equal(encodeDateRangeValue({ from: '2026-01-01' }), '2026-01-01..')
|
|
81
|
-
assert.equal(encodeDateRangeValue({ to: '2026-12-31' }), '..2026-12-31')
|
|
82
|
-
})
|
|
83
|
-
it('returns empty string when both sides are missing', () => {
|
|
84
|
-
assert.equal(encodeDateRangeValue({}), '')
|
|
85
|
-
assert.equal(encodeDateRangeValue({ from: '', to: '' }), '')
|
|
86
|
-
})
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
describe('DateRangeFilter shape', () => {
|
|
90
|
-
it('toMeta emits kind:dateRange + includesTime + Any placeholder', () => {
|
|
91
|
-
const meta = DateRangeFilter.make('publishedAt').toMeta()
|
|
92
|
-
assert.equal(meta.kind, 'dateRange')
|
|
93
|
-
assert.equal(meta.includesTime, false)
|
|
94
|
-
assert.equal(meta.placeholder, 'Any')
|
|
95
|
-
assert.equal(meta.minDate, undefined)
|
|
96
|
-
assert.equal(meta.maxDate, undefined)
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
it('includesTime() flips the meta flag', () => {
|
|
100
|
-
const meta = DateRangeFilter.make('publishedAt').includesTime(true).toMeta()
|
|
101
|
-
assert.equal(meta.includesTime, true)
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('minDate / maxDate accept ISO strings + Date objects', () => {
|
|
105
|
-
const date = new Date(2026, 0, 15) // 2026-01-15 local
|
|
106
|
-
const meta = DateRangeFilter.make('publishedAt')
|
|
107
|
-
.minDate('2020-01-01')
|
|
108
|
-
.maxDate(date)
|
|
109
|
-
.toMeta()
|
|
110
|
-
assert.equal(meta.minDate, '2020-01-01')
|
|
111
|
-
assert.equal(meta.maxDate, '2026-01-15')
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
it('Date bounds include time when includesTime(true) was set first', () => {
|
|
115
|
-
const date = new Date(2026, 0, 15, 14, 30) // 2026-01-15 14:30 local
|
|
116
|
-
const meta = DateRangeFilter.make('publishedAt')
|
|
117
|
-
.includesTime(true)
|
|
118
|
-
.maxDate(date)
|
|
119
|
-
.toMeta()
|
|
120
|
-
assert.equal(meta.maxDate, '2026-01-15T14:30')
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
it('placeholder override wins over Any default', () => {
|
|
124
|
-
const meta = DateRangeFilter.make('publishedAt').placeholder('Any time').toMeta()
|
|
125
|
-
assert.equal(meta.placeholder, 'Any time')
|
|
126
|
-
})
|
|
127
|
-
})
|
|
128
|
-
|
|
129
|
-
describe('DateRangeFilter default query handler', () => {
|
|
130
|
-
it('closed range emits two where clauses (>=, <=)', async () => {
|
|
131
|
-
const M = fakeModel()
|
|
132
|
-
const table = Table.make()
|
|
133
|
-
.columns([Column.make('title')])
|
|
134
|
-
.filters([DateRangeFilter.make('publishedAt')])
|
|
135
|
-
const handler = modelTableRecords(M, table)
|
|
136
|
-
await handler({ filters: { publishedAt: '2026-01-01..2026-12-31' }, page: 1 })
|
|
137
|
-
const wheres = M.lastQuery!.ops.filter(o => o.op === 'where')
|
|
138
|
-
assert.deepEqual(wheres[0]!.args, ['publishedAt', '>=', '2026-01-01'])
|
|
139
|
-
assert.deepEqual(wheres[1]!.args, ['publishedAt', '<=', '2026-12-31'])
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
it('open-from emits a single >= clause', async () => {
|
|
143
|
-
const M = fakeModel()
|
|
144
|
-
const table = Table.make()
|
|
145
|
-
.columns([Column.make('title')])
|
|
146
|
-
.filters([DateRangeFilter.make('publishedAt')])
|
|
147
|
-
const handler = modelTableRecords(M, table)
|
|
148
|
-
await handler({ filters: { publishedAt: '2026-01-01..' }, page: 1 })
|
|
149
|
-
const wheres = M.lastQuery!.ops.filter(o => o.op === 'where')
|
|
150
|
-
assert.equal(wheres.length, 1)
|
|
151
|
-
assert.deepEqual(wheres[0]!.args, ['publishedAt', '>=', '2026-01-01'])
|
|
152
|
-
})
|
|
153
|
-
|
|
154
|
-
it('open-to emits a single <= clause', async () => {
|
|
155
|
-
const M = fakeModel()
|
|
156
|
-
const table = Table.make()
|
|
157
|
-
.columns([Column.make('title')])
|
|
158
|
-
.filters([DateRangeFilter.make('publishedAt')])
|
|
159
|
-
const handler = modelTableRecords(M, table)
|
|
160
|
-
await handler({ filters: { publishedAt: '..2026-12-31' }, page: 1 })
|
|
161
|
-
const wheres = M.lastQuery!.ops.filter(o => o.op === 'where')
|
|
162
|
-
assert.equal(wheres.length, 1)
|
|
163
|
-
assert.deepEqual(wheres[0]!.args, ['publishedAt', '<=', '2026-12-31'])
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
it('parseFilterValues drops `..` (no clauses)', async () => {
|
|
167
|
-
// The parser drops empty values upstream, but `..` is non-empty —
|
|
168
|
-
// the default handler itself short-circuits the no-bound case.
|
|
169
|
-
const M = fakeModel()
|
|
170
|
-
const table = Table.make()
|
|
171
|
-
.columns([Column.make('title')])
|
|
172
|
-
.filters([DateRangeFilter.make('publishedAt')])
|
|
173
|
-
const handler = modelTableRecords(M, table)
|
|
174
|
-
await handler({ filters: { publishedAt: '..' }, page: 1 })
|
|
175
|
-
const wheres = M.lastQuery!.ops.filter(o => o.op === 'where')
|
|
176
|
-
assert.equal(wheres.length, 0)
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
it('user .query(fn) override sees the raw encoded value', async () => {
|
|
180
|
-
const M = fakeModel()
|
|
181
|
-
let received: string | undefined
|
|
182
|
-
const table = Table.make()
|
|
183
|
-
.columns([Column.make('title')])
|
|
184
|
-
.filters([
|
|
185
|
-
DateRangeFilter.make('publishedAt').query((q, value) => {
|
|
186
|
-
received = value
|
|
187
|
-
return q
|
|
188
|
-
}),
|
|
189
|
-
])
|
|
190
|
-
const handler = modelTableRecords(M, table)
|
|
191
|
-
await handler({ filters: { publishedAt: '2026-01-01..2026-12-31' }, page: 1 })
|
|
192
|
-
assert.equal(received, '2026-01-01..2026-12-31')
|
|
193
|
-
})
|
|
194
|
-
})
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import { Filter, type FilterKind, type FilterMeta } from './Filter.js'
|
|
2
|
-
import type { ModelQuery } from '../orm/modelDefaults.js'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Parsed shape of a `DateRangeFilter`'s URL value. Either side may be
|
|
6
|
-
* absent; `{ from, to }` are kept as raw strings so callers can decide
|
|
7
|
-
* whether to coerce to `Date` or pass straight through to the ORM.
|
|
8
|
-
*/
|
|
9
|
-
export interface DateRangeValue {
|
|
10
|
-
from?: string
|
|
11
|
-
to?: string
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Round-trip a `from..to`-encoded URL value to `{ from?, to? }`. Either
|
|
16
|
-
* side may be empty (`'2026-01-01..'` → `{ from: '2026-01-01' }`). The
|
|
17
|
-
* empty string and `'..'` both parse to `{}`.
|
|
18
|
-
*
|
|
19
|
-
* Exposed as a public helper so users writing a custom `Filter.query(fn)`
|
|
20
|
-
* can avoid re-parsing the encoding by hand.
|
|
21
|
-
*/
|
|
22
|
-
export function parseDateRangeValue(value: string): DateRangeValue {
|
|
23
|
-
if (!value || value === '..') return {}
|
|
24
|
-
const idx = value.indexOf('..')
|
|
25
|
-
if (idx === -1) {
|
|
26
|
-
// No separator — treat as a single "from" bound. Rare; users are
|
|
27
|
-
// more likely to pass the encoded form.
|
|
28
|
-
const from = value.trim()
|
|
29
|
-
return from ? { from } : {}
|
|
30
|
-
}
|
|
31
|
-
const from = value.slice(0, idx).trim()
|
|
32
|
-
const to = value.slice(idx + 2).trim()
|
|
33
|
-
const out: DateRangeValue = {}
|
|
34
|
-
if (from) out.from = from
|
|
35
|
-
if (to) out.to = to
|
|
36
|
-
return out
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Encode a `{ from?, to? }` pair as the canonical `from..to` URL value.
|
|
41
|
-
* The empty / both-missing case returns `''` (caller should drop the
|
|
42
|
-
* URL key entirely rather than emitting `..`).
|
|
43
|
-
*/
|
|
44
|
-
export function encodeDateRangeValue(range: DateRangeValue): string {
|
|
45
|
-
const from = range.from?.trim() ?? ''
|
|
46
|
-
const to = range.to?.trim() ?? ''
|
|
47
|
-
if (!from && !to) return ''
|
|
48
|
-
return `${from}..${to}`
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Pair-of-date-inputs filter. Encodes the URL value as `from..to`
|
|
53
|
-
* keyed off the filter name so the existing single-key `parseFilterValues`
|
|
54
|
-
* contract holds:
|
|
55
|
-
*
|
|
56
|
-
* `?publishedAt=2026-01-01..2026-12-31` closed range
|
|
57
|
-
* `?publishedAt=2026-01-01..` open-ended `>=` from
|
|
58
|
-
* `?publishedAt=..2026-12-31` open-ended `<=` to
|
|
59
|
-
*
|
|
60
|
-
* Default ORM clauses:
|
|
61
|
-
*
|
|
62
|
-
* from + to → `where(name,'>=',from).where(name,'<=',to)`
|
|
63
|
-
* from only → `where(name,'>=',from)`
|
|
64
|
-
* to only → `where(name,'<=',to)`
|
|
65
|
-
*
|
|
66
|
-
* `to` is treated as inclusive — for `DateTime` columns with
|
|
67
|
-
* `includesTime(false)`, callers wanting "all of 2026-12-31" should
|
|
68
|
-
* widen the upper bound via `Filter.query(fn)`.
|
|
69
|
-
*
|
|
70
|
-
* @example
|
|
71
|
-
* DateRangeFilter.make('publishedAt').label('Published')
|
|
72
|
-
* DateRangeFilter.make('createdAt').includesTime(true).maxDate(new Date())
|
|
73
|
-
*/
|
|
74
|
-
export class DateRangeFilter extends Filter {
|
|
75
|
-
private _includesTime = false
|
|
76
|
-
private _minDate?: string
|
|
77
|
-
private _maxDate?: string
|
|
78
|
-
|
|
79
|
-
static make(name: string): DateRangeFilter {
|
|
80
|
-
const f = new DateRangeFilter(name)
|
|
81
|
-
f.query((q: ModelQuery, value: string) => {
|
|
82
|
-
const { from, to } = parseDateRangeValue(value)
|
|
83
|
-
let next = q
|
|
84
|
-
if (from) next = next.where(f.name, '>=', from)
|
|
85
|
-
if (to) next = next.where(f.name, '<=', to)
|
|
86
|
-
return next
|
|
87
|
-
})
|
|
88
|
-
return f
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Switch the inputs to `<input type="datetime-local">` and the URL
|
|
92
|
-
* values to ISO timestamps. Default: `false`. */
|
|
93
|
-
includesTime(value = true): this { this._includesTime = value; return this }
|
|
94
|
-
|
|
95
|
-
/** Lower bound for the input's `min` attribute. Accepts an ISO string or `Date`. */
|
|
96
|
-
minDate(value: string | Date): this {
|
|
97
|
-
this._minDate = toBoundString(value, this._includesTime)
|
|
98
|
-
return this
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/** Upper bound for the input's `max` attribute. Accepts an ISO string or `Date`. */
|
|
102
|
-
maxDate(value: string | Date): this {
|
|
103
|
-
this._maxDate = toBoundString(value, this._includesTime)
|
|
104
|
-
return this
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Getters used by tests + the renderer.
|
|
108
|
-
getIncludesTime(): boolean { return this._includesTime }
|
|
109
|
-
getMinDate(): string | undefined { return this._minDate }
|
|
110
|
-
getMaxDate(): string | undefined { return this._maxDate }
|
|
111
|
-
|
|
112
|
-
override getKind(): FilterKind { return 'dateRange' }
|
|
113
|
-
|
|
114
|
-
protected override formatActiveValue(value: string): string {
|
|
115
|
-
const { from, to } = parseDateRangeValue(value)
|
|
116
|
-
if (from && to) return `${from} → ${to}`
|
|
117
|
-
if (from) return `≥ ${from}`
|
|
118
|
-
if (to) return `≤ ${to}`
|
|
119
|
-
return value
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
override toMeta(): FilterMeta {
|
|
123
|
-
return {
|
|
124
|
-
...this.buildBaseMeta(),
|
|
125
|
-
includesTime: this._includesTime,
|
|
126
|
-
...(this._minDate !== undefined ? { minDate: this._minDate } : {}),
|
|
127
|
-
...(this._maxDate !== undefined ? { maxDate: this._maxDate } : {}),
|
|
128
|
-
placeholder: this.getPlaceholder() ?? 'Any',
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/** Coerce a `Date | string` bound to the canonical `YYYY-MM-DD` or
|
|
134
|
-
* `YYYY-MM-DDTHH:mm` input-friendly string. Strings pass through
|
|
135
|
-
* trimmed; the renderer's `min` / `max` attributes accept either. */
|
|
136
|
-
function toBoundString(value: string | Date, includesTime: boolean): string {
|
|
137
|
-
if (typeof value === 'string') return value.trim()
|
|
138
|
-
// Use the local-time split so `<input type="date">` doesn't shift the
|
|
139
|
-
// bound a day under negative-UTC-offset clients.
|
|
140
|
-
const yyyy = value.getFullYear().toString().padStart(4, '0')
|
|
141
|
-
const mm = (value.getMonth() + 1).toString().padStart(2, '0')
|
|
142
|
-
const dd = value.getDate().toString().padStart(2, '0')
|
|
143
|
-
const date = `${yyyy}-${mm}-${dd}`
|
|
144
|
-
if (!includesTime) return date
|
|
145
|
-
const hh = value.getHours().toString().padStart(2, '0')
|
|
146
|
-
const min = value.getMinutes().toString().padStart(2, '0')
|
|
147
|
-
return `${date}T${hh}:${min}`
|
|
148
|
-
}
|