@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
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { onBeforeUnmount, onMounted, ref, type Ref } from "vue";
|
|
2
|
+
|
|
3
|
+
// Any reka layer still open: a dialog, a drawer, a modal menu.
|
|
4
|
+
const OPEN_LAYER_SELECTOR = '[data-dismissable-layer][data-state="open"]';
|
|
5
|
+
|
|
6
|
+
// What blocks the page while it is mounted: a layer and a backdrop.
|
|
7
|
+
const LAYER_SELECTOR = "[data-dismissable-layer]";
|
|
8
|
+
const BLOCKING_SELECTOR = `${LAYER_SELECTOR}, [data-slot="overlay"]`;
|
|
9
|
+
|
|
10
|
+
// A drawer, a modal or a slideover still open: what a closing one covered.
|
|
11
|
+
const OPEN_DIALOG_SELECTOR = [
|
|
12
|
+
'[role="dialog"][data-dismissable-layer][data-state="open"]',
|
|
13
|
+
'[role="alertdialog"][data-dismissable-layer][data-state="open"]',
|
|
14
|
+
].join(", ");
|
|
15
|
+
|
|
16
|
+
// A popover's content is a dialog layer too, but anchored to its trigger: it
|
|
17
|
+
// covers nothing.
|
|
18
|
+
const POPOVER_SELECTOR = "[data-reka-popper-content-wrapper]";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Whether a drawer, a modal or a slideover is open on the page, whoever
|
|
22
|
+
* rendered it: the same dialogs the closing-layers watch tracks.
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
export function hasOpenDialog(root: Document): boolean {
|
|
26
|
+
return [...root.querySelectorAll(OPEN_DIALOG_SELECTOR)].some(
|
|
27
|
+
(dialog) => !dialog.closest(POPOVER_SELECTOR),
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* {@link hasOpenDialog}, kept up to date while the calling component is
|
|
33
|
+
* mounted: for what must step aside while a dialog covers the page (a
|
|
34
|
+
* floating prompt). False on the server and until mounted.
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
export function useOpenDialog(): Readonly<Ref<boolean>> {
|
|
38
|
+
const isOpen = ref(false);
|
|
39
|
+
let observer: MutationObserver | null = null;
|
|
40
|
+
const update = () => {
|
|
41
|
+
isOpen.value = hasOpenDialog(document);
|
|
42
|
+
};
|
|
43
|
+
onMounted(() => {
|
|
44
|
+
// A dialog mounts open: its insertion counts, not only its state.
|
|
45
|
+
observer = new MutationObserver(update);
|
|
46
|
+
observer.observe(document.body, {
|
|
47
|
+
subtree: true,
|
|
48
|
+
childList: true,
|
|
49
|
+
attributeFilter: ["data-state"],
|
|
50
|
+
});
|
|
51
|
+
update();
|
|
52
|
+
});
|
|
53
|
+
onBeforeUnmount(() => {
|
|
54
|
+
observer?.disconnect();
|
|
55
|
+
observer = null;
|
|
56
|
+
});
|
|
57
|
+
return isOpen;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Reka's FocusScope, as it unmounts: cancelling it keeps the focus in place.
|
|
61
|
+
const UNMOUNT_AUTOFOCUS_EVENT = "focusScope.autoFocusOnUnmount";
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Set on the dialog a closing one uncovered, until that one has unmounted:
|
|
65
|
+
* the dialog's theme gives it its pointer events back (`UNCOVERED_PANEL_CLASS`).
|
|
66
|
+
* @internal
|
|
67
|
+
*/
|
|
68
|
+
export const UNCOVERED_ATTRIBUTE = "data-dms-uncovered";
|
|
69
|
+
|
|
70
|
+
function isClosing(mutation: MutationRecord): boolean {
|
|
71
|
+
const target = mutation.target as Element;
|
|
72
|
+
return (
|
|
73
|
+
target.getAttribute("data-state") === "closed" &&
|
|
74
|
+
target.matches(BLOCKING_SELECTOR)
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Lets the page be used as soon as any dialog, drawer, slideover or modal
|
|
80
|
+
* menu starts closing, whoever rendered it, while it still animates out.
|
|
81
|
+
*
|
|
82
|
+
* - Reka sets the body's `pointer-events: none` from the content's
|
|
83
|
+
* dismissable layer and from the backdrop's scroll lock, and clears it only
|
|
84
|
+
* once they have animated out and unmounted (about 140 to 230ms): a click
|
|
85
|
+
* on the page in the meantime was lost. From reka 2.11 the lock is an owner
|
|
86
|
+
* of the body's pointer events, which the content letting go of them does
|
|
87
|
+
* not clear. The body is released after the closing render, once reka has
|
|
88
|
+
* let go of it, unless another layer is still open and blocks the page.
|
|
89
|
+
* - A dialog closing over one that stays open (a confirm over a drawer)
|
|
90
|
+
* leaves the page blocked, but not the dialog below: reka disables every
|
|
91
|
+
* layer under the highest one that blocks the page, and the closing one
|
|
92
|
+
* stays the highest until it unmounts, so a click on the drawer was lost.
|
|
93
|
+
* The topmost dialog still open is marked uncovered until the closing one
|
|
94
|
+
* has unmounted or another one opens above it.
|
|
95
|
+
* - A click that opens another drawer or modal in the meantime keeps it
|
|
96
|
+
* open: the closing one gave the focus back to its trigger as it unmounted,
|
|
97
|
+
* outside the new one, which closed at once. Its focus stays put when a
|
|
98
|
+
* layer opened since it started closing; one closing over a layer that
|
|
99
|
+
* stays open (a confirm over a drawer) still gives it back. Reka's event
|
|
100
|
+
* does not bubble, so it is caught on its way down.
|
|
101
|
+
*
|
|
102
|
+
* Returns the function that stops watching.
|
|
103
|
+
* @internal
|
|
104
|
+
*/
|
|
105
|
+
export function watchClosingLayers(root: Document): () => void {
|
|
106
|
+
// Each closing layer and the layers that were open when it started.
|
|
107
|
+
const openBeforeClosing = new WeakMap<Element, Set<Element>>();
|
|
108
|
+
const openLayers = () => [...root.querySelectorAll(OPEN_LAYER_SELECTOR)];
|
|
109
|
+
const uncovering = watchUncovered(root);
|
|
110
|
+
|
|
111
|
+
const observer = new MutationObserver((mutations) => {
|
|
112
|
+
const closing = mutations
|
|
113
|
+
.filter(isClosing)
|
|
114
|
+
.map(({ target }) => target as Element);
|
|
115
|
+
if (closing.length === 0) return;
|
|
116
|
+
const open = openLayers();
|
|
117
|
+
for (const target of closing) {
|
|
118
|
+
openBeforeClosing.set(target, new Set(open));
|
|
119
|
+
}
|
|
120
|
+
uncovering.start(
|
|
121
|
+
closing.filter((target) => target.matches(LAYER_SELECTOR)),
|
|
122
|
+
);
|
|
123
|
+
if (open.length > 0) return;
|
|
124
|
+
if (root.body.style.pointerEvents === "none") {
|
|
125
|
+
root.body.style.pointerEvents = "";
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
observer.observe(root.body, {
|
|
129
|
+
subtree: true,
|
|
130
|
+
attributeFilter: ["data-state"],
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
const onUnmountAutoFocus = (event: Event): void => {
|
|
134
|
+
const before = openBeforeClosing.get(event.target as Element);
|
|
135
|
+
if (!before) return;
|
|
136
|
+
if (openLayers().some((layer) => !before.has(layer))) {
|
|
137
|
+
event.preventDefault();
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
root.addEventListener(UNMOUNT_AUTOFOCUS_EVENT, onUnmountAutoFocus, true);
|
|
141
|
+
|
|
142
|
+
return () => {
|
|
143
|
+
observer.disconnect();
|
|
144
|
+
uncovering.stop();
|
|
145
|
+
root.removeEventListener(UNMOUNT_AUTOFOCUS_EVENT, onUnmountAutoFocus, true);
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
interface Uncovering {
|
|
150
|
+
/** Marks the topmost dialog still open while `closing` animates out. */
|
|
151
|
+
start(closing: Element[]): void;
|
|
152
|
+
stop(): void;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function watchUncovered(root: Document): Uncovering {
|
|
156
|
+
let uncovered: Element | null = null;
|
|
157
|
+
let closingLayers: Element[] = [];
|
|
158
|
+
const topmostOpen = () =>
|
|
159
|
+
[...root.querySelectorAll(OPEN_DIALOG_SELECTOR)].at(-1) ?? null;
|
|
160
|
+
|
|
161
|
+
function stop(): void {
|
|
162
|
+
watcher.disconnect();
|
|
163
|
+
uncovered?.removeAttribute(UNCOVERED_ATTRIBUTE);
|
|
164
|
+
uncovered = null;
|
|
165
|
+
closingLayers = [];
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Only while a dialog closes over another: a short-lived watch of the DOM,
|
|
169
|
+
// until the closing one is gone or another one opened above.
|
|
170
|
+
const watcher = new MutationObserver(() => {
|
|
171
|
+
const isStillCovering = closingLayers.some((layer) => layer.isConnected);
|
|
172
|
+
if (!isStillCovering || topmostOpen() !== uncovered) stop();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
function start(closing: Element[]): void {
|
|
176
|
+
stop();
|
|
177
|
+
const top = closing.length > 0 ? topmostOpen() : null;
|
|
178
|
+
if (!top) return;
|
|
179
|
+
uncovered = top;
|
|
180
|
+
closingLayers = closing;
|
|
181
|
+
top.setAttribute(UNCOVERED_ATTRIBUTE, "");
|
|
182
|
+
watcher.observe(root.body, { childList: true, subtree: true });
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return { start, stop };
|
|
186
|
+
}
|
|
@@ -14,3 +14,17 @@ export interface DmsContainerContext {
|
|
|
14
14
|
/** Provided by DynamicDrawer and DynamicModal to their body. */
|
|
15
15
|
export const DMS_CONTAINER_KEY: InjectionKey<DmsContainerContext> =
|
|
16
16
|
Symbol("dms:container");
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Marks the options of a drawer the DMS opens itself (a row action's, a
|
|
20
|
+
* relation's, a table form's): it keeps the app's provides only, as before
|
|
21
|
+
* `useDrawer()` handed a module's drawer its caller's. A table or a form
|
|
22
|
+
* around the trigger must not reach the drawer's own form.
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
25
|
+
export const DMS_OWN_DRAWER = Symbol("dms:own-drawer");
|
|
26
|
+
|
|
27
|
+
/** Options of a drawer the DMS opens itself (see {@link DMS_OWN_DRAWER}). @internal */
|
|
28
|
+
export function dmsOwnDrawer<T extends object>(options: T): T {
|
|
29
|
+
return Object.assign(options, { [DMS_OWN_DRAWER]: true });
|
|
30
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { ref, type Component, type Ref } from "vue";
|
|
2
|
+
import type { DrawerDirection } from "../../../composables/containers/types";
|
|
3
|
+
import type { ModalSize } from "../../../types/modal";
|
|
4
|
+
import type { ResizeBounds, ResizeStorage } from "./useResizableSize";
|
|
5
|
+
|
|
6
|
+
/** @internal */
|
|
7
|
+
export const DRAWER_SIZE_COOKIE = "dms-drawer-sizes";
|
|
8
|
+
const DRAWER_SIZE_COOKIE_MAX_AGE = 60 * 60 * 24 * 365;
|
|
9
|
+
// A cookie travels with every request: the oldest sizes give way past this.
|
|
10
|
+
const MAX_STORED_SIZES = 40;
|
|
11
|
+
|
|
12
|
+
/** Width of a `left` / `right` drawer without `size`, from `lg`. @internal */
|
|
13
|
+
export const DEFAULT_SIDE_DRAWER_WIDTH = 600;
|
|
14
|
+
/** @internal */
|
|
15
|
+
export const MIN_SIDE_DRAWER_WIDTH = 360;
|
|
16
|
+
/** @internal */
|
|
17
|
+
export const MIN_EDGE_DRAWER_HEIGHT = 240;
|
|
18
|
+
// Share of the window a resized drawer may take, and the room it leaves (the
|
|
19
|
+
// drawer's own `max-w-[calc(100%-2rem)]`).
|
|
20
|
+
const MAX_DRAWER_SHARE = 0.9;
|
|
21
|
+
const DRAWER_WINDOW_MARGIN = 32;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* A modal's width at each size (see `DynamicModal`): the width a sized side
|
|
25
|
+
* drawer opens at. `full` takes the whole window.
|
|
26
|
+
*
|
|
27
|
+
* @internal
|
|
28
|
+
*/
|
|
29
|
+
export const DRAWER_SIZE_WIDTHS: Record<Exclude<ModalSize, "full">, number> = {
|
|
30
|
+
sm: 384,
|
|
31
|
+
md: 448,
|
|
32
|
+
lg: 512,
|
|
33
|
+
xl: 576,
|
|
34
|
+
"2xl": 672,
|
|
35
|
+
"3xl": 768,
|
|
36
|
+
"4xl": 896,
|
|
37
|
+
"5xl": 1024,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The sizes the user gave drawers on this device, per drawer key: widths of
|
|
42
|
+
* `left` / `right` drawers, heights of `top` / `bottom` ones.
|
|
43
|
+
*
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
46
|
+
export interface DrawerSizePreferences {
|
|
47
|
+
widths: Record<string, number>;
|
|
48
|
+
heights: Record<string, number>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type DrawerSizeAxis = keyof DrawerSizePreferences;
|
|
52
|
+
|
|
53
|
+
/** @internal */
|
|
54
|
+
export interface Viewport {
|
|
55
|
+
width: number;
|
|
56
|
+
height: number;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const SIDE_DIRECTIONS: ReadonlySet<DrawerDirection> = new Set([
|
|
60
|
+
"left",
|
|
61
|
+
"right",
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
/** @internal */
|
|
65
|
+
export function isSideDrawer(direction: DrawerDirection): boolean {
|
|
66
|
+
return SIDE_DIRECTIONS.has(direction);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function emptyPreferences(): DrawerSizePreferences {
|
|
70
|
+
return { widths: {}, heights: {} };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function readSizes(value: unknown): Record<string, number> {
|
|
74
|
+
if (typeof value !== "object" || value === null) return {};
|
|
75
|
+
return Object.fromEntries(
|
|
76
|
+
Object.entries(value).filter(
|
|
77
|
+
([, size]) => typeof size === "number" && Number.isFinite(size),
|
|
78
|
+
),
|
|
79
|
+
) as Record<string, number>;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Reads the cookie back defensively: an older or hand-edited value must not
|
|
84
|
+
* break a drawer.
|
|
85
|
+
*
|
|
86
|
+
* @internal
|
|
87
|
+
*/
|
|
88
|
+
export function readDrawerSizePreferences(
|
|
89
|
+
value: unknown,
|
|
90
|
+
): DrawerSizePreferences {
|
|
91
|
+
if (typeof value !== "object" || value === null) return emptyPreferences();
|
|
92
|
+
const record = value as Record<string, unknown>;
|
|
93
|
+
return {
|
|
94
|
+
widths: readSizes(record.widths),
|
|
95
|
+
heights: readSizes(record.heights),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** @internal */
|
|
100
|
+
export function useDrawerSizePreferences(): Ref<DrawerSizePreferences> {
|
|
101
|
+
return useDmsCookie<DrawerSizePreferences>(DRAWER_SIZE_COOKIE, {
|
|
102
|
+
default: emptyPreferences,
|
|
103
|
+
maxAge: DRAWER_SIZE_COOKIE_MAX_AGE,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// The key written last moves to the end, so the oldest ones are dropped
|
|
108
|
+
// first.
|
|
109
|
+
function withSize(
|
|
110
|
+
sizes: Record<string, number>,
|
|
111
|
+
key: string,
|
|
112
|
+
size: number | undefined,
|
|
113
|
+
): Record<string, number> {
|
|
114
|
+
const { [key]: _previous, ...others } = sizes;
|
|
115
|
+
const kept = Object.entries(others).slice(
|
|
116
|
+
-(MAX_STORED_SIZES - (size === undefined ? 0 : 1)),
|
|
117
|
+
);
|
|
118
|
+
return Object.fromEntries(size === undefined ? kept : [...kept, [key, size]]);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Where a drawer keeps the size the user gives it: under `key`, on the axis
|
|
123
|
+
* its direction resizes. A drawer without a key is resizable, but forgets.
|
|
124
|
+
*
|
|
125
|
+
* @internal
|
|
126
|
+
*/
|
|
127
|
+
export function drawerSizeStorage(
|
|
128
|
+
preferences: Ref<DrawerSizePreferences>,
|
|
129
|
+
key: string | undefined,
|
|
130
|
+
direction: DrawerDirection,
|
|
131
|
+
): ResizeStorage {
|
|
132
|
+
const axis: DrawerSizeAxis = isSideDrawer(direction) ? "widths" : "heights";
|
|
133
|
+
const unsaved = ref<number | undefined>();
|
|
134
|
+
if (!key) {
|
|
135
|
+
return {
|
|
136
|
+
read: () => unsaved.value,
|
|
137
|
+
write: (size) => {
|
|
138
|
+
unsaved.value = size;
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
read: () => readDrawerSizePreferences(preferences.value)[axis][key],
|
|
144
|
+
write: (size) => {
|
|
145
|
+
const current = readDrawerSizePreferences(preferences.value);
|
|
146
|
+
preferences.value = {
|
|
147
|
+
...current,
|
|
148
|
+
[axis]: withSize(current[axis], key, size),
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const COMPONENT_SIZE_KEY_PREFIX = "component:";
|
|
155
|
+
// The name `defineAsyncComponent` gives every wrapper: it names no drawer.
|
|
156
|
+
const ASYNC_WRAPPER_NAME = "AsyncComponentWrapper";
|
|
157
|
+
|
|
158
|
+
interface NamedComponent {
|
|
159
|
+
name?: string;
|
|
160
|
+
/** The file name a `<script setup>` component is compiled with. */
|
|
161
|
+
__name?: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* The key a drawer opened without `sizeKey` or `containerId` keeps its size
|
|
166
|
+
* under: its component's name, the same at every opening. Undefined for an
|
|
167
|
+
* anonymous component, which forgets.
|
|
168
|
+
*
|
|
169
|
+
* @internal
|
|
170
|
+
*/
|
|
171
|
+
export function componentSizeKey(component: Component): string | undefined {
|
|
172
|
+
const { name, __name } = component as NamedComponent;
|
|
173
|
+
const componentName = [name, __name].find(
|
|
174
|
+
(candidate) => candidate && candidate !== ASYNC_WRAPPER_NAME,
|
|
175
|
+
);
|
|
176
|
+
return componentName
|
|
177
|
+
? `${COMPONENT_SIZE_KEY_PREFIX}${componentName}`
|
|
178
|
+
: undefined;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function sideDrawerWidthBounds(
|
|
182
|
+
viewport: Viewport,
|
|
183
|
+
size?: ModalSize,
|
|
184
|
+
): ResizeBounds {
|
|
185
|
+
if (size === "full") {
|
|
186
|
+
return {
|
|
187
|
+
defaultSize: viewport.width,
|
|
188
|
+
minSize: Math.min(MIN_SIDE_DRAWER_WIDTH, viewport.width),
|
|
189
|
+
maxSize: viewport.width,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const maxSize = Math.max(
|
|
193
|
+
0,
|
|
194
|
+
Math.min(
|
|
195
|
+
viewport.width * MAX_DRAWER_SHARE,
|
|
196
|
+
viewport.width - DRAWER_WINDOW_MARGIN,
|
|
197
|
+
),
|
|
198
|
+
);
|
|
199
|
+
const minSize = Math.min(MIN_SIDE_DRAWER_WIDTH, maxSize);
|
|
200
|
+
const preferred = size ? DRAWER_SIZE_WIDTHS[size] : DEFAULT_SIDE_DRAWER_WIDTH;
|
|
201
|
+
return {
|
|
202
|
+
defaultSize: Math.min(maxSize, Math.max(minSize, preferred)),
|
|
203
|
+
minSize,
|
|
204
|
+
maxSize,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// A top or bottom drawer keeps its natural height until the user resizes it.
|
|
209
|
+
function edgeDrawerHeightBounds(viewport: Viewport): ResizeBounds {
|
|
210
|
+
const maxSize = Math.round(viewport.height * MAX_DRAWER_SHARE);
|
|
211
|
+
return { minSize: Math.min(MIN_EDGE_DRAWER_HEIGHT, maxSize), maxSize };
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* The sizes a drawer may take in this window: a side drawer's width (600px
|
|
216
|
+
* by default, or a modal's width at its `size`), an edge drawer's height
|
|
217
|
+
* (its natural one by default). Both stop at 90% of the window.
|
|
218
|
+
*
|
|
219
|
+
* @internal
|
|
220
|
+
*/
|
|
221
|
+
export function resolveDrawerSizeBounds(
|
|
222
|
+
direction: DrawerDirection,
|
|
223
|
+
viewport: Viewport,
|
|
224
|
+
size?: ModalSize,
|
|
225
|
+
): ResizeBounds {
|
|
226
|
+
return isSideDrawer(direction)
|
|
227
|
+
? sideDrawerWidthBounds(viewport, size)
|
|
228
|
+
: edgeDrawerHeightBounds(viewport);
|
|
229
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import { computed, ref, toValue, type MaybeRefOrGetter, type Ref } from "vue";
|
|
2
|
+
|
|
3
|
+
/** @internal */
|
|
4
|
+
export const RESIZE_KEYBOARD_STEP_PX = 16;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The edge of the window a resizable sheet is attached to: its handle sits on
|
|
8
|
+
* the opposite, inner edge. `left` / `right` sheets resize their width,
|
|
9
|
+
* `top` / `bottom` ones their height.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
export type ResizeSide = "left" | "right" | "top" | "bottom";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The sizes a sheet may take, in px. Without `defaultSize` the sheet keeps
|
|
17
|
+
* its natural size until the user resizes it, and a reset goes back to it.
|
|
18
|
+
*
|
|
19
|
+
* @internal
|
|
20
|
+
*/
|
|
21
|
+
export interface ResizeBounds {
|
|
22
|
+
defaultSize?: number;
|
|
23
|
+
minSize: number;
|
|
24
|
+
maxSize: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Where the size the user gives a sheet is kept: `write(undefined)` forgets
|
|
29
|
+
* it.
|
|
30
|
+
*
|
|
31
|
+
* @internal
|
|
32
|
+
*/
|
|
33
|
+
export interface ResizeStorage {
|
|
34
|
+
read: () => number | undefined;
|
|
35
|
+
write: (size: number | undefined) => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** @internal */
|
|
39
|
+
export interface ResizableSizeOptions {
|
|
40
|
+
side: ResizeSide;
|
|
41
|
+
bounds: MaybeRefOrGetter<ResizeBounds>;
|
|
42
|
+
storage: ResizeStorage;
|
|
43
|
+
/**
|
|
44
|
+
* The size while the handle is dragged (`null` at rest), for a caller that
|
|
45
|
+
* shares it with the rest of the page (the dashboard frame follows a side
|
|
46
|
+
* panel's handle). A local ref otherwise.
|
|
47
|
+
*/
|
|
48
|
+
liveSize?: Ref<number | null>;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** @internal */
|
|
52
|
+
export interface ResizableSize {
|
|
53
|
+
/** The size in px, `undefined` while the sheet keeps its natural size. */
|
|
54
|
+
size: Readonly<Ref<number | undefined>>;
|
|
55
|
+
bounds: Readonly<Ref<ResizeBounds>>;
|
|
56
|
+
isDragging: Readonly<Ref<boolean>>;
|
|
57
|
+
startDrag: (event: PointerEvent, renderedSize: number) => void;
|
|
58
|
+
drag: (event: PointerEvent) => void;
|
|
59
|
+
endDrag: () => void;
|
|
60
|
+
/** Arrow keys, Home and End; `renderedSize` stands for a natural size. */
|
|
61
|
+
resizeFromKey: (event: KeyboardEvent, renderedSize?: number) => void;
|
|
62
|
+
reset: () => void;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
type SizeStep = (size: number, bounds: ResizeBounds) => number;
|
|
66
|
+
|
|
67
|
+
const grow: SizeStep = (size) => size + RESIZE_KEYBOARD_STEP_PX;
|
|
68
|
+
const shrink: SizeStep = (size) => size - RESIZE_KEYBOARD_STEP_PX;
|
|
69
|
+
|
|
70
|
+
// The arrow pointing away from the sheet's edge grows it.
|
|
71
|
+
const KEYBOARD_STEPS: Record<ResizeSide, Record<string, SizeStep>> = {
|
|
72
|
+
right: { ArrowLeft: grow, ArrowRight: shrink },
|
|
73
|
+
left: { ArrowRight: grow, ArrowLeft: shrink },
|
|
74
|
+
bottom: { ArrowUp: grow, ArrowDown: shrink },
|
|
75
|
+
top: { ArrowDown: grow, ArrowUp: shrink },
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const BOUND_KEYS: Record<string, SizeStep> = {
|
|
79
|
+
Home: (_, bounds) => bounds.minSize,
|
|
80
|
+
End: (_, bounds) => bounds.maxSize,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
interface PointerAxis {
|
|
84
|
+
coordinate: (event: PointerEvent) => number;
|
|
85
|
+
/** +1 when moving the pointer along the axis grows the sheet. */
|
|
86
|
+
direction: 1 | -1;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const POINTER_AXES: Record<ResizeSide, PointerAxis> = {
|
|
90
|
+
right: { coordinate: (event) => event.clientX, direction: -1 },
|
|
91
|
+
left: { coordinate: (event) => event.clientX, direction: 1 },
|
|
92
|
+
bottom: { coordinate: (event) => event.clientY, direction: -1 },
|
|
93
|
+
top: { coordinate: (event) => event.clientY, direction: 1 },
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
interface DragStart {
|
|
97
|
+
position: number;
|
|
98
|
+
size: number;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** @internal */
|
|
102
|
+
export function clampSize(size: number, bounds: ResizeBounds): number {
|
|
103
|
+
return Math.round(Math.min(bounds.maxSize, Math.max(bounds.minSize, size)));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* The size of a sheet docked on one edge of the window (a side panel, a
|
|
108
|
+
* drawer) and the handlers of the resize handle on its inner edge: dragging,
|
|
109
|
+
* the arrow / Home / End keys, and a reset. The size is written to `storage`
|
|
110
|
+
* once a change settles; while the handle is dragged, `liveSize` holds it.
|
|
111
|
+
*
|
|
112
|
+
* @internal
|
|
113
|
+
*/
|
|
114
|
+
export function useResizableSize(options: ResizableSizeOptions): ResizableSize {
|
|
115
|
+
const { storage } = options;
|
|
116
|
+
const axis = POINTER_AXES[options.side];
|
|
117
|
+
const keySteps = { ...KEYBOARD_STEPS[options.side], ...BOUND_KEYS };
|
|
118
|
+
const liveSize = options.liveSize ?? ref<number | null>(null);
|
|
119
|
+
const bounds = computed(() => toValue(options.bounds));
|
|
120
|
+
const size = computed(() => {
|
|
121
|
+
if (liveSize.value !== null) return liveSize.value;
|
|
122
|
+
const stored = storage.read() ?? bounds.value.defaultSize;
|
|
123
|
+
return stored === undefined ? undefined : clampSize(stored, bounds.value);
|
|
124
|
+
});
|
|
125
|
+
let dragStart: DragStart | null = null;
|
|
126
|
+
|
|
127
|
+
function commit(next: number): void {
|
|
128
|
+
storage.write(clampSize(next, bounds.value));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function startDrag(event: PointerEvent, renderedSize: number): void {
|
|
132
|
+
event.preventDefault();
|
|
133
|
+
dragStart = { position: axis.coordinate(event), size: renderedSize };
|
|
134
|
+
liveSize.value = clampSize(renderedSize, bounds.value);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function drag(event: PointerEvent): void {
|
|
138
|
+
if (dragStart === null) return;
|
|
139
|
+
const moved = axis.coordinate(event) - dragStart.position;
|
|
140
|
+
const next = dragStart.size + moved * axis.direction;
|
|
141
|
+
liveSize.value = clampSize(next, bounds.value);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function endDrag(): void {
|
|
145
|
+
if (dragStart === null) return;
|
|
146
|
+
dragStart = null;
|
|
147
|
+
if (size.value !== undefined) commit(size.value);
|
|
148
|
+
liveSize.value = null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function resizeFromKey(event: KeyboardEvent, renderedSize?: number): void {
|
|
152
|
+
const step = keySteps[event.key];
|
|
153
|
+
const current = size.value ?? renderedSize;
|
|
154
|
+
if (!step || current === undefined) return;
|
|
155
|
+
event.preventDefault();
|
|
156
|
+
commit(step(current, bounds.value));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function reset(): void {
|
|
160
|
+
const { defaultSize } = bounds.value;
|
|
161
|
+
if (defaultSize === undefined) storage.write(undefined);
|
|
162
|
+
else commit(defaultSize);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
size,
|
|
167
|
+
bounds,
|
|
168
|
+
isDragging: computed(() => liveSize.value !== null),
|
|
169
|
+
startDrag,
|
|
170
|
+
drag,
|
|
171
|
+
endDrag,
|
|
172
|
+
resizeFromKey,
|
|
173
|
+
reset,
|
|
174
|
+
};
|
|
175
|
+
}
|