@vcmap/ui 6.1.0-rc.7 → 6.1.0

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 (94) hide show
  1. package/config/base.config.json +7 -3
  2. package/config/cluster.config.json +1 -1
  3. package/config/dev.config.json +172 -56
  4. package/config/projects.config.json +2 -1
  5. package/config/vectorTile.config.json +42 -1
  6. package/dist/assets/cesium.js +1 -1
  7. package/dist/assets/{core-52c2ef11.js → core-841b71a4.js} +7544 -5485
  8. package/dist/assets/core.js +1 -1
  9. package/dist/assets/ol.js +1 -1
  10. package/dist/assets/{ui-dccb9009.css → ui-2fd6f47d.css} +1 -1
  11. package/dist/assets/{ui-dccb9009.js → ui-2fd6f47d.js} +21402 -20661
  12. package/dist/assets/ui.js +1 -1
  13. package/dist/assets/vue.js +1 -1
  14. package/dist/assets/{vuetify-43a20e18.css → vuetify-4bc77ff7.css} +2 -2
  15. package/dist/assets/{vuetify-43a20e18.js → vuetify-4bc77ff7.js} +6694 -6593
  16. package/dist/assets/vuetify.js +1 -1
  17. package/index.d.ts +13 -5
  18. package/index.js +13 -5
  19. package/package.json +9 -8
  20. package/plugins/@vcmap-show-case/vector-properties-example/src/LayerSettings.vue +39 -0
  21. package/plugins/@vcmap-show-case/vector-properties-example/src/VectorPropertiesExample.vue +3 -0
  22. package/plugins/@vcmap-show-case/vector-properties-example/src/lib.js +13 -0
  23. package/plugins/@vcmap-show-case/window-tester/src/WindowExample.vue +9 -0
  24. package/plugins/package.json +7 -5
  25. package/src/actions/actionHelper.d.ts +6 -0
  26. package/src/actions/actionHelper.js +22 -0
  27. package/src/actions/deepPickingAction.d.ts +23 -0
  28. package/src/actions/deepPickingAction.js +399 -0
  29. package/src/application/VcsApp.vue +3 -0
  30. package/src/application/VcsApp.vue.d.ts +4 -0
  31. package/src/application/VcsAttributionsFooter.vue +1 -0
  32. package/src/application/VcsContainer.vue +1 -0
  33. package/src/application/VcsContainer.vue.d.ts +4 -0
  34. package/src/application/VcsMobileMenuList.vue +34 -41
  35. package/src/application/VcsNavbar.vue +3 -0
  36. package/src/application/VcsNavbarMobile.vue +6 -18
  37. package/src/application/VcsNavbarMobile.vue.d.ts +0 -1
  38. package/src/application/VcsPositionDisplay.vue +1 -0
  39. package/src/components/buttons/VcsActionButtonList.vue +1 -0
  40. package/src/components/form-inputs-controls/VcsSelect.vue +8 -6
  41. package/src/components/icons/+all.d.ts +5 -0
  42. package/src/components/icons/+all.js +14 -0
  43. package/src/components/lists/VcsActionList.vue +1 -0
  44. package/src/components/lists/VcsGroupedList.vue +2 -1
  45. package/src/components/lists/VcsListItemComponent.vue +1 -0
  46. package/src/components/lists/VcsTreeNode.vue +5 -1
  47. package/src/components/lists/VcsTreeview.vue +4 -1
  48. package/src/components/style/{MenuWrapper.vue → StyleMenuWrapper.vue} +2 -1
  49. package/src/components/style/VcsFillMenu.vue +4 -4
  50. package/src/components/style/VcsImageMenu.vue +4 -4
  51. package/src/components/style/VcsStrokeMenu.vue +4 -4
  52. package/src/components/style/VcsTextMenu.vue +4 -4
  53. package/src/contentTree/LayerTree.vue +8 -46
  54. package/src/contentTree/LayerTree.vue.d.ts +1 -3
  55. package/src/contentTree/contentTreeCollection.d.ts +7 -0
  56. package/src/contentTree/contentTreeCollection.js +30 -10
  57. package/src/contentTree/contentTreeItem.d.ts +4 -4
  58. package/src/contentTree/contentTreeItem.js +2 -2
  59. package/src/contentTree/groupContentTreeItem.d.ts +5 -0
  60. package/src/contentTree/groupContentTreeItem.js +1 -1
  61. package/src/contentTree/layerContentTreeItem.js +1 -1
  62. package/src/contentTree/nodeContentTreeItem.d.ts +21 -0
  63. package/src/contentTree/nodeContentTreeItem.js +31 -2
  64. package/src/contentTree/wmsChildContentTreeItem.d.ts +56 -0
  65. package/src/contentTree/wmsChildContentTreeItem.js +159 -0
  66. package/src/contentTree/wmsGroupContentTreeItem.d.ts +171 -0
  67. package/src/contentTree/wmsGroupContentTreeItem.js +619 -0
  68. package/src/featureInfo/ClusterFeatureComponent.vue +47 -11
  69. package/src/featureInfo/ClusterFeatureComponent.vue.d.ts +1 -0
  70. package/src/featureInfo/balloonFeatureInfoView.d.ts +3 -0
  71. package/src/featureInfo/balloonFeatureInfoView.js +78 -11
  72. package/src/featureInfo/balloonHelper.js +8 -12
  73. package/src/featureInfo/featureInfo.d.ts +32 -7
  74. package/src/featureInfo/featureInfo.js +190 -90
  75. package/src/i18n/de.d.ts +22 -16
  76. package/src/i18n/de.js +4 -0
  77. package/src/i18n/en.d.ts +22 -16
  78. package/src/i18n/en.js +4 -0
  79. package/src/manager/toolbox/GroupToolboxComponent.vue +1 -0
  80. package/src/manager/toolbox/SelectToolboxComponent.vue +2 -0
  81. package/src/manager/toolbox/ToolboxManagerComponent.vue +1 -0
  82. package/src/manager/window/windowManager.d.ts +2 -2
  83. package/src/manager/window/windowManager.js +12 -10
  84. package/src/navigation/MapNavigation.vue +3 -1
  85. package/src/notifier/NotifierComponent.vue +1 -0
  86. package/src/search/search.js +3 -16
  87. package/src/state.d.ts +2 -1
  88. package/src/state.js +2 -1
  89. package/src/uiConfig.d.ts +9 -0
  90. package/src/uiConfig.js +1 -0
  91. /package/dist/assets/{cesium-6c6aa853.js → cesium-664ad022.js} +0 -0
  92. /package/dist/assets/{ol-b0589b0c.js → ol-2e095c08.js} +0 -0
  93. /package/dist/assets/{vue-f7a0b088.js → vue-71fd14e8.js} +0 -0
  94. /package/src/components/style/{MenuWrapper.vue.d.ts → StyleMenuWrapper.vue.d.ts} +0 -0
@@ -15,12 +15,14 @@
15
15
  v-model="localModelValue"
16
16
  >
17
17
  <template #selection="{ item, index }">
18
- <span v-if="index === 0" class="text-truncate w-100">
19
- {{ $st(getTitle(item.raw)) }}
20
- </span>
21
- <span v-if="index === 1" class="text-no-wrap">
22
- (+{{ additionalItems }})
23
- </span>
18
+ <slot name="selection" v-bind="{ item, index }">
19
+ <span v-if="index === 0" class="text-truncate w-100">
20
+ {{ $st(getTitle(item.raw)) }}
21
+ </span>
22
+ <span v-if="index === 1" class="text-no-wrap">
23
+ (+{{ additionalItems }})
24
+ </span>
25
+ </slot>
24
26
  </template>
25
27
 
26
28
  <template #append-inner="scope">
@@ -1,3 +1,8 @@
1
+ /**
2
+ * @param {string} color
3
+ * @returns {import("ol/style/Icon").Options}
4
+ */
5
+ export function getColoredMapIcon(color: string): import("ol/style/Icon").Options;
1
6
  export const IconIds: string[];
2
7
  export const IconNames: string[];
3
8
  export default Icons;
@@ -497,3 +497,17 @@ export const IconNames = Object.keys(Icons).map((name) => {
497
497
  return `$${name}`;
498
498
  });
499
499
  export default Icons;
500
+
501
+ /**
502
+ * @param {string} color
503
+ * @returns {import("ol/style/Icon").Options}
504
+ */
505
+ export function getColoredMapIcon(color) {
506
+ return {
507
+ src: `data:image/svg+xml,%3Csvg xmlns:dc='http://purl.org/dc/elements/1.1/' xmlns:cc='http://creativecommons.org/ns%23' xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns%23' xmlns:svg='http://www.w3.org/2000/svg' xmlns='http://www.w3.org/2000/svg' xmlns:sodipodi='http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd' xmlns:inkscape='http://www.inkscape.org/namespaces/inkscape' id='icon_24_poi' width='24' height='23.994' viewBox='0 0 24 23.994' sodipodi:docname='mapIcon.svg'%3E%3Cg id='Gruppe_1972' transform='translate(-571 -609.477)'%3E%3Cpath id='Pfad_773' d='M583,611a8.009,8.009,0,0,0-8,8c0,5.314,6.952,13.32,7.248,13.658a1,1,0,0,0,1.5,0c.3-.338,7.248-8.344,7.248-13.658A8.009,8.009,0,0,0,583,611Zm0,19.444c-2.18-2.685-6-8.09-6-11.444a6,6,0,0,1,12,0C589,622.354,585.18,627.759,583,630.444Z' fill='currentColor' /%3E%3Cpath id='Pfad_774' d='M583,615a4,4,0,1,0,4,4A4,4,0,0,0,583,615Zm0,6a2,2,0,1,1,2-2A2,2,0,0,1,583,621Z' fill='currentColor' /%3E%3C/g%3E%3Cpath fill='${encodeURIComponent(
508
+ color,
509
+ )}' d='M 11.672998,20.526286 C 8.5115524,16.526958 6.4310003,12.714969 6.0702695,10.260963 6.0109099,9.8571482 6.0115821,9.1201807 6.0716855,8.7084104 6.4424582,6.1682348 8.3335069,4.1603103 10.828528,3.6575721 c 1.904966,-0.383844 3.881822,0.1903514 5.289639,1.5364231 0.993092,0.9495349 1.610829,2.1488769 1.810148,3.5144152 0.0601,0.4117703 0.06077,1.1487378 0.0014,1.5525526 -0.357076,2.429138 -2.337816,6.081898 -5.487559,10.119822 -0.224045,0.287223 -0.415188,0.530536 -0.424763,0.540696 -0.0096,0.01016 -0.16456,-0.167678 -0.344411,-0.395195 z m 0.990366,-7.047968 c 0.894914,-0.146674 1.762065,-0.627065 2.349286,-1.301476 0.86707,-0.995812 1.194989,-2.3427819 0.880571,-3.6170541 -0.379849,-1.5394474 -1.596396,-2.6842781 -3.173401,-2.9863277 -0.368703,-0.070619 -1.070937,-0.070619 -1.43964,0 C 9.7056173,5.875042 8.48604,7.0227247 8.1067793,8.5597879 7.8410265,9.6368274 8.0329903,10.787029 8.6317551,11.705317 c 0.5717674,0.876885 1.4205679,1.474277 2.4457369,1.721329 0.47704,0.114961 1.079877,0.134602 1.585872,0.05167 z' id='path1432' /%3E%3C/svg%3E`,
510
+ scale: 1,
511
+ color,
512
+ };
513
+ }
@@ -2,6 +2,7 @@
2
2
  <v-list v-if="actions.length > 0" rounded class="vcs-action-list">
3
3
  <v-list-item
4
4
  v-for="(action, index) in actions"
5
+ :data-action-name="action.name"
5
6
  :key="`${action.name}-${index}`"
6
7
  :class="action.active ? 'text-primary' : ''"
7
8
  :disabled="action.disabled || disabled"
@@ -142,7 +142,7 @@
142
142
  </script>
143
143
 
144
144
  <template>
145
- <div>
145
+ <div class="vcs-grouped-list">
146
146
  <VcsTreeviewSearchbar
147
147
  v-if="searchable"
148
148
  :placeholder="searchbarPlaceholder"
@@ -156,6 +156,7 @@
156
156
  >
157
157
  <vcs-expansion-panel
158
158
  v-for="(group, i) in groups"
159
+ :data-group-name="group.name"
159
160
  :key="i"
160
161
  :value="group.name"
161
162
  :disabled="group.disabled"
@@ -93,6 +93,7 @@
93
93
  :disabled="item.disabled"
94
94
  v-bind="$attrs"
95
95
  class="vcs-list-item-component"
96
+ :data-list-item-name="item.name"
96
97
  >
97
98
  <template #prepend="scope">
98
99
  <slot name="prepend" v-bind="scope">
@@ -1,5 +1,9 @@
1
1
  <template>
2
- <div class="vcs-tree-node" v-if="matchFilter">
2
+ <div
3
+ class="vcs-tree-node"
4
+ v-if="matchFilter"
5
+ :data-tree-item-name="item.name"
6
+ >
3
7
  <v-row
4
8
  no-gutters
5
9
  class="treenode flex-nowrap text-truncate"
@@ -114,7 +114,10 @@
114
114
  [() => props.items, () => props.openAll],
115
115
  () => {
116
116
  if (props.openAll) {
117
- localOpenedItems.value = [...props.items.map((item) => item.name)];
117
+ const newItems = props.items
118
+ .map((item) => item.name)
119
+ .filter((name) => !localOpenedItems.value.includes(name));
120
+ localOpenedItems.value.push(...newItems);
118
121
  }
119
122
  },
120
123
  {
@@ -5,6 +5,7 @@
5
5
  :model-value="!!modelValue"
6
6
  @update:modelValue="handleCheckbox"
7
7
  :disabled="disabled"
8
+ class="pr-1"
8
9
  />
9
10
  <v-menu
10
11
  :close-on-content-click="false"
@@ -73,7 +74,7 @@
73
74
  * @vue-prop {boolean} [disabled=false] - Disable the input
74
75
  */
75
76
  export default {
76
- name: 'MenuWrapper',
77
+ name: 'StyleMenuWrapper',
77
78
  components: {
78
79
  VSheet,
79
80
  VMenu,
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <MenuWrapper
2
+ <StyleMenuWrapper
3
3
  class="vcs-fill-menu"
4
4
  :value-fallback="{ color: [255, 255, 255, 1] }"
5
5
  name="components.style.fill"
@@ -11,7 +11,7 @@
11
11
  <template #content>
12
12
  <VcsFillSelector v-bind="{ ...$attrs, ...$props }" />
13
13
  </template>
14
- </MenuWrapper>
14
+ </StyleMenuWrapper>
15
15
  </template>
16
16
 
17
17
  <script>
@@ -19,7 +19,7 @@
19
19
  import { VSheet } from 'vuetify/components';
20
20
  import { useProxiedAtomicModel } from '../modelHelper.js';
21
21
  import VcsFillSelector from './VcsFillSelector.vue';
22
- import MenuWrapper from './MenuWrapper.vue';
22
+ import StyleMenuWrapper from './StyleMenuWrapper.vue';
23
23
  import { useColorObject, rgbaObjectToString } from './composables.js';
24
24
 
25
25
  /**
@@ -33,7 +33,7 @@
33
33
  components: {
34
34
  VSheet,
35
35
  VcsFillSelector,
36
- MenuWrapper,
36
+ StyleMenuWrapper,
37
37
  },
38
38
  props: {
39
39
  modelValue: {
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <MenuWrapper
2
+ <StyleMenuWrapper
3
3
  class="vcs-image-menu"
4
4
  :value-fallback="{
5
5
  radius: 10,
@@ -21,13 +21,13 @@
21
21
  class="pb-2"
22
22
  />
23
23
  </template>
24
- </MenuWrapper>
24
+ </StyleMenuWrapper>
25
25
  </template>
26
26
 
27
27
  <script>
28
28
  import { onMounted, ref, watch } from 'vue';
29
29
  import { useProxiedAtomicModel } from '../modelHelper.js';
30
- import MenuWrapper from './MenuWrapper.vue';
30
+ import StyleMenuWrapper from './StyleMenuWrapper.vue';
31
31
  import VcsImageSelector, { drawImageStyle } from './VcsImageSelector.vue';
32
32
 
33
33
  /**
@@ -37,7 +37,7 @@
37
37
  export default {
38
38
  name: 'VcsImageMenu',
39
39
  components: {
40
- MenuWrapper,
40
+ StyleMenuWrapper,
41
41
  VcsImageSelector,
42
42
  },
43
43
  props: {
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <MenuWrapper
2
+ <StyleMenuWrapper
3
3
  class="vcs-stroke-menu"
4
4
  :value-fallback="{ color: [0, 0, 0, 1], width: 1 }"
5
5
  name="components.style.stroke"
@@ -18,7 +18,7 @@
18
18
  <template #content>
19
19
  <VcsStrokeSelector v-bind="{ ...$attrs, ...$props }" />
20
20
  </template>
21
- </MenuWrapper>
21
+ </StyleMenuWrapper>
22
22
  </template>
23
23
 
24
24
  <script>
@@ -26,7 +26,7 @@
26
26
  import { VSheet } from 'vuetify/components';
27
27
  import { useProxiedAtomicModel } from '../modelHelper.js';
28
28
  import VcsStrokeSelector from './VcsStrokeSelector.vue';
29
- import MenuWrapper from './MenuWrapper.vue';
29
+ import StyleMenuWrapper from './StyleMenuWrapper.vue';
30
30
  import { useColorObject, rgbaObjectToString } from './composables.js';
31
31
 
32
32
  /**
@@ -40,7 +40,7 @@
40
40
  components: {
41
41
  VSheet,
42
42
  VcsStrokeSelector,
43
- MenuWrapper,
43
+ StyleMenuWrapper,
44
44
  },
45
45
  props: {
46
46
  modelValue: {
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <MenuWrapper
2
+ <StyleMenuWrapper
3
3
  class="vcs-text-menu"
4
4
  :value-fallback="fallbackStyle"
5
5
  name="components.style.text"
@@ -13,14 +13,14 @@
13
13
  <template #content>
14
14
  <VcsTextSelector v-bind="{ ...$attrs, ...$props }" />
15
15
  </template>
16
- </MenuWrapper>
16
+ </StyleMenuWrapper>
17
17
  </template>
18
18
 
19
19
  <script>
20
20
  import { computed } from 'vue';
21
21
  import { useProxiedAtomicModel } from '../modelHelper.js';
22
22
  import VcsTextSelector from './VcsTextSelector.vue';
23
- import MenuWrapper from './MenuWrapper.vue';
23
+ import StyleMenuWrapper from './StyleMenuWrapper.vue';
24
24
  import { rgbaObjectToString, useColorObject } from './composables.js';
25
25
 
26
26
  export const fallbackStyle = {
@@ -38,7 +38,7 @@
38
38
  export default {
39
39
  name: 'VcsTextMenu',
40
40
  components: {
41
- MenuWrapper,
41
+ StyleMenuWrapper,
42
42
  VcsTextSelector,
43
43
  },
44
44
  props: {
@@ -16,12 +16,10 @@
16
16
  </template>
17
17
 
18
18
  <script>
19
- import { inject, ref, watch } from 'vue';
19
+ import { inject, computed } from 'vue';
20
20
  import { VSheet } from 'vuetify/components';
21
21
  import VcsTreeview from '../components/lists/VcsTreeview.vue';
22
22
 
23
- /** The open state Symbol of the ContentTree */
24
- export const openStateMapSymbol = Symbol('openStateMap');
25
23
  /**
26
24
  * @description
27
25
  * Implements Treeview and shows content tree
@@ -37,52 +35,16 @@
37
35
  },
38
36
  setup(props) {
39
37
  const app = inject('vcsApp');
40
- const open = ref(app.contentTree.getTreeOpenState(props.windowState.id));
41
- const tree = app.contentTree.getComputedVisibleTree(props.windowState.id);
42
-
43
- function getWithVisibleChildren(item) {
44
- return [
45
- item.name,
46
- ...(item.visibleChildren
47
- ?.map((c) => getWithVisibleChildren(c))
48
- ?.flat() ?? []),
49
- ];
50
- }
51
38
 
52
- if (!app.contentTree[openStateMapSymbol]) {
53
- app.contentTree[openStateMapSymbol] = new Map();
54
- }
55
- /**
56
- * @type {Map<string, string[]>}
57
- */
58
- const openStateMap = app.contentTree[openStateMapSymbol];
59
- // watch for new visible children, which should start init open
60
- watch(
61
- tree,
62
- (value, oldValue) => {
63
- if (openStateMap.has(app.maps.activeMap?.name)) {
64
- open.value = openStateMap.get(app.maps.activeMap?.name);
65
- } else {
66
- const items = [...app.contentTree]
67
- .filter((i) => i.initOpen && i.getTreeViewItem().visible)
68
- .map(({ name }) => name);
69
- const oldValues = oldValue
70
- ? oldValue.map(getWithVisibleChildren).flat()
71
- : [];
72
- const changed = items.filter(
73
- (name) => !oldValues.includes(name) && !open.value.includes(name),
74
- );
75
- open.value.push(...changed);
76
- }
39
+ const open = computed({
40
+ get: () => app.contentTree.getTreeOpenState(props.windowState.id),
41
+ set: (value) => {
42
+ app.contentTree
43
+ .getTreeOpenState(props.windowState.id)
44
+ .splice(0, Infinity, ...value);
77
45
  },
78
- { immediate: true },
79
- );
80
-
81
- watch(open, () => {
82
- if (app.maps.activeMap) {
83
- openStateMap.set(app.maps.activeMap.name, [...open.value]);
84
- }
85
46
  });
47
+ const tree = app.contentTree.getComputedVisibleTree(props.windowState.id);
86
48
 
87
49
  return {
88
50
  tree,
@@ -1,5 +1,3 @@
1
- /** The open state Symbol of the ContentTree */
2
- export const openStateMapSymbol: unique symbol;
3
1
  declare const _default: import("vue").DefineComponent<{
4
2
  windowState: {
5
3
  type: ObjectConstructor;
@@ -7,7 +5,7 @@ declare const _default: import("vue").DefineComponent<{
7
5
  };
8
6
  }, {
9
7
  tree: any;
10
- open: import("vue").Ref<any>;
8
+ open: import("vue").WritableComputedRef<any>;
11
9
  }, any, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
12
10
  windowState: {
13
11
  type: ObjectConstructor;
@@ -78,6 +78,12 @@ declare class ContentTreeCollection extends IndexedCollection<ContentTreeItem> {
78
78
  * @private
79
79
  */
80
80
  private _setTreeView;
81
+ /**
82
+ * @param {ContentTreeItem} item
83
+ * @returns {string}
84
+ * @private
85
+ */
86
+ private _getSubtreeIdForItem;
81
87
  /**
82
88
  * Returns all managed subtrees. Entries are not persisted and will change, if the trees get recalculated.
83
89
  * @type {import("vue").Ref<Map<string, import("./contentTreeItem.js").TreeViewItem>>}
@@ -107,6 +113,7 @@ declare class ContentTreeCollection extends IndexedCollection<ContentTreeItem> {
107
113
  */
108
114
  getChildrenForSubTree(id: string): Array<ContentTreeItem>;
109
115
  /**
116
+ * This returns a proxy to the subtrees open state. You should mutate this array in place.
110
117
  * @param {string} id
111
118
  * @returns {Array<string>}
112
119
  */
@@ -99,6 +99,10 @@ class ContentTreeCollection extends IndexedCollection {
99
99
  child.name,
100
100
  child.weightChanged.addEventListener(recreateTree),
101
101
  );
102
+ if (child.initOpen) {
103
+ const subTreeId = this._getSubtreeIdForItem(child);
104
+ this.getTreeOpenState(subTreeId).push(child.name);
105
+ }
102
106
  }),
103
107
  this.removed.addEventListener((child) => {
104
108
  recreateTree();
@@ -287,25 +291,42 @@ class ContentTreeCollection extends IndexedCollection {
287
291
  return tree.name;
288
292
  }),
289
293
  );
294
+
290
295
  subTrees.forEach((subTree) => {
291
- if (!this._app.navbarManager.has(subTree.name) || resetSubtreeButtons) {
296
+ if (!this._subTreeViewItems.value.has(subTree.name)) {
292
297
  this._subTreeViewItems.value.set(subTree.name, subTree);
293
- if (!this._app.uiConfig.config.hideContentTree) {
298
+ }
299
+
300
+ if (!this._app.uiConfig.config.hideContentTree) {
301
+ if (!this._app.navbarManager.has(subTree.name) || resetSubtreeButtons) {
294
302
  this._subTreeListeners.get(subTree.name)?.();
295
303
  this._subTreeListeners.set(
296
304
  subTree.name,
297
305
  this._createSubtreeActionButton(subTree),
298
306
  );
299
- }
300
- } else {
301
- const buttonComponent = this._app.navbarManager.get(subTree.name);
302
- if (buttonComponent.weight !== subTree[subTreeItemWeight]) {
303
- buttonComponent.weight = subTree[subTreeItemWeight];
307
+ } else {
308
+ const buttonComponent = this._app.navbarManager.get(subTree.name);
309
+ if (buttonComponent.weight !== subTree[subTreeItemWeight]) {
310
+ buttonComponent.weight = subTree[subTreeItemWeight];
311
+ }
304
312
  }
305
313
  }
306
314
  });
307
315
  }
308
316
 
317
+ /**
318
+ * @param {ContentTreeItem} item
319
+ * @returns {string}
320
+ * @private
321
+ */
322
+ _getSubtreeIdForItem(item) {
323
+ const [parent] = item.name.split('.');
324
+ if (this._subTreeViewItems.value.has(parent)) {
325
+ return parent;
326
+ }
327
+ return defaultContentTreeComponentId;
328
+ }
329
+
309
330
  /**
310
331
  * Returns all managed subtrees. Entries are not persisted and will change, if the trees get recalculated.
311
332
  * @type {import("vue").Ref<Map<string, import("./contentTreeItem.js").TreeViewItem>>}
@@ -376,6 +397,7 @@ class ContentTreeCollection extends IndexedCollection {
376
397
  }
377
398
 
378
399
  /**
400
+ * This returns a proxy to the subtrees open state. You should mutate this array in place.
379
401
  * @param {string} id
380
402
  * @returns {Array<string>}
381
403
  */
@@ -383,9 +405,7 @@ class ContentTreeCollection extends IndexedCollection {
383
405
  const subTree = this._getSubTree(id);
384
406
  if (subTree) {
385
407
  if (!subTree[subTreeOpenStateSymbol]) {
386
- subTree[subTreeOpenStateSymbol] = this.getChildrenForSubTree(id)
387
- .filter((i) => i.initOpen)
388
- .map((i) => i.name);
408
+ subTree[subTreeOpenStateSymbol] = [];
389
409
  }
390
410
  return subTree[subTreeOpenStateSymbol];
391
411
  }
@@ -99,14 +99,14 @@ declare class ContentTreeItem {
99
99
  static get className(): string;
100
100
  /**
101
101
  * @param {ContentTreeItemOptions} options
102
- * @param {import("@src/vcsUiApp.js").default} app
102
+ * @param {import("../vcsUiApp.js").default} app
103
103
  */
104
- constructor(options: ContentTreeItemOptions, app: import("@src/vcsUiApp.js").default);
104
+ constructor(options: ContentTreeItemOptions, app: import("../vcsUiApp.js").default);
105
105
  /**
106
106
  * @protected
107
- * @type {import("@src/vcsUiApp.js").default}
107
+ * @type {import("../vcsUiApp.js").default}
108
108
  */
109
- protected _app: import("@src/vcsUiApp.js").default;
109
+ protected _app: import("../vcsUiApp.js").default;
110
110
  /**
111
111
  * @type {import("vue").ShallowRef<Array<import("../actions/actionHelper.js").VcsAction>>}
112
112
  * @private
@@ -65,12 +65,12 @@ class ContentTreeItem {
65
65
 
66
66
  /**
67
67
  * @param {ContentTreeItemOptions} options
68
- * @param {import("@src/vcsUiApp.js").default} app
68
+ * @param {import("../vcsUiApp.js").default} app
69
69
  */
70
70
  constructor(options, app) {
71
71
  /**
72
72
  * @protected
73
- * @type {import("@src/vcsUiApp.js").default}
73
+ * @type {import("../vcsUiApp.js").default}
74
74
  */
75
75
  this._app = app;
76
76
 
@@ -12,6 +12,11 @@ export type GroupContentTreeItemOptions = import("./contentTreeItem.js").Content
12
12
  * @extends {ContentTreeItem}
13
13
  */
14
14
  declare class GroupContentTreeItem extends ContentTreeItem {
15
+ /**
16
+ * @param {GroupContentTreeItemOptions} options
17
+ * @param {import("@src/vcsUiApp.js").default} app
18
+ */
19
+ constructor(options: GroupContentTreeItemOptions, app: import("@src/vcsUiApp.js").default);
15
20
  /**
16
21
  * @type {boolean}
17
22
  * @private
@@ -24,7 +24,7 @@ class GroupContentTreeItem extends ContentTreeItem {
24
24
  }
25
25
 
26
26
  /**
27
- * @param {import("./contentTreeItem.js").ContentTreeItemOptions} options
27
+ * @param {GroupContentTreeItemOptions} options
28
28
  * @param {import("@src/vcsUiApp.js").default} app
29
29
  */
30
30
  constructor(options, app) {
@@ -35,7 +35,7 @@ export function setViewpointAction(item, app, viewpoint) {
35
35
  const action = createGoToViewpointAction(
36
36
  {
37
37
  name,
38
- icon: 'mdi-crosshairs-gps',
38
+ icon: 'mdi-target',
39
39
  },
40
40
  viewpoint,
41
41
  app.viewpoints,
@@ -1,10 +1,31 @@
1
1
  export default NodeContentTreeItem;
2
+ export type NodeContentTreeItemOptions = import("./contentTreeItem.js").ContentTreeItemOptions & {
3
+ disableIfChildrenDisabled?: boolean;
4
+ };
5
+ /**
6
+ * @typedef {import("./contentTreeItem.js").ContentTreeItemOptions & { disableIfChildrenDisabled?: boolean }} NodeContentTreeItemOptions
7
+ * @property {boolean} [disableIfChildrenDisabled=false] - optional flag to disable the contentTreeItem if all children are disabled.
8
+ */
2
9
  /**
3
10
  * A group item which has _no click handler_
4
11
  * @class
5
12
  * @extends {ContentTreeItem}
6
13
  */
7
14
  declare class NodeContentTreeItem extends ContentTreeItem {
15
+ /**
16
+ * @param {NodeContentTreeItemOptions} options
17
+ * @param {import("@src/vcsUiApp.js").default} app
18
+ */
19
+ constructor(options: NodeContentTreeItemOptions, app: import("@src/vcsUiApp.js").default);
20
+ /**
21
+ * @type {boolean}
22
+ * @private
23
+ */
24
+ private _disableIfChildrenDisabled;
8
25
  _childWatcher: import("vue").WatchStopHandle;
26
+ /**
27
+ * @returns {NodeContentTreeItemOptions}
28
+ */
29
+ toJSON(): NodeContentTreeItemOptions;
9
30
  }
10
31
  import ContentTreeItem from './contentTreeItem.js';
@@ -1,8 +1,14 @@
1
1
  import { watch } from 'vue';
2
+ import { parseBoolean } from '@vcsuite/parsers';
2
3
  import ContentTreeItem, {
3
4
  contentTreeClassRegistry,
4
5
  } from './contentTreeItem.js';
5
6
 
7
+ /**
8
+ * @typedef {import("./contentTreeItem.js").ContentTreeItemOptions & { disableIfChildrenDisabled?: boolean }} NodeContentTreeItemOptions
9
+ * @property {boolean} [disableIfChildrenDisabled=false] - optional flag to disable the contentTreeItem if all children are disabled.
10
+ */
11
+
6
12
  /**
7
13
  * A group item which has _no click handler_
8
14
  * @class
@@ -17,7 +23,7 @@ class NodeContentTreeItem extends ContentTreeItem {
17
23
  }
18
24
 
19
25
  /**
20
- * @param {import("./contentTreeItem.js").ContentTreeItemOptions} options
26
+ * @param {NodeContentTreeItemOptions} options
21
27
  * @param {import("@src/vcsUiApp.js").default} app
22
28
  */
23
29
  constructor(options, app) {
@@ -25,16 +31,39 @@ class NodeContentTreeItem extends ContentTreeItem {
25
31
 
26
32
  this.clickable = false;
27
33
 
34
+ /**
35
+ * @type {boolean}
36
+ * @private
37
+ */
38
+ this._disableIfChildrenDisabled = parseBoolean(
39
+ options.disableIfChildrenDisabled,
40
+ false,
41
+ );
42
+
28
43
  this._childWatcher = watch(
29
44
  this._children,
30
45
  () => {
31
46
  const children = this._children.value;
32
47
  this.visible = children.some((c) => c.visible);
48
+ if (this._disableIfChildrenDisabled) {
49
+ this.disabled = children.every((c) => c.disabled);
50
+ }
33
51
  },
34
- { deep: true },
52
+ { deep: true, immediate: true },
35
53
  );
36
54
  }
37
55
 
56
+ /**
57
+ * @returns {NodeContentTreeItemOptions}
58
+ */
59
+ toJSON() {
60
+ const config = super.toJSON();
61
+ if (this._disableIfChildrenDisabled) {
62
+ config.disableIfChildrenDisabled = this._disableIfChildrenDisabled;
63
+ }
64
+ return config;
65
+ }
66
+
38
67
  destroy() {
39
68
  this._childWatcher();
40
69
  super.destroy();