@antelopejs/dms 0.9.8 → 0.11.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 +171 -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 -2
- package/dist/config.js +5 -7
- 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 +38 -94
- 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 +7 -2
- package/dist/implementations/dms/manifest.js +12 -7
- 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 +32 -55
- 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/index.d.ts +0 -1
- package/dist/routes/index.js +0 -1
- package/dist/routes/index.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 +25 -15
- package/dist/store/listing.js.map +1 -1
- package/dist/store/operations.js +5 -2
- 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/utils/ensure-default-tenant.d.ts +2 -1
- package/dist/utils/ensure-default-tenant.js +13 -1
- package/dist/utils/ensure-default-tenant.js.map +1 -1
- package/dist/utils/export-jobs.js +2 -1
- package/dist/utils/export-jobs.js.map +1 -1
- 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 +33 -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/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 +25 -5
- package/frontend-vue/layers/dms-core/app/composables/useApiError.ts +3 -5
- package/frontend-vue/layers/dms-core/app/composables/useFieldErrors.ts +61 -26
- 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 +134 -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 +136 -2
- package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +136 -2
- package/frontend-vue/layers/dms-core/shared/types/app-config.ts +0 -2
- package/frontend-vue/layers/dms-layout/app/app.config.ts +0 -1
- package/frontend-vue/layers/dms-layout/app/assets/css/main.css +11 -1
- package/frontend-vue/layers/dms-layout/app/assets/fonts/Geist-Italic-Variable.woff2 +0 -0
- package/frontend-vue/layers/dms-layout/app/assets/fonts/Geist-Variable.woff2 +0 -0
- package/frontend-vue/layers/dms-layout/app/assets/fonts/GeistMono-Italic-Variable.woff2 +0 -0
- package/frontend-vue/layers/dms-layout/app/assets/fonts/GeistMono-Variable.woff2 +0 -0
- 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 +6 -2
- 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 -107
- package/frontend-vue/layers/dms-layout/app/build/components/layout/GithubStarPrompt.vue +18 -7
- package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionsOutlet.vue +4 -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/github-repository.ts +2 -2
- 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/useServedHeaderActions.ts +49 -11
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleHistory.ts +3 -2
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +59 -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/usePageRecordDeletion.ts +162 -0
- package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +6 -1
- package/frontend-vue/layers/dms-layout/app/custom-layouts/DefaultLayout.vue +6 -2
- package/frontend-vue/layers/dms-layout/app/custom-layouts/SettingsLayout.vue +6 -2
- 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 +13 -22
- package/frontend-vue/layers/dms-layout/app/plugins/command-palette-navigation.client.ts +11 -10
- package/frontend-vue/layers/dms-layout/app/plugins/command-palette-personal-pages.client.ts +22 -52
- package/frontend-vue/layers/dms-layout/app/plugins/command-palette-quick-actions.client.ts +7 -7
- package/frontend-vue/layers/dms-layout/app/plugins/command-palette-session.client.ts +11 -10
- package/frontend-vue/layers/dms-layout/app/plugins/dms-dev-reload.client.ts +14 -9
- package/frontend-vue/layers/dms-layout/app/theme/actions.ts +5 -4
- 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/i18n/locales/layout-en-GB.json +45 -18
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +44 -17
- 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/FormLoadError.vue +5 -1
- 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 +48 -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/actionError.ts +6 -1
- 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 +44 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +123 -27
- 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 +92 -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/formRecordLoad.ts +11 -5
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formSections.ts +32 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formValidation.ts +36 -4
- 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 +78 -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 +97 -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 +90 -35
- 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/composables/table-view/utils/writeError.ts +21 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/addressSuggestions.ts +49 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/codeHighlight.ts +67 -29
- 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/htmlGrammar.ts +79 -0
- 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/shellParser.ts +1 -1
- package/frontend-vue/layers/dms-ui/app/build/utils/streamHighlight.ts +90 -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/code-snippet/CodeSnippet.vue +23 -12
- 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 +119 -36
- package/frontend-vue/layers/dms-ui/app/components/form/components/Cascader.vue +53 -22
- 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 +226 -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 +210 -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/CascaderPath.vue +74 -25
- package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +48 -44
- 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 +426 -97
- 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 +16 -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 +189 -28
- package/frontend-vue/layers/dms-ui/app/composables/live/usePageLive.ts +29 -12
- 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/cascader.ts +94 -3
- package/frontend-vue/layers/dms-ui/app/utils/componentTree.ts +24 -0
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-en-GB.json +20 -5
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-fr-FR.json +20 -5
- package/frontend-vue/package.json +6 -2
- package/package.json +11 -10
- 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 +129 -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/dist/hooks/tenant-instances.d.ts +0 -1
- package/dist/hooks/tenant-instances.js +0 -13
- package/dist/hooks/tenant-instances.js.map +0 -1
- package/dist/routes/startup-gate.d.ts +0 -7
- package/dist/routes/startup-gate.js +0 -39
- package/dist/routes/startup-gate.js.map +0 -1
- package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +0 -42
|
@@ -6,7 +6,8 @@ import type {
|
|
|
6
6
|
UseDmsPagedDataOptions,
|
|
7
7
|
UseFetchOptions,
|
|
8
8
|
} from "#dms/frontend-module";
|
|
9
|
-
import type
|
|
9
|
+
import { getCurrentInstance, toValue, watch, type MaybeRefOrGetter } from "vue";
|
|
10
|
+
import { useLivePauseSource } from "../live/livePauseSource";
|
|
10
11
|
|
|
11
12
|
/** The authenticated fetch `useAuthFetch` returns. */
|
|
12
13
|
type AuthFetch = ReturnType<typeof useAuthFetch>["$authFetch"];
|
|
@@ -61,7 +62,9 @@ export type ApiDataResult<T> = DmsAsyncDataResult<T>;
|
|
|
61
62
|
* - `refreshInterval: ms` reads again on an interval while the caller shows
|
|
62
63
|
* the data and the page is visible: polling. A ref or a getter changes it,
|
|
63
64
|
* and `0` or `null` stops it; a call from a plugin polls for as long as
|
|
64
|
-
* the application runs.
|
|
65
|
+
* the application runs. On a page declared with `live`, a component's
|
|
66
|
+
* polling stops while the page is paused, and reads once on resume if a
|
|
67
|
+
* poll fell due meanwhile.
|
|
65
68
|
* - The page's refreshes (`refreshPageBlocks()`) and the events of its
|
|
66
69
|
* realtime topics read the data again; `refreshOnPageRefresh: false` opts
|
|
67
70
|
* out, for data with refresh triggers of its own.
|
|
@@ -88,15 +91,74 @@ export function useApiData<T>(
|
|
|
88
91
|
): ApiDataResult<T> {
|
|
89
92
|
const { $authFetch } = useAuthFetch();
|
|
90
93
|
if (typeof handlerOrOptions === "function")
|
|
91
|
-
return
|
|
92
|
-
|
|
93
|
-
|
|
94
|
+
return followLivePause(
|
|
95
|
+
useDmsAsyncData<T>(
|
|
96
|
+
source,
|
|
97
|
+
({ signal }) => handlerOrOptions({ $authFetch, signal }),
|
|
98
|
+
gateLivePolling(options),
|
|
99
|
+
),
|
|
94
100
|
options,
|
|
95
101
|
);
|
|
96
|
-
return
|
|
97
|
-
|
|
98
|
-
|
|
102
|
+
return followLivePause(
|
|
103
|
+
useDmsFetch<T>(source, {
|
|
104
|
+
...gateLivePolling(handlerOrOptions),
|
|
105
|
+
$fetch: $authFetch as UseFetchOptions<T>["$fetch"],
|
|
106
|
+
}),
|
|
107
|
+
handlerOrOptions,
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The options of a read that polls, as far as its live gate goes. */
|
|
112
|
+
interface PolledReadOptions {
|
|
113
|
+
refreshInterval?: MaybeRefOrGetter<number | null | undefined>;
|
|
114
|
+
immediate?: boolean;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// A component's polling is one more read nobody asked for: on a live page it
|
|
118
|
+
// stops while the page is paused, as the blocks' refetches do. A plugin's
|
|
119
|
+
// polling (the shell's, outside any page) keeps running. The pause is read
|
|
120
|
+
// through the app's `LIVE_PAUSE_SOURCE`, which the UI layer provides.
|
|
121
|
+
function isGatedPolling(
|
|
122
|
+
options: PolledReadOptions | undefined,
|
|
123
|
+
): options is PolledReadOptions {
|
|
124
|
+
return options?.refreshInterval !== undefined && !!getCurrentInstance();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** `options` with a `refreshInterval` that is `null` while the page is paused. */
|
|
128
|
+
function gateLivePolling<O extends PolledReadOptions>(
|
|
129
|
+
options: O | undefined,
|
|
130
|
+
): O | undefined {
|
|
131
|
+
if (!isGatedPolling(options)) return options;
|
|
132
|
+
const interval = options.refreshInterval;
|
|
133
|
+
const isPaused = useLivePauseSource();
|
|
134
|
+
return {
|
|
135
|
+
...options,
|
|
136
|
+
refreshInterval: () => (isPaused() ? null : toValue(interval)),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Reads once as the page resumes when a poll fell due while it was paused:
|
|
142
|
+
* the polling restarts with a full interval, which would show the data of
|
|
143
|
+
* before the pause that long.
|
|
144
|
+
*/
|
|
145
|
+
function followLivePause<
|
|
146
|
+
R extends Pick<DmsAsyncDataResult<unknown>, "status" | "refresh">,
|
|
147
|
+
>(result: R, options: PolledReadOptions | undefined): R {
|
|
148
|
+
if (typeof window === "undefined" || !isGatedPolling(options)) return result;
|
|
149
|
+
let pausedAt = 0;
|
|
150
|
+
watch(useLivePauseSource(), (isPaused) => {
|
|
151
|
+
if (isPaused) {
|
|
152
|
+
pausedAt = Date.now();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const period = toValue(options.refreshInterval);
|
|
156
|
+
if (!period || period <= 0 || Date.now() - pausedAt < period) return;
|
|
157
|
+
// Data a caller waits to `execute()` is not read before it is.
|
|
158
|
+
if (result.status.value === "idle" && options.immediate === false) return;
|
|
159
|
+
void result.refresh({ dedupe: "defer" });
|
|
99
160
|
});
|
|
161
|
+
return result;
|
|
100
162
|
}
|
|
101
163
|
|
|
102
164
|
/** What a handler reading one page of API data receives. */
|
|
@@ -139,10 +201,13 @@ export function useApiPagedData<TPage, TCursor = unknown>(
|
|
|
139
201
|
options: UseDmsPagedDataOptions<TPage, TCursor>,
|
|
140
202
|
): ApiPagedDataResult<TPage, TCursor> {
|
|
141
203
|
const { $authFetch } = useAuthFetch();
|
|
142
|
-
return
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
204
|
+
return followLivePause(
|
|
205
|
+
useDmsPagedData<TPage, TCursor>(
|
|
206
|
+
key,
|
|
207
|
+
({ cursor, index, signal }) =>
|
|
208
|
+
handler({ $authFetch, cursor, index, signal }),
|
|
209
|
+
gateLivePolling(options) ?? options,
|
|
210
|
+
),
|
|
146
211
|
options,
|
|
147
212
|
);
|
|
148
213
|
}
|
|
@@ -36,6 +36,24 @@ export type DataTypeValueComparer = (
|
|
|
36
36
|
options?: unknown,
|
|
37
37
|
) => boolean;
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* The value a filter chip's editor holds for a filter value a view declared
|
|
41
|
+
* as text (`"0"`, `"2026-10-01,2026-10-15"`), under a compare mode: a number
|
|
42
|
+
* input refuses `"0"`, a date range wants its two days. The filter itself
|
|
43
|
+
* keeps its text, so the query is unchanged.
|
|
44
|
+
*/
|
|
45
|
+
export type DataTypeFilterEditorValue = (text: string, mode: string) => unknown;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Where the rows a value names are read: the `select` route of the table a
|
|
49
|
+
* relation points to, and the keys its rows carry their label and value under
|
|
50
|
+
* (`label` and `value` unless mapped).
|
|
51
|
+
*/
|
|
52
|
+
export interface DataTypeSelectSource {
|
|
53
|
+
searchUrl: string;
|
|
54
|
+
keyMapping?: { label?: string; value?: string };
|
|
55
|
+
}
|
|
56
|
+
|
|
39
57
|
export interface DataType {
|
|
40
58
|
id: string;
|
|
41
59
|
formatter?: RecordWithDefault<DataTypeFormatter>;
|
|
@@ -46,6 +64,14 @@ export interface DataType {
|
|
|
46
64
|
* or whose order does not count. Without it, values compare deeply.
|
|
47
65
|
*/
|
|
48
66
|
isSameValue?: DataTypeValueComparer;
|
|
67
|
+
/** Without it, a filter's editor reads a declared text value as is. */
|
|
68
|
+
filterEditorValue?: DataTypeFilterEditorValue;
|
|
69
|
+
/**
|
|
70
|
+
* For a type whose value holds the ids of rows another table serves (a
|
|
71
|
+
* relation): where those rows are read, from the options of the type's
|
|
72
|
+
* control. A filter chip or a read-only list names the ids through it.
|
|
73
|
+
*/
|
|
74
|
+
selectSource?: (options: unknown) => DataTypeSelectSource | undefined;
|
|
49
75
|
displayComponent?: Component;
|
|
50
76
|
}
|
|
51
77
|
const dataTypes: Record<string, DataType> = {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { inject, type InjectionKey } from "vue";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Whether the page on screen is paused right now (by the user, a hold or a
|
|
5
|
+
* hidden tab). Reactive: it reads reactive state, so a `watch` or a
|
|
6
|
+
* `computed` over it follows the pause.
|
|
7
|
+
*/
|
|
8
|
+
export type LivePauseSource = () => boolean;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The extension point the core layer reads a page's pause through. The core
|
|
12
|
+
* owns `useApiData` but not the page's live controller: the layer that owns
|
|
13
|
+
* the controller provides a source at the app level
|
|
14
|
+
* (`app.provide(LIVE_PAUSE_SOURCE, source)`), so every component reads the
|
|
15
|
+
* same page, drawers and modals included. Without one, nothing is paused.
|
|
16
|
+
*/
|
|
17
|
+
export const LIVE_PAUSE_SOURCE: InjectionKey<LivePauseSource> = Symbol(
|
|
18
|
+
"dms-live-pause-source",
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const NEVER_PAUSED: LivePauseSource = () => false;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The pause source of the app the calling component belongs to; never
|
|
25
|
+
* paused without one. Call it in a component setup.
|
|
26
|
+
*/
|
|
27
|
+
export function useLivePauseSource(): LivePauseSource {
|
|
28
|
+
return inject(LIVE_PAUSE_SOURCE, NEVER_PAUSED);
|
|
29
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readerClock } from "./periodClock";
|
|
2
|
-
import type { PeriodComparison, PeriodRange } from "./types";
|
|
2
|
+
import type { PeriodComparison, PeriodPreset, PeriodRange } from "./types";
|
|
3
3
|
|
|
4
4
|
const NON_OVERLAP_GAP_MS = 1;
|
|
5
5
|
|
|
@@ -30,7 +30,132 @@ function isWholeDayRange(range: PeriodRange): boolean {
|
|
|
30
30
|
);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
const MONTHS_PER_YEAR = 12;
|
|
34
|
+
const MONTHS_PER_QUARTER = 3;
|
|
35
|
+
|
|
36
|
+
// The calendar unit a preset counts in, in months: its previous period is
|
|
37
|
+
// the previous month, quarter or year, not the same number of days before.
|
|
38
|
+
const PRESET_MONTH_SPANS: Partial<Record<PeriodPreset, number>> = {
|
|
39
|
+
"this-month": 1,
|
|
40
|
+
"last-month": 1,
|
|
41
|
+
"this-quarter": MONTHS_PER_QUARTER,
|
|
42
|
+
"last-quarter": MONTHS_PER_QUARTER,
|
|
43
|
+
ytd: MONTHS_PER_YEAR,
|
|
44
|
+
"last-year": MONTHS_PER_YEAR,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** What a comparison is resolved against beyond the range itself. */
|
|
48
|
+
export interface ComparisonContext {
|
|
49
|
+
/** The preset the range comes from: a calendar one compares by calendar. */
|
|
50
|
+
preset?: PeriodPreset;
|
|
51
|
+
/**
|
|
52
|
+
* The instant the range was resolved at: a range running past today
|
|
53
|
+
* (`this-month` on the 18th) compares what has elapsed of it.
|
|
54
|
+
*/
|
|
55
|
+
now?: Date;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** `date` moved by whole months, its day kept within the month reached. */
|
|
59
|
+
function shiftMonths(date: Date, months: number): Date {
|
|
60
|
+
const clock = readerClock();
|
|
61
|
+
const wall = clock.wall(date);
|
|
62
|
+
const month = wall.month + months;
|
|
63
|
+
// Day 0 of the next month is the last day of this one.
|
|
64
|
+
const lastDay = clock.wall(
|
|
65
|
+
clock.instant({ ...wall, month: month + 1, day: 0 }),
|
|
66
|
+
).day;
|
|
67
|
+
return clock.instant({ ...wall, month, day: Math.min(wall.day, lastDay) });
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Whether `date` is the midnight opening a calendar month. */
|
|
71
|
+
function isMonthStart(date: Date): boolean {
|
|
72
|
+
return isLocalMidnight(date) && readerClock().wall(date).day === 1;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** Months from one month start to another. */
|
|
76
|
+
function monthsBetween(from: Date, to: Date): number {
|
|
77
|
+
const clock = readerClock();
|
|
78
|
+
const start = clock.wall(from);
|
|
79
|
+
const end = clock.wall(to);
|
|
80
|
+
return (end.year - start.year) * MONTHS_PER_YEAR + end.month - start.month;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The instant right after a range: the start of what follows it. */
|
|
84
|
+
const afterRange = (range: PeriodRange): Date =>
|
|
85
|
+
new Date(range.to.getTime() + NON_OVERLAP_GAP_MS);
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The calendar months a range's previous period steps back by: the unit of a
|
|
89
|
+
* calendar preset, else the months of a range made of whole months (a custom
|
|
90
|
+
* September compares to August). Any other range has none.
|
|
91
|
+
*/
|
|
92
|
+
function calendarMonthSpan(
|
|
93
|
+
range: PeriodRange,
|
|
94
|
+
preset: PeriodPreset | undefined,
|
|
95
|
+
): number | undefined {
|
|
96
|
+
if (!isMonthStart(range.from)) return undefined;
|
|
97
|
+
const presetSpan = preset ? PRESET_MONTH_SPANS[preset] : undefined;
|
|
98
|
+
if (presetSpan) return presetSpan;
|
|
99
|
+
const end = afterRange(range);
|
|
100
|
+
return isMonthStart(end) ? monthsBetween(range.from, end) : undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** The last millisecond of `date`'s day. */
|
|
104
|
+
function endOfDay(date: Date): Date {
|
|
105
|
+
const clock = readerClock();
|
|
106
|
+
const nextDay = clock.instant({
|
|
107
|
+
...clock.wall(date),
|
|
108
|
+
day: clock.wall(date).day + 1,
|
|
109
|
+
hours: 0,
|
|
110
|
+
minutes: 0,
|
|
111
|
+
seconds: 0,
|
|
112
|
+
milliseconds: 0,
|
|
113
|
+
});
|
|
114
|
+
return new Date(nextDay.getTime() - NON_OVERLAP_GAP_MS);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* What a comparison measures of a range: all of it, or what has elapsed of
|
|
119
|
+
* it when it runs past today — the rest holds no data yet, and comparing it
|
|
120
|
+
* with a whole period reads as a fall.
|
|
121
|
+
*/
|
|
122
|
+
function elapsedEnd(range: PeriodRange, now: Date | undefined): Date {
|
|
123
|
+
if (!now) return range.to;
|
|
124
|
+
const today = endOfDay(now);
|
|
125
|
+
return range.to.getTime() > today.getTime() ? today : range.to;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The same span of the calendar `months` earlier: the whole month, quarter
|
|
130
|
+
* or year before when the range is complete and ends with a month, else its
|
|
131
|
+
* elapsed days (the 1st to the 18th of the month before).
|
|
132
|
+
*/
|
|
133
|
+
function shiftCalendarPeriod(
|
|
134
|
+
range: PeriodRange,
|
|
135
|
+
months: number,
|
|
136
|
+
end: Date,
|
|
137
|
+
): PeriodRange {
|
|
138
|
+
const from = shiftMonths(range.from, -months);
|
|
139
|
+
const isComplete = end.getTime() === range.to.getTime();
|
|
140
|
+
if (isComplete && isMonthStart(afterRange(range))) {
|
|
141
|
+
// The span before ends where the range starts: August, then September.
|
|
142
|
+
return { from, to: new Date(range.from.getTime() - NON_OVERLAP_GAP_MS) };
|
|
143
|
+
}
|
|
144
|
+
return { from, to: endOfDay(shiftMonths(end, -months)) };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function shiftPreviousPeriod(
|
|
148
|
+
range: PeriodRange,
|
|
149
|
+
context: ComparisonContext,
|
|
150
|
+
): PeriodRange {
|
|
151
|
+
const months = calendarMonthSpan(range, context.preset);
|
|
152
|
+
if (months) {
|
|
153
|
+
return shiftCalendarPeriod(range, months, elapsedEnd(range, context.now));
|
|
154
|
+
}
|
|
155
|
+
return shiftEqualLength(range);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function shiftEqualLength(range: PeriodRange): PeriodRange {
|
|
34
159
|
// Whole-day ranges move by calendar days, so a span crossing a daylight
|
|
35
160
|
// saving change still starts at midnight (a fixed-ms shift lands an hour off).
|
|
36
161
|
if (isWholeDayRange(range)) {
|
|
@@ -50,7 +175,14 @@ function shiftPreviousPeriod(range: PeriodRange): PeriodRange {
|
|
|
50
175
|
};
|
|
51
176
|
}
|
|
52
177
|
|
|
53
|
-
function shiftPreviousYear(
|
|
178
|
+
function shiftPreviousYear(
|
|
179
|
+
range: PeriodRange,
|
|
180
|
+
context: ComparisonContext,
|
|
181
|
+
): PeriodRange {
|
|
182
|
+
const end = elapsedEnd(range, context.now);
|
|
183
|
+
if (end.getTime() !== range.to.getTime()) {
|
|
184
|
+
return shiftCalendarPeriod(range, MONTHS_PER_YEAR, end);
|
|
185
|
+
}
|
|
54
186
|
return {
|
|
55
187
|
from: shiftCalendar(range.from, 0, -1),
|
|
56
188
|
to: shiftCalendar(range.to, 0, -1),
|
|
@@ -59,7 +191,7 @@ function shiftPreviousYear(range: PeriodRange): PeriodRange {
|
|
|
59
191
|
|
|
60
192
|
const COMPARISON_RESOLVERS: Record<
|
|
61
193
|
Exclude<PeriodComparison, "none" | "custom">,
|
|
62
|
-
(range: PeriodRange) => PeriodRange
|
|
194
|
+
(range: PeriodRange, context: ComparisonContext) => PeriodRange
|
|
63
195
|
> = {
|
|
64
196
|
"previous-period": shiftPreviousPeriod,
|
|
65
197
|
"previous-year": shiftPreviousYear,
|
|
@@ -68,18 +200,27 @@ const COMPARISON_RESOLVERS: Record<
|
|
|
68
200
|
/**
|
|
69
201
|
* Resolves the range a period is compared against.
|
|
70
202
|
*
|
|
71
|
-
* `previous-period`
|
|
72
|
-
*
|
|
203
|
+
* `previous-period` follows the calendar for a calendar range: a month, a
|
|
204
|
+
* quarter or a year (`this-month`, `last-quarter`, `ytd`…, or a custom range
|
|
205
|
+
* of whole months) compares to the one before — September to August, Q3 to
|
|
206
|
+
* Q2. Any other range compares to the equal-length span immediately before
|
|
207
|
+
* it, offset by a single millisecond so the two never overlap; ranges of
|
|
73
208
|
* whole days are measured in calendar days, so daylight saving changes don't
|
|
74
|
-
* move their midnight boundaries.
|
|
75
|
-
*
|
|
209
|
+
* move their midnight boundaries.
|
|
210
|
+
*
|
|
211
|
+
* A range running past today (`this-month` on the 18th) compares what has
|
|
212
|
+
* elapsed of it — the 1st to the 18th of the month before, or of the year
|
|
213
|
+
* before with `previous-year` — not a whole period against days that hold no
|
|
214
|
+
* data yet. A range with no bounds (`all`) has nothing to compare against:
|
|
215
|
+
* `null`.
|
|
76
216
|
*/
|
|
77
217
|
export function resolveComparisonRange(
|
|
78
218
|
comparison: PeriodComparison,
|
|
79
219
|
range: PeriodRange | null,
|
|
80
220
|
customRange: PeriodRange | null,
|
|
221
|
+
context: ComparisonContext = {},
|
|
81
222
|
): PeriodRange | null {
|
|
82
223
|
if (comparison === "none" || !range) return null;
|
|
83
224
|
if (comparison === "custom") return customRange;
|
|
84
|
-
return COMPARISON_RESOLVERS[comparison](range);
|
|
225
|
+
return COMPARISON_RESOLVERS[comparison](range, context);
|
|
85
226
|
}
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
resolvePresetRange,
|
|
26
26
|
} from "./presetResolvers";
|
|
27
27
|
import { resolveComparisonRange } from "./compareResolvers";
|
|
28
|
-
import {
|
|
28
|
+
import { serverRenderNow } from "../../build/composables/renderNow";
|
|
29
29
|
|
|
30
30
|
export interface UsePeriodOptions {
|
|
31
31
|
defaultPreset?: PeriodPreset;
|
|
@@ -52,7 +52,7 @@ export interface UsePeriodReturn {
|
|
|
52
52
|
state: ComputedRef<PeriodState>;
|
|
53
53
|
/**
|
|
54
54
|
* The instant `state` was resolved from: what a label relative to now
|
|
55
|
-
* reads, so it holds still with the period (see `
|
|
55
|
+
* reads, so it holds still with the period (see `serverRenderNow`).
|
|
56
56
|
*/
|
|
57
57
|
resolvedAt: ComputedRef<Date>;
|
|
58
58
|
setPreset: (preset: PeriodPreset) => void;
|
|
@@ -150,6 +150,7 @@ function buildState(refs: PeriodRefs, now: Date): PeriodState {
|
|
|
150
150
|
refs.comparison.value,
|
|
151
151
|
range,
|
|
152
152
|
refs.customCompareRange.value,
|
|
153
|
+
{ preset: refs.preset.value, now },
|
|
153
154
|
);
|
|
154
155
|
const partial = {
|
|
155
156
|
preset: refs.preset.value,
|
|
@@ -184,7 +185,7 @@ function makeSetters(refs: PeriodRefs) {
|
|
|
184
185
|
* comparisons and custom ranges resolved into a publishable `state`.
|
|
185
186
|
*
|
|
186
187
|
* A server render and the browser hydrating it resolve from the same instant
|
|
187
|
-
* (`
|
|
188
|
+
* (`serverRenderNow`), so both render the same bounds.
|
|
188
189
|
*
|
|
189
190
|
* Relative presets (`last-hour`, `last-24h`) re-resolve every minute, and
|
|
190
191
|
* calendar presets (`today`, `last-7-days`, `this-month`...) at each local
|
|
@@ -214,7 +215,8 @@ export function usePeriod(options: UsePeriodOptions = {}): UsePeriodReturn {
|
|
|
214
215
|
// The server render's instant, until the clock ticks or the period changes
|
|
215
216
|
// once on screen: what the setup applies (the URL's period) the server
|
|
216
217
|
// applied as well.
|
|
217
|
-
const
|
|
218
|
+
const serverNow = serverRenderNow();
|
|
219
|
+
const renderNow = shallowRef(serverNow === null ? null : new Date(serverNow));
|
|
218
220
|
if (renderNow.value) {
|
|
219
221
|
onMounted(() => {
|
|
220
222
|
watch(
|
|
@@ -88,7 +88,7 @@ function readPeriodScope(
|
|
|
88
88
|
* first render must match the HTML. It also answers `null` for a period the
|
|
89
89
|
* browser could resolve to other bounds: before the browser's cookie names
|
|
90
90
|
* its time zone. A relative preset resolves from the instant the server
|
|
91
|
-
* hands the browser (`
|
|
91
|
+
* hands the browser (`serverRenderNow`), so both read the same bounds.
|
|
92
92
|
*/
|
|
93
93
|
export function usePeriodScope(
|
|
94
94
|
id: string | undefined,
|
|
@@ -144,3 +144,29 @@ export function appendPeriodToUrl(
|
|
|
144
144
|
const separator = url.includes("?") ? "&" : "?";
|
|
145
145
|
return `${url}${separator}${params.toString()}`;
|
|
146
146
|
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* The comparison a period scope applies, `null` while no selector registers
|
|
150
|
+
* it: `none` too when the selected comparison resolves to no window (every
|
|
151
|
+
* period, `all`, has nothing to compare against), so a consumer hides its
|
|
152
|
+
* comparison exactly when the request carries no `compareFrom` /
|
|
153
|
+
* `compareTo`. A server render reads it once, as the caller sets up, like
|
|
154
|
+
* `usePeriodScopePreset`: the mode is a name, the same on both sides.
|
|
155
|
+
*/
|
|
156
|
+
export function usePeriodScopeComparison(
|
|
157
|
+
id: string | undefined,
|
|
158
|
+
): ComputedRef<PeriodState["comparison"] | null> {
|
|
159
|
+
if (!id) return computed(() => null);
|
|
160
|
+
const registry = usePeriodScopeRegistry();
|
|
161
|
+
if (typeof window !== "undefined")
|
|
162
|
+
return computed(() => effectiveComparison(readPeriodScope(registry, id)));
|
|
163
|
+
const comparison = effectiveComparison(readPeriodScope(registry, id));
|
|
164
|
+
return computed(() => comparison);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function effectiveComparison(
|
|
168
|
+
state: PeriodState | null,
|
|
169
|
+
): PeriodState["comparison"] | null {
|
|
170
|
+
if (!state) return null;
|
|
171
|
+
return state.compareRange ? state.comparison : "none";
|
|
172
|
+
}
|