@cccsaurora/howler-ui 2.14.0-dev.232 → 2.14.0-dev.247
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/commons/components/app/AppConfigs.d.ts +9 -3
- package/components/app/App.js +1 -0
- package/components/app/providers/FavouritesProvider.js +2 -2
- package/components/hooks/useMyPreferences.js +208 -193
- package/components/hooks/useMySitemap.js +3 -1
- package/components/routes/hits/search/InformationPane.js +6 -3
- package/components/routes/hits/view/HitViewer.js +2 -2
- package/package.json +1 -1
- package/plugins/HowlerPlugin.d.ts +53 -1
- package/plugins/HowlerPlugin.js +146 -14
- package/plugins/store.d.ts +51 -14
- package/plugins/store.js +36 -21
- package/utils/menuUtils.d.ts +89 -0
- package/utils/menuUtils.js +243 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { MainMenuInsertOperation } from '../plugins/store';
|
|
2
|
+
import { isNil } from 'lodash-es';
|
|
3
|
+
class AppMenuBuilder {
|
|
4
|
+
items;
|
|
5
|
+
indexMap;
|
|
6
|
+
constructor(defaultMenu) {
|
|
7
|
+
this.items = defaultMenu;
|
|
8
|
+
this.updateMenuMap();
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Applies a collection of Menu Operation objects created by the plugin system
|
|
12
|
+
*
|
|
13
|
+
* @param operations Operations created by the plugin system
|
|
14
|
+
*/
|
|
15
|
+
applyOperations(operations) {
|
|
16
|
+
for (const operation of operations) {
|
|
17
|
+
switch (operation.operation) {
|
|
18
|
+
case MainMenuInsertOperation.Insert:
|
|
19
|
+
// Inserts at end or adds to sub-elements
|
|
20
|
+
this.insert(operation.targetId, operation.item);
|
|
21
|
+
break;
|
|
22
|
+
case MainMenuInsertOperation.InsertBefore:
|
|
23
|
+
// Inserts before target element
|
|
24
|
+
this.insertBefore(operation.targetId, operation.item);
|
|
25
|
+
break;
|
|
26
|
+
case MainMenuInsertOperation.InsertAfter:
|
|
27
|
+
// Inserts after target element
|
|
28
|
+
this.insertAfter(operation.targetId, operation.item);
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
this.updateMenuMap();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get the completed menu structure
|
|
36
|
+
*/
|
|
37
|
+
get menu() {
|
|
38
|
+
return this.items;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Insert provided menu item at the menu object identified by targetId.
|
|
42
|
+
*
|
|
43
|
+
* If the target menu is a group then item will be placed at end of sub items
|
|
44
|
+
*
|
|
45
|
+
* If the target menu is a standard item then group will be created and new item added, warning this removes
|
|
46
|
+
* the route from the new group parent item, be sure to add it back.
|
|
47
|
+
*
|
|
48
|
+
* If the target is 'root' then item will be added to end of root menu
|
|
49
|
+
*
|
|
50
|
+
* @param targetId Identifier of menu to insert to
|
|
51
|
+
* @param item Menu Item to insert
|
|
52
|
+
*/
|
|
53
|
+
insert(targetId, item) {
|
|
54
|
+
const menuLocation = this.indexOfMenuId(targetId);
|
|
55
|
+
const target = this.menuFromIndex(menuLocation.index, menuLocation.subIndex);
|
|
56
|
+
if (!Array.isArray(target) && this.isGroupElement(target)) {
|
|
57
|
+
if (item.type === 'divider') {
|
|
58
|
+
console.warn(`Skipping DIVIDER Operation: INSERT on Target: ${targetId}, Dividers cannot be inserted to sub-menus`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const group = target.element;
|
|
62
|
+
const newItem = { ...item.element, nested: true };
|
|
63
|
+
group.items = [...group.items, newItem];
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (Array.isArray(target)) {
|
|
67
|
+
target.push(item);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (menuLocation.index !== -1 && isNil(menuLocation.subIndex)) {
|
|
71
|
+
if (item.type === 'divider') {
|
|
72
|
+
console.warn(`Skipping DIVIDER Operation: INSERT on Target: ${targetId}, Dividers cannot be inserted to sub-menus`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (!this.isItemElement(target)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const header = target.element;
|
|
79
|
+
const inserted = { ...item.element, nested: true };
|
|
80
|
+
const newGroup = {
|
|
81
|
+
type: 'group',
|
|
82
|
+
element: {
|
|
83
|
+
id: header.id,
|
|
84
|
+
open: true,
|
|
85
|
+
i18nKey: header.i18nKey,
|
|
86
|
+
title: header.text,
|
|
87
|
+
userPropValidators: header.userPropValidators,
|
|
88
|
+
icon: header.icon,
|
|
89
|
+
items: [inserted]
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
this.items[menuLocation.index] = newGroup;
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Insert provided menu item before the menu object identified by targetId.
|
|
98
|
+
*
|
|
99
|
+
* @param targetId Identifier of menu to insert to
|
|
100
|
+
* @param item Menu Item to insert
|
|
101
|
+
*/
|
|
102
|
+
insertBefore(targetId, item) {
|
|
103
|
+
const menuLocation = this.indexOfMenuId(targetId);
|
|
104
|
+
if (!isNil(menuLocation.subIndex)) {
|
|
105
|
+
if (item.type === 'divider') {
|
|
106
|
+
console.warn(`Skipping DIVIDER Operation: INSERT on Target: ${targetId}, Dividers cannot be inserted to sub-menus`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const parentElement = this.menuFromIndex(menuLocation.index);
|
|
110
|
+
if (!Array.isArray(parentElement) && this.isGroupElement(parentElement)) {
|
|
111
|
+
const group = parentElement.element;
|
|
112
|
+
const newItem = { ...item.element, nested: true };
|
|
113
|
+
group.items = [
|
|
114
|
+
...group.items.slice(0, menuLocation.subIndex),
|
|
115
|
+
newItem,
|
|
116
|
+
...group.items.slice(menuLocation.subIndex)
|
|
117
|
+
];
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// Root level insertion before target index
|
|
122
|
+
if (menuLocation.index < 0) {
|
|
123
|
+
menuLocation.index = 0;
|
|
124
|
+
}
|
|
125
|
+
this.items = [...this.items.slice(0, menuLocation.index), item, ...this.items.slice(menuLocation.index)];
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Insert provided menu item after the menu object identified by targetId.
|
|
129
|
+
*
|
|
130
|
+
* @param targetId Identifier of menu to insert to
|
|
131
|
+
* @param item Menu Item to insert
|
|
132
|
+
*/
|
|
133
|
+
insertAfter(targetId, item) {
|
|
134
|
+
const menuLocation = this.indexOfMenuId(targetId);
|
|
135
|
+
if (!isNil(menuLocation.subIndex)) {
|
|
136
|
+
if (item.type === 'divider') {
|
|
137
|
+
console.warn(`Skipping DIVIDER Operation: INSERT on Target: ${targetId}, Dividers cannot be inserted to sub-menus`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const parentElement = this.menuFromIndex(menuLocation.index);
|
|
141
|
+
if (!Array.isArray(parentElement) && this.isGroupElement(parentElement)) {
|
|
142
|
+
const group = parentElement.element;
|
|
143
|
+
const newItem = { ...item.element, nested: true };
|
|
144
|
+
group.items = [
|
|
145
|
+
...group.items.slice(0, menuLocation.subIndex + 1),
|
|
146
|
+
newItem,
|
|
147
|
+
...group.items.slice(menuLocation.subIndex + 1)
|
|
148
|
+
];
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
// Root level insertion after target index
|
|
153
|
+
if (menuLocation.index < 0) {
|
|
154
|
+
menuLocation.index = this.items.length;
|
|
155
|
+
}
|
|
156
|
+
this.items = [...this.items.slice(0, menuLocation.index + 1), item, ...this.items.slice(menuLocation.index + 1)];
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Locates menu location in menu structure by menu id.
|
|
160
|
+
*
|
|
161
|
+
* @param id Menu Id to search for
|
|
162
|
+
* @return {index: number, subIndex: number} A dictionary containing indexes needed to access Menu Item
|
|
163
|
+
*/
|
|
164
|
+
indexOfMenuId(id) {
|
|
165
|
+
if (id === 'root') {
|
|
166
|
+
// Root item so return entire menu
|
|
167
|
+
return { index: -1 };
|
|
168
|
+
}
|
|
169
|
+
else if (id in this.indexMap) {
|
|
170
|
+
// Item exists, check if it's a subitem
|
|
171
|
+
if ('parent' in this.indexMap[id]) {
|
|
172
|
+
return { index: this.indexMap[id].parent, subIndex: this.indexMap[id].index };
|
|
173
|
+
}
|
|
174
|
+
return { index: this.indexMap[id].index };
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
throw new Error(`Menu element with id of '${id}' not found.`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Grabs a menu by indexes, helper function to account for an index of -1
|
|
182
|
+
* to represent the root menu
|
|
183
|
+
*
|
|
184
|
+
* @param index First level index
|
|
185
|
+
* @param subIndex Second level index
|
|
186
|
+
* @return {} Menu item
|
|
187
|
+
*/
|
|
188
|
+
menuFromIndex(index, subIndex) {
|
|
189
|
+
if (index === -1) {
|
|
190
|
+
return this.items;
|
|
191
|
+
}
|
|
192
|
+
if (isNil(subIndex)) {
|
|
193
|
+
return this.items[index];
|
|
194
|
+
}
|
|
195
|
+
const menuItem = this.items[index];
|
|
196
|
+
if (menuItem.type === 'group') {
|
|
197
|
+
return menuItem.element.items[subIndex];
|
|
198
|
+
}
|
|
199
|
+
throw new Error(`Menu item at index ${index} is not a group and does not have sub-items.`);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Determine if provided element is a group element
|
|
203
|
+
*
|
|
204
|
+
* @param elem Element to check
|
|
205
|
+
* @private
|
|
206
|
+
*/
|
|
207
|
+
isGroupElement(elem) {
|
|
208
|
+
return !!elem && typeof elem === 'object' && elem.type === 'group';
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Determine if provided element is an item element
|
|
212
|
+
*
|
|
213
|
+
* @param elem Element to check
|
|
214
|
+
* @private
|
|
215
|
+
*/
|
|
216
|
+
isItemElement(elem) {
|
|
217
|
+
return !!elem && typeof elem === 'object' && elem.type === 'item';
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Creates a flat list of menu items and subitems by menu id
|
|
221
|
+
* for quick lookup.
|
|
222
|
+
*
|
|
223
|
+
* @private
|
|
224
|
+
*/
|
|
225
|
+
updateMenuMap() {
|
|
226
|
+
const indexMap = {};
|
|
227
|
+
for (let index = 0; index < this.items.length; index++) {
|
|
228
|
+
const menuItem = this.items[index];
|
|
229
|
+
if (menuItem.type === 'divider') {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
indexMap[menuItem.element.id] = { index };
|
|
233
|
+
if (menuItem.type === 'group') {
|
|
234
|
+
for (let subIndex = 0; subIndex < menuItem.element.items.length; subIndex++) {
|
|
235
|
+
const subMenuItem = menuItem.element.items[subIndex];
|
|
236
|
+
indexMap[subMenuItem.id] = { index: subIndex, parent: index };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
this.indexMap = indexMap;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
export default AppMenuBuilder;
|