@vcmap/ui 5.0.0-rc.16 → 5.0.0-rc.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/build/buildHelpers.js +7 -1
  2. package/config/base.config.json +3 -45
  3. package/config/www.config.json +9 -10
  4. package/dist/assets/{cesium.430460.js → cesium.41de56.js} +0 -0
  5. package/dist/assets/cesium.js +1 -1
  6. package/dist/assets/{core.5089ba.js → core.af84e3.js} +1700 -1718
  7. package/dist/assets/core.js +1 -1
  8. package/dist/assets/{index.854f8e2b.js → index.5b773cad.js} +1 -1
  9. package/dist/assets/{ol.9be53a.js → ol.5c7490.js} +0 -0
  10. package/dist/assets/ol.js +1 -1
  11. package/dist/assets/{ui.49010a.css → ui.dffe32.css} +1 -1
  12. package/dist/assets/{ui.49010a.js → ui.dffe32.js} +6345 -6011
  13. package/dist/assets/ui.js +1 -1
  14. package/dist/assets/{vue.247c1c.js → vue.25da17.js} +0 -0
  15. package/dist/assets/vue.js +2 -2
  16. package/dist/assets/{vuetify.735e58.css → vuetify.e4ece7.css} +0 -0
  17. package/dist/assets/{vuetify.735e58.js → vuetify.e4ece7.js} +5 -2
  18. package/dist/assets/vuetify.js +2 -2
  19. package/dist/index.html +1 -1
  20. package/index.js +14 -3
  21. package/package.json +2 -2
  22. package/plugins/@vcmap/pluginExample/index.js +2 -1
  23. package/plugins/@vcmap/pluginExample/pluginExampleComponent.vue +7 -0
  24. package/plugins/categoryTest/Categories.vue +27 -13
  25. package/plugins/categoryTest/Category.vue +7 -1
  26. package/plugins/categoryTest/index.js +1 -1
  27. package/plugins/test/allIconsComponent.vue +3 -3
  28. package/plugins/test/index.js +6 -4
  29. package/plugins/test/testList.vue +4 -1
  30. package/plugins/test/vcsContent.vue +1 -1
  31. package/plugins/test/windowManagerExample.vue +9 -7
  32. package/src/actions/actionHelper.js +10 -9
  33. package/src/application/VcsApp.vue +25 -26
  34. package/src/components/form-inputs-controls/VcsCheckbox.vue +1 -0
  35. package/src/components/form-inputs-controls/VcsFormSection.vue +14 -6
  36. package/src/components/lists/VcsList.vue +4 -2
  37. package/src/contentTree/contentTreeCollection.js +9 -0
  38. package/src/contentTree/layerContentTreeItem.js +1 -1
  39. package/src/featureInfo/BalloonComponent.vue +3 -0
  40. package/src/featureInfo/balloonFeatureInfoView.js +2 -8
  41. package/src/featureInfo/balloonHelper.js +22 -5
  42. package/src/featureInfo/featureInfo.js +1 -2
  43. package/src/i18n/de.js +6 -1
  44. package/src/i18n/en.js +6 -1
  45. package/src/manager/categoryManager/CategoryComponent.vue +115 -0
  46. package/src/manager/categoryManager/CategoryComponentList.vue +57 -0
  47. package/src/manager/categoryManager/CategoryManager.vue +35 -0
  48. package/src/manager/categoryManager/categoryManager.js +251 -165
  49. package/src/manager/contextMenu/contextMenuManager.js +8 -2
  50. package/src/manager/window/WindowComponent.vue +49 -75
  51. package/src/manager/window/WindowComponentHeader.vue +49 -7
  52. package/src/manager/window/WindowManager.vue +53 -30
  53. package/src/manager/window/windowHelper.js +341 -0
  54. package/src/manager/window/windowManager.js +162 -150
  55. package/src/notifier/notifier.js +4 -5
  56. package/src/vcsUiApp.js +7 -1
  57. package/src/manager/categoryManager/ComponentsManager.vue +0 -30
@@ -1,5 +1,5 @@
1
- import { ref } from 'vue';
2
- import { contextIdSymbol, IndexedCollection } from '@vcmap/core';
1
+ import { reactive } from 'vue';
2
+ import { contextIdSymbol, IndexedCollection, VcsEvent } from '@vcmap/core';
3
3
  import { check } from '@vcsuite/check';
4
4
  import { sortByOwner } from '../navbarManager.js';
5
5
  import { validateAction, validateActions } from '../../components/lists/VcsActionList.vue';
@@ -8,7 +8,7 @@ import { validateAction, validateActions } from '../../components/lists/VcsActio
8
8
  * @callback MappingFunction
9
9
  * @param {T} item
10
10
  * @param {import("@vcmap/core").Category<T>} category
11
- * @param {import("@vcmap/ui").TreeViewItem} treeViewItem
11
+ * @param {import("@vcmap/ui").VcsListItem & { destroy: (function():void)|undefined }} listItem - you can add a destroy callback for cleanup
12
12
  * @template {Object} T
13
13
  */
14
14
 
@@ -30,63 +30,99 @@ import { validateAction, validateActions } from '../../components/lists/VcsActio
30
30
  */
31
31
 
32
32
  /**
33
- * uses the itemMappings to transform the given Item to an TreeViewItem usable in the VCSTreeView
33
+ * @typedef {Object} ManagedCategory
34
+ * @property {string} categoryName
35
+ * @property {string} title
36
+ * @property {Array<VcsAction>} actions
37
+ * @property {Array<VcsListItem & { destroy: (function():void|undefined) }>} items
38
+ * @property {boolean} selectable
39
+ * @property {boolean} singleSelect
40
+ * @property {Array<VcsListItem>} selection
41
+ * @property {function():void} destroy - called when the item is destroyed. do not call yourself, remove the category from the manager
42
+ */
43
+
44
+ /**
45
+ * @typedef {Object} ManagedCategoryOptions
46
+ * @property {string} categoryName
47
+ * @property {boolean} [selectable]
48
+ * @property {boolean} [singleSelect]
49
+ * @property {Array<VcsAction>} [actions]
50
+ */
51
+
52
+ /**
53
+ * uses the itemMappings to transform the given Item to an VcsListItem usable in the VcsList
34
54
  * @param {T} item
35
55
  * @param {import("@vcmap/core").Category<T>} category
36
56
  * @param {Array<ItemMapping<T>>} itemMappings
37
- * @returns {import("@vcmap/ui").TreeViewItem}
57
+ * @returns {import("@vcmap/ui").VcsListItem & { destroy: (function():void)|undefined }}
38
58
  * @template T
39
59
  * @private
40
60
  */
41
61
  function transformItem(item, category, itemMappings) {
42
62
  const keyProperty = category.collection.uniqueKey;
43
- const treeViewItem = {
63
+ const listItem = {
44
64
  get id() { return item[keyProperty]; },
45
65
  title: item?.properties?.title || item[keyProperty],
46
66
  actions: [],
47
- children: [],
48
67
  };
49
68
  itemMappings.forEach((itemMapping) => {
50
69
  if (itemMapping.predicate(item, category)) {
51
- itemMapping.mappingFunction(item, category, treeViewItem);
70
+ itemMapping.mappingFunction(item, category, listItem);
52
71
  }
53
72
  });
54
- treeViewItem.actions = treeViewItem.actions.filter((action) => {
73
+ listItem.actions = listItem.actions.filter((action) => {
55
74
  return validateAction(action);
56
75
  });
57
- return treeViewItem;
76
+ return listItem;
58
77
  }
59
78
 
60
79
  /**
61
- * Inserts the item into the children array at the correct relative position in respect to the position of the item
80
+ * Inserts the item into the items array at the correct relative position in respect to the position of the item
62
81
  * in the collection
63
- * @param {import("@vcmap/ui").TreeViewItem} item
82
+ * @param {import("@vcmap/ui").VcsListItem} item
64
83
  * @param {import("@vcmap/core").Collection} collection
65
- * @param {Array<import("@vcmap/ui").TreeViewItem>} children
84
+ * @param {Array<import("@vcmap/ui").VcsListItem>} items
66
85
  * @private
67
86
  */
68
- function insertItem(item, collection, children) {
87
+ function insertItem(item, collection, items) {
69
88
  if (collection instanceof IndexedCollection) {
70
89
  const newItemIndex = collection.indexOfKey(item.id);
71
90
  if (newItemIndex === collection.size - 1) {
72
- children.push(item);
91
+ items.push(item);
73
92
  } else {
74
- const positionInChildren = children.findIndex((treeViewItem) => {
75
- const treeViewItemIndex = collection.indexOfKey(treeViewItem.id);
93
+ const positionInChildren = items.findIndex((listItem) => {
94
+ const treeViewItemIndex = collection.indexOfKey(listItem.id);
76
95
  return newItemIndex < treeViewItemIndex;
77
96
  });
78
97
  if (positionInChildren >= 0) {
79
- children.splice(positionInChildren, 0, item);
98
+ items.splice(positionInChildren, 0, item);
99
+ } else {
100
+ items.push(item);
80
101
  }
81
102
  }
82
103
  } else {
83
- children.push(item);
104
+ items.push(item);
105
+ }
106
+ }
107
+
108
+ /**
109
+ * @param {ManagedCategoryOptions} current
110
+ * @param {ManagedCategoryOptions} next
111
+ * @returns {ManagedCategoryOptions}
112
+ */
113
+ function reduceCategoryOptions(current, next) {
114
+ if (next.actions?.length > 0) {
115
+ current.actions.push(...next.actions);
84
116
  }
117
+ current.selectable = current.selectable ?? next.selectable;
118
+ current.singleSelect = current.singleSelect ?? next.singleSelect;
119
+ return current;
85
120
  }
86
121
 
87
122
  /**
88
123
  * a categoryManager manages categories, and synchronizes a tree of VcsTreeView Items.
89
124
  * provides an API to add/remove Categories.
125
+ * @implements {VcsComponentManager<ManagedCategory, ManagedCategoryOptions>}
90
126
  */
91
127
  class CategoryManager {
92
128
  /**
@@ -100,16 +136,10 @@ class CategoryManager {
100
136
  this._app = app;
101
137
 
102
138
  /**
103
- * @type {Map<string, Map<string|symbol, Array<import("@vcmap/ui").VcsAction>>>}
139
+ * @type {Map<string, Map<string|symbol, ManagedCategoryOptions>>}
104
140
  * @private
105
141
  */
106
- this._managedCategories = new Map();
107
-
108
- /**
109
- * @type {Map<string, Array<function():void>>}
110
- * @private
111
- */
112
- this._managedCategoriesListeners = new Map();
142
+ this._managedCategoryOptions = new Map();
113
143
 
114
144
  /**
115
145
  * @type {function():void}
@@ -117,7 +147,7 @@ class CategoryManager {
117
147
  */
118
148
  this._dynamicContextIdListener = this._app.dynamicContextIdChanged.addEventListener((id) => {
119
149
  this._dynamicContextId = id;
120
- this._resetItems();
150
+ this._resetManagedCategories();
121
151
  });
122
152
 
123
153
  /**
@@ -139,12 +169,23 @@ class CategoryManager {
139
169
  * @private
140
170
  */
141
171
  this._itemMappings = [];
142
-
143
172
  /**
144
- * @type {import("vue").Ref<Array<import("@vcmap/ui").TreeViewItem>>}
173
+ * @type {Map<string, ManagedCategory>}
145
174
  * @private
146
175
  */
147
- this._items = ref([]);
176
+ this._managedCategories = new Map();
177
+ /**
178
+ * @type {Array<string>}
179
+ */
180
+ this.componentIds = [];
181
+ /**
182
+ * @type {VcsEvent<ManagedCategory>}
183
+ */
184
+ this.added = new VcsEvent();
185
+ /**
186
+ * @type {VcsEvent<ManagedCategory>}
187
+ */
188
+ this.removed = new VcsEvent();
148
189
  }
149
190
 
150
191
  /**
@@ -158,27 +199,10 @@ class CategoryManager {
158
199
  const itemMappings = this._itemMappings.filter((itemMapping) => {
159
200
  return itemMapping.categoryNames.includes(category.name);
160
201
  });
161
- const finishedUiItem = transformItem(item, category, itemMappings);
162
- const categoryItem = this.items.value.find((elem) => { return elem.id === category.name; });
163
- if (categoryItem) {
164
- insertItem(finishedUiItem, category.collection, categoryItem.children);
165
- /* if (category.collection instanceof IndexedCollection) {
166
- const newItemIndex = category.collection.indexOf(item);
167
- let indexToInsert = 0;
168
- // eslint-disable-next-line for-direction
169
- for (let index = categoryItem.children.length - 1; index >= 0; index--) {
170
- const treeViewItem = categoryItem.children[index];
171
- const treeViewItemIndex = category.collection.indexOfKey(treeViewItem.id);
172
- if (treeViewItemIndex < newItemIndex) {
173
- // should be added directly after this item
174
- indexToInsert = index + 1;
175
- break;
176
- }
177
- }
178
- categoryItem.children.splice(indexToInsert, 0, finishedUiItem);
179
- } else {
180
- categoryItem.children.push(finishedUiItem);
181
- } */
202
+ const listItem = transformItem(item, category, itemMappings);
203
+ const managedCategory = this.get(category.name);
204
+ if (managedCategory) {
205
+ insertItem(listItem, category.collection, managedCategory.items);
182
206
  }
183
207
  }
184
208
 
@@ -191,13 +215,14 @@ class CategoryManager {
191
215
  * @private
192
216
  */
193
217
  _handleItemMoved(item, category) {
194
- const categoryItem = this.items.value.find((elem) => { return elem.id === category.name; });
195
- if (categoryItem) {
196
- const index = categoryItem.children.findIndex((elem) => { return elem.id === item.name; });
218
+ const managedCategory = this.get(category.name);
219
+ if (managedCategory) {
220
+ const index = managedCategory.items
221
+ .findIndex((elem) => { return elem.id === item[category.collection.uniqueKey]; });
197
222
  if (index > -1) {
198
- const treeViewItem = categoryItem.children[index];
199
- categoryItem.children.splice(index, 1);
200
- insertItem(treeViewItem, category.collection, categoryItem.children);
223
+ const listItem = managedCategory.items[index];
224
+ managedCategory.items.splice(index, 1);
225
+ insertItem(listItem, category.collection, managedCategory.items);
201
226
  }
202
227
  }
203
228
  }
@@ -210,73 +235,75 @@ class CategoryManager {
210
235
  * @private
211
236
  */
212
237
  _handleItemRemoved(item, category) {
213
- const categoryItem = this.items.value.find((elem) => { return elem.id === category.name; });
214
- if (categoryItem) {
215
- const index = categoryItem.children.findIndex((elem) => { return elem.id === item.name; });
238
+ const managedCategory = this.get(category.name);
239
+ if (managedCategory) {
240
+ const index = managedCategory.items
241
+ .findIndex((elem) => { return elem.id === item[category.collection.uniqueKey]; });
216
242
  if (index > -1) {
217
- categoryItem.children.splice(index, 1);
243
+ const listItem = managedCategory.items[index];
244
+ if (listItem.destroy) {
245
+ listItem.destroy();
246
+ }
247
+ managedCategory.items.splice(index, 1);
218
248
  }
219
249
  }
220
250
  }
221
251
 
222
252
  /**
223
- * removes all Items and rebuilds the item tree depending on the ContextId
253
+ * removes all items from all categories and rebuilds the item tree depending on the ContextId
224
254
  * @private
225
255
  */
226
- _resetItems() {
227
- this.items.value.splice(0);
228
- this._managedCategories.forEach((value, categoryName) => {
229
- this._resetCategory(categoryName);
256
+ _resetManagedCategories() {
257
+ this._managedCategoryOptions.forEach((value, categoryName) => {
258
+ this._resetCategoryItems(categoryName);
230
259
  });
231
260
  }
232
261
 
233
262
  /**
234
- * resets the category, removes the currently managed state and rebuilds the categoryItem and all children
235
263
  * @param {string} categoryName
236
264
  * @private
237
265
  */
238
- _resetCategory(categoryName) {
266
+ _resetCategoryItems(categoryName) {
239
267
  const category = this._app.categories.getByKey(categoryName);
240
- if (!category) {
241
- throw new Error(`Could not find Category: ${categoryName}`);
242
- }
243
- // cleanup existing listeners
244
- if (this._managedCategoriesListeners.has(categoryName)) {
245
- this._managedCategoriesListeners.get(categoryName).forEach((listener) => {
246
- listener();
247
- });
248
- this._managedCategoriesListeners.delete(categoryName);
249
- }
268
+ const managedCategory = this.get(categoryName);
250
269
 
251
- const categoryItemIndex = this._items.value.findIndex((item) => {
252
- return item.id === category.name;
253
- });
254
- const actions = [...this._managedCategories.get(category.name).values()].flatMap(ownerActions => ownerActions);
255
-
256
- const itemMappings = this._itemMappings.filter((itemMapping) => {
257
- return itemMapping.categoryNames.includes(category.name);
258
- });
259
- const children = [...category.collection]
260
- .filter((item) => {
261
- return item[contextIdSymbol] === this._dynamicContextId;
262
- })
263
- .map((item) => {
264
- return transformItem(item, category, itemMappings);
270
+ if (category && managedCategory) {
271
+ const itemMappings = this._itemMappings.filter((itemMapping) => {
272
+ return itemMapping.categoryNames.includes(categoryName);
265
273
  });
274
+ if (managedCategory.selection.length > 0) {
275
+ managedCategory.selection = [];
276
+ }
277
+ managedCategory.items.forEach((item) => {
278
+ if (item.destroy) {
279
+ item.destroy();
280
+ }
281
+ });
282
+ managedCategory.items = [...category.collection]
283
+ .filter((item) => {
284
+ return item[contextIdSymbol] === this._dynamicContextId;
285
+ })
286
+ .map((item) => {
287
+ return transformItem(item, category, itemMappings);
288
+ });
289
+ }
290
+ }
266
291
 
267
-
268
- const categoryItem = {
269
- id: category.name,
270
- title: category.title,
271
- children,
272
- actions,
273
- };
274
- if (categoryItemIndex >= 0) {
275
- this._items.value.splice(categoryItemIndex, 1, categoryItem);
276
- } else {
277
- this._items.value.push(categoryItem);
292
+ /**
293
+ * create the managed category
294
+ * @param {string} categoryName
295
+ * @returns {ManagedCategory}
296
+ * @private
297
+ */
298
+ _createManagedCategory(categoryName) {
299
+ const category = this._app.categories.getByKey(categoryName);
300
+ if (!category) {
301
+ throw new Error(`Could not find Category: ${categoryName}`);
278
302
  }
279
303
 
304
+ const options = [...this._managedCategoryOptions.get(category.name).values()] // does not have to be sorted, since this is the first owner
305
+ .reduce(reduceCategoryOptions, { actions: [] });
306
+
280
307
  const listeners = [
281
308
  category.collection.added.addEventListener((item) => {
282
309
  if (item[contextIdSymbol] === this._dynamicContextId) {
@@ -303,77 +330,124 @@ class CategoryManager {
303
330
  }));
304
331
  }
305
332
 
306
- this._managedCategoriesListeners.set(category.name, listeners);
333
+ /** @type {ManagedCategory} */
334
+ const managedCategory = reactive({
335
+ ...options,
336
+ get categoryName() { return category.name; },
337
+ selection: [],
338
+ title: category.title,
339
+ items: [],
340
+ destroy() {
341
+ listeners.forEach((cb) => { cb(); });
342
+ this.items.forEach((item) => {
343
+ if (item.destroy) {
344
+ item.destroy();
345
+ }
346
+ });
347
+ this.items = [];
348
+ },
349
+ });
350
+
351
+ this._managedCategories.set(categoryName, managedCategory);
352
+ this._resetCategoryItems(categoryName);
353
+ this.componentIds.push(categoryName);
354
+ this.added.raiseEvent(managedCategory);
355
+
356
+ return managedCategory;
307
357
  }
308
358
 
309
359
  /**
310
- * updates the root item of this category in the items array.
311
- * Only updates the actions
360
+ * updates the managed categorys actions
312
361
  * @param {string} categoryName
313
362
  * @private
314
363
  */
315
364
  _updateCategory(categoryName) {
316
- if (this._managedCategories.has(categoryName)) {
317
- const categoryUiItem = this._items.value.find((item) => {
318
- return item.id === categoryName;
319
- });
320
- if (categoryUiItem) {
365
+ if (this._managedCategoryOptions.has(categoryName)) {
366
+ const managedCategory = this.get(categoryName);
367
+ if (managedCategory) {
321
368
  const pluginNames = [...this._app.plugins].map(p => p.name);
322
- const actions = [...this._managedCategories.get(categoryName).entries()]
369
+ const options = [...this._managedCategoryOptions.get(categoryName).entries()]
323
370
  .sort(([ownerA], [ownerB]) => sortByOwner(ownerA, ownerB, pluginNames))
324
371
  .map(([, value]) => value)
325
- .flatMap(ownerActions => ownerActions);
326
- categoryUiItem.actions = actions;
372
+ .reduce(reduceCategoryOptions, { actions: [] });
373
+ Object.assign(managedCategory, options);
327
374
  }
328
375
  }
329
376
  }
330
377
 
378
+ /**
379
+ * @param {string} categoryName
380
+ * @returns {ManagedCategory|undefined}
381
+ */
382
+ get(categoryName) {
383
+ return this._managedCategories.get(categoryName);
384
+ }
385
+
386
+ /**
387
+ * @param {string} categoryName
388
+ * @returns {boolean}
389
+ */
390
+ has(categoryName) {
391
+ return this._managedCategories.has(categoryName);
392
+ }
393
+
331
394
  /**
332
395
  * adds a category to this manager, the category will be shown in the components window.
333
396
  * If a category has been added by several owners the actions will be merged and sorted by the order of the
334
397
  * owner in the app.plugins collection.
335
- * @param {string} categoryName
336
- * @param {string | symbol} owner
337
- * @param {Array<VcsAction>} actions
398
+ * @param {ManagedCategoryOptions} managedCategoryOptions
399
+ * @param {string|symbol} owner
400
+ * @returns {ManagedCategory}
338
401
  */
339
- addCategory(categoryName, owner, actions) {
340
- check(categoryName, String);
402
+ add(managedCategoryOptions, owner) {
403
+ check(managedCategoryOptions, { categoryName: String });
341
404
  check(owner, [String, Symbol]);
342
- if (!validateActions(actions)) {
405
+
406
+ const { categoryName } = managedCategoryOptions;
407
+ if (managedCategoryOptions.actions && !validateActions(managedCategoryOptions.actions)) {
343
408
  throw new Error('Invalid actions Array');
344
409
  }
345
410
  if (!this._app.categories.hasKey(categoryName)) {
346
411
  throw new Error(`Could not find category: ${categoryName}`);
347
412
  }
348
413
 
349
- if (this._managedCategories.get(categoryName)?.has(owner)) {
414
+ if (this._managedCategoryOptions.get(categoryName)?.has(owner)) {
350
415
  throw new Error(`Category has already been added by this owner: ${categoryName}, ${owner}`);
351
416
  }
352
417
 
353
- if (!this._managedCategories.has(categoryName)) {
418
+ /** @type {ManagedCategoryOptions} */
419
+ const clonedOptions = {
420
+ categoryName,
421
+ actions: managedCategoryOptions.actions?.slice?.() ?? [],
422
+ selectable: managedCategoryOptions.selectable,
423
+ singleSelect: managedCategoryOptions.singleSelect,
424
+ };
425
+
426
+ if (!this._managedCategoryOptions.has(categoryName)) {
354
427
  const managedCategory = new Map();
355
- managedCategory.set(owner, actions.slice());
356
- this._managedCategories.set(categoryName, managedCategory);
357
- this._resetCategory(categoryName);
428
+ managedCategory.set(owner, clonedOptions);
429
+ this._managedCategoryOptions.set(categoryName, managedCategory);
430
+ return this._createManagedCategory(categoryName);
358
431
  } else {
359
- this._managedCategories.get(categoryName).set(owner, actions.slice());
432
+ this._managedCategoryOptions.get(categoryName).set(owner, clonedOptions);
360
433
  this._updateCategory(categoryName);
434
+ return this.get(categoryName);
361
435
  }
362
436
  }
363
437
 
364
438
  /**
365
- * removes a category from this manager
439
+ * removes a category for a specific owner from this manager
366
440
  * @param {string} categoryName
367
- * @param {string | symbol} owner
441
+ * @param {string|symbol} owner
368
442
  */
369
- removeCategory(categoryName, owner) {
443
+ remove(categoryName, owner) {
370
444
  check(categoryName, String);
371
445
  check(owner, [String, Symbol]);
372
- if (!this._managedCategories.has(categoryName)) {
446
+ if (!this._managedCategoryOptions.has(categoryName)) {
373
447
  return;
374
448
  }
375
- this._managedCategories.get(categoryName).delete(owner);
376
- if (this._managedCategories.get(categoryName).size > 0) {
449
+ this._managedCategoryOptions.get(categoryName).delete(owner);
450
+ if (this._managedCategoryOptions.get(categoryName).size > 0) {
377
451
  this._updateCategory(categoryName);
378
452
  } else {
379
453
  this._removeCategory(categoryName);
@@ -381,28 +455,26 @@ class CategoryManager {
381
455
  }
382
456
 
383
457
  /**
384
- * removes a Category from management, removes all Listeners, and updates the treeViewItems
458
+ * removes a Category from management, removes all Listeners, and updates the listItems
385
459
  * @param {string} categoryName
386
460
  * @private
387
461
  */
388
462
  _removeCategory(categoryName) {
389
- // remove rootCategoryItem
390
- const categoryItemIndex = this._items.value.findIndex((item) => {
391
- return item.id === categoryName;
392
- });
393
- if (categoryItemIndex >= 0) {
394
- this._items.value.splice(categoryItemIndex, 1);
463
+ const managedCategory = this.get(categoryName);
464
+ this._managedCategoryOptions.delete(categoryName);
465
+ const index = this.componentIds.indexOf(categoryName);
466
+ if (index > -1) {
467
+ this.componentIds.splice(index, 1);
468
+ }
469
+ if (managedCategory) {
470
+ this._managedCategories.delete(categoryName);
471
+ managedCategory.destroy();
472
+ this.removed.raiseEvent(managedCategory);
395
473
  }
396
- this._managedCategoriesListeners.get(categoryName)
397
- ?.forEach((listenerCallback) => {
398
- listenerCallback();
399
- });
400
- this._managedCategoriesListeners.delete(categoryName);
401
- this._managedCategories.delete(categoryName);
402
474
  }
403
475
 
404
476
  /**
405
- * adds MappingFunction the categoryManager. For the given categoryNames each Item will be transformed by the
477
+ * adds MappingFunction to the categoryManager. For the given categoryNames each Item will be transformed by the
406
478
  * mappingFunction if the predicate returns true.
407
479
  * @param {PredicateFunction} predicate
408
480
  * @param {MappingFunction} mappingFunction
@@ -431,8 +503,8 @@ class CategoryManager {
431
503
  };
432
504
  this._itemMappings.push(itemMapping);
433
505
  itemMapping.categoryNames.forEach((categoryName) => {
434
- if (this._managedCategories.has(categoryName)) {
435
- this._resetCategory(categoryName);
506
+ if (this._managedCategoryOptions.has(categoryName)) {
507
+ this._resetCategoryItems(categoryName);
436
508
  }
437
509
  });
438
510
  }
@@ -454,7 +526,7 @@ class CategoryManager {
454
526
  return true;
455
527
  });
456
528
  new Set(affectedCategories).forEach((categoryName) => {
457
- this._resetCategory(categoryName);
529
+ this._resetCategoryItems(categoryName);
458
530
  });
459
531
  }
460
532
 
@@ -464,24 +536,36 @@ class CategoryManager {
464
536
  */
465
537
  removeOwner(owner) {
466
538
  check(owner, [String, Symbol]);
467
- this._managedCategories.forEach((managedCategory, categoryName) => {
468
- managedCategory.delete(owner);
539
+ const affectedCategories = [];
540
+ this._managedCategoryOptions.forEach((managedCategory, categoryName) => {
541
+ if (managedCategory.delete(owner)) {
542
+ affectedCategories.push(categoryName);
543
+ }
469
544
  if (managedCategory.size === 0) {
470
- this._managedCategories.delete(categoryName);
545
+ this._managedCategoryOptions.delete(categoryName);
471
546
  }
472
547
  });
473
- this._itemMappings = this._itemMappings.filter((itemMapping) => {
474
- return itemMapping.owner !== owner;
548
+ affectedCategories.forEach((categoryName) => {
549
+ if (this._managedCategoryOptions.has(categoryName)) {
550
+ this._updateCategory(categoryName);
551
+ } else {
552
+ this._removeCategory(categoryName);
553
+ }
475
554
  });
476
- this._resetItems();
555
+ this._itemMappings = this._itemMappings
556
+ .filter((itemMapping) => {
557
+ return itemMapping.owner !== owner;
558
+ });
559
+ this._resetManagedCategories();
477
560
  }
478
561
 
479
562
  /**
480
- * Array to render in TreeView
481
- * @returns {import("vue").Ref<Array<import("@vcmap/ui").TreeViewItem>>}
563
+ * Clears the manager of all added categories and item mappings
482
564
  */
483
- get items() {
484
- return this._items;
565
+ clear() {
566
+ [...this.componentIds]
567
+ .forEach((categoryName) => { this._removeCategory(categoryName); });
568
+ this._itemMappings = [];
485
569
  }
486
570
 
487
571
  /**
@@ -490,12 +574,14 @@ class CategoryManager {
490
574
  destroy() {
491
575
  this._dynamicContextIdListener();
492
576
  this._appCategoriesRemovedListener();
493
- this.items.value.splice(0);
494
- this._managedCategories.clear();
495
- [...this._managedCategoriesListeners.values()].forEach((listeners) => {
496
- listeners.forEach((listener) => { listener(); });
577
+ this.componentIds = [];
578
+ this._managedCategories.forEach((item) => {
579
+ item.destroy();
497
580
  });
498
- this._managedCategoriesListeners.clear();
581
+ this._managedCategories.clear();
582
+ this._managedCategoryOptions.clear();
583
+ this.added.destroy();
584
+ this.removed.destroy();
499
585
  }
500
586
  }
501
587
 
@@ -4,7 +4,8 @@ import { check } from '@vcsuite/check';
4
4
  import ContextMenuInteraction from './contextMenuInteraction.js';
5
5
  import { vcsAppSymbol } from '../../pluginHelper.js';
6
6
  import { validateAction } from '../../components/lists/VcsActionList.vue';
7
- import { getFittedWindowPositionOptionsFromMapEvent, WindowSlot } from '../window/windowManager.js';
7
+ import { WindowSlot } from '../window/windowManager.js';
8
+ import { getFittedWindowPositionOptionsFromMapEvent } from '../window/windowHelper.js';
8
9
  import ContextMenuComponent, { contextMenuWindowId } from './contextMenuComponent.vue';
9
10
  import { sortByOwner } from '../navbarManager.js';
10
11
 
@@ -108,7 +109,12 @@ class ContextMenuManager {
108
109
  .filter(validateAction);
109
110
 
110
111
  if (actions.length > 0) {
111
- const position = getFittedWindowPositionOptionsFromMapEvent(event.windowPosition, 320, actions.length * 32);
112
+ const position = getFittedWindowPositionOptionsFromMapEvent(
113
+ event.windowPosition,
114
+ 320,
115
+ actions.length * 32,
116
+ this._app.maps.target,
117
+ );
112
118
  if (position.left) { // ensure we nudge the window, so it does not trigger the default right click.
113
119
  position.left += 1;
114
120
  } else {