@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.
- package/README.md +11 -4
- package/build/build.js +0 -3
- package/build/buildHelpers.js +0 -1
- package/build/buildPreview.js +7 -0
- package/config/aerowest.config.json +13 -3
- package/config/base.config.json +89 -64
- package/config/codes.config.json +397 -0
- package/config/dev.config.json +169 -0
- package/config/graphFeatureInfo.config.json +100 -0
- package/config/www.config.json +1232 -0
- package/dist/assets/{cesium.eb5667.js → cesium.e67536.js} +0 -0
- package/dist/assets/cesium.js +1 -1
- package/dist/assets/core.ebf665.js +4 -0
- package/dist/assets/core.js +1 -1
- package/dist/assets/{index.4ccd4433.js → index.9b213929.js} +1 -1
- package/dist/assets/{ol.ef03b1.js → ol.8bbd50.js} +0 -0
- package/dist/assets/ol.js +1 -1
- package/dist/assets/ui.fdfe0d.css +1 -0
- package/dist/assets/ui.fdfe0d.js +68 -0
- package/dist/assets/ui.js +1 -1
- package/dist/assets/vue.0bb7c6.js +9 -0
- package/dist/assets/vue.js +2 -1
- package/dist/assets/{vuetify.401a29.css → vuetify.53300f.css} +1 -1
- package/dist/assets/{vuetify.401a29.js → vuetify.53300f.js} +71 -71
- package/dist/assets/vuetify.js +2 -2
- package/dist/index.html +1 -1
- package/index.js +36 -5
- package/lib/vue.js +1 -0
- package/map.config.json +15 -6
- package/package.json +6 -7
- package/plugins/@vcmap/create-link/fallbackCreateLink.vue +71 -0
- package/plugins/@vcmap/create-link/index.js +83 -0
- package/plugins/@vcmap/create-link/package.json +6 -0
- package/plugins/@vcmap/pluginExample/index.js +1 -1
- package/plugins/@vcmap/pluginExample/pluginExampleComponent.vue +20 -3
- package/plugins/@vcmap/project-selector/ProjectSelectorComponent.vue +1 -1
- package/plugins/@vcmap/project-selector/index.js +1 -1
- package/plugins/@vcmap/project-selector/package.json +1 -2
- package/plugins/@vcmap/theme-changer/ThemeChangerComponent.vue +1 -1
- package/plugins/@vcmap/theme-changer/index.js +1 -1
- package/plugins/@vcmap/theme-changer/package.json +1 -2
- package/plugins/categoryTest/Categories.vue +89 -1
- package/plugins/categoryTest/Category.vue +1 -1
- package/plugins/simple-graph/README.md +51 -0
- package/plugins/simple-graph/SimpleGraphComponent.vue +70 -0
- package/plugins/simple-graph/index.js +17 -0
- package/plugins/simple-graph/package.json +11 -0
- package/plugins/simple-graph/simpleGraphView.js +76 -0
- package/plugins/test/editor.vue +1 -1
- package/plugins/test/index.js +63 -2
- package/plugins/test/windowManagerExample.vue +1 -1
- package/src/actions/stateRefAction.js +2 -2
- package/src/actions/styleSelector.vue +1 -1
- package/src/application/Navbar.vue +13 -2
- package/src/application/VcsApp.vue +201 -92
- package/src/application/VcsMap.vue +1 -1
- package/src/application/VcsSettings.vue +1 -1
- package/src/application/vcsAppWrapper.vue +1 -0
- package/src/components/form-inputs-controls/VcsCheckbox.vue +13 -0
- package/src/components/form-inputs-controls/VcsColorPicker.vue +1 -1
- package/src/components/form-inputs-controls/VcsRadio.vue +123 -0
- package/src/components/form-output/VcsFormattedNumber.vue +1 -1
- package/src/components/lists/VcsActionList.vue +13 -4
- package/src/components/lists/VcsTreeview.vue +4 -4
- package/src/components/lists/VcsTreeviewLeaf.vue +9 -2
- package/src/components/lists/VcsTreeviewSearchbar.vue +1 -2
- package/src/components/tables/VcsTable.vue +245 -0
- package/src/contentTree/LayerTree.vue +1 -1
- package/src/contentTree/contentTreeCollection.js +4 -4
- package/src/contentTree/contentTreeItem.js +9 -9
- package/src/contentTree/groupContentTreeItem.js +1 -1
- package/src/contentTree/layerContentTreeItem.js +15 -1
- package/src/contentTree/layerGroupContentTreeItem.js +21 -1
- package/src/contentTree/nodeContentTreeItem.js +1 -1
- package/src/featureInfo/AddressBalloonComponent.vue +47 -0
- package/src/featureInfo/BalloonComponent.vue +138 -0
- package/src/featureInfo/abstractFeatureInfoView.js +313 -0
- package/src/featureInfo/addressBalloonFeatureInfoView.js +118 -0
- package/src/featureInfo/balloonFeatureInfoView.js +151 -0
- package/src/featureInfo/balloonHelper.js +132 -0
- package/src/featureInfo/featureInfo.js +455 -0
- package/src/featureInfo/featureInfoInteraction.js +42 -0
- package/src/featureInfo/iframeFeatureInfoView.js +95 -0
- package/src/featureInfo/tableFeatureInfoView.js +106 -0
- package/src/i18n/de.js +16 -0
- package/src/i18n/en.js +16 -0
- package/src/i18n/i18nCollection.js +17 -0
- package/src/manager/buttonManager.js +5 -5
- package/src/manager/categoryManager/ComponentsManager.vue +30 -0
- package/src/manager/categoryManager/categoryManager.js +500 -0
- package/src/manager/contextMenu/contextMenuComponent.vue +43 -0
- package/src/manager/contextMenu/contextMenuInteraction.js +42 -0
- package/src/manager/contextMenu/contextMenuManager.js +197 -0
- package/src/manager/navbarManager.js +8 -8
- package/src/manager/toolbox/ToolboxManager.vue +2 -2
- package/src/manager/toolbox/toolboxManager.js +7 -3
- package/src/manager/window/WindowComponent.vue +1 -1
- package/src/manager/window/WindowManager.vue +5 -3
- package/src/manager/window/windowManager.js +118 -14
- package/src/navigation/mapNavigation.vue +3 -5
- package/src/navigation/overviewMap.js +28 -5
- package/src/navigation/vcsCompass.vue +1 -1
- package/src/setup.js +0 -2
- package/src/state.js +256 -0
- package/src/styles/_theming.scss +0 -5
- package/src/uiConfig.js +79 -0
- package/src/vcsUiApp.js +210 -20
- package/src/vuePlugins/vuetify.js +14 -4
- package/config/berlin.config.json +0 -510
- package/dist/assets/core.216494.js +0 -4
- package/dist/assets/ui.99a1a7.css +0 -1
- package/dist/assets/ui.99a1a7.js +0 -70
- package/dist/assets/vue-composition-api.c5aca1.js +0 -14
- package/dist/assets/vue-composition-api.js +0 -2
- package/dist/assets/vue.762edd.js +0 -9
- 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>
|
@@ -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 '
|
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("
|
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("
|
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("
|
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 '
|
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("
|
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("
|
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("
|
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("
|
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("
|
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("
|
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("
|
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("
|
132
|
+
* @type {import("vue").Ref<Array<TreeViewItem>>}
|
133
133
|
* @protected
|
134
134
|
*/
|
135
135
|
this._children = ref([]);
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { ViewPoint } from '@vcmap/core';
|
2
|
-
import { reactive } from '
|
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();
|
@@ -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>
|