@vc-shell/framework 1.1.22 → 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 (220) hide show
  1. package/CHANGELOG.md +12 -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/useLanguages/index.ts +78 -78
  7. package/core/plugins/modularity/index.ts +17 -6
  8. package/core/services/global-search-service/index.ts +36 -0
  9. package/dist/core/composables/useAppInsights/index.d.ts +5 -2
  10. package/dist/core/composables/useAppInsights/index.d.ts.map +1 -1
  11. package/dist/core/composables/useBladeRegistry/index.d.ts +48 -0
  12. package/dist/core/composables/useBladeRegistry/index.d.ts.map +1 -0
  13. package/dist/core/composables/useDynamicProperties/index.d.ts +12 -9
  14. package/dist/core/composables/useDynamicProperties/index.d.ts.map +1 -1
  15. package/dist/core/composables/useErrorHandler/index.d.ts.map +1 -1
  16. package/dist/core/plugins/modularity/index.d.ts.map +1 -1
  17. package/dist/core/services/global-search-service/index.d.ts +10 -0
  18. package/dist/core/services/global-search-service/index.d.ts.map +1 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/index.d.ts +7 -12
  21. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/index.d.ts.map +1 -1
  22. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeActions.d.ts +15 -0
  23. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeActions.d.ts.map +1 -0
  24. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeRouteResolver.d.ts +11 -0
  25. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeRouteResolver.d.ts.map +1 -0
  26. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeState.d.ts +15 -0
  27. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeState.d.ts.map +1 -0
  28. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeWatchers.d.ts +6 -0
  29. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeWatchers.d.ts.map +1 -0
  30. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/routerUtils.d.ts +28 -0
  31. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/internal/routerUtils.d.ts.map +1 -0
  32. package/dist/shared/components/blade-navigation/plugin.d.ts.map +1 -1
  33. package/dist/shared/components/blade-navigation/types/index.d.ts +5 -5
  34. package/dist/shared/components/blade-navigation/types/index.d.ts.map +1 -1
  35. package/dist/shared/components/notification-template/notification-template.vue.d.ts +2 -2
  36. package/dist/shared/components/notification-template/notification-template.vue.d.ts.map +1 -1
  37. package/dist/shared/components/notifications/components/notification-container/index.d.ts +6 -6
  38. package/dist/shared/components/notifications/components/notification-container/index.d.ts.map +1 -1
  39. package/dist/shared/modules/assets-manager/components/assets-manager/assets-manager.vue.d.ts +2 -2
  40. package/dist/shared/modules/dynamic/index.d.ts.map +1 -1
  41. package/dist/shared/modules/dynamic/pages/dynamic-blade-list.vue.d.ts +2 -2
  42. package/dist/shared/pages/InvitePage/components/invite/Invite.vue.d.ts +1 -1
  43. package/dist/shared/pages/InvitePage/components/invite/Invite.vue.d.ts.map +1 -1
  44. package/dist/shared/pages/ResetPasswordPage/components/reset-password/ResetPassword.vue.d.ts +1 -1
  45. package/dist/shared/pages/ResetPasswordPage/components/reset-password/ResetPassword.vue.d.ts.map +1 -1
  46. package/dist/tsconfig.tsbuildinfo +1 -1
  47. package/dist/ui/components/atoms/vc-badge/vc-badge.vue.d.ts +1 -1
  48. package/dist/ui/components/atoms/vc-badge/vc-badge.vue.d.ts.map +1 -1
  49. package/dist/ui/components/atoms/vc-container/vc-container.vue.d.ts +2 -2
  50. package/dist/ui/components/atoms/vc-icon/vc-icon.vue.d.ts +1 -1
  51. package/dist/ui/components/atoms/vc-icon/vc-icon.vue.d.ts.map +1 -1
  52. package/dist/ui/components/atoms/vc-icon/vc-material-icon.vue.d.ts +1 -1
  53. package/dist/ui/components/atoms/vc-icon/vc-material-icon.vue.d.ts.map +1 -1
  54. package/dist/ui/components/atoms/vc-progress/vc-progress.vue.d.ts +1 -1
  55. package/dist/ui/components/atoms/vc-progress/vc-progress.vue.d.ts.map +1 -1
  56. package/dist/ui/components/molecules/vc-editor/vc-editor.vue.d.ts +2 -2
  57. package/dist/ui/components/molecules/vc-file-upload/vc-file-upload.vue.d.ts +2 -2
  58. package/dist/ui/components/molecules/vc-file-upload/vc-file-upload.vue.d.ts.map +1 -1
  59. package/dist/ui/components/molecules/vc-input/vc-input.vue.d.ts +2 -2
  60. package/dist/ui/components/molecules/vc-input/vc-input.vue.d.ts.map +1 -1
  61. package/dist/ui/components/molecules/vc-input-dropdown/vc-input-dropdown.vue.d.ts +2 -0
  62. package/dist/ui/components/molecules/vc-input-dropdown/vc-input-dropdown.vue.d.ts.map +1 -1
  63. package/dist/ui/components/molecules/vc-pagination/vc-pagination.vue.d.ts +1 -1
  64. package/dist/ui/components/molecules/vc-pagination/vc-pagination.vue.d.ts.map +1 -1
  65. package/dist/ui/components/molecules/vc-rating/vc-rating.vue.d.ts +1 -1
  66. package/dist/ui/components/molecules/vc-rating/vc-rating.vue.d.ts.map +1 -1
  67. 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
  68. 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
  69. package/dist/ui/components/organisms/vc-app/vc-app.vue.d.ts.map +1 -1
  70. 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
  71. 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
  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-button.vue.d.ts.map +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-circle-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-circle-button.vue.d.ts.map +1 -1
  75. package/dist/ui/components/organisms/vc-blade/vc-blade.backupsb.d.ts +3 -3
  76. package/dist/ui/components/organisms/vc-blade/vc-blade.vue.d.ts +1 -1
  77. package/dist/ui/components/organisms/vc-blade/vc-blade.vue.d.ts.map +1 -1
  78. package/dist/ui/components/organisms/vc-dynamic-property/vc-dynamic-property.vue.d.ts.map +1 -1
  79. package/dist/ui/components/organisms/vc-gallery/_internal/vc-gallery-item/vc-gallery-item.vue.d.ts +2 -2
  80. package/dist/ui/components/organisms/vc-gallery/_internal/vc-gallery-preview/vc-gallery-preview.vue.d.ts +1 -1
  81. package/dist/ui/components/organisms/vc-gallery/_internal/vc-gallery-preview/vc-gallery-preview.vue.d.ts.map +1 -1
  82. package/dist/ui/components/organisms/vc-gallery/vc-gallery.vue.d.ts +3 -3
  83. package/dist/ui/components/organisms/vc-gallery/vc-gallery.vue.d.ts.map +1 -1
  84. package/dist/ui/components/organisms/vc-popup/vc-popup.vue.d.ts +1 -1
  85. package/dist/ui/components/organisms/vc-popup/vc-popup.vue.d.ts.map +1 -1
  86. package/dist/ui/components/organisms/vc-table/_internal/vc-table-counter/vc-table-counter.vue.d.ts +1 -1
  87. package/dist/ui/components/organisms/vc-table/_internal/vc-table-counter/vc-table-counter.vue.d.ts.map +1 -1
  88. package/package.json +4 -4
  89. package/shared/components/blade-navigation/composables/useBladeNavigation/index.ts +199 -597
  90. package/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeActions.ts +151 -0
  91. package/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeRouteResolver.ts +243 -0
  92. package/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeState.ts +93 -0
  93. package/shared/components/blade-navigation/composables/useBladeNavigation/internal/bladeWatchers.ts +90 -0
  94. package/shared/components/blade-navigation/composables/useBladeNavigation/internal/routerUtils.ts +150 -0
  95. package/shared/components/blade-navigation/plugin.ts +17 -12
  96. package/shared/components/blade-navigation/types/index.ts +2 -4
  97. package/shared/components/notification-template/notification-template.vue +2 -2
  98. package/shared/modules/dynamic/index.ts +2 -8
  99. package/ui/components/molecules/vc-input-dropdown/vc-input-dropdown.vue +4 -0
  100. package/ui/components/organisms/vc-app/vc-app.vue +11 -6
  101. package/ui/components/organisms/vc-dynamic-property/vc-dynamic-property.vue +10 -1
  102. package/dist/apl-B2DGVGxc.js +0 -76
  103. package/dist/asciiarmor-2LVJmxlE.js +0 -34
  104. package/dist/asn1-jKiBa2Ya.js +0 -95
  105. package/dist/asterisk-DS281yxp.js +0 -271
  106. package/dist/brainfuck-C_p9pTT8.js +0 -34
  107. package/dist/clike-DGTaUp48.js +0 -620
  108. package/dist/clojure-CCKyeQKf.js +0 -800
  109. package/dist/cmake-CuaCgAKt.js +0 -28
  110. package/dist/cobol-BlTKFDRj.js +0 -72
  111. package/dist/coffeescript-BVCvwO8I.js +0 -179
  112. package/dist/commonlisp-D_kxz07b.js +0 -75
  113. package/dist/crystal-D309uH6_.js +0 -217
  114. package/dist/css-BkF-NPzE.js +0 -1553
  115. package/dist/cypher-BMq4Fwjl.js +0 -68
  116. package/dist/d-BZcgY6La.js +0 -127
  117. package/dist/diff-Cg9d_RX2.js +0 -18
  118. package/dist/dockerfile-DIy8NleC.js +0 -194
  119. package/dist/dtd-CtLokQ-U.js +0 -84
  120. package/dist/dylan-QYeExnWK.js +0 -234
  121. package/dist/ebnf-DUPDuY4r.js +0 -78
  122. package/dist/ecl-CiXN-g_D.js +0 -121
  123. package/dist/eiffel-yQhjl4T1.js +0 -110
  124. package/dist/elm-CNT9vbN0.js +0 -108
  125. package/dist/erlang-CFOYdy9e.js +0 -487
  126. package/dist/factor-DDOC7X6P.js +0 -65
  127. package/dist/fcl-CPC2WYrI.js +0 -103
  128. package/dist/forth-BmxRyE9S.js +0 -60
  129. package/dist/fortran-9bvPyrOW.js +0 -442
  130. package/dist/framework.js +0 -290
  131. package/dist/gas-cpmYfFX2.js +0 -183
  132. package/dist/gherkin-CJuwpceU.js +0 -34
  133. package/dist/groovy-DZeT_VM-.js +0 -146
  134. package/dist/haskell-Bvt3Qq1t.js +0 -375
  135. package/dist/haxe-70NVW1pR.js +0 -359
  136. package/dist/http-D9LttvKF.js +0 -44
  137. package/dist/idl-B6TRFYjl.js +0 -947
  138. package/dist/index--F0eI_oT.js +0 -75
  139. package/dist/index--KQZx7Nr.js +0 -341
  140. package/dist/index-BAngL0ix.js +0 -299
  141. package/dist/index-BQtOyLub.js +0 -58
  142. package/dist/index-Bf4s6An9.js +0 -308
  143. package/dist/index-BxrA7EzT.js +0 -288
  144. package/dist/index-C-y5H4_R.js +0 -147509
  145. package/dist/index-C_zSZ2pX.js +0 -611
  146. package/dist/index-DFdFt54f.js +0 -71
  147. package/dist/index-DI3UVoln.js +0 -93
  148. package/dist/index-DL0-yUXC.js +0 -538
  149. package/dist/index-DoHQrH4a.js +0 -98
  150. package/dist/index-DqDgL4W3.js +0 -156
  151. package/dist/index-DwjKpYAo.js +0 -137
  152. package/dist/index-d16x5dY_.js +0 -268
  153. package/dist/index-wjw1DwqR.js +0 -134
  154. package/dist/index-xCbUzsUb.js +0 -249
  155. package/dist/index.css +0 -9
  156. package/dist/javascript-C2yteZeJ.js +0 -691
  157. package/dist/jinja2-DnB6dQmV.js +0 -154
  158. package/dist/julia-DpvXAuO6.js +0 -241
  159. package/dist/livescript-CanGTf8u.js +0 -272
  160. package/dist/lua-XplVlWi_.js +0 -217
  161. package/dist/mathematica-jaRHnSxC.js +0 -35
  162. package/dist/mbox-BctzC1hL.js +0 -76
  163. package/dist/mirc-CFBPAOaF.js +0 -72
  164. package/dist/mllike-BSnXJBGA.js +0 -272
  165. package/dist/modelica-vUgVs--1.js +0 -93
  166. package/dist/mscgen-Cpl0NYLN.js +0 -104
  167. package/dist/mumps-CQoS1kWX.js +0 -25
  168. package/dist/nginx-zDPm3Z74.js +0 -89
  169. package/dist/nsis-fePjrhq7.js +0 -62
  170. package/dist/ntriples-CsNjv2QF.js +0 -79
  171. package/dist/octave-C8PmmSRH.js +0 -143
  172. package/dist/oz-Ce8aN8oE.js +0 -151
  173. package/dist/pascal-De0D6mP7.js +0 -77
  174. package/dist/perl-B4bSCe1C.js +0 -915
  175. package/dist/pig-D24Z8EXi.js +0 -54
  176. package/dist/powershell-DkYVfTzP.js +0 -249
  177. package/dist/properties-Dn9wna3M.js +0 -26
  178. package/dist/protobuf-BPIjwpzm.js +0 -49
  179. package/dist/pug-CwAQJzGR.js +0 -248
  180. package/dist/puppet-nyd4dhjf.js +0 -45
  181. package/dist/python-BkR3uSy8.js +0 -313
  182. package/dist/q-DXjKs-tC.js +0 -83
  183. package/dist/r-LKEuhEGI.js +0 -104
  184. package/dist/rpm-IznJm2Xc.js +0 -57
  185. package/dist/ruby-CcYfvIk6.js +0 -228
  186. package/dist/sas-7E8yHoCW.js +0 -105
  187. package/dist/scheme-DjibxsNh.js +0 -124
  188. package/dist/shared/modules/dynamic/components/fields/storybook/pages/DynamicRender.d.ts +0 -110
  189. package/dist/shared/modules/dynamic/components/fields/storybook/pages/DynamicRender.d.ts.map +0 -1
  190. package/dist/shared/modules/dynamic/pages/dynamic-blade-form.vue.d.ts +0 -147
  191. package/dist/shared/modules/dynamic/pages/dynamic-blade-form.vue.d.ts.map +0 -1
  192. package/dist/shell-C0C2sNA_.js +0 -182
  193. package/dist/sieve-Bwz7vjP5.js +0 -72
  194. package/dist/simple-mode-B0dvCdAA.js +0 -89
  195. package/dist/smalltalk-Bhddl2pB.js +0 -48
  196. package/dist/solr-BNlsLglM.js +0 -41
  197. package/dist/sparql-FarWu_Gb.js +0 -197
  198. package/dist/spreadsheet-C-cy4P5N.js +0 -49
  199. package/dist/sql-CfG5lQ3l.js +0 -282
  200. package/dist/stex-Du4h4KAU.js +0 -129
  201. package/dist/stylus-CAdqWld3.js +0 -250
  202. package/dist/swift-DSxqR9R6.js +0 -230
  203. package/dist/tcl-xfoLljhY.js +0 -81
  204. package/dist/textile-D1AWE-pc.js +0 -295
  205. package/dist/tiddlywiki-5wqsXtSk.js +0 -155
  206. package/dist/tiki-__Kn3CeS.js +0 -181
  207. package/dist/toml-BHiuTcfn.js +0 -49
  208. package/dist/troff-D2UO-fKf.js +0 -35
  209. package/dist/ttcn-Bsa4sfRm.js +0 -123
  210. package/dist/ttcn-cfg-Bac_acMi.js +0 -88
  211. package/dist/turtle-xwJUxoPV.js +0 -80
  212. package/dist/vb-c2kQGd6-.js +0 -74
  213. package/dist/vbscript-1f_Dhg5H.js +0 -324
  214. package/dist/velocity-DJd0pTTC.js +0 -96
  215. package/dist/verilog-CiS1jyi5.js +0 -262
  216. package/dist/vhdl-T9HkrbI2.js +0 -106
  217. package/dist/webidl-CjfDENEo.js +0 -155
  218. package/dist/xquery-BUQdORAS.js +0 -422
  219. package/dist/yacas-C0absKBh.js +0 -73
  220. 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
+ }