@vc-shell/framework 1.0.191 → 1.0.193
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 +23 -0
- package/core/plugins/modularity/index.ts +9 -2
- package/dist/core/plugins/modularity/index.d.ts.map +1 -1
- package/dist/framework.js +14460 -14304
- package/dist/shared/components/blade-navigation/composables/useBladeNavigation/index.d.ts +7 -2
- package/dist/shared/components/blade-navigation/composables/useBladeNavigation/index.d.ts.map +1 -1
- package/dist/shared/components/blade-navigation/types/index.d.ts +0 -1
- package/dist/shared/components/blade-navigation/types/index.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 +8 -9
- 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.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- 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
- package/dist/ui/components/organisms/vc-app/vc-app.vue.d.ts.map +1 -1
- package/package.json +4 -4
- package/shared/components/blade-navigation/composables/useBladeNavigation/index.ts +303 -192
- package/shared/components/blade-navigation/plugin.ts +1 -1
- package/shared/components/blade-navigation/types/index.ts +0 -1
- package/shared/modules/dynamic/index.ts +5 -0
- package/shared/modules/dynamic/pages/dynamic-blade-form.vue +4 -1
- package/shared/modules/dynamic/pages/dynamic-blade-list.vue +12 -5
- package/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/_internal/vc-app-menu-link.vue +14 -11
- package/ui/components/organisms/vc-app/vc-app.vue +8 -1
- package/ui/components/organisms/vc-table/vc-table.vue +2 -2
|
@@ -5,16 +5,24 @@ import {
|
|
|
5
5
|
inject,
|
|
6
6
|
warn,
|
|
7
7
|
Component,
|
|
8
|
-
watch,
|
|
9
8
|
isVNode,
|
|
10
9
|
h,
|
|
11
10
|
shallowRef,
|
|
12
11
|
ComputedRef,
|
|
12
|
+
mergeProps,
|
|
13
13
|
} from "vue";
|
|
14
14
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
15
|
-
import { createSharedComposable, reactiveComputed } from "@vueuse/core";
|
|
15
|
+
import { createSharedComposable, reactiveComputed, watchDebounced } from "@vueuse/core";
|
|
16
16
|
import * as _ from "lodash-es";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
RouteLocationNormalized,
|
|
19
|
+
useRoute,
|
|
20
|
+
NavigationFailure,
|
|
21
|
+
RouteRecordName,
|
|
22
|
+
RouteParams,
|
|
23
|
+
Router,
|
|
24
|
+
LocationQuery,
|
|
25
|
+
} from "vue-router";
|
|
18
26
|
import { bladeNavigationInstance } from "../../plugin";
|
|
19
27
|
import {
|
|
20
28
|
BladeComponentInternalInstance,
|
|
@@ -24,6 +32,7 @@ import {
|
|
|
24
32
|
IParentCallArgs,
|
|
25
33
|
BladeInstanceConstructor,
|
|
26
34
|
BladeRoutesRecord,
|
|
35
|
+
ExtractedBladeOptions,
|
|
27
36
|
} from "../../types";
|
|
28
37
|
import { navigationViewLocation } from "../../injectionKeys";
|
|
29
38
|
import { usePermissions } from "../../../../../core/composables";
|
|
@@ -43,38 +52,54 @@ interface IUseBladeNavigation {
|
|
|
43
52
|
onParentCall: (parentExposedMethods: Record<string, any>, args: IParentCallArgs) => void;
|
|
44
53
|
onBeforeClose: (cb: () => Promise<boolean | undefined>) => void;
|
|
45
54
|
resolveBladeByName: (name: string) => BladeInstanceConstructor;
|
|
46
|
-
routeResolver: (to: RouteLocationNormalized) =>
|
|
55
|
+
routeResolver: (to: RouteLocationNormalized) =>
|
|
56
|
+
| Promise<
|
|
57
|
+
| {
|
|
58
|
+
name: RouteRecordName | undefined;
|
|
59
|
+
params: RouteParams;
|
|
60
|
+
}
|
|
61
|
+
| undefined
|
|
62
|
+
>
|
|
63
|
+
| undefined;
|
|
47
64
|
getCurrentBlade: () => BladeVNode;
|
|
65
|
+
setNavigationQuery: (query: Record<string, string | number>) => void;
|
|
66
|
+
getNavigationQuery: () => Record<string, string | number>;
|
|
48
67
|
}
|
|
49
68
|
|
|
50
69
|
const activeWorkspace = shallowRef<BladeVNode>();
|
|
51
70
|
const mainRouteBaseParamURL = shallowRef<string>();
|
|
52
71
|
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
const match = url.match(urlRegex);
|
|
72
|
+
const utils = (router: Router) => {
|
|
73
|
+
const route = useRoute();
|
|
56
74
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
75
|
+
function parseUrl(url: string) {
|
|
76
|
+
// remove parts of url that does not contain workspace, blade or param - everything before workspace
|
|
77
|
+
const parts = url.split("/");
|
|
78
|
+
const workspaceIndex = parts.findIndex((part) => {
|
|
79
|
+
const route = router
|
|
80
|
+
.getRoutes()
|
|
81
|
+
.find((r) => r.path.endsWith("/" + part) && (r.components?.default as BladeVNode).type.isWorkspace);
|
|
60
82
|
|
|
61
|
-
|
|
62
|
-
|
|
83
|
+
return route !== undefined;
|
|
84
|
+
});
|
|
85
|
+
const cleanUrl = "/" + parts.slice(workspaceIndex).join("/");
|
|
63
86
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
blade: blade || undefined,
|
|
67
|
-
param: param || undefined,
|
|
68
|
-
};
|
|
69
|
-
}
|
|
87
|
+
const urlRegex = /^\/([a-zA-Z0-9_-]+)(?:\/([a-zA-Z0-9_-]+))?(?:\/([a-zA-Z0-9_-]+))?$/;
|
|
88
|
+
const match = cleanUrl.match(urlRegex);
|
|
70
89
|
|
|
71
|
-
|
|
72
|
-
|
|
90
|
+
if (!match) {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
73
93
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
95
|
+
const [_, workspace, blade, param] = match;
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
workspace,
|
|
99
|
+
blade: blade || undefined,
|
|
100
|
+
param: param || undefined,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
78
103
|
|
|
79
104
|
function parseWorkspaceUrl(path: string): string {
|
|
80
105
|
// Object.values(route.params)[0] will always be base path of the app
|
|
@@ -89,81 +114,127 @@ const useBladeNavigationSingleton = createSharedComposable(() => {
|
|
|
89
114
|
return "/" + workspaceUrl;
|
|
90
115
|
}
|
|
91
116
|
|
|
92
|
-
|
|
117
|
+
function getURLQuery() {
|
|
118
|
+
if (route.query && Object.keys(route.query).length) {
|
|
119
|
+
return { params: new URLSearchParams(route.query as Record<string, string>).toString(), obj: route.query };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const [, query] = window.location.href.split("#")[1].split("?");
|
|
123
|
+
const params = new URLSearchParams(query).toString();
|
|
124
|
+
|
|
125
|
+
return { params, obj: Object.fromEntries(new URLSearchParams(query)) };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
parseUrl,
|
|
130
|
+
parseWorkspaceUrl,
|
|
131
|
+
getURLQuery,
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const useBladeNavigationSingleton = createSharedComposable(() => {
|
|
136
|
+
const route = useRoute();
|
|
137
|
+
|
|
138
|
+
const instance: BladeComponentInternalInstance = getCurrentInstance() as BladeComponentInternalInstance;
|
|
139
|
+
const navigationInstance =
|
|
140
|
+
(instance !== null && inject<BladeNavigationPlugin>("bladeNavigationPlugin")) || bladeNavigationInstance;
|
|
141
|
+
const router = navigationInstance?.router;
|
|
142
|
+
|
|
143
|
+
const { parseUrl, parseWorkspaceUrl, getURLQuery } = utils(router);
|
|
144
|
+
|
|
145
|
+
watchDebounced(
|
|
93
146
|
() => route.path,
|
|
94
147
|
async (newVal, oldVal) => {
|
|
95
148
|
const workspaceUrl = parseWorkspaceUrl(newVal);
|
|
96
149
|
|
|
97
|
-
const wsRouteComponent =
|
|
98
|
-
(route?.matched?.[1]?.components?.default as BladeVNode) ??
|
|
99
|
-
(router.resolve({ path: workspaceUrl })?.matched?.[1]?.components?.default as BladeVNode);
|
|
150
|
+
const wsRouteComponent = getWorkspaceRouteComponent(workspaceUrl);
|
|
100
151
|
|
|
101
152
|
if (wsRouteComponent !== undefined) {
|
|
102
153
|
if (isVNode(wsRouteComponent) && wsRouteComponent.type.isBlade) {
|
|
103
|
-
|
|
104
|
-
navigationInstance.blades.value[0] = markRaw(wsRouteComponent);
|
|
105
|
-
activeWorkspace.value = wsRouteComponent;
|
|
154
|
+
updateActiveWorkspace(wsRouteComponent);
|
|
106
155
|
} else {
|
|
107
|
-
|
|
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
|
-
}
|
|
156
|
+
await handleNonBladePage(oldVal);
|
|
116
157
|
}
|
|
117
158
|
}
|
|
118
159
|
},
|
|
119
160
|
{ immediate: true },
|
|
120
161
|
);
|
|
121
162
|
|
|
122
|
-
|
|
163
|
+
async function handleNonBladePage(oldVal?: string) {
|
|
164
|
+
const isPrevented = await closeBlade(0);
|
|
165
|
+
if (!isPrevented) {
|
|
166
|
+
navigationInstance.blades.value = [];
|
|
167
|
+
activeWorkspace.value = undefined;
|
|
168
|
+
} else {
|
|
169
|
+
if (oldVal) router.replace({ path: oldVal });
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function updateActiveWorkspace(wsRouteComponent: BladeVNode) {
|
|
174
|
+
navigationInstance.blades.value[0] = markRaw(
|
|
175
|
+
Object.assign(wsRouteComponent, {
|
|
176
|
+
props: mergeProps(wsRouteComponent.props, {
|
|
177
|
+
navigation: {
|
|
178
|
+
idx: 0,
|
|
179
|
+
},
|
|
180
|
+
}),
|
|
181
|
+
}),
|
|
182
|
+
);
|
|
183
|
+
activeWorkspace.value = wsRouteComponent;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function getWorkspaceRouteComponent(workspaceUrl: string) {
|
|
187
|
+
return (
|
|
188
|
+
(route?.matched?.[1]?.components?.default as BladeVNode) ??
|
|
189
|
+
(router.resolve({ path: workspaceUrl })?.matched?.[1]?.components?.default as BladeVNode)
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
watchDebounced(
|
|
123
194
|
navigationInstance.blades,
|
|
124
|
-
(newVal) => {
|
|
195
|
+
async (newVal) => {
|
|
125
196
|
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
|
-
};
|
|
197
|
+
const lastBlade = getLastItemWithUrl(newVal);
|
|
137
198
|
|
|
138
199
|
if (workspace?.type?.url) {
|
|
139
|
-
|
|
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
|
-
|
|
200
|
+
const url = constructUrl(workspace, lastBlade);
|
|
155
201
|
if (url) {
|
|
156
|
-
|
|
157
|
-
(mainRouteBaseParamURL.value && !url.startsWith(mainRouteBaseParamURL.value)
|
|
158
|
-
? mainRouteBaseParamURL.value
|
|
159
|
-
: "") + url,
|
|
160
|
-
);
|
|
202
|
+
updateRouterHistory(url);
|
|
161
203
|
}
|
|
162
204
|
}
|
|
163
205
|
},
|
|
164
|
-
{ deep: true },
|
|
206
|
+
{ deep: true, debounce: 1 },
|
|
165
207
|
);
|
|
166
208
|
|
|
209
|
+
function getLastItemWithUrl(newVal: BladeVNode[]) {
|
|
210
|
+
for (let i = newVal.length - 1; i > 0; i--) {
|
|
211
|
+
if (newVal[i].type.url) {
|
|
212
|
+
return newVal[i];
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function constructUrl(workspace: BladeVNode, lastBlade?: BladeVNode) {
|
|
218
|
+
const wsBladeUrl = workspace?.type.url;
|
|
219
|
+
const lastBladeUrl = lastBlade?.type.url;
|
|
220
|
+
const param = lastBlade?.props?.param;
|
|
221
|
+
if (lastBlade && wsBladeUrl) {
|
|
222
|
+
return "/" + parseUrl(wsBladeUrl)?.workspace + lastBladeUrl + (param ? "/" + param : "");
|
|
223
|
+
} else {
|
|
224
|
+
return wsBladeUrl;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function updateRouterHistory(url: string) {
|
|
229
|
+
const params = getURLQuery().params;
|
|
230
|
+
|
|
231
|
+
router.options.history.replace(
|
|
232
|
+
(mainRouteBaseParamURL.value && !url.startsWith(mainRouteBaseParamURL.value) ? mainRouteBaseParamURL.value : "") +
|
|
233
|
+
url +
|
|
234
|
+
(params ? "?" + params : ""),
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
|
|
167
238
|
async function closeBlade(index: number) {
|
|
168
239
|
console.debug(`[@vc-shell/framework#useBladeNavigation] - closeBlade called.`);
|
|
169
240
|
|
|
@@ -215,10 +286,15 @@ export function useBladeNavigation(): IUseBladeNavigation {
|
|
|
215
286
|
const instance: BladeComponentInternalInstance = getCurrentInstance() as BladeComponentInternalInstance;
|
|
216
287
|
|
|
217
288
|
const { router, route, navigationInstance, closeBlade } = useBladeNavigationSingleton();
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
289
|
+
const { parseUrl, getURLQuery } = utils(router);
|
|
290
|
+
const routerRoutes = router.getRoutes();
|
|
291
|
+
const mainRoute = routerRoutes.find((r) => r.meta?.root)!;
|
|
292
|
+
|
|
293
|
+
async function openWorkspace<Blade extends Component>(
|
|
294
|
+
{ blade, param, options }: IBladeEvent<Blade>,
|
|
295
|
+
query: LocationQuery | undefined = undefined,
|
|
296
|
+
params: RouteParams = {},
|
|
297
|
+
) {
|
|
222
298
|
const createdComponent = h(blade, {
|
|
223
299
|
param,
|
|
224
300
|
options,
|
|
@@ -228,26 +304,25 @@ export function useBladeNavigation(): IUseBladeNavigation {
|
|
|
228
304
|
}) as BladeVNode;
|
|
229
305
|
|
|
230
306
|
try {
|
|
231
|
-
|
|
307
|
+
// Close all blades except the first one cause it will be overwritten
|
|
308
|
+
const isPrevented = await closeBlade(1);
|
|
232
309
|
|
|
233
310
|
if (!isPrevented && createdComponent.type?.url) {
|
|
234
311
|
if (hasAccess(blade.permissions)) {
|
|
235
312
|
navigationInstance.blades.value = [createdComponent];
|
|
236
|
-
|
|
237
313
|
// Find the route with the matching URL and update the components.default property with the new component
|
|
238
|
-
const
|
|
239
|
-
if (
|
|
240
|
-
|
|
314
|
+
const wsroute = routerRoutes.find((r) => r.path.endsWith(createdComponent.type?.url as string));
|
|
315
|
+
if (wsroute && wsroute.components) {
|
|
316
|
+
wsroute.components.default = createdComponent;
|
|
241
317
|
}
|
|
242
|
-
|
|
243
|
-
return await router.replace({ path: createdComponent.type?.url as string });
|
|
318
|
+
return await router.replace({ name: wsroute?.name, params: { ...params, ...route.params }, query });
|
|
244
319
|
} else
|
|
245
320
|
notification.error(i18n.global.t("PERMISSION_MESSAGES.ACCESS_RESTRICTED"), {
|
|
246
321
|
timeout: 3000,
|
|
247
322
|
});
|
|
248
323
|
}
|
|
249
324
|
} catch (e) {
|
|
250
|
-
console.
|
|
325
|
+
console.error(e);
|
|
251
326
|
throw new Error(`Opening workspace '${blade.type.name}' is prevented`);
|
|
252
327
|
}
|
|
253
328
|
}
|
|
@@ -267,52 +342,64 @@ export function useBladeNavigation(): IUseBladeNavigation {
|
|
|
267
342
|
try {
|
|
268
343
|
const instanceComponent = navigationView || activeWorkspace.value;
|
|
269
344
|
|
|
270
|
-
if (
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
.findLastIndex((x) => _.isEqual(x.type, instanceComponent.type));
|
|
345
|
+
if (!instanceComponent) {
|
|
346
|
+
throw new Error("No workspace found");
|
|
347
|
+
}
|
|
274
348
|
|
|
275
|
-
|
|
276
|
-
|
|
349
|
+
const instanceComponentIndex = findInstanceComponentIndex(instanceComponent);
|
|
350
|
+
const instanceComponentChild = navigationInstance.blades.value[instanceComponentIndex + 1];
|
|
277
351
|
|
|
278
|
-
|
|
352
|
+
let isPrevented = false;
|
|
279
353
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
354
|
+
if (instanceComponentChild) {
|
|
355
|
+
isPrevented = await closeBlade(instanceComponentChild.props?.navigation?.idx);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const currentBladeIdx = instanceComponent.props?.navigation?.idx ?? 0;
|
|
283
359
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
-
});
|
|
360
|
+
const bladeNode = createBladeNode<Blade>({ blade, currentBladeIdx, options, param, onClose, onOpen });
|
|
361
|
+
|
|
362
|
+
if (!isPrevented) {
|
|
363
|
+
if (hasAccess(blade.permissions)) {
|
|
364
|
+
navigationInstance.blades.value.push(bladeNode);
|
|
365
|
+
} else {
|
|
366
|
+
notification.error(i18n.global.t("PERMISSION_MESSAGES.ACCESS_RESTRICTED"), { timeout: 3000 });
|
|
307
367
|
}
|
|
308
|
-
} else {
|
|
309
|
-
throw new Error("No workspace found");
|
|
310
368
|
}
|
|
311
369
|
} catch (e) {
|
|
312
370
|
console.error(e);
|
|
313
371
|
}
|
|
314
372
|
}
|
|
315
373
|
|
|
374
|
+
function findInstanceComponentIndex(instanceComponent: BladeVNode) {
|
|
375
|
+
return navigationInstance.blades.value.findIndex((x) => _.isEqual(x.type, instanceComponent.type));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function createBladeNode<Blade extends Component>(args: {
|
|
379
|
+
blade: BladeInstanceConstructor<Blade>;
|
|
380
|
+
currentBladeIdx: number;
|
|
381
|
+
options: ExtractedBladeOptions<InstanceType<BladeInstanceConstructor<Blade>>["$props"], "options"> | undefined;
|
|
382
|
+
param?: string;
|
|
383
|
+
onClose?: () => void;
|
|
384
|
+
onOpen?: () => void;
|
|
385
|
+
}) {
|
|
386
|
+
const { blade, currentBladeIdx, options, param, onClose, onOpen } = args;
|
|
387
|
+
return h(
|
|
388
|
+
blade,
|
|
389
|
+
Object.assign(
|
|
390
|
+
{},
|
|
391
|
+
reactiveComputed(() => ({ options, param })),
|
|
392
|
+
{
|
|
393
|
+
navigation: {
|
|
394
|
+
onClose,
|
|
395
|
+
onOpen,
|
|
396
|
+
idx: currentBladeIdx + 1,
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
),
|
|
400
|
+
) as BladeVNode;
|
|
401
|
+
}
|
|
402
|
+
|
|
316
403
|
async function onParentCall(parentExposedMethods: Record<string, any>, args: IParentCallArgs) {
|
|
317
404
|
console.debug(`vc-app#onParentCall({ method: ${args.method} }) called.`);
|
|
318
405
|
|
|
@@ -355,7 +442,7 @@ export function useBladeNavigation(): IUseBladeNavigation {
|
|
|
355
442
|
}
|
|
356
443
|
|
|
357
444
|
function hasNecessaryRoute(to: RouteLocationNormalized) {
|
|
358
|
-
return
|
|
445
|
+
return routerRoutes.find((route) => route.path === to.path);
|
|
359
446
|
}
|
|
360
447
|
|
|
361
448
|
/**
|
|
@@ -369,92 +456,77 @@ export function useBladeNavigation(): IUseBladeNavigation {
|
|
|
369
456
|
*/
|
|
370
457
|
async function generateRoute(to: RouteLocationNormalized, routes: BladeRoutesRecord[]) {
|
|
371
458
|
// 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
|
-
);
|
|
459
|
+
const params = Object.fromEntries(Object.entries(to.params).filter(([key]) => key !== "pathMatch"));
|
|
381
460
|
|
|
382
461
|
// Get the raw path of the main route.
|
|
383
|
-
const parentRawPath =
|
|
462
|
+
const parentRawPath = routerRoutes.find((route) => route.name === mainRoute.name)?.path;
|
|
384
463
|
|
|
385
464
|
// Determine the parent path based on the parameters.
|
|
386
|
-
const parentPath =
|
|
387
|
-
|
|
388
|
-
: "";
|
|
465
|
+
const parentPath =
|
|
466
|
+
parentRawPath && parentRawPath.includes(Object.keys(params)[0]) ? `/${Object.values(params)[0]}` : "";
|
|
389
467
|
|
|
390
468
|
// Set the base param value.
|
|
391
469
|
mainRouteBaseParamURL.value = parentPath;
|
|
392
470
|
|
|
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
471
|
// Parse the URL to extract relevant route information.
|
|
400
|
-
const parsedRoutes = parseUrl(
|
|
472
|
+
const parsedRoutes = parseUrl(parentPath !== "/" ? to.path.slice(parentPath.length) : to.path);
|
|
473
|
+
|
|
474
|
+
if (parsedRoutes !== undefined) {
|
|
475
|
+
const { workspace, blade, param } = parsedRoutes;
|
|
401
476
|
|
|
402
|
-
if (parsedRoutes) {
|
|
403
477
|
// Find the registered route component.
|
|
404
|
-
const
|
|
478
|
+
const registeredWorkspaceComponent = routes.find((route) => route.route === `/${workspace}`)?.component;
|
|
479
|
+
const registeredRouteComponent = routes.find((route) => route.route === `/${blade}`)?.component;
|
|
405
480
|
|
|
406
|
-
if (
|
|
407
|
-
|
|
408
|
-
|
|
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;
|
|
481
|
+
if (!registeredWorkspaceComponent) {
|
|
482
|
+
return goToRoot();
|
|
483
|
+
}
|
|
452
484
|
|
|
453
|
-
|
|
485
|
+
// Open the workspace component or workspace route.
|
|
486
|
+
if (registeredRouteComponent?.type.isWorkspace) {
|
|
487
|
+
await openWorkspace({
|
|
488
|
+
blade: registeredRouteComponent as unknown as BladeInstanceConstructor,
|
|
489
|
+
});
|
|
490
|
+
return { name: registeredRouteComponent?.type.name, params };
|
|
454
491
|
}
|
|
492
|
+
|
|
493
|
+
// Open the workspace component with param or workspace route.
|
|
494
|
+
if (registeredWorkspaceComponent) {
|
|
495
|
+
await openWorkspace(
|
|
496
|
+
{
|
|
497
|
+
blade: registeredWorkspaceComponent as unknown as BladeInstanceConstructor,
|
|
498
|
+
param:
|
|
499
|
+
registeredRouteComponent?.type.moduleUid === registeredWorkspaceComponent.type.moduleUid
|
|
500
|
+
? param
|
|
501
|
+
: undefined,
|
|
502
|
+
},
|
|
503
|
+
getURLQuery().obj,
|
|
504
|
+
params,
|
|
505
|
+
);
|
|
506
|
+
|
|
507
|
+
// Open the route if it's not from the workspace module.
|
|
508
|
+
if (
|
|
509
|
+
registeredRouteComponent?.type.moduleUid !== registeredWorkspaceComponent.type.moduleUid &&
|
|
510
|
+
registeredRouteComponent?.type.routable
|
|
511
|
+
) {
|
|
512
|
+
await openBlade({
|
|
513
|
+
blade: registeredRouteComponent as unknown as BladeInstanceConstructor,
|
|
514
|
+
param: param,
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
return { name: registeredWorkspaceComponent?.type.name, params, query: to.query };
|
|
518
|
+
}
|
|
519
|
+
} else {
|
|
520
|
+
return goToRoot();
|
|
455
521
|
}
|
|
456
522
|
}
|
|
457
523
|
|
|
524
|
+
function goToRoot() {
|
|
525
|
+
const mainRoute = routerRoutes.find((route) => route.meta?.root);
|
|
526
|
+
const mainRouteAlias = routerRoutes.find((route) => route.aliasOf?.path === mainRoute?.path) || mainRoute;
|
|
527
|
+
return { name: mainRouteAlias?.name, params: route.params };
|
|
528
|
+
}
|
|
529
|
+
|
|
458
530
|
/**
|
|
459
531
|
* The function getCurrentBlade returns the current BladeVNode instance's vnode.
|
|
460
532
|
* @returns the `vnode` property of the `instance` object, which is of type `BladeVNode`.
|
|
@@ -475,6 +547,43 @@ export function useBladeNavigation(): IUseBladeNavigation {
|
|
|
475
547
|
}
|
|
476
548
|
}
|
|
477
549
|
|
|
550
|
+
function setNavigationQuery(query: Record<string, string | number>) {
|
|
551
|
+
// add blade name to query keys
|
|
552
|
+
const namedQuery = _.mapKeys(
|
|
553
|
+
_.mapValues(query, (value) => value?.toString()),
|
|
554
|
+
(value, key) => instance.vnode.type.name.toLowerCase() + "_" + key,
|
|
555
|
+
);
|
|
556
|
+
const cleanQuery = _.omitBy(namedQuery, _.isNil);
|
|
557
|
+
|
|
558
|
+
router.options.history.replace(
|
|
559
|
+
decodeURIComponent(
|
|
560
|
+
`${window.location.hash.substring(1).split("?")[0]}?${new URLSearchParams(cleanQuery).toString()}`,
|
|
561
|
+
),
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
function getNavigationQuery() {
|
|
566
|
+
const queryKeys = Array.from(Object.keys(route.query));
|
|
567
|
+
const bladeQueryKeys = queryKeys.filter((key) => key.startsWith(instance.vnode.type.name.toLowerCase()));
|
|
568
|
+
|
|
569
|
+
const namedQuery = _.mapKeys(_.pick(route.query, bladeQueryKeys), (value, key) =>
|
|
570
|
+
key.replace(instance.vnode.type.name.toLowerCase() + "_", ""),
|
|
571
|
+
) as Record<string, string | number>;
|
|
572
|
+
|
|
573
|
+
const obj: typeof namedQuery = {};
|
|
574
|
+
for (const [key, value] of Object.entries(namedQuery)) {
|
|
575
|
+
const numValue = Number(value);
|
|
576
|
+
|
|
577
|
+
if (!isNaN(numValue)) {
|
|
578
|
+
obj[key] = numValue;
|
|
579
|
+
} else {
|
|
580
|
+
obj[key] = value;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
return obj;
|
|
585
|
+
}
|
|
586
|
+
|
|
478
587
|
return {
|
|
479
588
|
blades: computed(() => navigationInstance.blades.value),
|
|
480
589
|
openBlade,
|
|
@@ -485,5 +594,7 @@ export function useBladeNavigation(): IUseBladeNavigation {
|
|
|
485
594
|
getCurrentBlade,
|
|
486
595
|
currentBladeNavigationData,
|
|
487
596
|
onBeforeClose,
|
|
597
|
+
setNavigationQuery,
|
|
598
|
+
getNavigationQuery,
|
|
488
599
|
};
|
|
489
600
|
}
|
|
@@ -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[];
|