@vcmap/ui 5.0.0-rc.10 → 5.0.0-rc.11

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 (116) hide show
  1. package/README.md +11 -4
  2. package/build/build.js +0 -3
  3. package/build/buildHelpers.js +0 -1
  4. package/build/buildPreview.js +7 -0
  5. package/config/aerowest.config.json +13 -3
  6. package/config/base.config.json +89 -64
  7. package/config/codes.config.json +397 -0
  8. package/config/dev.config.json +169 -0
  9. package/config/graphFeatureInfo.config.json +100 -0
  10. package/config/www.config.json +1232 -0
  11. package/dist/assets/{cesium.eb5667.js → cesium.e67536.js} +0 -0
  12. package/dist/assets/cesium.js +1 -1
  13. package/dist/assets/core.ebf665.js +4 -0
  14. package/dist/assets/core.js +1 -1
  15. package/dist/assets/{index.4ccd4433.js → index.9b213929.js} +1 -1
  16. package/dist/assets/{ol.ef03b1.js → ol.8bbd50.js} +0 -0
  17. package/dist/assets/ol.js +1 -1
  18. package/dist/assets/ui.fdfe0d.css +1 -0
  19. package/dist/assets/ui.fdfe0d.js +68 -0
  20. package/dist/assets/ui.js +1 -1
  21. package/dist/assets/vue.0bb7c6.js +9 -0
  22. package/dist/assets/vue.js +2 -1
  23. package/dist/assets/{vuetify.401a29.css → vuetify.53300f.css} +1 -1
  24. package/dist/assets/{vuetify.401a29.js → vuetify.53300f.js} +71 -71
  25. package/dist/assets/vuetify.js +2 -2
  26. package/dist/index.html +1 -1
  27. package/index.js +36 -5
  28. package/lib/vue.js +1 -0
  29. package/map.config.json +15 -6
  30. package/package.json +6 -7
  31. package/plugins/@vcmap/create-link/fallbackCreateLink.vue +71 -0
  32. package/plugins/@vcmap/create-link/index.js +83 -0
  33. package/plugins/@vcmap/create-link/package.json +6 -0
  34. package/plugins/@vcmap/pluginExample/index.js +1 -1
  35. package/plugins/@vcmap/pluginExample/pluginExampleComponent.vue +20 -3
  36. package/plugins/@vcmap/project-selector/ProjectSelectorComponent.vue +1 -1
  37. package/plugins/@vcmap/project-selector/index.js +1 -1
  38. package/plugins/@vcmap/project-selector/package.json +1 -2
  39. package/plugins/@vcmap/theme-changer/ThemeChangerComponent.vue +1 -1
  40. package/plugins/@vcmap/theme-changer/index.js +1 -1
  41. package/plugins/@vcmap/theme-changer/package.json +1 -2
  42. package/plugins/categoryTest/Categories.vue +89 -1
  43. package/plugins/categoryTest/Category.vue +1 -1
  44. package/plugins/simple-graph/README.md +51 -0
  45. package/plugins/simple-graph/SimpleGraphComponent.vue +70 -0
  46. package/plugins/simple-graph/index.js +17 -0
  47. package/plugins/simple-graph/package.json +11 -0
  48. package/plugins/simple-graph/simpleGraphView.js +76 -0
  49. package/plugins/test/editor.vue +1 -1
  50. package/plugins/test/index.js +63 -2
  51. package/plugins/test/windowManagerExample.vue +1 -1
  52. package/src/actions/stateRefAction.js +2 -2
  53. package/src/actions/styleSelector.vue +1 -1
  54. package/src/application/Navbar.vue +13 -2
  55. package/src/application/VcsApp.vue +201 -92
  56. package/src/application/VcsMap.vue +1 -1
  57. package/src/application/VcsSettings.vue +1 -1
  58. package/src/application/vcsAppWrapper.vue +1 -0
  59. package/src/components/form-inputs-controls/VcsCheckbox.vue +13 -0
  60. package/src/components/form-inputs-controls/VcsColorPicker.vue +1 -1
  61. package/src/components/form-inputs-controls/VcsRadio.vue +123 -0
  62. package/src/components/form-output/VcsFormattedNumber.vue +1 -1
  63. package/src/components/lists/VcsActionList.vue +13 -4
  64. package/src/components/lists/VcsTreeview.vue +4 -4
  65. package/src/components/lists/VcsTreeviewLeaf.vue +9 -2
  66. package/src/components/lists/VcsTreeviewSearchbar.vue +1 -2
  67. package/src/components/tables/VcsTable.vue +245 -0
  68. package/src/contentTree/LayerTree.vue +1 -1
  69. package/src/contentTree/contentTreeCollection.js +4 -4
  70. package/src/contentTree/contentTreeItem.js +9 -9
  71. package/src/contentTree/groupContentTreeItem.js +1 -1
  72. package/src/contentTree/layerContentTreeItem.js +15 -1
  73. package/src/contentTree/layerGroupContentTreeItem.js +21 -1
  74. package/src/contentTree/nodeContentTreeItem.js +1 -1
  75. package/src/featureInfo/AddressBalloonComponent.vue +47 -0
  76. package/src/featureInfo/BalloonComponent.vue +138 -0
  77. package/src/featureInfo/abstractFeatureInfoView.js +313 -0
  78. package/src/featureInfo/addressBalloonFeatureInfoView.js +118 -0
  79. package/src/featureInfo/balloonFeatureInfoView.js +151 -0
  80. package/src/featureInfo/balloonHelper.js +132 -0
  81. package/src/featureInfo/featureInfo.js +455 -0
  82. package/src/featureInfo/featureInfoInteraction.js +42 -0
  83. package/src/featureInfo/iframeFeatureInfoView.js +95 -0
  84. package/src/featureInfo/tableFeatureInfoView.js +106 -0
  85. package/src/i18n/de.js +16 -0
  86. package/src/i18n/en.js +16 -0
  87. package/src/i18n/i18nCollection.js +17 -0
  88. package/src/manager/buttonManager.js +5 -5
  89. package/src/manager/categoryManager/ComponentsManager.vue +30 -0
  90. package/src/manager/categoryManager/categoryManager.js +500 -0
  91. package/src/manager/contextMenu/contextMenuComponent.vue +43 -0
  92. package/src/manager/contextMenu/contextMenuInteraction.js +42 -0
  93. package/src/manager/contextMenu/contextMenuManager.js +197 -0
  94. package/src/manager/navbarManager.js +8 -8
  95. package/src/manager/toolbox/ToolboxManager.vue +2 -2
  96. package/src/manager/toolbox/toolboxManager.js +7 -3
  97. package/src/manager/window/WindowComponent.vue +1 -1
  98. package/src/manager/window/WindowManager.vue +5 -3
  99. package/src/manager/window/windowManager.js +118 -14
  100. package/src/navigation/mapNavigation.vue +3 -5
  101. package/src/navigation/overviewMap.js +28 -5
  102. package/src/navigation/vcsCompass.vue +1 -1
  103. package/src/setup.js +0 -2
  104. package/src/state.js +256 -0
  105. package/src/styles/_theming.scss +0 -5
  106. package/src/uiConfig.js +79 -0
  107. package/src/vcsUiApp.js +210 -20
  108. package/src/vuePlugins/vuetify.js +14 -4
  109. package/config/berlin.config.json +0 -510
  110. package/dist/assets/core.216494.js +0 -4
  111. package/dist/assets/ui.99a1a7.css +0 -1
  112. package/dist/assets/ui.99a1a7.js +0 -70
  113. package/dist/assets/vue-composition-api.c5aca1.js +0 -14
  114. package/dist/assets/vue-composition-api.js +0 -2
  115. package/dist/assets/vue.762edd.js +0 -9
  116. package/lib/vue-composition-api.js +0 -2
@@ -0,0 +1,245 @@
1
+ <template>
2
+ <v-card>
3
+ <VcsTreeviewSearchbar
4
+ v-if="showSearchbar"
5
+ :placeholder="$t(searchbarPlaceholder)"
6
+ v-model="search"
7
+ />
8
+ <v-data-table
9
+ dense
10
+ item-key="key"
11
+ :headers="translatedHeaders"
12
+ :items="items"
13
+ :items-per-page.sync="itemsPerPageRef"
14
+ :page.sync="page"
15
+ :search="search"
16
+ :custom-filter="handleFilter"
17
+ hide-default-footer
18
+ class="vcs-table"
19
+ >
20
+ <!-- eslint-disable-next-line -->
21
+ <template #item.key="{ item }">
22
+ <td class="vcs-table">
23
+ {{ $t(item.key) }}
24
+ </td>
25
+ </template>
26
+ <!-- eslint-disable-next-line -->
27
+ <template #item.value="{ item }">
28
+ <td class="vcs-table">
29
+ {{ $t(item.value) }}
30
+ </td>
31
+ </template>
32
+ <template #footer>
33
+ <v-divider />
34
+ <v-container class="pa-2" v-if="items.length > itemsPerPageRef">
35
+ <v-row
36
+ dense
37
+ no-gutters
38
+ align="center"
39
+ justify="center"
40
+ >
41
+ <v-menu offset-y dense>
42
+ <template #activator="{ on, attrs }">
43
+ <VcsButton
44
+ small
45
+ color="primary"
46
+ v-bind="attrs"
47
+ v-on="on"
48
+ >
49
+ {{ itemsPerPageRef }}
50
+ <v-icon>mdi-chevron-down</v-icon>
51
+ </VcsButton>
52
+ </template>
53
+ <v-list>
54
+ <v-list-item
55
+ v-for="(number, index) in itemsPerPageArray"
56
+ :key="index"
57
+ @click="updateItemsPerPage(number)"
58
+ >
59
+ <v-list-item-title>{{ number }}</v-list-item-title>
60
+ </v-list-item>
61
+ </v-list>
62
+ </v-menu>
63
+ <span class="grey--text mx-2">{{ $t('components.vcsTable.itemsPerPage') }}</span>
64
+ <span class="grey--text mx-2">{{ itemsFrom }} - {{ itemsTo }} of {{ items.length }}</span>
65
+ <VcsButton
66
+ small
67
+ icon="mdi-chevron-left"
68
+ @click="formerPage"
69
+ tooltip="components.vcsTable.formerPage"
70
+ :disabled="page < 2"
71
+ class="mx-2"
72
+ />
73
+ <VcsButton
74
+ small
75
+ icon="mdi-chevron-right"
76
+ @click="nextPage"
77
+ tooltip="components.vcsTable.nextPage"
78
+ :disabled="page > numberOfPages - 1"
79
+ class="mx-1"
80
+ />
81
+ </v-row>
82
+ </v-container>
83
+ </template>
84
+ </v-data-table>
85
+ </v-card>
86
+ </template>
87
+ <script>
88
+ import { getCurrentInstance, ref, computed } from 'vue';
89
+ import VcsTreeviewSearchbar from '../lists/VcsTreeviewSearchbar.vue';
90
+ import VcsButton from '../buttons/VcsButton.vue';
91
+
92
+ /**
93
+ * @typedef {Object} TableItem
94
+ * @property {string} key
95
+ * @property {string} value
96
+ */
97
+
98
+ /**
99
+ * @param {Object} attributes
100
+ * @param {Array<TableItem>} [items]
101
+ * @param {string} [parent]
102
+ * @returns {Array<TableItem>}
103
+ */
104
+ export function attributesToItems(attributes, items = [], parent = undefined) {
105
+ const nestedKey = (key, nested) => (nested ? `${nested}.${key}` : key);
106
+ Object.entries(attributes).forEach(([key, value]) => {
107
+ if (value instanceof Object) {
108
+ attributesToItems(value, items, nestedKey(key, parent));
109
+ } else {
110
+ const item = { key: nestedKey(key, parent), value };
111
+ if (parent) { item.group = parent; }
112
+ items.push(item);
113
+ }
114
+ });
115
+ return items;
116
+ }
117
+
118
+ /**
119
+ * @description A table view for feature attributes using {@link https://vuetifyjs.com/en/api/v-data-table/#props|vuetify v-data-table }
120
+ * @vue-prop {string} featureId - feature's id
121
+ * @vue-prop {Object} attributes - feature's attributes
122
+ * @vue-prop {Array<{text: string, value: string}>} [headers] - optional array defining column names
123
+ * @vue-prop {boolean} [showSearchbar=true] - whether to show searchbar
124
+ * @vue-prop {string} [searchbarPlaceholder='Search'] - placeholder for searchbar
125
+ * @vue-computed {Array<{key:string,value:string}>} items - from attributes derived table items
126
+ */
127
+ export default {
128
+ name: 'VcsTable',
129
+ components: { VcsButton, VcsTreeviewSearchbar },
130
+ props: {
131
+ featureId: {
132
+ type: String,
133
+ required: true,
134
+ },
135
+ attributes: {
136
+ type: Object,
137
+ required: true,
138
+ },
139
+ headers: {
140
+ type: Array,
141
+ default: () => [
142
+ { text: 'components.vcsTable.key', value: 'key', width: '160px' },
143
+ { text: 'components.vcsTable.value', value: 'value', width: '160px' },
144
+ ],
145
+ },
146
+ itemsPerPage: {
147
+ type: Number,
148
+ default: 10,
149
+ },
150
+ itemsPerPageArray: {
151
+ type: Array,
152
+ default: () => [5, 10, 15],
153
+ },
154
+ showSearchbar: {
155
+ type: Boolean,
156
+ default: true,
157
+ },
158
+ searchbarPlaceholder: {
159
+ type: String,
160
+ default: 'components.vcsTable.searchbarPlaceholder',
161
+ },
162
+ },
163
+ setup(props) {
164
+ const vm = getCurrentInstance().proxy;
165
+
166
+ const items = computed(() => {
167
+ return attributesToItems({
168
+ featureId: props.featureId,
169
+ ...props.attributes,
170
+ });
171
+ });
172
+
173
+ const translatedHeaders = computed(() => {
174
+ return props.headers.map((hd) => {
175
+ hd.text = vm.$t(hd.text);
176
+ return hd;
177
+ });
178
+ });
179
+
180
+ const itemsPerPageRef = ref(props.itemsPerPage);
181
+ const numberOfPages = computed(() => {
182
+ return Math.ceil(items.value.length / itemsPerPageRef.value);
183
+ });
184
+
185
+ const page = ref(1);
186
+ const itemsFrom = computed(() => ((page.value - 1) * itemsPerPageRef.value) + 1);
187
+ const itemsTo = computed(() => {
188
+ const last = page.value * itemsPerPageRef.value;
189
+ return last < items.value.length ? last : items.value.length;
190
+ });
191
+
192
+ /**
193
+ * @param {any} value
194
+ * @param {string} search
195
+ * @param {TableItem} item
196
+ * @returns {boolean}
197
+ */
198
+ // eslint-disable-next-line default-param-last
199
+ const handleFilter = (value, search = '', item) => {
200
+ const q = search.toLocaleLowerCase();
201
+ return [item.key, item.value].some((i) => {
202
+ const content = i.toString();
203
+ const translated = vm.$t(content);
204
+ return translated.toLowerCase().includes(q) || content.toLowerCase().includes(q);
205
+ });
206
+ };
207
+
208
+ return {
209
+ search: ref(''),
210
+ page,
211
+ items,
212
+ itemsPerPageRef,
213
+ itemsFrom,
214
+ itemsTo,
215
+ numberOfPages,
216
+ nextPage() {
217
+ if (page.value + 1 <= numberOfPages.value) {
218
+ page.value += 1;
219
+ }
220
+ },
221
+ formerPage() {
222
+ if (page.value - 1 >= 1) {
223
+ page.value -= 1;
224
+ }
225
+ },
226
+ updateItemsPerPage(number) {
227
+ itemsPerPageRef.value = number;
228
+ },
229
+ handleFilter,
230
+ translatedHeaders,
231
+ };
232
+ },
233
+ };
234
+ </script>
235
+
236
+ <style lang="scss" scoped>
237
+ .vcs-table {
238
+ td {
239
+ max-width: 160px;
240
+ overflow: hidden;
241
+ text-overflow: ellipsis;
242
+ white-space: nowrap;
243
+ }
244
+ }
245
+ </style>
@@ -10,7 +10,7 @@
10
10
  </template>
11
11
 
12
12
  <script>
13
- import { inject } from '@vue/composition-api';
13
+ import { inject } from 'vue';
14
14
  import VcsTreeview from '../components/lists/VcsTreeview.vue';
15
15
 
16
16
  /**
@@ -1,7 +1,7 @@
1
1
  // eslint-disable-next-line max-classes-per-file
2
2
  import { IndexedCollection, makeOverrideCollection, getObjectFromClassRegistry } from '@vcmap/core';
3
3
  import { v4 as uuid } from 'uuid';
4
- import { computed, ref } from '@vue/composition-api';
4
+ import { computed, ref } from 'vue';
5
5
  import ContentTreeItem from './contentTreeItem.js';
6
6
  import { vcsAppSymbol } from '../pluginHelper.js';
7
7
  import SubContentTreeItem, { subTreeSymbol } from './subContentTreeItem.js';
@@ -73,7 +73,7 @@ class ContentTreeCollection extends IndexedCollection {
73
73
  */
74
74
  this._defaultSubtreeItem = new SubContentTreeItem({ name: 'Content', icon: '$vcsLayers', title: 'content.title' }, app);
75
75
  /**
76
- * @type {import("@vue/composition-api").Ref<Map<string, TreeViewItem>>}
76
+ * @type {import("vue").Ref<Map<string, TreeViewItem>>}
77
77
  * @private
78
78
  */
79
79
  this._subTreeViewItems = ref(new Map());
@@ -211,7 +211,7 @@ class ContentTreeCollection extends IndexedCollection {
211
211
 
212
212
  /**
213
213
  * @param {string} id
214
- * @returns {import("@vue/composition-api").ComputedRef<Array<TreeViewItem>>}
214
+ * @returns {import("vue").ComputedRef<Array<TreeViewItem>>}
215
215
  */
216
216
  getComputedVisibleTree(id) {
217
217
  return computed(() => {
@@ -253,7 +253,7 @@ class ContentTreeCollection extends IndexedCollection {
253
253
 
254
254
  /**
255
255
  * @param {string} id
256
- * @returns {import("@vue/composition-api").Ref<Array<string>>}
256
+ * @returns {import("vue").Ref<Array<string>>}
257
257
  */
258
258
  getTreeOpenStateRef(id) {
259
259
  const subTree = this._getSubTree(id);
@@ -1,4 +1,4 @@
1
- import { ref, reactive, computed } from '@vue/composition-api';
1
+ import { ref, reactive, computed } from 'vue';
2
2
  import { check, checkMaybe } from '@vcsuite/check';
3
3
  import { parseBoolean, parseNumber } from '@vcsuite/parsers';
4
4
  import { ClassRegistry, VcsEvent } from '@vcmap/core';
@@ -63,7 +63,7 @@ class ContentTreeItem {
63
63
  this._app = app;
64
64
 
65
65
  /**
66
- * @type {import("@vue/composition-api").Ref<Array<VcsAction>>}
66
+ * @type {import("vue").Ref<Array<VcsAction>>}
67
67
  * @private
68
68
  */
69
69
  this._actions = ref([]);
@@ -76,28 +76,28 @@ class ContentTreeItem {
76
76
 
77
77
  /**
78
78
  * Whether to display this item or not.
79
- * @type {import("@vue/composition-api").Ref<boolean>}
79
+ * @type {import("vue").Ref<boolean>}
80
80
  * @private
81
81
  */
82
82
  this._visible = ref(true);
83
83
 
84
84
  /**
85
85
  * Whether this item reacts to click events, e.g. with visual feedback
86
- * @type {import("@vue/composition-api").Ref<boolean>}
86
+ * @type {import("vue").Ref<boolean>}
87
87
  * @private
88
88
  */
89
89
  this._clickable = ref(true);
90
90
 
91
91
  /**
92
92
  * Whether this item should be displayed as disabled.
93
- * @type {import("@vue/composition-api").Ref<boolean>}
93
+ * @type {import("vue").Ref<boolean>}
94
94
  * @private
95
95
  */
96
96
  this._disabled = ref(false);
97
97
 
98
98
  /**
99
99
  * The state of this item. NONE if this item cannot have a state.
100
- * @type {import("@vue/composition-api").Ref<StateActionState>}
100
+ * @type {import("vue").Ref<StateActionState>}
101
101
  * @private
102
102
  */
103
103
  this._state = ref(StateActionState.NONE);
@@ -116,20 +116,20 @@ class ContentTreeItem {
116
116
  this.infoUrl = options.infoUrl;
117
117
 
118
118
  /**
119
- * @type {import("@vue/composition-api").Ref<Object<string, string>|string|undefined>}
119
+ * @type {import("vue").Ref<Object<string, string>|string|undefined>}
120
120
  * @private
121
121
  */
122
122
  this._title = ref(options.title);
123
123
 
124
124
  /**
125
125
  * An optional icon to display with this item. Can be an URL or HTMLElement.
126
- * @type {import("@vue/composition-api").Ref<string|HTMLCanvasElement|HTMLImageElement|undefined>}
126
+ * @type {import("vue").Ref<string|HTMLCanvasElement|HTMLImageElement|undefined>}
127
127
  * @private
128
128
  */
129
129
  this._icon = ref(options.icon);
130
130
 
131
131
  /**
132
- * @type {import("@vue/composition-api").Ref<Array<TreeViewItem>>}
132
+ * @type {import("vue").Ref<Array<TreeViewItem>>}
133
133
  * @protected
134
134
  */
135
135
  this._children = ref([]);
@@ -1,4 +1,4 @@
1
- import { watch } from '@vue/composition-api';
1
+ import { watch } from 'vue';
2
2
  import ContentTreeItem, { contentTreeClassRegistry } from './contentTreeItem.js';
3
3
  import { StateActionState } from '../actions/stateRefAction.js';
4
4
 
@@ -1,5 +1,5 @@
1
1
  import { ViewPoint } from '@vcmap/core';
2
- import { reactive } from '@vue/composition-api';
2
+ import { reactive } from 'vue';
3
3
  import { StateActionState } from '../actions/stateRefAction.js';
4
4
  import { createGoToViewpointAction, createModalAction } from '../actions/actionHelper.js';
5
5
  import component from '../actions/styleSelector.vue';
@@ -10,6 +10,7 @@ import { contentTreeClassRegistry } from './contentTreeItem.js';
10
10
  /**
11
11
  * @typedef {ContentTreeItemOptions} LayerContentTreeItemOptions
12
12
  * @property {string} layerName
13
+ * @property {Array<string>} layerNamesToDeactivate list of LayerNames which should be deactivated if the click activates the layer
13
14
  */
14
15
 
15
16
  /**
@@ -80,6 +81,14 @@ class LayerContentTreeItem extends VcsObjectContentTreeItem {
80
81
  */
81
82
  this._layerName = options.layerName;
82
83
 
84
+ /**
85
+ * @type {Array<string>}
86
+ * @private
87
+ */
88
+ this._layerNamesToDeactivate = Array.isArray(options.layerNamesToDeactivate) ?
89
+ options.layerNamesToDeactivate.slice() :
90
+ [];
91
+
83
92
  /**
84
93
  * @type {Array<Function>}
85
94
  * @private
@@ -210,6 +219,10 @@ class LayerContentTreeItem extends VcsObjectContentTreeItem {
210
219
  if (this._layer) {
211
220
  if (this.state === StateActionState.INACTIVE) {
212
221
  await this._layer.activate();
222
+ this._layerNamesToDeactivate
223
+ .map(n => this._app.layers.getByKey(n))
224
+ .filter(l => l)
225
+ .forEach(l => l.deactivate());
213
226
  } else {
214
227
  this._layer.deactivate();
215
228
  }
@@ -222,6 +235,7 @@ class LayerContentTreeItem extends VcsObjectContentTreeItem {
222
235
  toJSON() {
223
236
  const config = super.toJSON();
224
237
  config.layerName = this._layerName;
238
+ config.layerNamesToDeactivate = this._layerNamesToDeactivate.slice();
225
239
  return config;
226
240
  }
227
241
 
@@ -4,7 +4,8 @@ import { StateActionState } from '../actions/stateRefAction.js';
4
4
 
5
5
  /**
6
6
  * @typedef {ContentTreeItemOptions} LayerGroupContentTreeItemOptions
7
- * @property {Array<string>} layerNames
7
+ * @property {Array<string>} layerNames list of LayerNames which should be activated on click
8
+ * @property {Array<string>} layerNamesToDeactivate list of LayerNames which should be deactivated on click if the click activates the layer in layerNames
8
9
  * @property {string} [defaultViewpoint] - the name of an optional default viewpoint
9
10
  */
10
11
 
@@ -55,6 +56,14 @@ class LayerGroupContentTreeItem extends ContentTreeItem {
55
56
  options.layerNames.slice() :
56
57
  [];
57
58
 
59
+ /**
60
+ * @type {Array<string>}
61
+ * @private
62
+ */
63
+ this._layerNamesToDeactivate = Array.isArray(options.layerNamesToDeactivate) ?
64
+ options.layerNamesToDeactivate.slice() :
65
+ [];
66
+
58
67
  /**
59
68
  * @type {Array<function():void>}
60
69
  * @private
@@ -122,11 +131,18 @@ class LayerGroupContentTreeItem extends ContentTreeItem {
122
131
  }));
123
132
  }
124
133
 
134
+ /**
135
+ * @returns {Promise<void>}
136
+ */
125
137
  async clicked() {
126
138
  const layers = this._layers;
127
139
  const activate = layers.some(l => !(l.active || l.loading));
128
140
  if (activate) {
129
141
  await Promise.all(layers.map(l => l.activate()));
142
+ this._layerNamesToDeactivate
143
+ .map(n => this._app.layers.getByKey(n))
144
+ .filter(l => l)
145
+ .forEach(l => l.deactivate());
130
146
  } else {
131
147
  layers.forEach((l) => { l.deactivate(); });
132
148
  }
@@ -138,12 +154,16 @@ class LayerGroupContentTreeItem extends ContentTreeItem {
138
154
  toJSON() {
139
155
  const config = super.toJSON();
140
156
  config.layerNames = this._layerNames.slice();
157
+ config.layerNamesToDeactivate = this._layerNamesToDeactivate.slice();
141
158
  if (this._defaultViewpoint) {
142
159
  config.defaultViewpoint = this._defaultViewpoint;
143
160
  }
144
161
  return config;
145
162
  }
146
163
 
164
+ /**
165
+ * @inheritDoc
166
+ */
147
167
  destroy() {
148
168
  this._clearListeners();
149
169
  super.destroy();
@@ -1,4 +1,4 @@
1
- import { watch } from '@vue/composition-api';
1
+ import { watch } from 'vue';
2
2
  import ContentTreeItem, { contentTreeClassRegistry } from './contentTreeItem.js';
3
3
 
4
4
  /**
@@ -0,0 +1,47 @@
1
+ <template>
2
+ <BalloonComponent
3
+ v-bind="{...$attrs}"
4
+ >
5
+ <template #default="{ attrs }">
6
+ <v-list-item two-line v-if="Object.values(attrs.attributes).length > 0">
7
+ <v-list-item-avatar
8
+ tile
9
+ size="40"
10
+ >
11
+ <v-icon size="20" color="secondary">
12
+ $vcsHomePoint
13
+ </v-icon>
14
+ </v-list-item-avatar>
15
+ <v-list-item-content>
16
+ <v-list-item-title v-if="attrs.attributes.addressName">
17
+ {{ attrs.attributes.addressName }}
18
+ </v-list-item-title>
19
+ <v-list-item-title v-else>
20
+ {{ attrs.attributes.street }} {{ attrs.attributes.number }}
21
+ </v-list-item-title>
22
+ <v-list-item-subtitle v-if="attrs.attributes.name">
23
+ {{ attrs.attributes.street }} {{ attrs.attributes.number }}
24
+ </v-list-item-subtitle>
25
+ <v-list-item-subtitle>{{ attrs.attributes.zip }} {{ attrs.attributes.city }}</v-list-item-subtitle>
26
+ <v-list-item-subtitle>{{ attrs.attributes.country }}</v-list-item-subtitle>
27
+ </v-list-item-content>
28
+ </v-list-item>
29
+ </template>
30
+ </BalloonComponent>
31
+ </template>
32
+ <script>
33
+
34
+ import BalloonComponent from './BalloonComponent.vue';
35
+
36
+ /**
37
+ * @description A balloon viewing address information
38
+ */
39
+ export default {
40
+ name: 'AddressBalloonComponent',
41
+ components: { BalloonComponent },
42
+ };
43
+ </script>
44
+
45
+ <style>
46
+
47
+ </style>
@@ -0,0 +1,138 @@
1
+ <template>
2
+ <v-card
3
+ class="mx-auto"
4
+ max-width="400"
5
+ v-if="position"
6
+ >
7
+ <slot name="balloon-header" :attrs="{...$props, ...$attrs}">
8
+ <v-list-item two-line>
9
+ <v-list-item-avatar
10
+ tile
11
+ size="40"
12
+ >
13
+ <v-icon color="primary">
14
+ $vcsInfo
15
+ </v-icon>
16
+ </v-list-item-avatar>
17
+ <v-list-item-content>
18
+ <v-list-item-title class="text-h5">
19
+ {{ title }}
20
+ </v-list-item-title>
21
+ <v-list-item-subtitle>{{ subtitle }}</v-list-item-subtitle>
22
+ </v-list-item-content>
23
+ <VcsButton
24
+ @click.stop="close"
25
+ small
26
+ icon="mdi-close-thick"
27
+ class="mb-8"
28
+ />
29
+ </v-list-item>
30
+ </slot>
31
+
32
+ <v-divider />
33
+
34
+ <v-card class="overflow-y-auto" max-height="250">
35
+ <slot :attrs="{...$props, ...$attrs}">
36
+ <v-list v-for="(value, name, index) in attributes" :key="`attribute-${index}`">
37
+ <v-list-item>
38
+ <v-list-item-content>
39
+ <v-list-item-title>
40
+ {{ name }}
41
+ </v-list-item-title>
42
+ <v-list-item-subtitle>{{ value }}</v-list-item-subtitle>
43
+ </v-list-item-content>
44
+ </v-list-item>
45
+ </v-list>
46
+ </slot>
47
+ </v-card>
48
+ </v-card>
49
+ </template>
50
+ <script>
51
+
52
+ import { inject, onMounted, onUnmounted, watch } from 'vue';
53
+ import { setupBalloonPositionListener } from './balloonHelper.js';
54
+
55
+ /**
56
+ * @description A balloon viewing feature attributes. Size dynamic dependent on number of attributes.
57
+ * Scrollable, if more than 6 attributes are provided.
58
+ * @vue-prop {string} featureId - feature's id
59
+ * @vue-prop {string} title - balloon title
60
+ * @vue-prop {string} subtitle - balloon subtitle
61
+ * @vue-prop {Object} attributes - feature's attributes
62
+ * @vue-prop {Array<import("ol/coordinate").Coordinate>} position - clicked position balloon is rendered at
63
+ * @vue-data {slot} [#balloon-header] - slot to override balloon header, $props and $attrs are passed to `attrs`
64
+ * @vue-data {slot} [#default] - slot to override balloon content, $props and $attrs are passed to `attrs`
65
+ */
66
+ export default {
67
+ name: 'BalloonComponent',
68
+ props: {
69
+ featureId: {
70
+ type: String,
71
+ required: true,
72
+ },
73
+ title: {
74
+ type: String,
75
+ required: true,
76
+ },
77
+ subtitle: {
78
+ type: String,
79
+ required: true,
80
+ },
81
+ attributes: {
82
+ type: Object,
83
+ required: true,
84
+ },
85
+ position: {
86
+ type: Array,
87
+ default: null,
88
+ },
89
+ },
90
+ setup(props, { attrs }) {
91
+ const app = inject('vcsApp');
92
+ const windowId = attrs['window-state'].id;
93
+
94
+ let balloonPositionListener = null;
95
+ const destroyListener = () => {
96
+ if (balloonPositionListener) {
97
+ balloonPositionListener();
98
+ }
99
+ };
100
+
101
+ onMounted(async () => {
102
+ balloonPositionListener = await setupBalloonPositionListener(app, windowId, props.position);
103
+ });
104
+
105
+ watch(() => props.featureId, async () => {
106
+ destroyListener();
107
+ balloonPositionListener = await setupBalloonPositionListener(app, windowId, props.position);
108
+ });
109
+
110
+ onUnmounted(() => {
111
+ destroyListener();
112
+ });
113
+
114
+ const close = () => {
115
+ app.windowManager.remove(attrs['window-state'].id);
116
+ destroyListener();
117
+ };
118
+
119
+ return {
120
+ close,
121
+ };
122
+ },
123
+ };
124
+ </script>
125
+
126
+ <style>
127
+ .balloon:before {
128
+ content: "";
129
+ position: absolute;
130
+ bottom: -20px;
131
+ left: 40px;
132
+ border-width: 20px 20px 0;
133
+ border-style: solid;
134
+ border-color: white transparent;
135
+ display: block;
136
+ width: 0;
137
+ }
138
+ </style>