@antelopejs/dms 0.9.7 → 0.10.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 +114 -0
- package/README.md +1 -1
- package/data/module-store/catalog.json +2 -2
- package/dist/attachments/table-view.js +2 -2
- package/dist/attachments/table-view.js.map +1 -1
- package/dist/automation/actions.d.ts +2 -0
- package/dist/automation/actions.js +116 -71
- package/dist/automation/actions.js.map +1 -1
- package/dist/automation/i18n.d.ts +4 -0
- package/dist/automation/i18n.js +19 -0
- package/dist/automation/i18n.js.map +1 -0
- package/dist/automation/send-notification.d.ts +21 -0
- package/dist/automation/send-notification.js +163 -0
- package/dist/automation/send-notification.js.map +1 -0
- package/dist/automation/triggers.js +19 -25
- package/dist/automation/triggers.js.map +1 -1
- package/dist/client-origin-cors.d.ts +1 -0
- package/dist/client-origin-cors.js +19 -0
- package/dist/client-origin-cors.js.map +1 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +5 -3
- package/dist/config.js.map +1 -1
- package/dist/frontend-module.d.ts +4 -0
- package/dist/frontend-module.js +37 -0
- package/dist/frontend-module.js.map +1 -0
- package/dist/implementations/dms/client-ip.js +1 -1
- package/dist/implementations/dms/client-ip.js.map +1 -1
- package/dist/implementations/dms/dev-reload.d.ts +5 -0
- package/dist/implementations/dms/dev-reload.js +23 -3
- package/dist/implementations/dms/dev-reload.js.map +1 -1
- package/dist/implementations/dms/frontend-modules.d.ts +2 -4
- package/dist/implementations/dms/frontend-modules.js +22 -89
- package/dist/implementations/dms/frontend-modules.js.map +1 -1
- package/dist/implementations/dms/frontend-ready.d.ts +12 -2
- package/dist/implementations/dms/frontend-ready.js +34 -4
- package/dist/implementations/dms/frontend-ready.js.map +1 -1
- package/dist/implementations/dms/layout-banners.d.ts +2 -1
- package/dist/implementations/dms/layout-banners.js +9 -0
- package/dist/implementations/dms/layout-banners.js.map +1 -1
- package/dist/implementations/dms/manifest.d.ts +1 -2
- package/dist/implementations/dms/manifest.js +2 -0
- package/dist/implementations/dms/manifest.js.map +1 -1
- package/dist/implementations/dms/page.d.ts +4 -3
- package/dist/implementations/dms/page.js +49 -18
- package/dist/implementations/dms/page.js.map +1 -1
- package/dist/implementations/dms/permissions-resolver.d.ts +2 -2
- package/dist/implementations/dms/permissions-resolver.js +2 -2
- package/dist/implementations/dms/permissions-resolver.js.map +1 -1
- package/dist/implementations/dms/realtime.js +1 -0
- package/dist/implementations/dms/realtime.js.map +1 -1
- package/dist/implementations/dms-base/bulk-selection.d.ts +1 -1
- package/dist/implementations/dms-base/bulk-selection.js +23 -1
- package/dist/implementations/dms-base/bulk-selection.js.map +1 -1
- package/dist/implementations/dms-base/filter-tokens.js +5 -3
- package/dist/implementations/dms-base/filter-tokens.js.map +1 -1
- package/dist/implementations/dms-base/search-route.d.ts +1 -0
- package/dist/implementations/dms-base/search-route.js +35 -13
- package/dist/implementations/dms-base/search-route.js.map +1 -1
- package/dist/implementations/dms-base/table-view.js +11 -6
- package/dist/implementations/dms-base/table-view.js.map +1 -1
- package/dist/implementations/dms-notifications/index.d.ts +2 -2
- package/dist/implementations/dms-notifications/index.js +5 -5
- package/dist/implementations/dms-notifications/index.js.map +1 -1
- package/dist/index.js +28 -45
- package/dist/index.js.map +1 -1
- package/dist/pages/modules/index.d.ts +1 -0
- package/dist/pages/modules/index.js +1 -0
- package/dist/pages/modules/index.js.map +1 -1
- package/dist/pages/modules/modules-store.d.ts +9 -0
- package/dist/pages/modules/modules-store.js +30 -0
- package/dist/pages/modules/modules-store.js.map +1 -0
- package/dist/pages/settings/users/account-credentials.js +2 -2
- package/dist/pages/settings/users/account-credentials.js.map +1 -1
- package/dist/pages/settings/users/account-data-store.js +25 -8
- package/dist/pages/settings/users/account-data-store.js.map +1 -1
- package/dist/pages/settings/users/invite-extension-routes.js +9 -3
- package/dist/pages/settings/users/invite-extension-routes.js.map +1 -1
- package/dist/pages/settings/users/member-management.js +13 -14
- package/dist/pages/settings/users/member-management.js.map +1 -1
- package/dist/pages/settings/users/profile.d.ts +1 -4
- package/dist/pages/settings/users/profile.js +0 -35
- package/dist/pages/settings/users/profile.js.map +1 -1
- package/dist/pages/settings/users/role-editor-store.js +26 -18
- package/dist/pages/settings/users/role-editor-store.js.map +1 -1
- package/dist/pages/settings/users/role-editor.d.ts +0 -1
- package/dist/pages/settings/users/role-editor.js +1 -2
- package/dist/pages/settings/users/role-editor.js.map +1 -1
- package/dist/pages/settings/users/role-preview.js +1 -2
- package/dist/pages/settings/users/role-preview.js.map +1 -1
- package/dist/pages/settings/users/roles.js +2 -2
- package/dist/pages/settings/users/roles.js.map +1 -1
- package/dist/pages/settings/users/two-factor-operations.js.map +1 -1
- package/dist/realtime/broker.d.ts +6 -0
- package/dist/realtime/broker.js +8 -1
- package/dist/realtime/broker.js.map +1 -1
- package/dist/realtime/presence.d.ts +8 -3
- package/dist/realtime/presence.js +8 -4
- package/dist/realtime/presence.js.map +1 -1
- package/dist/realtime/sessions.d.ts +11 -1
- package/dist/realtime/sessions.js +21 -3
- package/dist/realtime/sessions.js.map +1 -1
- package/dist/realtime/table-view.d.ts +2 -2
- package/dist/realtime/table-view.js +4 -3
- package/dist/realtime/table-view.js.map +1 -1
- package/dist/routes/auth/client-ip.d.ts +1 -0
- package/dist/routes/auth/client-ip.js +18 -0
- package/dist/routes/auth/client-ip.js.map +1 -1
- package/dist/routes/auth/oauth/config.d.ts +2 -1
- package/dist/routes/auth/oauth/config.js +5 -1
- package/dist/routes/auth/oauth/config.js.map +1 -1
- package/dist/routes/auth/session-handoff.d.ts +0 -1
- package/dist/routes/auth/session-handoff.js +0 -2
- package/dist/routes/auth/session-handoff.js.map +1 -1
- package/dist/routes/auth.js +3 -2
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/realtime.d.ts +13 -1
- package/dist/routes/realtime.js +114 -29
- package/dist/routes/realtime.js.map +1 -1
- package/dist/startup-config.d.ts +2 -0
- package/dist/startup-config.js +62 -0
- package/dist/startup-config.js.map +1 -0
- package/dist/store/interfaces.d.ts +3 -2
- package/dist/store/interfaces.js +16 -6
- package/dist/store/interfaces.js.map +1 -1
- package/dist/store/listing.js +24 -13
- package/dist/store/listing.js.map +1 -1
- package/dist/store/operations.js +4 -0
- package/dist/store/operations.js.map +1 -1
- package/dist/store/requirements.js +3 -2
- package/dist/store/requirements.js.map +1 -1
- package/dist/store/runtime.d.ts +10 -1
- package/dist/store/runtime.js +33 -6
- package/dist/store/runtime.js.map +1 -1
- package/dist/store/types.d.ts +10 -0
- package/dist/validation/auth.schema.d.ts +2 -2
- package/dist/validation/member-invite.schema.d.ts +2 -10
- package/dist/validation/member-invite.schema.js +2 -15
- package/dist/validation/member-invite.schema.js.map +1 -1
- package/frontend-vue/dms.frontend.ts +24 -5
- package/frontend-vue/dms.server.ts +17 -0
- package/frontend-vue/layers/dms-auth/server/accounts.ts +3 -4
- package/frontend-vue/layers/dms-core/app/build/composables/renderNow.ts +34 -0
- package/frontend-vue/layers/dms-core/app/build/composables/user/tablePreferences.ts +5 -1
- package/frontend-vue/layers/dms-core/app/build/utils/crossTab.ts +199 -0
- package/frontend-vue/layers/dms-core/app/build/utils/sharedRealtimeConnection.ts +410 -0
- package/frontend-vue/layers/dms-core/app/composables/auth/useApiData.ts +77 -12
- package/frontend-vue/layers/dms-core/app/composables/data-types/useDataType.ts +26 -0
- package/frontend-vue/layers/dms-core/app/composables/hydration/isHydratingComponent.ts +29 -0
- package/frontend-vue/layers/dms-core/app/composables/live/livePauseSource.ts +29 -0
- package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +150 -9
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +6 -4
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +27 -1
- package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +256 -119
- package/frontend-vue/layers/dms-core/app/composables/translation/useComposedText.ts +21 -8
- package/frontend-vue/layers/dms-core/app/composables/translation/useTranslation.ts +22 -5
- package/frontend-vue/layers/dms-core/app/plugins/dev-error-log.ts +16 -0
- package/frontend-vue/layers/dms-core/app/types/user.ts +6 -0
- package/frontend-vue/layers/dms-core/app/utils/composedText.ts +7 -2
- package/frontend-vue/layers/dms-core/app/utils/formatter.ts +166 -35
- package/frontend-vue/layers/dms-core/app/utils/url-interpolation.ts +18 -1
- package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +134 -2
- package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +134 -2
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardBanners.vue +3 -2
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardHeader.vue +41 -21
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardPageBody.vue +9 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +4 -19
- package/frontend-vue/layers/dms-layout/app/build/components/layout/Footer.vue +11 -3
- package/frontend-vue/layers/dms-layout/app/build/components/layout/GithubStarPrompt.vue +18 -7
- package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionBar.vue +8 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionsOutlet.vue +14 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/RowSidePanel.vue +43 -82
- package/frontend-vue/layers/dms-layout/app/build/components/layout/SidebarUserMenu.vue +44 -3
- package/frontend-vue/layers/dms-layout/app/build/components/layout/UserIdentity.vue +11 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleCatalogTile.vue +132 -35
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleManageMenu.vue +59 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleSourceLabel.vue +25 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreBanners.vue +73 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreOperation.vue +116 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreRequirements.vue +8 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreTile.vue +85 -207
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTile.vue +10 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTileSkeleton.vue +68 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +24 -8
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxItem.vue +8 -5
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/region/RegionTimeZoneInput.vue +1 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-permissions.ts +19 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/useRoleEditor.ts +2 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/layout/useSidePanelWidth.ts +31 -70
- package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationGroupsOpen.ts +11 -1
- package/frontend-vue/layers/dms-layout/app/build/utils/dev-reload-stream.ts +81 -0
- package/frontend-vue/layers/dms-layout/app/build/utils/module-store-catalog.ts +232 -36
- package/frontend-vue/layers/dms-layout/app/build/utils/modules-catalog.ts +122 -14
- package/frontend-vue/layers/dms-layout/app/composables/layout/useLayoutBanners.ts +32 -3
- package/frontend-vue/layers/dms-layout/app/composables/layout/usePageRecord.ts +50 -0
- package/frontend-vue/layers/dms-layout/app/composables/layout/useServedHeaderActions.ts +25 -7
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleHistory.ts +3 -2
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +51 -4
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStoreActions.ts +194 -0
- package/frontend-vue/layers/dms-layout/app/composables/page/usePageComponentPreload.ts +23 -3
- package/frontend-vue/layers/dms-layout/app/composables/page/usePageLayout.ts +4 -2
- package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +6 -1
- package/frontend-vue/layers/dms-layout/app/custom-layouts/DefaultLayout.vue +9 -0
- package/frontend-vue/layers/dms-layout/app/custom-layouts/SettingsLayout.vue +9 -0
- package/frontend-vue/layers/dms-layout/app/custom-pages/modules-store.vue +291 -0
- package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +87 -474
- package/frontend-vue/layers/dms-layout/app/middleware/module-routing.global.ts +3 -0
- package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +6 -22
- package/frontend-vue/layers/dms-layout/app/plugins/command-palette-navigation.client.ts +2 -1
- package/frontend-vue/layers/dms-layout/app/plugins/command-palette-personal-pages.client.ts +14 -44
- package/frontend-vue/layers/dms-layout/app/plugins/dms-dev-reload.client.ts +14 -9
- package/frontend-vue/layers/dms-layout/app/theme/form-controls.ts +9 -4
- package/frontend-vue/layers/dms-layout/app/theme/surfaces.ts +22 -6
- package/frontend-vue/layers/dms-layout/app/types/module-store.ts +9 -0
- package/frontend-vue/layers/dms-layout/app/types/page.ts +15 -1
- package/frontend-vue/layers/dms-layout/app/utils/account-menu.ts +11 -0
- package/frontend-vue/layers/dms-layout/app/utils/breadcrumb.ts +57 -0
- package/frontend-vue/layers/dms-layout/app/utils/pageQuery.ts +26 -0
- package/frontend-vue/layers/dms-layout/app/utils/pageRecord.ts +71 -0
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +39 -14
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +38 -13
- package/frontend-vue/layers/dms-ui/app/build/components/containers/TargetComponentTree.vue +31 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/constants.ts +25 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/drawer/DynamicDrawer.vue +244 -15
- package/frontend-vue/layers/dms-ui/app/build/components/containers/modal/DynamicModal.vue +42 -25
- package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowHeader.vue +153 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowNotFound.vue +44 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/row/drawerRowHeader.ts +35 -0
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormEntries.vue +44 -9
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormErrorSummary.vue +11 -4
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldControl.vue +38 -5
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldNotes.vue +29 -5
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormSectionNav.vue +3 -1
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormSections.vue +106 -27
- package/frontend-vue/layers/dms-ui/app/build/components/table/Actions.vue +12 -4
- package/frontend-vue/layers/dms-ui/app/build/components/table/Empty.vue +4 -2
- package/frontend-vue/layers/dms-ui/app/build/components/table/FiltersRow.vue +38 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table/Pagination.vue +55 -54
- package/frontend-vue/layers/dms-ui/app/build/components/table/RowSelection.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table/Table.vue +396 -288
- package/frontend-vue/layers/dms-ui/app/build/components/table/Tabs.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table/Views.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardAvatar.vue +59 -0
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardGridSkeleton.vue +19 -2
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardRowActions.vue +38 -0
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardsDisplay.vue +148 -104
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/LiveHoldBanner.vue +17 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/bulkSelection.ts +43 -9
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowComponentOptions.ts +76 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowNavigation.ts +172 -27
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/useActionTargets.ts +270 -46
- package/frontend-vue/layers/dms-ui/app/build/composables/confirm/confirmDialogTexts.ts +2 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/containers/closingLayers.ts +186 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/containers/context.ts +14 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/containers/drawerSize.ts +229 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/containers/useResizableSize.ts +175 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/metricCells.ts +18 -35
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/numberSuffix.ts +47 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +99 -24
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerFieldTypes.ts +2 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationFilter.ts +75 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationRows.ts +71 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationValue.ts +39 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/twoLineCell.ts +3 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/useColumnValueRenderer.ts +6 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/form/createFormFields.ts +15 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldSaved.ts +40 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldValueRule.ts +62 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formEntryContext.ts +5 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formFooter.ts +42 -7
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formLayout.ts +106 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formSections.ts +32 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/readonlyValue.ts +19 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/registerFormFunctions.ts +27 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/repeaterTable.ts +65 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/serverFieldErrors.ts +49 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/toasterClearance.ts +118 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/useInstantForm.ts +12 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/live/currentPageLive.ts +41 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/live/livePoller.ts +10 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/live/pageReadClock.ts +28 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/live/shortcuts.ts +1 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageLiveHost.ts +17 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/live/useUpdatedLabel.ts +38 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/pageToolbarContext.ts +75 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/toolbarControlHeight.ts +29 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodComparison.ts +52 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +21 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodShortLabels.ts +28 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/usePeriodDraft.ts +1 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useRelationFilterLabels.ts +73 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useRowScrollAnchor.ts +106 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useTable.ts +9 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useTableColumns.ts +1 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/archivedCount.ts +31 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/filterEditorValue.ts +38 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/formatFilterValue.ts +33 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/scrollEdges.ts +31 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useCardFields.ts +33 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useMobileDisplay.ts +48 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useScrolledPastTop.ts +41 -18
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableFooter.ts +3 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLinkedVisit.ts +78 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLiveHold.ts +135 -22
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +56 -25
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +77 -32
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +86 -10
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/bulkActions.ts +14 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/card.ts +127 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/compareValues.ts +13 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/expandedRows.ts +28 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/groupedRows.ts +114 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/linkedArrival.ts +308 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/locateRow.ts +106 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pageArrival.ts +34 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pinnedRows.ts +17 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/quickFilters.ts +41 -33
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/recordLink.ts +24 -8
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableCounts.ts +113 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableQuery.ts +27 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/views.ts +22 -6
- package/frontend-vue/layers/dms-ui/app/build/utils/addressSuggestions.ts +49 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/eyebrow.ts +12 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/fieldOptionsI18n.ts +51 -11
- package/frontend-vue/layers/dms-ui/app/build/utils/inputWidth.ts +53 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/numberAdjustment.ts +6 -3
- package/frontend-vue/layers/dms-ui/app/build/utils/relativeDate.ts +19 -5
- package/frontend-vue/layers/dms-ui/app/build/utils/scrollWithin.ts +99 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/urlVariables.ts +36 -14
- package/frontend-vue/layers/dms-ui/app/components/PasswordStrength.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/RecursiveComponent.vue +9 -1
- package/frontend-vue/layers/dms-ui/app/components/blocks/LiveToggleBlock.vue +80 -32
- package/frontend-vue/layers/dms-ui/app/components/blocks/PageToolbarBlock.vue +137 -0
- package/frontend-vue/layers/dms-ui/app/components/blocks/StatGroupBlock.vue +5 -0
- package/frontend-vue/layers/dms-ui/app/components/chart/ApexChartHost.vue +16 -2
- package/frontend-vue/layers/dms-ui/app/components/chart/Chart.vue +24 -5
- package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +41 -23
- package/frontend-vue/layers/dms-ui/app/components/confirm/ConfirmModal.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/container-footer/ContainerFooter.vue +34 -0
- package/frontend-vue/layers/dms-ui/app/components/field-row/FieldRow.vue +25 -2
- package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +2 -2
- package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +118 -36
- package/frontend-vue/layers/dms-ui/app/components/form/components/DatePicker.vue +20 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/Display.vue +13 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +8 -3
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +53 -5
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputCode.vue +29 -5
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputNumber.vue +45 -8
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputPercentage.vue +28 -5
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputSecret.vue +5 -0
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputText.vue +19 -2
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputTime.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +205 -65
- package/frontend-vue/layers/dms-ui/app/components/form/components/Repeater.vue +80 -30
- package/frontend-vue/layers/dms-ui/app/components/form/components/RepeaterTable.vue +203 -0
- package/frontend-vue/layers/dms-ui/app/components/form/components/Select.vue +4 -2
- package/frontend-vue/layers/dms-ui/app/components/grid/GridRow.vue +11 -2
- package/frontend-vue/layers/dms-ui/app/components/key-value-list/KeyValueList.vue +79 -70
- package/frontend-vue/layers/dms-ui/app/components/kpi/KpiCard.vue +117 -21
- package/frontend-vue/layers/dms-ui/app/components/meter/Meter.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +113 -34
- package/frontend-vue/layers/dms-ui/app/components/save-bar/SaveBar.vue +14 -2
- package/frontend-vue/layers/dms-ui/app/components/section-header/Eyebrow.vue +6 -1
- package/frontend-vue/layers/dms-ui/app/components/segmented/Segmented.vue +13 -0
- package/frontend-vue/layers/dms-ui/app/components/stat-group/StatGroup.vue +52 -3
- package/frontend-vue/layers/dms-ui/app/components/tab/Tab.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +45 -35
- package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanDisplay.vue +4 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +410 -96
- package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +33 -5
- package/frontend-vue/layers/dms-ui/app/composables/chart/apexCategoryLabels.ts +68 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/apexDatetimeBuckets.ts +306 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/formatValue.ts +4 -3
- package/frontend-vue/layers/dms-ui/app/composables/chart/heatmapColors.ts +93 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/localizeSeries.ts +62 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.ts +116 -8
- package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.types.ts +7 -0
- package/frontend-vue/layers/dms-ui/app/composables/containers/types.ts +84 -1
- package/frontend-vue/layers/dms-ui/app/composables/containers/useDrawer.ts +69 -8
- package/frontend-vue/layers/dms-ui/app/composables/form/types/field-saved.ts +11 -0
- package/frontend-vue/layers/dms-ui/app/composables/form/types/field.ts +4 -0
- package/frontend-vue/layers/dms-ui/app/composables/form/types/functions.ts +1 -0
- package/frontend-vue/layers/dms-ui/app/composables/form/useForm.ts +142 -21
- package/frontend-vue/layers/dms-ui/app/composables/live/usePageLive.ts +11 -4
- package/frontend-vue/layers/dms-ui/app/composables/page-toolbar/usePageToolbarSize.ts +30 -0
- package/frontend-vue/layers/dms-ui/app/composables/table-view/kanban.ts +4 -2
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/action-target.ts +22 -3
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/config.ts +4 -0
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/display.ts +24 -1
- package/frontend-vue/layers/dms-ui/app/plugins/closing-layers.client.ts +10 -0
- package/frontend-vue/layers/dms-ui/app/plugins/live-pause-source.ts +15 -0
- package/frontend-vue/layers/dms-ui/app/plugins/register.ts +2 -0
- package/frontend-vue/layers/dms-ui/app/types/input-number.ts +3 -2
- package/frontend-vue/layers/dms-ui/app/types/page-toolbar.ts +7 -0
- package/frontend-vue/layers/dms-ui/app/types/row-action.ts +0 -2
- package/frontend-vue/layers/dms-ui/app/types/row-navigation.ts +17 -0
- package/frontend-vue/layers/dms-ui/app/types/table/query.ts +2 -1
- package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +3 -1
- package/frontend-vue/layers/dms-ui/app/utils/componentTree.ts +24 -0
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-en-GB.json +16 -4
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-fr-FR.json +16 -4
- package/frontend-vue/package.json +2 -2
- package/package.json +7 -6
- package/skills/dms/SKILL.md +1 -1
- package/skills/dms-auth/SKILL.md +5 -3
- package/skills/dms-best-practices/REFERENCE.md +2 -0
- package/skills/dms-best-practices/SKILL.md +4 -0
- package/skills/dms-module/SKILL.md +3 -2
- package/skills/dms-pages/REFERENCE.md +125 -16
- package/skills/dms-pages/SKILL.md +1 -1
- package/skills/dms-project/DEV-SERVERS.md +23 -1
- package/skills/dms-project/REFERENCE.md +1 -1
- package/skills/dms-project/SKILL.md +4 -1
- package/skills/dms-project/scripts/dms-dev.sh +205 -17
- package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +0 -42
|
@@ -5,7 +5,12 @@ import type {
|
|
|
5
5
|
} from "../../../composables/form/types/field";
|
|
6
6
|
import { isFieldGroup } from "../../../composables/form/types/field";
|
|
7
7
|
import { FORM_ENTRY_CONTEXT_KEY } from "../../composables/form/formEntryContext";
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
GROUP_FIELDS_CLASSES,
|
|
10
|
+
entryFields,
|
|
11
|
+
entryRowLayout,
|
|
12
|
+
GROUP_MEMBER_CLASSES,
|
|
13
|
+
} from "../../composables/form/formLayout";
|
|
9
14
|
import { combineSaveStates } from "#dms-layout/app/composables/layout/useInstantSaveHeader";
|
|
10
15
|
import type { SaveStatusState } from "../../../components/save-bar/SaveStatus.vue";
|
|
11
16
|
import DmsFormFieldControl from "./FormFieldControl.vue";
|
|
@@ -19,9 +24,15 @@ interface FormEntriesProps {
|
|
|
19
24
|
|
|
20
25
|
const props = defineProps<FormEntriesProps>();
|
|
21
26
|
const context = inject(FORM_ENTRY_CONTEXT_KEY)!;
|
|
27
|
+
const { processI18n } = useTranslation();
|
|
22
28
|
|
|
23
29
|
const classes = computed(() => context.layoutClasses(props.inSection));
|
|
24
30
|
|
|
31
|
+
/** The row's layout: a wide field takes the full width under its label. */
|
|
32
|
+
function rowOf(entry: FormFieldOrGroup) {
|
|
33
|
+
return entryRowLayout(classes.value.row, entryFields(entry));
|
|
34
|
+
}
|
|
35
|
+
|
|
25
36
|
function groupFieldsClass(group: FieldGroup): string {
|
|
26
37
|
return GROUP_FIELDS_CLASSES[
|
|
27
38
|
group.orientation === "vertical" ? "vertical" : "horizontal"
|
|
@@ -32,6 +43,12 @@ function isGroupVisible(group: FieldGroup): boolean {
|
|
|
32
43
|
return group.fields.some((field) => !context.isFieldHidden(field));
|
|
33
44
|
}
|
|
34
45
|
|
|
46
|
+
// A description tells how to fill the field ("Left empty, the default rate
|
|
47
|
+
// applies"): a form where nothing can be changed shows only the values.
|
|
48
|
+
function descriptionOf(entry: FormFieldOrGroup): string | undefined {
|
|
49
|
+
return context.isReadOnly.value ? undefined : entry.description;
|
|
50
|
+
}
|
|
51
|
+
|
|
35
52
|
function isGroupRequired(group: FieldGroup): boolean {
|
|
36
53
|
return group.fields.some(context.isFieldRequired);
|
|
37
54
|
}
|
|
@@ -51,9 +68,9 @@ function saveStateOf(entry: FormFieldOrGroup): SaveStatusState {
|
|
|
51
68
|
<template v-if="isFieldGroup(entry)">
|
|
52
69
|
<DmsFieldRow
|
|
53
70
|
v-if="isGroupVisible(entry)"
|
|
54
|
-
v-bind="
|
|
71
|
+
v-bind="rowOf(entry)"
|
|
55
72
|
:label="entry.label || ''"
|
|
56
|
-
:description="entry
|
|
73
|
+
:description="descriptionOf(entry)"
|
|
57
74
|
:required="isGroupRequired(entry)"
|
|
58
75
|
>
|
|
59
76
|
<template v-if="saveStateOf(entry) !== 'idle'" #details>
|
|
@@ -63,14 +80,32 @@ function saveStateOf(entry: FormFieldOrGroup): SaveStatusState {
|
|
|
63
80
|
/>
|
|
64
81
|
</template>
|
|
65
82
|
<div :class="groupFieldsClass(entry)">
|
|
83
|
+
<!-- The group's label heads the cluster; each member keeps its
|
|
84
|
+
own above its control, so a group can title a run of
|
|
85
|
+
labelled fields. -->
|
|
66
86
|
<template v-for="field in entry.fields" :key="field.id">
|
|
67
|
-
<
|
|
87
|
+
<div
|
|
68
88
|
v-if="
|
|
69
89
|
field.component.componentName && !context.isFieldHidden(field)
|
|
70
90
|
"
|
|
71
|
-
:
|
|
72
|
-
|
|
73
|
-
|
|
91
|
+
:class="GROUP_MEMBER_CLASSES.root"
|
|
92
|
+
>
|
|
93
|
+
<label
|
|
94
|
+
v-if="field.label"
|
|
95
|
+
:for="field.id"
|
|
96
|
+
:class="GROUP_MEMBER_CLASSES.label"
|
|
97
|
+
>
|
|
98
|
+
{{ processI18n(field.label) }}
|
|
99
|
+
<span
|
|
100
|
+
v-if="context.isFieldRequired(field)"
|
|
101
|
+
class="text-error"
|
|
102
|
+
aria-hidden="true"
|
|
103
|
+
>
|
|
104
|
+
*
|
|
105
|
+
</span>
|
|
106
|
+
</label>
|
|
107
|
+
<DmsFormFieldControl :field class="min-w-0" />
|
|
108
|
+
</div>
|
|
74
109
|
</template>
|
|
75
110
|
</div>
|
|
76
111
|
</DmsFieldRow>
|
|
@@ -78,10 +113,10 @@ function saveStateOf(entry: FormFieldOrGroup): SaveStatusState {
|
|
|
78
113
|
|
|
79
114
|
<DmsFieldRow
|
|
80
115
|
v-else-if="!context.isFieldHidden(entry)"
|
|
81
|
-
v-bind="
|
|
116
|
+
v-bind="rowOf(entry)"
|
|
82
117
|
:label="entry.label || ''"
|
|
83
118
|
:label-for="entry.id"
|
|
84
|
-
:description="entry
|
|
119
|
+
:description="descriptionOf(entry)"
|
|
85
120
|
:required="context.isFieldRequired(entry)"
|
|
86
121
|
>
|
|
87
122
|
<template v-if="saveStateOf(entry) !== 'idle'" #details>
|
|
@@ -15,14 +15,18 @@ interface FormErrorSummaryProps {
|
|
|
15
15
|
const props = defineProps<FormErrorSummaryProps>();
|
|
16
16
|
const emit = defineEmits<{ select: [fieldId: string] }>();
|
|
17
17
|
const { t } = useI18n();
|
|
18
|
+
|
|
19
|
+
// Every field fixed: the summary says so in place, on as many lines, rather
|
|
20
|
+
// than leaving and moving the form up under the pointer.
|
|
21
|
+
const isResolved = computed(() => props.items.length === 0);
|
|
18
22
|
</script>
|
|
19
23
|
|
|
20
24
|
<template>
|
|
21
25
|
<UAlert
|
|
22
|
-
color="error"
|
|
26
|
+
:color="isResolved ? 'success' : 'error'"
|
|
23
27
|
variant="subtle"
|
|
24
|
-
icon="i-ph-warning-circle"
|
|
25
|
-
role="alert"
|
|
28
|
+
:icon="isResolved ? 'i-ph-check-circle' : 'i-ph-warning-circle'"
|
|
29
|
+
:role="isResolved ? 'status' : 'alert'"
|
|
26
30
|
:title="
|
|
27
31
|
t(
|
|
28
32
|
'dms.form.error_summary.title',
|
|
@@ -32,7 +36,10 @@ const { t } = useI18n();
|
|
|
32
36
|
"
|
|
33
37
|
>
|
|
34
38
|
<template #description>
|
|
35
|
-
<
|
|
39
|
+
<p v-if="isResolved" class="mt-1">
|
|
40
|
+
{{ t("dms.form.error_summary.resolved") }}
|
|
41
|
+
</p>
|
|
42
|
+
<ul v-else class="mt-1 flex flex-wrap gap-x-4 gap-y-1">
|
|
36
43
|
<li v-for="item in props.items" :key="item.id">
|
|
37
44
|
<a
|
|
38
45
|
:href="`#${item.id}`"
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
import { formErrorsInjectionKey } from "@nuxt/ui/composables/useFormField";
|
|
12
12
|
import DmsFormErrorText from "./FormErrorText.vue";
|
|
13
13
|
import DmsFormFieldNotes from "./FormFieldNotes.vue";
|
|
14
|
+
import { readonlyFieldValue } from "../../composables/form/readonlyValue";
|
|
14
15
|
|
|
15
16
|
interface FormFieldControlProps {
|
|
16
17
|
field: FormField;
|
|
@@ -22,13 +23,30 @@ const { state, initialValues, loading } = context;
|
|
|
22
23
|
const { processI18n } = useTranslation();
|
|
23
24
|
|
|
24
25
|
const isDisabled = computed(() => context.isFieldDisabled(props.field));
|
|
26
|
+
// A list of rows reads best as its rows, its own control disabled: the
|
|
27
|
+
// display of a cell only counts them. An empty one still reads "empty".
|
|
28
|
+
const READS_AS_CONTROL = new Set(["array", "key_value"]);
|
|
29
|
+
const hasRows = (value: unknown): boolean =>
|
|
30
|
+
!!value && typeof value === "object" && Object.keys(value).length > 0;
|
|
31
|
+
const showsDisplay = computed(
|
|
32
|
+
() =>
|
|
33
|
+
isDisabled.value &&
|
|
34
|
+
!!props.field.type &&
|
|
35
|
+
!(
|
|
36
|
+
READS_AS_CONTROL.has(props.field.type) &&
|
|
37
|
+
hasRows(state.value[props.field.id])
|
|
38
|
+
),
|
|
39
|
+
);
|
|
25
40
|
const component = computed(() => {
|
|
26
41
|
const name = props.field.component.componentName;
|
|
27
42
|
return name ? resolveDmsComponent(name) || name : null;
|
|
28
43
|
});
|
|
29
|
-
// The hint under the control, which describes it (`aria-describedby`)
|
|
44
|
+
// The hint under the control, which describes it (`aria-describedby`): a
|
|
45
|
+
// read-only field has no control to fill, and no hint telling how.
|
|
30
46
|
const hint = computed(() =>
|
|
31
|
-
props.field.hint
|
|
47
|
+
props.field.hint && !isDisabled.value
|
|
48
|
+
? processI18n(props.field.hint)
|
|
49
|
+
: undefined,
|
|
32
50
|
);
|
|
33
51
|
// UFormField shows its error slot whenever one is given, in the hint's
|
|
34
52
|
// place: it gets one only while the field holds an error.
|
|
@@ -37,6 +55,17 @@ const hasError = computed(() => {
|
|
|
37
55
|
const pattern = fieldErrorPattern(props.field.id);
|
|
38
56
|
return !!formErrors?.value.some((error) => pattern.test(error.name ?? ""));
|
|
39
57
|
});
|
|
58
|
+
// A read-only relation shows the row the server joined (its label, its
|
|
59
|
+
// avatar), as a table cell does, rather than the id its state holds.
|
|
60
|
+
const { getDataType } = useDataTypes();
|
|
61
|
+
const readonlyValue = computed(() =>
|
|
62
|
+
readonlyFieldValue(
|
|
63
|
+
state.value[props.field.id],
|
|
64
|
+
initialValues.value?.[props.field.id],
|
|
65
|
+
props.field.type ? getDataType(props.field.type) : undefined,
|
|
66
|
+
props.field.component.options,
|
|
67
|
+
),
|
|
68
|
+
);
|
|
40
69
|
// What a read-only field shows beside its value: a pill, a link.
|
|
41
70
|
const readonly = computed<FormFieldReadonly>(() =>
|
|
42
71
|
typeof props.field.readonly === "object" ? props.field.readonly : {},
|
|
@@ -44,7 +73,9 @@ const readonly = computed<FormFieldReadonly>(() =>
|
|
|
44
73
|
</script>
|
|
45
74
|
|
|
46
75
|
<template>
|
|
47
|
-
|
|
76
|
+
<!-- One explicit column (minmax(0, 1fr)): an implicit one grows to its
|
|
77
|
+
widest content, a long date or select label, past a narrow card. -->
|
|
78
|
+
<div class="grid min-w-0 grid-cols-1 gap-1.5">
|
|
48
79
|
<DmsLocalizedField
|
|
49
80
|
v-if="field.localized"
|
|
50
81
|
v-model="state[field.id] as Record<string, string> | undefined"
|
|
@@ -63,13 +94,15 @@ const readonly = computed<FormFieldReadonly>(() =>
|
|
|
63
94
|
:help="hint"
|
|
64
95
|
>
|
|
65
96
|
<div
|
|
66
|
-
v-if="
|
|
97
|
+
v-if="showsDisplay"
|
|
67
98
|
class="flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1"
|
|
68
99
|
>
|
|
69
100
|
<DmsDisplay
|
|
70
|
-
:model-value="
|
|
101
|
+
:model-value="readonlyValue"
|
|
71
102
|
:type="field.type"
|
|
72
103
|
:loading
|
|
104
|
+
:label="field.label ? processI18n(field.label) : undefined"
|
|
105
|
+
:row="state"
|
|
73
106
|
class="min-w-0"
|
|
74
107
|
v-bind="field.component.options || {}"
|
|
75
108
|
/>
|
|
@@ -24,10 +24,12 @@ const defaultValue = computed(() => props.field.defaultValue);
|
|
|
24
24
|
const showsChange = computed(
|
|
25
25
|
() => context.showsChanges && context.isFieldChanged(props.field.id),
|
|
26
26
|
);
|
|
27
|
-
// "Module default · Use default" on a value that is not the default,
|
|
28
|
-
//
|
|
27
|
+
// "Module default · Use default" on a value that is not the default, for a
|
|
28
|
+
// field that offers it (a setting, not a record's initial value), unless the
|
|
29
|
+
// change line already says what it was.
|
|
29
30
|
const showsDefault = computed(
|
|
30
31
|
() =>
|
|
32
|
+
!!props.field.offersDefault &&
|
|
31
33
|
defaultValue.value !== undefined &&
|
|
32
34
|
!context.isFieldDisabled(props.field) &&
|
|
33
35
|
!sameFormValue(value.value, defaultValue.value) &&
|
|
@@ -51,7 +53,26 @@ function wordValue(raw: unknown): VNodeChild | undefined {
|
|
|
51
53
|
return undefined;
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
/**
|
|
57
|
+
* The baseline as the form loaded it. A type keeping less in the form than
|
|
58
|
+
* it reads (a relation keeps the ids of the rows the server joined) words
|
|
59
|
+
* the loaded value, its label, while that is still the baseline.
|
|
60
|
+
*/
|
|
61
|
+
const loadedBaseline = computed(() => {
|
|
62
|
+
const loaded = context.initialValues.value[props.field.id];
|
|
63
|
+
const dataType = props.field.type ? getDataType(props.field.type) : undefined;
|
|
64
|
+
if (!dataType?.beforeStateMapper || isEmptyFormValue(loaded)) {
|
|
65
|
+
return baseline.value;
|
|
66
|
+
}
|
|
67
|
+
const options = props.field.component.options;
|
|
68
|
+
const mapped = dataType.beforeStateMapper(loaded, options);
|
|
69
|
+
const isSame = dataType.isSameValue
|
|
70
|
+
? dataType.isSameValue(mapped, baseline.value, options)
|
|
71
|
+
: sameFormValue(mapped, baseline.value);
|
|
72
|
+
return isSame ? loaded : baseline.value;
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const wordedBaseline = computed(() => wordValue(loadedBaseline.value));
|
|
55
76
|
|
|
56
77
|
function useDefault(): void {
|
|
57
78
|
context.state.value[props.field.id] = JSON.parse(
|
|
@@ -61,7 +82,10 @@ function useDefault(): void {
|
|
|
61
82
|
</script>
|
|
62
83
|
|
|
63
84
|
<template>
|
|
64
|
-
<div
|
|
85
|
+
<div
|
|
86
|
+
v-if="showsChange || showsDefault"
|
|
87
|
+
class="grid min-w-0 gap-0.5 text-xs break-words"
|
|
88
|
+
>
|
|
65
89
|
<p
|
|
66
90
|
v-if="showsChange"
|
|
67
91
|
class="text-muted flex flex-wrap items-center gap-1.5"
|
|
@@ -88,7 +112,7 @@ function useDefault(): void {
|
|
|
88
112
|
color="primary"
|
|
89
113
|
variant="link"
|
|
90
114
|
size="xs"
|
|
91
|
-
class="p-0"
|
|
115
|
+
class="p-0 text-start whitespace-normal"
|
|
92
116
|
@click="useDefault"
|
|
93
117
|
/>
|
|
94
118
|
</p>
|
|
@@ -63,8 +63,10 @@ const ITEM_CLASSES: Record<FormSectionNavVariant, string> = {
|
|
|
63
63
|
"border-default text-muted hover:text-default flex h-7 shrink-0 items-center gap-1.5 rounded-full border px-3 text-[12.5px]",
|
|
64
64
|
};
|
|
65
65
|
|
|
66
|
+
// The current section carries a mark of its own, so the entry under the
|
|
67
|
+
// pointer never reads as a second current one.
|
|
66
68
|
const ACTIVE_CLASSES: Record<FormSectionNavVariant, string> = {
|
|
67
|
-
list: "bg-elevated text-highlighted font-[550]",
|
|
69
|
+
list: "bg-elevated text-highlighted font-[550] shadow-[inset_2px_0_0_var(--ui-primary)]",
|
|
68
70
|
chips: "border-accented bg-elevated text-highlighted",
|
|
69
71
|
};
|
|
70
72
|
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { FormSectionNav } from "../../../composables/form/types/field";
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import {
|
|
4
|
+
currentSectionId,
|
|
5
|
+
type FormSectionState,
|
|
6
|
+
type FormSectionsLayout,
|
|
6
7
|
} from "../../composables/form/formSections";
|
|
7
8
|
import DmsFormEntries from "./FormEntries.vue";
|
|
8
9
|
import DmsFormSectionNav from "./FormSectionNav.vue";
|
|
10
|
+
import { scrollParent, scrollWithin } from "../../utils/scrollWithin";
|
|
9
11
|
|
|
10
12
|
interface FormSectionsProps {
|
|
11
13
|
layout: FormSectionsLayout;
|
|
@@ -17,9 +19,15 @@ interface FormSectionsProps {
|
|
|
17
19
|
|
|
18
20
|
const props = defineProps<FormSectionsProps>();
|
|
19
21
|
|
|
20
|
-
// A section is current once its top passes this
|
|
21
|
-
//
|
|
22
|
-
|
|
22
|
+
// A section is current once its top passes this line, a third of the way
|
|
23
|
+
// down the visible area: the one being read, not the one merely peeking in
|
|
24
|
+
// at the bottom.
|
|
25
|
+
const READING_LINE_RATIO = 0.35;
|
|
26
|
+
// A scroll this close to the end is at the end (subpixel rounding).
|
|
27
|
+
const END_TOLERANCE_PX = 2;
|
|
28
|
+
// A picked section stays current until the scroll it started has settled:
|
|
29
|
+
// on its way, the smooth scroll passes the sections in between.
|
|
30
|
+
const SCROLL_SETTLE_MS = 150;
|
|
23
31
|
|
|
24
32
|
const activeId = ref(props.layout.sections[0]?.id);
|
|
25
33
|
const sectionElements = useTemplateRef<HTMLElement[]>("sectionElements");
|
|
@@ -28,45 +36,116 @@ function anchorId(sectionId: string): string {
|
|
|
28
36
|
return `${props.formId}-section-${sectionId}`;
|
|
29
37
|
}
|
|
30
38
|
|
|
39
|
+
let pickedTimer: ReturnType<typeof setTimeout> | undefined;
|
|
40
|
+
let isPicked = false;
|
|
41
|
+
|
|
42
|
+
function holdPicked(): void {
|
|
43
|
+
clearTimeout(pickedTimer);
|
|
44
|
+
pickedTimer = setTimeout(() => {
|
|
45
|
+
isPicked = false;
|
|
46
|
+
}, SCROLL_SETTLE_MS);
|
|
47
|
+
}
|
|
48
|
+
|
|
31
49
|
function scrollToSection(sectionId: string): void {
|
|
32
50
|
activeId.value = sectionId;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
51
|
+
isPicked = true;
|
|
52
|
+
holdPicked();
|
|
53
|
+
const section = document.getElementById(anchorId(sectionId));
|
|
54
|
+
if (section) scrollWithin(section, { behavior: "smooth", block: "start" });
|
|
36
55
|
history.replaceState(history.state, "", `#${anchorId(sectionId)}`);
|
|
37
56
|
}
|
|
38
57
|
|
|
39
|
-
|
|
40
|
-
|
|
58
|
+
interface SpyViewport {
|
|
59
|
+
top: number;
|
|
60
|
+
height: number;
|
|
61
|
+
isAtEnd: boolean;
|
|
62
|
+
}
|
|
41
63
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
64
|
+
// Scrolled at all, and to the end: a form that fits never scrolls, and its
|
|
65
|
+
// first section stays current.
|
|
66
|
+
function isScrolledToEnd(
|
|
67
|
+
scrollTop: number,
|
|
68
|
+
height: number,
|
|
69
|
+
scrollHeight: number,
|
|
70
|
+
): boolean {
|
|
71
|
+
return scrollTop > 0 && scrollTop + height >= scrollHeight - END_TOLERANCE_PX;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function spyViewport(scroller: HTMLElement | null): SpyViewport {
|
|
75
|
+
if (scroller) {
|
|
76
|
+
return {
|
|
77
|
+
top: scroller.getBoundingClientRect().top,
|
|
78
|
+
height: scroller.clientHeight,
|
|
79
|
+
isAtEnd: isScrolledToEnd(
|
|
80
|
+
scroller.scrollTop,
|
|
81
|
+
scroller.clientHeight,
|
|
82
|
+
scroller.scrollHeight,
|
|
83
|
+
),
|
|
84
|
+
};
|
|
48
85
|
}
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
86
|
+
const root = document.documentElement;
|
|
87
|
+
return {
|
|
88
|
+
top: 0,
|
|
89
|
+
height: window.innerHeight,
|
|
90
|
+
isAtEnd: isScrolledToEnd(
|
|
91
|
+
window.scrollY,
|
|
92
|
+
window.innerHeight,
|
|
93
|
+
root.scrollHeight,
|
|
94
|
+
),
|
|
95
|
+
};
|
|
53
96
|
}
|
|
54
97
|
|
|
55
|
-
|
|
56
|
-
|
|
98
|
+
function updateActiveSection(): void {
|
|
99
|
+
const elements = sectionElements.value ?? [];
|
|
100
|
+
const first = elements[0];
|
|
101
|
+
if (!first) return;
|
|
102
|
+
const viewport = spyViewport(scrollParent(first));
|
|
103
|
+
const id = currentSectionId({
|
|
104
|
+
sections: elements.map((element) => ({
|
|
105
|
+
id: element.dataset.section ?? "",
|
|
106
|
+
top: element.getBoundingClientRect().top - viewport.top,
|
|
107
|
+
})),
|
|
108
|
+
readingLine: viewport.height * READING_LINE_RATIO,
|
|
109
|
+
isAtEnd: viewport.isAtEnd,
|
|
110
|
+
});
|
|
111
|
+
if (id) activeId.value = id;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let frame: number | undefined;
|
|
115
|
+
|
|
116
|
+
// Captured on the document: whichever container scrolls the form (the page
|
|
117
|
+
// body, a drawer, the window) reports here, even one that only became
|
|
118
|
+
// scrollable once the form loaded.
|
|
119
|
+
function onScroll(): void {
|
|
120
|
+
if (isPicked) {
|
|
121
|
+
holdPicked();
|
|
57
122
|
return;
|
|
58
123
|
}
|
|
59
|
-
|
|
60
|
-
|
|
124
|
+
if (frame !== undefined) return;
|
|
125
|
+
frame = requestAnimationFrame(() => {
|
|
126
|
+
frame = undefined;
|
|
127
|
+
updateActiveSection();
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
onMounted(() => {
|
|
132
|
+
if (props.nav === "none") return;
|
|
133
|
+
document.addEventListener("scroll", onScroll, {
|
|
134
|
+
capture: true,
|
|
135
|
+
passive: true,
|
|
61
136
|
});
|
|
62
|
-
for (const element of sectionElements.value ?? []) observer.observe(element);
|
|
63
137
|
const linked = props.layout.sections.find(
|
|
64
138
|
(section) => location.hash === `#${anchorId(section.id)}`,
|
|
65
139
|
);
|
|
66
140
|
if (linked) scrollToSection(linked.id);
|
|
141
|
+
else updateActiveSection();
|
|
67
142
|
});
|
|
68
143
|
|
|
69
|
-
onBeforeUnmount(() =>
|
|
144
|
+
onBeforeUnmount(() => {
|
|
145
|
+
document.removeEventListener("scroll", onScroll, { capture: true });
|
|
146
|
+
clearTimeout(pickedTimer);
|
|
147
|
+
if (frame !== undefined) cancelAnimationFrame(frame);
|
|
148
|
+
});
|
|
70
149
|
</script>
|
|
71
150
|
|
|
72
151
|
<template>
|
|
@@ -106,6 +106,10 @@ const theme = tv({
|
|
|
106
106
|
|
|
107
107
|
interface TableActionsProps {
|
|
108
108
|
canAddRow?: boolean;
|
|
109
|
+
/** The add action's configured label (`$`: i18n key); "New entry" without. */
|
|
110
|
+
addLabel?: string;
|
|
111
|
+
/** The add action's configured icon; the theme's plus without. */
|
|
112
|
+
addIcon?: string;
|
|
109
113
|
/** Shows the archive mode toggle, bound to the `showArchived` model. */
|
|
110
114
|
archiveToggle?: boolean;
|
|
111
115
|
customButtons?: CustomButton[];
|
|
@@ -162,6 +166,10 @@ const appConfig = useDmsAppConfig() as DmsAppConfig & {
|
|
|
162
166
|
|
|
163
167
|
const { t } = useI18n();
|
|
164
168
|
const { processI18n } = useTranslation();
|
|
169
|
+
const addRowLabel = computed(() =>
|
|
170
|
+
props.addLabel ? processI18n(props.addLabel) : t("dms.table.new_row"),
|
|
171
|
+
);
|
|
172
|
+
const addRowIcon = computed(() => props.addIcon ?? appConfig.ui.icons.plus);
|
|
165
173
|
|
|
166
174
|
const searchActive = ref(false);
|
|
167
175
|
const searchInputRef = ref<HTMLInputElement>();
|
|
@@ -625,16 +633,16 @@ const uiTableActions = computed(() => uiTableActionsVariant());
|
|
|
625
633
|
|
|
626
634
|
<UButton
|
|
627
635
|
v-if="canAddRow"
|
|
628
|
-
:label="
|
|
629
|
-
:icon="
|
|
636
|
+
:label="addRowLabel"
|
|
637
|
+
:icon="addRowIcon"
|
|
630
638
|
:class="uiTableActions.add()"
|
|
631
639
|
size="sm"
|
|
632
640
|
@click="tableSharedData!.emits('add')"
|
|
633
641
|
/>
|
|
634
642
|
<UButton
|
|
635
643
|
v-if="canAddRow"
|
|
636
|
-
:aria-label="
|
|
637
|
-
:icon="
|
|
644
|
+
:aria-label="addRowLabel"
|
|
645
|
+
:icon="addRowIcon"
|
|
638
646
|
:class="uiTableActions.addMobile()"
|
|
639
647
|
size="sm"
|
|
640
648
|
@click="tableSharedData!.emits('add')"
|
|
@@ -211,10 +211,12 @@ const builtInActions = computed(() => {
|
|
|
211
211
|
];
|
|
212
212
|
}
|
|
213
213
|
if (props.canAddRow) {
|
|
214
|
+
const addLabel = tableSharedData.value?.addLabel;
|
|
215
|
+
const addIcon = tableSharedData.value?.addIcon;
|
|
214
216
|
return [
|
|
215
217
|
{
|
|
216
|
-
label: t("dms.table.new_row"),
|
|
217
|
-
icon: "i-ph-plus",
|
|
218
|
+
label: addLabel ? processI18n(addLabel) : t("dms.table.new_row"),
|
|
219
|
+
icon: addIcon ?? "i-ph-plus",
|
|
218
220
|
color: "primary" as const,
|
|
219
221
|
size: "md" as const,
|
|
220
222
|
onClick: () => tableSharedData.value?.emits("add"),
|
|
@@ -13,7 +13,12 @@ import type {
|
|
|
13
13
|
} from "./Table.vue";
|
|
14
14
|
import TableMenuFilter from "./MenuFilter.vue";
|
|
15
15
|
import { formatFilterValue } from "../../composables/table/utils/formatFilterValue";
|
|
16
|
+
import { filterEditorValue } from "../../composables/table/utils/filterEditorValue";
|
|
16
17
|
import { clearTableFiltersLabelKey } from "../../composables/table/utils/clearTableFilters";
|
|
18
|
+
import {
|
|
19
|
+
type RelationFilterSource,
|
|
20
|
+
useRelationFilterLabels,
|
|
21
|
+
} from "../../composables/table/useRelationFilterLabels";
|
|
17
22
|
|
|
18
23
|
// v2 filters band: a muted strip of segmented chips (field │ mode │ value │ ×);
|
|
19
24
|
// a chip opens its value editor in a popover.
|
|
@@ -155,6 +160,16 @@ const addFilterOpen = ref(false);
|
|
|
155
160
|
const { locale } = useI18n();
|
|
156
161
|
const { getDataType } = useDataTypes();
|
|
157
162
|
|
|
163
|
+
// What a chip's editor holds: a view's text value read as its column's type.
|
|
164
|
+
const editorValue = (filter: TableFilter): unknown => {
|
|
165
|
+
const typeId = getColumn(filter.accessorKey)?.type?.id;
|
|
166
|
+
return filterEditorValue(
|
|
167
|
+
typeId ? getDataType(typeId) : undefined,
|
|
168
|
+
filter.mode,
|
|
169
|
+
filter.value,
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
|
|
158
173
|
// Select-like filters show the option labels, not the raw stored values
|
|
159
174
|
// (their formatter renders a pill, which a chip can't hold).
|
|
160
175
|
const itemLabels = (filter: TableFilter): string | undefined => {
|
|
@@ -170,7 +185,29 @@ const itemLabels = (filter: TableFilter): string | undefined => {
|
|
|
170
185
|
return labels.length ? labels.join(", ") : undefined;
|
|
171
186
|
};
|
|
172
187
|
|
|
188
|
+
// A relation filter holds the related rows' ids: its chip names the rows,
|
|
189
|
+
// read from where the column's data type says they live (its `select` route).
|
|
190
|
+
const relationSource = (
|
|
191
|
+
filter: TableFilter,
|
|
192
|
+
): RelationFilterSource | undefined => {
|
|
193
|
+
const column = getColumn(filter.accessorKey);
|
|
194
|
+
const component = resolveFilterComponent(filter);
|
|
195
|
+
if (!column?.type || !component) return undefined;
|
|
196
|
+
const source = getDataType(column.type.id)?.selectSource?.(component.options);
|
|
197
|
+
return source ? { ...source, value: filter.value } : undefined;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const { labelsOf: relationLabels } = useRelationFilterLabels(() =>
|
|
201
|
+
[...urlFilters.value, ...filters.value].flatMap((filter) => {
|
|
202
|
+
const source = relationSource(filter);
|
|
203
|
+
return source ? [source] : [];
|
|
204
|
+
}),
|
|
205
|
+
);
|
|
206
|
+
|
|
173
207
|
const displayValue = (filter: TableFilter): string => {
|
|
208
|
+
const source = relationSource(filter);
|
|
209
|
+
const names = source ? relationLabels(source) : [];
|
|
210
|
+
if (names.length) return names.join(", ");
|
|
174
211
|
const labels = itemLabels(filter);
|
|
175
212
|
if (labels) return labels;
|
|
176
213
|
const formatted = formatFilterValue(
|
|
@@ -251,7 +288,7 @@ const chipTitle = (filter: TableFilter, label?: string): string =>
|
|
|
251
288
|
)
|
|
252
289
|
"
|
|
253
290
|
v-if="resolveFilterComponent(filter)"
|
|
254
|
-
:model-value="filter
|
|
291
|
+
:model-value="editorValue(filter)"
|
|
255
292
|
:class="ui.inputWrapper()"
|
|
256
293
|
v-bind="
|
|
257
294
|
JSON.parse(
|