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

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 (67) hide show
  1. package/build/buildHelpers.js +7 -1
  2. package/config/base.config.json +3 -45
  3. package/config/www.config.json +756 -132
  4. package/dist/assets/{cesium.430460.js → cesium.2f992f.js} +0 -0
  5. package/dist/assets/cesium.js +1 -1
  6. package/dist/assets/{core.5089ba.js → core.cb0408.js} +1700 -1718
  7. package/dist/assets/core.js +1 -1
  8. package/dist/assets/{index.854f8e2b.js → index.bccdf969.js} +1 -1
  9. package/dist/assets/{ol.9be53a.js → ol.5e3fd0.js} +0 -0
  10. package/dist/assets/ol.js +1 -1
  11. package/dist/assets/ui.08c48f.css +1 -0
  12. package/dist/assets/{ui.49010a.js → ui.08c48f.js} +6254 -5906
  13. package/dist/assets/ui.js +1 -1
  14. package/dist/assets/{vue.247c1c.js → vue.228ead.js} +0 -0
  15. package/dist/assets/vue.js +2 -2
  16. package/dist/assets/{vuetify.735e58.css → vuetify.0b5039.css} +0 -0
  17. package/dist/assets/{vuetify.735e58.js → vuetify.0b5039.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/buttonExamples/ButtonExamples.vue +18 -0
  25. package/plugins/categoryTest/Categories.vue +27 -13
  26. package/plugins/categoryTest/Category.vue +7 -1
  27. package/plugins/categoryTest/index.js +1 -1
  28. package/plugins/package.json +1 -1
  29. package/plugins/test/allIconsComponent.vue +3 -3
  30. package/plugins/test/index.js +9 -5
  31. package/plugins/test/testList.vue +4 -1
  32. package/plugins/test/toolbox-data.js +168 -111
  33. package/plugins/test/vcsContent.vue +1 -1
  34. package/plugins/test/windowManagerExample.vue +9 -7
  35. package/src/actions/actionHelper.js +13 -10
  36. package/src/application/VcsApp.vue +25 -26
  37. package/src/application/VcsNavbar.vue +1 -1
  38. package/src/components/buttons/VcsButton.vue +14 -3
  39. package/src/components/form-inputs-controls/VcsCheckbox.vue +1 -0
  40. package/src/components/form-inputs-controls/VcsFormSection.vue +14 -6
  41. package/src/components/lists/VcsActionList.vue +2 -0
  42. package/src/components/lists/VcsList.vue +4 -2
  43. package/src/contentTree/contentTreeCollection.js +9 -0
  44. package/src/contentTree/layerContentTreeItem.js +3 -3
  45. package/src/featureInfo/BalloonComponent.vue +5 -2
  46. package/src/featureInfo/balloonFeatureInfoView.js +2 -8
  47. package/src/featureInfo/balloonHelper.js +22 -5
  48. package/src/featureInfo/featureInfo.js +1 -2
  49. package/src/i18n/de.js +12 -3
  50. package/src/i18n/en.js +10 -1
  51. package/src/legend/legendHelper.js +6 -7
  52. package/src/legend/vcsLegend.vue +12 -3
  53. package/src/manager/categoryManager/CategoryComponent.vue +115 -0
  54. package/src/manager/categoryManager/CategoryComponentList.vue +57 -0
  55. package/src/manager/categoryManager/CategoryManager.vue +35 -0
  56. package/src/manager/categoryManager/categoryManager.js +251 -165
  57. package/src/manager/contextMenu/contextMenuManager.js +8 -2
  58. package/src/manager/toolbox/ToolboxManager.vue +1 -0
  59. package/src/manager/window/WindowComponent.vue +49 -75
  60. package/src/manager/window/WindowComponentHeader.vue +49 -7
  61. package/src/manager/window/WindowManager.vue +53 -30
  62. package/src/manager/window/windowHelper.js +341 -0
  63. package/src/manager/window/windowManager.js +162 -150
  64. package/src/notifier/notifier.js +4 -5
  65. package/src/vcsUiApp.js +7 -1
  66. package/dist/assets/ui.49010a.css +0 -1
  67. package/src/manager/categoryManager/ComponentsManager.vue +0 -30
@@ -388,15 +388,17 @@
388
388
  .forEach(i => i.selectionChanged(false));
389
389
  selected.value = [];
390
390
  firstSelected = null;
391
+ emit('input', selected.value);
391
392
  },
392
393
  selectAll() {
393
394
  const currentSelection = [...selected.value];
394
395
  selected.value = this.renderingItems.slice(0);
395
396
  selected.value.forEach((item) => {
396
- if (item.selectionChanged && currentSelection.includes(item)) {
397
+ if (item.selectionChanged && !currentSelection.includes(item)) {
397
398
  item.selectionChanged(true);
398
399
  }
399
400
  });
401
+ emit('input', selected.value);
400
402
  },
401
403
  };
402
404
  },
@@ -409,7 +411,7 @@
409
411
  .v-list-item {
410
412
  padding: 4px 8px 4px 16px;
411
413
  display: grid;
412
- grid-template-columns: auto 1fr;
414
+ grid-template-columns: auto auto;
413
415
  &:after{
414
416
  display: none;
415
417
  }
@@ -198,6 +198,15 @@ class ContentTreeCollection extends IndexedCollection {
198
198
  this._subTreeListeners = subTrees.map(subTree => this._createSubtreeActionButton(subTree));
199
199
  }
200
200
 
201
+ /**
202
+ * Returns all managed subtrees. Entries are not persisted and will change, if the trees get recalculated.
203
+ * @type {import("vue").Ref<Map<string, TreeViewItem>>}
204
+ * @readonly
205
+ */
206
+ get subTreeViewItems() {
207
+ return this._subTreeViewItems;
208
+ }
209
+
201
210
  /**
202
211
  * All ids of the currently managed subtrees. Ids are not persisted and will change if
203
212
  * the trees get recalculated. The first ID is always the default tree. Other ids are subtree ids.
@@ -127,7 +127,7 @@ class LayerContentTreeItem extends VcsObjectContentTreeItem {
127
127
  layerName: this._layerName,
128
128
  }),
129
129
  },
130
- this._app.windowManager,
130
+ this._app,
131
131
  vcsAppSymbol,
132
132
  );
133
133
  this.addAction(action, 4);
@@ -139,7 +139,7 @@ class LayerContentTreeItem extends VcsObjectContentTreeItem {
139
139
  * @private
140
140
  */
141
141
  _setLayerExtentAction() {
142
- const name = 'LayerExtentAction';
142
+ const name = 'content.layerExtentAction.name';
143
143
  this.removeAction(name);
144
144
  if (this._layer) {
145
145
  const { extent } = this._layer.toJSON();
@@ -147,7 +147,7 @@ class LayerContentTreeItem extends VcsObjectContentTreeItem {
147
147
  const viewpoint = Viewpoint.createViewpointFromExtent(this._layer.extent);
148
148
  const action = createGoToViewpointAction(
149
149
  {
150
- name: 'LayerExtentAction',
150
+ name,
151
151
  title: 'content.layerExtentAction.title',
152
152
  },
153
153
  viewpoint,
@@ -40,9 +40,9 @@
40
40
  <v-list-item class="px-2">
41
41
  <v-list-item-content>
42
42
  <v-list-item-title>
43
- {{ name }}
43
+ {{ $t(name) }}
44
44
  </v-list-item-title>
45
- <v-list-item-subtitle>{{ value }}</v-list-item-subtitle>
45
+ <v-list-item-subtitle>{{ $t(value) }}</v-list-item-subtitle>
46
46
  </v-list-item-content>
47
47
  </v-list-item>
48
48
  </v-list>
@@ -150,6 +150,9 @@
150
150
  </script>
151
151
 
152
152
  <style lang="scss">
153
+ .balloon {
154
+ z-index: 0 !important;
155
+ }
153
156
  .balloon hr:first-child {
154
157
  display: none;
155
158
  }
@@ -4,9 +4,8 @@ import { Cartographic, Entity, Math as CesiumMath } from '@vcmap/cesium';
4
4
  import { Projection } from '@vcmap/core';
5
5
  import { check } from '@vcsuite/check';
6
6
  import AbstractFeatureInfoView from './abstractFeatureInfoView.js';
7
- import { getWindowPositionOptions, WindowAlignment, WindowSlot } from '../manager/window/windowManager.js';
7
+ import { WindowSlot } from '../manager/window/windowManager.js';
8
8
  import BalloonComponent from './BalloonComponent.vue';
9
- import { balloonOffset } from './balloonHelper.js';
10
9
 
11
10
  /**
12
11
  * derive value from attributes
@@ -124,12 +123,7 @@ class BalloonFeatureInfoView extends AbstractFeatureInfoView {
124
123
  options.state.hideHeader = true;
125
124
  options.state.classes = ['balloon'];
126
125
  options.slot = WindowSlot.DETACHED;
127
- options.position = getWindowPositionOptions(
128
- (featureInfo.windowPosition?.[0] ?? 0) - balloonOffset.x, // if we do not have a windowPosition, let the next render handle it
129
- (featureInfo.windowPosition?.[1] ?? 0) - balloonOffset.y,
130
- null,
131
- WindowAlignment.BOTTOM_LEFT,
132
- );
126
+ // windowPosition is handled by next render
133
127
  return options;
134
128
  }
135
129
 
@@ -4,7 +4,7 @@ import { unByKey } from 'ol/Observable.js';
4
4
  import {
5
5
  getWindowPositionOptionsFromMapEvent,
6
6
  WindowAlignment,
7
- } from '../manager/window/windowManager.js';
7
+ } from '../manager/window/windowHelper.js';
8
8
 
9
9
  /**
10
10
  * balloon offset from location or click position in pixel
@@ -63,8 +63,9 @@ export async function getBalloonPosition(app, position) {
63
63
  * @param {WindowManager} windowManager
64
64
  * @param {string} id - windowId of balloon
65
65
  * @param {import("@vcmap/cesium").Cartesian2|undefined} windowPosition
66
+ * @param {HTMLElement} target - the map's target { @link @import("@vcmap/core").MapCollection }
66
67
  */
67
- export function setBalloonPosition(windowManager, id, windowPosition) {
68
+ export function setBalloonPosition(windowManager, id, windowPosition, target) {
68
69
  if (!windowPosition) {
69
70
  return;
70
71
  }
@@ -72,6 +73,7 @@ export function setBalloonPosition(windowManager, id, windowPosition) {
72
73
  id,
73
74
  getWindowPositionOptionsFromMapEvent(
74
75
  new Cartesian2(windowPosition.x - balloonOffset.x, windowPosition.y - balloonOffset.y),
76
+ target,
75
77
  WindowAlignment.BOTTOM_LEFT,
76
78
  ),
77
79
  );
@@ -106,10 +108,20 @@ export async function setupBalloonPositionListener(vcsApp, windowId, clickedPosi
106
108
  const wgs84Position = Projection.mercatorToWgs84(position);
107
109
  const cartesian = Cartographic.toCartesian(Cartographic.fromDegrees(...wgs84Position));
108
110
  listeners.push(map.getScene().postRender.addEventListener((scene) => {
109
- setBalloonPosition(app.windowManager, windowId, getBalloonPositionCesium(scene, cartesian));
111
+ setBalloonPosition(
112
+ app.windowManager,
113
+ windowId,
114
+ getBalloonPositionCesium(scene, cartesian),
115
+ app.maps.target,
116
+ );
110
117
  }));
111
118
  } else if (map instanceof OpenlayersMap) {
112
- const handler = () => setBalloonPosition(app.windowManager, windowId, getBalloonPositionOL(map.olMap, position));
119
+ const handler = () => setBalloonPosition(
120
+ app.windowManager,
121
+ windowId,
122
+ getBalloonPositionOL(map.olMap, position),
123
+ app.maps.target,
124
+ );
113
125
  const key = map.olMap.on(
114
126
  'postrender',
115
127
  handler,
@@ -121,7 +133,12 @@ export async function setupBalloonPositionListener(vcsApp, windowId, clickedPosi
121
133
  position,
122
134
  );
123
135
  listeners.push(map.imageChanged.addEventListener(setup.bind(null, app, windowId, position)));
124
- const handler = () => setBalloonPosition(app.windowManager, windowId, getBalloonPositionOL(map.olMap, coords));
136
+ const handler = () => setBalloonPosition(
137
+ app.windowManager,
138
+ windowId,
139
+ getBalloonPositionOL(map.olMap, coords),
140
+ app.maps.target,
141
+ );
125
142
  const key = map.olMap.on(
126
143
  'postrender',
127
144
  handler,
@@ -25,7 +25,6 @@ import {
25
25
  } from '@vcmap/cesium';
26
26
  import { Feature } from 'ol';
27
27
  import { check, checkMaybe } from '@vcsuite/check';
28
- import { v4 as uuidv4 } from 'uuid';
29
28
 
30
29
  import { vcsAppSymbol } from '../pluginHelper.js';
31
30
  import FeatureInfoInteraction from './featureInfoInteraction.js';
@@ -402,7 +401,7 @@ class FeatureInfo {
402
401
  });
403
402
  this._clearHighlightingCb = () => layer.featureVisibility.unHighlight([featureId]);
404
403
  }
405
- this._windowId = uuidv4(); // we need to create a uuid, otherwise the window manager gets confused if we recreate a window in the same thread with the same id
404
+ this._windowId = usedFeatureInfoView.className; // use className for a type based position caching
406
405
  this._app.windowManager.add(
407
406
  {
408
407
  id: this._windowId,
package/src/i18n/de.js CHANGED
@@ -8,6 +8,9 @@ const messages = {
8
8
  menu: {
9
9
  tooltip: 'Menü',
10
10
  },
11
+ share: {
12
+ tooltip: 'Den aktuellen Kartenausschnitt teilen.',
13
+ },
11
14
  },
12
15
  content: {
13
16
  title: 'Inhalte',
@@ -30,7 +33,8 @@ const messages = {
30
33
  title: 'Öffne Stil Auswahl',
31
34
  },
32
35
  layerExtentAction: {
33
- title: 'Jump to layer extent',
36
+ name: 'Ebenenausdehnung',
37
+ title: 'Auf Ebenenausdehnung zoomen',
34
38
  },
35
39
  },
36
40
  navigation: {
@@ -38,12 +42,17 @@ const messages = {
38
42
  obliqueRightTooltip: 'Schrägluftbild nach rechts drehen',
39
43
  zoomInTooltip: 'Hineinzoomen',
40
44
  zoomOutTooltip: 'Herauszoomen',
41
- pitchTooltip: 'Kamera pitch: {0}°',
45
+ pitchTooltip: 'Kameraneigung: {0}°',
42
46
  overviewMapTooltip: 'Übersichtskarte',
43
47
  },
44
- components: {
48
+ categoryManager: {
45
49
  title: 'Komponenten',
46
50
  tooltip: 'Komponenten',
51
+ more: 'Weitere anzeigen...',
52
+ empty: 'Es gibt noch keine Einträge.',
53
+ },
54
+ components: {
55
+ pin: 'Fenster andocken.',
47
56
  close: 'Fenster schließen.',
48
57
  vcsFormSection: {
49
58
  help: 'Hilfe anzeigen.',
package/src/i18n/en.js CHANGED
@@ -8,6 +8,9 @@ const messages = {
8
8
  menu: {
9
9
  tooltip: 'Menu',
10
10
  },
11
+ share: {
12
+ tooltip: 'Share current view of the map.',
13
+ },
11
14
  },
12
15
  content: {
13
16
  title: 'Content',
@@ -30,6 +33,7 @@ const messages = {
30
33
  title: 'Open Style selector',
31
34
  },
32
35
  layerExtentAction: {
36
+ name: 'Layer extent',
33
37
  title: 'Jump to layer extent',
34
38
  },
35
39
  },
@@ -41,9 +45,14 @@ const messages = {
41
45
  pitchTooltip: 'Camera pitch: {0}°',
42
46
  overviewMapTooltip: 'Overview Map',
43
47
  },
44
- components: {
48
+ categoryManager: {
45
49
  title: 'Components',
46
50
  tooltip: 'Components',
51
+ more: 'Show more...',
52
+ empty: 'There are no entries yet.',
53
+ },
54
+ components: {
55
+ pin: 'Pin window.',
47
56
  close: 'Close window.',
48
57
  vcsFormSection: {
49
58
  help: 'Show help.',
@@ -1,5 +1,5 @@
1
1
  import { getShapeFromOptions } from '@vcmap/core';
2
- import { ref } from 'vue';
2
+ import { shallowRef } from 'vue';
3
3
 
4
4
  /**
5
5
  * @enum {string}
@@ -142,7 +142,7 @@ export function getLegendEntries(app) {
142
142
  /**
143
143
  * @type {import("vue").Ref<Array<LegendEntry>>}>}
144
144
  */
145
- const entries = ref([]);
145
+ const entries = shallowRef([]);
146
146
  /**
147
147
  * @type {Object<string,function():void>}
148
148
  */
@@ -153,10 +153,8 @@ export function getLegendEntries(app) {
153
153
  */
154
154
  function removeEntryForLayer(layer) {
155
155
  const layerName = layer.name;
156
- const index = entries.value.findIndex(({ key }) => { return key === layerName; });
157
- if (index >= 0) {
158
- entries.value.splice(index, 1);
159
- }
156
+ // reassign to trigger update
157
+ entries.value = entries.value.filter(({ key }) => key !== layerName);
160
158
  if (styleChangedListener[layerName]) {
161
159
  styleChangedListener[layerName]();
162
160
  delete styleChangedListener[layerName];
@@ -176,7 +174,8 @@ export function getLegendEntries(app) {
176
174
  const legend = layer.style?.properties?.legend ?? layer.properties?.legend;
177
175
  if (legend) {
178
176
  const legendEntry = createLayerLegendEntry(key, title, legend);
179
- entries.value.push(legendEntry);
177
+ // use spread since push won't trigger updates
178
+ entries.value = [...entries.value, legendEntry];
180
179
  }
181
180
  if (layer.styleChanged) {
182
181
  styleChangedListener[layer.name] = layer.styleChanged.addEventListener(() => syncLayerLegendEntries(layer));
@@ -27,7 +27,13 @@
27
27
  <v-list dense>
28
28
  <div v-for="(item, idx) in entry.legend" :key="idx">
29
29
  <div v-if="item.type === LegendType.Image" class="mx-2">
30
- <v-img :src="$t(item.src)" max-width="287" class="mx-2" :title="item.tooltip" />
30
+ <img
31
+ :src="$t(item.src)"
32
+ max-width="287"
33
+ max-height="auto"
34
+ class="mx-2 legend-image"
35
+ :title="item.tooltip"
36
+ >
31
37
  </div>
32
38
  <div v-else-if="item.type === LegendType.Iframe" class="mx-2">
33
39
  <iframe
@@ -58,7 +64,6 @@
58
64
  VExpansionPanelContent,
59
65
  VIcon,
60
66
  VList,
61
- VImg,
62
67
  } from 'vuetify/lib';
63
68
  import { LegendType } from './legendHelper.js';
64
69
  import StyleLegendItem from './styleLegendItem.vue';
@@ -80,7 +85,6 @@
80
85
  VExpansionPanelContent,
81
86
  VIcon,
82
87
  VList,
83
- VImg,
84
88
  },
85
89
  props: {
86
90
  entries: {
@@ -115,4 +119,9 @@
115
119
  max-width: 189px;
116
120
  }
117
121
  }
122
+ .legend-image {
123
+ max-width: 287px;
124
+ height: auto;
125
+ width: auto;
126
+ }
118
127
  </style>
@@ -0,0 +1,115 @@
1
+ <template>
2
+ <v-container>
3
+ <v-row @click="open = !open" class="px-2 py-1 cursor-pointer">
4
+ <span>
5
+ <v-icon v-if="open">mdi-chevron-up</v-icon>
6
+ <v-icon v-else>mdi-chevron-down</v-icon>
7
+ </span>
8
+ <span class="flex-grow-1 flex-shrink-1">{{ $t(category.title) }}</span>
9
+ <vcs-action-button-list
10
+ v-if="category.actions?.length > 0"
11
+ :actions="category.actions"
12
+ small
13
+ />
14
+ </v-row>
15
+ <template v-if="open">
16
+ <v-row>
17
+ <v-col class="pa-0">
18
+ <vcs-list
19
+ :items="category.items.slice(0, 10)"
20
+ :selectable="category.selectable"
21
+ :single-select="category.singleSelect"
22
+ v-model="selection"
23
+ :show-title="false"
24
+ />
25
+ </v-col>
26
+ </v-row>
27
+ <v-row v-if="category.items.length > 10">
28
+ <v-col class="pa-0">
29
+ <VcsButton @click="openCategoryItemWindow" class="pa-2" small>
30
+ {{ $t('categoryManager.more') }}
31
+ </VcsButton>
32
+ </v-col>
33
+ </v-row>
34
+ <v-row v-else-if="category.items.length === 0">
35
+ <v-col class="pa-2">
36
+ {{ $t('categoryManager.empty') }}
37
+ </v-col>
38
+ </v-row>
39
+ </template>
40
+ </v-container>
41
+ </template>
42
+
43
+ <script>
44
+ import { computed, inject, ref } from 'vue';
45
+ import { VIcon, VContainer, VRow, VCol } from 'vuetify/lib';
46
+ import VcsList from '../../components/lists/VcsList.vue';
47
+ import VcsActionButtonList from '../../components/buttons/VcsActionButtonList.vue';
48
+ import VcsButton from '../../components/buttons/VcsButton.vue';
49
+ import { vcsAppSymbol } from '../../pluginHelper.js';
50
+ import CategoryComponentList from './CategoryComponentList.vue';
51
+ import { WindowSlot } from '../window/windowManager.js';
52
+
53
+ export default {
54
+ name: 'CategoryComponent',
55
+ components: {
56
+ VcsActionButtonList,
57
+ VcsList,
58
+ VContainer,
59
+ VRow,
60
+ VcsButton,
61
+ VCol,
62
+ VIcon,
63
+ },
64
+ props: {
65
+ /** @type {ManagedCategory} */
66
+ category: {
67
+ type: Object,
68
+ required: true,
69
+ },
70
+ },
71
+ setup({ category }) {
72
+ /** @type {VcsUiApp} */
73
+ const app = inject('vcsApp');
74
+ const windowId = `${category.id}-category-list`;
75
+ const open = ref(false);
76
+
77
+ const selection = computed({
78
+ get() { return category.selection; },
79
+ set(value) {
80
+ // eslint-disable-next-line vue/no-mutating-props
81
+ category.selection = value;
82
+ },
83
+ });
84
+
85
+ return {
86
+ selection,
87
+ open,
88
+ openCategoryItemWindow() {
89
+ if (app.windowManager.has(windowId)) {
90
+ setTimeout(() => {
91
+ app.windowManager.bringWindowToTop(windowId);
92
+ }, 0);
93
+ } else {
94
+ app.windowManager.add({
95
+ id: windowId,
96
+ component: CategoryComponentList,
97
+ props: {
98
+ category,
99
+ windowId,
100
+ },
101
+ provides: {
102
+ selection,
103
+ },
104
+ slot: WindowSlot.DYNAMIC_LEFT,
105
+ }, vcsAppSymbol);
106
+ }
107
+ },
108
+ };
109
+ },
110
+ };
111
+ </script>
112
+
113
+ <style scoped>
114
+
115
+ </style>
@@ -0,0 +1,57 @@
1
+ <template>
2
+ <vcs-list
3
+ :items="category.items"
4
+ :selectable="category.selectable"
5
+ :single-select="category.singleSelect"
6
+ v-model="selection"
7
+ :title="category.title"
8
+ />
9
+ </template>
10
+
11
+ <script>
12
+ import { computed, inject, watch } from 'vue';
13
+ import VcsList from '../../components/lists/VcsList.vue';
14
+
15
+ export default {
16
+ name: 'CategoryComponenList',
17
+ components: {
18
+ VcsList,
19
+ },
20
+ props: {
21
+ category: {
22
+ type: Object,
23
+ required: true,
24
+ },
25
+ windowId: {
26
+ type: String,
27
+ required: true,
28
+ },
29
+ },
30
+ setup({ category, windowId }) {
31
+ /** @type {VcsUiApp} */
32
+ const app = inject('vcsApp');
33
+
34
+ const selection = computed({
35
+ get() { return category.selection; },
36
+ set(value) {
37
+ // eslint-disable-next-line vue/no-mutating-props
38
+ category.selection = value;
39
+ },
40
+ });
41
+
42
+ watch(app.categoryManager.componentIds, () => {
43
+ if (!app.categoryManager.get(category.id)) {
44
+ app.windowManager.remove(windowId);
45
+ }
46
+ });
47
+
48
+ return {
49
+ selection,
50
+ };
51
+ },
52
+ };
53
+ </script>
54
+
55
+ <style scoped>
56
+
57
+ </style>
@@ -0,0 +1,35 @@
1
+ <template>
2
+ <div
3
+ v-if="categories.length > 0"
4
+ >
5
+ <category-component
6
+ v-for="category in categories"
7
+ :category="category"
8
+ :key="category.categoryName"
9
+ />
10
+ </div>
11
+ </template>
12
+
13
+ <script>
14
+ import { inject, ref, computed } from 'vue';
15
+ import CategoryComponent from './CategoryComponent.vue';
16
+
17
+ /**
18
+ * @description
19
+ * uses a VcsList and renders 'a components Window' based on the "categoryManager"
20
+ * Uses the provided 'vcsApp'
21
+ */
22
+ export default {
23
+ name: 'CategoryManager',
24
+ components: { CategoryComponent },
25
+ setup() {
26
+ const app = inject('vcsApp');
27
+ const categoryIds = ref(app.categoryManager.componentIds);
28
+ const categories = computed(() => categoryIds.value.map(id => app.categoryManager.get(id)));
29
+
30
+ return {
31
+ categories,
32
+ };
33
+ },
34
+ };
35
+ </script>