@vc-shell/framework 1.1.21 → 1.1.23

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 (222) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/core/composables/useAppInsights/index.ts +11 -1
  3. package/core/composables/useBladeRegistry/index.ts +176 -0
  4. package/core/composables/useDynamicProperties/index.ts +380 -255
  5. package/core/composables/useErrorHandler/index.ts +2 -3
  6. package/core/composables/useKeyboardNavigation/index.ts +52 -10
  7. package/core/composables/useLanguages/index.ts +78 -78
  8. package/core/plugins/modularity/index.ts +17 -6
  9. package/core/services/global-search-service/index.ts +36 -0
  10. package/dist/core/composables/useAppInsights/index.d.ts +5 -2
  11. package/dist/core/composables/useAppInsights/index.d.ts.map +1 -1
  12. package/dist/core/composables/useBladeRegistry/index.d.ts +48 -0
  13. package/dist/core/composables/useBladeRegistry/index.d.ts.map +1 -0
  14. package/dist/core/composables/useDynamicProperties/index.d.ts +12 -9
  15. package/dist/core/composables/useDynamicProperties/index.d.ts.map +1 -1
  16. package/dist/core/composables/useErrorHandler/index.d.ts.map +1 -1
  17. package/dist/core/composables/useKeyboardNavigation/index.d.ts.map +1 -1
  18. package/dist/core/plugins/modularity/index.d.ts.map +1 -1
  19. package/dist/core/services/global-search-service/index.d.ts +10 -0
  20. package/dist/core/services/global-search-service/index.d.ts.map +1 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/index.d.ts +7 -12
  23. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/index.d.ts.map +1 -1
  24. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeActions.d.ts +15 -0
  25. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeActions.d.ts.map +1 -0
  26. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeRouteResolver.d.ts +11 -0
  27. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeRouteResolver.d.ts.map +1 -0
  28. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeState.d.ts +15 -0
  29. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeState.d.ts.map +1 -0
  30. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeWatchers.d.ts +6 -0
  31. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeWatchers.d.ts.map +1 -0
  32. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/routerUtils.d.ts +28 -0
  33. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/routerUtils.d.ts.map +1 -0
  34. package/dist/shared/components/blade-navigation/plugin.d.ts.map +1 -1
  35. package/dist/shared/components/blade-navigation/types/index.d.ts +5 -5
  36. package/dist/shared/components/blade-navigation/types/index.d.ts.map +1 -1
  37. package/dist/shared/components/notification-template/notification-template.vue.d.ts +2 -2
  38. package/dist/shared/components/notification-template/notification-template.vue.d.ts.map +1 -1
  39. package/dist/shared/components/notifications/components/notification-container/index.d.ts +6 -6
  40. package/dist/shared/components/notifications/components/notification-container/index.d.ts.map +1 -1
  41. package/dist/shared/modules/assets-manager/components/assets-manager/assets-manager.vue.d.ts +2 -2
  42. package/dist/shared/modules/dynamic/index.d.ts.map +1 -1
  43. package/dist/shared/modules/dynamic/pages/dynamic-blade-list.vue.d.ts +2 -2
  44. package/dist/shared/pages/InvitePage/components/invite/Invite.vue.d.ts +1 -1
  45. package/dist/shared/pages/InvitePage/components/invite/Invite.vue.d.ts.map +1 -1
  46. package/dist/shared/pages/ResetPasswordPage/components/reset-password/ResetPassword.vue.d.ts +1 -1
  47. package/dist/shared/pages/ResetPasswordPage/components/reset-password/ResetPassword.vue.d.ts.map +1 -1
  48. package/dist/tsconfig.tsbuildinfo +1 -1
  49. package/dist/ui/components/atoms/vc-badge/vc-badge.vue.d.ts +1 -1
  50. package/dist/ui/components/atoms/vc-badge/vc-badge.vue.d.ts.map +1 -1
  51. package/dist/ui/components/atoms/vc-container/vc-container.vue.d.ts +2 -2
  52. package/dist/ui/components/atoms/vc-icon/vc-icon.vue.d.ts +1 -1
  53. package/dist/ui/components/atoms/vc-icon/vc-icon.vue.d.ts.map +1 -1
  54. package/dist/ui/components/atoms/vc-icon/vc-material-icon.vue.d.ts +1 -1
  55. package/dist/ui/components/atoms/vc-icon/vc-material-icon.vue.d.ts.map +1 -1
  56. package/dist/ui/components/atoms/vc-progress/vc-progress.vue.d.ts +1 -1
  57. package/dist/ui/components/atoms/vc-progress/vc-progress.vue.d.ts.map +1 -1
  58. package/dist/ui/components/molecules/vc-editor/vc-editor.vue.d.ts +2 -2
  59. package/dist/ui/components/molecules/vc-file-upload/vc-file-upload.vue.d.ts +2 -2
  60. package/dist/ui/components/molecules/vc-file-upload/vc-file-upload.vue.d.ts.map +1 -1
  61. package/dist/ui/components/molecules/vc-input/vc-input.vue.d.ts +2 -2
  62. package/dist/ui/components/molecules/vc-input/vc-input.vue.d.ts.map +1 -1
  63. package/dist/ui/components/molecules/vc-input-dropdown/vc-input-dropdown.vue.d.ts +2 -0
  64. package/dist/ui/components/molecules/vc-input-dropdown/vc-input-dropdown.vue.d.ts.map +1 -1
  65. package/dist/ui/components/molecules/vc-pagination/vc-pagination.vue.d.ts +1 -1
  66. package/dist/ui/components/molecules/vc-pagination/vc-pagination.vue.d.ts.map +1 -1
  67. package/dist/ui/components/molecules/vc-rating/vc-rating.vue.d.ts +1 -1
  68. package/dist/ui/components/molecules/vc-rating/vc-rating.vue.d.ts.map +1 -1
  69. package/dist/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue.d.ts +1 -1
  70. package/dist/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue.d.ts.map +1 -1
  71. package/dist/ui/components/organisms/vc-app/vc-app.vue.d.ts.map +1 -1
  72. package/dist/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-buttons/_internal/vc-blade-toolbar-button/vc-blade-toolbar-base-button.vue.d.ts +1 -1
  73. package/dist/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-buttons/_internal/vc-blade-toolbar-button/vc-blade-toolbar-button.vue.d.ts +1 -1
  74. package/dist/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-buttons/_internal/vc-blade-toolbar-button/vc-blade-toolbar-button.vue.d.ts.map +1 -1
  75. package/dist/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-buttons/_internal/vc-blade-toolbar-button/vc-blade-toolbar-circle-button.vue.d.ts +1 -1
  76. package/dist/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-buttons/_internal/vc-blade-toolbar-button/vc-blade-toolbar-circle-button.vue.d.ts.map +1 -1
  77. package/dist/ui/components/organisms/vc-blade/vc-blade.backupsb.d.ts +3 -3
  78. package/dist/ui/components/organisms/vc-blade/vc-blade.vue.d.ts +1 -1
  79. package/dist/ui/components/organisms/vc-blade/vc-blade.vue.d.ts.map +1 -1
  80. package/dist/ui/components/organisms/vc-dynamic-property/vc-dynamic-property.vue.d.ts.map +1 -1
  81. package/dist/ui/components/organisms/vc-gallery/_internal/vc-gallery-item/vc-gallery-item.vue.d.ts +2 -2
  82. package/dist/ui/components/organisms/vc-gallery/_internal/vc-gallery-preview/vc-gallery-preview.vue.d.ts +1 -1
  83. package/dist/ui/components/organisms/vc-gallery/_internal/vc-gallery-preview/vc-gallery-preview.vue.d.ts.map +1 -1
  84. package/dist/ui/components/organisms/vc-gallery/vc-gallery.vue.d.ts +3 -3
  85. package/dist/ui/components/organisms/vc-gallery/vc-gallery.vue.d.ts.map +1 -1
  86. package/dist/ui/components/organisms/vc-popup/vc-popup.vue.d.ts +1 -1
  87. package/dist/ui/components/organisms/vc-popup/vc-popup.vue.d.ts.map +1 -1
  88. package/dist/ui/components/organisms/vc-table/_internal/vc-table-counter/vc-table-counter.vue.d.ts +1 -1
  89. package/dist/ui/components/organisms/vc-table/_internal/vc-table-counter/vc-table-counter.vue.d.ts.map +1 -1
  90. package/package.json +4 -4
  91. package/shared/components/blade-navigation/composables/useBladeNavigation/index.ts +199 -597
  92. package/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeActions.ts +151 -0
  93. package/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeRouteResolver.ts +243 -0
  94. package/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeState.ts +93 -0
  95. package/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeWatchers.ts +90 -0
  96. package/shared/components/blade-navigation/composables/useBladeNavigation/internal/routerUtils.ts +150 -0
  97. package/shared/components/blade-navigation/plugin.ts +17 -12
  98. package/shared/components/blade-navigation/types/index.ts +2 -4
  99. package/shared/components/notification-template/notification-template.vue +2 -2
  100. package/shared/modules/dynamic/index.ts +2 -8
  101. package/ui/components/molecules/vc-input-dropdown/vc-input-dropdown.vue +4 -0
  102. package/ui/components/organisms/vc-app/vc-app.vue +11 -6
  103. package/ui/components/organisms/vc-dynamic-property/vc-dynamic-property.vue +10 -1
  104. package/dist/apl-B2DGVGxc.js +0 -76
  105. package/dist/asciiarmor-2LVJmxlE.js +0 -34
  106. package/dist/asn1-jKiBa2Ya.js +0 -95
  107. package/dist/asterisk-DS281yxp.js +0 -271
  108. package/dist/brainfuck-C_p9pTT8.js +0 -34
  109. package/dist/clike-DGTaUp48.js +0 -620
  110. package/dist/clojure-CCKyeQKf.js +0 -800
  111. package/dist/cmake-CuaCgAKt.js +0 -28
  112. package/dist/cobol-BlTKFDRj.js +0 -72
  113. package/dist/coffeescript-BVCvwO8I.js +0 -179
  114. package/dist/commonlisp-D_kxz07b.js +0 -75
  115. package/dist/crystal-D309uH6_.js +0 -217
  116. package/dist/css-BkF-NPzE.js +0 -1553
  117. package/dist/cypher-BMq4Fwjl.js +0 -68
  118. package/dist/d-BZcgY6La.js +0 -127
  119. package/dist/diff-Cg9d_RX2.js +0 -18
  120. package/dist/dockerfile-DIy8NleC.js +0 -194
  121. package/dist/dtd-CtLokQ-U.js +0 -84
  122. package/dist/dylan-QYeExnWK.js +0 -234
  123. package/dist/ebnf-DUPDuY4r.js +0 -78
  124. package/dist/ecl-CiXN-g_D.js +0 -121
  125. package/dist/eiffel-yQhjl4T1.js +0 -110
  126. package/dist/elm-CNT9vbN0.js +0 -108
  127. package/dist/erlang-CFOYdy9e.js +0 -487
  128. package/dist/factor-DDOC7X6P.js +0 -65
  129. package/dist/fcl-CPC2WYrI.js +0 -103
  130. package/dist/forth-BmxRyE9S.js +0 -60
  131. package/dist/fortran-9bvPyrOW.js +0 -442
  132. package/dist/framework.js +0 -290
  133. package/dist/gas-cpmYfFX2.js +0 -183
  134. package/dist/gherkin-CJuwpceU.js +0 -34
  135. package/dist/groovy-DZeT_VM-.js +0 -146
  136. package/dist/haskell-Bvt3Qq1t.js +0 -375
  137. package/dist/haxe-70NVW1pR.js +0 -359
  138. package/dist/http-D9LttvKF.js +0 -44
  139. package/dist/idl-B6TRFYjl.js +0 -947
  140. package/dist/index-0xMdBsaq.js +0 -611
  141. package/dist/index-B6RKUVy8.js +0 -134
  142. package/dist/index-BXAxQnpt.js +0 -71
  143. package/dist/index-Ba3wq-Du.js +0 -147479
  144. package/dist/index-BgMVbUF5.js +0 -249
  145. package/dist/index-CE1KKIYQ.js +0 -268
  146. package/dist/index-CSMBCpee.js +0 -75
  147. package/dist/index-DCiQjA10.js +0 -58
  148. package/dist/index-DLIwas3l.js +0 -93
  149. package/dist/index-DwOkUUXE.js +0 -288
  150. package/dist/index-DwsB54rD.js +0 -308
  151. package/dist/index-Dyg1bSFn.js +0 -341
  152. package/dist/index-GpoS3tMh.js +0 -137
  153. package/dist/index-J_8wNmd3.js +0 -98
  154. package/dist/index-VLNTEhMQ.js +0 -156
  155. package/dist/index-WZKioztF.js +0 -299
  156. package/dist/index-_7O686wp.js +0 -538
  157. package/dist/index.css +0 -9
  158. package/dist/javascript-C2yteZeJ.js +0 -691
  159. package/dist/jinja2-DnB6dQmV.js +0 -154
  160. package/dist/julia-DpvXAuO6.js +0 -241
  161. package/dist/livescript-CanGTf8u.js +0 -272
  162. package/dist/lua-XplVlWi_.js +0 -217
  163. package/dist/mathematica-jaRHnSxC.js +0 -35
  164. package/dist/mbox-BctzC1hL.js +0 -76
  165. package/dist/mirc-CFBPAOaF.js +0 -72
  166. package/dist/mllike-BSnXJBGA.js +0 -272
  167. package/dist/modelica-vUgVs--1.js +0 -93
  168. package/dist/mscgen-Cpl0NYLN.js +0 -104
  169. package/dist/mumps-CQoS1kWX.js +0 -25
  170. package/dist/nginx-zDPm3Z74.js +0 -89
  171. package/dist/nsis-fePjrhq7.js +0 -62
  172. package/dist/ntriples-CsNjv2QF.js +0 -79
  173. package/dist/octave-C8PmmSRH.js +0 -143
  174. package/dist/oz-Ce8aN8oE.js +0 -151
  175. package/dist/pascal-De0D6mP7.js +0 -77
  176. package/dist/perl-B4bSCe1C.js +0 -915
  177. package/dist/pig-D24Z8EXi.js +0 -54
  178. package/dist/powershell-DkYVfTzP.js +0 -249
  179. package/dist/properties-Dn9wna3M.js +0 -26
  180. package/dist/protobuf-BPIjwpzm.js +0 -49
  181. package/dist/pug-CwAQJzGR.js +0 -248
  182. package/dist/puppet-nyd4dhjf.js +0 -45
  183. package/dist/python-BkR3uSy8.js +0 -313
  184. package/dist/q-DXjKs-tC.js +0 -83
  185. package/dist/r-LKEuhEGI.js +0 -104
  186. package/dist/rpm-IznJm2Xc.js +0 -57
  187. package/dist/ruby-CcYfvIk6.js +0 -228
  188. package/dist/sas-7E8yHoCW.js +0 -105
  189. package/dist/scheme-DjibxsNh.js +0 -124
  190. package/dist/shared/modules/dynamic/components/fields/storybook/pages/DynamicRender.d.ts +0 -110
  191. package/dist/shared/modules/dynamic/components/fields/storybook/pages/DynamicRender.d.ts.map +0 -1
  192. package/dist/shared/modules/dynamic/pages/dynamic-blade-form.vue.d.ts +0 -147
  193. package/dist/shared/modules/dynamic/pages/dynamic-blade-form.vue.d.ts.map +0 -1
  194. package/dist/shell-C0C2sNA_.js +0 -182
  195. package/dist/sieve-Bwz7vjP5.js +0 -72
  196. package/dist/simple-mode-B0dvCdAA.js +0 -89
  197. package/dist/smalltalk-Bhddl2pB.js +0 -48
  198. package/dist/solr-BNlsLglM.js +0 -41
  199. package/dist/sparql-FarWu_Gb.js +0 -197
  200. package/dist/spreadsheet-C-cy4P5N.js +0 -49
  201. package/dist/sql-CfG5lQ3l.js +0 -282
  202. package/dist/stex-Du4h4KAU.js +0 -129
  203. package/dist/stylus-CAdqWld3.js +0 -250
  204. package/dist/swift-DSxqR9R6.js +0 -230
  205. package/dist/tcl-xfoLljhY.js +0 -81
  206. package/dist/textile-D1AWE-pc.js +0 -295
  207. package/dist/tiddlywiki-5wqsXtSk.js +0 -155
  208. package/dist/tiki-__Kn3CeS.js +0 -181
  209. package/dist/toml-BHiuTcfn.js +0 -49
  210. package/dist/troff-D2UO-fKf.js +0 -35
  211. package/dist/ttcn-Bsa4sfRm.js +0 -123
  212. package/dist/ttcn-cfg-Bac_acMi.js +0 -88
  213. package/dist/turtle-xwJUxoPV.js +0 -80
  214. package/dist/vb-c2kQGd6-.js +0 -74
  215. package/dist/vbscript-1f_Dhg5H.js +0 -324
  216. package/dist/velocity-DJd0pTTC.js +0 -96
  217. package/dist/verilog-CiS1jyi5.js +0 -262
  218. package/dist/vhdl-T9HkrbI2.js +0 -106
  219. package/dist/webidl-CjfDENEo.js +0 -155
  220. package/dist/xquery-BUQdORAS.js +0 -422
  221. package/dist/yacas-C0absKBh.js +0 -73
  222. package/dist/z80-Pki2zAjW.js +0 -61
@@ -0,0 +1,151 @@
1
+ import { h, Component, isVNode } from "vue";
2
+ import { reactifyObject, reactiveComputed } from "@vueuse/core";
3
+ import * as _ from "lodash-es";
4
+ import { RouteLocationNormalized, Router, LocationQuery, NavigationFailure, RouteParams } from "vue-router";
5
+ import { useAppInsights } from "../../../../../../core/composables";
6
+ import { i18n } from "../../../../../../core/plugins/i18n";
7
+ import type { notification as NotificationServiceType } from "../../../../notifications";
8
+ import type { BladeVNode, IBladeEvent, BladeInstanceConstructor } from "../../../types";
9
+ import type { _createBladeStateManagement } from "./bladeState";
10
+ import type { _createRouterUtils } from "./routerUtils";
11
+
12
+ // --- Blade Actions Module ---
13
+ export function _createBladeActions(
14
+ router: Router,
15
+ route: RouteLocationNormalized,
16
+ bladeState: ReturnType<typeof _createBladeStateManagement>,
17
+ routerUtils: ReturnType<typeof _createRouterUtils>,
18
+ ensureBladeComponent: <Blade extends Component>(
19
+ bladeInput: BladeInstanceConstructor<Blade> | { name: string } | null | undefined,
20
+ ) => BladeInstanceConstructor<Blade>,
21
+ hasAccess: (permissions?: string | string[]) => boolean,
22
+ notificationServiceInstance: typeof NotificationServiceType,
23
+ i18nInstance: typeof i18n,
24
+ setupPageTrackingInstance: ReturnType<typeof useAppInsights>["setupPageTracking"],
25
+ ) {
26
+ async function openWorkspace<Blade extends Component>(
27
+ { blade: bladeInput, param, options }: IBladeEvent<Blade>,
28
+ query: LocationQuery | undefined = undefined,
29
+ routeParams: RouteParams = {},
30
+ replace = false,
31
+ ): Promise<NavigationFailure | void> {
32
+ const bladeComponent = ensureBladeComponent(bladeInput);
33
+ const componentProps = reactifyObject({
34
+ param,
35
+ options,
36
+ navigation: { idx: 0 },
37
+ });
38
+ const createdComponent = h(bladeComponent, componentProps) as BladeVNode;
39
+
40
+ try {
41
+ const isPrevented = await bladeState.removeBladesStartingFrom(0);
42
+ if (isPrevented) {
43
+ return;
44
+ }
45
+
46
+ if (createdComponent.type?.url) {
47
+ if (hasAccess(bladeComponent.permissions)) {
48
+ if (bladeState.blades.value.length > 0 && bladeState.blades.value[0].type.url === createdComponent.type.url) {
49
+ return;
50
+ }
51
+ bladeState.setActiveWorkspaceBlade(createdComponent);
52
+
53
+ const targetRoute = routerUtils.allRoutes.find((r) => r.path.endsWith(createdComponent.type?.url as string));
54
+ if (targetRoute && targetRoute.components) {
55
+ (targetRoute.components as Record<string, Component | BladeVNode>).default = createdComponent;
56
+ }
57
+
58
+ return router.push({
59
+ name: targetRoute?.name,
60
+ params: { ...routeParams, ...route.params },
61
+ query,
62
+ replace,
63
+ });
64
+ } else {
65
+ notificationServiceInstance.error(i18nInstance.global.t("PERMISSION_MESSAGES.ACCESS_RESTRICTED"), {
66
+ timeout: 3000,
67
+ });
68
+ }
69
+ }
70
+ } catch (e) {
71
+ console.error(e);
72
+ const bladeNameForError =
73
+ bladeComponent?.type?.name ||
74
+ (typeof bladeInput === "object" && "name" in bladeInput ? bladeInput.name : "Unknown");
75
+ throw new Error(`Opening workspace '${bladeNameForError}' is prevented or failed.`);
76
+ }
77
+ }
78
+
79
+ async function openBlade<Blade extends Component>(
80
+ args: IBladeEvent<Blade>,
81
+ isWorkspaceTarget = false,
82
+ sourceBladeInstance?: BladeVNode,
83
+ ): Promise<void | NavigationFailure> {
84
+ const { blade: bladeInput, param, options, onOpen, onClose, replaceCurrentBlade = false } = args;
85
+ const bladeComponent = ensureBladeComponent(bladeInput);
86
+
87
+ if (isWorkspaceTarget) {
88
+ return openWorkspace({ blade: bladeComponent, param, options });
89
+ }
90
+
91
+ try {
92
+ const instanceComponent = sourceBladeInstance || bladeState.activeWorkspace.value;
93
+
94
+ if (!instanceComponent) {
95
+ console.error("No active workspace or source blade instance provided to open a blade into.");
96
+ throw new Error("No workspace or source blade context to open blade.");
97
+ }
98
+ if (!isVNode(instanceComponent)) {
99
+ console.error("Source blade instance or active workspace is not a valid VNode.", instanceComponent);
100
+ throw new Error("Internal error: Invalid instanceComponent for opening blade.");
101
+ }
102
+
103
+ const instanceComponentIndex = _.findLastIndex(bladeState.blades.value, (x): x is BladeVNode => {
104
+ if (!x) return false;
105
+ return _.isEqual(x.type, instanceComponent.type);
106
+ });
107
+
108
+ const parentBladeIndex = instanceComponentIndex >= 0 ? instanceComponentIndex : -1;
109
+ const childBladeIndex = parentBladeIndex + 1;
110
+
111
+ let isPrevented = false;
112
+ if (bladeState.blades.value[childBladeIndex]) {
113
+ isPrevented = await bladeState.removeBladesStartingFrom(childBladeIndex);
114
+ }
115
+
116
+ const currentBladeIdx = parentBladeIndex;
117
+
118
+ const bladeNode = h(
119
+ bladeComponent,
120
+ Object.assign(
121
+ {},
122
+ reactiveComputed(() => ({ options, param })),
123
+ { navigation: { onOpen, onClose, idx: currentBladeIdx + 1, isVisible: true } },
124
+ ),
125
+ ) as BladeVNode;
126
+
127
+ if (!isPrevented) {
128
+ if (hasAccess(bladeComponent.permissions)) {
129
+ if (replaceCurrentBlade && currentBladeIdx >= 0 && bladeState.blades.value[currentBladeIdx]) {
130
+ if (bladeState.blades.value[currentBladeIdx]) {
131
+ bladeState.blades.value[currentBladeIdx].props.navigation.isVisible = false;
132
+ }
133
+ }
134
+ setupPageTrackingInstance.beforeEach({ name: bladeNode.type.name! });
135
+ bladeState.addBlade(bladeNode);
136
+ } else {
137
+ notificationServiceInstance.error(i18nInstance.global.t("PERMISSION_MESSAGES.ACCESS_RESTRICTED"), {
138
+ timeout: 3000,
139
+ });
140
+ }
141
+ }
142
+ } catch (e) {
143
+ console.error("Error in openBlade:", e);
144
+ }
145
+ }
146
+
147
+ return {
148
+ openWorkspace,
149
+ openBlade,
150
+ };
151
+ }
@@ -0,0 +1,243 @@
1
+ import { Component, isVNode } from "vue";
2
+ import * as _ from "lodash-es";
3
+ import { RouteLocationNormalized, Router, RouteLocationRaw } from "vue-router";
4
+ import { IBladeRegistry } from "../../../../../../core/composables/useBladeRegistry";
5
+ import type { BladeInstanceConstructor, BladeVNode } from "../../../types";
6
+ import type { _createBladeStateManagement } from "./bladeState";
7
+ import type { _createRouterUtils } from "./routerUtils";
8
+ import type { _createBladeActions } from "./bladeActions";
9
+
10
+ /**
11
+ * Checks if a component is routable from URL
12
+ */
13
+ function isComponentRoutable(component: BladeInstanceConstructor | undefined): boolean {
14
+ return !(component && typeof component.routable === "boolean" && !component.routable);
15
+ }
16
+
17
+ // --- Blade Route Resolver Module ---
18
+ export function _createBladeRouteResolver(
19
+ router: Router,
20
+ route: RouteLocationNormalized,
21
+ bladeRegistry: IBladeRegistry,
22
+ routerUtils: ReturnType<typeof _createRouterUtils>,
23
+ bladeActions: ReturnType<typeof _createBladeActions>,
24
+ bladeState: ReturnType<typeof _createBladeStateManagement>,
25
+ ensureBladeComponent: <Blade extends Component>(
26
+ bladeInput: BladeInstanceConstructor<Blade> | { name: string } | null | undefined,
27
+ ) => BladeInstanceConstructor<Blade>,
28
+ hasAccess: (permissions?: string | string[]) => boolean,
29
+ ) {
30
+ // Internal helper to find a blade component based on a URL segment
31
+ function findBladeComponentByUrlSegment(segment: string): BladeInstanceConstructor | undefined {
32
+ try {
33
+ const allRoutes = router.getRoutes();
34
+ const segmentWithSlash = segment.startsWith("/") ? segment : `/${segment}`;
35
+ const segmentWithoutSlash = segment.startsWith("/") ? segment.substring(1) : segment;
36
+
37
+ // 1. Check router's resolved routes by path suffix.
38
+ for (const r of allRoutes) {
39
+ let resolvedComponent: unknown;
40
+ let componentToCheck: unknown;
41
+
42
+ try {
43
+ const resolvedRoute = router.resolve({ name: r.name || undefined, path: !r.name ? r.path : undefined });
44
+
45
+ if (resolvedRoute.matched.length > 0) {
46
+ resolvedComponent = resolvedRoute.matched[resolvedRoute.matched.length - 1]?.components?.default;
47
+ }
48
+
49
+ componentToCheck = resolvedComponent;
50
+ if (resolvedComponent && isVNode(resolvedComponent) && resolvedComponent.type) {
51
+ componentToCheck = resolvedComponent.type;
52
+ }
53
+ } catch (e) {
54
+ // Silently continue to next route if resolution fails
55
+ continue;
56
+ }
57
+
58
+ const comp = componentToCheck as BladeInstanceConstructor;
59
+
60
+ if (comp && comp.isBlade && typeof comp.url === "string") {
61
+ const compUrlNormalized = comp.url.startsWith("/") ? comp.url : `/${comp.url}`;
62
+ if (compUrlNormalized === segmentWithSlash) {
63
+ return comp;
64
+ }
65
+ }
66
+ }
67
+
68
+ const potentialNames = [segmentWithoutSlash];
69
+ if (segment && segment !== "/" && segment.includes("-")) potentialNames.push(segment);
70
+
71
+ for (const nameToTry of _.uniq(potentialNames.filter(Boolean))) {
72
+ const bladeByName = bladeRegistry.getBladeComponent(nameToTry);
73
+ if (bladeByName && bladeByName.isBlade && typeof bladeByName.url === "string") {
74
+ const bladeByNameUrlNormalized = bladeByName.url.startsWith("/") ? bladeByName.url : `/${bladeByName.url}`;
75
+ if (bladeByNameUrlNormalized === segmentWithSlash) {
76
+ return bladeByName;
77
+ }
78
+ }
79
+ }
80
+
81
+ const bladeByFullPath = bladeRegistry.getBlade(segmentWithSlash);
82
+ if (bladeByFullPath && bladeByFullPath.component && bladeByFullPath.component.isBlade) {
83
+ const componentUrlNormalized = bladeByFullPath.component.url?.startsWith("/")
84
+ ? bladeByFullPath.component.url
85
+ : `/${bladeByFullPath.component.url}`;
86
+ if (componentUrlNormalized === segmentWithSlash) {
87
+ return bladeByFullPath.component as BladeInstanceConstructor;
88
+ }
89
+ }
90
+
91
+ return undefined;
92
+ } catch (error) {
93
+ console.error("Error in findBladeComponentByUrlSegment:", error instanceof Error ? error.message : String(error));
94
+ return undefined;
95
+ }
96
+ }
97
+
98
+ async function routeResolver(to: RouteLocationNormalized): Promise<RouteLocationRaw | undefined> {
99
+ const { path: toPath, query: toQueryFromTo } = to;
100
+ let currentConstructedPath: string | undefined;
101
+ if (bladeState.activeWorkspace.value) {
102
+ currentConstructedPath = routerUtils.constructBladePath(
103
+ bladeState.activeWorkspace.value,
104
+ _.last(bladeState.blades.value) as BladeVNode | undefined,
105
+ );
106
+ }
107
+ const currentFullPath = currentConstructedPath
108
+ ? (routerUtils.mainRouteBaseParamURL.value &&
109
+ !currentConstructedPath.startsWith(routerUtils.mainRouteBaseParamURL.value)
110
+ ? routerUtils.mainRouteBaseParamURL.value
111
+ : "") + currentConstructedPath
112
+ : undefined;
113
+
114
+ if (
115
+ currentFullPath &&
116
+ decodeURIComponent(to.fullPath).startsWith(decodeURIComponent(currentFullPath)) &&
117
+ bladeState.blades.value.length > 0
118
+ ) {
119
+ const toPathDecoded = decodeURIComponent(to.path);
120
+ const currentPathDecoded = decodeURIComponent(currentFullPath);
121
+ const toSegments = toPathDecoded.split("/").filter(Boolean);
122
+ const currentSegments = currentPathDecoded.split("/").filter(Boolean);
123
+
124
+ if (toPathDecoded.startsWith(currentPathDecoded) && toSegments.length === currentSegments.length) {
125
+ const targetRoute = routerUtils.allRoutes.find((r) => r.name === to.name);
126
+ if (
127
+ targetRoute &&
128
+ bladeState.activeWorkspace.value &&
129
+ targetRoute.components?.default === bladeState.activeWorkspace.value.type
130
+ ) {
131
+ return undefined;
132
+ }
133
+ }
134
+ }
135
+
136
+ const parsedUrl = routerUtils.parseUrlParams(to.path, to.params);
137
+
138
+ if (!parsedUrl || !parsedUrl.workspace) {
139
+ const rootRoute = routerUtils.goToRoot();
140
+ if (typeof rootRoute === "object" && rootRoute.name && rootRoute.name === to.name) {
141
+ return undefined;
142
+ }
143
+ return rootRoute;
144
+ }
145
+
146
+ const workspaceSegment = parsedUrl.workspace;
147
+ const bladeSegment = parsedUrl.blade;
148
+ const paramSegment = parsedUrl.param;
149
+
150
+ const workspaceComponent = findBladeComponentByUrlSegment(workspaceSegment);
151
+
152
+ if (!isComponentRoutable(workspaceComponent)) {
153
+ await bladeState.removeBladesStartingFrom(0);
154
+ bladeState.clearActiveWorkspaceState();
155
+ return routerUtils.goToRoot();
156
+ }
157
+
158
+ if (!workspaceComponent || !workspaceComponent.isWorkspace) {
159
+ const rootRoute = routerUtils.goToRoot();
160
+ if (typeof rootRoute === "object" && rootRoute.name && rootRoute.name === to.name) {
161
+ return undefined;
162
+ }
163
+ return rootRoute;
164
+ }
165
+
166
+ if (!hasAccess(workspaceComponent.permissions)) {
167
+ return routerUtils.goToRoot();
168
+ }
169
+
170
+ // Determine the parameter to pass to the workspace.
171
+ // It depends on whether a bladeSegment exists and if that blade shares its param with the parent.
172
+ let paramForWorkspace: string | undefined = undefined;
173
+ if (bladeSegment) {
174
+ const childBladeComponent = findBladeComponentByUrlSegment(bladeSegment);
175
+ // Check if workspace and child blade are from the same module using moduleUid
176
+ // and if the child blade is routable
177
+ if (
178
+ workspaceComponent &&
179
+ childBladeComponent &&
180
+ isComponentRoutable(childBladeComponent) &&
181
+ Object.prototype.hasOwnProperty.call(workspaceComponent, "moduleUid") &&
182
+ Object.prototype.hasOwnProperty.call(childBladeComponent, "moduleUid") &&
183
+ (workspaceComponent as BladeInstanceConstructor & { moduleUid?: string }).moduleUid ===
184
+ (childBladeComponent as BladeInstanceConstructor & { moduleUid?: string }).moduleUid
185
+ ) {
186
+ paramForWorkspace = paramSegment;
187
+ } else {
188
+ paramForWorkspace = undefined; // Default: child blade exists, its param is not for the workspace
189
+ }
190
+ } else {
191
+ paramForWorkspace = paramSegment; // No child blade segment, workspace gets the param from URL if present
192
+ }
193
+
194
+ const openWorkspaceResult = await bladeActions.openWorkspace(
195
+ {
196
+ blade: ensureBladeComponent(workspaceComponent),
197
+ param: paramForWorkspace,
198
+ },
199
+ to.query,
200
+ to.params,
201
+ true,
202
+ );
203
+
204
+ if (openWorkspaceResult && "redirectedFrom" in openWorkspaceResult) {
205
+ return undefined;
206
+ }
207
+
208
+ let lastOpenedBladeComponent = workspaceComponent;
209
+ let effectiveBladeSegment = bladeSegment;
210
+
211
+ if (effectiveBladeSegment) {
212
+ const bladeComponent = findBladeComponentByUrlSegment(effectiveBladeSegment);
213
+
214
+ if (!isComponentRoutable(bladeComponent)) {
215
+ effectiveBladeSegment = undefined;
216
+ }
217
+
218
+ if (effectiveBladeSegment && bladeComponent && bladeComponent.isBlade) {
219
+ if (!hasAccess(bladeComponent.permissions)) {
220
+ // No access to blade, workspace remains open
221
+ } else {
222
+ await bladeActions.openBlade({
223
+ blade: ensureBladeComponent(bladeComponent),
224
+ param: paramSegment,
225
+ });
226
+ lastOpenedBladeComponent = bladeComponent;
227
+ }
228
+ }
229
+ }
230
+
231
+ if (lastOpenedBladeComponent.name && String(lastOpenedBladeComponent.name) === String(to.name)) {
232
+ return undefined;
233
+ }
234
+
235
+ const finalTargetRoute = routerUtils.allRoutes.find((r) => String(r.name) === String(to.name));
236
+ if (finalTargetRoute && bladeState.blades.value.some((b) => b.type === finalTargetRoute.components?.default)) {
237
+ return undefined;
238
+ }
239
+
240
+ return undefined;
241
+ }
242
+ return routeResolver;
243
+ }
@@ -0,0 +1,93 @@
1
+ import { computed, shallowRef, Ref, toValue } from "vue";
2
+ import type { Router } from "vue-router";
3
+ import type { BladeVNode } from "../../../types";
4
+
5
+ // --- Blade State Management Module ---
6
+ export function _createBladeStateManagement(router: Router) {
7
+ const blades: Ref<BladeVNode[]> = shallowRef<BladeVNode[]>([]);
8
+ const activeWorkspace: Ref<BladeVNode | undefined> = shallowRef<BladeVNode | undefined>();
9
+
10
+ async function removeBladesStartingFrom(startIndex: number): Promise<boolean> {
11
+ if (blades.value.length === 0 || startIndex < 0) {
12
+ return false;
13
+ }
14
+
15
+ try {
16
+ const bladesToClose = blades.value.slice(startIndex).reverse();
17
+ let isPrevented = false;
18
+
19
+ for (const blade of bladesToClose) {
20
+ if (blade.props?.navigation?.onBeforeClose) {
21
+ const result = await blade.props.navigation.onBeforeClose();
22
+ if (result === false) {
23
+ isPrevented = true;
24
+ break; // Stop further checks if one blade prevents closing
25
+ }
26
+ }
27
+ }
28
+
29
+ if (!isPrevented) {
30
+ const prevBlade = blades.value[startIndex - 1];
31
+
32
+ if (startIndex > 0 && prevBlade?.props?.navigation?.isVisible === false) {
33
+ prevBlade.props.navigation.isVisible = true;
34
+ }
35
+
36
+ // Clear param of table blade when closing child blade to prevent table row selection from being preserved
37
+ if (
38
+ prevBlade &&
39
+ prevBlade.props?.navigation?.idx === 0 && // Is a workspace or first blade
40
+ toValue(prevBlade.props?.param) === toValue(blades.value[startIndex]?.props?.param)
41
+ ) {
42
+ prevBlade.props.param = undefined;
43
+ }
44
+
45
+ blades.value.splice(startIndex);
46
+ blades.value = [...blades.value]; // Trigger reactivity for watchers
47
+ }
48
+ return isPrevented;
49
+ } finally {
50
+ // Cleanup completed
51
+ }
52
+ }
53
+
54
+ function addBlade(bladeNode: BladeVNode): void {
55
+ blades.value.push(bladeNode);
56
+ blades.value = [...blades.value];
57
+ }
58
+
59
+ function replaceAllBlades(newBlades: BladeVNode[]): void {
60
+ blades.value = newBlades;
61
+ }
62
+
63
+ function setActiveWorkspaceBlade(workspaceBlade?: BladeVNode): void {
64
+ if (workspaceBlade && workspaceBlade.props?.navigation) {
65
+ workspaceBlade.props.navigation.idx = 0;
66
+ }
67
+ activeWorkspace.value = workspaceBlade;
68
+ if (workspaceBlade) {
69
+ blades.value = [workspaceBlade];
70
+ // Ensure subsequent blades are closed if workspace changes directly
71
+ removeBladesStartingFrom(1);
72
+ } else {
73
+ blades.value = [];
74
+ }
75
+ }
76
+
77
+ function clearActiveWorkspaceState(): void {
78
+ activeWorkspace.value = undefined;
79
+ blades.value = [];
80
+ }
81
+
82
+ return {
83
+ blades: computed(() => blades.value), // Expose as computed for readonly reactive access outside
84
+ _bladesRef: blades, // Internal ref for mutation
85
+ activeWorkspace: computed(() => activeWorkspace.value),
86
+ _activeWorkspaceRef: activeWorkspace,
87
+ removeBladesStartingFrom,
88
+ addBlade,
89
+ replaceAllBlades,
90
+ setActiveWorkspaceBlade,
91
+ clearActiveWorkspaceState,
92
+ };
93
+ }
@@ -0,0 +1,90 @@
1
+ import { watch, isVNode } from "vue";
2
+ import { watchDebounced } from "@vueuse/core";
3
+ import * as _ from "lodash-es";
4
+ import { RouteLocationNormalized, Router } from "vue-router";
5
+ import { useAppInsights } from "../../../../../../core/composables";
6
+ import type { BladeVNode } from "../../../types";
7
+ import type { _createBladeStateManagement } from "./bladeState";
8
+ import type { _createRouterUtils } from "./routerUtils";
9
+
10
+ // --- Watchers Module ---
11
+ export function _createBladeWatchers(
12
+ router: Router,
13
+ route: RouteLocationNormalized,
14
+ bladeState: ReturnType<typeof _createBladeStateManagement>,
15
+ routerUtils: ReturnType<typeof _createRouterUtils>,
16
+ setupPageTracking: ReturnType<typeof useAppInsights>["setupPageTracking"],
17
+ ) {
18
+ watch(
19
+ () => route.path,
20
+ async (newPath, oldPath) => {
21
+ const newWorkspaceUrlSegment = routerUtils.parseWorkspaceUrlPath(newPath);
22
+ const currentActiveWorkspaceUrl = bladeState.activeWorkspace.value?.type.url;
23
+
24
+ if (newWorkspaceUrlSegment && newWorkspaceUrlSegment !== currentActiveWorkspaceUrl) {
25
+ const wsRouteComponent =
26
+ routerUtils.getResolvedRouteComponent(newWorkspaceUrlSegment) ||
27
+ (router.resolve({ path: newWorkspaceUrlSegment }).matched?.[1]?.components?.default as BladeVNode);
28
+
29
+ if (wsRouteComponent && isVNode(wsRouteComponent) && wsRouteComponent.type.isBlade) {
30
+ const isPrevented = await bladeState.removeBladesStartingFrom(0);
31
+ if (!isPrevented) {
32
+ bladeState.setActiveWorkspaceBlade(wsRouteComponent);
33
+ } else {
34
+ if (oldPath && oldPath !== newPath) router.push({ path: oldPath });
35
+ }
36
+ } else {
37
+ const isPrevented = await bladeState.removeBladesStartingFrom(0);
38
+ if (!isPrevented) {
39
+ bladeState.clearActiveWorkspaceState();
40
+ } else {
41
+ if (oldPath && oldPath !== newPath) router.push({ path: oldPath });
42
+ }
43
+ }
44
+ } else if (!newWorkspaceUrlSegment && bladeState.activeWorkspace.value) {
45
+ const isPrevented = await bladeState.removeBladesStartingFrom(0);
46
+ if (!isPrevented) {
47
+ bladeState.clearActiveWorkspaceState();
48
+ } else {
49
+ if (oldPath && oldPath !== newPath) router.push({ path: oldPath });
50
+ }
51
+ }
52
+ },
53
+ { immediate: true },
54
+ );
55
+
56
+ watchDebounced(
57
+ bladeState.blades,
58
+ (newBlades) => {
59
+ const workspace = newBlades[0];
60
+ const lastBladeWithUrl = _.findLast(
61
+ newBlades,
62
+ (b: BladeVNode) => b && b.type.url && b.props?.navigation?.isVisible !== false,
63
+ );
64
+
65
+ if (workspace?.type?.url) {
66
+ const constructedPath = routerUtils.constructBladePath(workspace, lastBladeWithUrl as BladeVNode | undefined);
67
+ if (constructedPath) {
68
+ const query = routerUtils.getURLQuery();
69
+ const fullPath =
70
+ (routerUtils.mainRouteBaseParamURL.value &&
71
+ !constructedPath.startsWith(routerUtils.mainRouteBaseParamURL.value)
72
+ ? routerUtils.mainRouteBaseParamURL.value
73
+ : "") +
74
+ constructedPath +
75
+ (query.params ? "?" + query.params : "");
76
+
77
+ if (route.fullPath !== fullPath) {
78
+ router.options.history.replace(fullPath);
79
+ if (lastBladeWithUrl && (lastBladeWithUrl as BladeVNode).type.name) {
80
+ setupPageTracking.afterEach({ name: (lastBladeWithUrl as BladeVNode).type.name!, fullPath });
81
+ } else if (workspace.type.name) {
82
+ setupPageTracking.afterEach({ name: workspace.type.name, fullPath });
83
+ }
84
+ }
85
+ }
86
+ }
87
+ },
88
+ { deep: true, debounce: 10, flush: "post" },
89
+ );
90
+ }