@antelopejs/dms 0.9.8 → 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 +102 -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 -48
- 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 +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/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
|
@@ -16,6 +16,10 @@ import {
|
|
|
16
16
|
numberAdjustment,
|
|
17
17
|
parseTypedNumber,
|
|
18
18
|
} from "../../../build/utils/numberAdjustment";
|
|
19
|
+
import {
|
|
20
|
+
type InputWidth,
|
|
21
|
+
isCompactNumberInput,
|
|
22
|
+
} from "../../../build/utils/inputWidth";
|
|
19
23
|
import type { InputNumberAdjustment } from "../../../types/input-number";
|
|
20
24
|
|
|
21
25
|
defineOptions({ inheritAttrs: false });
|
|
@@ -28,12 +32,23 @@ interface InputNumberFieldProps extends InputNumberProps {
|
|
|
28
32
|
* it as the field's error, instead of clamping it into range on blur.
|
|
29
33
|
*/
|
|
30
34
|
clamp?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* `"compact"` sizes the field for a short number from `sm` up, `"full"`
|
|
37
|
+
* fills the row; left out, a `max` of a few digits makes it compact.
|
|
38
|
+
*/
|
|
39
|
+
width?: InputWidth;
|
|
40
|
+
/**
|
|
41
|
+
* `true` puts a typed value on the nearest `step` when the field is left.
|
|
42
|
+
* Off by default: `step` only drives the steppers and the arrow keys, and
|
|
43
|
+
* a value is never rewritten (9,692 kept as 9,692 with steps of 100).
|
|
44
|
+
*/
|
|
45
|
+
stepSnapping?: boolean;
|
|
31
46
|
}
|
|
32
47
|
|
|
33
48
|
interface InputNumberAdjustEmits {
|
|
34
49
|
/**
|
|
35
50
|
* The field kept another value than the one typed, on leaving it or on
|
|
36
|
-
* Enter: clamped to `min` / `max`, or put on its `step
|
|
51
|
+
* Enter: clamped to `min` / `max`, or put on its `step` (`stepSnapping`).
|
|
37
52
|
*/
|
|
38
53
|
adjust: [adjustment: InputNumberAdjustment];
|
|
39
54
|
}
|
|
@@ -42,6 +57,7 @@ type InputNumberFieldEmits = InputNumberEmits & InputNumberAdjustEmits;
|
|
|
42
57
|
|
|
43
58
|
const props = withDefaults(defineProps<InputNumberFieldProps>(), {
|
|
44
59
|
clamp: true,
|
|
60
|
+
stepSnapping: false,
|
|
45
61
|
});
|
|
46
62
|
const emits = defineEmits<InputNumberFieldEmits>();
|
|
47
63
|
defineSlots<InputNumberSlots>();
|
|
@@ -54,6 +70,7 @@ const inputProps = computed(() => {
|
|
|
54
70
|
const {
|
|
55
71
|
suffix: _suffix,
|
|
56
72
|
clamp: _clamp,
|
|
73
|
+
width: _width,
|
|
57
74
|
onAdjust: _onAdjust,
|
|
58
75
|
...rest
|
|
59
76
|
} = forwarded.value;
|
|
@@ -64,12 +81,32 @@ const inputProps = computed(() => {
|
|
|
64
81
|
...numberStepperState(props.modelValue, bounds.value),
|
|
65
82
|
};
|
|
66
83
|
});
|
|
84
|
+
const isCompact = computed(() => isCompactNumberInput(props));
|
|
67
85
|
const { processI18n } = useTranslation();
|
|
68
86
|
const { locale } = useI18n();
|
|
69
87
|
|
|
70
88
|
// v2 number fields keep the value left-aligned with a compact caret stepper.
|
|
71
89
|
const DEFAULT_ORIENTATION = "vertical";
|
|
72
90
|
|
|
91
|
+
// The unit sits inside the box, before the stepper, so a field with a unit
|
|
92
|
+
// is as wide as the fields around it: the value's end padding makes room for
|
|
93
|
+
// the stepper (2rem, `end-8` below), a gap and the unit, sized in
|
|
94
|
+
// characters (`ch` slightly overestimates a short unit, never clips it).
|
|
95
|
+
const SUFFIX_BASE_CLASS = "pe-[calc(var(--dms-field-suffix-width)+2.375rem)]";
|
|
96
|
+
const suffixText = computed(() =>
|
|
97
|
+
props.suffix ? processI18n(props.suffix) : "",
|
|
98
|
+
);
|
|
99
|
+
const suffixStyle = computed(() =>
|
|
100
|
+
suffixText.value
|
|
101
|
+
? { "--dms-field-suffix-width": `${suffixText.value.length}ch` }
|
|
102
|
+
: undefined,
|
|
103
|
+
);
|
|
104
|
+
const fieldUi = computed(() =>
|
|
105
|
+
suffixText.value
|
|
106
|
+
? { ...props.ui, base: [props.ui?.base, SUFFIX_BASE_CLASS] }
|
|
107
|
+
: props.ui,
|
|
108
|
+
);
|
|
109
|
+
|
|
73
110
|
// A minus typed or pasted where no negative number is allowed is refused
|
|
74
111
|
// by the field before it shows: it says why, rather than leaving an emptied
|
|
75
112
|
// field to read as never filled.
|
|
@@ -134,10 +171,9 @@ async function settle(event: Event): Promise<void> {
|
|
|
134
171
|
</script>
|
|
135
172
|
|
|
136
173
|
<template>
|
|
137
|
-
<!-- The unit sits after the field rather than inside it, where the
|
|
138
|
-
stepper already takes the end of the box. -->
|
|
139
174
|
<div
|
|
140
|
-
:class="
|
|
175
|
+
:class="suffixText ? 'relative flex items-center' : 'contents'"
|
|
176
|
+
:style="suffixStyle"
|
|
141
177
|
@beforeinput.capture="onIncomingText"
|
|
142
178
|
@input="onTyped"
|
|
143
179
|
@keydown.enter="settle"
|
|
@@ -146,17 +182,18 @@ async function settle(event: Event): Promise<void> {
|
|
|
146
182
|
v-bind="{ ...inputProps, ...$attrs }"
|
|
147
183
|
@blur="settle"
|
|
148
184
|
@update:model-value="onSettledValue"
|
|
149
|
-
:class="
|
|
185
|
+
:class="[suffixText && 'min-w-0 flex-1', isCompact && 'sm:max-w-44']"
|
|
186
|
+
:ui="fieldUi"
|
|
150
187
|
:orientation="props.orientation ?? DEFAULT_ORIENTATION"
|
|
151
188
|
:increment="{ color: 'neutral' }"
|
|
152
189
|
:decrement="{ color: 'neutral' }"
|
|
153
190
|
/>
|
|
154
191
|
<span
|
|
155
|
-
v-if="
|
|
156
|
-
class="text-muted
|
|
192
|
+
v-if="suffixText"
|
|
193
|
+
class="text-muted pointer-events-none absolute inset-y-0 end-8 flex items-center text-[13px] whitespace-nowrap"
|
|
157
194
|
data-field-suffix
|
|
158
195
|
>
|
|
159
|
-
{{
|
|
196
|
+
{{ suffixText }}
|
|
160
197
|
</span>
|
|
161
198
|
</div>
|
|
162
199
|
</template>
|
|
@@ -4,10 +4,20 @@ import type {
|
|
|
4
4
|
InputNumberEmits,
|
|
5
5
|
InputNumberSlots,
|
|
6
6
|
} from "@nuxt/ui";
|
|
7
|
+
import { computed } from "vue";
|
|
8
|
+
import {
|
|
9
|
+
type InputWidth,
|
|
10
|
+
isCompactNumberInput,
|
|
11
|
+
} from "../../../build/utils/inputWidth";
|
|
12
|
+
|
|
13
|
+
interface InputPercentageProps extends InputNumberProps {
|
|
14
|
+
/** As on `InputNumber`; left out, the 0-100 bounds make it compact. */
|
|
15
|
+
width?: InputWidth;
|
|
16
|
+
}
|
|
7
17
|
|
|
8
18
|
const PERCENTAGE_MULTIPLIER = 100;
|
|
9
19
|
|
|
10
|
-
const props = defineProps<
|
|
20
|
+
const props = defineProps<InputPercentageProps>();
|
|
11
21
|
const emits = defineEmits<InputNumberEmits>();
|
|
12
22
|
defineSlots<InputNumberSlots>();
|
|
13
23
|
|
|
@@ -25,13 +35,23 @@ const displayValue = computed({
|
|
|
25
35
|
},
|
|
26
36
|
});
|
|
27
37
|
|
|
38
|
+
const bounds = computed(() => ({
|
|
39
|
+
min: props.min ?? 0,
|
|
40
|
+
max: props.max ?? PERCENTAGE_MULTIPLIER,
|
|
41
|
+
}));
|
|
42
|
+
const isCompact = computed(() =>
|
|
43
|
+
isCompactNumberInput({ width: props.width, ...bounds.value }),
|
|
44
|
+
);
|
|
45
|
+
|
|
28
46
|
const forwardedProps = computed(() => {
|
|
47
|
+
const { width: _width, ...rest } = props;
|
|
29
48
|
return {
|
|
30
|
-
...
|
|
49
|
+
...rest,
|
|
50
|
+
...bounds.value,
|
|
31
51
|
modelValue: displayValue.value,
|
|
32
|
-
min: props.min ?? 0,
|
|
33
|
-
max: props.max ?? PERCENTAGE_MULTIPLIER,
|
|
34
52
|
step: props.step ?? 0.01,
|
|
53
|
+
// The step drives the steppers only: a stored 12.345 % stays as it is.
|
|
54
|
+
stepSnapping: props.stepSnapping ?? false,
|
|
35
55
|
// The wrapper takes the caller's class, so the field fills it.
|
|
36
56
|
class: "w-full",
|
|
37
57
|
ui: { ...props.ui, base: ["pe-7", props.ui?.base] },
|
|
@@ -40,7 +60,10 @@ const forwardedProps = computed(() => {
|
|
|
40
60
|
</script>
|
|
41
61
|
|
|
42
62
|
<template>
|
|
43
|
-
<div
|
|
63
|
+
<div
|
|
64
|
+
class="relative inline-flex"
|
|
65
|
+
:class="[props.class, isCompact && 'sm:max-w-44']"
|
|
66
|
+
>
|
|
44
67
|
<UInputNumber
|
|
45
68
|
v-bind="forwardedProps"
|
|
46
69
|
@update:model-value="displayValue = $event"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { pageRequestUrl } from "../../../build/composables/page/pageUrlValues";
|
|
3
3
|
import { useConfirm } from "../../../composables/confirm/useConfirm";
|
|
4
|
+
import { FORM_FIELD_SAVED_KEY } from "../../../composables/form/types/field-saved";
|
|
4
5
|
import DmsCopyButton from "../../copy/CopyButton.vue";
|
|
5
6
|
|
|
6
7
|
interface InputSecretProps {
|
|
@@ -23,6 +24,7 @@ const { t } = useI18n();
|
|
|
23
24
|
const toast = useToast();
|
|
24
25
|
const { confirm } = useConfirm();
|
|
25
26
|
const { $authFetch } = useAuthFetch();
|
|
27
|
+
const markFieldSaved = inject(FORM_FIELD_SAVED_KEY, null);
|
|
26
28
|
|
|
27
29
|
// v2 .secret: the end of the value stays visible, so a reader can tell two
|
|
28
30
|
// secrets apart without revealing either.
|
|
@@ -56,6 +58,9 @@ async function rotate(): Promise<void> {
|
|
|
56
58
|
method: "POST",
|
|
57
59
|
});
|
|
58
60
|
model.value = response.value;
|
|
61
|
+
// The server stored the new secret already: it is not a change to
|
|
62
|
+
// save, and "Discard" must not bring the old one back.
|
|
63
|
+
if (props.id) markFieldSaved?.(props.id, response.value);
|
|
59
64
|
},
|
|
60
65
|
});
|
|
61
66
|
if (confirmed) {
|
|
@@ -7,12 +7,23 @@ import type {
|
|
|
7
7
|
import { computed } from "vue";
|
|
8
8
|
import { useForwardPropsEmits } from "reka-ui";
|
|
9
9
|
import { characterCountText } from "../../../build/utils/characterCount";
|
|
10
|
+
import {
|
|
11
|
+
type InputWidth,
|
|
12
|
+
isCompactTextInput,
|
|
13
|
+
} from "../../../build/utils/inputWidth";
|
|
10
14
|
|
|
11
15
|
interface InputTextProps extends InputProps {
|
|
12
16
|
/** Longest value accepted; the counter reads against it. */
|
|
13
17
|
maxLength?: number;
|
|
14
18
|
/** Shows the characters typed at the end of the field ("4 / 60"). */
|
|
15
19
|
showCount?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* `"compact"` sizes the field for a short text from `sm` up, `"full"`
|
|
22
|
+
* fills the row; left out, a `maxLength` of 16 or less makes it compact.
|
|
23
|
+
*/
|
|
24
|
+
width?: InputWidth;
|
|
25
|
+
/** How the value reads shown, not edited (a URL's image): not the input's. */
|
|
26
|
+
preview?: "avatar" | "image";
|
|
16
27
|
}
|
|
17
28
|
|
|
18
29
|
const props = defineProps<InputTextProps>();
|
|
@@ -21,16 +32,22 @@ defineSlots<InputSlots>();
|
|
|
21
32
|
|
|
22
33
|
const forwarded = useForwardPropsEmits(props, emits);
|
|
23
34
|
const inputProps = computed(() => {
|
|
24
|
-
const {
|
|
35
|
+
const {
|
|
36
|
+
showCount: _showCount,
|
|
37
|
+
width: _width,
|
|
38
|
+
preview: _preview,
|
|
39
|
+
...rest
|
|
40
|
+
} = forwarded.value;
|
|
25
41
|
return rest;
|
|
26
42
|
});
|
|
43
|
+
const isCompact = computed(() => isCompactTextInput(props));
|
|
27
44
|
const countText = computed(() =>
|
|
28
45
|
characterCountText(props.modelValue, props.maxLength),
|
|
29
46
|
);
|
|
30
47
|
</script>
|
|
31
48
|
|
|
32
49
|
<template>
|
|
33
|
-
<UInput v-bind="inputProps">
|
|
50
|
+
<UInput v-bind="inputProps" :class="isCompact && 'sm:max-w-44'">
|
|
34
51
|
<template v-if="props.showCount" #trailing>
|
|
35
52
|
<span class="text-dimmed text-xs tabular-nums" data-character-count>
|
|
36
53
|
{{ countText }}
|
|
@@ -10,6 +10,7 @@ interface InputTimeProps extends Omit<InputProps, "modelValue" | "type"> {
|
|
|
10
10
|
|
|
11
11
|
const props = defineProps<InputTimeProps>();
|
|
12
12
|
const { report } = useControlError();
|
|
13
|
+
const { locale } = useI18n();
|
|
13
14
|
const UNREADABLE_DURATION = "$dms.field_errors.invalid_duration";
|
|
14
15
|
const emits = defineEmits<{
|
|
15
16
|
"update:modelValue": [value: number | null];
|
|
@@ -21,7 +22,7 @@ const isUserTyping = ref(false);
|
|
|
21
22
|
const updateDisplayValue = () => {
|
|
22
23
|
if (isUserTyping.value) return;
|
|
23
24
|
if (props.modelValue !== undefined && props.modelValue !== null) {
|
|
24
|
-
displayValue.value =
|
|
25
|
+
displayValue.value = formatDuration(props.modelValue, locale.value);
|
|
25
26
|
} else {
|
|
26
27
|
displayValue.value = "";
|
|
27
28
|
}
|
|
@@ -16,11 +16,24 @@ import type { ArrayOrNested } from "@nuxt/ui/runtime/types/utils.js";
|
|
|
16
16
|
|
|
17
17
|
import { refDebounced } from "@vueuse/core";
|
|
18
18
|
import DmsForm from "../Form.vue";
|
|
19
|
+
import { dmsOwnDrawer } from "../../../build/composables/containers/context";
|
|
19
20
|
import { FIELD_TRIGGER_ICON } from "../../../build/utils/fieldTrigger";
|
|
21
|
+
import { unlabeledRelationIds } from "../../../build/composables/data-types/relationValue";
|
|
22
|
+
import { useRelationRows } from "../../../build/composables/data-types/relationRows";
|
|
20
23
|
import type { FormProps } from "../../../composables/form/types";
|
|
24
|
+
import { sameFormValue } from "../../../build/composables/unsaved-changes/formValue";
|
|
25
|
+
import { FORM_ENTRY_CONTEXT_KEY } from "../../../build/composables/form/formEntryContext";
|
|
26
|
+
import {
|
|
27
|
+
type RelationFilterBy,
|
|
28
|
+
relationFilterParams,
|
|
29
|
+
resolveRelationPick,
|
|
30
|
+
} from "../../../build/composables/data-types/relationFilter";
|
|
21
31
|
|
|
22
32
|
const SEARCH_DEBOUNCE_MS = 200;
|
|
23
|
-
|
|
33
|
+
// How long the menu's content gets to leave the page before the add form opens
|
|
34
|
+
// anyway, and the beat its focus return takes once it has.
|
|
35
|
+
const MENU_CLOSE_MAX_MS = 1000;
|
|
36
|
+
const MENU_FOCUS_SETTLE_MS = 60;
|
|
24
37
|
|
|
25
38
|
/** Title and description of the drawer a relation picker's "add" opens. */
|
|
26
39
|
interface RelationAddFormTexts {
|
|
@@ -38,6 +51,11 @@ interface RelationProps extends SelectMenuProps<T, "value", M> {
|
|
|
38
51
|
avatar?: string;
|
|
39
52
|
disabled?: string;
|
|
40
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* Lists only the related rows whose `field` equals the value of the form
|
|
56
|
+
* field `from`; ignored outside a form (a filter's picker).
|
|
57
|
+
*/
|
|
58
|
+
filterBy?: RelationFilterBy;
|
|
41
59
|
addForm?: { componentName: string; options?: FormProps };
|
|
42
60
|
addPermissionIds?: string[];
|
|
43
61
|
/**
|
|
@@ -63,33 +81,24 @@ const forwardedProps = computed(() => {
|
|
|
63
81
|
addForm: ______,
|
|
64
82
|
addPermissionIds: _______,
|
|
65
83
|
addFormTexts: ________,
|
|
84
|
+
filterBy: _________,
|
|
66
85
|
...rest
|
|
67
86
|
} = props;
|
|
68
87
|
return rest as SelectMenuProps<T, "value", M>;
|
|
69
88
|
});
|
|
70
|
-
const
|
|
89
|
+
const forwardedPropsEmits = useForwardPropsEmits(forwardedProps, emits);
|
|
90
|
+
// The picks go through `handleUpdateModelValue` alone, which deselects a value
|
|
91
|
+
// picked again: forwarded as well, the form got the pick it had undone.
|
|
92
|
+
const forwarded = computed(() => {
|
|
93
|
+
const { "onUpdate:modelValue": _, ...rest } = forwardedPropsEmits.value as {
|
|
94
|
+
"onUpdate:modelValue"?: unknown;
|
|
95
|
+
};
|
|
96
|
+
return rest as typeof forwardedPropsEmits.value;
|
|
97
|
+
});
|
|
71
98
|
|
|
72
99
|
type ModelValue = SelectMenuEmits<T, "value", M>["update:modelValue"][0];
|
|
73
100
|
|
|
74
|
-
function isAddNewValue(value: unknown): boolean {
|
|
75
|
-
if (value === ADD_NEW_VALUE) return true;
|
|
76
|
-
if (Array.isArray(value)) return value.includes(ADD_NEW_VALUE);
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
101
|
function handleUpdateModelValue(value: ModelValue) {
|
|
81
|
-
if (isAddNewValue(value)) {
|
|
82
|
-
openAddDrawer();
|
|
83
|
-
if (Array.isArray(value)) {
|
|
84
|
-
emits(
|
|
85
|
-
"update:modelValue",
|
|
86
|
-
(value as unknown[]).filter((v) => v !== ADD_NEW_VALUE) as ModelValue,
|
|
87
|
-
);
|
|
88
|
-
} else {
|
|
89
|
-
emits("update:modelValue", props.modelValue as ModelValue);
|
|
90
|
-
}
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
102
|
const shouldDeselect =
|
|
94
103
|
props.deselectable && !props.multiple && value === props.modelValue;
|
|
95
104
|
emits(
|
|
@@ -103,7 +112,23 @@ const instanceId = useId();
|
|
|
103
112
|
const searchTerm = ref("");
|
|
104
113
|
const searchTermDebounced = refDebounced(searchTerm, SEARCH_DEBOUNCE_MS);
|
|
105
114
|
|
|
115
|
+
// The form this picker fills, whose `filterBy.from` field narrows it.
|
|
116
|
+
const formContext = inject(FORM_ENTRY_CONTEXT_KEY, null);
|
|
117
|
+
const filterSourceValue = computed(() =>
|
|
118
|
+
props.filterBy && formContext
|
|
119
|
+
? formContext.state.value[props.filterBy.from]
|
|
120
|
+
: undefined,
|
|
121
|
+
);
|
|
122
|
+
const filterParams = computed(() =>
|
|
123
|
+
relationFilterParams(props.filterBy, filterSourceValue.value),
|
|
124
|
+
);
|
|
125
|
+
// A string, so a recomputed but equal filter does not search again.
|
|
126
|
+
const filterKey = computed(() => JSON.stringify(filterParams.value));
|
|
127
|
+
// The source value the record loaded with, which its loaded pick matches.
|
|
128
|
+
const loadedFilterSource = filterSourceValue.value;
|
|
129
|
+
|
|
106
130
|
const { $authFetch } = useAuthFetch();
|
|
131
|
+
const { readRelationRows } = useRelationRows();
|
|
107
132
|
const toast = useToast();
|
|
108
133
|
const { t } = useI18n();
|
|
109
134
|
const { processI18n } = useTranslation();
|
|
@@ -118,6 +143,9 @@ const addContainerId = `relation-add-${useId()}`;
|
|
|
118
143
|
// unwrapped item is an unbounded type for the checker. The list is always
|
|
119
144
|
// replaced, never mutated in place.
|
|
120
145
|
const locallyAddedItems: Ref<SelectMenuItem[]> = shallowRef([]);
|
|
146
|
+
// The rows of the ids the value holds, read from the `select` route when the
|
|
147
|
+
// form's data answers the ids rather than the joined rows.
|
|
148
|
+
const valueItems: Ref<SelectMenuItem[]> = shallowRef([]);
|
|
121
149
|
|
|
122
150
|
const canAdd = computed(
|
|
123
151
|
() =>
|
|
@@ -197,6 +225,7 @@ async function fetchRelationItems(): Promise<RelationItems> {
|
|
|
197
225
|
const data = await $authFetch<RelationResponse>(url, {
|
|
198
226
|
params: {
|
|
199
227
|
search: searchTermDebounced.value || undefined,
|
|
228
|
+
...filterParams.value,
|
|
200
229
|
},
|
|
201
230
|
onRequestError: ({ error }: { error: Error }) => {
|
|
202
231
|
toast.add({
|
|
@@ -234,7 +263,7 @@ const { data, status, execute } = await useDmsAsyncData(
|
|
|
234
263
|
{
|
|
235
264
|
lazy: true,
|
|
236
265
|
immediate: false,
|
|
237
|
-
watch: [searchTermDebounced],
|
|
266
|
+
watch: [searchTermDebounced, filterKey],
|
|
238
267
|
// Options to pick from, not the page's data.
|
|
239
268
|
refreshOnPageRefresh: false,
|
|
240
269
|
},
|
|
@@ -246,6 +275,8 @@ function isMappableObject(value: unknown): value is Record<string, string> {
|
|
|
246
275
|
|
|
247
276
|
const initialItems = computed(() => {
|
|
248
277
|
if (!props.initialValue) return [];
|
|
278
|
+
// The loaded row matched the loaded source value, not another one.
|
|
279
|
+
if (!sameFormValue(filterSourceValue.value, loadedFilterSource)) return [];
|
|
249
280
|
|
|
250
281
|
const values = Array.isArray(props.initialValue)
|
|
251
282
|
? props.initialValue
|
|
@@ -254,42 +285,28 @@ const initialItems = computed(() => {
|
|
|
254
285
|
return values.filter(isMappableObject).map(mapData);
|
|
255
286
|
});
|
|
256
287
|
|
|
288
|
+
function itemValue(item: SelectMenuItem): unknown {
|
|
289
|
+
return typeof item === "object" && item !== null && "value" in item
|
|
290
|
+
? item.value
|
|
291
|
+
: item;
|
|
292
|
+
}
|
|
293
|
+
|
|
257
294
|
const mergedItems = computed<SelectMenuItem[]>(() => {
|
|
258
295
|
const searchResults = data.value?.results || [];
|
|
259
296
|
const initial = initialItems.value || [];
|
|
260
297
|
const added = locallyAddedItems.value;
|
|
261
298
|
|
|
262
299
|
const merged: SelectMenuItem[] = [...initial, ...added];
|
|
263
|
-
const existingValues = new Set(
|
|
264
|
-
merged.map((item) =>
|
|
265
|
-
typeof item === "object" && item !== null && "value" in item
|
|
266
|
-
? item.value
|
|
267
|
-
: item,
|
|
268
|
-
),
|
|
269
|
-
);
|
|
270
|
-
|
|
271
|
-
searchResults.forEach((item) => {
|
|
272
|
-
const itemValue =
|
|
273
|
-
typeof item === "object" && item !== null && "value" in item
|
|
274
|
-
? item.value
|
|
275
|
-
: item;
|
|
300
|
+
const existingValues = new Set(merged.map(itemValue));
|
|
276
301
|
|
|
277
|
-
|
|
302
|
+
[...valueItems.value, ...searchResults].forEach((item) => {
|
|
303
|
+
const value = itemValue(item);
|
|
304
|
+
if (!existingValues.has(value)) {
|
|
305
|
+
existingValues.add(value);
|
|
278
306
|
merged.push(item);
|
|
279
307
|
}
|
|
280
308
|
});
|
|
281
309
|
|
|
282
|
-
if (canAdd.value) {
|
|
283
|
-
if (merged.length > 0) {
|
|
284
|
-
merged.push({ type: "separator" });
|
|
285
|
-
}
|
|
286
|
-
merged.push({
|
|
287
|
-
label: t("dms.form.relation.add_new"),
|
|
288
|
-
value: ADD_NEW_VALUE,
|
|
289
|
-
icon: "i-ph-plus",
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
|
|
293
310
|
return merged;
|
|
294
311
|
});
|
|
295
312
|
|
|
@@ -326,34 +343,139 @@ watch(
|
|
|
326
343
|
{ immediate: true },
|
|
327
344
|
);
|
|
328
345
|
|
|
346
|
+
// The pick as the form holds it, read alongside the source field: a record
|
|
347
|
+
// loading both values changes them in the same tick.
|
|
348
|
+
const pickValue = computed(() =>
|
|
349
|
+
formContext && props.id
|
|
350
|
+
? formContext.state.value[props.id]
|
|
351
|
+
: props.modelValue,
|
|
352
|
+
);
|
|
353
|
+
let pickSource = loadedFilterSource;
|
|
354
|
+
|
|
355
|
+
// A pick made for another value of the source field is void once it changes.
|
|
356
|
+
watch(
|
|
357
|
+
[filterSourceValue, pickValue],
|
|
358
|
+
([source, pick], [prevSource, prevPick]) => {
|
|
359
|
+
if (!props.filterBy || !formContext) return;
|
|
360
|
+
const outcome = resolveRelationPick({
|
|
361
|
+
source,
|
|
362
|
+
pick,
|
|
363
|
+
previousSource: prevSource,
|
|
364
|
+
previousPick: prevPick,
|
|
365
|
+
pickSource,
|
|
366
|
+
});
|
|
367
|
+
pickSource = outcome.pickSource;
|
|
368
|
+
if (!outcome.shouldClear) return;
|
|
369
|
+
emits("update:modelValue", (props.multiple ? [] : undefined) as ModelValue);
|
|
370
|
+
},
|
|
371
|
+
);
|
|
372
|
+
|
|
373
|
+
const isMenuOpen = ref(false);
|
|
374
|
+
|
|
375
|
+
const slots = useSlots();
|
|
376
|
+
// `content-bottom` is filled below, after the add entry.
|
|
377
|
+
const forwardedSlotNames = computed(() =>
|
|
378
|
+
Object.keys(slots).filter((name) => name !== "content-bottom"),
|
|
379
|
+
);
|
|
380
|
+
|
|
381
|
+
const labeledIds = computed(
|
|
382
|
+
() => new Set(mergedItems.value.map((item) => String(itemValue(item)))),
|
|
383
|
+
);
|
|
384
|
+
// Asked once each: an id the route does not answer (deleted, out of reach)
|
|
385
|
+
// keeps showing as is rather than being asked again.
|
|
386
|
+
const requestedIds = new Set<string>();
|
|
387
|
+
|
|
388
|
+
async function loadValueItems(ids: string[]) {
|
|
389
|
+
for (const id of ids) requestedIds.add(id);
|
|
390
|
+
const rows = await readRelationRows(
|
|
391
|
+
{ searchUrl: props.searchUrl, keyMapping: props.keyMapping },
|
|
392
|
+
ids,
|
|
393
|
+
);
|
|
394
|
+
if (!rows.length) return;
|
|
395
|
+
valueItems.value = [
|
|
396
|
+
...valueItems.value,
|
|
397
|
+
...rows.map((item) => mapData(item as Record<string, string>)),
|
|
398
|
+
];
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// The picker shows a value through its item: an id no item carries yet is
|
|
402
|
+
// labelled (and given its avatar) from its row before the picker is opened.
|
|
403
|
+
onMounted(() => {
|
|
404
|
+
watch(
|
|
405
|
+
() =>
|
|
406
|
+
unlabeledRelationIds(props.modelValue, labeledIds.value).filter(
|
|
407
|
+
(id) => !requestedIds.has(id),
|
|
408
|
+
),
|
|
409
|
+
(ids) => {
|
|
410
|
+
if (ids.length > 0) void loadValueItems(ids);
|
|
411
|
+
},
|
|
412
|
+
{ immediate: true },
|
|
413
|
+
);
|
|
414
|
+
});
|
|
415
|
+
|
|
329
416
|
function onOpen(isOpen: boolean) {
|
|
417
|
+
isMenuOpen.value = isOpen;
|
|
330
418
|
if (isOpen && !data.value?.results?.length) {
|
|
331
419
|
execute();
|
|
332
420
|
}
|
|
333
421
|
}
|
|
334
422
|
|
|
423
|
+
const addEntryRef = useTemplateRef<HTMLElement>("addEntry");
|
|
424
|
+
|
|
425
|
+
function waitForDetached(element: HTMLElement | null): Promise<void> {
|
|
426
|
+
const startedAt = performance.now();
|
|
427
|
+
return new Promise((resolve) => {
|
|
428
|
+
const check = () => {
|
|
429
|
+
if (
|
|
430
|
+
!element?.isConnected ||
|
|
431
|
+
performance.now() - startedAt > MENU_CLOSE_MAX_MS
|
|
432
|
+
) {
|
|
433
|
+
resolve();
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
requestAnimationFrame(check);
|
|
437
|
+
};
|
|
438
|
+
check();
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Opens the add form once the menu has left the page: its content animates
|
|
444
|
+
* out, then hands the focus back to the field, and a drawer opened before
|
|
445
|
+
* that took the focus leaving it for a click outside and closed at once.
|
|
446
|
+
*/
|
|
447
|
+
async function openAddDrawerOnceClosed(): Promise<void> {
|
|
448
|
+
const menuContent = addEntryRef.value;
|
|
449
|
+
isMenuOpen.value = false;
|
|
450
|
+
await waitForDetached(menuContent);
|
|
451
|
+
await new Promise((resolve) => setTimeout(resolve, MENU_FOCUS_SETTLE_MS));
|
|
452
|
+
openAddDrawer();
|
|
453
|
+
}
|
|
454
|
+
|
|
335
455
|
function openAddDrawer() {
|
|
336
456
|
const formOptions = addFormOptions.value;
|
|
337
457
|
if (!formOptions) return;
|
|
338
|
-
const drawer = openDrawer(
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
458
|
+
const drawer = openDrawer(
|
|
459
|
+
dmsOwnDrawer({
|
|
460
|
+
title: props.addFormTexts?.displayName
|
|
461
|
+
? processI18n(props.addFormTexts.displayName)
|
|
462
|
+
: t("dms.table.new_item"),
|
|
463
|
+
description: props.addFormTexts?.description
|
|
464
|
+
? processI18n(props.addFormTexts.description)
|
|
465
|
+
: t("dms.table.new_item_description"),
|
|
466
|
+
direction: "bottom",
|
|
467
|
+
containerId: addContainerId,
|
|
468
|
+
component: DmsForm,
|
|
469
|
+
componentOptions: {
|
|
470
|
+
...formOptions,
|
|
471
|
+
onSuccessCallback: (response: unknown, formData: unknown) => {
|
|
472
|
+
clearGuards(addContainerId);
|
|
473
|
+
drawer.close();
|
|
474
|
+
handleAddSuccess(response, formData);
|
|
475
|
+
},
|
|
354
476
|
},
|
|
355
|
-
},
|
|
356
|
-
|
|
477
|
+
}),
|
|
478
|
+
);
|
|
357
479
|
}
|
|
358
480
|
|
|
359
481
|
function extractInsertedId(response: unknown): string | undefined {
|
|
@@ -428,20 +550,38 @@ const addFormOptions = computed(() => {
|
|
|
428
550
|
v-model:search-term="searchTerm"
|
|
429
551
|
:trailing-icon="props.trailingIcon ?? FIELD_TRIGGER_ICON"
|
|
430
552
|
:model-value="props.modelValue"
|
|
553
|
+
:open="isMenuOpen"
|
|
431
554
|
:items="displayItems as T"
|
|
432
555
|
:loading="status === 'pending'"
|
|
433
556
|
value-key="value"
|
|
434
557
|
:portal="false"
|
|
435
558
|
:ignore-filter="true"
|
|
436
559
|
:ui="{ content: 'z-50' }"
|
|
560
|
+
:aria-label="t('dms.a11y.show_options')"
|
|
437
561
|
@update:open="onOpen"
|
|
438
562
|
@update:model-value="handleUpdateModelValue"
|
|
439
563
|
>
|
|
440
|
-
<template v-for="
|
|
564
|
+
<template v-for="name in forwardedSlotNames" :key="name" #[name]="slotData">
|
|
441
565
|
<slot
|
|
442
566
|
:name="name as keyof SelectMenuSlots<T, 'value', M>"
|
|
443
567
|
v-bind="slotData ?? {}"
|
|
444
568
|
/>
|
|
445
569
|
</template>
|
|
570
|
+
<!-- The add entry is no item: picked, it became the field's value. -->
|
|
571
|
+
<template v-if="canAdd || $slots['content-bottom']" #content-bottom>
|
|
572
|
+
<div v-if="canAdd" ref="addEntry" class="border-default border-t p-1">
|
|
573
|
+
<UButton
|
|
574
|
+
:label="t('dms.form.relation.add_new')"
|
|
575
|
+
icon="i-ph-plus"
|
|
576
|
+
color="neutral"
|
|
577
|
+
variant="ghost"
|
|
578
|
+
size="sm"
|
|
579
|
+
block
|
|
580
|
+
class="justify-start"
|
|
581
|
+
@click="openAddDrawerOnceClosed"
|
|
582
|
+
/>
|
|
583
|
+
</div>
|
|
584
|
+
<slot name="content-bottom" />
|
|
585
|
+
</template>
|
|
446
586
|
</USelectMenu>
|
|
447
587
|
</template>
|