@vc-shell/framework 1.1.0-alpha.5 → 1.1.0-alpha.7
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/core/composables/useGlobalSearch/index.ts +4 -4
- package/core/composables/useMenuService/index.ts +20 -110
- package/core/composables/useWidgets/index.ts +2 -1
- package/core/constants/index.ts +2 -0
- package/core/plugins/modularity/index.ts +4 -4
- package/core/services/menu-service.ts +459 -0
- package/core/services/widget-service.ts +20 -0
- package/core/types/index.ts +14 -1
- package/dist/core/composables/useGlobalSearch/index.d.ts +2 -2
- package/dist/core/composables/useMenuService/index.d.ts +4 -10
- package/dist/core/composables/useMenuService/index.d.ts.map +1 -1
- package/dist/core/composables/useWidgets/index.d.ts +2 -1
- package/dist/core/composables/useWidgets/index.d.ts.map +1 -1
- package/dist/core/constants/index.d.ts +1 -0
- package/dist/core/constants/index.d.ts.map +1 -1
- package/dist/core/plugins/modularity/index.d.ts.map +1 -1
- package/dist/core/services/menu-service.d.ts +50 -0
- package/dist/core/services/menu-service.d.ts.map +1 -0
- package/dist/core/services/widget-service.d.ts +4 -0
- package/dist/core/services/widget-service.d.ts.map +1 -1
- package/dist/core/types/index.d.ts +14 -1
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/framework.js +227 -216
- package/dist/{index-DftzTUkM.js → index-4fNoXD3u.js} +1 -1
- package/dist/{index-NzMAKJjd.js → index-7YHBATKO.js} +1 -1
- package/dist/{index-CVRw68l1.js → index-7gQRbSrG.js} +1 -1
- package/dist/{index-CabhPQ7N.js → index-B1NfXGpu.js} +1 -1
- package/dist/{index-CebrwV8l.js → index-B42ra4oQ.js} +26625 -26111
- package/dist/{index-Bvk6Bxh6.js → index-Bixm_Atu.js} +1 -1
- package/dist/{index-BzlggnM4.js → index-C9NLyptv.js} +1 -1
- package/dist/{index-T0rTYtsD.js → index-CPjPhNQr.js} +1 -1
- package/dist/{index-ClLnTkeM.js → index-CR62_U0-.js} +1 -1
- package/dist/{index-z-TY6ELw.js → index-DHUS6fMi.js} +1 -1
- package/dist/{index-BRw_Gtzh.js → index-DKMXMXmO.js} +1 -1
- package/dist/{index-XpeCWx3L.js → index-DxhPupsj.js} +1 -1
- package/dist/{index-BTjwjD_q.js → index-DyDl-e3b.js} +1 -1
- package/dist/{index-Dho76GDx.js → index-IdAZC2W6.js} +1 -1
- package/dist/{index-4hH_NTkO.js → index-QiYxGOfR.js} +1 -1
- package/dist/{index-BqAfhE0O.js → index-qjW_Kc_I.js} +1 -1
- package/dist/{index-CPfPePmU.js → index-tHx2asQS.js} +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/injection-keys.d.ts +2 -0
- package/dist/injection-keys.d.ts.map +1 -1
- package/dist/shared/components/blade-navigation/components/vc-blade-view/vc-blade-view.d.ts.map +1 -1
- package/dist/shared/components/draggable-dashboard/DraggableDashboard.vue.d.ts +2 -0
- package/dist/shared/components/draggable-dashboard/DraggableDashboard.vue.d.ts.map +1 -1
- package/dist/shared/components/draggable-dashboard/composables/useCellSizeCalculator.d.ts +25 -0
- package/dist/shared/components/draggable-dashboard/composables/useCellSizeCalculator.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useCollisionDetection.d.ts +27 -0
- package/dist/shared/components/draggable-dashboard/composables/useCollisionDetection.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useDashboardDragAndDrop.d.ts +22 -0
- package/dist/shared/components/draggable-dashboard/composables/useDashboardDragAndDrop.d.ts.map +1 -1
- package/dist/shared/components/draggable-dashboard/composables/useDashboardGrid.d.ts +12 -4
- package/dist/shared/components/draggable-dashboard/composables/useDashboardGrid.d.ts.map +1 -1
- package/dist/shared/components/draggable-dashboard/composables/useDragClone.d.ts +15 -0
- package/dist/shared/components/draggable-dashboard/composables/useDragClone.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useEventCoordinates.d.ts +33 -0
- package/dist/shared/components/draggable-dashboard/composables/useEventCoordinates.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useGridPosition.d.ts +57 -0
- package/dist/shared/components/draggable-dashboard/composables/useGridPosition.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useGridSystem.d.ts +22 -0
- package/dist/shared/components/draggable-dashboard/composables/useGridSystem.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useLayoutPersistence.d.ts +19 -0
- package/dist/shared/components/draggable-dashboard/composables/useLayoutPersistence.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useResizeObserver.d.ts +18 -0
- package/dist/shared/components/draggable-dashboard/composables/useResizeObserver.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useWidgetLayout.d.ts +14 -0
- package/dist/shared/components/draggable-dashboard/composables/useWidgetLayout.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/composables/useWidgetStyles.d.ts +21 -0
- package/dist/shared/components/draggable-dashboard/composables/useWidgetStyles.d.ts.map +1 -0
- package/dist/shared/components/draggable-dashboard/types.d.ts +5 -1
- package/dist/shared/components/draggable-dashboard/types.d.ts.map +1 -1
- package/dist/shared/components/notification-dropdown/notification-dropdown.vue.d.ts.map +1 -1
- package/dist/shared/components/sidebar/sidebar.vue.d.ts.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/ui/components/atoms/vc-badge/vc-badge.vue.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-icon/icons/FulfillmentCentersIcon.vue.d.ts +18 -0
- package/dist/ui/components/atoms/vc-icon/icons/FulfillmentCentersIcon.vue.d.ts.map +1 -0
- package/dist/ui/components/atoms/vc-icon/icons/OffersIcon.vue.d.ts +18 -0
- package/dist/ui/components/atoms/vc-icon/icons/OffersIcon.vue.d.ts.map +1 -0
- package/dist/ui/components/atoms/vc-icon/icons/OrdersIcon.vue.d.ts +18 -0
- package/dist/ui/components/atoms/vc-icon/icons/OrdersIcon.vue.d.ts.map +1 -0
- package/dist/ui/components/atoms/vc-icon/icons/PeopleIcon.vue.d.ts +18 -0
- package/dist/ui/components/atoms/vc-icon/icons/PeopleIcon.vue.d.ts.map +1 -0
- package/dist/ui/components/atoms/vc-icon/icons/ProductsIcon.vue.d.ts +18 -0
- package/dist/ui/components/atoms/vc-icon/icons/ProductsIcon.vue.d.ts.map +1 -0
- package/dist/ui/components/atoms/vc-icon/icons/ProfileIcon.vue.d.ts +18 -0
- package/dist/ui/components/atoms/vc-icon/icons/ProfileIcon.vue.d.ts.map +1 -0
- package/dist/ui/components/atoms/vc-icon/icons/index.d.ts +6 -0
- package/dist/ui/components/atoms/vc-icon/icons/index.d.ts.map +1 -1
- package/dist/ui/components/atoms/vc-icon/vc-icon.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-app/_internal/vc-app-bar/_internal/AppBarHeader.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-app/_internal/vc-app-bar/_internal/AppBarOverlay.vue.d.ts.map +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/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue.d.ts +2 -1
- 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
- package/dist/ui/components/organisms/vc-app/vc-app.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-blade/_internal/vc-blade-header/vc-blade-header.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/vc-blade-toolbar.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-blade/vc-blade.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/_internal/vc-table-base-header/vc-table-base-header.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/_internal/vc-table-desktop-view/_internal/vc-table-columns-header/vc-table-columns-header.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/_internal/vc-table-filter/vc-table-filter.vue.d.ts.map +1 -1
- package/dist/ui/components/organisms/vc-table/vc-table.vue.d.ts.map +1 -1
- package/dist/ui/composables/useVisibleElements.d.ts.map +1 -1
- package/package.json +4 -4
- package/shared/components/blade-navigation/components/vc-blade-view/vc-blade-view.ts +2 -2
- package/shared/components/blade-navigation/types/index.ts +117 -117
- package/shared/components/draggable-dashboard/DraggableDashboard.vue +116 -153
- package/shared/components/draggable-dashboard/_internal/DashboardWidget.vue +16 -32
- package/shared/components/draggable-dashboard/composables/useCellSizeCalculator.ts +121 -0
- package/shared/components/draggable-dashboard/composables/useCollisionDetection.ts +219 -0
- package/shared/components/draggable-dashboard/composables/useDashboardDragAndDrop.ts +126 -331
- package/shared/components/draggable-dashboard/composables/useDashboardGrid.ts +74 -220
- package/shared/components/draggable-dashboard/composables/useDragClone.ts +97 -0
- package/shared/components/draggable-dashboard/composables/useEventCoordinates.ts +91 -0
- package/shared/components/draggable-dashboard/composables/useGridPosition.ts +150 -0
- package/shared/components/draggable-dashboard/composables/useGridSystem.ts +169 -0
- package/shared/components/draggable-dashboard/composables/useLayoutPersistence.ts +89 -0
- package/shared/components/draggable-dashboard/composables/useResizeObserver.ts +105 -0
- package/shared/components/draggable-dashboard/composables/useWidgetLayout.ts +264 -0
- package/shared/components/draggable-dashboard/composables/useWidgetStyles.ts +120 -0
- package/shared/components/draggable-dashboard/types.ts +6 -1
- package/shared/components/notification-dropdown/notification-dropdown.vue +11 -1
- package/shared/components/sidebar/sidebar.vue +36 -34
- package/shared/pages/LoginPage/components/login/Login.vue +0 -2
- package/ui/components/atoms/vc-badge/vc-badge.vue +26 -10
- package/ui/components/atoms/vc-icon/icons/FulfillmentCentersIcon.vue +27 -0
- package/ui/components/atoms/vc-icon/icons/OffersIcon.vue +23 -0
- package/ui/components/atoms/vc-icon/icons/OrdersIcon.vue +19 -0
- package/ui/components/atoms/vc-icon/icons/PeopleIcon.vue +21 -0
- package/ui/components/atoms/vc-icon/icons/ProductsIcon.vue +23 -0
- package/ui/components/atoms/vc-icon/icons/ProfileIcon.vue +18 -0
- package/ui/components/atoms/vc-icon/icons/index.ts +6 -0
- package/ui/components/atoms/vc-icon/vc-icon.vue +101 -82
- package/ui/components/atoms/vc-tooltip/vc-tooltip.vue +2 -2
- package/ui/components/organisms/vc-app/_internal/vc-app-bar/_internal/AppBarHeader.vue +31 -2
- package/ui/components/organisms/vc-app/_internal/vc-app-bar/_internal/AppBarOverlay.vue +15 -13
- package/ui/components/organisms/vc-app/_internal/vc-app-bar/vc-app-bar.vue +26 -15
- package/ui/components/organisms/vc-app/_internal/vc-app-menu/_internal/vc-app-menu-item/vc-app-menu-item.vue +2 -2
- package/ui/components/organisms/vc-app/_internal/vc-app-menu/vc-app-menu.vue +1 -0
- package/ui/components/organisms/vc-app/vc-app.vue +16 -5
- package/ui/components/organisms/vc-blade/_internal/vc-blade-header/vc-blade-header.vue +4 -5
- package/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-buttons/_internal/vc-blade-toolbar-button/vc-blade-toolbar-circle-button.vue +2 -2
- package/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/_internal/vc-blade-toolbar-buttons/mobile/vc-blade-toolbar-mobile.vue +1 -1
- package/ui/components/organisms/vc-blade/_internal/vc-blade-toolbar/vc-blade-toolbar.vue +0 -3
- package/ui/components/organisms/vc-blade/vc-blade.vue +7 -2
- package/ui/components/organisms/vc-login-form/vc-login-form.vue +99 -99
- package/ui/components/organisms/vc-table/_internal/vc-table-base-header/vc-table-base-header.vue +5 -5
- package/ui/components/organisms/vc-table/_internal/vc-table-desktop-view/_internal/vc-table-columns-header/vc-table-columns-header.vue +1 -0
- package/ui/components/organisms/vc-table/_internal/vc-table-filter/vc-table-filter.vue +40 -28
- package/ui/components/organisms/vc-table/_internal/vc-table-header/vc-table-header.vue +1 -1
- package/ui/components/organisms/vc-table/vc-table.vue +9 -5
- package/ui/composables/useVisibleElements.ts +2 -0
|
@@ -4,19 +4,19 @@ import { GlobalSearchKey } from "../../../injection-keys";
|
|
|
4
4
|
export interface GlobalSearchState {
|
|
5
5
|
isSearchVisible: Ref<Record<string, boolean>>;
|
|
6
6
|
searchQuery: Ref<Record<string, string>>;
|
|
7
|
-
toggleSearch: (bladeId:
|
|
8
|
-
setSearchQuery: (bladeId:
|
|
7
|
+
toggleSearch: (bladeId: string) => void;
|
|
8
|
+
setSearchQuery: (bladeId: string, query: string) => void;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export function createGlobalSearch() {
|
|
12
12
|
const isSearchVisible = ref<Record<string, boolean>>({});
|
|
13
13
|
const searchQuery = ref<Record<string, string>>({});
|
|
14
14
|
|
|
15
|
-
const toggleSearch = (bladeId:
|
|
15
|
+
const toggleSearch = (bladeId: string) => {
|
|
16
16
|
isSearchVisible.value[bladeId] = !isSearchVisible.value[bladeId];
|
|
17
17
|
};
|
|
18
18
|
|
|
19
|
-
const setSearchQuery = (bladeId:
|
|
19
|
+
const setSearchQuery = (bladeId: string, query: string) => {
|
|
20
20
|
searchQuery.value[bladeId] = query;
|
|
21
21
|
};
|
|
22
22
|
|
|
@@ -1,110 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
constructMenu();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const upsert = createUnrefFn(
|
|
25
|
-
(array: (MenuItem | Omit<MenuItem, "icon">)[], element: MenuItem | Omit<MenuItem, "icon">) => {
|
|
26
|
-
const index = array.findIndex((_element) => _.isEqual(_.omit(_element, "title"), _.omit(element, "title")));
|
|
27
|
-
if (index > -1) {
|
|
28
|
-
array[index] = { ...element };
|
|
29
|
-
} else {
|
|
30
|
-
array.push({ ...element });
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
function sortByPriority(a: MenuItem, b: MenuItem): number {
|
|
36
|
-
const getPriority = (item: MenuItem): number => item.priority ?? Infinity;
|
|
37
|
-
return getPriority(a) - getPriority(b);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function sortByGroupPriority(a: MenuItem, b: MenuItem): number {
|
|
41
|
-
const getGroupPriority = (item: MenuItem): number => item.inGroupPriority ?? Infinity;
|
|
42
|
-
return getGroupPriority(a) - getGroupPriority(b);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function constructMenu(): void {
|
|
46
|
-
const constructedMenu: Ref<MenuItem[]> = ref([]);
|
|
47
|
-
|
|
48
|
-
rawMenu.value.forEach((item) => {
|
|
49
|
-
if (item.group) {
|
|
50
|
-
const isGroupExist = useArrayFind(
|
|
51
|
-
constructedMenu,
|
|
52
|
-
(m) => m.groupId === "group_" + _.snakeCase(t(item.group ?? "")),
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
const groupItem = _.omit(
|
|
56
|
-
{
|
|
57
|
-
...item,
|
|
58
|
-
title: computed(() => t(item.title as string)),
|
|
59
|
-
},
|
|
60
|
-
"group",
|
|
61
|
-
"groupIcon",
|
|
62
|
-
"groupPriority",
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
if (isGroupExist.value && isGroupExist.value.children) {
|
|
66
|
-
upsert(isGroupExist.value.children, groupItem);
|
|
67
|
-
} else {
|
|
68
|
-
const group: Omit<MenuItem, "icon"> = {
|
|
69
|
-
groupId: "group_" + _.snakeCase(t(item.group)),
|
|
70
|
-
groupIcon: item.groupIcon ?? "",
|
|
71
|
-
title: computed(() => t(item.group as string)),
|
|
72
|
-
children: [_.omit(groupItem)],
|
|
73
|
-
priority: item.priority,
|
|
74
|
-
};
|
|
75
|
-
upsert(constructedMenu.value, group);
|
|
76
|
-
}
|
|
77
|
-
} else {
|
|
78
|
-
if (item.title) {
|
|
79
|
-
upsert(constructedMenu.value, { ...item });
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
menuItems.value = constructedMenu.value
|
|
85
|
-
.map(
|
|
86
|
-
(x): MenuItem => ({
|
|
87
|
-
...x,
|
|
88
|
-
title: computed(() => t(x.title as string)),
|
|
89
|
-
id: _.snakeCase(t(x.title as string)),
|
|
90
|
-
children: x.children?.sort(sortByGroupPriority),
|
|
91
|
-
}),
|
|
92
|
-
)
|
|
93
|
-
.sort(sortByPriority);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function removeMenuItem(item: MenuItem): void {
|
|
97
|
-
const index = menuItems.value.indexOf(item);
|
|
98
|
-
menuItems.value.splice(index, 1);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
addMenuItem,
|
|
103
|
-
menuItems,
|
|
104
|
-
removeMenuItem,
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const useMenuService = createSharedComposable(useMenuServiceFn);
|
|
109
|
-
|
|
110
|
-
export { useMenuService };
|
|
1
|
+
import { provide, inject, getCurrentInstance } from "vue";
|
|
2
|
+
import { MenuService, createMenuService, addMenuItem } from "../../services/menu-service";
|
|
3
|
+
import { MenuServiceKey } from "../../../injection-keys";
|
|
4
|
+
|
|
5
|
+
export function provideMenuService(): MenuService {
|
|
6
|
+
const service = createMenuService();
|
|
7
|
+
provide(MenuServiceKey, service);
|
|
8
|
+
return service;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function useMenuService(): MenuService {
|
|
12
|
+
const service = inject(MenuServiceKey);
|
|
13
|
+
if (!service) {
|
|
14
|
+
console.error("Menu service not found in current context. Injection chain:", getCurrentInstance());
|
|
15
|
+
throw new Error("MenuService not provided");
|
|
16
|
+
}
|
|
17
|
+
return service;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { addMenuItem };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getCurrentInstance, inject, provide } from "vue";
|
|
2
|
-
import { createWidgetService, IWidgetService } from "./../../services/widget-service";
|
|
2
|
+
import { createWidgetService, IWidgetService, registerWidget } from "./../../services/widget-service";
|
|
3
3
|
import { WidgetServiceKey } from "./../../../injection-keys";
|
|
4
4
|
|
|
5
5
|
export function provideWidgetService(): IWidgetService {
|
|
@@ -17,3 +17,4 @@ export function useWidgets(): IWidgetService {
|
|
|
17
17
|
return service;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
export { registerWidget };
|
package/core/constants/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { App, Component, h, watch } from "vue";
|
|
1
|
+
import { App, Component, h, resolveComponent, watch } from "vue";
|
|
2
2
|
import { i18n } from "./../i18n";
|
|
3
3
|
import { Router } from "vue-router";
|
|
4
4
|
import { BladeInstanceConstructor, BladeVNode } from "./../../../shared/components/blade-navigation/types";
|
|
5
5
|
import { kebabToPascal } from "./../../utilities";
|
|
6
|
-
import { useMenuService, useNotifications } from "../../composables";
|
|
6
|
+
import { addMenuItem, useMenuService, useNotifications } from "../../composables";
|
|
7
7
|
import * as _ from "lodash-es";
|
|
8
8
|
import { notification } from "../../../shared";
|
|
9
9
|
|
|
@@ -200,12 +200,12 @@ export function createAppModule(
|
|
|
200
200
|
|
|
201
201
|
// Add to menu
|
|
202
202
|
if (page.menuItem) {
|
|
203
|
-
const { addMenuItem } = useMenuService();
|
|
204
203
|
addMenuItem({
|
|
205
204
|
...page.menuItem,
|
|
205
|
+
icon: resolveComponent(page.menuItem.icon as string),
|
|
206
206
|
url: page.url,
|
|
207
207
|
routeId: routeName,
|
|
208
|
-
permissions: page.permissions,
|
|
208
|
+
permissions: page.permissions || page.menuItem.permissions,
|
|
209
209
|
});
|
|
210
210
|
}
|
|
211
211
|
}
|
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
import { computed, ComputedRef, ref, type Ref, type Component } from "vue";
|
|
2
|
+
import * as _ from "lodash-es";
|
|
3
|
+
import { i18n } from "./../plugins/i18n";
|
|
4
|
+
import type { MenuItem, MenuItemConfig } from "../types";
|
|
5
|
+
import { createUnrefFn, useArrayFind } from "@vueuse/core";
|
|
6
|
+
import { usePermissions } from "../composables";
|
|
7
|
+
|
|
8
|
+
// Global state for pre-registering menu items
|
|
9
|
+
const preregisteredMenuItems: Ref<MenuItem[]> = ref([]);
|
|
10
|
+
|
|
11
|
+
// Separate interface for creating a group where id can be optional
|
|
12
|
+
export interface MenuItemConfigCreateOptions extends Omit<MenuItemConfig, "id"> {
|
|
13
|
+
id?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Configuration for creating a group together with a menu item
|
|
18
|
+
*/
|
|
19
|
+
export interface MenuItemWithGroupConfig extends Omit<MenuItemConfig, "group" | "groupId" | "groupIcon"> {
|
|
20
|
+
/**
|
|
21
|
+
* Required group configuration for automatically creating a group.
|
|
22
|
+
* The item will be added to this group.
|
|
23
|
+
*/
|
|
24
|
+
groupConfig: MenuItemConfigCreateOptions;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Global state for menu groups
|
|
28
|
+
const MenuItemConfigs: Ref<Record<string, MenuItemConfig>> = ref({});
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Helper function to create a menu item with group in one step
|
|
32
|
+
* @param item - The menu item properties
|
|
33
|
+
* @param group - The group properties
|
|
34
|
+
* @param inGroupPriority - Optional priority within the group, defaults to item.priority
|
|
35
|
+
* @returns A menu item configuration with embedded group
|
|
36
|
+
*/
|
|
37
|
+
export function createMenuItemWithGroup(
|
|
38
|
+
item: Omit<MenuItemConfig, "group" | "groupId">,
|
|
39
|
+
group: MenuItemConfigCreateOptions,
|
|
40
|
+
inGroupPriority?: number,
|
|
41
|
+
): MenuItemWithGroupConfig {
|
|
42
|
+
return {
|
|
43
|
+
...item,
|
|
44
|
+
// Use explicitly passed inGroupPriority or item.priority as a fallback
|
|
45
|
+
inGroupPriority: inGroupPriority ?? item.priority,
|
|
46
|
+
groupConfig: {
|
|
47
|
+
id: group.id || _.uniqueId("group_"),
|
|
48
|
+
...group,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Registers a menu item before the service is initialized
|
|
55
|
+
*/
|
|
56
|
+
export function addMenuItem(item: MenuItem | MenuItemWithGroupConfig): void {
|
|
57
|
+
// We need to convert the type because the external function can accept MenuItemWithGroupConfig,
|
|
58
|
+
// but preregisteredMenuItems expects MenuItem
|
|
59
|
+
if ("groupConfig" in item) {
|
|
60
|
+
// Conversion will be performed in the addMenuItem function inside createMenuService
|
|
61
|
+
preregisteredMenuItems.value.push(item as unknown as MenuItem);
|
|
62
|
+
} else {
|
|
63
|
+
preregisteredMenuItems.value.push(item);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface MenuService {
|
|
68
|
+
/**
|
|
69
|
+
* Adds a menu item to the menu.
|
|
70
|
+
* If the item contains a `groupConfig` property, the group will be created automatically
|
|
71
|
+
* if it doesn't exist yet.
|
|
72
|
+
* @param item - The menu item to add or a config with both item and group
|
|
73
|
+
*/
|
|
74
|
+
addMenuItem: (item: MenuItem | MenuItemWithGroupConfig) => void;
|
|
75
|
+
menuItems: Ref<MenuItem[]>;
|
|
76
|
+
removeMenuItem: (item: MenuItem) => void;
|
|
77
|
+
updateMenuItem: (id: string, updatedItem: Partial<MenuItem>) => boolean;
|
|
78
|
+
getMenuItem: (id: string) => MenuItem | undefined;
|
|
79
|
+
createMenuItemConfig: (group: MenuItemConfigCreateOptions) => string;
|
|
80
|
+
updateMenuItemConfig: (id: string, group: Partial<MenuItemConfig>) => boolean;
|
|
81
|
+
removeMenuItemConfig: (id: string) => boolean;
|
|
82
|
+
getMenuItemConfigs: () => Record<string, MenuItemConfig>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Default priority values
|
|
86
|
+
const DEFAULT_PRIORITY = Infinity;
|
|
87
|
+
const DEFAULT_GROUP_PRIORITY = Infinity;
|
|
88
|
+
|
|
89
|
+
// State
|
|
90
|
+
const menuItems: Ref<MenuItem[]> = ref([]);
|
|
91
|
+
const rawMenu: Ref<MenuItem[]> = ref([]);
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Menu service implementation
|
|
95
|
+
* Handles the registration and organization of menu items
|
|
96
|
+
*/
|
|
97
|
+
export function createMenuService(): MenuService {
|
|
98
|
+
const { t } = i18n.global;
|
|
99
|
+
const { hasAccess } = usePermissions();
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Add a new menu item to the raw menu and rebuild the menu structure
|
|
103
|
+
* @param item - The menu item to add, or a menu item with group configuration
|
|
104
|
+
*/
|
|
105
|
+
function addMenuItem(item: MenuItem | MenuItemWithGroupConfig): void {
|
|
106
|
+
// Check if item contains group configuration
|
|
107
|
+
if ("groupConfig" in item && item.groupConfig) {
|
|
108
|
+
// Either use existing group or create a new one
|
|
109
|
+
let groupId: string;
|
|
110
|
+
const groupConfig = item.groupConfig;
|
|
111
|
+
|
|
112
|
+
// If the group already exists with the same id, use that
|
|
113
|
+
if (groupConfig.id && MenuItemConfigs.value[groupConfig.id]) {
|
|
114
|
+
groupId = groupConfig.id;
|
|
115
|
+
// Optionally update the group with any new properties
|
|
116
|
+
updateMenuItemConfig(groupId, groupConfig);
|
|
117
|
+
} else {
|
|
118
|
+
// Create a new group
|
|
119
|
+
groupId = createMenuItemConfig(groupConfig);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Create the menu item with the group ID
|
|
123
|
+
const menuItem: MenuItem = {
|
|
124
|
+
..._.omit(item, ["groupConfig"]),
|
|
125
|
+
groupId,
|
|
126
|
+
// Если не указан inGroupPriority, используем priority для совместимости
|
|
127
|
+
inGroupPriority: item.inGroupPriority ?? item.priority,
|
|
128
|
+
} as MenuItem;
|
|
129
|
+
|
|
130
|
+
// Add it to the raw menu
|
|
131
|
+
rawMenu.value.push(menuItem);
|
|
132
|
+
} else {
|
|
133
|
+
// Regular menu item, just add it
|
|
134
|
+
rawMenu.value.push(item as MenuItem);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
constructMenu();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Removes a menu item from the menu
|
|
142
|
+
* @param item - The menu item to remove
|
|
143
|
+
*/
|
|
144
|
+
function removeMenuItem(item: MenuItem): void {
|
|
145
|
+
const index = rawMenu.value.findIndex(
|
|
146
|
+
(menuItem) => menuItem.id === item.id || (menuItem.title === item.title && menuItem.group === item.group),
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
if (index !== -1) {
|
|
150
|
+
rawMenu.value.splice(index, 1);
|
|
151
|
+
constructMenu();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Updates an existing menu item by id
|
|
157
|
+
* @param id - The id of the menu item to update
|
|
158
|
+
* @param updatedItem - The updated properties
|
|
159
|
+
* @returns true if the item was found and updated, false otherwise
|
|
160
|
+
*/
|
|
161
|
+
function updateMenuItem(id: string, updatedItem: Partial<MenuItem>): boolean {
|
|
162
|
+
const index = rawMenu.value.findIndex((item) => item.id === id);
|
|
163
|
+
if (index !== -1) {
|
|
164
|
+
rawMenu.value[index] = { ...rawMenu.value[index], ...updatedItem };
|
|
165
|
+
constructMenu();
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Get a menu item by id
|
|
173
|
+
* @param id - The id of the menu item
|
|
174
|
+
* @returns The menu item or undefined if not found
|
|
175
|
+
*/
|
|
176
|
+
function getMenuItem(id: string): MenuItem | undefined {
|
|
177
|
+
// First try to find in top level menu items
|
|
178
|
+
const topLevelItem = menuItems.value.find((item) => item.id === id);
|
|
179
|
+
if (topLevelItem) return topLevelItem;
|
|
180
|
+
|
|
181
|
+
// Then look in children
|
|
182
|
+
for (const item of menuItems.value) {
|
|
183
|
+
if (item.children) {
|
|
184
|
+
const childItem = item.children.find((child) => child.id === id);
|
|
185
|
+
if (childItem) return childItem;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Creates a new menu group
|
|
194
|
+
* @param group - The group to create
|
|
195
|
+
* @returns The id of the created group
|
|
196
|
+
*/
|
|
197
|
+
function createMenuItemConfig(group: MenuItemConfigCreateOptions): string {
|
|
198
|
+
// Use provided id or generate a new one
|
|
199
|
+
const groupId = group.id || _.uniqueId("group_");
|
|
200
|
+
|
|
201
|
+
MenuItemConfigs.value[groupId] = {
|
|
202
|
+
...group,
|
|
203
|
+
id: groupId,
|
|
204
|
+
} as MenuItemConfig;
|
|
205
|
+
|
|
206
|
+
// Rebuild menu to incorporate the new group
|
|
207
|
+
constructMenu();
|
|
208
|
+
|
|
209
|
+
return groupId;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Updates an existing menu group
|
|
214
|
+
* @param id - The id of the group to update
|
|
215
|
+
* @param group - The updated properties
|
|
216
|
+
* @returns true if the group was found and updated, false otherwise
|
|
217
|
+
*/
|
|
218
|
+
function updateMenuItemConfig(id: string, group: Partial<MenuItemConfig>): boolean {
|
|
219
|
+
if (MenuItemConfigs.value[id]) {
|
|
220
|
+
MenuItemConfigs.value[id] = { ...MenuItemConfigs.value[id], ...group };
|
|
221
|
+
constructMenu();
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Removes a menu group
|
|
229
|
+
* @param id - The id of the group to remove
|
|
230
|
+
* @returns true if the group was found and removed, false otherwise
|
|
231
|
+
*/
|
|
232
|
+
function removeMenuItemConfig(id: string): boolean {
|
|
233
|
+
if (MenuItemConfigs.value[id]) {
|
|
234
|
+
delete MenuItemConfigs.value[id];
|
|
235
|
+
|
|
236
|
+
// Update menu items that belonged to this group
|
|
237
|
+
rawMenu.value = rawMenu.value.filter((item) => item.groupId !== id);
|
|
238
|
+
|
|
239
|
+
constructMenu();
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Gets all menu groups
|
|
247
|
+
* @returns All menu groups
|
|
248
|
+
*/
|
|
249
|
+
function getMenuItemConfigs(): Record<string, MenuItemConfig> {
|
|
250
|
+
return { ...MenuItemConfigs.value };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Updates an element in an array or adds it if not found
|
|
255
|
+
*/
|
|
256
|
+
const upsert = createUnrefFn(
|
|
257
|
+
(array: (MenuItem | Omit<MenuItem, "icon">)[], element: MenuItem | Omit<MenuItem, "icon">) => {
|
|
258
|
+
const index = array.findIndex((_element) => _.isEqual(_.omit(_element, "title"), _.omit(element, "title")));
|
|
259
|
+
if (index > -1) {
|
|
260
|
+
array[index] = { ...element };
|
|
261
|
+
} else {
|
|
262
|
+
array.push({ ...element });
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Sorts menu items by their priority
|
|
269
|
+
*/
|
|
270
|
+
function sortByPriority(a: MenuItem, b: MenuItem): number {
|
|
271
|
+
const getPriority = (item: MenuItem): number => item.priority ?? DEFAULT_PRIORITY;
|
|
272
|
+
return getPriority(a) - getPriority(b);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Sorts items within a group by their inGroupPriority
|
|
277
|
+
*/
|
|
278
|
+
function sortByGroupPriority(a: MenuItem, b: MenuItem): number {
|
|
279
|
+
const getGroupPriority = (item: MenuItem): number =>
|
|
280
|
+
item.inGroupPriority ?? item.groupConfig?.priority ?? DEFAULT_GROUP_PRIORITY;
|
|
281
|
+
return getGroupPriority(a) - getGroupPriority(b);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Creates a localized ID from a title
|
|
286
|
+
*/
|
|
287
|
+
function createItemId(title: string | ComputedRef<string>): string {
|
|
288
|
+
const titleValue = typeof title === "string" ? t(title) : title.value;
|
|
289
|
+
return _.snakeCase(titleValue);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Creates a localized computed title
|
|
294
|
+
*/
|
|
295
|
+
function createLocalizedTitle(title: string): ComputedRef<string> {
|
|
296
|
+
return computed(() => t(title));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Checks if the current user has permission to see the menu item
|
|
301
|
+
* @param item - The menu item to check
|
|
302
|
+
* @returns true if the user has permission, false otherwise
|
|
303
|
+
*/
|
|
304
|
+
function hasPermissionForItem(item: MenuItem): boolean {
|
|
305
|
+
// If no permissions are specified, the item is visible to everyone
|
|
306
|
+
if (!item.permissions || (Array.isArray(item.permissions) && item.permissions.length === 0)) {
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Check if user has any of the required permissions
|
|
311
|
+
return hasAccess(item.permissions);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Processes a menu item that belongs to a group
|
|
316
|
+
*/
|
|
317
|
+
function processGroupItem(item: MenuItem, constructedMenu: Ref<MenuItem[]>): void {
|
|
318
|
+
// Skip items the user doesn't have permission to see
|
|
319
|
+
if (!hasPermissionForItem(item)) return;
|
|
320
|
+
|
|
321
|
+
// Handle both legacy (group by name) and new (group by id) approaches
|
|
322
|
+
let groupId: string;
|
|
323
|
+
let groupTitle: string;
|
|
324
|
+
let groupIcon: string | Component = "";
|
|
325
|
+
let groupPriority: number = DEFAULT_PRIORITY;
|
|
326
|
+
let groupPermissions: string | string[] | undefined;
|
|
327
|
+
|
|
328
|
+
// If using groupId (new way)
|
|
329
|
+
if (item.groupId && MenuItemConfigs.value[item.groupId]) {
|
|
330
|
+
const group = MenuItemConfigs.value[item.groupId];
|
|
331
|
+
groupId = "group_" + group.id;
|
|
332
|
+
groupTitle = group.title;
|
|
333
|
+
groupIcon = group.icon || "";
|
|
334
|
+
groupPriority = group.priority || DEFAULT_PRIORITY;
|
|
335
|
+
groupPermissions = group.permissions;
|
|
336
|
+
}
|
|
337
|
+
// If using group string (legacy way)
|
|
338
|
+
else if (item.group) {
|
|
339
|
+
console.warn("Using item.group is deprecated, please use item.groupId instead");
|
|
340
|
+
groupId = "group_" + createItemId(item.group);
|
|
341
|
+
groupTitle = item.group;
|
|
342
|
+
groupIcon = item.groupIcon || "";
|
|
343
|
+
groupPriority = item.priority || DEFAULT_PRIORITY;
|
|
344
|
+
groupPermissions = item.permissions;
|
|
345
|
+
}
|
|
346
|
+
// Skip if no valid group reference found
|
|
347
|
+
else {
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const existingGroup = useArrayFind(constructedMenu, (m) => m.groupId === groupId);
|
|
352
|
+
|
|
353
|
+
// Create the item to be added to the group
|
|
354
|
+
const groupItem = {
|
|
355
|
+
..._.omit(item, ["group", "groupIcon", "priority", "groupId"]),
|
|
356
|
+
title: createLocalizedTitle(item.title as string),
|
|
357
|
+
// Ensure inGroupPriority is preserved and used
|
|
358
|
+
inGroupPriority: item.inGroupPriority || item.priority || DEFAULT_GROUP_PRIORITY,
|
|
359
|
+
} as MenuItem;
|
|
360
|
+
|
|
361
|
+
if (existingGroup.value && existingGroup.value.children) {
|
|
362
|
+
// Add to existing group
|
|
363
|
+
upsert(existingGroup.value.children, groupItem);
|
|
364
|
+
} else {
|
|
365
|
+
// Skip creating group if user doesn't have permission for the group
|
|
366
|
+
if (groupPermissions && !hasAccess(groupPermissions)) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Create a new group with this item
|
|
371
|
+
const group = {
|
|
372
|
+
groupId,
|
|
373
|
+
groupIcon,
|
|
374
|
+
title: createLocalizedTitle(groupTitle),
|
|
375
|
+
children: [groupItem],
|
|
376
|
+
priority: groupPriority,
|
|
377
|
+
permissions: groupPermissions,
|
|
378
|
+
} as MenuItem;
|
|
379
|
+
upsert(constructedMenu.value, group);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Processes a standalone menu item (not in a group)
|
|
385
|
+
*/
|
|
386
|
+
function processStandaloneItem(item: MenuItem, constructedMenu: Ref<MenuItem[]>): void {
|
|
387
|
+
// Skip items the user doesn't have permission to see
|
|
388
|
+
if (!hasPermissionForItem(item)) return;
|
|
389
|
+
|
|
390
|
+
if (item.title) {
|
|
391
|
+
const standaloneItem = {
|
|
392
|
+
...item,
|
|
393
|
+
title: createLocalizedTitle(item.title as string),
|
|
394
|
+
} as MenuItem;
|
|
395
|
+
upsert(constructedMenu.value, standaloneItem);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Finalizes menu items by adding IDs and sorting children
|
|
401
|
+
* Also filters out groups with no visible children
|
|
402
|
+
*/
|
|
403
|
+
function finalizeMenuItems(items: MenuItem[]): MenuItem[] {
|
|
404
|
+
return (
|
|
405
|
+
items
|
|
406
|
+
.map(
|
|
407
|
+
(item): MenuItem => ({
|
|
408
|
+
...item,
|
|
409
|
+
title: createLocalizedTitle(item.title as string),
|
|
410
|
+
id: item.id || createItemId(item.title as ComputedRef<string>),
|
|
411
|
+
children: item.children?.filter((child) => hasPermissionForItem(child)).sort(sortByGroupPriority),
|
|
412
|
+
}),
|
|
413
|
+
)
|
|
414
|
+
// Filter out groups with no visible children
|
|
415
|
+
.filter((item) => !item.children || item.children.length > 0)
|
|
416
|
+
.sort(sortByPriority)
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Constructs the complete menu structure from raw menu items
|
|
422
|
+
*/
|
|
423
|
+
function constructMenu(): void {
|
|
424
|
+
const constructedMenu: Ref<MenuItem[]> = ref([]);
|
|
425
|
+
|
|
426
|
+
// Process each raw menu item
|
|
427
|
+
rawMenu.value.forEach((item) => {
|
|
428
|
+
if (item.group || item.groupId) {
|
|
429
|
+
processGroupItem(item, constructedMenu);
|
|
430
|
+
} else {
|
|
431
|
+
processStandaloneItem(item, constructedMenu);
|
|
432
|
+
}
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
// Finalize and set the menu items
|
|
436
|
+
menuItems.value = finalizeMenuItems(constructedMenu.value);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Process any pre-registered menu items, including those with group configurations
|
|
440
|
+
preregisteredMenuItems.value.forEach((item) => {
|
|
441
|
+
try {
|
|
442
|
+
addMenuItem(item);
|
|
443
|
+
} catch (e) {
|
|
444
|
+
console.warn(`Failed to register preregistered menu item ${item.id || item.title}:`, e);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
return {
|
|
449
|
+
addMenuItem,
|
|
450
|
+
menuItems,
|
|
451
|
+
removeMenuItem,
|
|
452
|
+
updateMenuItem,
|
|
453
|
+
getMenuItem,
|
|
454
|
+
createMenuItemConfig,
|
|
455
|
+
updateMenuItemConfig,
|
|
456
|
+
removeMenuItemConfig,
|
|
457
|
+
getMenuItemConfigs,
|
|
458
|
+
};
|
|
459
|
+
}
|
|
@@ -33,6 +33,18 @@ export interface IWidgetService {
|
|
|
33
33
|
isWidgetRegistered: (id: string) => boolean;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
+
// Global state for pre-registering widgets
|
|
37
|
+
const preregisteredWidgets: IWidgetRegistration[] = [];
|
|
38
|
+
const preregisteredIds = new Set<string>();
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Registers a widget before the service is initialized
|
|
42
|
+
*/
|
|
43
|
+
export function registerWidget(widget: IWidget, bladeId: string): void {
|
|
44
|
+
preregisteredWidgets.push({ bladeId, widget });
|
|
45
|
+
preregisteredIds.add(widget.id);
|
|
46
|
+
}
|
|
47
|
+
|
|
36
48
|
export function createWidgetService(): IWidgetService {
|
|
37
49
|
const widgetRegistry = reactive<Record<string, IWidget[]>>({});
|
|
38
50
|
const registeredWidgets = reactive<IWidgetRegistration[]>([]);
|
|
@@ -113,6 +125,14 @@ export function createWidgetService(): IWidgetService {
|
|
|
113
125
|
return activeWidgetRef.value?.id === id;
|
|
114
126
|
};
|
|
115
127
|
|
|
128
|
+
preregisteredWidgets.forEach((widget) => {
|
|
129
|
+
try {
|
|
130
|
+
registerWidget(widget.widget, widget.bladeId);
|
|
131
|
+
} catch (e) {
|
|
132
|
+
console.warn(`Failed to register preregistered widget ${widget.widget.id}:`, e);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
116
136
|
return {
|
|
117
137
|
registerWidget,
|
|
118
138
|
unregisterWidget,
|