@vc-shell/framework 1.0.148 → 1.0.149
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/core/composables/index.ts +1 -1
- package/core/composables/useLanguages/index.ts +52 -0
- package/core/plugins/i18n/index.ts +1 -1
- package/core/plugins/modularity/index.ts +10 -1
- package/core/plugins/validation/index.ts +0 -11
- package/core/plugins/validation/rules.ts +7 -6
- package/dist/core/composables/index.d.ts +1 -1
- package/dist/core/composables/index.d.ts.map +1 -1
- package/dist/core/composables/useLanguages/index.d.ts +12 -0
- package/dist/core/composables/useLanguages/index.d.ts.map +1 -0
- package/dist/core/plugins/modularity/index.d.ts.map +1 -1
- package/dist/core/plugins/validation/index.d.ts +0 -3
- package/dist/core/plugins/validation/index.d.ts.map +1 -1
- package/dist/core/plugins/validation/rules.d.ts +1 -1
- package/dist/core/plugins/validation/rules.d.ts.map +1 -1
- package/dist/framework.js +11732 -10993
- package/dist/index.css +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/shared/components/app-switcher/composables/useAppSwitcher/index.d.ts.map +1 -1
- package/dist/shared/components/blade-navigation/components/vc-blade-navigation/vc-blade-navigation.vue.d.ts.map +1 -1
- package/dist/shared/components/blade-navigation/components/vc-blade-view/vc-blade-view.d.ts +0 -12
- package/dist/shared/components/blade-navigation/components/vc-blade-view/vc-blade-view.d.ts.map +1 -1
- package/dist/shared/components/blade-navigation/composables/useBladeNavigation/index.d.ts +6 -3
- package/dist/shared/components/blade-navigation/composables/useBladeNavigation/index.d.ts.map +1 -1
- package/dist/shared/components/blade-navigation/plugin.d.ts.map +1 -1
- package/dist/shared/components/blade-navigation/types/index.d.ts +6 -8
- package/dist/shared/components/blade-navigation/types/index.d.ts.map +1 -1
- package/dist/shared/components/language-selector/language-selector.vue.d.ts.map +1 -1
- package/dist/shared/components/user-dropdown-button/user-dropdown-button.vue.d.ts.map +1 -1
- package/dist/shared/modules/dynamic/components/SchemaRender.d.ts +3 -3
- package/dist/shared/modules/dynamic/components/fields/Button.d.ts +1 -1
- package/dist/shared/modules/dynamic/components/fields/Card.d.ts +1 -1
- package/dist/shared/modules/dynamic/components/fields/Checkbox.d.ts +1 -1
- package/dist/shared/modules/dynamic/components/fields/ContentField.d.ts +1 -1
- package/dist/shared/modules/dynamic/components/fields/DynamicProperty.d.ts +1 -1
- package/dist/shared/modules/dynamic/components/fields/EditorField.d.ts +1 -1
- package/dist/shared/modules/dynamic/components/fields/Fieldset.d.ts +1 -1
- package/dist/shared/modules/dynamic/components/fields/GalleryField.d.ts +1 -1
- package/dist/shared/modules/dynamic/components/fields/ImageField.d.ts +1 -1
- package/dist/shared/modules/dynamic/components/fields/InputCurrency.d.ts +1 -1
- package/dist/shared/modules/dynamic/components/fields/InputField.d.ts +1 -1
- package/dist/shared/modules/dynamic/components/fields/MultivalueField.d.ts +1 -1
- package/dist/shared/modules/dynamic/components/fields/SelectField.d.ts +1 -1
- package/dist/shared/modules/dynamic/components/fields/StatusField.d.ts +1 -1
- package/dist/shared/modules/dynamic/components/fields/TextareaField.d.ts +1 -1
- package/dist/shared/modules/dynamic/components/fields/VideoField.d.ts +1 -1
- package/dist/shared/modules/dynamic/components/fields/props.d.ts +1 -1
- package/dist/shared/modules/dynamic/factories/types/index.d.ts +1 -1
- package/dist/shared/modules/dynamic/factories/types/index.d.ts.map +1 -1
- package/dist/shared/modules/dynamic/helpers/nodeBuilder.d.ts.map +1 -1
- package/dist/shared/modules/dynamic/helpers/override.d.ts.map +1 -1
- package/dist/shared/modules/dynamic/index.d.ts.map +1 -1
- package/dist/shared/modules/dynamic/pages/dynamic-blade-form.vue.d.ts +3 -1
- package/dist/shared/modules/dynamic/pages/dynamic-blade-form.vue.d.ts.map +1 -1
- package/dist/shared/modules/dynamic/pages/dynamic-blade-list.vue.d.ts +2 -0
- package/dist/shared/modules/dynamic/pages/dynamic-blade-list.vue.d.ts.map +1 -1
- package/dist/shared/modules/dynamic/types/index.d.ts +13 -4
- package/dist/shared/modules/dynamic/types/index.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/ui/components/organisms/vc-app/_internal/vc-app-bar/vc-app-bar.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-app/vc-app.vue.d.ts.map +1 -1
- package/package.json +6 -5
- package/shared/components/app-switcher/composables/useAppSwitcher/index.ts +2 -1
- package/shared/components/blade-navigation/components/vc-blade-navigation/vc-blade-navigation.vue +12 -26
- package/shared/components/blade-navigation/components/vc-blade-view/vc-blade-view.ts +18 -11
- package/shared/components/blade-navigation/composables/useBladeNavigation/index.ts +231 -337
- package/shared/components/blade-navigation/plugin.ts +2 -1
- package/shared/components/blade-navigation/types/index.ts +5 -11
- package/shared/components/language-selector/language-selector.vue +12 -10
- package/shared/components/notification-dropdown/notification-dropdown.vue +1 -1
- package/shared/components/user-dropdown-button/user-dropdown-button.vue +55 -40
- package/shared/modules/dynamic/factories/types/index.ts +1 -1
- package/shared/modules/dynamic/helpers/nodeBuilder.ts +6 -3
- package/shared/modules/dynamic/helpers/override.ts +29 -11
- package/shared/modules/dynamic/index.ts +1 -0
- package/shared/modules/dynamic/pages/dynamic-blade-form.vue +47 -17
- package/shared/modules/dynamic/pages/dynamic-blade-list.vue +11 -1
- package/shared/modules/dynamic/types/index.ts +13 -4
- package/ui/components/atoms/vc-label/vc-label.vue +18 -19
- package/ui/components/molecules/vc-multivalue/vc-multivalue.vue +1 -0
- package/ui/components/organisms/vc-app/_internal/vc-app-bar/vc-app-bar.vue +5 -19
- package/ui/components/organisms/vc-app/_internal/vc-app-menu/vc-app-menu.vue +1 -1
- package/ui/components/organisms/vc-app/vc-app.vue +2 -8
- package/core/composables/useI18n/index.ts +0 -7
- package/dist/core/composables/useI18n/index.d.ts +0 -3
- package/dist/core/composables/useI18n/index.d.ts.map +0 -1
|
@@ -1,6 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
markRaw,
|
|
3
|
+
computed,
|
|
4
|
+
getCurrentInstance,
|
|
5
|
+
inject,
|
|
6
|
+
warn,
|
|
7
|
+
Component,
|
|
8
|
+
watch,
|
|
9
|
+
isVNode,
|
|
10
|
+
h,
|
|
11
|
+
shallowRef,
|
|
12
|
+
ComputedRef,
|
|
13
|
+
} from "vue";
|
|
1
14
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { reactiveComputed } from "@vueuse/core";
|
|
3
|
-
import { computed, getCurrentInstance, inject, warn, Component, watch, isVNode, h, shallowRef, ComputedRef } from "vue";
|
|
15
|
+
import { createSharedComposable, reactiveComputed } from "@vueuse/core";
|
|
4
16
|
import * as _ from "lodash-es";
|
|
5
17
|
import { RouteLocationNormalized, useRoute, NavigationFailure } from "vue-router";
|
|
6
18
|
import { bladeNavigationInstance } from "../../plugin";
|
|
@@ -11,27 +23,25 @@ import {
|
|
|
11
23
|
IBladeEvent,
|
|
12
24
|
IParentCallArgs,
|
|
13
25
|
BladeInstanceConstructor,
|
|
14
|
-
BladeRouteRecordLocationNormalized,
|
|
15
26
|
BladeRoutesRecord,
|
|
16
27
|
} from "../../types";
|
|
17
28
|
import { navigationViewLocation } from "../../injectionKeys";
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
name: string;
|
|
24
|
-
};
|
|
29
|
+
import { usePermissions } from "../../../../../core/composables";
|
|
30
|
+
import { notification } from "./../../../notifications";
|
|
31
|
+
import "core-js/actual/array/find-last";
|
|
32
|
+
import "core-js/actual/array/find-last-index";
|
|
33
|
+
import { i18n } from "../../../../../core/plugins/i18n";
|
|
25
34
|
|
|
26
35
|
interface IUseBladeNavigation {
|
|
27
|
-
readonly blades: ComputedRef<
|
|
36
|
+
readonly blades: ComputedRef<BladeVNode[]>;
|
|
28
37
|
readonly currentBladeNavigationData: ComputedRef<BladeVNode["props"]["navigation"]>;
|
|
29
38
|
openBlade: <Blade extends Component>(
|
|
30
39
|
{ blade, param, options, onOpen, onClose }: IBladeEvent<Blade>,
|
|
31
40
|
isWorkspace?: boolean,
|
|
32
41
|
) => Promise<void | NavigationFailure>;
|
|
33
|
-
closeBlade: (index: number
|
|
42
|
+
closeBlade: (index: number) => Promise<boolean>;
|
|
34
43
|
onParentCall: (parentExposedMethods: Record<string, any>, args: IParentCallArgs) => void;
|
|
44
|
+
onBeforeClose: (cb: () => Promise<boolean | undefined>) => void;
|
|
35
45
|
resolveBladeByName: (name: string) => BladeInstanceConstructor;
|
|
36
46
|
routeResolver: (to: RouteLocationNormalized) => Promise<void | NavigationFailure | undefined> | undefined;
|
|
37
47
|
getCurrentBlade: () => BladeVNode;
|
|
@@ -39,25 +49,33 @@ interface IUseBladeNavigation {
|
|
|
39
49
|
|
|
40
50
|
const activeWorkspace = shallowRef<BladeVNode>();
|
|
41
51
|
const baseUrl = shallowRef<string>();
|
|
42
|
-
export function useBladeNavigation(): IUseBladeNavigation {
|
|
43
|
-
const navigationView = inject(navigationViewLocation, undefined) as BladeVNode;
|
|
44
52
|
|
|
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);
|
|
56
|
+
|
|
57
|
+
if (!match) {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
62
|
+
const [_, workspace, blade, param] = match;
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
workspace,
|
|
66
|
+
blade: blade || undefined,
|
|
67
|
+
param: param || undefined,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const useBladeNavigationSingleton = createSharedComposable(() => {
|
|
45
72
|
const route = useRoute();
|
|
46
73
|
|
|
47
74
|
const instance: BladeComponentInternalInstance = getCurrentInstance() as BladeComponentInternalInstance;
|
|
48
75
|
const navigationInstance =
|
|
49
76
|
(instance !== null && inject<BladeNavigationPlugin>("bladeNavigationPlugin")) || bladeNavigationInstance;
|
|
50
|
-
|
|
51
77
|
const router = navigationInstance?.router;
|
|
52
78
|
|
|
53
|
-
const mainRoute = router.getRoutes().find((r) => r.meta?.root)!;
|
|
54
|
-
|
|
55
|
-
const blades = computed(() => {
|
|
56
|
-
return router.getRoutes().find((routeItem) => {
|
|
57
|
-
return route.name === routeItem.name;
|
|
58
|
-
});
|
|
59
|
-
}) as ComputedRef<BladeRouteRecordLocationNormalized>;
|
|
60
|
-
|
|
61
79
|
function parseWorkspaceUrl(path: string): string {
|
|
62
80
|
// Object.values(route.params)[0] will always be base path of the app
|
|
63
81
|
if (!baseUrl.value) {
|
|
@@ -71,41 +89,135 @@ export function useBladeNavigation(): IUseBladeNavigation {
|
|
|
71
89
|
|
|
72
90
|
watch(
|
|
73
91
|
() => route.path,
|
|
74
|
-
(newVal) => {
|
|
92
|
+
async (newVal, oldVal) => {
|
|
75
93
|
const workspaceUrl = parseWorkspaceUrl(newVal);
|
|
76
94
|
|
|
77
|
-
const wsRouteComponent =
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
95
|
+
const wsRouteComponent =
|
|
96
|
+
(route?.matched?.[1]?.components?.default as BladeVNode) ??
|
|
97
|
+
(router.resolve({ path: workspaceUrl })?.matched?.[1]?.components?.default as BladeVNode);
|
|
98
|
+
|
|
99
|
+
if (wsRouteComponent !== undefined) {
|
|
100
|
+
if (isVNode(wsRouteComponent)) {
|
|
101
|
+
navigationInstance.blades.value[0] = markRaw(wsRouteComponent);
|
|
102
|
+
activeWorkspace.value = wsRouteComponent;
|
|
103
|
+
} else {
|
|
104
|
+
const isPrevented = await closeBlade(0);
|
|
105
|
+
|
|
106
|
+
if (!isPrevented) {
|
|
107
|
+
// add not blade page to blades array to show simple routes as
|
|
108
|
+
// we use only one router-view for all routes
|
|
109
|
+
navigationInstance.blades.value = [markRaw(wsRouteComponent)];
|
|
110
|
+
} else {
|
|
111
|
+
if (oldVal) router.replace({ path: oldVal });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
81
114
|
}
|
|
82
115
|
},
|
|
83
116
|
{ immediate: true },
|
|
84
117
|
);
|
|
85
118
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
119
|
+
watch(
|
|
120
|
+
navigationInstance.blades,
|
|
121
|
+
(newVal) => {
|
|
122
|
+
const workspace = navigationInstance.blades.value[0];
|
|
123
|
+
|
|
124
|
+
// 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
|
|
125
|
+
const getLastItemWithUrl = () => {
|
|
126
|
+
// Find the previous item with a URL
|
|
127
|
+
for (let i = newVal.length - 1; i > 0; i--) {
|
|
128
|
+
if (newVal[i].type.url) {
|
|
129
|
+
return newVal[i]; // return the previous item with a URL
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
if (workspace?.type?.url) {
|
|
136
|
+
let url: string;
|
|
137
|
+
// when restoring blades we need to use full path of the blade to show last blade from the url as workspace.
|
|
138
|
+
// fullPath is provided by generateRoute function
|
|
139
|
+
const wsBladeUrl = workspace?.props?.navigation?.fullPath || workspace?.type.url;
|
|
140
|
+
|
|
141
|
+
const lastBlade = getLastItemWithUrl();
|
|
142
|
+
if (getLastItemWithUrl()) {
|
|
143
|
+
url =
|
|
144
|
+
"/" +
|
|
145
|
+
parseUrl(wsBladeUrl)?.workspace +
|
|
146
|
+
lastBlade?.type.url +
|
|
147
|
+
(lastBlade?.props?.param ? "/" + lastBlade?.props.param : "");
|
|
148
|
+
} else {
|
|
149
|
+
url = wsBladeUrl;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (url) history.replaceState({}, "", "#" + url);
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{ deep: true },
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
async function closeBlade(index: number) {
|
|
159
|
+
console.debug(`[@vc-shell/framework#useBladeNavigation] - closeBlade called.`);
|
|
160
|
+
|
|
161
|
+
if (navigationInstance.blades.value.length === 1) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const children = navigationInstance.blades.value.slice(index).reverse();
|
|
166
|
+
let isPrevented = false;
|
|
167
|
+
for (let index = 0; index < children.length; index++) {
|
|
168
|
+
const element = children[index];
|
|
169
|
+
|
|
170
|
+
if (element.props?.navigation?.onBeforeClose) {
|
|
171
|
+
const result = await element.props.navigation.onBeforeClose();
|
|
172
|
+
|
|
173
|
+
if (result === false) {
|
|
174
|
+
isPrevented = true;
|
|
175
|
+
console.debug(`[@vc-shell/framework#useBladeNavigation] - Navigation is prevented`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!isPrevented) {
|
|
181
|
+
navigationInstance.blades.value.splice(index);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return isPrevented;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
navigationInstance,
|
|
189
|
+
router,
|
|
190
|
+
closeBlade,
|
|
191
|
+
};
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
export function useBladeNavigation(): IUseBladeNavigation {
|
|
195
|
+
const navigationView = inject(navigationViewLocation, undefined) as BladeVNode;
|
|
196
|
+
|
|
197
|
+
const { hasAccess } = usePermissions();
|
|
198
|
+
|
|
199
|
+
const instance: BladeComponentInternalInstance = getCurrentInstance() as BladeComponentInternalInstance;
|
|
200
|
+
|
|
201
|
+
const { router, navigationInstance, closeBlade } = useBladeNavigationSingleton();
|
|
202
|
+
|
|
203
|
+
const mainRoute = router.getRoutes().find((r) => r.meta?.root)!;
|
|
204
|
+
|
|
92
205
|
async function openWorkspace<Blade extends Component>({ blade, param, options }: IBladeEvent<Blade>) {
|
|
93
|
-
const createdComponent = h(blade, {
|
|
206
|
+
const createdComponent = h(blade, {
|
|
207
|
+
param,
|
|
208
|
+
options,
|
|
209
|
+
navigation: {
|
|
210
|
+
idx: 0,
|
|
211
|
+
},
|
|
212
|
+
}) as BladeVNode;
|
|
94
213
|
|
|
95
214
|
try {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
},
|
|
103
|
-
meta: {
|
|
104
|
-
permissions: createdComponent.type?.permissions,
|
|
105
|
-
},
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
return await router.push({ path: createdComponent.type?.url as string });
|
|
215
|
+
const isPrevented = await closeBlade(0);
|
|
216
|
+
|
|
217
|
+
if (!isPrevented && createdComponent.type?.url) {
|
|
218
|
+
navigationInstance.blades.value = [createdComponent];
|
|
219
|
+
|
|
220
|
+
return await router.replace({ path: createdComponent.type?.url as string });
|
|
109
221
|
}
|
|
110
222
|
} catch (e) {
|
|
111
223
|
console.log(e);
|
|
@@ -113,17 +225,9 @@ export function useBladeNavigation(): IUseBladeNavigation {
|
|
|
113
225
|
}
|
|
114
226
|
}
|
|
115
227
|
|
|
116
|
-
/**
|
|
117
|
-
* The `openBlade` function is used to open a blade component in a workspace or navigation view.
|
|
118
|
-
* @param - - `blade`: The component that represents the blade to be opened.
|
|
119
|
-
* @param [isWorkspace=false] - A boolean value indicating whether the blade is being opened as a
|
|
120
|
-
* workspace or not.
|
|
121
|
-
* @returns a Promise that resolves to the result of the `router.push()` method.
|
|
122
|
-
*/
|
|
123
228
|
async function openBlade<Blade extends Component>(
|
|
124
229
|
{ blade, param, options, onOpen, onClose }: IBladeEvent<Blade>,
|
|
125
230
|
isWorkspace = false,
|
|
126
|
-
// update = true,
|
|
127
231
|
) {
|
|
128
232
|
if (!blade) {
|
|
129
233
|
throw new Error("You should pass blade component as openBlade argument");
|
|
@@ -136,34 +240,22 @@ export function useBladeNavigation(): IUseBladeNavigation {
|
|
|
136
240
|
try {
|
|
137
241
|
const instanceComponent = navigationView || activeWorkspace.value;
|
|
138
242
|
|
|
139
|
-
if (instanceComponent
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
243
|
+
if (isVNode(instanceComponent) || activeWorkspace.value) {
|
|
244
|
+
const instanceComponentIndex =
|
|
245
|
+
navigationInstance.blades.value /* @ts-expect-error - findLastIndex is not parsed correctly by ts */
|
|
246
|
+
.findLastIndex((x) => _.isEqual(x.type, instanceComponent.type));
|
|
143
247
|
|
|
144
|
-
const
|
|
145
|
-
|
|
248
|
+
const instanceComponentChild =
|
|
249
|
+
instanceComponentIndex >= 0 ? navigationInstance.blades.value[instanceComponentIndex + 1] : undefined;
|
|
146
250
|
|
|
147
|
-
|
|
251
|
+
let isPrevented = false;
|
|
148
252
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
* Removes routes without paths and default route from next route.
|
|
153
|
-
*/
|
|
154
|
-
const alreadyAdded = _.omitBy(initialBlade?.components, (value: BladeVNode, key) =>
|
|
155
|
-
blade.url ? !value?.props?.navigation?.bladePath : false || key === "default",
|
|
156
|
-
);
|
|
253
|
+
if (instanceComponentChild) {
|
|
254
|
+
isPrevented = await closeBlade(instanceComponentChild.props?.navigation?.idx);
|
|
255
|
+
}
|
|
157
256
|
|
|
158
257
|
const currentBladeIdx = instanceComponent.props?.navigation?.idx ? instanceComponent.props?.navigation?.idx : 0;
|
|
159
258
|
|
|
160
|
-
/**
|
|
161
|
-
* Closes all child blades in current route to prevent blades without url to preserve.
|
|
162
|
-
*/
|
|
163
|
-
await closeBlade(currentBladeIdx + 1, false);
|
|
164
|
-
|
|
165
|
-
const isInitialBlade = activeWorkspace.value?.type.url === url;
|
|
166
|
-
|
|
167
259
|
const bladeNode = h(
|
|
168
260
|
blade,
|
|
169
261
|
Object.assign(
|
|
@@ -171,41 +263,21 @@ export function useBladeNavigation(): IUseBladeNavigation {
|
|
|
171
263
|
reactiveComputed(() => ({ options, param })),
|
|
172
264
|
{
|
|
173
265
|
navigation: {
|
|
174
|
-
bladePath: blade.url ? blade.url + (param ? "/" + param : "") : undefined,
|
|
175
|
-
fullPath: url,
|
|
176
266
|
onClose,
|
|
177
267
|
onOpen,
|
|
178
268
|
idx: currentBladeIdx + 1,
|
|
179
|
-
uniqueRouteKey: generateId(),
|
|
180
269
|
},
|
|
181
270
|
},
|
|
182
271
|
),
|
|
183
|
-
);
|
|
272
|
+
) as BladeVNode;
|
|
184
273
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
blade.url
|
|
193
|
-
? {
|
|
194
|
-
[url]: bladeNode,
|
|
195
|
-
}
|
|
196
|
-
: {
|
|
197
|
-
[blade.name]: bladeNode,
|
|
198
|
-
},
|
|
199
|
-
),
|
|
200
|
-
meta: {
|
|
201
|
-
permissions: activeWorkspace.value && mergePermissions(activeWorkspace.value, blade),
|
|
202
|
-
},
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
return await router.push({
|
|
206
|
-
path: url,
|
|
207
|
-
replace: !blade.url,
|
|
208
|
-
});
|
|
274
|
+
if (!isPrevented) {
|
|
275
|
+
if (hasAccess(blade.permissions)) navigationInstance.blades.value.push(bladeNode);
|
|
276
|
+
else
|
|
277
|
+
notification.error(i18n.global.t("PERMISSION_MESSAGES.ACCESS_RESTRICTED"), {
|
|
278
|
+
timeout: 3000,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
209
281
|
} else {
|
|
210
282
|
throw new Error("No workspace found");
|
|
211
283
|
}
|
|
@@ -214,97 +286,10 @@ export function useBladeNavigation(): IUseBladeNavigation {
|
|
|
214
286
|
}
|
|
215
287
|
}
|
|
216
288
|
|
|
217
|
-
/**
|
|
218
|
-
* The function merges the permissions of a workspace blade and a child blade.
|
|
219
|
-
* @param {BladeVNode} workspaceBlade - The `workspaceBlade` parameter is a BladeVNode object
|
|
220
|
-
* representing the workspace blade.
|
|
221
|
-
* @param {BladeVNode | BladeInstanceConstructor} childBlade - The `childBlade` parameter is either a
|
|
222
|
-
* `BladeVNode` or a `BladeInstanceConstructor`.
|
|
223
|
-
* @returns an array of permissions.
|
|
224
|
-
*/
|
|
225
|
-
function mergePermissions(workspaceBlade: BladeVNode, childBlade: BladeVNode | BladeInstanceConstructor) {
|
|
226
|
-
const child = (isVNode(childBlade) ? childBlade : h(childBlade)) as BladeVNode;
|
|
227
|
-
if (child && child.type?.permissions) {
|
|
228
|
-
const childPermissionsArr =
|
|
229
|
-
typeof child.type.permissions === "string" ? [child.type.permissions] : child.type.permissions;
|
|
230
|
-
if (workspaceBlade.type.permissions) {
|
|
231
|
-
const workspacePermissionsArr =
|
|
232
|
-
typeof workspaceBlade.type.permissions === "string"
|
|
233
|
-
? [workspaceBlade.type.permissions]
|
|
234
|
-
: workspaceBlade.type.permissions;
|
|
235
|
-
return workspacePermissionsArr.concat(childPermissionsArr);
|
|
236
|
-
} else {
|
|
237
|
-
return childPermissionsArr;
|
|
238
|
-
}
|
|
239
|
-
} else return workspaceBlade.type.permissions;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* The function removes a specified substring from a given input string.
|
|
244
|
-
* @param {string} inputString - The input string from which the substring will be removed.
|
|
245
|
-
* @param {string} substringToRemove - The `substringToRemove` parameter is a string that represents
|
|
246
|
-
* the substring that you want to remove from the `inputString`.
|
|
247
|
-
* @returns the input string with the specified substring removed.
|
|
248
|
-
*/
|
|
249
|
-
function removeSubstring(inputString: string, substringToRemove: string) {
|
|
250
|
-
const regex = new RegExp(`${substringToRemove}+$`);
|
|
251
|
-
return inputString.replace(regex, "");
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* The `closeBlade` function is used to close a blade and update the router location if necessary.
|
|
256
|
-
* @param {number} index - The `index` parameter is a number that represents the index of the blade
|
|
257
|
-
* to be closed.
|
|
258
|
-
* @param [changeLocation=true] - A boolean value indicating whether the location should be changed
|
|
259
|
-
* when closing the blade. The default value is `true`.
|
|
260
|
-
* @returns a Promise that resolves to a boolean value if `changeLocation` is true and the router
|
|
261
|
-
* successfully replaces the path, otherwise it returns undefined.
|
|
262
|
-
*/
|
|
263
|
-
async function closeBlade(index: number, changeLocation = true) {
|
|
264
|
-
console.debug(`[@vc-shell/framework#useBladeNavigation] - closeBlade called.`);
|
|
265
|
-
|
|
266
|
-
const bladeByIndex = Object.values(blades.value?.components || {}).find(
|
|
267
|
-
(x) => "props" in x && x.props?.navigation?.idx === index,
|
|
268
|
-
) as BladeVNode;
|
|
269
|
-
|
|
270
|
-
if (bladeByIndex && bladeByIndex?.props?.navigation?.bladePath) {
|
|
271
|
-
const path = removeSubstring(bladeByIndex.props.navigation.fullPath, bladeByIndex.props.navigation?.bladePath);
|
|
272
|
-
|
|
273
|
-
return changeLocation && (await router.replace(path));
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const routeWithNamedBlade = router.getRoutes().find((r) => r.path === bladeByIndex?.props?.navigation?.fullPath);
|
|
277
|
-
|
|
278
|
-
if (routeWithNamedBlade) {
|
|
279
|
-
const isInitialBlade = activeWorkspace.value?.type.name === routeWithNamedBlade.name;
|
|
280
|
-
|
|
281
|
-
router.addRoute(mainRoute.name as string, {
|
|
282
|
-
name: isInitialBlade ? routeWithNamedBlade?.name : routeWithNamedBlade?.path,
|
|
283
|
-
path: routeWithNamedBlade?.path,
|
|
284
|
-
components: _.omitBy(routeWithNamedBlade?.components, (value: BladeVNode) => {
|
|
285
|
-
if (value.props && value.props.navigation && bladeByIndex.props) {
|
|
286
|
-
return value.props.navigation.idx >= bladeByIndex.props.navigation.idx;
|
|
287
|
-
}
|
|
288
|
-
}) as Record<string, BladeVNode>,
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
return changeLocation && (await router.replace(routeWithNamedBlade?.path));
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* The function `onParentCall` handles method calls from a parent component and executes the
|
|
297
|
-
* corresponding method if it exists, otherwise it logs an error message.
|
|
298
|
-
* @param parentExposedMethods - parentExposedMethods is an object that contains the methods exposed
|
|
299
|
-
* by the parent blade. Each method is represented as a key-value pair, where the key is the method
|
|
300
|
-
* name and the value is the method itself.
|
|
301
|
-
* @param {IParentCallArgs} args - The `args` parameter is an object that contains the following
|
|
302
|
-
* properties:
|
|
303
|
-
*/
|
|
304
289
|
async function onParentCall(parentExposedMethods: Record<string, any>, args: IParentCallArgs) {
|
|
305
290
|
console.debug(`vc-app#onParentCall({ method: ${args.method} }) called.`);
|
|
306
291
|
|
|
307
|
-
if (args.method && typeof parentExposedMethods[args.method] === "function") {
|
|
292
|
+
if (args.method && parentExposedMethods && typeof parentExposedMethods[args.method] === "function") {
|
|
308
293
|
const method = parentExposedMethods[args.method] as (args: unknown) => Promise<unknown>;
|
|
309
294
|
const result = await method(args.args);
|
|
310
295
|
if (typeof args.callback === "function") {
|
|
@@ -317,13 +302,6 @@ export function useBladeNavigation(): IUseBladeNavigation {
|
|
|
317
302
|
}
|
|
318
303
|
}
|
|
319
304
|
|
|
320
|
-
/**
|
|
321
|
-
* The function `resolveBladeByName` resolves a Blade component by its name and returns its
|
|
322
|
-
* constructor.
|
|
323
|
-
* @param {string} name - The `name` parameter is a string that represents the name of a Blade
|
|
324
|
-
* component.
|
|
325
|
-
* @returns a BladeInstanceConstructor, which is the constructor function for a Blade instance.
|
|
326
|
-
*/
|
|
327
305
|
function resolveBladeByName(name: string): BladeInstanceConstructor {
|
|
328
306
|
if (!instance) {
|
|
329
307
|
warn("resolveComponentByName can only be used in setup().");
|
|
@@ -343,159 +321,66 @@ export function useBladeNavigation(): IUseBladeNavigation {
|
|
|
343
321
|
}
|
|
344
322
|
}
|
|
345
323
|
|
|
346
|
-
/**
|
|
347
|
-
* The function `routeResolver` checks if a necessary route exists and generates a route if it
|
|
348
|
-
* doesn't.
|
|
349
|
-
* @param {RouteLocationNormalized} to - The `to` parameter is of type `RouteLocationNormalized`,
|
|
350
|
-
* which represents the target route that needs to be resolved. It contains information about the
|
|
351
|
-
* target route, such as the path, query parameters, and hash.
|
|
352
|
-
* @returns the result of the `generateRoute` function if the `hasNecessaryRoute` function returns
|
|
353
|
-
* false.
|
|
354
|
-
*/
|
|
355
324
|
function routeResolver(to: RouteLocationNormalized) {
|
|
356
325
|
if (!hasNecessaryRoute(to)) {
|
|
357
326
|
return generateRoute(to, navigationInstance.internalRoutes);
|
|
358
327
|
}
|
|
359
328
|
}
|
|
360
329
|
|
|
361
|
-
/**
|
|
362
|
-
* The function checks if a given route exists in a list of routes.
|
|
363
|
-
* @param {RouteLocationNormalized} to - The "to" parameter is of type RouteLocationNormalized, which
|
|
364
|
-
* represents the destination route location.
|
|
365
|
-
* @returns a route object from the `routes` array that has a matching `path` property with the
|
|
366
|
-
* `to.path` value.
|
|
367
|
-
*/
|
|
368
330
|
function hasNecessaryRoute(to: RouteLocationNormalized) {
|
|
369
331
|
return router.getRoutes().find((route) => route.path === to.path);
|
|
370
332
|
}
|
|
371
333
|
|
|
372
|
-
/**
|
|
373
|
-
* The function generates a route based on the provided destination and routes, and then pushes the
|
|
374
|
-
* generated route to the router.
|
|
375
|
-
* @param {RouteLocationNormalized} to - The `to` parameter is of type `RouteLocationNormalized` and
|
|
376
|
-
* represents the destination route that we want to generate. It contains information about the path,
|
|
377
|
-
* query parameters, and other route-related data.
|
|
378
|
-
* @param {RouteRecordNormalized[]} routes - The `routes` parameter is an array of
|
|
379
|
-
* `RouteRecordNormalized` objects. Each object represents a route in the application and contains
|
|
380
|
-
* information such as the route path, components to render, and any meta information associated with
|
|
381
|
-
* the route.
|
|
382
|
-
* @returns the result of the `router.push()` method.
|
|
383
|
-
*/
|
|
384
334
|
async function generateRoute(to: RouteLocationNormalized, routes: BladeRoutesRecord[]) {
|
|
385
|
-
const parsedRoutes
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
},
|
|
409
|
-
}) as BladeVNode;
|
|
410
|
-
|
|
411
|
-
// Add routes one by one
|
|
335
|
+
const parsedRoutes = parseUrl(to.path);
|
|
336
|
+
|
|
337
|
+
if (parsedRoutes) {
|
|
338
|
+
// here we check if route is registered in bladeRoutes
|
|
339
|
+
const registeredRouteComponent = routes.find((route) => route.route === "/" + parsedRoutes?.blade)?.component;
|
|
340
|
+
|
|
341
|
+
if (registeredRouteComponent) {
|
|
342
|
+
if (registeredRouteComponent.type?.isWorkspace) {
|
|
343
|
+
// if route is workspace we just push it to router
|
|
344
|
+
router.replace({ path: registeredRouteComponent.type.url as string });
|
|
345
|
+
} else {
|
|
346
|
+
// if route is not workspace we need to add it to router and push
|
|
347
|
+
const url =
|
|
348
|
+
"/" +
|
|
349
|
+
parsedRoutes?.workspace +
|
|
350
|
+
"/" +
|
|
351
|
+
parsedRoutes.blade +
|
|
352
|
+
(parsedRoutes.param ? "/" + parsedRoutes.param : "");
|
|
353
|
+
|
|
354
|
+
// if route is not routable we will redirect to workspace
|
|
355
|
+
if (!parsedRoutes.param && !registeredRouteComponent.type?.routable) {
|
|
356
|
+
return router.replace("/" + parsedRoutes?.workspace);
|
|
357
|
+
} else {
|
|
412
358
|
router.addRoute(mainRoute.name as string, {
|
|
413
|
-
name:
|
|
414
|
-
path:
|
|
359
|
+
name: url,
|
|
360
|
+
path: url,
|
|
415
361
|
components: {
|
|
416
|
-
default:
|
|
417
|
-
|
|
362
|
+
default: _.merge({}, registeredRouteComponent, {
|
|
363
|
+
props: {
|
|
364
|
+
param: parsedRoutes.param,
|
|
365
|
+
navigation: {
|
|
366
|
+
fullPath: url,
|
|
367
|
+
idx: 0,
|
|
368
|
+
},
|
|
369
|
+
},
|
|
370
|
+
}),
|
|
418
371
|
},
|
|
419
372
|
meta: {
|
|
420
|
-
permissions:
|
|
373
|
+
permissions: registeredRouteComponent.type?.permissions,
|
|
421
374
|
},
|
|
422
375
|
});
|
|
423
|
-
}
|
|
424
|
-
});
|
|
425
|
-
|
|
426
|
-
const mergedPermissions = Object.values(children)
|
|
427
|
-
.filter((childComponent) => childComponent.type.permissions)
|
|
428
|
-
.flatMap((comp) => mergePermissions(workspaceComponent, comp));
|
|
429
|
-
|
|
430
|
-
// Add summary route
|
|
431
|
-
router.addRoute(mainRoute.name as string, {
|
|
432
|
-
name: to.path,
|
|
433
|
-
path: to.path,
|
|
434
|
-
components: {
|
|
435
|
-
default: workspaceComponent,
|
|
436
|
-
...children,
|
|
437
|
-
},
|
|
438
|
-
meta: {
|
|
439
|
-
permissions: mergedPermissions.length ? mergedPermissions : undefined,
|
|
440
|
-
},
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
return router.push(to.path);
|
|
444
|
-
} else return router.push({ name: mainRoute.name as string });
|
|
445
|
-
}
|
|
446
376
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
* array of route objects with blade, param, and name properties.
|
|
450
|
-
* @param {string} route - The `route` parameter is a string representing a route path. It is the
|
|
451
|
-
* route that needs to be parsed into its individual parts.
|
|
452
|
-
* @param {RouteRecordNormalized[]} commonRoutes - commonRoutes is an array of RouteRecordNormalized
|
|
453
|
-
* objects. Each object represents a common route in the application and has properties like "path"
|
|
454
|
-
* which represents the route path.
|
|
455
|
-
* @returns The function `parseRoutes` returns an array of objects. Each object in the array
|
|
456
|
-
* represents a part of the route and contains the following properties: blade, param, and name.
|
|
457
|
-
*/
|
|
458
|
-
function parseRoutes(route: string, commonRoutes: BladeRoutesRecord[]) {
|
|
459
|
-
const parts: string[] = route.split("/").filter((part) => part !== "");
|
|
460
|
-
const result = [];
|
|
461
|
-
let currentBlade = "";
|
|
462
|
-
let currentName = "";
|
|
463
|
-
|
|
464
|
-
for (let i = 0; i < parts.length; i++) {
|
|
465
|
-
currentBlade = "/" + parts[i];
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* If current blade is not registered in routes, then it's a param
|
|
469
|
-
*/
|
|
470
|
-
if (!navigationInstance.internalRoutes.some((x) => currentBlade === x?.route)) {
|
|
471
|
-
baseUrl.value = currentBlade;
|
|
472
|
-
continue;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
let currentParam = null;
|
|
476
|
-
|
|
477
|
-
if (i + 1 < parts.length) {
|
|
478
|
-
const nextPart = "/" + parts.slice(i + 1, i + 2).join("/");
|
|
479
|
-
currentName += currentBlade;
|
|
480
|
-
|
|
481
|
-
if (!commonRoutes.some((route) => nextPart === route.route)) {
|
|
482
|
-
currentParam = parts[i + 1];
|
|
483
|
-
currentName += "/" + currentParam;
|
|
484
|
-
i++; // Skip the next part as it's a param
|
|
377
|
+
return router.replace(url);
|
|
378
|
+
}
|
|
485
379
|
}
|
|
486
|
-
} else
|
|
487
|
-
|
|
488
|
-
currentName += nextPart;
|
|
380
|
+
} else {
|
|
381
|
+
return router.replace({ name: mainRoute.name as string });
|
|
489
382
|
}
|
|
490
|
-
|
|
491
|
-
result.push({
|
|
492
|
-
blade: currentBlade,
|
|
493
|
-
param: currentParam,
|
|
494
|
-
name: currentName,
|
|
495
|
-
});
|
|
496
383
|
}
|
|
497
|
-
|
|
498
|
-
return result;
|
|
499
384
|
}
|
|
500
385
|
|
|
501
386
|
/**
|
|
@@ -506,12 +391,20 @@ export function useBladeNavigation(): IUseBladeNavigation {
|
|
|
506
391
|
return instance.vnode;
|
|
507
392
|
}
|
|
508
393
|
|
|
509
|
-
const currentBladeNavigationData = computed(
|
|
510
|
-
|
|
511
|
-
)
|
|
394
|
+
const currentBladeNavigationData = computed(() => navigationView?.props?.navigation ?? undefined);
|
|
395
|
+
|
|
396
|
+
function onBeforeClose(cb: () => Promise<boolean | undefined>) {
|
|
397
|
+
const instanceComponent = navigationView;
|
|
398
|
+
|
|
399
|
+
const currentBlade = navigationInstance.blades.value.find((x: any) => _.isEqual(x, instanceComponent));
|
|
400
|
+
|
|
401
|
+
if (currentBlade) {
|
|
402
|
+
currentBlade.props.navigation.onBeforeClose = cb;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
512
405
|
|
|
513
406
|
return {
|
|
514
|
-
blades,
|
|
407
|
+
blades: computed(() => navigationInstance.blades.value),
|
|
515
408
|
openBlade,
|
|
516
409
|
closeBlade,
|
|
517
410
|
onParentCall,
|
|
@@ -519,5 +412,6 @@ export function useBladeNavigation(): IUseBladeNavigation {
|
|
|
519
412
|
routeResolver,
|
|
520
413
|
getCurrentBlade,
|
|
521
414
|
currentBladeNavigationData,
|
|
415
|
+
onBeforeClose,
|
|
522
416
|
};
|
|
523
417
|
}
|