@vcmap/ui 5.0.0-rc.14 → 5.0.0-rc.15
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 +33 -31
- package/build/build.js +9 -0
- package/build/buildHelpers.js +12 -10
- package/build/commonViteConfig.js +3 -10
- package/config/base.config.json +26 -24
- package/config/dev.config.json +8 -0
- package/config/www.config.json +102 -17
- package/dist/assets/cesium.2e288a.js +137226 -0
- package/dist/assets/cesium.js +1 -1
- package/dist/assets/core.8014d3.js +14473 -0
- package/dist/assets/core.js +1 -1
- package/dist/assets/index.3f74fa92.js +1 -0
- package/dist/assets/ol.31c3a5.js +44279 -0
- package/dist/assets/ol.js +1 -1
- package/dist/assets/ui.36f84f.css +1 -0
- package/dist/assets/ui.36f84f.js +16101 -0
- package/dist/assets/ui.js +1 -1
- package/dist/assets/vue.a39c10.js +4675 -0
- package/dist/assets/vue.js +5 -2
- package/dist/assets/{vuetify.202322.css → vuetify.378637.css} +1 -1
- package/dist/assets/vuetify.378637.js +21019 -0
- package/dist/assets/vuetify.js +5 -2
- package/dist/index.html +1 -1
- package/index.js +4 -1
- package/package.json +10 -10
- package/plugins/@vcmap/pluginExample/index.js +14 -0
- package/plugins/@vcmap/pluginExample/pluginExampleComponent.vue +93 -67
- package/plugins/@vcmap/project-selector/ContextsListComponent.vue +8 -1
- package/plugins/@vcmap/project-selector/ProjectSelectorComponent.vue +27 -1
- package/plugins/@vcmap/search-nominatim/LICENSE.md +14 -0
- package/plugins/@vcmap/search-nominatim/README.md +2 -0
- package/plugins/@vcmap/search-nominatim/config.json +4 -0
- package/plugins/@vcmap/search-nominatim/index.js +26 -0
- package/plugins/@vcmap/search-nominatim/nominatim.js +170 -0
- package/plugins/@vcmap/search-nominatim/package.json +43 -0
- package/plugins/@vcmap/theme-changer/ThemeChangerComponent.vue +24 -0
- package/plugins/buttonExamples/ButtonExamples.vue +28 -1
- package/plugins/categoryTest/Categories.vue +16 -0
- package/plugins/categoryTest/Category.vue +30 -4
- package/plugins/example/mySuperComponent.vue +12 -1
- package/plugins/package.json +2 -1
- package/plugins/simple-graph/SimpleGraphComponent.vue +5 -11
- package/plugins/test/allIconsComponent.vue +16 -0
- package/plugins/test/editor.vue +3 -0
- package/plugins/test/emptyComponent.vue +3 -0
- package/plugins/test/vcsContent.vue +3 -0
- package/src/actions/actionHelper.js +103 -2
- package/src/actions/styleSelector.vue +9 -0
- package/src/application/VcsApp.vue +77 -9
- package/src/application/VcsAttributions.vue +63 -0
- package/src/application/VcsAttributionsFooter.vue +87 -0
- package/src/application/{Navbar.vue → VcsNavbar.vue} +35 -2
- package/src/application/VcsSettings.vue +4 -0
- package/src/application/attributionsHelper.js +150 -0
- package/src/application/vcsAppWrapper.vue +5 -1
- package/src/components/buttons/VcsActionButtonList.vue +8 -1
- package/src/components/buttons/VcsButton.vue +7 -1
- package/src/components/form-inputs-controls/VcsCheckbox.vue +7 -2
- package/src/components/form-inputs-controls/VcsColorPicker.vue +4 -0
- package/src/components/form-inputs-controls/VcsFormSection.vue +48 -2
- package/src/components/form-inputs-controls/VcsRadio.vue +7 -1
- package/src/components/form-inputs-controls/VcsSelect.vue +5 -1
- package/src/components/form-inputs-controls/VcsTextArea.vue +2 -0
- package/src/components/form-inputs-controls/VcsTextField.vue +6 -2
- package/src/components/lists/VcsActionList.vue +12 -1
- package/src/components/lists/VcsTreeview.vue +6 -1
- package/src/components/lists/VcsTreeviewLeaf.vue +5 -1
- package/src/components/lists/VcsTreeviewSearchbar.vue +5 -0
- package/src/components/notification/VcsTooltip.vue +14 -9
- package/src/components/tables/VcsTable.vue +117 -38
- package/src/featureInfo/AddressBalloonComponent.vue +17 -1
- package/src/featureInfo/BalloonComponent.vue +57 -23
- package/src/featureInfo/balloonFeatureInfoView.js +2 -2
- package/src/featureInfo/featureInfo.js +10 -2
- package/src/i18n/de.js +15 -0
- package/src/i18n/en.js +15 -0
- package/src/legend/legendHelper.js +18 -12
- package/src/legend/styleLegendItem.vue +20 -1
- package/src/legend/vcsLegend.vue +29 -3
- package/src/manager/toolbox/GroupToolboxComponent.vue +13 -1
- package/src/manager/toolbox/SelectToolboxComponent.vue +13 -1
- package/src/manager/toolbox/ToolboxManager.vue +3 -0
- package/src/manager/window/WindowComponent.vue +6 -0
- package/src/manager/window/WindowComponentHeader.vue +6 -1
- package/src/navigation/mapNavigation.vue +15 -36
- package/src/navigation/orientationToolsButton.vue +6 -1
- package/src/navigation/overviewMap.js +10 -39
- package/src/navigation/tiltSlider.vue +3 -0
- package/src/navigation/vcsCompass.vue +2 -0
- package/src/search/resultItem.vue +89 -0
- package/src/search/resultsComponent.vue +98 -0
- package/src/search/search.js +326 -0
- package/src/search/searchComponent.vue +90 -0
- package/src/styles/_typography.scss +3 -0
- package/src/styles/utils/_cursor.scss +4 -0
- package/src/styles/variables.scss +4 -1
- package/src/vcsUiApp.js +16 -0
- package/dist/assets/cesium.9489f8.js +0 -8699
- package/dist/assets/core.aa346a.js +0 -4
- package/dist/assets/index.3cd4fffa.js +0 -1
- package/dist/assets/ol.39651b.js +0 -439
- package/dist/assets/ui.15ef6a.css +0 -1
- package/dist/assets/ui.15ef6a.js +0 -71
- package/dist/assets/vue.cbe9d8.js +0 -9
- package/dist/assets/vuetify.202322.js +0 -148
@@ -108,6 +108,7 @@
|
|
108
108
|
|
109
109
|
import { Subject } from 'rxjs';
|
110
110
|
import { debounceTime } from 'rxjs/operators';
|
111
|
+
import { VIcon, VTextField } from 'vuetify/lib';
|
111
112
|
|
112
113
|
|
113
114
|
/**
|
@@ -117,6 +118,10 @@
|
|
117
118
|
*/
|
118
119
|
export default {
|
119
120
|
name: 'VcsTreeviewSearchbar',
|
121
|
+
components: {
|
122
|
+
VIcon,
|
123
|
+
VTextField,
|
124
|
+
},
|
120
125
|
props: {
|
121
126
|
placeholder: {
|
122
127
|
type: String,
|
@@ -110,15 +110,17 @@
|
|
110
110
|
</style>
|
111
111
|
<script>
|
112
112
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
113
|
+
import { VTooltip } from 'vuetify/lib';
|
114
|
+
|
115
|
+
/**
|
116
|
+
* @enum {string} TooltipPositions
|
117
|
+
* @property {string} bottom
|
118
|
+
* @property {string} left
|
119
|
+
* @property {string} top
|
120
|
+
* @property {string} right
|
121
|
+
* @readonly
|
122
|
+
* @module VcsTooltip
|
123
|
+
*/
|
122
124
|
const TooltipPositions = {
|
123
125
|
bottom: 'arrow-top',
|
124
126
|
top: 'arrow-bottom',
|
@@ -134,6 +136,9 @@
|
|
134
136
|
*/
|
135
137
|
export default {
|
136
138
|
name: 'VcsTooltip',
|
139
|
+
components: {
|
140
|
+
VTooltip,
|
141
|
+
},
|
137
142
|
props: {
|
138
143
|
tooltip: {
|
139
144
|
type: String,
|
@@ -19,19 +19,19 @@
|
|
19
19
|
>
|
20
20
|
<!-- eslint-disable-next-line -->
|
21
21
|
<template #item.key="{ item }">
|
22
|
-
<td class="vcs-table">
|
22
|
+
<td class="vcs-table px-2 overflow-max-width" :title="$t(item.key)">
|
23
23
|
{{ $t(item.key) }}
|
24
24
|
</td>
|
25
25
|
</template>
|
26
26
|
<!-- eslint-disable-next-line -->
|
27
27
|
<template #item.value="{ item }">
|
28
|
-
<td class="vcs-table">
|
28
|
+
<td class="vcs-table px-2 overflow-max-width" :title="$t(item.value)">
|
29
29
|
{{ $t(item.value) }}
|
30
30
|
</td>
|
31
31
|
</template>
|
32
32
|
<template #footer>
|
33
33
|
<v-divider />
|
34
|
-
<v-container class="pa-2" v-if="items.length > itemsPerPageRef">
|
34
|
+
<v-container class="pa-2 vcs-pagination-bar accent" v-if="items.length > itemsPerPageRef">
|
35
35
|
<v-row
|
36
36
|
dense
|
37
37
|
no-gutters
|
@@ -55,20 +55,23 @@
|
|
55
55
|
v-for="(number, index) in itemsPerPageArray"
|
56
56
|
:key="index"
|
57
57
|
@click="updateItemsPerPage(number)"
|
58
|
+
style="min-height: auto; height: 24px; text-align: right;"
|
58
59
|
>
|
59
60
|
<v-list-item-title>{{ number }}</v-list-item-title>
|
60
61
|
</v-list-item>
|
61
62
|
</v-list>
|
62
63
|
</v-menu>
|
63
|
-
<span class="
|
64
|
-
<span class="
|
64
|
+
<span class="mx-2">{{ $t('components.vcsTable.itemsPerPage') }}</span>
|
65
|
+
<span class="mx-2">
|
66
|
+
{{ itemsFrom }} - {{ itemsTo }} {{ $t('components.vcsTable.ofItems') }} {{ numberOfItems }}
|
67
|
+
</span>
|
65
68
|
<VcsButton
|
66
69
|
small
|
67
70
|
icon="mdi-chevron-left"
|
68
71
|
@click="formerPage"
|
69
72
|
tooltip="components.vcsTable.formerPage"
|
70
73
|
:disabled="page < 2"
|
71
|
-
class="
|
74
|
+
class="ml-1"
|
72
75
|
/>
|
73
76
|
<VcsButton
|
74
77
|
small
|
@@ -76,7 +79,7 @@
|
|
76
79
|
@click="nextPage"
|
77
80
|
tooltip="components.vcsTable.nextPage"
|
78
81
|
:disabled="page > numberOfPages - 1"
|
79
|
-
class="
|
82
|
+
class="ml-1"
|
80
83
|
/>
|
81
84
|
</v-row>
|
82
85
|
</v-container>
|
@@ -86,6 +89,9 @@
|
|
86
89
|
</template>
|
87
90
|
<script>
|
88
91
|
import { getCurrentInstance, ref, computed } from 'vue';
|
92
|
+
import {
|
93
|
+
VContainer, VDataTable, VList, VListItem, VListItemTitle, VMenu, VRow,
|
94
|
+
} from 'vuetify/lib';
|
89
95
|
import VcsTreeviewSearchbar from '../lists/VcsTreeviewSearchbar.vue';
|
90
96
|
import VcsButton from '../buttons/VcsButton.vue';
|
91
97
|
|
@@ -116,17 +122,32 @@
|
|
116
122
|
}
|
117
123
|
|
118
124
|
/**
|
119
|
-
* @description A table view for feature attributes using {@link https://vuetifyjs.com/en/api/v-data-table/#props
|
125
|
+
* @description A table view for feature attributes using {@link https://vuetifyjs.com/en/api/v-data-table/#props v-data-table }
|
120
126
|
* @vue-prop {string} featureId - feature's id
|
121
127
|
* @vue-prop {Object} attributes - feature's attributes
|
122
128
|
* @vue-prop {Array<{text: string, value: string}>} [headers] - optional array defining column names
|
123
129
|
* @vue-prop {boolean} [showSearchbar=true] - whether to show searchbar
|
124
130
|
* @vue-prop {string} [searchbarPlaceholder='Search'] - placeholder for searchbar
|
125
|
-
* @vue-computed {Array<
|
131
|
+
* @vue-computed {Array<TableItem>} items - from attributes derived table items
|
132
|
+
* @vue-computed {Array<TableItem>} filteredItems - array of items with search filter applied on. If search string is empty, same as items array.
|
133
|
+
* @vue-computed {number} numberOfItems - number of filtered items (depending on search).
|
134
|
+
* @vue-computed {number} numberOfPages - number of pages depending on number of items, search and itemsPerPage.
|
135
|
+
* @vue-computed {number} itemsFrom - index of first item shown on current page.
|
136
|
+
* @vue-computed {number} itemsTo - index of last item shown on current page.
|
126
137
|
*/
|
127
138
|
export default {
|
128
139
|
name: 'VcsTable',
|
129
|
-
components: {
|
140
|
+
components: {
|
141
|
+
VcsButton,
|
142
|
+
VcsTreeviewSearchbar,
|
143
|
+
VDataTable,
|
144
|
+
VContainer,
|
145
|
+
VRow,
|
146
|
+
VMenu,
|
147
|
+
VList,
|
148
|
+
VListItem,
|
149
|
+
VListItemTitle,
|
150
|
+
},
|
130
151
|
props: {
|
131
152
|
featureId: {
|
132
153
|
type: String,
|
@@ -162,7 +183,13 @@
|
|
162
183
|
},
|
163
184
|
setup(props) {
|
164
185
|
const vm = getCurrentInstance().proxy;
|
165
|
-
|
186
|
+
/**
|
187
|
+
* @type {Ref<UnwrapRef<string>>}
|
188
|
+
*/
|
189
|
+
const search = ref('');
|
190
|
+
/**
|
191
|
+
* @type {ComputedRef<Array<TableItem>>}
|
192
|
+
*/
|
166
193
|
const items = computed(() => {
|
167
194
|
return attributesToItems({
|
168
195
|
featureId: props.featureId,
|
@@ -170,6 +197,33 @@
|
|
170
197
|
});
|
171
198
|
});
|
172
199
|
|
200
|
+
/**
|
201
|
+
* @param {any} value
|
202
|
+
* @param {string|undefined} filter
|
203
|
+
* @param {TableItem} item
|
204
|
+
* @returns {boolean}
|
205
|
+
*/
|
206
|
+
const handleFilter = (value, filter, item) => {
|
207
|
+
if (filter) {
|
208
|
+
const q = filter.toLocaleLowerCase();
|
209
|
+
return [item.key, item.value].some((i) => {
|
210
|
+
const content = i.toString();
|
211
|
+
const translated = vm.$t(content);
|
212
|
+
return translated.toLowerCase().includes(q) || content.toLowerCase().includes(q);
|
213
|
+
});
|
214
|
+
}
|
215
|
+
return true;
|
216
|
+
};
|
217
|
+
|
218
|
+
/**
|
219
|
+
* @type {ComputedRef<TableItem[]>}
|
220
|
+
*/
|
221
|
+
const filteredItems = computed(() => items.value.filter(item => handleFilter(item.value, search.value, item)));
|
222
|
+
const numberOfItems = computed(() => filteredItems.value.length);
|
223
|
+
|
224
|
+
/**
|
225
|
+
* @type {ComputedRef<Array<{text: string, value: string}>>}
|
226
|
+
*/
|
173
227
|
const translatedHeaders = computed(() => {
|
174
228
|
return props.headers.map((hd) => {
|
175
229
|
hd.text = vm.$t(hd.text);
|
@@ -177,42 +231,33 @@
|
|
177
231
|
});
|
178
232
|
});
|
179
233
|
|
234
|
+
/**
|
235
|
+
* @type {Ref<UnwrapRef<number>>}
|
236
|
+
*/
|
180
237
|
const itemsPerPageRef = ref(props.itemsPerPage);
|
181
238
|
const numberOfPages = computed(() => {
|
182
|
-
return Math.ceil(
|
239
|
+
return Math.ceil(numberOfItems.value / itemsPerPageRef.value);
|
183
240
|
});
|
184
|
-
|
241
|
+
/**
|
242
|
+
* @type {Ref<UnwrapRef<number>>}
|
243
|
+
*/
|
185
244
|
const page = ref(1);
|
186
245
|
const itemsFrom = computed(() => ((page.value - 1) * itemsPerPageRef.value) + 1);
|
187
246
|
const itemsTo = computed(() => {
|
188
247
|
const last = page.value * itemsPerPageRef.value;
|
189
|
-
return last <
|
248
|
+
return last < numberOfItems.value ? last : numberOfItems.value;
|
190
249
|
});
|
191
250
|
|
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
251
|
return {
|
209
|
-
search
|
252
|
+
search,
|
210
253
|
page,
|
211
254
|
items,
|
255
|
+
filteredItems,
|
212
256
|
itemsPerPageRef,
|
213
257
|
itemsFrom,
|
214
258
|
itemsTo,
|
215
259
|
numberOfPages,
|
260
|
+
numberOfItems,
|
216
261
|
nextPage() {
|
217
262
|
if (page.value + 1 <= numberOfPages.value) {
|
218
263
|
page.value += 1;
|
@@ -233,13 +278,47 @@
|
|
233
278
|
};
|
234
279
|
</script>
|
235
280
|
|
236
|
-
<style lang="scss"
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
281
|
+
<style lang="scss" scoped>
|
282
|
+
::v-deep{
|
283
|
+
.vcs-table {
|
284
|
+
td {
|
285
|
+
overflow: hidden;
|
286
|
+
text-overflow: ellipsis;
|
287
|
+
white-space: nowrap;
|
288
|
+
&.overflow-max-width{
|
289
|
+
max-width: 160px;
|
290
|
+
}
|
291
|
+
&.v-data-table__mobile-row{
|
292
|
+
justify-content: left;
|
293
|
+
height: 27px;
|
294
|
+
min-height: auto;
|
295
|
+
}
|
296
|
+
}
|
297
|
+
th.sortable{
|
298
|
+
padding: 0 8px;
|
299
|
+
span{
|
300
|
+
vertical-align: middle;
|
301
|
+
padding: 0 4px 0 0;
|
302
|
+
}
|
303
|
+
}
|
304
|
+
}
|
305
|
+
.v-data-table__mobile-row__cell{
|
306
|
+
td.vcs-table.overflow-max-width{
|
307
|
+
max-width: 260px;
|
308
|
+
}
|
309
|
+
}
|
310
|
+
.v-btn.vcs-button--small{
|
311
|
+
height: 100% !important;
|
312
|
+
display: block;
|
313
|
+
}
|
314
|
+
}
|
315
|
+
.vcs-pagination-bar{
|
316
|
+
.vcs-button-wrap{
|
317
|
+
height: 25px;
|
318
|
+
border: 1px solid lightgrey;
|
319
|
+
padding: 0 4px;
|
320
|
+
background-color: var(--v-basic-base);
|
321
|
+
border-radius: 4px;
|
243
322
|
}
|
244
323
|
}
|
245
324
|
</style>
|
@@ -31,6 +31,14 @@
|
|
31
31
|
</template>
|
32
32
|
<script>
|
33
33
|
|
34
|
+
import {
|
35
|
+
VIcon,
|
36
|
+
VListItem,
|
37
|
+
VListItemAvatar,
|
38
|
+
VListItemContent,
|
39
|
+
VListItemSubtitle,
|
40
|
+
VListItemTitle,
|
41
|
+
} from 'vuetify/lib';
|
34
42
|
import BalloonComponent from './BalloonComponent.vue';
|
35
43
|
|
36
44
|
/**
|
@@ -38,7 +46,15 @@
|
|
38
46
|
*/
|
39
47
|
export default {
|
40
48
|
name: 'AddressBalloonComponent',
|
41
|
-
components: {
|
49
|
+
components: {
|
50
|
+
BalloonComponent,
|
51
|
+
VListItem,
|
52
|
+
VListItemAvatar,
|
53
|
+
VIcon,
|
54
|
+
VListItemContent,
|
55
|
+
VListItemTitle,
|
56
|
+
VListItemSubtitle,
|
57
|
+
},
|
42
58
|
};
|
43
59
|
</script>
|
44
60
|
|
@@ -1,40 +1,42 @@
|
|
1
1
|
<template>
|
2
2
|
<v-card
|
3
|
-
class="mx-auto"
|
3
|
+
class="mx-auto mb-1 elevation-0"
|
4
4
|
max-width="400"
|
5
5
|
v-if="position"
|
6
6
|
>
|
7
7
|
<slot name="balloon-header" :attrs="{...$props, ...$attrs}">
|
8
|
-
<v-list-item
|
8
|
+
<v-list-item class="px-2 pr-1 align-start">
|
9
9
|
<v-list-item-avatar
|
10
10
|
tile
|
11
|
-
size="
|
11
|
+
size="20"
|
12
|
+
class="mr-2 align-self-start"
|
12
13
|
>
|
13
14
|
<v-icon color="primary">
|
14
15
|
$vcsInfo
|
15
16
|
</v-icon>
|
16
17
|
</v-list-item-avatar>
|
17
|
-
<v-list-item-content>
|
18
|
-
<v-list-item-title class="text-
|
19
|
-
{{ title }}
|
18
|
+
<v-list-item-content class="py-2 pr-1 align-self-start">
|
19
|
+
<v-list-item-title class="text-h6">
|
20
|
+
{{ $t(title) }}
|
20
21
|
</v-list-item-title>
|
21
|
-
<v-list-item-subtitle
|
22
|
+
<v-list-item-subtitle v-if="subtitle">
|
23
|
+
{{ $t(subtitle) }}
|
24
|
+
</v-list-item-subtitle>
|
22
25
|
</v-list-item-content>
|
23
26
|
<VcsButton
|
24
27
|
@click.stop="close"
|
25
28
|
small
|
26
29
|
icon="mdi-close-thick"
|
27
|
-
class="mb-8"
|
28
30
|
/>
|
29
31
|
</v-list-item>
|
30
32
|
</slot>
|
31
33
|
|
32
34
|
<v-divider />
|
33
35
|
|
34
|
-
<v-card class="overflow-y-auto" max-height="250">
|
36
|
+
<v-card class="overflow-y-auto py-2 elevation-0" max-height="250">
|
35
37
|
<slot :attrs="{...$props, ...$attrs}">
|
36
38
|
<v-list v-for="(value, name, index) in attributes" :key="`attribute-${index}`">
|
37
|
-
<v-list-item>
|
39
|
+
<v-list-item class="px-2">
|
38
40
|
<v-list-item-content>
|
39
41
|
<v-list-item-title>
|
40
42
|
{{ name }}
|
@@ -50,6 +52,15 @@
|
|
50
52
|
<script>
|
51
53
|
|
52
54
|
import { inject, onMounted, onUnmounted, watch } from 'vue';
|
55
|
+
import {
|
56
|
+
VCard, VDivider,
|
57
|
+
VIcon,
|
58
|
+
VList,
|
59
|
+
VListItem,
|
60
|
+
VListItemAvatar,
|
61
|
+
VListItemContent, VListItemSubtitle,
|
62
|
+
VListItemTitle,
|
63
|
+
} from 'vuetify/lib';
|
53
64
|
import { setupBalloonPositionListener } from './balloonHelper.js';
|
54
65
|
import VcsButton from '../components/buttons/VcsButton.vue';
|
55
66
|
|
@@ -66,7 +77,18 @@
|
|
66
77
|
*/
|
67
78
|
export default {
|
68
79
|
name: 'BalloonComponent',
|
69
|
-
components: {
|
80
|
+
components: {
|
81
|
+
VcsButton,
|
82
|
+
VCard,
|
83
|
+
VList,
|
84
|
+
VListItem,
|
85
|
+
VListItemAvatar,
|
86
|
+
VIcon,
|
87
|
+
VListItemContent,
|
88
|
+
VListItemTitle,
|
89
|
+
VListItemSubtitle,
|
90
|
+
VDivider,
|
91
|
+
},
|
70
92
|
props: {
|
71
93
|
featureId: {
|
72
94
|
type: String,
|
@@ -125,16 +147,28 @@
|
|
125
147
|
};
|
126
148
|
</script>
|
127
149
|
|
128
|
-
<style>
|
129
|
-
.balloon:
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
150
|
+
<style lang="scss">
|
151
|
+
.balloon hr:first-child {
|
152
|
+
display: none;
|
153
|
+
}
|
154
|
+
.balloon:after {
|
155
|
+
content: "";
|
156
|
+
position: absolute;
|
157
|
+
bottom: -20px;
|
158
|
+
left: 40px;
|
159
|
+
border-width: 20px 20px 0;
|
160
|
+
border-style: solid;
|
161
|
+
display: block;
|
162
|
+
width: 0;
|
163
|
+
}
|
164
|
+
.v-sheet.theme--light.balloon:after{
|
165
|
+
border-color: #FFFFFF transparent;
|
166
|
+
}
|
167
|
+
.v-sheet.theme--dark.balloon:after{
|
168
|
+
border-color: #1E1E1E transparent;
|
169
|
+
}
|
170
|
+
.balloon .v-list-item .v-list-item__title,
|
171
|
+
.balloon .v-list-item .v-list-item__subtitle {
|
172
|
+
line-height: 18px;
|
173
|
+
}
|
140
174
|
</style>
|
@@ -105,10 +105,10 @@ class BalloonFeatureInfoView extends AbstractFeatureInfoView {
|
|
105
105
|
return {
|
106
106
|
...properties,
|
107
107
|
position: featureInfo.position ?? getPositionFromFeature(featureInfo.feature),
|
108
|
-
title: this.title ?
|
108
|
+
title: this.title != null ?
|
109
109
|
extractNestedKey(this.title, properties.attributes, this.title) :
|
110
110
|
properties.layerProperties.title,
|
111
|
-
subtitle: this.subtitle ?
|
111
|
+
subtitle: this.subtitle != null ?
|
112
112
|
extractNestedKey(this.subtitle, properties.attributes, this.subtitle) :
|
113
113
|
properties.featureId,
|
114
114
|
};
|
@@ -59,7 +59,7 @@ function getLogger() {
|
|
59
59
|
* @param {string} defaultFillColor
|
60
60
|
* @returns {import("ol/style/Style").default|import("@vcmap/core").VectorStyleItem}
|
61
61
|
*/
|
62
|
-
function getHighlightStyle(feature, layer, defaultFillColor) {
|
62
|
+
export function getHighlightStyle(feature, layer, defaultFillColor) {
|
63
63
|
if (layer && layer.highlightStyle) {
|
64
64
|
return layer.highlightStyle;
|
65
65
|
}
|
@@ -175,6 +175,12 @@ function setupFeatureInfoTool(app) {
|
|
175
175
|
*/
|
176
176
|
export const featureInfoClassRegistry = new ClassRegistry();
|
177
177
|
|
178
|
+
/**
|
179
|
+
* Symbol added to features to overwrite the layers predefined feature info
|
180
|
+
* @type {symbol}
|
181
|
+
*/
|
182
|
+
export const featureInfoViewSymbol = Symbol('featureInfoView');
|
183
|
+
|
178
184
|
/**
|
179
185
|
* @class FeatureInfo
|
180
186
|
* @description Provides registration of featureInfoClasses and stores featureInfoView instances in its collection.
|
@@ -355,7 +361,9 @@ class FeatureInfo {
|
|
355
361
|
checkMaybe(windowPosition, [Number]);
|
356
362
|
checkMaybe(featureInfoView, AbstractFeatureInfoView);
|
357
363
|
|
358
|
-
const usedFeatureInfoView =
|
364
|
+
const usedFeatureInfoView = feature[featureInfoViewSymbol] ??
|
365
|
+
featureInfoView ??
|
366
|
+
this._getFeatureInfoViewForFeature(feature);
|
359
367
|
const layer = this._app.layers.getByKey(feature[vcsLayerName]);
|
360
368
|
|
361
369
|
if (usedFeatureInfoView && layer) {
|
package/src/i18n/de.js
CHANGED
@@ -44,11 +44,15 @@ const messages = {
|
|
44
44
|
components: {
|
45
45
|
title: 'Komponenten',
|
46
46
|
tooltip: 'Komponenten',
|
47
|
+
vcsFormSection: {
|
48
|
+
help: 'Hilfe anzeigen.',
|
49
|
+
},
|
47
50
|
vcsTable: {
|
48
51
|
key: 'Name',
|
49
52
|
value: 'Wert',
|
50
53
|
searchbarPlaceholder: 'Name, Wert, ...',
|
51
54
|
itemsPerPage: 'pro Seite',
|
55
|
+
ofItems: 'von',
|
52
56
|
nextPage: 'Nächste Seite',
|
53
57
|
formerPage: 'Vorherige Seite',
|
54
58
|
},
|
@@ -69,12 +73,23 @@ const messages = {
|
|
69
73
|
openInNew: 'In neuem Browser Tab öffnen',
|
70
74
|
defaultLabelText: 'Text',
|
71
75
|
},
|
76
|
+
search: {
|
77
|
+
title: 'Suche',
|
78
|
+
tooltip: 'Suche',
|
79
|
+
select: 'Suchergebnis selektieren',
|
80
|
+
placeholder: 'Suche nach Straße, Adresse, Ort, POI',
|
81
|
+
zoomToFeatureAction: 'Auf Ergebnis zoomen',
|
82
|
+
},
|
72
83
|
toolbox: {
|
73
84
|
flight: 'Flug',
|
74
85
|
miscellaneous: 'Verschiedenes',
|
75
86
|
},
|
76
87
|
footer: {
|
77
88
|
title: 'Fußzeile',
|
89
|
+
attributions: {
|
90
|
+
title: 'Attribution',
|
91
|
+
tooltip: 'Öffne Attribution Fenster',
|
92
|
+
},
|
78
93
|
},
|
79
94
|
};
|
80
95
|
|
package/src/i18n/en.js
CHANGED
@@ -44,11 +44,15 @@ const messages = {
|
|
44
44
|
components: {
|
45
45
|
title: 'Components',
|
46
46
|
tooltip: 'Components',
|
47
|
+
vcsFormSection: {
|
48
|
+
help: 'Show help.',
|
49
|
+
},
|
47
50
|
vcsTable: {
|
48
51
|
key: 'Name',
|
49
52
|
value: 'Value',
|
50
53
|
searchbarPlaceholder: 'Name, Value, ...',
|
51
54
|
itemsPerPage: 'per page',
|
55
|
+
ofItems: 'of',
|
52
56
|
nextPage: 'Next page',
|
53
57
|
formerPage: 'Former page',
|
54
58
|
},
|
@@ -69,12 +73,23 @@ const messages = {
|
|
69
73
|
openInNew: 'Open in new tab',
|
70
74
|
defaultLabelText: 'Text',
|
71
75
|
},
|
76
|
+
search: {
|
77
|
+
title: 'Search',
|
78
|
+
tooltip: 'Search',
|
79
|
+
select: 'Select result item',
|
80
|
+
placeholder: 'Search for Street, Address, Landmark, POI',
|
81
|
+
zoomToFeatureAction: 'Zoom to result',
|
82
|
+
},
|
72
83
|
toolbox: {
|
73
84
|
flight: 'flight',
|
74
85
|
miscellaneous: 'miscellaneous',
|
75
86
|
},
|
76
87
|
footer: {
|
77
88
|
title: 'Footer',
|
89
|
+
attributions: {
|
90
|
+
title: 'Attributions',
|
91
|
+
tooltip: 'Open Attributions Window',
|
92
|
+
},
|
78
93
|
},
|
79
94
|
};
|
80
95
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import { getShapeFromOptions } from '@vcmap/core';
|
2
|
-
import
|
2
|
+
import { ref } from 'vue';
|
3
3
|
|
4
4
|
/**
|
5
5
|
* @enum {string}
|
@@ -104,6 +104,7 @@ export function getImageSrcFromShape(image) {
|
|
104
104
|
|
105
105
|
/**
|
106
106
|
* @typedef {Object} LegendEntry
|
107
|
+
* @property {string} key
|
107
108
|
* @property {string} title - layer or entry name
|
108
109
|
* @property {Array<LegendItem>} legend - legend properties
|
109
110
|
* @property {Array<VcsAction>} actions - popout actions
|
@@ -111,11 +112,12 @@ export function getImageSrcFromShape(image) {
|
|
111
112
|
|
112
113
|
/**
|
113
114
|
* creates a LegendEntry with title, options and optionally popout action to the entries array
|
115
|
+
* @param {string} key - layerName
|
114
116
|
* @param {string} title
|
115
117
|
* @param {Array<LegendItem>} legend
|
116
118
|
* @returns {LegendEntry}
|
117
119
|
*/
|
118
|
-
export function createLayerLegendEntry(title, legend) {
|
120
|
+
export function createLayerLegendEntry(key, title, legend) {
|
119
121
|
const actions = [];
|
120
122
|
legend.forEach((item) => {
|
121
123
|
// XXX only one popout button allowed. Rethink if use case for multiple popout buttons comes up.
|
@@ -128,19 +130,19 @@ export function createLayerLegendEntry(title, legend) {
|
|
128
130
|
});
|
129
131
|
}
|
130
132
|
});
|
131
|
-
return { title, legend, actions };
|
133
|
+
return { key, title, legend, actions };
|
132
134
|
}
|
133
135
|
|
134
136
|
/**
|
135
137
|
*
|
136
138
|
* @param {VcsUiApp} app
|
137
|
-
* @returns {{entries: import("vue").
|
139
|
+
* @returns {{entries: import("vue").Ref<Array<LegendEntry>>, destroy: (function():void)}}
|
138
140
|
*/
|
139
141
|
export function getLegendEntries(app) {
|
140
142
|
/**
|
141
|
-
* @type {import("vue").
|
143
|
+
* @type {import("vue").Ref<Array<LegendEntry>>}>}
|
142
144
|
*/
|
143
|
-
const entries =
|
145
|
+
const entries = ref([]);
|
144
146
|
/**
|
145
147
|
* @type {Object<string,function():void>}
|
146
148
|
*/
|
@@ -150,11 +152,14 @@ export function getLegendEntries(app) {
|
|
150
152
|
* @param {import("@vcmap/core").Layer} layer
|
151
153
|
*/
|
152
154
|
function removeEntryForLayer(layer) {
|
153
|
-
const
|
154
|
-
|
155
|
-
if (
|
156
|
-
|
157
|
-
|
155
|
+
const layerName = layer.name;
|
156
|
+
const index = entries.value.findIndex(({ key }) => { return key === layerName; });
|
157
|
+
if (index >= 0) {
|
158
|
+
entries.value.splice(index, 1);
|
159
|
+
}
|
160
|
+
if (styleChangedListener[layerName]) {
|
161
|
+
styleChangedListener[layerName]();
|
162
|
+
delete styleChangedListener[layerName];
|
158
163
|
}
|
159
164
|
}
|
160
165
|
|
@@ -170,7 +175,8 @@ export function getLegendEntries(app) {
|
|
170
175
|
const title = layer.properties.title || layer.name;
|
171
176
|
const legend = layer.style?.properties?.legend ?? layer.properties?.legend;
|
172
177
|
if (legend) {
|
173
|
-
|
178
|
+
const legendEntry = createLayerLegendEntry(key, title, legend);
|
179
|
+
entries.value.push(legendEntry);
|
174
180
|
}
|
175
181
|
if (layer.styleChanged) {
|
176
182
|
styleChangedListener[layer.name] = layer.styleChanged.addEventListener(() => syncLayerLegendEntries(layer));
|