@vc-shell/framework 1.0.191 → 1.0.192

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 (28) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/core/plugins/modularity/index.ts +9 -2
  3. package/dist/core/plugins/modularity/index.d.ts.map +1 -1
  4. package/dist/framework.js +11901 -11824
  5. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/index.d.ts +5 -2
  6. package/dist/shared/components/blade-navigation/composables/useBladeNavigation/index.d.ts.map +1 -1
  7. package/dist/shared/components/blade-navigation/types/index.d.ts +0 -1
  8. package/dist/shared/components/blade-navigation/types/index.d.ts.map +1 -1
  9. package/dist/shared/modules/dynamic/index.d.ts.map +1 -1
  10. package/dist/shared/modules/dynamic/pages/dynamic-blade-form.vue.d.ts +8 -9
  11. package/dist/shared/modules/dynamic/pages/dynamic-blade-form.vue.d.ts.map +1 -1
  12. package/dist/shared/modules/dynamic/pages/dynamic-blade-list.vue.d.ts.map +1 -1
  13. package/dist/tsconfig.tsbuildinfo +1 -1
  14. package/dist/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/_internal/vc-app-menu-link.vue.d.ts +1 -0
  15. package/dist/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/_internal/vc-app-menu-link.vue.d.ts.map +1 -1
  16. 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
  17. package/dist/ui/components/organisms/vc-app/vc-app.vue.d.ts.map +1 -1
  18. package/package.json +4 -4
  19. package/shared/components/blade-navigation/composables/useBladeNavigation/index.ts +235 -191
  20. package/shared/components/blade-navigation/plugin.ts +1 -1
  21. package/shared/components/blade-navigation/types/index.ts +0 -1
  22. package/shared/modules/dynamic/index.ts +5 -0
  23. package/shared/modules/dynamic/pages/dynamic-blade-form.vue +4 -1
  24. package/shared/modules/dynamic/pages/dynamic-blade-list.vue +5 -1
  25. package/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/_internal/vc-app-menu-link.vue +4 -15
  26. package/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue +2 -0
  27. package/ui/components/organisms/vc-app/vc-app.vue +8 -1
  28. package/ui/components/organisms/vc-table/vc-table.vue +2 -2
@@ -5,6 +5,7 @@ export interface Props {
5
5
  icon: string;
6
6
  title?: string;
7
7
  url?: string;
8
+ isActive?: boolean;
8
9
  }
9
10
  export interface Emits {
10
11
  (event: "onClick", item?: MenuItem): void;
@@ -1 +1 @@
1
- {"version":3,"file":"vc-app-menu-link.vue.d.ts","sourceRoot":"","sources":["../../../../../../../../../../ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/_internal/vc-app-menu-link.vue.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAC;AAIjE,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,KAAK;IACpB,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC3C;;;;;;;;;;;;AAoOD,wBAOG;AACH,KAAK,sBAAsB,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS,GAAG,KAAK,GAAG,CAAC,CAAC;AACjE,KAAK,6BAA6B,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;QAAE,IAAI,EAAE,OAAO,KAAK,EAAE,QAAQ,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,OAAO,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE;CAAE,CAAC;AAC9M,KAAK,kBAAkB,CAAC,CAAC,EAAE,CAAC,IAAI;KAE1B,CAAC,IAAI,MAAM,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG;QACxE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;KACb,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CACT,CAAC;AACN,KAAK,cAAc,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAG,GAAG,EAAE,CAAC"}
1
+ {"version":3,"file":"vc-app-menu-link.vue.d.ts","sourceRoot":"","sources":["../../../../../../../../../../ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/_internal/vc-app-menu-link.vue.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAC;AAIjE,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,KAAK;IACpB,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CAC3C;;;;;;;;;;;;AAyND,wBAOG;AACH,KAAK,sBAAsB,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS,GAAG,KAAK,GAAG,CAAC,CAAC;AACjE,KAAK,6BAA6B,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;QAAE,IAAI,EAAE,OAAO,KAAK,EAAE,QAAQ,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,OAAO,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE;CAAE,CAAC;AAC9M,KAAK,kBAAkB,CAAC,CAAC,EAAE,CAAC,IAAI;KAE1B,CAAC,IAAI,MAAM,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG;QACxE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;KACb,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CACT,CAAC;AACN,KAAK,cAAc,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAG,GAAG,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"vc-app-menu-item.vue.d.ts","sourceRoot":"","sources":["../../../../../../../../../ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAE9D,MAAM,WAAW,KAAK;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,KAAK;IACpB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CACzC;;;;;;;;;;;;;;;;;AA2HD,wBAOG;AACH,KAAK,sBAAsB,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS,GAAG,KAAK,GAAG,CAAC,CAAC;AACjE,KAAK,6BAA6B,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;QAAE,IAAI,EAAE,OAAO,KAAK,EAAE,QAAQ,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,OAAO,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE;CAAE,CAAC;AAC9M,KAAK,kBAAkB,CAAC,CAAC,EAAE,CAAC,IAAI;KAE1B,CAAC,IAAI,MAAM,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG;QACxE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;KACb,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CACT,CAAC;AACN,KAAK,cAAc,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAG,GAAG,EAAE,CAAC"}
1
+ {"version":3,"file":"vc-app-menu-item.vue.d.ts","sourceRoot":"","sources":["../../../../../../../../../ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAC;AAE9D,MAAM,WAAW,KAAK;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,KAAK;IACpB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;CACzC;;;;;;;;;;;;;;;;;AA8HD,wBAOG;AACH,KAAK,sBAAsB,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS,GAAG,KAAK,GAAG,CAAC,CAAC;AACjE,KAAK,6BAA6B,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;QAAE,IAAI,EAAE,OAAO,KAAK,EAAE,QAAQ,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,OAAO,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE;CAAE,CAAC;AAC9M,KAAK,kBAAkB,CAAC,CAAC,EAAE,CAAC,IAAI;KAE1B,CAAC,IAAI,MAAM,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG;QACxE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;KACb,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CACT,CAAC;AACN,KAAK,cAAc,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAG,GAAG,EAAE,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"vc-app.vue.d.ts","sourceRoot":"","sources":["../../../../../ui/components/organisms/vc-app/vc-app.vue.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmcD,wBAAwG;AACxG,KAAK,sBAAsB,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS,GAAG,KAAK,GAAG,CAAC,CAAC;AACjE,KAAK,6BAA6B,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;QAAE,IAAI,EAAE,OAAO,KAAK,EAAE,QAAQ,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,OAAO,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE;CAAE,CAAC;AAC9M,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAAE,QAAO;QAClD,MAAM,EAAE,CAAC,CAAC;KACT,CAAA;CAAE,CAAC"}
1
+ {"version":3,"file":"vc-app.vue.d.ts","sourceRoot":"","sources":["../../../../../ui/components/organisms/vc-app/vc-app.vue.ts"],"names":[],"mappings":"AAmBA,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0cD,wBAAwG;AACxG,KAAK,sBAAsB,CAAC,CAAC,IAAI,CAAC,SAAS,SAAS,GAAG,KAAK,GAAG,CAAC,CAAC;AACjE,KAAK,6BAA6B,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,EAAE,SAAS,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;QAAE,IAAI,EAAE,OAAO,KAAK,EAAE,QAAQ,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,OAAO,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAAC,QAAQ,EAAE,IAAI,CAAA;KAAE;CAAE,CAAC;AAC9M,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAAE,QAAO;QAClD,MAAM,EAAE,CAAC,CAAC;KACT,CAAA;CAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vc-shell/framework",
3
- "version": "1.0.191",
3
+ "version": "1.0.192",
4
4
  "type": "module",
5
5
  "main": "./dist/framework.js",
6
6
  "types": "./dist/index.d.ts",
@@ -59,9 +59,9 @@
59
59
  "devDependencies": {
60
60
  "@types/dompurify": "^3.0.5",
61
61
  "@types/quill": "^2.0.14",
62
- "@vc-shell/api-client-generator": "^1.0.191",
63
- "@vc-shell/config-generator": "^1.0.191",
64
- "@vc-shell/ts-config": "^1.0.191",
62
+ "@vc-shell/api-client-generator": "^1.0.192",
63
+ "@vc-shell/config-generator": "^1.0.192",
64
+ "@vc-shell/ts-config": "^1.0.192",
65
65
  "@vitejs/plugin-vue": "^5.0.3",
66
66
  "sass": "^1.69.6",
67
67
  "shx": "^0.3.4",
@@ -10,11 +10,12 @@ import {
10
10
  h,
11
11
  shallowRef,
12
12
  ComputedRef,
13
+ mergeProps,
13
14
  } from "vue";
14
15
  /* eslint-disable @typescript-eslint/no-explicit-any */
15
- import { createSharedComposable, reactiveComputed } from "@vueuse/core";
16
+ import { createSharedComposable, reactiveComputed, toValue, watchDebounced } from "@vueuse/core";
16
17
  import * as _ from "lodash-es";
17
- import { RouteLocationNormalized, useRoute, NavigationFailure } from "vue-router";
18
+ import { RouteLocationNormalized, useRoute, NavigationFailure, RouteRecordName, RouteParams, Router } from "vue-router";
18
19
  import { bladeNavigationInstance } from "../../plugin";
19
20
  import {
20
21
  BladeComponentInternalInstance,
@@ -24,6 +25,7 @@ import {
24
25
  IParentCallArgs,
25
26
  BladeInstanceConstructor,
26
27
  BladeRoutesRecord,
28
+ ExtractedBladeOptions,
27
29
  } from "../../types";
28
30
  import { navigationViewLocation } from "../../injectionKeys";
29
31
  import { usePermissions } from "../../../../../core/composables";
@@ -43,30 +45,66 @@ interface IUseBladeNavigation {
43
45
  onParentCall: (parentExposedMethods: Record<string, any>, args: IParentCallArgs) => void;
44
46
  onBeforeClose: (cb: () => Promise<boolean | undefined>) => void;
45
47
  resolveBladeByName: (name: string) => BladeInstanceConstructor;
46
- routeResolver: (to: RouteLocationNormalized) => Promise<void | NavigationFailure | undefined> | undefined;
48
+ routeResolver: (to: RouteLocationNormalized) =>
49
+ | Promise<
50
+ | {
51
+ name: RouteRecordName | undefined;
52
+ params: RouteParams;
53
+ }
54
+ | undefined
55
+ >
56
+ | undefined;
47
57
  getCurrentBlade: () => BladeVNode;
48
58
  }
49
59
 
50
60
  const activeWorkspace = shallowRef<BladeVNode>();
51
61
  const mainRouteBaseParamURL = shallowRef<string>();
52
62
 
53
- function parseUrl(url: string) {
54
- const urlRegex = /^\/([a-zA-Z0-9_-]+)(?:\/([a-zA-Z0-9_-]+))?(?:\/([a-zA-Z0-9_-]+))?$/;
55
- const match = url.match(urlRegex);
63
+ const utils = (router: Router) => {
64
+ const route = useRoute();
65
+
66
+ function parseUrl(url: string) {
67
+ // remove parts of url that does not contain workspace, blade or param - everything before workspace
68
+ const parts = url.split("/");
69
+ const workspaceIndex = parts.findIndex((part) => {
70
+ const route = router
71
+ .getRoutes()
72
+ .find((r) => r.path.endsWith("/" + part) && (r.components?.default as BladeVNode).type.isWorkspace);
73
+
74
+ return route !== undefined;
75
+ });
76
+ const cleanUrl = "/" + parts.slice(workspaceIndex).join("/");
77
+
78
+ const urlRegex = /^\/([a-zA-Z0-9_-]+)(?:\/([a-zA-Z0-9_-]+))?(?:\/([a-zA-Z0-9_-]+))?$/;
79
+ const match = cleanUrl.match(urlRegex);
56
80
 
57
- if (!match) {
58
- return undefined;
81
+ if (!match) {
82
+ return undefined;
83
+ }
84
+
85
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
86
+ const [_, workspace, blade, param] = match;
87
+
88
+ return {
89
+ workspace,
90
+ blade: blade || undefined,
91
+ param: param || undefined,
92
+ };
59
93
  }
60
94
 
61
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
62
- const [_, workspace, blade, param] = match;
95
+ function parseWorkspaceUrl(path: string): string {
96
+ const basePath = "/" + (Object.values(route.params)?.[0] ?? "");
97
+ const pathWithoutBase = path.startsWith(basePath) ? path.slice(basePath.length) : path;
98
+ const segments = pathWithoutBase.split("/").filter(Boolean);
99
+ const workspaceUrl = segments.slice(0, 1).join("/");
100
+ return "/" + workspaceUrl;
101
+ }
63
102
 
64
103
  return {
65
- workspace,
66
- blade: blade || undefined,
67
- param: param || undefined,
104
+ parseUrl,
105
+ parseWorkspaceUrl,
68
106
  };
69
- }
107
+ };
70
108
 
71
109
  const useBladeNavigationSingleton = createSharedComposable(() => {
72
110
  const route = useRoute();
@@ -76,94 +114,100 @@ const useBladeNavigationSingleton = createSharedComposable(() => {
76
114
  (instance !== null && inject<BladeNavigationPlugin>("bladeNavigationPlugin")) || bladeNavigationInstance;
77
115
  const router = navigationInstance?.router;
78
116
 
79
- function parseWorkspaceUrl(path: string): string {
80
- // Object.values(route.params)[0] will always be base path of the app
81
- if (!mainRouteBaseParamURL.value) {
82
- mainRouteBaseParamURL.value = "/" + (Object.values(route.params)?.[0] ?? "");
83
- }
84
- const pathWithoutBase = path.startsWith(mainRouteBaseParamURL.value)
85
- ? path.slice(mainRouteBaseParamURL.value.length)
86
- : path;
87
- const segments = pathWithoutBase.split("/").filter(Boolean);
88
- const workspaceUrl = segments.slice(0, 1).join("/");
89
- return "/" + workspaceUrl;
90
- }
117
+ const { parseUrl, parseWorkspaceUrl } = utils(router);
91
118
 
92
- watch(
119
+ watchDebounced(
93
120
  () => route.path,
94
121
  async (newVal, oldVal) => {
95
122
  const workspaceUrl = parseWorkspaceUrl(newVal);
96
123
 
97
- const wsRouteComponent =
98
- (route?.matched?.[1]?.components?.default as BladeVNode) ??
99
- (router.resolve({ path: workspaceUrl })?.matched?.[1]?.components?.default as BladeVNode);
124
+ const wsRouteComponent = getWorkspaceRouteComponent(workspaceUrl);
100
125
 
101
126
  if (wsRouteComponent !== undefined) {
102
127
  if (isVNode(wsRouteComponent) && wsRouteComponent.type.isBlade) {
103
- //&& wsRouteComponent.type.isBlade
104
- navigationInstance.blades.value[0] = markRaw(wsRouteComponent);
105
- activeWorkspace.value = wsRouteComponent;
128
+ updateActiveWorkspace(wsRouteComponent);
106
129
  } else {
107
- const isPrevented = await closeBlade(0);
108
-
109
- if (!isPrevented) {
110
- // add not blade page to blades array to show simple routes as
111
- // we use only one router-view for all routes
112
- navigationInstance.blades.value = [];
113
- } else {
114
- if (oldVal) router.replace({ path: oldVal });
115
- }
130
+ await handleNonBladePage(oldVal);
116
131
  }
117
132
  }
118
133
  },
119
134
  { immediate: true },
120
135
  );
121
136
 
122
- watch(
137
+ async function handleNonBladePage(oldVal?: string) {
138
+ const isPrevented = await closeBlade(0);
139
+ if (!isPrevented) {
140
+ navigationInstance.blades.value = [];
141
+ activeWorkspace.value = undefined;
142
+ } else {
143
+ if (oldVal) router.replace({ path: oldVal });
144
+ }
145
+ }
146
+
147
+ function updateActiveWorkspace(wsRouteComponent: BladeVNode) {
148
+ navigationInstance.blades.value[0] = markRaw(
149
+ Object.assign(wsRouteComponent, {
150
+ props: mergeProps(wsRouteComponent.props, {
151
+ navigation: {
152
+ idx: 0,
153
+ },
154
+ }),
155
+ }),
156
+ );
157
+ activeWorkspace.value = wsRouteComponent;
158
+ }
159
+
160
+ function getWorkspaceRouteComponent(workspaceUrl: string) {
161
+ return (
162
+ (route?.matched?.[1]?.components?.default as BladeVNode) ??
163
+ (router.resolve({ path: workspaceUrl })?.matched?.[1]?.components?.default as BladeVNode)
164
+ );
165
+ }
166
+
167
+ watchDebounced(
123
168
  navigationInstance.blades,
124
- (newVal) => {
169
+ async (newVal) => {
125
170
  const workspace = navigationInstance.blades.value[0];
126
-
127
- // method that checks if last item in newVal array has url and returns item. If it has no url - it returns previous item with url, but not first item from array as it's workspace
128
- const getLastItemWithUrl = () => {
129
- // Find the previous item with a URL
130
- for (let i = newVal.length - 1; i > 0; i--) {
131
- if (newVal[i].type.url) {
132
- return newVal[i]; // return the previous item with a URL
133
- }
134
- }
135
- return;
136
- };
171
+ const lastBlade = getLastItemWithUrl(newVal);
137
172
 
138
173
  if (workspace?.type?.url) {
139
- let url: string;
140
- // when restoring blades we need to use full path of the blade to show last blade from the url as workspace.
141
- // fullPath is provided by generateRoute function
142
- const wsBladeUrl = workspace?.props?.navigation?.fullPath || workspace?.type.url;
143
-
144
- const lastBlade = getLastItemWithUrl();
145
- if (getLastItemWithUrl()) {
146
- url =
147
- "/" +
148
- parseUrl(wsBladeUrl)?.workspace +
149
- lastBlade?.type.url +
150
- (lastBlade?.props?.param ? "/" + lastBlade?.props.param : "");
151
- } else {
152
- url = wsBladeUrl;
153
- }
154
-
174
+ const url = constructUrl(workspace, lastBlade);
155
175
  if (url) {
156
- router.options.history.replace(
157
- (mainRouteBaseParamURL.value && !url.startsWith(mainRouteBaseParamURL.value)
158
- ? mainRouteBaseParamURL.value
159
- : "") + url,
160
- );
176
+ const query = window.location.search;
177
+ updateRouterHistory(url, query);
161
178
  }
162
179
  }
163
180
  },
164
- { deep: true },
181
+ { deep: true, debounce: 1 },
165
182
  );
166
183
 
184
+ function getLastItemWithUrl(newVal: BladeVNode[]) {
185
+ for (let i = newVal.length - 1; i > 0; i--) {
186
+ if (newVal[i].type.url) {
187
+ return newVal[i];
188
+ }
189
+ }
190
+ }
191
+
192
+ function constructUrl(workspace: BladeVNode, lastBlade?: BladeVNode) {
193
+ const wsBladeUrl = workspace?.type.url;
194
+ const lastBladeUrl = lastBlade?.type.url;
195
+ const param = lastBlade?.props?.param;
196
+ if (lastBlade && wsBladeUrl) {
197
+ return "/" + parseUrl(wsBladeUrl)?.workspace + lastBladeUrl + (param ? "/" + param : "");
198
+ } else {
199
+ return wsBladeUrl;
200
+ }
201
+ }
202
+
203
+ function updateRouterHistory(url: string, query: string) {
204
+ router.options.history.replace(
205
+ (mainRouteBaseParamURL.value && !url.startsWith(mainRouteBaseParamURL.value) ? mainRouteBaseParamURL.value : "") +
206
+ url +
207
+ (query ? "?" + query : ""),
208
+ );
209
+ }
210
+
167
211
  async function closeBlade(index: number) {
168
212
  console.debug(`[@vc-shell/framework#useBladeNavigation] - closeBlade called.`);
169
213
 
@@ -215,8 +259,9 @@ export function useBladeNavigation(): IUseBladeNavigation {
215
259
  const instance: BladeComponentInternalInstance = getCurrentInstance() as BladeComponentInternalInstance;
216
260
 
217
261
  const { router, route, navigationInstance, closeBlade } = useBladeNavigationSingleton();
218
-
219
- const mainRoute = router.getRoutes().find((r) => r.meta?.root)!;
262
+ const { parseUrl } = utils(router);
263
+ const routerRoutes = router.getRoutes();
264
+ const mainRoute = routerRoutes.find((r) => r.meta?.root)!;
220
265
 
221
266
  async function openWorkspace<Blade extends Component>({ blade, param, options }: IBladeEvent<Blade>) {
222
267
  const createdComponent = h(blade, {
@@ -228,26 +273,26 @@ export function useBladeNavigation(): IUseBladeNavigation {
228
273
  }) as BladeVNode;
229
274
 
230
275
  try {
231
- const isPrevented = await closeBlade(0);
276
+ // Close all blades except the first one cause it will be overwritten
277
+ const isPrevented = await closeBlade(1);
232
278
 
233
279
  if (!isPrevented && createdComponent.type?.url) {
234
280
  if (hasAccess(blade.permissions)) {
235
281
  navigationInstance.blades.value = [createdComponent];
236
-
237
282
  // Find the route with the matching URL and update the components.default property with the new component
238
- const route = router.getRoutes().find((r) => r.path === createdComponent.type?.url);
239
- if (route && route.components) {
240
- route.components.default = createdComponent;
283
+ const wsroute = routerRoutes.find((r) => r.path.endsWith(createdComponent.type?.url as string));
284
+ if (wsroute && wsroute.components) {
285
+ wsroute.components.default = createdComponent;
241
286
  }
242
287
 
243
- return await router.replace({ path: createdComponent.type?.url as string });
288
+ return await router.replace({ name: wsroute?.name, params: { ...route.params } });
244
289
  } else
245
290
  notification.error(i18n.global.t("PERMISSION_MESSAGES.ACCESS_RESTRICTED"), {
246
291
  timeout: 3000,
247
292
  });
248
293
  }
249
294
  } catch (e) {
250
- console.log(e);
295
+ console.error(e);
251
296
  throw new Error(`Opening workspace '${blade.type.name}' is prevented`);
252
297
  }
253
298
  }
@@ -267,52 +312,64 @@ export function useBladeNavigation(): IUseBladeNavigation {
267
312
  try {
268
313
  const instanceComponent = navigationView || activeWorkspace.value;
269
314
 
270
- if (isVNode(instanceComponent) || activeWorkspace.value) {
271
- const instanceComponentIndex =
272
- navigationInstance.blades.value /* @ts-expect-error - findLastIndex is not parsed correctly by ts */
273
- .findLastIndex((x) => _.isEqual(x.type, instanceComponent.type));
315
+ if (!instanceComponent) {
316
+ throw new Error("No workspace found");
317
+ }
318
+
319
+ const instanceComponentIndex = findInstanceComponentIndex(instanceComponent);
320
+ const instanceComponentChild = navigationInstance.blades.value[instanceComponentIndex + 1];
274
321
 
275
- const instanceComponentChild =
276
- instanceComponentIndex >= 0 ? navigationInstance.blades.value[instanceComponentIndex + 1] : undefined;
322
+ let isPrevented = false;
277
323
 
278
- let isPrevented = false;
324
+ if (instanceComponentChild) {
325
+ isPrevented = await closeBlade(instanceComponentChild.props?.navigation?.idx);
326
+ }
279
327
 
280
- if (instanceComponentChild) {
281
- isPrevented = await closeBlade(instanceComponentChild.props?.navigation?.idx);
282
- }
328
+ const currentBladeIdx = instanceComponent.props?.navigation?.idx ?? 0;
329
+
330
+ const bladeNode = createBladeNode<Blade>({ blade, currentBladeIdx, options, param, onClose, onOpen });
283
331
 
284
- const currentBladeIdx = instanceComponent.props?.navigation?.idx ? instanceComponent.props?.navigation?.idx : 0;
285
-
286
- const bladeNode = h(
287
- blade,
288
- Object.assign(
289
- {},
290
- reactiveComputed(() => ({ options, param })),
291
- {
292
- navigation: {
293
- onClose,
294
- onOpen,
295
- idx: currentBladeIdx + 1,
296
- },
297
- },
298
- ),
299
- ) as BladeVNode;
300
-
301
- if (!isPrevented) {
302
- if (hasAccess(blade.permissions)) navigationInstance.blades.value.push(bladeNode);
303
- else
304
- notification.error(i18n.global.t("PERMISSION_MESSAGES.ACCESS_RESTRICTED"), {
305
- timeout: 3000,
306
- });
332
+ if (!isPrevented) {
333
+ if (hasAccess(blade.permissions)) {
334
+ navigationInstance.blades.value.push(bladeNode);
335
+ } else {
336
+ notification.error(i18n.global.t("PERMISSION_MESSAGES.ACCESS_RESTRICTED"), { timeout: 3000 });
307
337
  }
308
- } else {
309
- throw new Error("No workspace found");
310
338
  }
311
339
  } catch (e) {
312
340
  console.error(e);
313
341
  }
314
342
  }
315
343
 
344
+ function findInstanceComponentIndex(instanceComponent: BladeVNode) {
345
+ return navigationInstance.blades.value.findIndex((x) => _.isEqual(x.type, instanceComponent.type));
346
+ }
347
+
348
+ function createBladeNode<Blade extends Component>(args: {
349
+ blade: BladeInstanceConstructor<Blade>;
350
+ currentBladeIdx: number;
351
+ options: ExtractedBladeOptions<InstanceType<BladeInstanceConstructor<Blade>>["$props"], "options"> | undefined;
352
+ param?: string;
353
+ onClose?: () => void;
354
+ onOpen?: () => void;
355
+ }) {
356
+ const { blade, currentBladeIdx, options, param, onClose, onOpen } = args;
357
+ return h(
358
+ blade,
359
+ Object.assign(
360
+ {},
361
+ reactiveComputed(() => ({ options, param })),
362
+ {
363
+ navigation: {
364
+ onClose,
365
+ onOpen,
366
+ idx: currentBladeIdx + 1,
367
+ },
368
+ },
369
+ ),
370
+ ) as BladeVNode;
371
+ }
372
+
316
373
  async function onParentCall(parentExposedMethods: Record<string, any>, args: IParentCallArgs) {
317
374
  console.debug(`vc-app#onParentCall({ method: ${args.method} }) called.`);
318
375
 
@@ -355,7 +412,7 @@ export function useBladeNavigation(): IUseBladeNavigation {
355
412
  }
356
413
 
357
414
  function hasNecessaryRoute(to: RouteLocationNormalized) {
358
- return router.getRoutes().find((route) => route.path === to.path);
415
+ return routerRoutes.find((route) => route.path === to.path);
359
416
  }
360
417
 
361
418
  /**
@@ -369,92 +426,79 @@ export function useBladeNavigation(): IUseBladeNavigation {
369
426
  */
370
427
  async function generateRoute(to: RouteLocationNormalized, routes: BladeRoutesRecord[]) {
371
428
  // Extract parameters excluding "pathMatch". This is necessary if a variable is used as the App component URL, for example, /:userId?
372
- const params = Object.entries(to.params)
373
- .filter(([key]) => key !== "pathMatch")
374
- .reduce(
375
- (acc, [key, value]) => {
376
- acc[key] = value;
377
- return acc;
378
- },
379
- {} as Record<string, string | string[]>,
380
- );
429
+ const params = Object.fromEntries(Object.entries(to.params).filter(([key]) => key !== "pathMatch"));
381
430
 
382
431
  // Get the raw path of the main route.
383
- const parentRawPath = router.getRoutes().find((route) => route.name === mainRoute.name)!.path!;
432
+ const parentRawPath = routerRoutes.find((route) => route.name === mainRoute.name)?.path;
384
433
 
385
434
  // Determine the parent path based on the parameters.
386
- const parentPath = parentRawPath.includes(Object.keys(params)?.[0] as string)
387
- ? "/" + (Object.values(params)?.[0] ?? "")
388
- : "";
435
+ const parentPath =
436
+ parentRawPath && parentRawPath.includes(Object.keys(params)[0]) ? `/${Object.values(params)[0]}` : "";
389
437
 
390
438
  // Set the base param value.
391
439
  mainRouteBaseParamURL.value = parentPath;
392
440
 
393
- // Check if the current path matches the parent path.
394
- const isCorrectParentPath = parentRawPath !== "/" && to.path.startsWith(parentPath);
395
-
396
- // Check if the current route is a parent route.
397
- const isRouteParent = router.getRoutes().find((x) => x.path.endsWith(parentPath));
398
-
399
441
  // Parse the URL to extract relevant route information.
400
- const parsedRoutes = parseUrl(!isRouteParent ? to.path.slice(parentPath.length) : to.path);
442
+ const parsedRoutes = parseUrl(parentPath !== "/" ? to.path.slice(parentPath.length) : to.path);
443
+
444
+ if (parsedRoutes !== undefined) {
445
+ const { workspace, blade, param } = parsedRoutes;
401
446
 
402
- if (parsedRoutes) {
403
447
  // Find the registered route component.
404
- const registeredRouteComponent = routes.find((route) => route.route === "/" + parsedRoutes?.blade)?.component;
448
+ const registeredWorkspaceComponent = routes.find((route) => route.route === `/${workspace}`)?.component;
449
+ const registeredRouteComponent = routes.find((route) => route.route === `/${blade}`)?.component;
405
450
 
406
- if (registeredRouteComponent) {
407
- if (registeredRouteComponent.type?.isWorkspace) {
408
- // If the route is a workspace, navigate to it directly.
409
- router.replace({ path: registeredRouteComponent.type.url as string });
410
- } else {
411
- // If the route is not a workspace, add it to the router and navigate to it.
412
- const url =
413
- (!isRouteParent ? parentPath : "") +
414
- "/" +
415
- parsedRoutes?.workspace +
416
- "/" +
417
- parsedRoutes.blade +
418
- (parsedRoutes.param ? "/" + parsedRoutes.param : "");
419
-
420
- // If the route is not routable, redirect to the workspace.
421
- if (!parsedRoutes.param && !registeredRouteComponent.type?.routable) {
422
- return router.replace("/" + parsedRoutes?.workspace);
423
- } else {
424
- // Add the route to the router.
425
- router.addRoute(mainRoute.name as string, {
426
- name: url,
427
- path: parentRawPath !== "/" ? parentRawPath + url : "" + url,
428
- components: {
429
- default: _.merge({}, registeredRouteComponent, {
430
- props: {
431
- param: parsedRoutes.param,
432
- navigation: {
433
- fullPath: url,
434
- idx: 0,
435
- },
436
- },
437
- }),
438
- },
439
- meta: {
440
- permissions: registeredRouteComponent.type?.permissions,
441
- },
442
- });
443
-
444
- // Navigate to the newly added route.
445
- return router.replace({ name: url, params: isCorrectParentPath ? params : {} });
446
- }
447
- }
448
- } else {
449
- // If the registered route component is not found, navigate to the main route.
450
- const mainRoute = router.getRoutes().find((route) => route.meta?.root);
451
- const mainRouteAlias = router.getRoutes().find((route) => route.aliasOf?.path === mainRoute?.path) ?? mainRoute;
451
+ if (!registeredWorkspaceComponent) {
452
+ return goToRoot();
453
+ }
454
+
455
+ // Open the workspace component or workspace route.
456
+ if (registeredRouteComponent?.type.isWorkspace) {
457
+ await openBlade(
458
+ {
459
+ blade: registeredRouteComponent as unknown as BladeInstanceConstructor,
460
+ },
461
+ true,
462
+ );
463
+ return { name: registeredRouteComponent?.type.name, params };
464
+ }
452
465
 
453
- router.replace({ name: mainRouteAlias?.name, params: route.params });
466
+ // Open the workspace component with param or workspace route.
467
+ if (registeredWorkspaceComponent) {
468
+ await openBlade(
469
+ {
470
+ blade: registeredWorkspaceComponent as unknown as BladeInstanceConstructor,
471
+ param:
472
+ registeredRouteComponent?.type.moduleUid === registeredWorkspaceComponent.type.moduleUid
473
+ ? param
474
+ : undefined,
475
+ },
476
+ true,
477
+ );
478
+
479
+ // Open the route if it's not from the workspace module.
480
+ if (
481
+ registeredRouteComponent?.type.moduleUid !== registeredWorkspaceComponent.type.moduleUid &&
482
+ registeredRouteComponent?.type.routable
483
+ ) {
484
+ await openBlade({
485
+ blade: registeredRouteComponent as unknown as BladeInstanceConstructor,
486
+ param: param,
487
+ });
488
+ }
489
+ return { name: registeredWorkspaceComponent?.type.name, params };
454
490
  }
491
+ } else {
492
+ return goToRoot();
455
493
  }
456
494
  }
457
495
 
496
+ function goToRoot() {
497
+ const mainRoute = routerRoutes.find((route) => route.meta?.root);
498
+ const mainRouteAlias = routerRoutes.find((route) => route.aliasOf?.path === mainRoute?.path) || mainRoute;
499
+ return { name: mainRouteAlias?.name, params: route.params };
500
+ }
501
+
458
502
  /**
459
503
  * The function getCurrentBlade returns the current BladeVNode instance's vnode.
460
504
  * @returns the `vnode` property of the `instance` object, which is of type `BladeVNode`.
@@ -29,7 +29,7 @@ export const VcBladeNavigationComponent = {
29
29
  };
30
30
 
31
31
  app.config.globalProperties.$bladeNavigationPlugin = bladeNavigationPlugin;
32
- app.provide("bladeNavigationPlugin", bladeNavigationPlugin);
33
32
  bladeNavigationInstance = bladeNavigationPlugin;
33
+ app.provide("bladeNavigationPlugin", bladeNavigationPlugin);
34
34
  },
35
35
  };
@@ -95,7 +95,6 @@ export interface BladeVNode extends VNode {
95
95
  onClose?: () => void;
96
96
  onBeforeClose?: () => Promise<boolean | undefined>;
97
97
  instance: Ref<CoreBladeExposed | undefined | null>;
98
- fullPath: string;
99
98
  idx: number;
100
99
  };
101
100
  onVnodeUnmounted?: VNodeMountHook | VNodeMountHook[];