@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.
Files changed (420) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/README.md +1 -1
  3. package/data/module-store/catalog.json +2 -2
  4. package/dist/attachments/table-view.js +2 -2
  5. package/dist/attachments/table-view.js.map +1 -1
  6. package/dist/automation/actions.d.ts +2 -0
  7. package/dist/automation/actions.js +116 -71
  8. package/dist/automation/actions.js.map +1 -1
  9. package/dist/automation/i18n.d.ts +4 -0
  10. package/dist/automation/i18n.js +19 -0
  11. package/dist/automation/i18n.js.map +1 -0
  12. package/dist/automation/send-notification.d.ts +21 -0
  13. package/dist/automation/send-notification.js +163 -0
  14. package/dist/automation/send-notification.js.map +1 -0
  15. package/dist/automation/triggers.js +19 -25
  16. package/dist/automation/triggers.js.map +1 -1
  17. package/dist/client-origin-cors.d.ts +1 -0
  18. package/dist/client-origin-cors.js +19 -0
  19. package/dist/client-origin-cors.js.map +1 -0
  20. package/dist/config.d.ts +3 -0
  21. package/dist/config.js +5 -3
  22. package/dist/config.js.map +1 -1
  23. package/dist/frontend-module.d.ts +4 -0
  24. package/dist/frontend-module.js +37 -0
  25. package/dist/frontend-module.js.map +1 -0
  26. package/dist/implementations/dms/client-ip.js +1 -1
  27. package/dist/implementations/dms/client-ip.js.map +1 -1
  28. package/dist/implementations/dms/dev-reload.d.ts +5 -0
  29. package/dist/implementations/dms/dev-reload.js +23 -3
  30. package/dist/implementations/dms/dev-reload.js.map +1 -1
  31. package/dist/implementations/dms/frontend-modules.d.ts +2 -4
  32. package/dist/implementations/dms/frontend-modules.js +22 -89
  33. package/dist/implementations/dms/frontend-modules.js.map +1 -1
  34. package/dist/implementations/dms/frontend-ready.d.ts +12 -2
  35. package/dist/implementations/dms/frontend-ready.js +34 -4
  36. package/dist/implementations/dms/frontend-ready.js.map +1 -1
  37. package/dist/implementations/dms/layout-banners.d.ts +2 -1
  38. package/dist/implementations/dms/layout-banners.js +9 -0
  39. package/dist/implementations/dms/layout-banners.js.map +1 -1
  40. package/dist/implementations/dms/manifest.d.ts +1 -2
  41. package/dist/implementations/dms/manifest.js +2 -0
  42. package/dist/implementations/dms/manifest.js.map +1 -1
  43. package/dist/implementations/dms/page.d.ts +4 -3
  44. package/dist/implementations/dms/page.js +49 -18
  45. package/dist/implementations/dms/page.js.map +1 -1
  46. package/dist/implementations/dms/permissions-resolver.d.ts +2 -2
  47. package/dist/implementations/dms/permissions-resolver.js +2 -2
  48. package/dist/implementations/dms/permissions-resolver.js.map +1 -1
  49. package/dist/implementations/dms/realtime.js +1 -0
  50. package/dist/implementations/dms/realtime.js.map +1 -1
  51. package/dist/implementations/dms-base/bulk-selection.d.ts +1 -1
  52. package/dist/implementations/dms-base/bulk-selection.js +23 -1
  53. package/dist/implementations/dms-base/bulk-selection.js.map +1 -1
  54. package/dist/implementations/dms-base/filter-tokens.js +5 -3
  55. package/dist/implementations/dms-base/filter-tokens.js.map +1 -1
  56. package/dist/implementations/dms-base/search-route.d.ts +1 -0
  57. package/dist/implementations/dms-base/search-route.js +35 -13
  58. package/dist/implementations/dms-base/search-route.js.map +1 -1
  59. package/dist/implementations/dms-base/table-view.js +11 -6
  60. package/dist/implementations/dms-base/table-view.js.map +1 -1
  61. package/dist/implementations/dms-notifications/index.d.ts +2 -2
  62. package/dist/implementations/dms-notifications/index.js +5 -5
  63. package/dist/implementations/dms-notifications/index.js.map +1 -1
  64. package/dist/index.js +28 -48
  65. package/dist/index.js.map +1 -1
  66. package/dist/pages/modules/index.d.ts +1 -0
  67. package/dist/pages/modules/index.js +1 -0
  68. package/dist/pages/modules/index.js.map +1 -1
  69. package/dist/pages/modules/modules-store.d.ts +9 -0
  70. package/dist/pages/modules/modules-store.js +30 -0
  71. package/dist/pages/modules/modules-store.js.map +1 -0
  72. package/dist/pages/settings/users/account-credentials.js +2 -2
  73. package/dist/pages/settings/users/account-credentials.js.map +1 -1
  74. package/dist/pages/settings/users/account-data-store.js +25 -8
  75. package/dist/pages/settings/users/account-data-store.js.map +1 -1
  76. package/dist/pages/settings/users/invite-extension-routes.js +9 -3
  77. package/dist/pages/settings/users/invite-extension-routes.js.map +1 -1
  78. package/dist/pages/settings/users/member-management.js +13 -14
  79. package/dist/pages/settings/users/member-management.js.map +1 -1
  80. package/dist/pages/settings/users/profile.d.ts +1 -4
  81. package/dist/pages/settings/users/profile.js +0 -35
  82. package/dist/pages/settings/users/profile.js.map +1 -1
  83. package/dist/pages/settings/users/role-editor-store.js +26 -18
  84. package/dist/pages/settings/users/role-editor-store.js.map +1 -1
  85. package/dist/pages/settings/users/role-editor.d.ts +0 -1
  86. package/dist/pages/settings/users/role-editor.js +1 -2
  87. package/dist/pages/settings/users/role-editor.js.map +1 -1
  88. package/dist/pages/settings/users/role-preview.js +1 -2
  89. package/dist/pages/settings/users/role-preview.js.map +1 -1
  90. package/dist/pages/settings/users/roles.js +2 -2
  91. package/dist/pages/settings/users/roles.js.map +1 -1
  92. package/dist/pages/settings/users/two-factor-operations.js.map +1 -1
  93. package/dist/realtime/broker.d.ts +6 -0
  94. package/dist/realtime/broker.js +8 -1
  95. package/dist/realtime/broker.js.map +1 -1
  96. package/dist/realtime/presence.d.ts +8 -3
  97. package/dist/realtime/presence.js +8 -4
  98. package/dist/realtime/presence.js.map +1 -1
  99. package/dist/realtime/sessions.d.ts +11 -1
  100. package/dist/realtime/sessions.js +21 -3
  101. package/dist/realtime/sessions.js.map +1 -1
  102. package/dist/realtime/table-view.d.ts +2 -2
  103. package/dist/realtime/table-view.js +4 -3
  104. package/dist/realtime/table-view.js.map +1 -1
  105. package/dist/routes/auth/client-ip.d.ts +1 -0
  106. package/dist/routes/auth/client-ip.js +18 -0
  107. package/dist/routes/auth/client-ip.js.map +1 -1
  108. package/dist/routes/auth/oauth/config.d.ts +2 -1
  109. package/dist/routes/auth/oauth/config.js +5 -1
  110. package/dist/routes/auth/oauth/config.js.map +1 -1
  111. package/dist/routes/auth/session-handoff.d.ts +0 -1
  112. package/dist/routes/auth/session-handoff.js +0 -2
  113. package/dist/routes/auth/session-handoff.js.map +1 -1
  114. package/dist/routes/auth.js +3 -2
  115. package/dist/routes/auth.js.map +1 -1
  116. package/dist/routes/index.d.ts +0 -1
  117. package/dist/routes/index.js +0 -1
  118. package/dist/routes/index.js.map +1 -1
  119. package/dist/routes/realtime.d.ts +13 -1
  120. package/dist/routes/realtime.js +114 -29
  121. package/dist/routes/realtime.js.map +1 -1
  122. package/dist/startup-config.d.ts +2 -0
  123. package/dist/startup-config.js +62 -0
  124. package/dist/startup-config.js.map +1 -0
  125. package/dist/store/interfaces.d.ts +3 -2
  126. package/dist/store/interfaces.js +16 -6
  127. package/dist/store/interfaces.js.map +1 -1
  128. package/dist/store/listing.js +24 -13
  129. package/dist/store/listing.js.map +1 -1
  130. package/dist/store/operations.js +4 -0
  131. package/dist/store/operations.js.map +1 -1
  132. package/dist/store/requirements.js +3 -2
  133. package/dist/store/requirements.js.map +1 -1
  134. package/dist/store/runtime.d.ts +10 -1
  135. package/dist/store/runtime.js +33 -6
  136. package/dist/store/runtime.js.map +1 -1
  137. package/dist/store/types.d.ts +10 -0
  138. package/dist/validation/auth.schema.d.ts +2 -2
  139. package/dist/validation/member-invite.schema.d.ts +2 -10
  140. package/dist/validation/member-invite.schema.js +2 -15
  141. package/dist/validation/member-invite.schema.js.map +1 -1
  142. package/frontend-vue/dms.frontend.ts +24 -5
  143. package/frontend-vue/dms.server.ts +17 -0
  144. package/frontend-vue/layers/dms-auth/server/accounts.ts +3 -4
  145. package/frontend-vue/layers/dms-core/app/build/composables/renderNow.ts +34 -0
  146. package/frontend-vue/layers/dms-core/app/build/composables/user/tablePreferences.ts +5 -1
  147. package/frontend-vue/layers/dms-core/app/build/utils/crossTab.ts +199 -0
  148. package/frontend-vue/layers/dms-core/app/build/utils/sharedRealtimeConnection.ts +410 -0
  149. package/frontend-vue/layers/dms-core/app/composables/auth/useApiData.ts +77 -12
  150. package/frontend-vue/layers/dms-core/app/composables/data-types/useDataType.ts +26 -0
  151. package/frontend-vue/layers/dms-core/app/composables/hydration/isHydratingComponent.ts +29 -0
  152. package/frontend-vue/layers/dms-core/app/composables/live/livePauseSource.ts +29 -0
  153. package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +150 -9
  154. package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +6 -4
  155. package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +27 -1
  156. package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +256 -119
  157. package/frontend-vue/layers/dms-core/app/composables/translation/useComposedText.ts +21 -8
  158. package/frontend-vue/layers/dms-core/app/composables/translation/useTranslation.ts +22 -5
  159. package/frontend-vue/layers/dms-core/app/plugins/dev-error-log.ts +16 -0
  160. package/frontend-vue/layers/dms-core/app/types/user.ts +6 -0
  161. package/frontend-vue/layers/dms-core/app/utils/composedText.ts +7 -2
  162. package/frontend-vue/layers/dms-core/app/utils/formatter.ts +166 -35
  163. package/frontend-vue/layers/dms-core/app/utils/url-interpolation.ts +18 -1
  164. package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +134 -2
  165. package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +134 -2
  166. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardBanners.vue +3 -2
  167. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardHeader.vue +41 -21
  168. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardPageBody.vue +9 -0
  169. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +4 -19
  170. package/frontend-vue/layers/dms-layout/app/build/components/layout/Footer.vue +11 -3
  171. package/frontend-vue/layers/dms-layout/app/build/components/layout/GithubStarPrompt.vue +18 -7
  172. package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionBar.vue +8 -0
  173. package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionsOutlet.vue +14 -0
  174. package/frontend-vue/layers/dms-layout/app/build/components/layout/RowSidePanel.vue +43 -82
  175. package/frontend-vue/layers/dms-layout/app/build/components/layout/SidebarUserMenu.vue +44 -3
  176. package/frontend-vue/layers/dms-layout/app/build/components/layout/UserIdentity.vue +11 -3
  177. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleCatalogTile.vue +132 -35
  178. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleManageMenu.vue +59 -0
  179. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleSourceLabel.vue +25 -0
  180. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreBanners.vue +73 -0
  181. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreOperation.vue +116 -0
  182. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreRequirements.vue +8 -3
  183. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreTile.vue +85 -207
  184. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTile.vue +10 -3
  185. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTileSkeleton.vue +68 -0
  186. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +24 -8
  187. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxItem.vue +8 -5
  188. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/region/RegionTimeZoneInput.vue +1 -0
  189. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-permissions.ts +19 -0
  190. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/useRoleEditor.ts +2 -1
  191. package/frontend-vue/layers/dms-layout/app/build/composables/layout/useSidePanelWidth.ts +31 -70
  192. package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationGroupsOpen.ts +11 -1
  193. package/frontend-vue/layers/dms-layout/app/build/utils/dev-reload-stream.ts +81 -0
  194. package/frontend-vue/layers/dms-layout/app/build/utils/module-store-catalog.ts +232 -36
  195. package/frontend-vue/layers/dms-layout/app/build/utils/modules-catalog.ts +122 -14
  196. package/frontend-vue/layers/dms-layout/app/composables/layout/useLayoutBanners.ts +32 -3
  197. package/frontend-vue/layers/dms-layout/app/composables/layout/usePageRecord.ts +50 -0
  198. package/frontend-vue/layers/dms-layout/app/composables/layout/useServedHeaderActions.ts +25 -7
  199. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleHistory.ts +3 -2
  200. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +51 -4
  201. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStoreActions.ts +194 -0
  202. package/frontend-vue/layers/dms-layout/app/composables/page/usePageComponentPreload.ts +23 -3
  203. package/frontend-vue/layers/dms-layout/app/composables/page/usePageLayout.ts +4 -2
  204. package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +6 -1
  205. package/frontend-vue/layers/dms-layout/app/custom-layouts/DefaultLayout.vue +9 -0
  206. package/frontend-vue/layers/dms-layout/app/custom-layouts/SettingsLayout.vue +9 -0
  207. package/frontend-vue/layers/dms-layout/app/custom-pages/modules-store.vue +291 -0
  208. package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +87 -474
  209. package/frontend-vue/layers/dms-layout/app/middleware/module-routing.global.ts +3 -0
  210. package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +6 -22
  211. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-navigation.client.ts +2 -1
  212. package/frontend-vue/layers/dms-layout/app/plugins/command-palette-personal-pages.client.ts +14 -44
  213. package/frontend-vue/layers/dms-layout/app/plugins/dms-dev-reload.client.ts +14 -9
  214. package/frontend-vue/layers/dms-layout/app/theme/form-controls.ts +9 -4
  215. package/frontend-vue/layers/dms-layout/app/theme/surfaces.ts +22 -6
  216. package/frontend-vue/layers/dms-layout/app/types/module-store.ts +9 -0
  217. package/frontend-vue/layers/dms-layout/app/types/page.ts +15 -1
  218. package/frontend-vue/layers/dms-layout/app/utils/account-menu.ts +11 -0
  219. package/frontend-vue/layers/dms-layout/app/utils/breadcrumb.ts +57 -0
  220. package/frontend-vue/layers/dms-layout/app/utils/pageQuery.ts +26 -0
  221. package/frontend-vue/layers/dms-layout/app/utils/pageRecord.ts +71 -0
  222. package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +39 -14
  223. package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +38 -13
  224. package/frontend-vue/layers/dms-ui/app/build/components/containers/TargetComponentTree.vue +31 -0
  225. package/frontend-vue/layers/dms-ui/app/build/components/containers/constants.ts +25 -0
  226. package/frontend-vue/layers/dms-ui/app/build/components/containers/drawer/DynamicDrawer.vue +244 -15
  227. package/frontend-vue/layers/dms-ui/app/build/components/containers/modal/DynamicModal.vue +42 -25
  228. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowHeader.vue +153 -0
  229. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowNotFound.vue +44 -0
  230. package/frontend-vue/layers/dms-ui/app/build/components/containers/row/drawerRowHeader.ts +35 -0
  231. package/frontend-vue/layers/dms-ui/app/build/components/form/FormEntries.vue +44 -9
  232. package/frontend-vue/layers/dms-ui/app/build/components/form/FormErrorSummary.vue +11 -4
  233. package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldControl.vue +38 -5
  234. package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldNotes.vue +29 -5
  235. package/frontend-vue/layers/dms-ui/app/build/components/form/FormSectionNav.vue +3 -1
  236. package/frontend-vue/layers/dms-ui/app/build/components/form/FormSections.vue +106 -27
  237. package/frontend-vue/layers/dms-ui/app/build/components/table/Actions.vue +12 -4
  238. package/frontend-vue/layers/dms-ui/app/build/components/table/Empty.vue +4 -2
  239. package/frontend-vue/layers/dms-ui/app/build/components/table/FiltersRow.vue +38 -1
  240. package/frontend-vue/layers/dms-ui/app/build/components/table/Pagination.vue +55 -54
  241. package/frontend-vue/layers/dms-ui/app/build/components/table/RowSelection.vue +2 -1
  242. package/frontend-vue/layers/dms-ui/app/build/components/table/Table.vue +396 -288
  243. package/frontend-vue/layers/dms-ui/app/build/components/table/Tabs.vue +2 -1
  244. package/frontend-vue/layers/dms-ui/app/build/components/table/Views.vue +2 -1
  245. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardAvatar.vue +59 -0
  246. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardGridSkeleton.vue +19 -2
  247. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardRowActions.vue +38 -0
  248. package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardsDisplay.vue +148 -104
  249. package/frontend-vue/layers/dms-ui/app/build/components/table-view/LiveHoldBanner.vue +17 -2
  250. package/frontend-vue/layers/dms-ui/app/build/composables/actions/bulkSelection.ts +43 -9
  251. package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowComponentOptions.ts +76 -0
  252. package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowNavigation.ts +172 -27
  253. package/frontend-vue/layers/dms-ui/app/build/composables/actions/useActionTargets.ts +270 -46
  254. package/frontend-vue/layers/dms-ui/app/build/composables/confirm/confirmDialogTexts.ts +2 -0
  255. package/frontend-vue/layers/dms-ui/app/build/composables/containers/closingLayers.ts +186 -0
  256. package/frontend-vue/layers/dms-ui/app/build/composables/containers/context.ts +14 -0
  257. package/frontend-vue/layers/dms-ui/app/build/composables/containers/drawerSize.ts +229 -0
  258. package/frontend-vue/layers/dms-ui/app/build/composables/containers/useResizableSize.ts +175 -0
  259. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/metricCells.ts +18 -35
  260. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/numberSuffix.ts +47 -0
  261. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +99 -24
  262. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerFieldTypes.ts +2 -1
  263. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationFilter.ts +75 -0
  264. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationRows.ts +71 -0
  265. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationValue.ts +39 -0
  266. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/twoLineCell.ts +3 -3
  267. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/useColumnValueRenderer.ts +6 -2
  268. package/frontend-vue/layers/dms-ui/app/build/composables/form/createFormFields.ts +15 -0
  269. package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldSaved.ts +40 -0
  270. package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldValueRule.ts +62 -0
  271. package/frontend-vue/layers/dms-ui/app/build/composables/form/formEntryContext.ts +5 -0
  272. package/frontend-vue/layers/dms-ui/app/build/composables/form/formFooter.ts +42 -7
  273. package/frontend-vue/layers/dms-ui/app/build/composables/form/formLayout.ts +106 -4
  274. package/frontend-vue/layers/dms-ui/app/build/composables/form/formSections.ts +32 -0
  275. package/frontend-vue/layers/dms-ui/app/build/composables/form/readonlyValue.ts +19 -0
  276. package/frontend-vue/layers/dms-ui/app/build/composables/form/registerFormFunctions.ts +27 -0
  277. package/frontend-vue/layers/dms-ui/app/build/composables/form/repeaterTable.ts +65 -0
  278. package/frontend-vue/layers/dms-ui/app/build/composables/form/serverFieldErrors.ts +49 -0
  279. package/frontend-vue/layers/dms-ui/app/build/composables/form/toasterClearance.ts +118 -0
  280. package/frontend-vue/layers/dms-ui/app/build/composables/form/useInstantForm.ts +12 -1
  281. package/frontend-vue/layers/dms-ui/app/build/composables/live/currentPageLive.ts +41 -3
  282. package/frontend-vue/layers/dms-ui/app/build/composables/live/livePoller.ts +10 -2
  283. package/frontend-vue/layers/dms-ui/app/build/composables/live/pageReadClock.ts +28 -0
  284. package/frontend-vue/layers/dms-ui/app/build/composables/live/shortcuts.ts +1 -1
  285. package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageLiveHost.ts +17 -3
  286. package/frontend-vue/layers/dms-ui/app/build/composables/live/useUpdatedLabel.ts +38 -0
  287. package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/pageToolbarContext.ts +75 -0
  288. package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/toolbarControlHeight.ts +29 -0
  289. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodComparison.ts +52 -0
  290. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +21 -0
  291. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodShortLabels.ts +28 -0
  292. package/frontend-vue/layers/dms-ui/app/build/composables/period/usePeriodDraft.ts +1 -0
  293. package/frontend-vue/layers/dms-ui/app/build/composables/table/useRelationFilterLabels.ts +73 -0
  294. package/frontend-vue/layers/dms-ui/app/build/composables/table/useRowScrollAnchor.ts +106 -0
  295. package/frontend-vue/layers/dms-ui/app/build/composables/table/useTable.ts +9 -0
  296. package/frontend-vue/layers/dms-ui/app/build/composables/table/useTableColumns.ts +1 -2
  297. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/archivedCount.ts +31 -0
  298. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/filterEditorValue.ts +38 -0
  299. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/formatFilterValue.ts +33 -2
  300. package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/scrollEdges.ts +31 -0
  301. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useCardFields.ts +33 -0
  302. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useMobileDisplay.ts +48 -0
  303. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useScrolledPastTop.ts +41 -18
  304. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableFooter.ts +3 -2
  305. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLinkedVisit.ts +78 -0
  306. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLiveHold.ts +135 -22
  307. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +56 -25
  308. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +77 -32
  309. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +86 -10
  310. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/bulkActions.ts +14 -4
  311. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/card.ts +127 -2
  312. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/compareValues.ts +13 -0
  313. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/expandedRows.ts +28 -0
  314. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/groupedRows.ts +114 -1
  315. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/linkedArrival.ts +308 -0
  316. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/locateRow.ts +106 -0
  317. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pageArrival.ts +34 -0
  318. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pinnedRows.ts +17 -0
  319. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/quickFilters.ts +41 -33
  320. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/recordLink.ts +24 -8
  321. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableCounts.ts +113 -0
  322. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableQuery.ts +27 -4
  323. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/views.ts +22 -6
  324. package/frontend-vue/layers/dms-ui/app/build/utils/addressSuggestions.ts +49 -0
  325. package/frontend-vue/layers/dms-ui/app/build/utils/eyebrow.ts +12 -0
  326. package/frontend-vue/layers/dms-ui/app/build/utils/fieldOptionsI18n.ts +51 -11
  327. package/frontend-vue/layers/dms-ui/app/build/utils/inputWidth.ts +53 -0
  328. package/frontend-vue/layers/dms-ui/app/build/utils/numberAdjustment.ts +6 -3
  329. package/frontend-vue/layers/dms-ui/app/build/utils/relativeDate.ts +19 -5
  330. package/frontend-vue/layers/dms-ui/app/build/utils/scrollWithin.ts +99 -0
  331. package/frontend-vue/layers/dms-ui/app/build/utils/urlVariables.ts +36 -14
  332. package/frontend-vue/layers/dms-ui/app/components/PasswordStrength.vue +1 -1
  333. package/frontend-vue/layers/dms-ui/app/components/RecursiveComponent.vue +9 -1
  334. package/frontend-vue/layers/dms-ui/app/components/blocks/LiveToggleBlock.vue +80 -32
  335. package/frontend-vue/layers/dms-ui/app/components/blocks/PageToolbarBlock.vue +137 -0
  336. package/frontend-vue/layers/dms-ui/app/components/blocks/StatGroupBlock.vue +5 -0
  337. package/frontend-vue/layers/dms-ui/app/components/chart/ApexChartHost.vue +16 -2
  338. package/frontend-vue/layers/dms-ui/app/components/chart/Chart.vue +24 -5
  339. package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +41 -23
  340. package/frontend-vue/layers/dms-ui/app/components/confirm/ConfirmModal.vue +1 -1
  341. package/frontend-vue/layers/dms-ui/app/components/container-footer/ContainerFooter.vue +34 -0
  342. package/frontend-vue/layers/dms-ui/app/components/field-row/FieldRow.vue +25 -2
  343. package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +2 -2
  344. package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +118 -36
  345. package/frontend-vue/layers/dms-ui/app/components/form/components/DatePicker.vue +20 -1
  346. package/frontend-vue/layers/dms-ui/app/components/form/components/Display.vue +13 -1
  347. package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +8 -3
  348. package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +53 -5
  349. package/frontend-vue/layers/dms-ui/app/components/form/components/InputCode.vue +29 -5
  350. package/frontend-vue/layers/dms-ui/app/components/form/components/InputNumber.vue +45 -8
  351. package/frontend-vue/layers/dms-ui/app/components/form/components/InputPercentage.vue +28 -5
  352. package/frontend-vue/layers/dms-ui/app/components/form/components/InputSecret.vue +5 -0
  353. package/frontend-vue/layers/dms-ui/app/components/form/components/InputText.vue +19 -2
  354. package/frontend-vue/layers/dms-ui/app/components/form/components/InputTime.vue +2 -1
  355. package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +205 -65
  356. package/frontend-vue/layers/dms-ui/app/components/form/components/Repeater.vue +80 -30
  357. package/frontend-vue/layers/dms-ui/app/components/form/components/RepeaterTable.vue +203 -0
  358. package/frontend-vue/layers/dms-ui/app/components/form/components/Select.vue +4 -2
  359. package/frontend-vue/layers/dms-ui/app/components/grid/GridRow.vue +11 -2
  360. package/frontend-vue/layers/dms-ui/app/components/key-value-list/KeyValueList.vue +79 -70
  361. package/frontend-vue/layers/dms-ui/app/components/kpi/KpiCard.vue +117 -21
  362. package/frontend-vue/layers/dms-ui/app/components/meter/Meter.vue +2 -1
  363. package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +113 -34
  364. package/frontend-vue/layers/dms-ui/app/components/save-bar/SaveBar.vue +14 -2
  365. package/frontend-vue/layers/dms-ui/app/components/section-header/Eyebrow.vue +6 -1
  366. package/frontend-vue/layers/dms-ui/app/components/segmented/Segmented.vue +13 -0
  367. package/frontend-vue/layers/dms-ui/app/components/stat-group/StatGroup.vue +52 -3
  368. package/frontend-vue/layers/dms-ui/app/components/tab/Tab.vue +2 -1
  369. package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +45 -35
  370. package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanDisplay.vue +4 -1
  371. package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +410 -96
  372. package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +33 -5
  373. package/frontend-vue/layers/dms-ui/app/composables/chart/apexCategoryLabels.ts +68 -0
  374. package/frontend-vue/layers/dms-ui/app/composables/chart/apexDatetimeBuckets.ts +306 -0
  375. package/frontend-vue/layers/dms-ui/app/composables/chart/formatValue.ts +4 -3
  376. package/frontend-vue/layers/dms-ui/app/composables/chart/heatmapColors.ts +93 -0
  377. package/frontend-vue/layers/dms-ui/app/composables/chart/localizeSeries.ts +62 -0
  378. package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.ts +116 -8
  379. package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.types.ts +7 -0
  380. package/frontend-vue/layers/dms-ui/app/composables/containers/types.ts +84 -1
  381. package/frontend-vue/layers/dms-ui/app/composables/containers/useDrawer.ts +69 -8
  382. package/frontend-vue/layers/dms-ui/app/composables/form/types/field-saved.ts +11 -0
  383. package/frontend-vue/layers/dms-ui/app/composables/form/types/field.ts +4 -0
  384. package/frontend-vue/layers/dms-ui/app/composables/form/types/functions.ts +1 -0
  385. package/frontend-vue/layers/dms-ui/app/composables/form/useForm.ts +142 -21
  386. package/frontend-vue/layers/dms-ui/app/composables/live/usePageLive.ts +11 -4
  387. package/frontend-vue/layers/dms-ui/app/composables/page-toolbar/usePageToolbarSize.ts +30 -0
  388. package/frontend-vue/layers/dms-ui/app/composables/table-view/kanban.ts +4 -2
  389. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/action-target.ts +22 -3
  390. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/config.ts +4 -0
  391. package/frontend-vue/layers/dms-ui/app/composables/table-view/types/display.ts +24 -1
  392. package/frontend-vue/layers/dms-ui/app/plugins/closing-layers.client.ts +10 -0
  393. package/frontend-vue/layers/dms-ui/app/plugins/live-pause-source.ts +15 -0
  394. package/frontend-vue/layers/dms-ui/app/plugins/register.ts +2 -0
  395. package/frontend-vue/layers/dms-ui/app/types/input-number.ts +3 -2
  396. package/frontend-vue/layers/dms-ui/app/types/page-toolbar.ts +7 -0
  397. package/frontend-vue/layers/dms-ui/app/types/row-action.ts +0 -2
  398. package/frontend-vue/layers/dms-ui/app/types/row-navigation.ts +17 -0
  399. package/frontend-vue/layers/dms-ui/app/types/table/query.ts +2 -1
  400. package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +3 -1
  401. package/frontend-vue/layers/dms-ui/app/utils/componentTree.ts +24 -0
  402. package/frontend-vue/layers/dms-ui/i18n/locales/ui-en-GB.json +16 -4
  403. package/frontend-vue/layers/dms-ui/i18n/locales/ui-fr-FR.json +16 -4
  404. package/frontend-vue/package.json +2 -2
  405. package/package.json +7 -6
  406. package/skills/dms/SKILL.md +1 -1
  407. package/skills/dms-auth/SKILL.md +5 -3
  408. package/skills/dms-best-practices/REFERENCE.md +2 -0
  409. package/skills/dms-best-practices/SKILL.md +4 -0
  410. package/skills/dms-module/SKILL.md +3 -2
  411. package/skills/dms-pages/REFERENCE.md +125 -16
  412. package/skills/dms-pages/SKILL.md +1 -1
  413. package/skills/dms-project/DEV-SERVERS.md +23 -1
  414. package/skills/dms-project/REFERENCE.md +1 -1
  415. package/skills/dms-project/SKILL.md +4 -1
  416. package/skills/dms-project/scripts/dms-dev.sh +205 -17
  417. package/dist/routes/startup-gate.d.ts +0 -7
  418. package/dist/routes/startup-gate.js +0 -39
  419. package/dist/routes/startup-gate.js.map +0 -1
  420. package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +0 -42
@@ -1,12 +1,38 @@
1
1
  <script setup lang="ts">
2
- import type { Component } from "vue";
2
+ import { getCurrentInstance, type Component } from "vue";
3
+ import {
4
+ formBusInjectionKey,
5
+ formErrorsInjectionKey,
6
+ formFieldInjectionKey,
7
+ formInputsInjectionKey,
8
+ formLoadingInjectionKey,
9
+ formOptionsInjectionKey,
10
+ formStateInjectionKey,
11
+ inputIdInjectionKey,
12
+ } from "@nuxt/ui/runtime/composables/useFormField.js";
3
13
  import { DialogDescription, DialogTitle, VisuallyHidden } from "reka-ui";
14
+ import { useWindowSize } from "@vueuse/core";
4
15
  import type {
5
16
  ContainerColor,
6
17
  DrawerDirection,
7
18
  } from "../../../../composables/containers/types";
19
+ import type { ModalSize } from "../../../../types/modal";
20
+ import {
21
+ drawerSizeStorage,
22
+ isSideDrawer,
23
+ resolveDrawerSizeBounds,
24
+ useDrawerSizePreferences,
25
+ } from "../../../composables/containers/drawerSize";
26
+ import {
27
+ type ResizeSide,
28
+ useResizableSize,
29
+ } from "../../../composables/containers/useResizableSize";
8
30
  import DmsIconWell from "../../../../components/icon-well/IconWell.vue";
9
- import { CONTAINER_SKELETON_FIELDS } from "../constants";
31
+ import {
32
+ CLOSING_OVERLAY_CLASS,
33
+ CONTAINER_SKELETON_CLASS,
34
+ CONTAINER_SKELETON_FIELDS,
35
+ } from "../constants";
10
36
  import { DMS_CONTAINER_KEY } from "../../../composables/containers/context";
11
37
 
12
38
  interface DynamicDrawerProps {
@@ -20,8 +46,20 @@ interface DynamicDrawerProps {
20
46
  headerComponent?: Component;
21
47
  headerComponentOptions?: Record<string, unknown>;
22
48
  direction?: DrawerDirection;
49
+ /**
50
+ * Width a side drawer opens at, from `lg`: a modal's width at that size;
51
+ * 600px without one. Below `lg`, only `full` fills a phone's screen.
52
+ */
53
+ size?: ModalSize;
54
+ /** The key the size the user gives the drawer is remembered under. */
55
+ sizeKey?: string;
23
56
  icon?: string;
24
57
  color?: ContainerColor;
58
+ /**
59
+ * What the component that opened the drawer provides (`useDrawer` passes
60
+ * it): the drawer and its body inject it as if rendered in its tree.
61
+ */
62
+ callerProvides?: object;
25
63
  }
26
64
 
27
65
  interface DynamicDrawerEmits {
@@ -34,29 +72,181 @@ const props = withDefaults(defineProps<DynamicDrawerProps>(), {
34
72
  headerComponent: undefined,
35
73
  headerComponentOptions: undefined,
36
74
  direction: "bottom",
75
+ size: undefined,
76
+ sizeKey: undefined,
37
77
  icon: undefined,
38
78
  color: "primary",
79
+ callerProvides: undefined,
39
80
  });
40
81
  const emit = defineEmits<DynamicDrawerEmits>();
41
82
 
42
- // A side drawer is a sheet as wide as a side panel by default (see
43
- // `useAppSidePanels`), never wider than the window less the drawer's gutter.
44
- // Its own padding is the gutter: the containers drop theirs.
45
- const SIDE_DIRECTIONS: readonly DrawerDirection[] = ["left", "right"];
83
+ // Mounted by the overlay provider, the drawer would only see the app's
84
+ // provides: its own start from the caller's instead, before it provides
85
+ // anything (Vue's `provide` then writes into them). What the drawer provides
86
+ // itself (its container, the form surface) still wins over the caller's.
87
+ const NUXT_UI_FORM_KEYS = [
88
+ formOptionsInjectionKey,
89
+ formBusInjectionKey,
90
+ formStateInjectionKey,
91
+ formFieldInjectionKey,
92
+ inputIdInjectionKey,
93
+ formInputsInjectionKey,
94
+ formLoadingInjectionKey,
95
+ formErrorsInjectionKey,
96
+ ];
97
+ const drawerInstance = getCurrentInstance() as { provides: object } | null;
98
+ if (drawerInstance && props.callerProvides) {
99
+ drawerInstance.provides = Object.create(props.callerProvides);
100
+ // A drawer is a surface of its own: a field in its body does not join a
101
+ // Nuxt UI form or form field around the component that opened it.
102
+ for (const key of NUXT_UI_FORM_KEYS) provide(key as symbol, undefined);
103
+ }
104
+
105
+ // Below `lg` a side drawer is a sheet as wide as a side panel (see
106
+ // `useAppSidePanels`), or a modal's width at its size, never wider than the
107
+ // window less the drawer's gutter (Nuxt UI's `max-w-[calc(100%-2rem)]`): on
108
+ // a phone every side drawer leaves the same strip of backdrop, sized or not,
109
+ // and only `full` fills the screen. From
110
+ // `lg` it takes the width the user resizes it to (600px at first), kept per
111
+ // drawer. Its own padding is the gutter: the containers drop theirs.
46
112
  const SIDE_DRAWER_CONTENT = "w-[440px]";
113
+ const SIDE_DRAWER_SIZE_CONTENT: Record<ModalSize, string> = {
114
+ sm: "w-sm",
115
+ md: "w-md",
116
+ lg: "w-lg",
117
+ xl: "w-xl",
118
+ "2xl": "w-2xl",
119
+ "3xl": "w-3xl",
120
+ "4xl": "w-4xl",
121
+ "5xl": "w-5xl",
122
+ full: "w-full max-w-full rounded-none",
123
+ };
124
+ const SIZE_VARIABLE = "--dms-drawer-size";
125
+ const SIDE_DRAWER_DESKTOP_CONTENT = "lg:w-(--dms-drawer-size)";
126
+ const EDGE_DRAWER_DESKTOP_CONTENT = "lg:h-(--dms-drawer-size)";
47
127
  const SIDE_CONTAINER_CLASS = "px-0 sm:px-0 lg:px-0";
48
128
  const EDGE_CONTAINER_CLASS = "max-sm:px-0";
49
129
 
50
- const isSide = computed(() => SIDE_DIRECTIONS.includes(props.direction));
130
+ // The handle sits on the drawer's inner edge, over its border.
131
+ const HANDLE_CLASSES: Record<ResizeSide, string> = {
132
+ right: "inset-y-0 left-0 w-2 -translate-x-1/2 cursor-ew-resize",
133
+ left: "inset-y-0 right-0 w-2 translate-x-1/2 cursor-ew-resize",
134
+ bottom: "inset-x-0 top-0 h-2 -translate-y-1/2 cursor-ns-resize",
135
+ top: "inset-x-0 bottom-0 h-2 translate-y-1/2 cursor-ns-resize",
136
+ };
137
+ const SIDE_HANDLE_LINE = "inset-y-0 start-1/2 w-0.5 -translate-x-1/2";
138
+ const EDGE_HANDLE_LINE = "inset-x-0 top-1/2 h-0.5 -translate-y-1/2";
139
+
140
+ const isSide = computed(() => isSideDrawer(props.direction));
51
141
  const containerClass = computed(() =>
52
142
  isSide.value ? SIDE_CONTAINER_CLASS : EDGE_CONTAINER_CLASS,
53
143
  );
144
+
145
+ const viewport = useWindowSize();
146
+ const {
147
+ size: resizedSize,
148
+ bounds: sizeBounds,
149
+ isDragging,
150
+ startDrag,
151
+ drag,
152
+ endDrag,
153
+ resizeFromKey,
154
+ reset: resetSize,
155
+ } = useResizableSize({
156
+ side: props.direction,
157
+ bounds: () =>
158
+ resolveDrawerSizeBounds(
159
+ props.direction,
160
+ { width: viewport.width.value, height: viewport.height.value },
161
+ props.size,
162
+ ),
163
+ storage: drawerSizeStorage(
164
+ useDrawerSizePreferences(),
165
+ props.sizeKey,
166
+ props.direction,
167
+ ),
168
+ });
169
+
170
+ const sideContentClass = computed(() => {
171
+ const narrow = props.size
172
+ ? SIDE_DRAWER_SIZE_CONTENT[props.size]
173
+ : SIDE_DRAWER_CONTENT;
174
+ return `${narrow} ${SIDE_DRAWER_DESKTOP_CONTENT}`;
175
+ });
176
+ // A top or bottom drawer keeps its natural height until resized.
177
+ const contentClass = computed(() => {
178
+ if (isSide.value) return sideContentClass.value;
179
+ return resizedSize.value === undefined
180
+ ? undefined
181
+ : EDGE_DRAWER_DESKTOP_CONTENT;
182
+ });
183
+ // Reka keeps the page's `pointer-events: none` on the body until the
184
+ // panel has slid out (half a second): released as soon as the close starts,
185
+ // a click on the page lands while the drawer animates away.
186
+ const contentProps = computed(() => ({
187
+ disableOutsidePointerEvents: isOpen.value,
188
+ ...(resizedSize.value !== undefined && {
189
+ style: { [SIZE_VARIABLE]: `${resizedSize.value}px` },
190
+ }),
191
+ }));
192
+ const handleClass = computed(() => HANDLE_CLASSES[props.direction]);
193
+ const handleLineClass = computed(() =>
194
+ isSide.value ? SIDE_HANDLE_LINE : EDGE_HANDLE_LINE,
195
+ );
196
+ const handleOrientation = computed(() =>
197
+ isSide.value ? "vertical" : "horizontal",
198
+ );
199
+
200
+ function renderedSize(handle: Element): number | undefined {
201
+ const content = handle.closest("[data-slot='content']");
202
+ if (!content) return undefined;
203
+ const rect = content.getBoundingClientRect();
204
+ return isSide.value ? rect.width : rect.height;
205
+ }
206
+
207
+ // The pointerdown is left to bubble: reka's outside-click detection listens
208
+ // on the document and, if it never saw this press inside the drawer, would
209
+ // take the next click on the backdrop for one inside and ignore it. The
210
+ // drawer's own drag cannot start from it: vaul drags only a dismissible
211
+ // drawer, and this one is not.
212
+ function startHandleDrag(event: PointerEvent): void {
213
+ const handle = event.currentTarget as HTMLElement;
214
+ handle.setPointerCapture?.(event.pointerId);
215
+ const rendered = renderedSize(handle) || resizedSize.value;
216
+ if (rendered) startDrag(event, rendered);
217
+ }
218
+
219
+ function resizeHandleFromKey(event: KeyboardEvent): void {
220
+ resizeFromKey(event, renderedSize(event.currentTarget as HTMLElement));
221
+ }
222
+
223
+ // From lg the resize handle takes the inner edge: the drawer's own grip,
224
+ // drawn in its middle, would sit next to it. Below lg a top or bottom
225
+ // drawer keeps it. A side drawer never draws one: it is not dragged away
226
+ // (`dismissible` is off), and the grip took 22px of a phone's gutter.
227
+ // Important: vaul's unlayered `[data-vaul-handle]` rule outranks a utility.
228
+ const DESKTOP_HIDDEN_GRIP = "lg:hidden!";
229
+
230
+ // The body scrolls, not the whole drawer: the header (title, row steps,
231
+ // close) stays in view. The body reaches over the container's side padding
232
+ // so its scrollbar sits on the drawer's edge, and carries the bottom padding
233
+ // a form's sticky footer reaches over to sit flush with it (see Form.vue).
234
+ // The body is a flex column: a body component whose root grows (`flex-1`)
235
+ // fills the drawer's height while its content is short, so a
236
+ // `<DmsContainerFooter>` at its end sits at the bottom, and it scrolls past
237
+ // it while long.
238
+ const DRAWER_CONTAINER_CLASS =
239
+ "[--dms-form-foot-pb:1rem] min-h-0 overflow-y-hidden pb-0";
240
+ const DRAWER_BODY_CLASS =
241
+ "flex min-h-0 flex-col overflow-y-auto -mx-4 px-4 pb-4";
242
+
54
243
  const drawerUi = computed(() => ({
55
- ...(isSide.value && { content: SIDE_DRAWER_CONTENT }),
244
+ ...(contentClass.value && { content: contentClass.value }),
245
+ overlay: CLOSING_OVERLAY_CLASS,
246
+ handle: DESKTOP_HIDDEN_GRIP,
56
247
  header: "border-b border-default flex shrink-0 items-center gap-4 pb-4",
57
- // The scroll area's bottom padding: a form's sticky footer sits flush
58
- // with the drawer's edge (see Form.vue).
59
- container: "[--dms-form-foot-pb:1rem]",
248
+ container: DRAWER_CONTAINER_CLASS,
249
+ body: DRAWER_BODY_CLASS,
60
250
  }));
61
251
 
62
252
  // Let a DmsForm rendered in the body drop its DmsCard (the drawer is the surface).
@@ -126,10 +316,41 @@ const { t } = useI18n();
126
316
  :description="description"
127
317
  :title="title"
128
318
  :dismissible="false"
319
+ :handle="!isSide"
129
320
  :ui="drawerUi"
321
+ :content="contentProps"
130
322
  @close:prevent="tryClose"
131
323
  >
132
324
  <template #header>
325
+ <!-- Positioned against the drawer's content, which is fixed. From lg,
326
+ a drag or the arrow keys resize the drawer, a double-click gives it
327
+ its default size back. -->
328
+ <div
329
+ role="separator"
330
+ tabindex="0"
331
+ data-dms-drawer-handle
332
+ :aria-orientation="handleOrientation"
333
+ :aria-label="t('dms.drawer.resize')"
334
+ :aria-valuenow="resizedSize"
335
+ :aria-valuemin="sizeBounds.minSize"
336
+ :aria-valuemax="sizeBounds.maxSize"
337
+ :data-dragging="isDragging || undefined"
338
+ class="group/handle absolute z-10 hidden touch-none outline-none select-none lg:block"
339
+ :class="handleClass"
340
+ @pointerdown="startHandleDrag"
341
+ @pointermove="drag"
342
+ @pointerup="endDrag"
343
+ @pointercancel="endDrag"
344
+ @lostpointercapture="endDrag"
345
+ @keydown="resizeHandleFromKey"
346
+ @dblclick="resetSize"
347
+ >
348
+ <span
349
+ aria-hidden="true"
350
+ class="group-hover/handle:bg-primary group-focus-visible/handle:bg-primary absolute transition-colors motion-reduce:transition-none"
351
+ :class="[handleLineClass, { 'bg-primary': isDragging }]"
352
+ />
353
+ </div>
133
354
  <!-- Same container as the body so the title aligns with the form. On
134
355
  phones the drawer's own padding is the 16px gutter: the container
135
356
  drops its own. -->
@@ -139,8 +360,11 @@ const { t } = useI18n();
139
360
  >
140
361
  <DmsIconWell v-if="icon" :icon="icon" :tone="color" />
141
362
  <div v-if="headerComponent" class="min-w-0 flex-1">
363
+ <!-- The header component draws the visible heading (the row
364
+ header's names the row): the dialog's own title only labels it,
365
+ without a second heading of the same text. -->
142
366
  <VisuallyHidden>
143
- <DialogTitle>{{ title }}</DialogTitle>
367
+ <DialogTitle as="span">{{ title }}</DialogTitle>
144
368
  <DialogDescription>{{ description }}</DialogDescription>
145
369
  </VisuallyHidden>
146
370
  <Suspense>
@@ -170,14 +394,19 @@ const { t } = useI18n();
170
394
  variant="ghost"
171
395
  size="sm"
172
396
  square
173
- aria-label="Close"
397
+ :aria-label="t('dms.a11y.close')"
174
398
  :disabled="isContentLoading"
175
399
  @click="tryClose"
176
400
  />
177
401
  </UContainer>
178
402
  </template>
179
403
  <template #body>
180
- <UContainer :class="containerClass">
404
+ <!-- A size container: what the body lays out by width (a form's
405
+ columns, a list's) follows the drawer as it is resized. -->
406
+ <UContainer
407
+ class="@container flex flex-1 flex-col"
408
+ :class="containerClass"
409
+ >
181
410
  <Suspense @pending="onContentPending" @resolve="onContentResolve">
182
411
  <component
183
412
  :is="component"
@@ -187,7 +416,7 @@ const { t } = useI18n();
187
416
  @success="handleSuccess"
188
417
  />
189
418
  <template #fallback>
190
- <div class="grid gap-4 sm:grid-cols-2" aria-busy="true">
419
+ <div :class="CONTAINER_SKELETON_CLASS" aria-busy="true">
191
420
  <div
192
421
  v-for="field in CONTAINER_SKELETON_FIELDS"
193
422
  :key="field"
@@ -4,7 +4,11 @@ import { DialogDescription, DialogTitle, VisuallyHidden } from "reka-ui";
4
4
  import type { ModalSize } from "../../../../types/modal";
5
5
  import type { ContainerColor } from "../../../../composables/containers/types";
6
6
  import DmsIconWell from "../../../../components/icon-well/IconWell.vue";
7
- import { CONTAINER_SKELETON_FIELDS } from "../constants";
7
+ import {
8
+ CLOSING_OVERLAY_CLASS,
9
+ CONTAINER_SKELETON_CLASS,
10
+ CONTAINER_SKELETON_FIELDS,
11
+ } from "../constants";
8
12
  import { DMS_CONTAINER_KEY } from "../../../composables/containers/context";
9
13
 
10
14
  interface DynamicModalProps {
@@ -78,6 +82,7 @@ const resolvedSize = computed(() => {
78
82
  // A divided head: the body below is a form, not a continuation of the title.
79
83
  const modalUi = computed(() => ({
80
84
  content: SIZE_CONTENT[resolvedSize.value],
85
+ overlay: CLOSING_OVERLAY_CLASS,
81
86
  header: "border-b border-default pb-4 shrink-0",
82
87
  // The body's bottom and side padding: a form's sticky footer sits flush
83
88
  // with the modal's edges (see Form.vue).
@@ -87,6 +92,11 @@ const modalUi = computed(() => ({
87
92
  const { executeGuards, clearGuards } = useLeaveGuard();
88
93
 
89
94
  const isOpen = defineModel<boolean>("open", { default: true });
95
+ // Reka keeps the page's `pointer-events: none` on the body until the modal
96
+ // has scaled out: released as soon as the close starts, a click lands.
97
+ const contentProps = computed(() => ({
98
+ disableOutsidePointerEvents: isOpen.value,
99
+ }));
90
100
  const isContentLoading = ref(true);
91
101
  let closePromise: Promise<void> | null = null;
92
102
 
@@ -148,6 +158,7 @@ const { t } = useI18n();
148
158
  :title="title"
149
159
  :dismissible="false"
150
160
  :ui="modalUi"
161
+ :content="contentProps"
151
162
  @close:prevent="tryClose"
152
163
  >
153
164
  <template #header>
@@ -185,37 +196,43 @@ const { t } = useI18n();
185
196
  variant="ghost"
186
197
  size="sm"
187
198
  square
188
- aria-label="Close"
199
+ :aria-label="t('dms.a11y.close')"
189
200
  :disabled="isContentLoading"
190
201
  @click="tryClose"
191
202
  />
192
203
  </div>
193
204
  </template>
194
205
  <template #body>
195
- <Suspense @pending="onContentPending" @resolve="onContentResolve">
196
- <component
197
- :is="component"
198
- :key="componentKey"
199
- v-bind="componentOptions"
200
- :container-id="containerId"
201
- @success="handleSuccess"
202
- />
203
- <template #fallback>
204
- <div class="grid gap-4 sm:grid-cols-2" aria-busy="true">
205
- <div
206
- v-for="field in CONTAINER_SKELETON_FIELDS"
207
- :key="field"
208
- class="flex flex-col gap-2"
209
- >
210
- <USkeleton :aria-label="t('dms.a11y.loading')" class="h-3 w-24" />
211
- <USkeleton
212
- :aria-label="t('dms.a11y.loading')"
213
- class="h-8 w-full"
214
- />
206
+ <!-- A size container: the body lays out by the modal's width. -->
207
+ <div class="@container">
208
+ <Suspense @pending="onContentPending" @resolve="onContentResolve">
209
+ <component
210
+ :is="component"
211
+ :key="componentKey"
212
+ v-bind="componentOptions"
213
+ :container-id="containerId"
214
+ @success="handleSuccess"
215
+ />
216
+ <template #fallback>
217
+ <div :class="CONTAINER_SKELETON_CLASS" aria-busy="true">
218
+ <div
219
+ v-for="field in CONTAINER_SKELETON_FIELDS"
220
+ :key="field"
221
+ class="flex flex-col gap-2"
222
+ >
223
+ <USkeleton
224
+ :aria-label="t('dms.a11y.loading')"
225
+ class="h-3 w-24"
226
+ />
227
+ <USkeleton
228
+ :aria-label="t('dms.a11y.loading')"
229
+ class="h-8 w-full"
230
+ />
231
+ </div>
215
232
  </div>
216
- </div>
217
- </template>
218
- </Suspense>
233
+ </template>
234
+ </Suspense>
235
+ </div>
219
236
  </template>
220
237
  </UModal>
221
238
  </template>
@@ -0,0 +1,153 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * The header the DMS draws over a row opened in a side panel or a drawer:
4
+ * the title (naming the row when the target words it), the row's position
5
+ * among the rows the table shows, and previous / next (J and K too). The
6
+ * container adds its close button in the default slot. A long title wraps
7
+ * onto two lines before it is cut (a ticket subject, a site name on a phone).
8
+ */
9
+ import { computed } from "vue";
10
+ import type { Component } from "vue";
11
+ import type { RowNavigation } from "../../../../types/row-navigation";
12
+
13
+ interface RowHeaderProps {
14
+ heading?: string;
15
+ /** i18n key (`$`-prefixed) of the position, receiving `{ index, total }`. */
16
+ positionLabel?: string;
17
+ navigation?: RowNavigation;
18
+ /** Prefix of the `data-*` attributes tests and styles find the parts by. */
19
+ part: string;
20
+ headingClass?: string;
21
+ /** Wraps the title only after this separator (`<wbr>` after each one). */
22
+ headingBreakAfter?: string;
23
+ /** Text after the position, on its line, cut at its end. */
24
+ subtitle?: string;
25
+ subtitleClass?: string;
26
+ /** Drawn under the position line (pills, hints). */
27
+ component?: Component;
28
+ componentOptions?: Record<string, unknown>;
29
+ }
30
+
31
+ const props = withDefaults(defineProps<RowHeaderProps>(), {
32
+ heading: undefined,
33
+ positionLabel: undefined,
34
+ navigation: undefined,
35
+ headingClass: "text-[15px] leading-[1.3] font-[650] tracking-[-0.015em]",
36
+ headingBreakAfter: undefined,
37
+ subtitle: undefined,
38
+ subtitleClass: undefined,
39
+ component: undefined,
40
+ componentOptions: undefined,
41
+ });
42
+
43
+ const DEFAULT_POSITION_KEY = "side_panel.position";
44
+
45
+ const { t, locale } = useI18n();
46
+ const { processI18n } = useTranslation();
47
+
48
+ // "26 120", not "26120": the figures take the locale's grouping.
49
+ const numberFormat = computed(() => new Intl.NumberFormat(locale.value));
50
+
51
+ const position = computed(() => {
52
+ if (!props.navigation) return undefined;
53
+ const params = {
54
+ index: numberFormat.value.format(props.navigation.index + 1),
55
+ total: numberFormat.value.format(props.navigation.total),
56
+ };
57
+ return props.positionLabel
58
+ ? processI18n(props.positionLabel, params)
59
+ : t(DEFAULT_POSITION_KEY, params);
60
+ });
61
+
62
+ // Each segment but the last keeps the separator that follows it.
63
+ const headingSegments = computed(() => {
64
+ const separator = props.headingBreakAfter;
65
+ if (!props.heading || !separator) return undefined;
66
+ const parts = props.heading.split(separator);
67
+ return parts.map((part, index) =>
68
+ index < parts.length - 1 ? `${part}${separator}` : part,
69
+ );
70
+ });
71
+
72
+ const partAttribute = (name: string) => ({
73
+ [`data-${props.part}-${name}`]: "",
74
+ });
75
+ </script>
76
+
77
+ <template>
78
+ <div class="flex min-w-0 flex-1 items-start gap-2">
79
+ <div
80
+ class="min-w-0 flex-1"
81
+ :class="component ? 'space-y-1.5' : 'self-center'"
82
+ >
83
+ <div>
84
+ <h2
85
+ class="text-highlighted line-clamp-2 [overflow-wrap:anywhere]"
86
+ :class="headingClass"
87
+ :title="heading"
88
+ >
89
+ <template v-if="headingSegments">
90
+ <template v-for="(segment, index) in headingSegments" :key="index">
91
+ <span>{{ segment }}</span>
92
+ <wbr />
93
+ </template>
94
+ </template>
95
+ <template v-else>{{ heading }}</template>
96
+ </h2>
97
+ <p
98
+ v-if="position || subtitle"
99
+ class="text-muted mt-0.5 text-[12.5px]"
100
+ :class="
101
+ subtitle ? 'flex min-w-0 items-baseline gap-x-1.5' : 'tabular-nums'
102
+ "
103
+ >
104
+ <span
105
+ v-if="position"
106
+ v-bind="partAttribute('position')"
107
+ :class="subtitle && 'shrink-0 tabular-nums'"
108
+ >
109
+ {{ position }}
110
+ </span>
111
+ <span v-if="position && subtitle" aria-hidden="true">·</span>
112
+ <span
113
+ v-if="subtitle"
114
+ v-bind="partAttribute('subtitle')"
115
+ class="min-w-0 truncate"
116
+ :class="subtitleClass"
117
+ :title="subtitle"
118
+ >
119
+ {{ subtitle }}
120
+ </span>
121
+ </p>
122
+ </div>
123
+ <component :is="component" v-if="component" v-bind="componentOptions" />
124
+ </div>
125
+ <div class="flex shrink-0 items-center gap-0.5">
126
+ <template v-if="navigation">
127
+ <UButton
128
+ v-bind="partAttribute('prev')"
129
+ icon="i-ph-caret-up"
130
+ color="neutral"
131
+ variant="ghost"
132
+ size="sm"
133
+ square
134
+ :aria-label="t('side_panel.previous')"
135
+ :disabled="!navigation.hasPrev"
136
+ @click="navigation.prev()"
137
+ />
138
+ <UButton
139
+ v-bind="partAttribute('next')"
140
+ icon="i-ph-caret-down"
141
+ color="neutral"
142
+ variant="ghost"
143
+ size="sm"
144
+ square
145
+ :aria-label="t('side_panel.next')"
146
+ :disabled="!navigation.hasNext"
147
+ @click="navigation.next()"
148
+ />
149
+ </template>
150
+ <slot />
151
+ </div>
152
+ </div>
153
+ </template>
@@ -0,0 +1,44 @@
1
+ <script setup lang="ts">
2
+ /**
3
+ * What a side panel or a drawer opened on a row shows in place of its
4
+ * component when the row its link names is gone (a 404): the state, and a
5
+ * close button.
6
+ */
7
+ import { computed } from "vue";
8
+ import DmsEmptyState from "../../../../components/empty-state/EmptyState.vue";
9
+
10
+ interface RowNotFoundProps {
11
+ title: string;
12
+ description: string;
13
+ }
14
+
15
+ interface RowNotFoundEmits {
16
+ (e: "close"): void;
17
+ }
18
+
19
+ defineProps<RowNotFoundProps>();
20
+ const emit = defineEmits<RowNotFoundEmits>();
21
+
22
+ const { t } = useI18n();
23
+
24
+ const actions = computed(() => [
25
+ {
26
+ label: t("side_panel.close"),
27
+ icon: "i-ph-x",
28
+ color: "neutral" as const,
29
+ variant: "outline" as const,
30
+ onClick: () => emit("close"),
31
+ },
32
+ ]);
33
+ </script>
34
+
35
+ <template>
36
+ <DmsEmptyState
37
+ data-dms-row-not-found
38
+ variant="no-result"
39
+ :title="title"
40
+ :description="description"
41
+ :actions="actions"
42
+ class="py-10"
43
+ />
44
+ </template>
@@ -0,0 +1,35 @@
1
+ import { markRaw } from "vue";
2
+ import type { DrawerRowHeader } from "../../../../composables/containers/types";
3
+
4
+ // The drawer's header title, as `DynamicDrawer` draws its own.
5
+ const DRAWER_ROW_HEADING_CLASS =
6
+ "text-[17px] leading-[1.3] font-[650] tracking-[-0.02em]";
7
+ // The `data-dms-row-drawer-*` hooks tests and styles find the parts by.
8
+ const DRAWER_ROW_HEADER_PART = "dms-row-drawer";
9
+
10
+ /**
11
+ * The props of `RowHeader` over a drawer: a row action's drawer and one a
12
+ * module opens with `useDrawer({ rowHeader })` draw the same header.
13
+ *
14
+ * @internal
15
+ */
16
+ export function drawerRowHeaderProps(
17
+ header: DrawerRowHeader,
18
+ title: string,
19
+ ): Record<string, unknown> {
20
+ return {
21
+ part: DRAWER_ROW_HEADER_PART,
22
+ heading: header.title ?? title,
23
+ positionLabel: header.positionLabel,
24
+ navigation: header.navigation,
25
+ headingClass: header.titleClass
26
+ ? `${DRAWER_ROW_HEADING_CLASS} ${header.titleClass}`
27
+ : DRAWER_ROW_HEADING_CLASS,
28
+ headingBreakAfter: header.titleBreakAfter,
29
+ subtitle: header.subtitle,
30
+ subtitleClass: header.subtitleClass,
31
+ // Kept out of the overlay's reactive props, as the body component is.
32
+ component: header.component && markRaw(header.component),
33
+ componentOptions: header.componentOptions,
34
+ };
35
+ }