@vcmap/ui 6.3.11 → 6.3.13

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 (77) hide show
  1. package/build/build.js +5 -0
  2. package/build/staging/Dockerfile +2 -0
  3. package/config/base.config.json +207 -404
  4. package/config/clipping.config.json +16 -80
  5. package/config/cluster.config.json +2 -2
  6. package/config/components-show-case.config.json +1 -1
  7. package/config/concepts-show-case.config.json +1 -1
  8. package/config/dev/geojson.config.json +491 -0
  9. package/config/{i3s.config.json → dev/i3s.config.json} +78 -150
  10. package/config/{mapbox.config.json → dev/mapbox.config.json} +24 -58
  11. package/config/dev/vector-tile.config.json +196 -0
  12. package/config/{wms-layers.config.json → dev/wms.config.json} +22 -39
  13. package/config/dev.config.json +91 -887
  14. package/config/geofence.config.json +2 -4
  15. package/config/graph-feature-info.config.json +84 -0
  16. package/config/guided-tour.config.json +2 -2
  17. package/config/hello-world.config.json +1 -1
  18. package/config/projects.config.json +19 -20
  19. package/config/splashscreen.config.json +1 -1
  20. package/config/theming.config.json +1 -1
  21. package/config/www/city-planning.config.json +215 -0
  22. package/config/www/climate-environment.config.json +1244 -0
  23. package/config/www/iot-sensors.config.json +440 -0
  24. package/config/www/soest.config.json +632 -0
  25. package/config/www.config.json +1352 -1722
  26. package/dist/.htaccess +5 -0
  27. package/dist/assets/{cesium-09c8c37e.js → cesium-d2fefdc2.js} +25018 -24401
  28. package/dist/assets/cesium.js +1 -1
  29. package/dist/assets/{core-f093e6ba.js → core-be181847.js} +1138 -1138
  30. package/dist/assets/core-workers/panoramaImageWorker.js +1 -1
  31. package/dist/assets/core.js +1 -1
  32. package/dist/assets/ol.js +1 -1
  33. package/dist/assets/ui-8f8426e4.css +1 -0
  34. package/dist/assets/{ui-da598216.js → ui-8f8426e4.js} +13290 -12542
  35. package/dist/assets/ui.js +1 -1
  36. package/dist/assets/vue.js +1 -1
  37. package/dist/assets/{vuetify-1429f242.js → vuetify-8df23ea4.js} +1 -1
  38. package/dist/assets/vuetify.js +1 -1
  39. package/package.json +2 -2
  40. package/plugins/@vcmap-show-case/extent-example/src/ExtentExample.vue +16 -2
  41. package/plugins/@vcmap-show-case/icons-example/src/AllIconsComponent.vue +7 -5
  42. package/plugins/@vcmap-show-case/notifier-tester/src/NotifierTester.vue +5 -6
  43. package/plugins/@vcmap-show-case/project-selector/README.md +18 -7
  44. package/plugins/@vcmap-show-case/project-selector/src/ProjectSelectorComponent.vue +9 -0
  45. package/plugins/@vcmap-show-case/project-selector/src/index.js +87 -27
  46. package/plugins/@vcmap-show-case/simple-graph/src/SimpleGraphComponent.vue +10 -3
  47. package/plugins/@vcmap-show-case/simple-graph/src/index.js +10 -1
  48. package/plugins/@vcmap-show-case/switch-map-callback-example/README.md +1 -1
  49. package/plugins/package.json +4 -3
  50. package/src/application/VcsApp.vue +3 -7
  51. package/src/application/attributionsHelper.d.ts +27 -5
  52. package/src/application/attributionsHelper.js +116 -58
  53. package/src/components/lists/VcsGroupedList.vue +29 -1
  54. package/src/components/lists/VcsList.vue +35 -4
  55. package/src/components/lists/VcsList.vue.d.ts +2 -0
  56. package/src/components/lists/VcsTreeview.vue +63 -31
  57. package/src/components/lists/VcsTreeview.vue.d.ts +9 -0
  58. package/src/components/plugins/VcsWorkspaceWrapper.vue +6 -0
  59. package/src/components/plugins/VcsWorkspaceWrapper.vue.d.ts +9 -0
  60. package/src/contentTree/LayerTree.vue +13 -2
  61. package/src/featureInfo/ClusterFeatureComponent.vue +1 -0
  62. package/src/featureInfo/abstractFeatureInfoView.js +13 -9
  63. package/src/manager/window/WindowComponent.vue +5 -1
  64. package/src/vcsUiApp.d.ts +19 -7
  65. package/src/vcsUiApp.js +17 -0
  66. package/config/aerowest.config.json +0 -386
  67. package/config/analysis.config.json +0 -17
  68. package/config/dynamic-layer.config.json +0 -33
  69. package/config/graphFeatureInfo.config.json +0 -141
  70. package/config/pano.config.json +0 -726
  71. package/config/solar.config.json +0 -66
  72. package/config/vectorTile.config.json +0 -112
  73. package/dist/assets/ui-da598216.css +0 -1
  74. /package/dist/assets/core-workers/{panoramaImageWorker.js-e1e34b3c.js → panoramaImageWorker.js-eecf04e1.js} +0 -0
  75. /package/dist/assets/{ol-5a959f4c.js → ol-3438e1c5.js} +0 -0
  76. /package/dist/assets/{vue-20dd2b78.js → vue-dd509a16.js} +0 -0
  77. /package/dist/assets/{vuetify-1429f242.css → vuetify-8df23ea4.css} +0 -0
@@ -1,4 +1,4 @@
1
- import { reactive } from 'vue';
1
+ import { reactive, watch } from 'vue';
2
2
  import { ObliqueMap } from '@vcmap/core';
3
3
 
4
4
  /**
@@ -16,6 +16,14 @@ import { ObliqueMap } from '@vcmap/core';
16
16
  * @property {AttributionOptions|Array<AttributionOptions>} attributions - attributions of a map, layer or oblique collection
17
17
  */
18
18
 
19
+ /**
20
+ * @typedef {Object} Attributions
21
+ * @property {import("vue").UnwrapRef<Array<AttributionEntry>>} entries - reactive array of attribution entries
22
+ * @property {function(import("@vcmap/core").VcsMap, string|import('../pluginHelper.js').vcsAppSymbol): function():void} registerMap - function to register a map and its layer collection for an owner, returns a function to unregister it
23
+ * @property {function(string|import('../pluginHelper.js').vcsAppSymbol):void} removeOwner - unregister all maps registered by an owner
24
+ * @property {function():void} destroy - function to clear all listeners and clean up attributions
25
+ */
26
+
19
27
  /**
20
28
  * merges attribution entries of same providers
21
29
  * @param {Array<AttributionEntry>} entries
@@ -64,17 +72,17 @@ export function mergeAttributions(entries) {
64
72
  * Listens to state changes of maps, layers and oblique collections and synchronizes the entries array correspondingly.
65
73
  * Returns a destroy function to clear listeners.
66
74
  * @param {import("../vcsUiApp.js").default} app
67
- * @returns {{entries: import("vue").UnwrapRef<Array<AttributionEntry>>, destroy: function():void}}
75
+ * @returns {Attributions}
68
76
  */
69
77
  export function getAttributions(app) {
70
78
  /**
71
79
  * @type {import("vue").UnwrapRef<Array<AttributionEntry>>}
72
80
  */
73
81
  const entries = reactive([]);
74
- /**
75
- * @type {function():void}
76
- */
77
- let obliqueListener = () => {};
82
+ /** @type {Map<import("@vcmap/core").VcsMap, { collection: import("@vcmap/core").LayerCollection, registrations: Map<function():void, string|import('../pluginHelper.js').vcsAppSymbol>, removeListeners: Array<function():void> }>} */
83
+ const registeredMaps = new Map();
84
+ /** @type {Map<import("@vcmap/core").ObliqueMap, function():void>} */
85
+ const obliqueListeners = new Map();
78
86
 
79
87
  /**
80
88
  * Adds an entry for an object using a combination of the object's className and name as key.
@@ -99,73 +107,123 @@ export function getAttributions(app) {
99
107
  }
100
108
  }
101
109
 
102
- /**
103
- * @param {import("@vcmap/core").VcsMap|import("@vcmap/core").Layer|import("@vcmap/core").ObliqueCollection} object
104
- */
105
- function removeAttributions(object) {
106
- const idx = entries.findIndex(
107
- (e) => e.key === `${object.className}_${object.name}`,
108
- );
109
- if (idx >= 0) {
110
- entries.splice(idx, 1);
110
+ function updateAttributions() {
111
+ entries.splice(0);
112
+ /** @type {Map<import("@vcmap/core").VcsMap, import("@vcmap/core").LayerCollection>} */
113
+ const visibleMaps = new Map();
114
+ if (app.maps.activeMap) {
115
+ visibleMaps.set(app.maps.activeMap, app.maps.activeMap.layerCollection);
111
116
  }
117
+ if (app.overviewMap.active) {
118
+ const overviewMap = app.overviewMap.map;
119
+ visibleMaps.set(overviewMap, overviewMap.layerCollection);
120
+ }
121
+ registeredMaps.forEach(({ collection }, map) => {
122
+ if (map.active) {
123
+ visibleMaps.set(map, collection);
124
+ }
125
+ });
126
+ obliqueListeners.forEach((removeListener, map) => {
127
+ if (!visibleMaps.has(map)) {
128
+ removeListener();
129
+ obliqueListeners.delete(map);
130
+ }
131
+ });
132
+ visibleMaps.forEach((collection, map) => {
133
+ if (map.active) {
134
+ addAttributions(map);
135
+ }
136
+ [...collection].forEach((layer) => {
137
+ if (layer.active && layer.isSupported(map)) {
138
+ addAttributions(layer);
139
+ }
140
+ });
141
+ if (map instanceof ObliqueMap) {
142
+ if (map.collection) {
143
+ addAttributions(map.collection);
144
+ }
145
+ if (!obliqueListeners.has(map)) {
146
+ obliqueListeners.set(
147
+ map,
148
+ map.collectionChanged.addEventListener(updateAttributions),
149
+ );
150
+ }
151
+ }
152
+ });
112
153
  }
113
154
 
155
+ const listeners = [
156
+ app.maps.mapActivated.addEventListener(updateAttributions),
157
+ app.layers.stateChanged.addEventListener(updateAttributions),
158
+ app.layers.added.addEventListener(updateAttributions),
159
+ app.layers.removed.addEventListener(updateAttributions),
160
+ app.maps.removed.addEventListener(updateAttributions),
161
+ watch(app.overviewMap.currentState, updateAttributions),
162
+ ];
163
+
164
+ updateAttributions();
165
+
114
166
  /**
115
- * adds or removes a AttributionEntry for layers or maps
116
- * @param {import("@vcmap/core").VcsMap|import("@vcmap/core").Layer} object
167
+ * @param {import("@vcmap/core").VcsMap} map
168
+ * @param {string|import('../pluginHelper.js').vcsAppSymbol} owner
169
+ * @returns {function():void}
117
170
  */
118
- function syncAttributions(object) {
119
- if (object?.properties?.attributions === undefined) {
120
- return;
121
- }
122
- if (object.active) {
123
- addAttributions(object);
124
- } else {
125
- removeAttributions(object);
171
+ function registerMap(map, owner) {
172
+ if (!registeredMaps.has(map)) {
173
+ registeredMaps.set(map, {
174
+ collection: map.layerCollection,
175
+ registrations: new Map(),
176
+ removeListeners: [
177
+ map.stateChanged.addEventListener(updateAttributions),
178
+ map.layerCollection.added.addEventListener(updateAttributions),
179
+ map.layerCollection.removed.addEventListener(updateAttributions),
180
+ ],
181
+ });
126
182
  }
183
+ const unregister = () => {
184
+ const current = registeredMaps.get(map);
185
+ if (!current?.registrations.delete(unregister)) {
186
+ return;
187
+ }
188
+ if (current.registrations.size === 0) {
189
+ current.removeListeners.forEach((removeListener) => {
190
+ removeListener();
191
+ });
192
+ registeredMaps.delete(map);
193
+ }
194
+ updateAttributions();
195
+ };
196
+ registeredMaps.get(map).registrations.set(unregister, owner);
197
+ updateAttributions();
198
+ return unregister;
127
199
  }
128
200
 
129
201
  /**
130
- *
131
- * @param {import("@vcmap/core").VcsMap} map
202
+ * @param {string|import('../pluginHelper.js').vcsAppSymbol} owner
132
203
  */
133
- function initAttributions(map) {
134
- if (!map) {
135
- return;
136
- }
137
- obliqueListener();
138
- entries.splice(0);
139
- syncAttributions(map);
140
- [...map.layerCollection].forEach((layer) => {
141
- if (layer.isSupported(map)) {
142
- syncAttributions(layer);
143
- }
204
+ function removeOwner(owner) {
205
+ registeredMaps.forEach(({ registrations }) => {
206
+ registrations.forEach((registrationOwner, unregister) => {
207
+ if (registrationOwner === owner) {
208
+ unregister();
209
+ }
210
+ });
144
211
  });
145
- if (map instanceof ObliqueMap) {
146
- addAttributions(map.collection);
147
- obliqueListener = map.collectionChanged.addEventListener(
148
- (obliqueCollection) => {
149
- [...app.obliqueCollections].forEach(removeAttributions);
150
- addAttributions(obliqueCollection);
151
- },
152
- );
153
- }
154
212
  }
155
213
 
156
- const listeners = [
157
- app.maps.mapActivated.addEventListener(initAttributions),
158
- app.layers.stateChanged.addEventListener(syncAttributions),
159
- app.layers.removed.addEventListener(removeAttributions),
160
- app.maps.removed.addEventListener(removeAttributions),
161
- ];
162
-
163
- initAttributions(app.maps.activeMap);
164
-
165
214
  const destroy = () => {
166
215
  listeners.forEach((cb) => cb());
167
- obliqueListener();
216
+ obliqueListeners.forEach((removeListener) => {
217
+ removeListener();
218
+ });
219
+ obliqueListeners.clear();
220
+ registeredMaps.forEach(({ removeListeners }) => {
221
+ removeListeners.forEach((removeListener) => {
222
+ removeListener();
223
+ });
224
+ });
225
+ registeredMaps.clear();
168
226
  };
169
227
 
170
- return { entries, destroy };
228
+ return { entries, registerMap, removeOwner, destroy };
171
229
  }
@@ -35,6 +35,7 @@
35
35
  * @vue-prop {boolean} [singleSelect=false]
36
36
  * @vue-prop {Array<import("./VcsListItemComponent.vue").VcsListItem>} [modelValue=[]] - the initial items to be selected.
37
37
  * @vue-prop {boolean} [searchable=false] - if this list can have its items searched.
38
+ * @vue-prop {boolean} [stickySearchbar=false] - Keeps the searchbar at the top of the scroll container.
38
39
  * @vue-prop {function(import("./VcsListItemComponent.vue").VcsListItem, string):boolean} [customFilter] - a function to customize filtering when searching.
39
40
  * @vue-prop {string} [searchbarPlaceholder] - placeholder to render inside the search field
40
41
  */
@@ -73,6 +74,10 @@
73
74
  type: Boolean,
74
75
  default: false,
75
76
  },
77
+ stickySearchbar: {
78
+ type: Boolean,
79
+ default: false,
80
+ },
76
81
  searchbarPlaceholder: {
77
82
  type: String,
78
83
  default: undefined,
@@ -142,7 +147,10 @@
142
147
  </script>
143
148
 
144
149
  <template>
145
- <div class="vcs-grouped-list">
150
+ <div
151
+ class="vcs-grouped-list"
152
+ :class="{ 'sticky-searchbar': stickySearchbar && searchable }"
153
+ >
146
154
  <VcsTreeviewSearchbar
147
155
  v-if="searchable"
148
156
  :placeholder="searchbarPlaceholder"
@@ -178,6 +186,26 @@
178
186
  </template>
179
187
 
180
188
  <style scoped lang="scss">
189
+ .vcs-grouped-list {
190
+ display: block;
191
+ flex: 1 1 auto;
192
+ min-height: 0;
193
+ overflow: auto;
194
+ scrollbar-gutter: stable;
195
+ }
196
+
197
+ .vcs-grouped-list.sticky-searchbar {
198
+ --searchbar-height: calc(var(--v-vcs-font-size) * 2 + 18px);
199
+ margin-top: var(--searchbar-height);
200
+
201
+ > .vcs-treeview-searchbar {
202
+ width: 100%;
203
+ position: absolute !important;
204
+ margin-top: calc(var(--searchbar-height) * -1);
205
+ z-index: 2;
206
+ }
207
+ }
208
+
181
209
  .panels .vcs-expansion-panel:not(:last-child) {
182
210
  border-bottom: 1px solid rgb(var(--v-theme-base-lighten-2));
183
211
  }
@@ -1,5 +1,10 @@
1
1
  <template>
2
- <div class="d-contents vcs-list">
2
+ <div
3
+ class="vcs-list"
4
+ :class="{
5
+ 'sticky-searchbar': stickySearchbar && searchable,
6
+ }"
7
+ >
3
8
  <vcs-treeview-searchbar
4
9
  v-if="searchable"
5
10
  :placeholder="searchbarPlaceholder"
@@ -113,6 +118,7 @@
113
118
  * @vue-prop {boolean} [singleSelect=false]
114
119
  * @vue-prop {Array<import("./VcsListItemComponent.vue").VcsListItem>} [modelValue=[]] - the initial items to be selected.
115
120
  * @vue-prop {boolean} [searchable=false] - if this list can have its items searched.
121
+ * @vue-prop {boolean} [stickySearchbar=false] - Keeps the searchbar at the top of the scroll container.
116
122
  * @vue-prop {function(import("./VcsListItemComponent.vue").VcsListItem, string):boolean} [customFilter] - a function to customize filtering when searching.
117
123
  * @vue-prop {string} [searchbarPlaceholder] - placeholder to render inside the search field
118
124
  * @vue-prop {boolean} [showTitle=true] - show the title component
@@ -162,6 +168,10 @@
162
168
  type: Boolean,
163
169
  default: false,
164
170
  },
171
+ stickySearchbar: {
172
+ type: Boolean,
173
+ default: false,
174
+ },
165
175
  customFilter: {
166
176
  type: Function,
167
177
  default: undefined,
@@ -282,6 +292,30 @@
282
292
  </script>
283
293
 
284
294
  <style lang="scss" scoped>
295
+ .vcs-list {
296
+ display: block;
297
+ flex: 1 1 auto;
298
+ min-height: 0;
299
+ overflow: auto;
300
+ scrollbar-gutter: stable;
301
+ }
302
+
303
+ .vcs-list > .v-list {
304
+ overflow: visible;
305
+ }
306
+
307
+ .vcs-list.sticky-searchbar {
308
+ --searchbar-height: calc(var(--v-vcs-font-size) * 2 + 18px);
309
+ margin-top: var(--searchbar-height);
310
+
311
+ > .vcs-treeview-searchbar {
312
+ width: 100%;
313
+ position: absolute !important;
314
+ margin-top: calc(var(--searchbar-height) * -1);
315
+ z-index: 2;
316
+ }
317
+ }
318
+
285
319
  :deep(.v-list) {
286
320
  .v-list-item__lighten_even:nth-child(even) {
287
321
  background-color: rgb(var(--v-theme-base-lighten-4));
@@ -339,7 +373,4 @@
339
373
  }
340
374
  }
341
375
  }
342
- .d-contents {
343
- display: contents;
344
- }
345
376
  </style>
@@ -7,6 +7,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{}
7
7
  items: unknown[];
8
8
  selectable: boolean;
9
9
  actions: unknown[];
10
+ stickySearchbar: boolean;
10
11
  singleSelect: boolean;
11
12
  searchable: boolean;
12
13
  showTitle: boolean;
@@ -24,6 +25,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<{}
24
25
  readonly items?: unknown[] | undefined;
25
26
  readonly selectable?: boolean | undefined;
26
27
  readonly actions?: unknown[] | undefined;
28
+ readonly stickySearchbar?: boolean | undefined;
27
29
  readonly singleSelect?: boolean | undefined;
28
30
  readonly searchable?: boolean | undefined;
29
31
  readonly showTitle?: boolean | undefined;
@@ -1,5 +1,8 @@
1
1
  <template>
2
- <div class="vcs-treeview">
2
+ <div
3
+ class="vcs-treeview"
4
+ :class="{ 'sticky-searchbar': stickySearchbar && showSearchbar }"
5
+ >
3
6
  <VcsTreeviewSearchbar
4
7
  v-if="showSearchbar"
5
8
  :placeholder="searchbarPlaceholder"
@@ -12,37 +15,39 @@
12
15
  <slot name="search-append" v-bind="searchAppend" />
13
16
  </template>
14
17
  </VcsTreeviewSearchbar>
15
- <div
16
- v-for="(item, index) in items"
17
- :key="item.name"
18
- class="vcs-treeitem"
19
- :class="{ 'mobile-spacing': xs }"
20
- >
21
- <VcsTreeNode
22
- class="root-node"
23
- :class="{
24
- 'vcs-draggable-item': isDraggable,
25
- }"
26
- :item="item"
27
- :path="[index]"
28
- :search="localSearchValue"
29
- v-model:opened="localOpenedItems"
30
- :custom-filter="customFilter"
31
- :open-on-click="openOnClick"
32
- :item-children="itemChildren"
33
- :draggable="isDraggable"
34
- @mousedown.shift="$event.preventDefault()"
35
- @dragstart="dragStart"
36
- @dragover="dragOver"
37
- @dragend="dragEnd"
38
- @drop="drop"
39
- @item-toggled="itemToggled"
40
- @click="itemClicked"
18
+ <div class="treeview-items">
19
+ <div
20
+ v-for="(item, index) in items"
21
+ :key="item.name"
22
+ class="vcs-treeitem"
23
+ :class="{ 'mobile-spacing': xs }"
41
24
  >
42
- <template v-for="slot of forwardSlots" #[slot]="scope">
43
- <slot :name="slot" v-bind="scope ?? {}" />
44
- </template>
45
- </VcsTreeNode>
25
+ <VcsTreeNode
26
+ class="root-node"
27
+ :class="{
28
+ 'vcs-draggable-item': isDraggable,
29
+ }"
30
+ :item="item"
31
+ :path="[index]"
32
+ :search="localSearchValue"
33
+ v-model:opened="localOpenedItems"
34
+ :custom-filter="customFilter"
35
+ :open-on-click="openOnClick"
36
+ :item-children="itemChildren"
37
+ :draggable="isDraggable"
38
+ @mousedown.shift="$event.preventDefault()"
39
+ @dragstart="dragStart"
40
+ @dragover="dragOver"
41
+ @dragend="dragEnd"
42
+ @drop="drop"
43
+ @item-toggled="itemToggled"
44
+ @click="itemClicked"
45
+ >
46
+ <template v-for="slot of forwardSlots" #[slot]="scope">
47
+ <slot :name="slot" v-bind="scope ?? {}" />
48
+ </template>
49
+ </VcsTreeNode>
50
+ </div>
46
51
  </div>
47
52
  </div>
48
53
  </template>
@@ -73,6 +78,7 @@
73
78
  * @vue-prop {boolean} [openOnClick=false] - Whether to open items on title click.
74
79
  * @vue-prop {string} [search=''] - The value used to filter the items.
75
80
  * @vue-prop {boolean} [showSearchbar=false] - Whether there is a searchbar for this treeview.
81
+ * @vue-prop {boolean} [stickySearchbar=false] - Keeps the searchbar at the top of the scroll container.
76
82
  * @vue-prop {string} [searchbarPlaceholder] - Placeholder text for the searchbar, will be translated.
77
83
  * @vue-prop {function(arg0: import("./VcsTreeNode.vue").VcsTreeNodeItem, arg1: string|undefined):boolean} [customFilter] - a function to customize filtering when searching.
78
84
  * @vue-prop {import("./dragHelper.js").DropTargetZonesFunction} [dropTargetZones] - a function to define allowed drop target zones per item.
@@ -116,6 +122,10 @@
116
122
  type: Boolean,
117
123
  default: false,
118
124
  },
125
+ stickySearchbar: {
126
+ type: Boolean,
127
+ default: false,
128
+ },
119
129
  searchbarPlaceholder: {
120
130
  type: String,
121
131
  default: undefined,
@@ -203,6 +213,28 @@
203
213
  </script>
204
214
 
205
215
  <style lang="scss" scoped>
216
+ .vcs-treeview {
217
+ display: block;
218
+ flex: 1 1 auto;
219
+ min-height: 0;
220
+ overflow: auto;
221
+ scrollbar-gutter: stable;
222
+ }
223
+
224
+ .treeview-items {
225
+ display: block;
226
+ }
227
+ .vcs-treeview.sticky-searchbar {
228
+ --searchbar-height: calc(var(--v-vcs-font-size) * 2 + 18px);
229
+ margin-top: var(--searchbar-height);
230
+
231
+ > .vcs-treeview-searchbar {
232
+ width: 100%;
233
+ position: absolute !important;
234
+ margin-top: calc(var(--searchbar-height) * -1);
235
+ z-index: 2;
236
+ }
237
+ }
206
238
  // Hide node component when not rendered (e.g. filtered by search)
207
239
  .vcs-treeitem:not(:has(.vcs-tree-node)) {
208
240
  display: none;
@@ -31,6 +31,10 @@ declare const _default: import("vue").DefineComponent<{
31
31
  type: BooleanConstructor;
32
32
  default: boolean;
33
33
  };
34
+ stickySearchbar: {
35
+ type: BooleanConstructor;
36
+ default: boolean;
37
+ };
34
38
  searchbarPlaceholder: {
35
39
  type: StringConstructor;
36
40
  default: undefined;
@@ -92,6 +96,10 @@ declare const _default: import("vue").DefineComponent<{
92
96
  type: BooleanConstructor;
93
97
  default: boolean;
94
98
  };
99
+ stickySearchbar: {
100
+ type: BooleanConstructor;
101
+ default: boolean;
102
+ };
95
103
  searchbarPlaceholder: {
96
104
  type: StringConstructor;
97
105
  default: undefined;
@@ -122,6 +130,7 @@ declare const _default: import("vue").DefineComponent<{
122
130
  customFilter: Function;
123
131
  openAll: boolean;
124
132
  showSearchbar: boolean;
133
+ stickySearchbar: boolean;
125
134
  searchbarPlaceholder: string;
126
135
  dropTargetZones: Function;
127
136
  }, {}>;
@@ -14,6 +14,7 @@
14
14
  <div class="d-flex gc-2 w-100 justify-end">
15
15
  <slot name="secondaryButton" />
16
16
  <VcsFormButton
17
+ v-if="showNew"
17
18
  variant="filled"
18
19
  :disabled="disableNew"
19
20
  :tooltip="tooltipNew"
@@ -39,6 +40,7 @@
39
40
  * Providing a footer with add and new buttons.
40
41
  * @vue-prop {boolean} [showFooter=true] - Flag to hide the footer.
41
42
  * @vue-prop {boolean} [showAdd=true] - Flag to show an add button in the footer. You need to handle @addClicked in a child component.
43
+ * @vue-prop {boolean} [showNew=true] - Flag to show a new button in the footer.
42
44
  * @vue-prop {boolean} [disableAdd=false] - Flag to render add button disabled.
43
45
  * @vue-prop {boolean} [tooltipAdd='components.addToMyWorkspace'] - Option to change the add button tooltip.
44
46
  * @vue-prop {boolean} [disableNew=false] - Flag to render new button disabled.
@@ -66,6 +68,10 @@
66
68
  type: Boolean,
67
69
  default: true,
68
70
  },
71
+ showNew: {
72
+ type: Boolean,
73
+ default: true,
74
+ },
69
75
  disableAdd: {
70
76
  type: Boolean,
71
77
  default: false,
@@ -7,6 +7,10 @@ declare const _default: import("vue").DefineComponent<{
7
7
  type: BooleanConstructor;
8
8
  default: boolean;
9
9
  };
10
+ showNew: {
11
+ type: BooleanConstructor;
12
+ default: boolean;
13
+ };
10
14
  disableAdd: {
11
15
  type: BooleanConstructor;
12
16
  default: boolean;
@@ -42,6 +46,10 @@ declare const _default: import("vue").DefineComponent<{
42
46
  type: BooleanConstructor;
43
47
  default: boolean;
44
48
  };
49
+ showNew: {
50
+ type: BooleanConstructor;
51
+ default: boolean;
52
+ };
45
53
  disableAdd: {
46
54
  type: BooleanConstructor;
47
55
  default: boolean;
@@ -73,6 +81,7 @@ declare const _default: import("vue").DefineComponent<{
73
81
  actions: unknown[];
74
82
  showFooter: boolean;
75
83
  showAdd: boolean;
84
+ showNew: boolean;
76
85
  disableAdd: boolean;
77
86
  tooltipAdd: string;
78
87
  newButtonTitle: string;
@@ -3,9 +3,10 @@
3
3
  <VcsTreeview
4
4
  v-if="tree && tree.length"
5
5
  :items="tree"
6
- :show-searchbar="true"
7
- v-model:opened="open"
6
+ show-searchbar
7
+ sticky-searchbar
8
8
  open-on-click
9
+ v-model:opened="open"
9
10
  :searchbar-placeholder="'content.search.placeholder'"
10
11
  item-children="visibleChildren"
11
12
  >
@@ -103,3 +104,13 @@
103
104
  },
104
105
  };
105
106
  </script>
107
+
108
+ <style lang="scss" scoped>
109
+ .layer-tree {
110
+ display: flex;
111
+ flex-direction: column;
112
+ flex: 1 1 auto;
113
+ min-height: 0;
114
+ overflow: hidden;
115
+ }
116
+ </style>
@@ -83,6 +83,7 @@
83
83
  v-model="selected"
84
84
  selectable
85
85
  single-select
86
+ sticky-searchbar
86
87
  searchable
87
88
  open-all
88
89
  />
@@ -287,19 +287,20 @@ function getWindowState(app, state, attributes) {
287
287
  }
288
288
 
289
289
  /**
290
- * Recursively searches for parent feature attributes, if __PARENT_FEATURE property is set. Parent feature is searched in the batchTable of the content of the given feature.
290
+ * Recursively searches for parent feature attributes, if __PARENT_FEATURE property is set. Parent feature is searched in the content of the given feature.
291
291
  * @param {import("ol").Feature|import("@vcmap-cesium/engine").Cesium3DTileFeature|import("@vcmap-cesium/engine").Cesium3DTilePointFeature} feature
292
292
  * @param {Array<object>} records - array of parent feature attributes
293
293
  * @returns {Array<object>} record of parent feature ids and their attributes
294
294
  */
295
295
  function getParentFeatureAttributes(feature, records = []) {
296
296
  const parentId = feature.getProperty('__PARENT_FEATURE');
297
- if (parentId !== 'null' && feature.content?.batchTable) {
298
- for (let i = 0; i < feature.content.batchTable.featuresLength; i++) {
299
- const batchTableFeature = feature.content.batchTable.getFeature(i);
300
- if (batchTableFeature.getProperty('id') === parentId) {
301
- records.push(batchTableFeature.getAttributes());
302
- return getParentFeatureAttributes(batchTableFeature, records);
297
+ if (parentId !== undefined && parentId !== 'null' && feature.content) {
298
+ const { content } = feature;
299
+ for (let i = 0; i < content.featuresLength; i++) {
300
+ const contentFeature = content.getFeature(i);
301
+ if (contentFeature.getProperty('id') === parentId) {
302
+ records.push(contentFeature.getAttributes());
303
+ return getParentFeatureAttributes(contentFeature, records);
303
304
  }
304
305
  }
305
306
  }
@@ -442,6 +443,10 @@ class AbstractFeatureInfoView extends VcsObject {
442
443
  */
443
444
  getAttributes(feature) {
444
445
  let attributes = this._getAttributesFromFeature(feature);
446
+ attributes = applyEmptyAttributesFilter(
447
+ attributes,
448
+ this.removeNoDataAttributes,
449
+ );
445
450
  if (this.attributeKeys.length > 0) {
446
451
  attributes = applyAttributeFilter(attributes, this.attributeKeys);
447
452
  }
@@ -452,8 +457,7 @@ class AbstractFeatureInfoView extends VcsObject {
452
457
  applyKeyMapping(attributes, this.keyMapping);
453
458
  }
454
459
  attributes = applyOlcsAttributeFilter(attributes, this.attributeKeys);
455
- attributes = applyDoubleUnderscoreFilter(attributes, this.attributeKeys);
456
- return applyEmptyAttributesFilter(attributes, this.removeNoDataAttributes);
460
+ return applyDoubleUnderscoreFilter(attributes, this.attributeKeys);
457
461
  }
458
462
 
459
463
  /**
@@ -28,7 +28,7 @@
28
28
  </div>
29
29
  <v-divider v-if="!windowState.hideHeader" />
30
30
  <div
31
- class="overflow-x-hidden overflow-y-auto mb-1 d-flex flex-grow-1 flex-column"
31
+ class="window-content overflow-x-hidden overflow-y-auto mb-1 d-flex flex-grow-1 flex-column"
32
32
  :class="{
33
33
  rounded: !isDocked,
34
34
  }"
@@ -169,6 +169,10 @@
169
169
  </script>
170
170
 
171
171
  <style scoped>
172
+ .window-content > :deep(:has(> .sticky-searchbar:only-child)) {
173
+ display: contents;
174
+ }
175
+
172
176
  .marginToTop {
173
177
  margin-top: 2px;
174
178
  }