@antelopejs/interface-dms 0.10.4 → 0.13.0
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 +126 -0
- package/README.md +21 -10
- package/dist/attachments.d.ts +15 -2
- package/dist/attachments.js +14 -1
- package/dist/attachments.js.map +1 -1
- package/dist/auth/db/models/sessions.model.d.ts +7 -5
- package/dist/auth/db/models/sessions.model.js +2 -9
- package/dist/auth/db/models/sessions.model.js.map +1 -1
- package/dist/auth/db/models/userExternalIdentities.model.d.ts +7 -5
- package/dist/auth/db/models/users.model.d.ts +7 -5
- package/dist/auth/db/tables/users.table.d.ts +2 -2
- package/dist/auth/db/tables/users.table.js.map +1 -1
- package/dist/auth/index.d.ts +4 -4
- package/dist/auth/internal/jwt-auth-decorator.d.ts +1 -1
- package/dist/auth/internal/jwt-auth-decorator.js +6 -3
- package/dist/auth/internal/jwt-auth-decorator.js.map +1 -1
- package/dist/auth/internal/request-authenticators.d.ts +12 -0
- package/dist/auth/internal/request-authenticators.js +53 -10
- package/dist/auth/internal/request-authenticators.js.map +1 -1
- package/dist/base/activity-feed.d.ts +34 -32
- package/dist/base/activity-feed.js +2 -0
- package/dist/base/activity-feed.js.map +1 -1
- package/dist/base/banner.d.ts +12 -10
- package/dist/base/banner.js +2 -0
- package/dist/base/banner.js.map +1 -1
- package/dist/base/block-registry/internal/describe.js +4 -12
- package/dist/base/block-registry/internal/describe.js.map +1 -1
- package/dist/base/block-registry/internal/ui.js +1 -1
- package/dist/base/block-registry/internal/ui.js.map +1 -1
- package/dist/base/card.d.ts +11 -9
- package/dist/base/card.js +2 -0
- package/dist/base/card.js.map +1 -1
- package/dist/base/chart-card.d.ts +4 -2
- package/dist/base/chart-card.js +2 -0
- package/dist/base/chart-card.js.map +1 -1
- package/dist/base/chart-schemas.d.ts +148 -148
- package/dist/base/chart.d.ts +65 -0
- package/dist/base/chart.js +65 -0
- package/dist/base/chart.js.map +1 -1
- package/dist/base/code-block.d.ts +5 -3
- package/dist/base/code-block.js +2 -0
- package/dist/base/code-block.js.map +1 -1
- package/dist/base/custom.d.ts +5 -0
- package/dist/base/custom.js +5 -0
- package/dist/base/custom.js.map +1 -1
- package/dist/base/data-types/default/address.d.ts +5 -0
- package/dist/base/data-types/default/address.js +5 -0
- package/dist/base/data-types/default/address.js.map +1 -1
- package/dist/base/data-types/default/choices.d.ts +20 -0
- package/dist/base/data-types/default/choices.js +20 -0
- package/dist/base/data-types/default/choices.js.map +1 -1
- package/dist/base/data-types/default/files.d.ts +22 -5
- package/dist/base/data-types/default/files.js +20 -0
- package/dist/base/data-types/default/files.js.map +1 -1
- package/dist/base/data-types/default/numbers.d.ts +15 -0
- package/dist/base/data-types/default/numbers.js +25 -9
- package/dist/base/data-types/default/numbers.js.map +1 -1
- package/dist/base/data-types/default/relations.d.ts +10 -2
- package/dist/base/data-types/default/relations.js +9 -6
- package/dist/base/data-types/default/relations.js.map +1 -1
- package/dist/base/data-types/default/strings.d.ts +25 -0
- package/dist/base/data-types/default/strings.js +25 -0
- package/dist/base/data-types/default/strings.js.map +1 -1
- package/dist/base/data-types/default-types.d.ts +1 -1
- package/dist/base/data-types/field-types.d.ts +10 -0
- package/dist/base/data-types/field-types.js +10 -0
- package/dist/base/data-types/field-types.js.map +1 -1
- package/dist/base/data-types/internal/relation-endpoints.js +1 -0
- package/dist/base/data-types/internal/relation-endpoints.js.map +1 -1
- package/dist/base/data-types/status-type.d.ts +5 -0
- package/dist/base/data-types/status-type.js +5 -0
- package/dist/base/data-types/status-type.js.map +1 -1
- package/dist/base/display.d.ts +6 -6
- package/dist/base/empty-state.d.ts +15 -13
- package/dist/base/empty-state.js +2 -0
- package/dist/base/empty-state.js.map +1 -1
- package/dist/base/form-block-schema.d.ts +50 -50
- package/dist/base/form-schema.d.ts +2 -0
- package/dist/base/form-schema.js +2 -0
- package/dist/base/form-schema.js.map +1 -1
- package/dist/base/grid.d.ts +4 -0
- package/dist/base/grid.js +4 -0
- package/dist/base/grid.js.map +1 -1
- package/dist/base/guard.d.ts +30 -28
- package/dist/base/guard.js +2 -0
- package/dist/base/guard.js.map +1 -1
- package/dist/base/helpers/archive-mode-helpers.d.ts +1 -1
- package/dist/base/helpers/file-refs.js +15 -15
- package/dist/base/helpers/file-refs.js.map +1 -1
- package/dist/base/internal/form-schema-validation.js +7 -1
- package/dist/base/internal/form-schema-validation.js.map +1 -1
- package/dist/base/internal/realtime-topics.js.map +1 -1
- package/dist/base/internal/searchable.d.ts +2 -1
- package/dist/base/internal/searchable.js +1 -3
- package/dist/base/internal/searchable.js.map +1 -1
- package/dist/base/key-value-list.d.ts +21 -19
- package/dist/base/key-value-list.js +2 -0
- package/dist/base/key-value-list.js.map +1 -1
- package/dist/base/kpi-card.d.ts +4 -2
- package/dist/base/kpi-card.js +2 -0
- package/dist/base/kpi-card.js.map +1 -1
- package/dist/base/live-toggle.d.ts +2 -0
- package/dist/base/live-toggle.js +2 -0
- package/dist/base/live-toggle.js.map +1 -1
- package/dist/base/meter.d.ts +23 -21
- package/dist/base/meter.js +2 -0
- package/dist/base/meter.js.map +1 -1
- package/dist/base/nav-card-grid.d.ts +24 -19
- package/dist/base/nav-card-grid.js +5 -0
- package/dist/base/nav-card-grid.js.map +1 -1
- package/dist/base/page-toolbar.d.ts +2 -0
- package/dist/base/page-toolbar.js +2 -0
- package/dist/base/page-toolbar.js.map +1 -1
- package/dist/base/period-selector.d.ts +3 -1
- package/dist/base/period-selector.js +2 -0
- package/dist/base/period-selector.js.map +1 -1
- package/dist/base/placeholder.d.ts +2 -0
- package/dist/base/placeholder.js +2 -0
- package/dist/base/placeholder.js.map +1 -1
- package/dist/base/resource-form-schema.d.ts +2 -2
- package/dist/base/searchable.d.ts +3 -2
- package/dist/base/searchable.js +2 -2
- package/dist/base/searchable.js.map +1 -1
- package/dist/base/section.d.ts +6 -2
- package/dist/base/section.js +4 -0
- package/dist/base/section.js.map +1 -1
- package/dist/base/stack.d.ts +6 -0
- package/dist/base/stack.js +6 -0
- package/dist/base/stack.js.map +1 -1
- package/dist/base/stat-group.d.ts +16 -14
- package/dist/base/stat-group.js +2 -0
- package/dist/base/stat-group.js.map +1 -1
- package/dist/base/tab.d.ts +10 -8
- package/dist/base/tab.js +2 -0
- package/dist/base/tab.js.map +1 -1
- package/dist/base/table-view/column-display.d.ts +1 -1
- package/dist/base/table-view/column-display.js.map +1 -1
- package/dist/base/table-view/data-functions.d.ts +10 -4
- package/dist/base/table-view/data-functions.js.map +1 -1
- package/dist/base/table-view/factory.d.ts +5 -0
- package/dist/base/table-view/factory.js +8 -4
- package/dist/base/table-view/factory.js.map +1 -1
- package/dist/base/table-view/internal/auth.d.ts +17 -3
- package/dist/base/table-view/internal/auth.js +26 -5
- package/dist/base/table-view/internal/auth.js.map +1 -1
- package/dist/base/table-view/internal/column-conditions.d.ts +8 -1
- package/dist/base/table-view/internal/column-conditions.js +25 -13
- package/dist/base/table-view/internal/column-conditions.js.map +1 -1
- package/dist/base/table-view/internal/column-display.d.ts +1 -2
- package/dist/base/table-view/internal/column-display.js +3 -9
- package/dist/base/table-view/internal/column-display.js.map +1 -1
- package/dist/base/table-view/internal/factory-helpers.d.ts +1 -1
- package/dist/base/table-view/internal/factory-helpers.js +17 -14
- package/dist/base/table-view/internal/factory-helpers.js.map +1 -1
- package/dist/base/table-view/internal/factory-lifecycle-created.d.ts +2 -3
- package/dist/base/table-view/internal/factory-lifecycle-created.js +5 -5
- package/dist/base/table-view/internal/factory-lifecycle-created.js.map +1 -1
- package/dist/base/table-view/internal/factory-lifecycle-filter.d.ts +2 -2
- package/dist/base/table-view/internal/factory-private-actions.d.ts +2 -2
- package/dist/base/table-view/internal/files.d.ts +2 -1
- package/dist/base/table-view/internal/files.js +14 -5
- package/dist/base/table-view/internal/files.js.map +1 -1
- package/dist/base/table-view/internal/footer.d.ts +3 -3
- package/dist/base/table-view/internal/footer.js +16 -15
- package/dist/base/table-view/internal/footer.js.map +1 -1
- package/dist/base/table-view/internal/guards.d.ts +27 -5
- package/dist/base/table-view/internal/guards.js +133 -49
- package/dist/base/table-view/internal/guards.js.map +1 -1
- package/dist/base/table-view/internal/meta-helpers.d.ts +1 -1
- package/dist/base/table-view/internal/meta-helpers.js +3 -4
- package/dist/base/table-view/internal/meta-helpers.js.map +1 -1
- package/dist/base/table-view/internal/meta.js +2 -3
- package/dist/base/table-view/internal/meta.js.map +1 -1
- package/dist/base/table-view/internal/option-contract.d.ts +9 -1
- package/dist/base/table-view/internal/option-footer.d.ts +2 -2
- package/dist/base/table-view/internal/option-row-actions.d.ts +1 -1
- package/dist/base/table-view/internal/realtime.d.ts +52 -11
- package/dist/base/table-view/internal/realtime.js +23 -16
- package/dist/base/table-view/internal/realtime.js.map +1 -1
- package/dist/base/table-view/internal/resource-form.d.ts +1 -1
- package/dist/base/table-view/internal/resource-form.js +1 -1
- package/dist/base/table-view/internal/resource-form.js.map +1 -1
- package/dist/base/table-view/internal/row-actions-schema.d.ts +12 -12
- package/dist/base/table-view/internal/row-rule-shape.d.ts +11 -5
- package/dist/base/table-view/internal/row-rule-shape.js +60 -30
- package/dist/base/table-view/internal/row-rule-shape.js.map +1 -1
- package/dist/base/table-view/internal/row-rules.d.ts +28 -1
- package/dist/base/table-view/internal/row-rules.js +8 -19
- package/dist/base/table-view/internal/row-rules.js.map +1 -1
- package/dist/base/table-view/internal/tabs.d.ts +1 -1
- package/dist/base/table-view/internal/tabs.js +1 -4
- package/dist/base/table-view/internal/tabs.js.map +1 -1
- package/dist/base/table-view/internal/validation.d.ts +2 -2
- package/dist/base/table-view/internal/validation.js +7 -7
- package/dist/base/table-view/internal/validation.js.map +1 -1
- package/dist/base/table-view/meta.d.ts +7 -6
- package/dist/base/table-view/meta.js +12 -7
- package/dist/base/table-view/meta.js.map +1 -1
- package/dist/base/table-view/resource-form.d.ts +3 -1
- package/dist/base/table-view/resource-form.js +2 -0
- package/dist/base/table-view/resource-form.js.map +1 -1
- package/dist/base/table-view/routes.d.ts +73 -21
- package/dist/base/table-view/routes.js +13 -10
- package/dist/base/table-view/routes.js.map +1 -1
- package/dist/base/table-view/schema.d.ts +157 -152
- package/dist/base/table-view/schema.js +2 -1
- package/dist/base/table-view/schema.js.map +1 -1
- package/dist/base/table-view/source.js +4 -1
- package/dist/base/table-view/source.js.map +1 -1
- package/dist/base/table-view.d.ts +1 -1
- package/dist/base/table-view.js.map +1 -1
- package/dist/base/top-list-card.d.ts +35 -33
- package/dist/base/top-list-card.js +2 -0
- package/dist/base/top-list-card.js.map +1 -1
- package/dist/base/types/error.js +5 -2
- package/dist/base/types/error.js.map +1 -1
- package/dist/base/types/guards.d.ts +4 -4
- package/dist/base/types/json.d.ts +1 -10
- package/dist/base/types/row-action.d.ts +7 -7
- package/dist/component-options/confirm-dialog.d.ts +2 -0
- package/dist/component-options/confirm-dialog.js.map +1 -1
- package/dist/component-options/data-type.d.ts +2 -2
- package/dist/component-options/data-type.js +7 -7
- package/dist/component-options/data-type.js.map +1 -1
- package/dist/component-options/page-layout.d.ts +3 -2
- package/dist/component-options/page-layout.js.map +1 -1
- package/dist/component-options/row-action.d.ts +14 -9
- package/dist/component-slots.d.ts +11 -2
- package/dist/component-slots.js +1 -0
- package/dist/component-slots.js.map +1 -1
- package/dist/component.d.ts +7 -8
- package/dist/component.js +17 -25
- package/dist/component.js.map +1 -1
- package/dist/data-controllers/members.d.ts +11 -7
- package/dist/data-controllers/members.js +1 -1
- package/dist/data-controllers/members.js.map +1 -1
- package/dist/data-controllers/roles.d.ts +32 -10
- package/dist/db/internal/exportJobs.model.d.ts +7 -5
- package/dist/db/internal/inviteResolutions.model.d.ts +7 -5
- package/dist/db/internal/tenantLifecycle.model.d.ts +15 -7
- package/dist/db/internal/tenantLifecycle.model.js +16 -7
- package/dist/db/internal/tenantLifecycle.model.js.map +1 -1
- package/dist/db/models/roles.model.d.ts +7 -5
- package/dist/db/models/tenantMembers.model.d.ts +7 -5
- package/dist/db/models/tenants.model.d.ts +7 -5
- package/dist/db/models/user_invites.model.d.ts +7 -5
- package/dist/guards.d.ts +1 -1
- package/dist/hooks.d.ts +1 -2
- package/dist/hooks.js +33 -26
- package/dist/hooks.js.map +1 -1
- package/dist/internal/attachments.d.ts +2 -2
- package/dist/internal/component-builder.js +2 -1
- package/dist/internal/component-builder.js.map +1 -1
- package/dist/internal/component-serialization.d.ts +2 -4
- package/dist/internal/component-serialization.js +4 -3
- package/dist/internal/component-serialization.js.map +1 -1
- package/dist/internal/component-slots.d.ts +19 -2
- package/dist/internal/component-slots.js +35 -12
- package/dist/internal/component-slots.js.map +1 -1
- package/dist/internal/component-watch-filter.d.ts +11 -0
- package/dist/internal/component-watch-filter.js +22 -0
- package/dist/internal/component-watch-filter.js.map +1 -0
- package/dist/internal/component-watches.js +0 -1
- package/dist/internal/component-watches.js.map +1 -1
- package/dist/internal/own-entry.d.ts +7 -0
- package/dist/internal/own-entry.js +13 -0
- package/dist/internal/own-entry.js.map +1 -0
- package/dist/internal/uploads.d.ts +30 -0
- package/dist/internal/uploads.js +19 -0
- package/dist/internal/uploads.js.map +1 -1
- package/dist/invite-extension-payloads.d.ts +14 -2
- package/dist/invite-extensions/internal/delivery.js +1 -2
- package/dist/invite-extensions/internal/delivery.js.map +1 -1
- package/dist/invite-extensions/internal/edit.d.ts +2 -1
- package/dist/invite-extensions/internal/edit.js +8 -9
- package/dist/invite-extensions/internal/edit.js.map +1 -1
- package/dist/invite-extensions/internal/form-slot.js +8 -15
- package/dist/invite-extensions/internal/form-slot.js.map +1 -1
- package/dist/invite-extensions/internal/payload-slices.d.ts +33 -3
- package/dist/invite-extensions/internal/payload-slices.js +84 -19
- package/dist/invite-extensions/internal/payload-slices.js.map +1 -1
- package/dist/invite-extensions/internal/types.d.ts +10 -1
- package/dist/invite-resolution.js +6 -4
- package/dist/invite-resolution.js.map +1 -1
- package/dist/json.d.ts +10 -0
- package/dist/json.js +3 -0
- package/dist/json.js.map +1 -0
- package/dist/layout-banners.d.ts +5 -2
- package/dist/layout-banners.js.map +1 -1
- package/dist/page/controllers.d.ts +2 -2
- package/dist/page/internal/extension-validation.js +3 -2
- package/dist/page/internal/extension-validation.js.map +1 -1
- package/dist/page/internal/metadata-layout.js +1 -1
- package/dist/page/internal/metadata-layout.js.map +1 -1
- package/dist/page/internal/metadata-publish.js +3 -2
- package/dist/page/internal/metadata-publish.js.map +1 -1
- package/dist/page/internal/metadata-registration.js +5 -4
- package/dist/page/internal/metadata-registration.js.map +1 -1
- package/dist/page/internal/metadata-upload-registration.d.ts +4 -2
- package/dist/page/internal/metadata-upload-registration.js +13 -4
- package/dist/page/internal/metadata-upload-registration.js.map +1 -1
- package/dist/page/internal/metadata-uploads.d.ts +5 -5
- package/dist/page/internal/metadata-uploads.js +4 -8
- package/dist/page/internal/metadata-uploads.js.map +1 -1
- package/dist/page/internal/page-record.d.ts +2 -3
- package/dist/page/internal/page-record.js +23 -16
- package/dist/page/internal/page-record.js.map +1 -1
- package/dist/page/layout-filter.js +17 -6
- package/dist/page/layout-filter.js.map +1 -1
- package/dist/page/metadata.js +5 -7
- package/dist/page/metadata.js.map +1 -1
- package/dist/request-tenant.d.ts +10 -4
- package/dist/request-tenant.js +16 -34
- package/dist/request-tenant.js.map +1 -1
- package/dist/tenant-export.d.ts +2 -1
- package/dist/tenant-lifecycle.d.ts +43 -4
- package/dist/tenant-lifecycle.js +97 -18
- package/dist/tenant-lifecycle.js.map +1 -1
- package/dist/types.d.ts +20 -0
- package/dist/uploads.d.ts +1 -1
- package/dist/uploads.js +39 -60
- package/dist/uploads.js.map +1 -1
- package/dist/utils/fire-and-forget.js.map +1 -1
- package/dist/utils/internal/decorated-class.d.ts +14 -0
- package/dist/utils/internal/decorated-class.js +37 -0
- package/dist/utils/internal/decorated-class.js.map +1 -0
- package/dist/utils/internal/type-check.d.ts +9 -0
- package/dist/utils/internal/type-check.js +13 -5
- package/dist/utils/internal/type-check.js.map +1 -1
- package/dist/utils/internal/value-parser.d.ts +1 -1
- package/dist/utils/internal/value-parser.js +22 -9
- package/dist/utils/internal/value-parser.js.map +1 -1
- package/dist/watch-actions.d.ts +1 -1
- package/docs/1.core/1.introduction.md +82 -77
- package/docs/1.core/10.layout-banners.md +57 -45
- package/docs/1.core/11.modules-and-dynamic-menus.md +178 -0
- package/docs/1.core/12.frontend-modules-and-request-helpers.md +155 -0
- package/docs/1.core/13.tenant-data-export.md +120 -0
- package/docs/1.core/2.pages-and-modules.md +140 -367
- package/docs/1.core/3.components.md +21 -22
- package/docs/1.core/4.permissions.md +92 -71
- package/docs/1.core/5.tenancy.md +150 -86
- package/docs/1.core/6.invites.md +90 -65
- package/docs/1.core/7.hooks.md +96 -166
- package/docs/1.core/8.realtime.md +44 -45
- package/docs/1.core/9.quick-actions.md +37 -30
- package/docs/2.auth/1.introduction.md +33 -35
- package/docs/2.auth/2.authentication.md +60 -38
- package/docs/2.auth/3.tokens-and-sessions.md +56 -55
- package/docs/2.auth/4.users.md +79 -51
- package/docs/2.auth/5.external-identities.md +25 -26
- package/docs/3.base/1.introduction.md +64 -60
- package/docs/3.base/10.display-blocks.md +15 -16
- package/docs/3.base/11.page-layouts.md +270 -0
- package/docs/3.base/12.table-row-actions.md +122 -0
- package/docs/3.base/13.table-filters-and-forms.md +104 -0
- package/docs/3.base/14.table-events-and-helpers.md +114 -0
- package/docs/3.base/15.table-sources-and-gates.md +24 -0
- package/docs/3.base/2.types.md +44 -41
- package/docs/3.base/3.forms.md +27 -28
- package/docs/3.base/4.tables.md +35 -368
- package/docs/3.base/5.charts-and-widgets.md +28 -29
- package/docs/3.base/6.layout.md +21 -270
- package/docs/3.base/8.data-types.md +39 -40
- package/docs/3.base/9.helpers.md +36 -38
- package/docs/4.notifications/1.introduction.md +22 -26
- package/docs/4.notifications/2.categories-and-subjects.md +58 -55
- package/docs/4.notifications/3.sending.md +72 -68
- package/docs/5.html-render/1.introduction.md +119 -81
- package/docs/5.html-render/2.built-in-emails.md +71 -51
- package/docs/index.md +18 -29
- package/docs/tsconfig.json +4 -0
- package/package.json +11 -11
- package/docs/images/emails-admin-invite.png +0 -0
- package/docs/images/emails-change-notice.png +0 -0
- package/docs/images/emails-change-verification.png +0 -0
- package/docs/images/emails-digest-logo-dark.png +0 -0
- package/docs/images/emails-digest-logo-light.png +0 -0
- package/docs/images/emails-digest.png +0 -0
- package/docs/images/emails-export-partial.png +0 -0
- package/docs/images/emails-export-ready.png +0 -0
- package/docs/images/emails-reset-password.png +0 -0
- package/docs/images/emails-two-factor.png +0 -0
- package/docs/images/emails-user-validation.png +0 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Table events and helpers"
|
|
3
|
+
description: Exports, realtime updates, the metadata, the route helpers and the events of a table view.
|
|
4
|
+
mode: reference
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
These are the parts of a [table view](/docs/interfaces/base/tables) that run around its rows: exports, realtime updates, the metadata it registers, the route helpers and its events.
|
|
8
|
+
|
|
9
|
+
## Exports
|
|
10
|
+
|
|
11
|
+
The export action is offered only when the controller registered all three `ExportRoutes`. Pressing it starts a background export job, polls its status, and downloads the result; the job machinery itself is shared with any other long export and is documented in [Helpers](/docs/interfaces/base/helpers). Which columns land in the file is decided by `@Exported`.
|
|
12
|
+
|
|
13
|
+
## Realtime
|
|
14
|
+
|
|
15
|
+
Unless `realtime: false` is set, a table view registers two realtime topics for its page (and for each generated sub-page), and its routes publish every create, update and delete on the first and track who has a row open on the second. Both are named after the controller's location:
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import {
|
|
19
|
+
tableViewPresenceTopic,
|
|
20
|
+
tableViewRowTopic,
|
|
21
|
+
} from "@antelopejs/interface-dms/base/table-view";
|
|
22
|
+
|
|
23
|
+
tableViewRowTopic("/api/orders"); // "tableview:row:/api/orders"
|
|
24
|
+
tableViewPresenceTopic("/api/orders"); // "tableview:presence:/api/orders"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The topics are registered with `RegisterPageTopic` from the table view's `onCreated`, so they belong to the module whose page it is, as described in [Realtime](/docs/interfaces/core/realtime). A module that publishes on a table's row topic itself, such as a background job updating rows, builds the name with `tableViewRowTopic` rather than spelling out the prefix, and passes the tenant of the rows as `tenantId`.
|
|
28
|
+
|
|
29
|
+
The topics are the same for every tenant, but what is published on them is not: the routes publish each change, and each presence, for the tenant the request was made in, so the sessions of the other tenants neither refetch nor see who has a row open. The DMS decides who else receives them, with no option on the table: a table whose rows live in the schema every tenant shares (`CORE_SCHEMA_NAME`) publishes to every tenant, and a session on a platform page (a module's page) receives the changes of every tenant, as such a page lists the data of all of them. `RealtimeMutationContext` and `RealtimePresenceContext` carry the tenant as `tenantId`, absent on a table of the shared schema.
|
|
30
|
+
|
|
31
|
+
Two functions let other modules observe those mutations:
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import {
|
|
35
|
+
registerRealtimeMutationListener,
|
|
36
|
+
unregisterRealtimeMutationListener,
|
|
37
|
+
type RealtimeMutationContext,
|
|
38
|
+
} from "@antelopejs/interface-dms/base/table-view";
|
|
39
|
+
|
|
40
|
+
const listener = (context: RealtimeMutationContext) => {
|
|
41
|
+
// context.controllerLocation, context.eventType, context.ids, …
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
registerRealtimeMutationListener(listener);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Listeners are additive side observers: they run after the DMS has published the mutation, and a listener that throws is logged, never propagated to the mutating request. A listener is released with the module that registered it, including when that module reloads in development; `unregisterRealtimeMutationListener` is for a module that stops listening while it stays loaded.
|
|
48
|
+
|
|
49
|
+
| Type | Shape |
|
|
50
|
+
| --- | --- |
|
|
51
|
+
| `RealtimeMutationEventType` | `"created" \| "updated" \| "deleted"` |
|
|
52
|
+
| `RealtimeMutationContext` | `controllerLocation`, `rowIdKey`, `eventType`, `ids: string[]`, `sessionId?`, `actor?: RealtimePresenceActor` |
|
|
53
|
+
| `RealtimePresenceActor` | `id`, `displayName?`, `avatarUrl?` |
|
|
54
|
+
| `RealtimePresenceContext` | `controllerLocation`, `rowIdKey`, `sessionId`, `rowId`, `actor` |
|
|
55
|
+
|
|
56
|
+
The routes reach the DMS through two interface functions it implements, `internal.PublishMutation` and `internal.AcquirePresence`. They are internal: nothing outside the DMS implements them, and the core binds them to each generation of the DMS module, so a mutation made while the DMS reloads waits for the next generation instead of reaching the one being torn down.
|
|
57
|
+
|
|
58
|
+
## Metadata
|
|
59
|
+
|
|
60
|
+
`TableViewMeta` is the metadata object the decorators write to and the builder reads from. Retrieve it with `GetMetadata(controller, TableViewMeta)`.
|
|
61
|
+
|
|
62
|
+
| Member | Description |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `columns` | `Record<string, ColumnOptions>` keyed by field name. |
|
|
65
|
+
| `groups` | `Record<string, ColumnGroupConfig>` keyed by group id. |
|
|
66
|
+
| `options` | The `TableViewOptions` of the writing `TableView`, or of the last one built while none writes. |
|
|
67
|
+
| `archiveField` | Field name recorded by `@ArchiveField`. |
|
|
68
|
+
| `controllerRowActionRules` | The rules the write routes enforce: the writing table view's `rowActions`, archive-mode defaults included. |
|
|
69
|
+
| `controllerGuards` | Guards promoted to controller-wide guards. |
|
|
70
|
+
| `componentBuilders` | Every live `TableView` builder built on the controller, in build order. |
|
|
71
|
+
| `writingComponentBuilders` | The live builders of `componentBuilders` that write through the data routes. |
|
|
72
|
+
| `actionPermissionIds(actionId)` | The permission id the action carries on each mounted table view of the controller, on the writing one only for a write action. |
|
|
73
|
+
| `addComponentBuilder(builder, access?)` | Writer the builder calls to record itself, with its `TableViewAccess` (`"read"` or `"write"`, the default). A custom component standing in for a table view (the roles editor) records itself the same way. |
|
|
74
|
+
| `resourceFormBuilders` | Every live `new` / `edit` form built over the controller, in build order: the `ResourceForm` blocks and the forms each table view opens. |
|
|
75
|
+
| `addResourceFormBuilder` | Writer the form builder calls to record a form that submits to the controller. |
|
|
76
|
+
| `writingComponents` | Every live component that submits to the controller's write routes: its writing table view and its `new` / `edit` forms, including the ones a page-mode table view mounts on its form sub-pages. A native file staged by any of them is accepted on save; one staged by any other component is refused. |
|
|
77
|
+
| `inherit(parent)` | Called by `GetMetadata` for a derived controller: copies the parent's `columns` (each with its own copy of its data type), `groups`, `options` and `archiveField`, never its table views or forms. |
|
|
78
|
+
| `config` | Getter returning `{ location, columns }`, the serialized column list plus the controller's route location. |
|
|
79
|
+
| `getFormFields(mode)` | Builds the `FormFieldOrGroup[]` for `"view"`, `"edit"` or `"new"`. |
|
|
80
|
+
| `setColumn`, `setGroup`, `setOptions`, `setArchiveField`, `setControllerRowActionRules`, `setControllerGuards` | Writers used by the decorators and the builder. |
|
|
81
|
+
|
|
82
|
+
Each entry of `config.columns` carries `id`, `header`, `isVisible`, `type` (the serialized data type), `accessorKey`, `listable`, `enableSorting`, `enableColumnFilter`, `defaultValue`, `accessMode` and `readonlyBehavior`.
|
|
83
|
+
|
|
84
|
+
## Route helpers
|
|
85
|
+
|
|
86
|
+
`table-view` also exports the interface functions the wrapped routes call. They are the seam between the interface and its implementation. A consumer wires them by declaring `TableViewRoutes`, and does not normally call them directly.
|
|
87
|
+
|
|
88
|
+
| Function | Role |
|
|
89
|
+
| --- | --- |
|
|
90
|
+
| `listWithSearch` | Runs the list query with search, filters, sorting and pagination; returns `{ results, total, offset, limit }`. |
|
|
91
|
+
| `countWithSearch` | Same query, count only; returns `{ total }`. |
|
|
92
|
+
| `startExport` | Queues an export job; returns `{ jobId, format, extension }`. |
|
|
93
|
+
| `getExportStatus` | Returns `{ status, progress, error? }` for a job. |
|
|
94
|
+
| `downloadExport` | Streams a completed job's file to the response. |
|
|
95
|
+
| `archiveRows` | Sets the archive field on the given ids; returns `{ success, archivedCount }`. |
|
|
96
|
+
| `restoreRows` | Clears it; returns `{ success, restoredCount }`. |
|
|
97
|
+
| `validateRowsAgainstRule` | Splits ids into `{ eligibleIds, rejectedIds }` against a `RowActionRule`. |
|
|
98
|
+
| `fetchRowForGuard` | Loads one row by id field, for the `edit` guard and the file-cleanup passes. |
|
|
99
|
+
|
|
100
|
+
## Events and functions
|
|
101
|
+
|
|
102
|
+
| Namespace member | Value |
|
|
103
|
+
| --- | --- |
|
|
104
|
+
| `TableViewEvents.ROW_CLICK` | `"DmsComponent.TableView.RowClick"` |
|
|
105
|
+
| `TableViewEvents.ROW_SELECT` | `"DmsComponent.TableView.RowSelect"` |
|
|
106
|
+
| `TableViewEvents.ROW_DELETE` | `"DmsComponent.TableView.RowDelete"` |
|
|
107
|
+
| `TableViewEvents.ROW_ADD` | `"DmsComponent.TableView.RowAdd"` |
|
|
108
|
+
| `TableViewEvents.ROW_EDIT` | `"DmsComponent.TableView.RowEdit"` |
|
|
109
|
+
| `TableViewEvents.FILTER_CHANGE` | `"DmsComponent.TableView.FilterChange"` |
|
|
110
|
+
| `TableViewEvents.SORT_CHANGE` | `"DmsComponent.TableView.SortChange"` |
|
|
111
|
+
| `TableViewEvents.EXPORT` | `"DmsComponent.TableView.Export"` |
|
|
112
|
+
| `TableViewFunctions.CUSTOM_PAGE_FORM_SUCCESS` | `"DmsComponent.TableView.CustomPageFormSuccess"` |
|
|
113
|
+
|
|
114
|
+
`CUSTOM_PAGE_FORM_SUCCESS` registers `{ url: string; preserveQuery?: string[] }` in `WatchFunctionParamMap`. The builder uses it itself: the generated `new` and `edit` page forms watch `FormEvents.SUBMIT_SUCCESS` and navigate back to the table, preserving the query parameters named in `queryParamFilters`. The handler resolves `{{params.X}}` / `{{query.X}}` tokens in `url` against the route being left before navigating; the generated forms rely on it to send the user back to a page whose slug carries parameters: each placeholder of the page's slug becomes the token reading it on the form route (`/workspaces/:id` becomes `/workspaces/{{params.id:1}}` from `/workspaces/:id/invoiceTable/:id/edit`, `/workspaces/{{params.id}}` from `/workspaces/:id/invoiceTable/new`). A slug without parameters is used as is. The `view` form submits nothing and carries no such watch. All three generated pages are registered with `FormPageLayout()`, the constrained-column layout ([Layout and containers](/docs/interfaces/base/layout)).
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Table sources and tenant gates"
|
|
3
|
+
description: Table views over a module's own route, and table views that stay readable behind a tenant access gate.
|
|
4
|
+
mode: reference
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
A [table view](/docs/interfaces/base/tables) usually reads a data controller. These two cases differ: a table view over a module's own route, and a table view that stays readable while a tenant access gate denies the tenant.
|
|
8
|
+
|
|
9
|
+
## Tables over a module's route
|
|
10
|
+
|
|
11
|
+
```typescript fragment
|
|
12
|
+
TableView.fromSource(options: TableViewSourceOptions): ComponentBuilder<TableViewOptionsSerialized>;
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
A read-only table view with no data controller (`tableViewFromSource`). `TableViewSourceOptions` takes `columns` (a record of `TableViewSourceColumn`, a `@Column`'s options plus `sortable`), `fetchUrl` (its `{{params.X}}` / `{{query.X}}` tokens are filled from the page URL, percent-encoded; the table and its tab counters list again when they change and request nothing while one has no value), `fetchRowUrl?` (a route answering one row by id, `{{row.id}}` its percent-encoded id, with the same tokens: a deep link to a row the table does not list opens through it; serialized on `TableViewSourceSerialized`), `rowIdKey?`, `periodScope?` (the id of a `PeriodSelector`: every request carries its `from`, `to`, `preset` and `comparison`, the table lists again when the period changes, and nothing is requested until the selector is on the page), `capabilities?` (`TableViewSourceCapabilities`: `search`, `sort`, `paginate`, `filter`), `queryParamFilters?` (URL query parameters sent to the route as hidden `filter_<field>` filters), custom row actions and buttons only, and the presentation options of a table view (`caption`, `layout`, `pagination`, `tabs`, `quickFilters`, `displays`, `card`, `emptyStates`, `footer` texts and legend…). The route answers `{ results, total }` and is sent the part of the list query its capabilities name, nothing else; the browser does the rest over the rows it answered, except that a paging route's rows are neither searched nor sorted by the browser. Registration runs the checks of `TableView()` that apply (page size, display ids, tab targets) and throws when tabs, quick filters or `queryParamFilters` lack `capabilities.filter`, or when a key they, `labelKey`, `card.fields` or `expandable` name is not a column, or when `expandable` sets `lazyLoad` (a source has no `get` route), or when `fetchRowUrl` lacks `{{row.id}}`; it warns about a quick filter on a column with no values to pick. There is no export, realtime nor footer summary.
|
|
16
|
+
|
|
17
|
+
## Reach a table behind a tenant gate
|
|
18
|
+
|
|
19
|
+
`bypassTenantAccessGate` is the table-view mirror of the guard option of the same name. Reserve it for tables that must stay readable while a gate denies the tenant, such as the invoices on the billing page of a suspended tenant, so it can regularize its situation. Permission checks still apply.
|
|
20
|
+
|
|
21
|
+
Data routes are registered against the controller, not against a page, so the opt-out is controller-level and **latched**: once any `TableView()` call on a controller declares it, the controller's routes bypass the gate wherever they are rendered. A later registration of the same controller without the flag cannot re-gate them, and gets a warning saying so. Two consequences worth designing around:
|
|
22
|
+
|
|
23
|
+
- Declaring the flag on a controller opens its routes for every table view built on it, including one mounted on an ordinary page.
|
|
24
|
+
- Actions that carry no permission id, such as a plain list on a component that was never mounted, are then served to any member of a denied tenant. That is the escape hatch working as intended, so keep every action of a flagged controller stamped with a permission.
|
package/docs/3.base/2.types.md
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "Shared
|
|
2
|
+
title: "Shared types"
|
|
3
3
|
description: "The `types` subpath holds the vocabulary every other part of the `base` domain is built from: the base props interface every component's props extend, the colour and size scales, the action targets a button or a row…"
|
|
4
|
+
mode: reference
|
|
4
5
|
---
|
|
5
6
|
|
|
6
|
-
# Shared Types
|
|
7
|
-
|
|
8
7
|
## Overview
|
|
9
8
|
|
|
10
9
|
The `types` subpath holds the vocabulary every other part of the `base` domain is built from: the base props interface every component's props extend, the colour and size scales, the action targets a button or a row action can point at, the rule algebra that decides whether a row action applies to a row, the guard signatures a table view runs before a mutation, and a handful of small enums and value unions.
|
|
@@ -30,7 +29,7 @@ Four modules are not re-exported by that barrel and are imported from their own
|
|
|
30
29
|
| `FIELD_OPERATORS`, `FIELD_KEY`, `FieldOperatorKey`, `FieldRule`, `AnyFieldRule`, `LOGICAL_OPERATOR_KEYS`, `LogicalOperatorKey` | `@antelopejs/interface-dms/base/types/row-action-operators` |
|
|
31
30
|
| `EditGuardArgs`, `DeleteGuardArgs`, `BulkGuardArgs`, `NewGuardArgs`, `GuardFn`, `AfterWrite`, `TableViewGuards` | `@antelopejs/interface-dms/base/types/guards` |
|
|
32
31
|
|
|
33
|
-
## Base
|
|
32
|
+
## Base component props
|
|
34
33
|
|
|
35
34
|
`BaseComponentProps` is the one field every component props interface inherits.
|
|
36
35
|
|
|
@@ -48,9 +47,9 @@ interface BaseComponentProps {
|
|
|
48
47
|
}
|
|
49
48
|
```
|
|
50
49
|
|
|
51
|
-
`FormProps`, `
|
|
50
|
+
`FormProps`, `TabProps`, `KpiCardProps`, `ChartCardProps`, `TopListCardProps`, `PeriodSelectorProps` and the chart props all extend it. The `ComponentBuilder` from `@antelopejs/interface-dms/component` strips a caller-supplied `watchActions` from the options it receives and fills the field itself from `.watch()` / `.watchOn()` calls, so the property is the transport rather than something you set by hand.
|
|
52
51
|
|
|
53
|
-
### Watch
|
|
52
|
+
### Watch actions
|
|
54
53
|
|
|
55
54
|
A `WatchAction` binds an event emitted by a component to a function exposed by a component.
|
|
56
55
|
|
|
@@ -66,13 +65,13 @@ import type {
|
|
|
66
65
|
| `component` | `string` | Identifier of the component that emits the event. `ComponentId.SELF` (`"$component:self"`) refers to the declaring component. |
|
|
67
66
|
| `event` | `string` | Event name, e.g. `FormEvents.SUBMIT_SUCCESS`. |
|
|
68
67
|
| `functionId` | `string` | Identifier of the function to invoke, e.g. `FormFunctions.SET_FIELD_HIDDEN`. |
|
|
69
|
-
| `params` | `
|
|
68
|
+
| `params` | `object` | Arguments passed to the function. Read a member after checking it (`"targetField" in params`), or read the shape the function registers through `WatchFunctionParams`. |
|
|
70
69
|
| `onParam` | `WatchActionCondition \| WatchActionCondition[]` | Only run when the event payload matches these `{ key, value }` pairs. |
|
|
71
70
|
| `requirePermission` | `string` | Permission id the viewer must hold for the action to be kept. |
|
|
72
71
|
|
|
73
|
-
`WatchFunctionParamMap` is an interface extended by declaration merging: each module that exposes a watch function adds its `functionId` and the exact parameter shape to the map. `WatchFunctionId` is `keyof WatchFunctionParamMap & string`, and `WatchFunctionParams<F>` resolves to the registered parameter shape for `F`, falling back to `Record<string, unknown> | undefined` for ids that are not in the map. `Form` and `TableView` both register their functions in this map
|
|
72
|
+
`WatchFunctionParamMap` is an interface extended by declaration merging: each module that exposes a watch function adds its `functionId` and the exact parameter shape to the map. `WatchFunctionId` is `keyof WatchFunctionParamMap & string`, and `WatchFunctionParams<F>` resolves to the registered parameter shape for `F`, falling back to `Record<string, unknown> | undefined` for ids that are not in the map. `Form` and `TableView` both register their functions in this map, as [Forms](/docs/interfaces/base/forms) and [Tables](/docs/interfaces/base/tables) describe.
|
|
74
73
|
|
|
75
|
-
## Colors and
|
|
74
|
+
## Colors and sizes
|
|
76
75
|
|
|
77
76
|
```typescript
|
|
78
77
|
import {
|
|
@@ -86,7 +85,7 @@ import {
|
|
|
86
85
|
|
|
87
86
|
`Color` is an enum of the semantic palette names: `primary`, `secondary`, `success`, `info`, `warning`, `error`, `neutral`. `ColorValue` is `Color | (string & {})`, so a component that accepts a `ColorValue` takes either an enum member or an arbitrary string while keeping the enum members in editor completion.
|
|
88
87
|
|
|
89
|
-
`TONES` (`Tone`, from `types/tone`) is `neutral`, `primary`, `secondary`, `success`, `warning`, `error`, `info`; `ICON_TONES` (`IconTone`) adds `muted`. `color` is for what is clicked (buttons, row actions, header actions, tabs), `tone` for what shows a state (pills, icon wells, banners, meters, notifications, blocks); see [Display
|
|
88
|
+
`TONES` (`Tone`, from `types/tone`) is `neutral`, `primary`, `secondary`, `success`, `warning`, `error`, `info`; `ICON_TONES` (`IconTone`) adds `muted`. `color` is for what is clicked (buttons, row actions, header actions, tabs), `tone` for what shows a state (pills, icon wells, banners, meters, notifications, blocks); see [Display blocks](/docs/interfaces/base/display-blocks).
|
|
90
89
|
|
|
91
90
|
`Size` maps readable names onto the scale tokens: `tiny` → `"xs"`, `small` → `"sm"`, `medium` → `"md"`, `large` → `"lg"`, `huge` → `"xl"`. `SizeValue` widens it the same way `ColorValue` widens `Color`.
|
|
92
91
|
|
|
@@ -94,7 +93,7 @@ import {
|
|
|
94
93
|
|
|
95
94
|
## Buttons
|
|
96
95
|
|
|
97
|
-
`ButtonVariant` is an enum with `solid`, `outline`, `soft`, `ghost` and `link`. `ButtonColor` is a plain string union
|
|
96
|
+
`ButtonVariant` is an enum with `solid`, `outline`, `soft`, `ghost` and `link`. `ButtonColor` is a plain string union, `"primary" | "secondary" | "success" | "error" | "warning" | "info" | "neutral"`, used by `CustomButton` in place of the `Color` enum.
|
|
98
97
|
|
|
99
98
|
`CustomButton` describes a toolbar button; it is what `TableViewOptions.customButtons` takes.
|
|
100
99
|
|
|
@@ -123,7 +122,7 @@ const inviteButton: CustomButton = {
|
|
|
123
122
|
| `permissionId` | `string` | Absolute permission id the button requires; wins over `permission`. |
|
|
124
123
|
| `availability` | `CustomButtonAvailability` | Disable the button, with a reason, for a request it refuses. |
|
|
125
124
|
|
|
126
|
-
`permission` takes either form: a string names one of the owner's actions, relative to its permission id
|
|
125
|
+
`permission` takes either form: a string names one of the owner's actions, relative to its permission id: the table's for a toolbar button (`"add"` → `<table permission id>.add`), the page's for a header button (`DefaultLayout({ headerActions })`). An `Action` references any component's action, such as another table's `add`. `permissionId` takes an absolute permission id (`"settings.workspace.invites"`) and wins when both are set. A button the caller lacks the permission for is stripped from the serialized options rather than disabled, so it never reaches the browser. A declared permission that cannot be resolved to an id, such as an action on a page that never registered, fails closed and strips the button too.
|
|
127
126
|
|
|
128
127
|
A button with an `id` can be exposed as a [quick action](/docs/interfaces/core/quick-actions) with a `"button"` target. The quick action opens what the button opens and inherits the button's `permission`: it is listed for exactly the callers the button is shown to.
|
|
129
128
|
|
|
@@ -135,7 +134,7 @@ A button with an `id` can be exposed as a [quick action](/docs/interfaces/core/q
|
|
|
135
134
|
|
|
136
135
|
`PermissionGate`, imported from `@antelopejs/interface-dms/permission-gate`, names the shared optional `permission?: ButtonPermission` and `permissionId?: string` fields described above. `ButtonPermission` is `string | Action`; `permissionId` takes precedence. The shape is shared by permission-filtered buttons, row actions, table tabs and views. It does not authorize the route that a control calls: protect that route with a guard too.
|
|
137
136
|
|
|
138
|
-
## Action
|
|
137
|
+
## Action targets
|
|
139
138
|
|
|
140
139
|
`ActionTarget` is a discriminated union on `type`. It is what a custom button or a custom row action points at.
|
|
141
140
|
|
|
@@ -147,7 +146,7 @@ import type { ActionTarget } from "@antelopejs/interface-dms/base/types/action-t
|
|
|
147
146
|
| --- | --- |
|
|
148
147
|
| `"drawer"` | `component: Component`, `title?`, `description?`, `direction?: DrawerDirection`, plus `DrawerTargetOptions`: `size?: ModalSize`, `rowHeader?`, `rowTitle?`, `positionLabel?`, `deepLink?`, `notFound?` |
|
|
149
148
|
| `"modal"` | `component: Component`, `size?: ModalSize`, `title?`, `description?` |
|
|
150
|
-
| `"sidePanel"` | `component: Component`, plus `SidePanelTargetOptions`: `title?`, `rowTitle?`, `positionLabel?`, `deepLink?`, `notFound
|
|
149
|
+
| `"sidePanel"` | `component: Component`, plus `SidePanelTargetOptions`: `title?`, `rowTitle?`, `positionLabel?`, `deepLink?`, `notFound?`. For global, page-independent panels. A detail panel is a `drawer`. |
|
|
151
150
|
| `"page"` | `url: string`: the page URL's `{{params.X}}` / `{{query.X}}` (a query pair without a value left out), then on a row action the row's `{{row.field}}`; see [Template tokens](/docs/building/pages-and-components#template-tokens) |
|
|
152
151
|
| `"external"` | `url: string`, `newTab?: boolean`; the same tokens as `page` |
|
|
153
152
|
| `"download"` | `url: string`, `filename?: string` (`DownloadActionTarget`); the same tokens as `page`, the file name taking them as written. Fetched with the user's session and saved under `filename`, else the server's `Content-Disposition` name, else the URL's last segment; a failure is toasted |
|
|
@@ -155,15 +154,17 @@ import type { ActionTarget } from "@antelopejs/interface-dms/base/types/action-t
|
|
|
155
154
|
| `"api"` | `url: string`, `method?: "GET" \| "POST" \| "PUT" \| "DELETE" \| "PATCH"`, `body?`, `copy?`, `successMessage: string`; the same tokens as `page` |
|
|
156
155
|
| `"exportJob"` | `url: string`, `method?: "GET" \| "POST"`, `statusUrl?`, `downloadUrl?`, `labels?`; the same tokens as `page`, plus `{{job.id}}` in `statusUrl` and `downloadUrl` |
|
|
157
156
|
|
|
158
|
-
`SidePanelTargetOptions` sets the header the DMS draws above a `sidePanel` target's component: `title` (the action's label by default; `$`: an i18n key), `rowTitle`, the title while a row is open, an i18n key or a literal receiving the row's fields (`"{method} {path}"`), `positionLabel`, an i18n key receiving `{ index }` (from 1) and `{ total }` for the row's position among all the rows the table lists, its count rather than the page loaded (the DMS's "{index} of {total}" by default), and `deepLink`, which keeps the open row in the URL like the action's own `deepLink
|
|
157
|
+
`SidePanelTargetOptions` sets the header the DMS draws above a `sidePanel` target's component: `title` (the action's label by default; `$`: an i18n key), `rowTitle`, the title while a row is open, an i18n key or a literal receiving the row's fields (`"{method} {path}"`), `positionLabel`, an i18n key receiving `{ index }` (from 1) and `{ total }` for the row's position among all the rows the table lists, its count rather than the page loaded (the DMS's "{index} of {total}" by default), and `deepLink`, which keeps the open row in the URL like the action's own `deepLink`. It is on by default for a side panel, `false` opting out (the action's own `deepLink` wins when set), and `notFound` (`SidePanelNotFoundTexts`: `title?`, `description?`), the state the panel draws with a close button when the row a link names answers 404. The panel is non-modal and docked on the right, one at a time with a module's side panels. It is meant for global, page-independent panels (an assistant dock): a detail or record panel opens in a `drawer`.
|
|
159
158
|
|
|
160
|
-
`DrawerTargetOptions` gives a `drawer` target opened on a row the same options, so a detail panel moves from `sidePanel` to `drawer` without losing any: `rowTitle`, `positionLabel` and `notFound` as above; `rowHeader`, the DMS drawing the row header (the title naming the row, its position, previous and next buttons next to the close button)
|
|
159
|
+
`DrawerTargetOptions` gives a `drawer` target opened on a row the same options, so a detail panel moves from `sidePanel` to `drawer` without losing any: `rowTitle`, `positionLabel` and `notFound` as above; `rowHeader`, the DMS drawing the row header (the title naming the row, its position, previous and next buttons next to the close button), on when `rowTitle` or `positionLabel` is set, `false` leaving the drawer's own title, `true` drawing it with the defaults; `deepLink`, off by default for a drawer (the action's own `deepLink` wins when set); and `size` (`ModalSize`), the width a `left` / `right` drawer opens at from `lg`, a modal's width at that size. J and K step through the rows in place whatever the header. A drawer shows its not-found state only once its target sets `notFound`; otherwise a deep link to a missing row is a toast, as before.
|
|
161
160
|
|
|
162
161
|
`DrawerDirection` is `"top" | "right" | "bottom" | "left"`, the edge the drawer slides in from: `bottom` by default, while `left` and `right` open a side sheet spanning the window's height, 600px wide from `lg` without a `size` (capped to the window) and 440px below it. From `lg` the user resizes every drawer by its inner edge (width or height), and the drawer opens again at the size it was left at.
|
|
163
162
|
|
|
164
163
|
A target carries no confirmation: the action does (`CustomButton.confirm`, `CustomRowAction.confirm`, `RowActionConfig.confirm`), with one type, `ActionConfirm = ConfirmDialog | ConfirmDialogFrom` (see below). The `labels` object on `exportJob` accepts `title`, `exporting`, `downloading`, `successTitle`, `successMessage`, `errorTitle` and `retry`, all optional strings.
|
|
165
164
|
|
|
166
165
|
```typescript
|
|
166
|
+
import { ActionTarget } from "@antelopejs/interface-dms/base/types/action-target";
|
|
167
|
+
|
|
167
168
|
const validateEmail: ActionTarget = {
|
|
168
169
|
type: "api",
|
|
169
170
|
url: "/settings/workspace/members/{{row._id}}/validate-email",
|
|
@@ -181,15 +182,15 @@ import type {
|
|
|
181
182
|
} from "@antelopejs/interface-dms/base/types/confirm-dialog";
|
|
182
183
|
```
|
|
183
184
|
|
|
184
|
-
`ConfirmDialog` is the one confirmation every action asks in: `title`, `description?`, `params?` (interpolated into the texts, besides a row action's row fields; a numeric `count` pluralizes them), `icon?`, `color?` (`ButtonColor`), `confirmLabel?`, `cancelLabel?`, `impact?: ConfirmDialogImpact[]` (`{ icon, label, count? }`), `secondaryAction?: ConfirmDialogSecondaryAction` (`{ label, icon?, target }`, a button next to cancel and confirm that leaves the dialog open; its `target` is an `external` link or `{ type: "download", url, filename? }`, fetched with the user's session), `blocked?` (the dialog only explains why the action cannot run), `confirmText?` (type-to-confirm), `confirmTextCaseInsensitive?` (the typed text compared ignoring case), `fields?: FormField[]` and `component?: Component`. The values of `fields` are sent with the action's request
|
|
185
|
+
`ConfirmDialog` is the one confirmation every action asks in: `title`, `description?`, `params?` (interpolated into the texts, besides a row action's row fields; a numeric `count` pluralizes them), `icon?`, `color?` (`ButtonColor`), `confirmLabel?`, `cancelLabel?`, `impact?: ConfirmDialogImpact[]` (`{ icon, label, count? }`), `secondaryAction?: ConfirmDialogSecondaryAction` (`{ label, icon?, target }`, a button next to cancel and confirm that leaves the dialog open; its `target` is an `external` link or `{ type: "download", url, filename? }`, fetched with the user's session), `blocked?` (the dialog only explains why the action cannot run), `confirmText?` (type-to-confirm), `confirmTextCaseInsensitive?` (the typed text compared ignoring case), `fields?: FormField[]` and `component?: Component`. The values of `fields` are sent with the action's request, merged into an `api` target's `body` with the input winning. A field error the server answers shows under its field. `ConfirmDialogFrom` is `{ from: string }`: a URL (on a row action, built-in or custom, the row's fields filled in, such as `{{row._id}}`, plus `{{row.id}}`, the row's id) answering a `ConfirmDialogSerialized`, which `serializeConfirmDialog(dialog)` builds. `ActionConfirm` is either.
|
|
185
186
|
|
|
186
187
|
`ActionTargetSerialized` mirrors the union exactly, with `component` on the `drawer`, `modal` and `sidePanel` variants replaced by `ComponentInfoSerialized`.
|
|
187
188
|
|
|
188
|
-
## Row
|
|
189
|
+
## Row action rules
|
|
189
190
|
|
|
190
191
|
A row action rule decides, per row, whether an action applies. The same rule value is sent to the frontend so the row's menu matches what the backend will accept, and evaluated on the backend before an `edit`, `delete`, `archive` or `restore` runs.
|
|
191
192
|
|
|
192
|
-
### Field
|
|
193
|
+
### Field operators
|
|
193
194
|
|
|
194
195
|
`FIELD_OPERATORS` is the catalogue of leaf comparisons. Each entry declares whether the rule value is an array and carries the comparison itself.
|
|
195
196
|
|
|
@@ -210,9 +211,9 @@ import {
|
|
|
210
211
|
|
|
211
212
|
`FieldOperatorKey` is `keyof typeof FIELD_OPERATORS`. `FIELD_KEY` is the literal `"field"`, the property name that carries the field name inside a leaf rule. `LOGICAL_OPERATOR_KEYS` is the tuple `["and", "or", "not"]` and `LogicalOperatorKey` is its element type.
|
|
212
213
|
|
|
213
|
-
### Rule
|
|
214
|
+
### Rule shapes
|
|
214
215
|
|
|
215
|
-
A leaf rule is `{ field: K }` plus the operator key. `FieldRule<T, Op>` builds that shape for one operator over the keys of `T`, choosing `T[K][]` for the array operators and `T[K]
|
|
216
|
+
A leaf rule is `{ field: K }` plus the operator key. `FieldRule<T, Op>` builds that shape for one operator over the keys of `T`, choosing `Awaited<T[K]>[]` for the array operators and `Awaited<T[K]>` for the others: a getter answering a promise is compared by the value it resolves to, as the served row holds it. `AnyFieldRule<T>` is the union over all four operators.
|
|
216
217
|
|
|
217
218
|
```typescript
|
|
218
219
|
import type {
|
|
@@ -235,7 +236,7 @@ const editable: AndRule<Invoice> = {
|
|
|
235
236
|
|
|
236
237
|
The four named leaf aliases are `FieldEqualsRule<T>`, `FieldNotEqualsRule<T>`, `FieldInRule<T>` and `FieldNotInRule<T>`, each an alias of `FieldRule<T, Op>` for the matching operator. The three combinators are `AndRule<T>` (`{ and: RowActionRule<T>[] }`), `OrRule<T>` (`{ or: RowActionRule<T>[] }`) and `NotRule<T>` (`{ not: RowActionRule<T> }`); `LogicalRule<T>` is their union. `RowActionRule<T>` is `AnyFieldRule<T> | LogicalRule<T>`, so rules nest to any depth.
|
|
237
238
|
|
|
238
|
-
###
|
|
239
|
+
### Attach a rule
|
|
239
240
|
|
|
240
241
|
`RowActionConfig<T>` is the object form of a built-in row action.
|
|
241
242
|
|
|
@@ -258,7 +259,7 @@ The four named leaf aliases are `FieldEqualsRule<T>`, `FieldNotEqualsRule<T>`, `
|
|
|
258
259
|
|
|
259
260
|
`CustomRowActionSerialized` is the same shape with `target: ActionTargetSerialized` and an untyped `RowActionRule`.
|
|
260
261
|
|
|
261
|
-
|
|
262
|
+
[Tables](/docs/interfaces/base/tables) covers where these are consumed: `TableViewOptions.rowActions` and the archive-mode defaults.
|
|
262
263
|
|
|
263
264
|
## Guards
|
|
264
265
|
|
|
@@ -309,13 +310,13 @@ A mutating guard may return an `AfterWrite`: the table view runs it once the wri
|
|
|
309
310
|
|
|
310
311
|
`T` is the data controller instance `TableView()` infers from its controller; it defaults to `Record<string, unknown>` on every one of them. How guards are attached and when each one fires is documented in [Tables](/docs/interfaces/base/tables).
|
|
311
312
|
|
|
312
|
-
## Form and
|
|
313
|
+
## Form and field types
|
|
313
314
|
|
|
314
|
-
### FormMode
|
|
315
|
+
### `FormMode`
|
|
315
316
|
|
|
316
317
|
`FormMode` is the enum `new`, `edit`, `view`. It selects which variant of a form the table view generates and which readonly behaviour applies.
|
|
317
318
|
|
|
318
|
-
### ReadonlyBehavior
|
|
319
|
+
### `ReadonlyBehavior`
|
|
319
320
|
|
|
320
321
|
`ReadonlyBehaviorType` is an enum with `disabled`, `hidden` and `default`. `ReadonlyBehaviorConfig` gives one value per mode:
|
|
321
322
|
|
|
@@ -331,7 +332,7 @@ const createdAtBehavior = {
|
|
|
331
332
|
|
|
332
333
|
`ReadonlyBehavior` is `ReadonlyBehaviorType | ReadonlyBehaviorConfig`, so a column may pass a single value that applies to every mode or a per-mode object. It is read by `ColumnOptions.readonlyBehavior`.
|
|
333
334
|
|
|
334
|
-
### ListableConfig and FieldMetadata
|
|
335
|
+
### `ListableConfig` and `FieldMetadata`
|
|
335
336
|
|
|
336
337
|
`FieldMetadata` is the slice of a field's data-api metadata the table view reads when it builds a column or a form field.
|
|
337
338
|
|
|
@@ -342,35 +343,37 @@ const createdAtBehavior = {
|
|
|
342
343
|
| `listable` | `ListableConfig` | Which pluck modes include the field. |
|
|
343
344
|
| `sortable` | `boolean` | Whether the field can be sorted on. |
|
|
344
345
|
|
|
345
|
-
`ListableConfig` is `{ list?: boolean; select?: boolean; export?: boolean }
|
|
346
|
+
`ListableConfig` is `{ list?: boolean; select?: boolean; export?: boolean }`, one flag per pluck mode.
|
|
346
347
|
|
|
347
|
-
### Field
|
|
348
|
+
### Field values
|
|
348
349
|
|
|
349
350
|
`DefaultValue` and `FormFieldValue` are the same recursive union: `string | number | boolean | Date | null` and arrays of themselves. `DefaultValue` types `FormField.defaultValue` and `ColumnOptions.defaultValue`; `FormFieldValue` types a value held by a form field.
|
|
350
351
|
|
|
351
|
-
### ComponentOrNoInput
|
|
352
|
+
### `ComponentOrNoInput`
|
|
352
353
|
|
|
353
354
|
```typescript
|
|
355
|
+
import { Component } from "vue";
|
|
356
|
+
|
|
354
357
|
type ComponentOrNoInput<T = undefined> = Component<T> | "noInput";
|
|
355
358
|
```
|
|
356
359
|
|
|
357
|
-
The literal `"noInput"` stands for a compare mode that takes no value
|
|
360
|
+
The literal `"noInput"` stands for a compare mode that takes no value. `is_empty` and `is_not_empty` are registered that way, and `serializeType` substitutes the literal for their filter component. The serialized counterpart used by data types is `FilterComponent`, documented in [Data types](/docs/interfaces/base/data-types).
|
|
358
361
|
|
|
359
|
-
## Transport and
|
|
362
|
+
## Transport and other types
|
|
360
363
|
|
|
361
|
-
### HttpMethod
|
|
364
|
+
### `HttpMethod`
|
|
362
365
|
|
|
363
366
|
`HttpMethod` is an enum of `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`, keyed by the lowercase name (`HttpMethod.get`, `HttpMethod.post`, …). Every component with a `fetchUrl` also accepts a `fetchUrlMethod` of this type.
|
|
364
367
|
|
|
365
|
-
### AxeOrientation
|
|
368
|
+
### `AxeOrientation`
|
|
366
369
|
|
|
367
370
|
`AxeOrientation` is the enum `horizontal` / `vertical`. `TabProps.orientation`, `BarChartProps.orientation` and `FormComponents.RadioGroupOptions.orientation` take it.
|
|
368
371
|
|
|
369
|
-
### ExportStatus
|
|
372
|
+
### `ExportStatus`
|
|
370
373
|
|
|
371
|
-
`ExportStatus` is the enum `pending`, `completed`, `failed`. It is the status stored on an export job and returned by the export-status route
|
|
374
|
+
`ExportStatus` is the enum `pending`, `completed`, `failed`. It is the status stored on an export job and returned by the export-status route, as [Helpers](/docs/interfaces/base/helpers) describes.
|
|
372
375
|
|
|
373
|
-
###
|
|
376
|
+
### JSON values
|
|
374
377
|
|
|
375
378
|
```typescript
|
|
376
379
|
type JsonPrimitive = string | number | boolean | null;
|
|
@@ -379,9 +382,9 @@ type JsonValue = JsonPrimitive | JsonValue[] | { [key: string]: JsonValue };
|
|
|
379
382
|
|
|
380
383
|
`JsonValue` describes anything that survives a JSON round trip.
|
|
381
384
|
|
|
382
|
-
### Error
|
|
385
|
+
### Error helpers
|
|
383
386
|
|
|
384
|
-
```typescript
|
|
387
|
+
```typescript fragment
|
|
385
388
|
import {
|
|
386
389
|
getErrorMessage,
|
|
387
390
|
isErrorWithMessage,
|
|
@@ -399,6 +402,6 @@ try {
|
|
|
399
402
|
|
|
400
403
|
`ErrorWithMessage` is `{ message: string }`. `isErrorWithMessage` is a type guard that accepts any object carrying a `message` property. `getErrorMessage` returns that message, or the string `"Unknown error"` for anything else, including an object whose `message` is not a string.
|
|
401
404
|
|
|
402
|
-
## Next
|
|
405
|
+
## Next steps
|
|
403
406
|
|
|
404
|
-
- [Forms](/docs/interfaces/base/forms)
|
|
407
|
+
- [Forms](/docs/interfaces/base/forms): Build a form, its fields, and its validation schema.
|
package/docs/3.base/3.forms.md
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Forms"
|
|
3
3
|
description: "The Form builder, its fields and field groups, sections, validation and the save modes."
|
|
4
|
+
mode: reference
|
|
4
5
|
---
|
|
5
6
|
|
|
6
|
-
# Forms
|
|
7
|
-
|
|
8
7
|
## Overview
|
|
9
8
|
|
|
10
|
-
`Form` builds a form component from a list of fields. Each field names a [data type](/docs/interfaces/base/data-types), and the data type supplies both the input component the frontend renders and the Zod schema the value is validated against. The builder does three things when you call it: it serializes every field, it compiles the field list into a single Zod object schema, and it attaches the JSON Schema form of that schema to the component options
|
|
9
|
+
`Form` builds a form component from a list of fields. Each field names a [data type](/docs/interfaces/base/data-types), and the data type supplies both the input component the frontend renders and the Zod schema the value is validated against. The builder does three things when you call it: it serializes every field, it compiles the field list into a single Zod object schema, and it attaches the JSON Schema form of that schema to the component options. The description shipped to the frontend already carries its own validation contract.
|
|
11
10
|
|
|
12
11
|
Everything on this page is exported from the `form` module, reachable either from the package root or from its own subpath:
|
|
13
12
|
|
|
@@ -23,7 +22,7 @@ import { Form, FormComponents } from "@antelopejs/interface-dms/base/form";
|
|
|
23
22
|
|
|
24
23
|
`Form` returns a `FormBuilder`, which is a `ComponentBuilder<FormPropsSerialized>` with one extra readonly property, `fields`, carrying the original unserialized `FormFieldOrGroup[]`. That property is what lets `formSchema` derive a schema straight from a builder.
|
|
25
24
|
|
|
26
|
-
## Declare a
|
|
25
|
+
## Declare a form
|
|
27
26
|
|
|
28
27
|
Assign the builder to a static field of a page class. The form fetches its initial values from `fetchUrl` and posts back to `submitUrl`.
|
|
29
28
|
|
|
@@ -67,7 +66,7 @@ export class NotesPage extends PageController(
|
|
|
67
66
|
}
|
|
68
67
|
```
|
|
69
68
|
|
|
70
|
-
### FormProps
|
|
69
|
+
### `FormProps`
|
|
71
70
|
|
|
72
71
|
`FormProps` extends [`BaseComponentProps`](/docs/interfaces/base/types).
|
|
73
72
|
|
|
@@ -98,7 +97,7 @@ A form on a page that sets neither `saveMode` nor `kind` saves with the save bar
|
|
|
98
97
|
|
|
99
98
|
An `"action"` form resets after a successful submit, a `"record"` keeps its values; `labelKey` names the field labelling the record.
|
|
100
99
|
|
|
101
|
-
`FormPropsSerialized` is `FormProps` with `fields` replaced by `FormFieldOrGroupSerialized[]`
|
|
100
|
+
`FormPropsSerialized` is `FormProps` with `fields` replaced by `FormFieldOrGroupSerialized[]` (every entry of the form, its own then each section's, `formEntries`), `sections` by `FormSectionSerialized[]` (each section with the `fieldIds` of the entries it holds), and one added property, `schema`, holding the JSON Schema produced from the compiled Zod object. A required boolean field whose input is a checkbox validates as `z.literal(true)` (`isAcceptanceField`).
|
|
102
101
|
|
|
103
102
|
## Fields
|
|
104
103
|
|
|
@@ -123,9 +122,9 @@ A `FormField` describes one input.
|
|
|
123
122
|
|
|
124
123
|
A module's own form applies `visibleWhen` and `disabledWhen` in the browser; its submit handler enforces what it needs. A table view's columns carry them to its forms and its data routes enforce them as well (see [Tables](/docs/interfaces/base/tables)).
|
|
125
124
|
|
|
126
|
-
`FormFieldSerialized` replaces `type` with the data type's registered id string and `inputComponent` with a mandatory `component: ComponentInfoSerialized
|
|
125
|
+
`FormFieldSerialized` replaces `type` with the data type's registered id string and `inputComponent` with a mandatory `component: ComponentInfoSerialized`: the resolved input component, taken from `inputComponent` when set and from `type.inputComponent()` otherwise.
|
|
127
126
|
|
|
128
|
-
## Group
|
|
127
|
+
## Group fields
|
|
129
128
|
|
|
130
129
|
A `FieldGroup` nests fields under a common heading. It is an alternative member of the `fields` array, not a wrapper around it.
|
|
131
130
|
|
|
@@ -158,7 +157,7 @@ const contact: FieldGroup = {
|
|
|
158
157
|
|
|
159
158
|
`FormFieldOrGroup` is `FormField | FieldGroup`, and `FormFieldOrGroupSerialized` is `FormFieldSerialized | FieldGroupSerialized`. Two guards discriminate them, both by checking for an array-valued `fields` property:
|
|
160
159
|
|
|
161
|
-
```typescript
|
|
160
|
+
```typescript fragment
|
|
162
161
|
import {
|
|
163
162
|
isFieldGroup,
|
|
164
163
|
isFieldGroupSerialized,
|
|
@@ -171,11 +170,11 @@ for (const item of formProps.fields) {
|
|
|
171
170
|
}
|
|
172
171
|
```
|
|
173
172
|
|
|
174
|
-
## Validate a
|
|
173
|
+
## Validate a submission
|
|
175
174
|
|
|
176
175
|
The schema the form ships to the frontend is also available on the backend, so the route that receives the submission validates against exactly the same rules.
|
|
177
176
|
|
|
178
|
-
```typescript
|
|
177
|
+
```typescript fragment
|
|
179
178
|
import { Controller, HTTPResult, JSONBody, Post } from "@antelopejs/interface-api";
|
|
180
179
|
import { formSchema } from "@antelopejs/interface-dms/base";
|
|
181
180
|
|
|
@@ -193,9 +192,9 @@ export class NotesController extends Controller("/playground/notes") {
|
|
|
193
192
|
}
|
|
194
193
|
```
|
|
195
194
|
|
|
196
|
-
### formSchema
|
|
195
|
+
### `formSchema`
|
|
197
196
|
|
|
198
|
-
```typescript
|
|
197
|
+
```typescript fragment
|
|
199
198
|
function formSchema(
|
|
200
199
|
source: FormSchemaSource,
|
|
201
200
|
): z.ZodObject<Record<string, z.ZodTypeAny>>;
|
|
@@ -203,9 +202,9 @@ function formSchema(
|
|
|
203
202
|
|
|
204
203
|
`FormSchemaSource` is `FormFieldOrGroup[] | FormProps | FormBuilder`. `formSchema` accepts any of the three: an array is compiled directly, a `FormBuilder` and a `FormProps` object are compiled from their `fields`. Anything else throws `Error("formSchema: unsupported source")`.
|
|
205
204
|
|
|
206
|
-
### buildFormSchema
|
|
205
|
+
### `buildFormSchema`
|
|
207
206
|
|
|
208
|
-
```typescript
|
|
207
|
+
```typescript fragment
|
|
209
208
|
function buildFormSchema(
|
|
210
209
|
fields: FormFieldOrGroup[],
|
|
211
210
|
): z.ZodObject<Record<string, z.ZodTypeAny>>;
|
|
@@ -213,9 +212,9 @@ function buildFormSchema(
|
|
|
213
212
|
|
|
214
213
|
`buildFormSchema` is the field-list form of the same operation and is what `Form` itself calls. It walks the list, flattens groups so a grouped field lands in the object under its own `id`, and adapts every field's base schema. The resulting object is flat: grouping affects layout, not the payload shape.
|
|
215
214
|
|
|
216
|
-
### adaptFieldValidationSchema
|
|
215
|
+
### `adaptFieldValidationSchema`
|
|
217
216
|
|
|
218
|
-
```typescript
|
|
217
|
+
```typescript fragment
|
|
219
218
|
function adaptFieldValidationSchema(
|
|
220
219
|
baseSchema: z.ZodTypeAny,
|
|
221
220
|
options: FieldValidationOptions, // { localized?, required?, fallbackLocale? }
|
|
@@ -228,11 +227,11 @@ function adaptFieldValidationSchema(
|
|
|
228
227
|
- When `required` is falsy the schema becomes `.nullable().optional()`.
|
|
229
228
|
- When `required` is true and the schema is a `ZodString` or a `ZodArray`, a `.min(1)` is added so an empty string or empty array does not satisfy the requirement.
|
|
230
229
|
|
|
231
|
-
It is exported because the same adaptation is applied outside forms
|
|
230
|
+
It is exported because the same adaptation is applied outside forms. The `Column` decorator uses it to register a field validator on a data controller.
|
|
232
231
|
|
|
233
|
-
### serializeFormFields
|
|
232
|
+
### `serializeFormFields`
|
|
234
233
|
|
|
235
|
-
```typescript
|
|
234
|
+
```typescript fragment
|
|
236
235
|
function serializeFormFields(
|
|
237
236
|
fields: FormFieldOrGroup[],
|
|
238
237
|
): FormFieldOrGroupSerialized[];
|
|
@@ -240,7 +239,7 @@ function serializeFormFields(
|
|
|
240
239
|
|
|
241
240
|
`serializeFormFields` produces the serialized field list: for each field it resolves the input component (`inputComponent` if given, otherwise `type.inputComponent()`) and replaces the data type instance with its registered id, falling back to the string `"unknown"` when the type is not registered. Groups keep their metadata and have their `fields` serialized in place.
|
|
242
241
|
|
|
243
|
-
## Input
|
|
242
|
+
## Input components
|
|
244
243
|
|
|
245
244
|
`FormComponents` is a namespace of factory functions, each returning a `ComponentInfoSerialized` for one input widget. Data types call them to produce their default input; you call them directly when a field needs a widget other than its type's default, via `FormField.inputComponent` or `ColumnOptions.inputComponent`.
|
|
246
245
|
|
|
@@ -258,7 +257,7 @@ const priority = FormComponents.InputSelect({
|
|
|
258
257
|
|
|
259
258
|
| Function | Options interface |
|
|
260
259
|
| --- | --- |
|
|
261
|
-
| `InputCheckbox()` |
|
|
260
|
+
| `InputCheckbox()` | None |
|
|
262
261
|
| `InputText(options?)` | `InputTextOptions` |
|
|
263
262
|
| `InputTextarea(options?)` | `TextareaOptions` |
|
|
264
263
|
| `InputEmail(options?)` | `InputEmailOptions` |
|
|
@@ -269,7 +268,7 @@ const priority = FormComponents.InputSelect({
|
|
|
269
268
|
| `InputTime(options?)` | `InputTimeOptions` |
|
|
270
269
|
| `InputPhone(options?)` | `InputPhoneOptions` |
|
|
271
270
|
| `InputSlider(options?)` | `SliderOptions` |
|
|
272
|
-
| `InputSwitch()` |
|
|
271
|
+
| `InputSwitch()` | None |
|
|
273
272
|
| `InputRadioGroup(options)` | `RadioGroupOptions` |
|
|
274
273
|
| `InputSelect(options)` | `SelectOptions` |
|
|
275
274
|
| `InputRelation(options)` | `RelationOptions` |
|
|
@@ -309,9 +308,9 @@ The option interfaces live in the same namespace. The ones with more than a plac
|
|
|
309
308
|
| `ImageResizeOptions` | `maxWidth: number`, `maxHeight: number`, `fit?: "cover" \| "contain"` |
|
|
310
309
|
| `InputEmailOptions`, `InputColorOptions`, `RichTextOptions` | `placeholder?` |
|
|
311
310
|
|
|
312
|
-
`ImageResizeOptions` downscales client-side before upload. `contain` (the default) scales down while preserving the source ratio; `cover` crops the image centred to the `maxWidth`/`maxHeight` ratio first, then scales down. An image already within bounds is uploaded untouched. This is a UX-level bound, not a server-enforced guarantee
|
|
311
|
+
`ImageResizeOptions` downscales client-side before upload. `contain` (the default) scales down while preserving the source ratio; `cover` crops the image centred to the `maxWidth`/`maxHeight` ratio first, then scales down. An image already within bounds is uploaded untouched. This is a UX-level bound, not a server-enforced guarantee. Keep `constraints.maxSize` for that.
|
|
313
312
|
|
|
314
|
-
## Events and
|
|
313
|
+
## Events and functions
|
|
315
314
|
|
|
316
315
|
`FormEvents` names the events a form emits, `FormFunctions` the functions it exposes to watch actions.
|
|
317
316
|
|
|
@@ -355,7 +354,7 @@ const form = Form({
|
|
|
355
354
|
|
|
356
355
|
`SET_FIELD_VALUE` writes a field from another one's change: `value` writes a fixed value, `valueMap` the entry named by the value of the field the event names, and a value the map has no entry for writes nothing. It skips the `initial` events, so a record opens with what it stored and a field nobody edited is never submitted as changed; a field it writes on a change counts as changed, like one typed in. A field it writes fires `FIELD_CHANGE` in turn, so two watches writing each other loop.
|
|
357
356
|
|
|
358
|
-
```typescript
|
|
357
|
+
```typescript fragment
|
|
359
358
|
form.watch(FormEvents.FIELD_CHANGE, FormFunctions.SET_FIELD_VALUE, {
|
|
360
359
|
params: { targetField: "probability", valueMap: { won: 100, lost: 0 } },
|
|
361
360
|
onParam: { key: "fieldId", value: "stage" },
|
|
@@ -364,6 +363,6 @@ form.watch(FormEvents.FIELD_CHANGE, FormFunctions.SET_FIELD_VALUE, {
|
|
|
364
363
|
|
|
365
364
|
`.watch()` records the watch on the component itself; `.watchOn(childId, …)` records it on a child declared with `.child()`. Both come from `ComponentBuilder` in `@antelopejs/interface-dms/component`.
|
|
366
365
|
|
|
367
|
-
## Next
|
|
366
|
+
## Next steps
|
|
368
367
|
|
|
369
|
-
- [Tables](/docs/interfaces/base/tables)
|
|
368
|
+
- [Tables](/docs/interfaces/base/tables): Turn a data controller into a table view with columns, row actions and guards.
|