@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.
Files changed (105) hide show
  1. package/README.md +33 -31
  2. package/build/build.js +9 -0
  3. package/build/buildHelpers.js +12 -10
  4. package/build/commonViteConfig.js +3 -10
  5. package/config/base.config.json +26 -24
  6. package/config/dev.config.json +8 -0
  7. package/config/www.config.json +102 -17
  8. package/dist/assets/cesium.2e288a.js +137226 -0
  9. package/dist/assets/cesium.js +1 -1
  10. package/dist/assets/core.8014d3.js +14473 -0
  11. package/dist/assets/core.js +1 -1
  12. package/dist/assets/index.3f74fa92.js +1 -0
  13. package/dist/assets/ol.31c3a5.js +44279 -0
  14. package/dist/assets/ol.js +1 -1
  15. package/dist/assets/ui.36f84f.css +1 -0
  16. package/dist/assets/ui.36f84f.js +16101 -0
  17. package/dist/assets/ui.js +1 -1
  18. package/dist/assets/vue.a39c10.js +4675 -0
  19. package/dist/assets/vue.js +5 -2
  20. package/dist/assets/{vuetify.202322.css → vuetify.378637.css} +1 -1
  21. package/dist/assets/vuetify.378637.js +21019 -0
  22. package/dist/assets/vuetify.js +5 -2
  23. package/dist/index.html +1 -1
  24. package/index.js +4 -1
  25. package/package.json +10 -10
  26. package/plugins/@vcmap/pluginExample/index.js +14 -0
  27. package/plugins/@vcmap/pluginExample/pluginExampleComponent.vue +93 -67
  28. package/plugins/@vcmap/project-selector/ContextsListComponent.vue +8 -1
  29. package/plugins/@vcmap/project-selector/ProjectSelectorComponent.vue +27 -1
  30. package/plugins/@vcmap/search-nominatim/LICENSE.md +14 -0
  31. package/plugins/@vcmap/search-nominatim/README.md +2 -0
  32. package/plugins/@vcmap/search-nominatim/config.json +4 -0
  33. package/plugins/@vcmap/search-nominatim/index.js +26 -0
  34. package/plugins/@vcmap/search-nominatim/nominatim.js +170 -0
  35. package/plugins/@vcmap/search-nominatim/package.json +43 -0
  36. package/plugins/@vcmap/theme-changer/ThemeChangerComponent.vue +24 -0
  37. package/plugins/buttonExamples/ButtonExamples.vue +28 -1
  38. package/plugins/categoryTest/Categories.vue +16 -0
  39. package/plugins/categoryTest/Category.vue +30 -4
  40. package/plugins/example/mySuperComponent.vue +12 -1
  41. package/plugins/package.json +2 -1
  42. package/plugins/simple-graph/SimpleGraphComponent.vue +5 -11
  43. package/plugins/test/allIconsComponent.vue +16 -0
  44. package/plugins/test/editor.vue +3 -0
  45. package/plugins/test/emptyComponent.vue +3 -0
  46. package/plugins/test/vcsContent.vue +3 -0
  47. package/src/actions/actionHelper.js +103 -2
  48. package/src/actions/styleSelector.vue +9 -0
  49. package/src/application/VcsApp.vue +77 -9
  50. package/src/application/VcsAttributions.vue +63 -0
  51. package/src/application/VcsAttributionsFooter.vue +87 -0
  52. package/src/application/{Navbar.vue → VcsNavbar.vue} +35 -2
  53. package/src/application/VcsSettings.vue +4 -0
  54. package/src/application/attributionsHelper.js +150 -0
  55. package/src/application/vcsAppWrapper.vue +5 -1
  56. package/src/components/buttons/VcsActionButtonList.vue +8 -1
  57. package/src/components/buttons/VcsButton.vue +7 -1
  58. package/src/components/form-inputs-controls/VcsCheckbox.vue +7 -2
  59. package/src/components/form-inputs-controls/VcsColorPicker.vue +4 -0
  60. package/src/components/form-inputs-controls/VcsFormSection.vue +48 -2
  61. package/src/components/form-inputs-controls/VcsRadio.vue +7 -1
  62. package/src/components/form-inputs-controls/VcsSelect.vue +5 -1
  63. package/src/components/form-inputs-controls/VcsTextArea.vue +2 -0
  64. package/src/components/form-inputs-controls/VcsTextField.vue +6 -2
  65. package/src/components/lists/VcsActionList.vue +12 -1
  66. package/src/components/lists/VcsTreeview.vue +6 -1
  67. package/src/components/lists/VcsTreeviewLeaf.vue +5 -1
  68. package/src/components/lists/VcsTreeviewSearchbar.vue +5 -0
  69. package/src/components/notification/VcsTooltip.vue +14 -9
  70. package/src/components/tables/VcsTable.vue +117 -38
  71. package/src/featureInfo/AddressBalloonComponent.vue +17 -1
  72. package/src/featureInfo/BalloonComponent.vue +57 -23
  73. package/src/featureInfo/balloonFeatureInfoView.js +2 -2
  74. package/src/featureInfo/featureInfo.js +10 -2
  75. package/src/i18n/de.js +15 -0
  76. package/src/i18n/en.js +15 -0
  77. package/src/legend/legendHelper.js +18 -12
  78. package/src/legend/styleLegendItem.vue +20 -1
  79. package/src/legend/vcsLegend.vue +29 -3
  80. package/src/manager/toolbox/GroupToolboxComponent.vue +13 -1
  81. package/src/manager/toolbox/SelectToolboxComponent.vue +13 -1
  82. package/src/manager/toolbox/ToolboxManager.vue +3 -0
  83. package/src/manager/window/WindowComponent.vue +6 -0
  84. package/src/manager/window/WindowComponentHeader.vue +6 -1
  85. package/src/navigation/mapNavigation.vue +15 -36
  86. package/src/navigation/orientationToolsButton.vue +6 -1
  87. package/src/navigation/overviewMap.js +10 -39
  88. package/src/navigation/tiltSlider.vue +3 -0
  89. package/src/navigation/vcsCompass.vue +2 -0
  90. package/src/search/resultItem.vue +89 -0
  91. package/src/search/resultsComponent.vue +98 -0
  92. package/src/search/search.js +326 -0
  93. package/src/search/searchComponent.vue +90 -0
  94. package/src/styles/_typography.scss +3 -0
  95. package/src/styles/utils/_cursor.scss +4 -0
  96. package/src/styles/variables.scss +4 -1
  97. package/src/vcsUiApp.js +16 -0
  98. package/dist/assets/cesium.9489f8.js +0 -8699
  99. package/dist/assets/core.aa346a.js +0 -4
  100. package/dist/assets/index.3cd4fffa.js +0 -1
  101. package/dist/assets/ol.39651b.js +0 -439
  102. package/dist/assets/ui.15ef6a.css +0 -1
  103. package/dist/assets/ui.15ef6a.js +0 -71
  104. package/dist/assets/vue.cbe9d8.js +0 -9
  105. 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
- * @enum {string} TooltipPositions
115
- * @property {string} bottom
116
- * @property {string} left
117
- * @property {string} top
118
- * @property {string} right
119
- * @readonly
120
- * @module VcsTooltip
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="grey--text mx-2">{{ $t('components.vcsTable.itemsPerPage') }}</span>
64
- <span class="grey--text mx-2">{{ itemsFrom }} - {{ itemsTo }} of {{ items.length }}</span>
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="mx-2"
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="mx-1"
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|vuetify v-data-table }
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<{key:string,value:string}>} items - from attributes derived table items
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: { VcsButton, VcsTreeviewSearchbar },
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(items.value.length / itemsPerPageRef.value);
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 < items.value.length ? last : items.value.length;
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: ref(''),
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" scoped>
237
- .vcs-table {
238
- td {
239
- max-width: 160px;
240
- overflow: hidden;
241
- text-overflow: ellipsis;
242
- white-space: nowrap;
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: { BalloonComponent },
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 two-line>
8
+ <v-list-item class="px-2 pr-1 align-start">
9
9
  <v-list-item-avatar
10
10
  tile
11
- size="40"
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-h5">
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>{{ subtitle }}</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: { VcsButton },
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:before {
130
- content: "";
131
- position: absolute;
132
- bottom: -20px;
133
- left: 40px;
134
- border-width: 20px 20px 0;
135
- border-style: solid;
136
- border-color: white transparent;
137
- display: block;
138
- width: 0;
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 = featureInfoView ?? this._getFeatureInfoViewForFeature(feature);
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 Vue, { reactive } from 'vue';
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").Reactive<{string,LegendEntry}>, destroy: (function():void)}}
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").Reactive<{string,LegendEntry}>}
143
+ * @type {import("vue").Ref<Array<LegendEntry>>}>}
142
144
  */
143
- const entries = reactive({});
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 key = layer.name;
154
- Vue.delete(entries, key); // XXX Vue.delete can be removed on Vue3
155
- if (styleChangedListener[key]) {
156
- styleChangedListener[key]();
157
- delete styleChangedListener[key];
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
- Vue.set(entries, key, createLayerLegendEntry(title, legend)); // XXX Vue.set can be removed on Vue3
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));