@vcmap/ui 5.0.0-rc.29 → 5.0.0-rc.30

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 (41) hide show
  1. package/config/dev.config.json +4 -0
  2. package/dist/assets/cesium.js +1 -1
  3. package/dist/assets/{core.74da2a.js → core.b16511.js} +2 -2
  4. package/dist/assets/core.js +1 -1
  5. package/dist/assets/index-c115e3a1.js +1 -0
  6. package/dist/assets/ol.js +1 -1
  7. package/dist/assets/{ui.d3054c.css → ui.ab815e.css} +2 -2
  8. package/dist/assets/{ui.d3054c.js → ui.ab815e.js} +4081 -3460
  9. package/dist/assets/ui.js +1 -1
  10. package/dist/assets/vue.js +2 -2
  11. package/dist/assets/{vuetify.946bd8.js → vuetify.ea3fa8.js} +1 -1
  12. package/dist/assets/vuetify.js +2 -2
  13. package/dist/index.html +1 -1
  14. package/index.js +6 -0
  15. package/package.json +1 -1
  16. package/plugins/@vcmap/search-nominatim/SearchNominatimEditor.vue +90 -0
  17. package/plugins/@vcmap/search-nominatim/index.js +37 -0
  18. package/plugins/@vcmap-show-case/form-inputs-example/FormInputsExample.vue +37 -1
  19. package/plugins/@vcmap-show-case/form-inputs-example/index.js +3 -0
  20. package/plugins/@vcmap-show-case/form-inputs-example/validation.js +11 -0
  21. package/plugins/@vcmap-show-case/style-input-example/styleExample.vue +0 -1
  22. package/plugins/@vcmap-show-case/vector-properties-example/index.js +40 -0
  23. package/plugins/@vcmap-show-case/vector-properties-example/package.json +5 -0
  24. package/plugins/@vcmap-show-case/vector-properties-example/vectorPropertiesExample.vue +109 -0
  25. package/src/components/form-inputs-controls/VcsChipArrayInput.vue +282 -0
  26. package/src/components/form-inputs-controls/VcsTextField.vue +9 -3
  27. package/src/components/plugins/AbstractConfigEditor.vue +84 -0
  28. package/src/components/style/VcsImageSelector.vue +6 -5
  29. package/src/components/style/VcsTextSelector.vue +1 -1
  30. package/src/components/vector-properties/VcsVectorPropertiesComponent.vue +737 -0
  31. package/src/components/vector-properties/composables.js +93 -0
  32. package/src/i18n/de.js +40 -9
  33. package/src/i18n/en.js +38 -7
  34. package/src/manager/collectionManager/collectionComponent.js +1 -1
  35. package/src/pluginHelper.js +57 -17
  36. package/src/vcsUiApp.js +17 -27
  37. package/dist/assets/index-cb070eff.js +0 -1
  38. /package/dist/assets/{cesium.16590b.js → cesium.eaf7cc.js} +0 -0
  39. /package/dist/assets/{ol.50a512.js → ol.4bbf0f.js} +0 -0
  40. /package/dist/assets/{vue.30740e.js → vue.67e80f.js} +0 -0
  41. /package/dist/assets/{vuetify.946bd8.css → vuetify.ea3fa8.css} +0 -0
@@ -0,0 +1,282 @@
1
+ <template>
2
+ <div id="vcs-chip-array-input" class="d-flex d-inline-block align-center">
3
+ <v-btn
4
+ v-if="hasScrollbar"
5
+ :dense="isDense"
6
+ x-small
7
+ icon
8
+ :ripple="false"
9
+ elevation="0"
10
+ @click="vcsChipArrayInput.scrollLeft -= scrollDx"
11
+ >
12
+ <v-icon>mdi-chevron-left</v-icon>
13
+ </v-btn>
14
+ <div
15
+ id="vcs-chip-array-input"
16
+ ref="vcsChipArrayInput"
17
+ class="d-flex d-inline-block"
18
+ :class="{
19
+ 'overflow-x-auto': !column,
20
+ 'hide-scrollbar': !column,
21
+ row: column,
22
+ 'ma-1': !hasScrollbar,
23
+ }"
24
+ >
25
+ <div v-for="(item, index) in value" :key="index" class="py-1 pr-1">
26
+ <v-chip
27
+ v-if="selected !== index"
28
+ v-bind="{ ...$attrs }"
29
+ :small="isDense"
30
+ :disabled="disabled"
31
+ :close="deletableChips"
32
+ @click="select(index)"
33
+ @click:close="remove(index)"
34
+ class="pa-2"
35
+ >
36
+ <span class="text-truncate d-inline-block">{{ item }}</span>
37
+ </v-chip>
38
+ <VcsTextField
39
+ v-else
40
+ hide-details
41
+ :dense="isDense"
42
+ rounded
43
+ filled
44
+ autofocus
45
+ no-padding
46
+ :height="24"
47
+ v-bind="{ ...$attrs }"
48
+ v-model="editValue"
49
+ @keydown.esc="selected = -1"
50
+ @blur="selected = -1"
51
+ @keydown.enter="submitChange"
52
+ @click:append="submitChange"
53
+ @update:error="(err) => (isEditValid = !err)"
54
+ append-icon="mdi-check"
55
+ :style="{ width: `${inputWidth}px` }"
56
+ />
57
+ </div>
58
+ <div class="py-1">
59
+ <v-chip
60
+ v-if="adding === false"
61
+ v-bind="{ ...$attrs }"
62
+ :small="isDense"
63
+ :disabled="disabled"
64
+ @click="adding = true"
65
+ class="pa-2"
66
+ >
67
+ <v-icon>$vcsPlus</v-icon>
68
+ </v-chip>
69
+ <VcsTextField
70
+ v-else
71
+ hide-details
72
+ :dense="isDense"
73
+ rounded
74
+ filled
75
+ autofocus
76
+ no-padding
77
+ :height="24"
78
+ class="vcs-inside-chip"
79
+ v-model="newValue"
80
+ v-bind="{ ...$attrs }"
81
+ @keydown.enter="add(newValue)"
82
+ @keydown.esc="cancel"
83
+ @blur="cancel"
84
+ @click:append="add(newValue)"
85
+ @update:error="(err) => (isNewValid = !err)"
86
+ append-icon="mdi-check"
87
+ :style="{ width: `${inputWidth}px` }"
88
+ />
89
+ </div>
90
+ </div>
91
+ <v-btn
92
+ v-if="hasScrollbar"
93
+ :dense="isDense"
94
+ x-small
95
+ icon
96
+ :ripple="false"
97
+ elevation="0"
98
+ @click="vcsChipArrayInput.scrollLeft += scrollDx"
99
+ >
100
+ <v-icon>mdi-chevron-right</v-icon>
101
+ </v-btn>
102
+ </div>
103
+ </template>
104
+
105
+ <style lang="scss" scoped>
106
+ .hide-scrollbar {
107
+ -ms-overflow-style: none; /* IE and Edge */
108
+ scrollbar-width: none; /* Firefox */
109
+ }
110
+ .hide-scrollbar::-webkit-scrollbar {
111
+ display: none;
112
+ }
113
+ .v-chip {
114
+ display: flex;
115
+ max-width: 260px;
116
+ padding: 0 8px;
117
+ .v-chip__content {
118
+ display: flex;
119
+ }
120
+ }
121
+ .vcs-inside-chip {
122
+ ::v-deep {
123
+ .v-input__slot {
124
+ .v-input__append-inner {
125
+ margin-top: 5px;
126
+ }
127
+ }
128
+ .v-text-field--filled > .v-input__control > .v-input__slot,
129
+ .v-text-field--outlined > .v-input__control > .v-input__slot {
130
+ min-height: unset;
131
+ }
132
+ }
133
+ }
134
+ </style>
135
+
136
+ <script>
137
+ import { computed, nextTick, onMounted, ref } from 'vue';
138
+ import { VBtn, VChip, VIcon } from 'vuetify/lib';
139
+ import VcsTextField from './VcsTextField.vue';
140
+
141
+ /**
142
+ * @description Renders elements of an array as chips with an input field to edit or add new elements.
143
+ * Provides two height options depending on "dense" property
144
+ * Provides VcsTooltip to show error messages on focus
145
+ * When clicking esc key, previous input is restored.
146
+ * @vue-prop {('bottom' | 'left' | 'top' | 'right')} [tooltipPosition='right'] - Position of the error tooltip.
147
+ * @vue-prop {string} [type] - The input type (string or number)
148
+ * @vue-prop {boolean} [disabled] - Disables adding or removing new elements
149
+ * @vue-prop {boolean} [column] - Remove horizontal pagination and wrap items as needed
150
+ * @vue-prop {boolean} [scrollDx=20] - scroll amount in px
151
+ * @vue-prop {boolean} [deletableChips=true] - Adds a delete button to elements to remove them from array
152
+ * @vue-prop {number} [inputWidth=50] - Width of the text fields in px.
153
+ */
154
+ export default {
155
+ name: 'VcsChipArrayInput',
156
+ components: {
157
+ VBtn,
158
+ VcsTextField,
159
+ VChip,
160
+ VIcon,
161
+ },
162
+ props: {
163
+ value: {
164
+ type: Array,
165
+ required: true,
166
+ },
167
+ tooltipPosition: {
168
+ type: String,
169
+ default: 'right',
170
+ },
171
+ deletableChips: {
172
+ type: Boolean,
173
+ default: true,
174
+ },
175
+ inputWidth: {
176
+ type: Number,
177
+ default: 50,
178
+ },
179
+ disabled: {
180
+ type: Boolean,
181
+ default: false,
182
+ },
183
+ column: {
184
+ type: Boolean,
185
+ default: false,
186
+ },
187
+ scrollDx: {
188
+ type: Number,
189
+ default: 20,
190
+ },
191
+ },
192
+ setup(props, { attrs, emit }) {
193
+ const selected = ref(-1);
194
+ const adding = ref(false);
195
+ const isEditValid = ref(true);
196
+ const editValue = ref(undefined);
197
+ const isNewValid = ref(true);
198
+ const newValue = ref(undefined);
199
+ const vcsChipArrayInput = ref();
200
+ const hasScrollbar = ref();
201
+ const isDense = computed(() => attrs.dense !== false);
202
+
203
+ function emitValue(value) {
204
+ if (attrs.type === 'number') {
205
+ emit(
206
+ 'input',
207
+ value.map((v) => parseFloat(v)),
208
+ );
209
+ } else {
210
+ emit('input', value);
211
+ }
212
+ }
213
+
214
+ function updateHasScrollbar() {
215
+ if (!props.column) {
216
+ hasScrollbar.value =
217
+ vcsChipArrayInput.value.scrollWidth -
218
+ vcsChipArrayInput.value.clientWidth >
219
+ 36; // size of the scroll buttons
220
+ }
221
+ }
222
+
223
+ onMounted(() => updateHasScrollbar());
224
+
225
+ function remove(index) {
226
+ emitValue(props.value.toSpliced(index, 1));
227
+ updateHasScrollbar();
228
+ }
229
+
230
+ function select(index) {
231
+ if (!props.disabled) {
232
+ selected.value = index;
233
+ editValue.value = props.value[index];
234
+ }
235
+ }
236
+
237
+ function submitChange() {
238
+ if (isEditValid.value) {
239
+ emitValue(props.value.toSpliced(selected.value, 1, editValue.value));
240
+ selected.value = -1;
241
+ }
242
+ }
243
+
244
+ function cancel() {
245
+ newValue.value = undefined;
246
+ adding.value = false;
247
+ }
248
+
249
+ async function add(value) {
250
+ if (isNewValid.value) {
251
+ if (value) {
252
+ emitValue([...props.value, value]);
253
+ await nextTick();
254
+ updateHasScrollbar();
255
+ await nextTick();
256
+ vcsChipArrayInput.value.scrollLeft =
257
+ vcsChipArrayInput.value.scrollWidth;
258
+ }
259
+ newValue.value = undefined;
260
+ adding.value = true;
261
+ }
262
+ }
263
+
264
+ return {
265
+ selected,
266
+ adding,
267
+ editValue,
268
+ isEditValid,
269
+ newValue,
270
+ isNewValid,
271
+ isDense,
272
+ vcsChipArrayInput,
273
+ hasScrollbar,
274
+ remove,
275
+ select,
276
+ submitChange,
277
+ add,
278
+ cancel,
279
+ };
280
+ },
281
+ };
282
+ </script>
@@ -25,8 +25,9 @@
25
25
  v-on="{ ...$listeners, ...on }"
26
26
  :height="isDense ? 24 : 32"
27
27
  :rules="rules"
28
- class="py-1 primary--placeholder align-center"
28
+ class="primary--placeholder align-center"
29
29
  :class="{
30
+ 'py-1': !noPadding,
30
31
  'remove-outline': !isOutlined,
31
32
  'outline--current': focus,
32
33
  'outline--error': !!errorMessage,
@@ -134,7 +135,7 @@
134
135
  .v-icon {
135
136
  font-size: 16px;
136
137
  }
137
- fieldset {
138
+ .v-text-field--rounded fieldset {
138
139
  border-radius: 2px;
139
140
  border-color: var(--v-base-base);
140
141
  }
@@ -160,6 +161,7 @@
160
161
  * @vue-prop {('bottom' | 'left' | 'top' | 'right')} [tooltipPosition='right'] - Position of the error tooltip.
161
162
  * @vue-prop {string} unit - Unit for number input fields. Is displayed behind the number.
162
163
  * @vue-prop {boolean} showSpinButtons - If true, spin buttons are displayed in number input fields. Overrides Vuetify hide-spin-buttons.
164
+ * @vue-prop {boolean} noPadding - Padding is required for usage within rows. For standalone usage this prop removes class py-1.
163
165
  * @vue-computed {boolean} isClearable - Whether textfield is isClearable. Makes sure icon is only shown on focus, hover or error.
164
166
  * @vue-computed {boolean} isDense - Whether size of textfield is dense.
165
167
  * @vue-computed {boolean} isOutlined - Textfield is outlined on either hover, focus or error, if not disabled.
@@ -186,6 +188,10 @@
186
188
  type: Boolean,
187
189
  default: false,
188
190
  },
191
+ noPadding: {
192
+ type: Boolean,
193
+ default: false,
194
+ },
189
195
  },
190
196
  setup(props, { attrs, emit }) {
191
197
  const hover = ref(false);
@@ -223,7 +229,7 @@
223
229
  get() {
224
230
  if (
225
231
  attrs.type === 'number' &&
226
- attrs.value &&
232
+ Number.isFinite(attrs.value) &&
227
233
  props.unit &&
228
234
  !focus.value &&
229
235
  !hover.value
@@ -0,0 +1,84 @@
1
+ <template>
2
+ <v-container class="pa-0">
3
+ <v-form v-model="isValid" @submit.prevent="submit">
4
+ <slot />
5
+ <div class="d-flex gap-2 px-2 pt-2 pb-1">
6
+ <div class="d-flex gap-2 w-full justify-start">
7
+ <VcsFormButton v-if="showReset" icon="$vcsReturn" @click="reset" />
8
+ </div>
9
+ <div class="d-flex gap-2 w-full justify-end">
10
+ <VcsFormButton type="submit" variant="filled" :disabled="!isValid">
11
+ {{ $t('appConfigurator.apply') }}
12
+ </VcsFormButton>
13
+ <VcsFormButton @click.stop="cancel">
14
+ {{ $t('appConfigurator.cancel') }}
15
+ </VcsFormButton>
16
+ <VcsActionButtonList :actions="actions" button="VcsFormButton" />
17
+ </div>
18
+ </div>
19
+ </v-form>
20
+ </v-container>
21
+ </template>
22
+
23
+ <script>
24
+ import { VContainer, VForm } from 'vuetify/lib';
25
+ import { ref, inject } from 'vue';
26
+ import VcsFormButton from '../buttons/VcsFormButton.vue';
27
+ import VcsActionButtonList from '../buttons/VcsActionButtonList.vue';
28
+
29
+ /**
30
+ * @description Basic wrapper for all config editor components using {@link https://vuetifyjs.com/en/api/v-form/ |vuetify form}.
31
+ * Providing a footer with submit, cancel and optionally reset button.
32
+ * @vue-prop {boolean} [showReset=false] - Flag to show a reset button in the footer. You need to handle @reset in a child component.
33
+ * @vue-prop {Array<VcsAction>} [actions] - Optional actions rendered as ActionButtonList in the footer.
34
+ * @vue-event {Event} submit - Event fired on clicking the submit button.
35
+ * @vue-event {Event} cancel - Event fired on clicking the cancel button.
36
+ * @vue-event {Event} reset - Event fired on clicking the reset button.
37
+ */
38
+ export default {
39
+ name: 'AbstractConfigEditor',
40
+ components: {
41
+ VContainer,
42
+ VForm,
43
+ VcsFormButton,
44
+ VcsActionButtonList,
45
+ },
46
+ props: {
47
+ showReset: {
48
+ type: Boolean,
49
+ default: false,
50
+ },
51
+ actions: {
52
+ type: Array,
53
+ default: () => [],
54
+ },
55
+ },
56
+ setup(props, { attrs, emit }) {
57
+ const app = inject('vcsApp');
58
+
59
+ const close = () => {
60
+ if (app.windowManager.has(attrs['window-state'].id)) {
61
+ app.windowManager.remove(attrs['window-state'].id);
62
+ }
63
+ };
64
+
65
+ return {
66
+ isValid: ref(true),
67
+ submit(e) {
68
+ close();
69
+ emit('submit', e);
70
+ },
71
+ cancel(e) {
72
+ close();
73
+ attrs.setConfig();
74
+ emit('cancel', e);
75
+ },
76
+ reset(e) {
77
+ emit('reset', e);
78
+ },
79
+ };
80
+ },
81
+ };
82
+ </script>
83
+
84
+ <style scoped></style>
@@ -74,14 +74,15 @@
74
74
  :min="input.range?.[0] || 0"
75
75
  :max="input.range?.[1] || undefined"
76
76
  :rules="[
77
- (v) => !input.isRequired || !!v || 'components.style.required',
77
+ (v) =>
78
+ !input.isRequired || !!v || 'components.validation.required',
78
79
  (v) =>
79
80
  !input.range ||
80
81
  (!input.isRequired && !v) ||
81
82
  between(v, input.range) ||
82
- `${$t('components.style.allowedRange')}: ${input.range.join(
83
- ' - ',
84
- )}`,
83
+ `${$t(
84
+ 'components.validation.allowedRange',
85
+ )}: ${input.range.join(' - ')}`,
85
86
  ]"
86
87
  :show-spin-buttons="true"
87
88
  />
@@ -458,7 +459,7 @@
458
459
  if (Array.isArray(props.value?.scale)) {
459
460
  return props.value.scale[index];
460
461
  } else {
461
- return props.value.scale;
462
+ return props.value?.scale;
462
463
  }
463
464
  },
464
465
  set(value) {
@@ -32,7 +32,7 @@
32
32
  type="number"
33
33
  unit="px"
34
34
  v-model="fontSize"
35
- :rules="[(v) => (!!v && v > 0) || 'components.style.notValid']"
35
+ :rules="[(v) => (!!v && v > 0) || 'components.validation.notValid']"
36
36
  />
37
37
  </v-col>
38
38
  </v-row>