@vcmap/ui 5.0.0-rc.28 → 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 (65) hide show
  1. package/config/base.config.json +22 -0
  2. package/config/dev.config.json +4 -0
  3. package/dist/assets/cesium/Workers/cesiumWorkerBootstrapper.js +1 -1
  4. package/dist/assets/cesium/Workers/package.js +1 -1
  5. package/dist/assets/cesium/Workers/transferTypedArrayTest.js +1 -1
  6. package/dist/assets/{cesium.973919.js → cesium.eaf7cc.js} +149552 -149560
  7. package/dist/assets/cesium.js +1 -1
  8. package/dist/assets/{core.7a2173.js → core.b16511.js} +4077 -3965
  9. package/dist/assets/core.js +1 -1
  10. package/dist/assets/index-c115e3a1.js +1 -0
  11. package/dist/assets/{ol.f6e2e4.js → ol.4bbf0f.js} +11405 -11126
  12. package/dist/assets/ol.js +1 -1
  13. package/dist/assets/{ui.bd7a9a.css → ui.ab815e.css} +2 -2
  14. package/dist/assets/{ui.bd7a9a.js → ui.ab815e.js} +4232 -3512
  15. package/dist/assets/ui.js +1 -1
  16. package/dist/assets/vue.js +2 -2
  17. package/dist/assets/{vuetify.95f6c3.js → vuetify.ea3fa8.js} +1 -1
  18. package/dist/assets/vuetify.js +2 -2
  19. package/dist/index.html +1 -1
  20. package/index.js +7 -0
  21. package/lib/olLib.js +6 -0
  22. package/package.json +3 -3
  23. package/plugins/@vcmap/search-nominatim/SearchNominatimEditor.vue +90 -0
  24. package/plugins/@vcmap/search-nominatim/index.js +37 -0
  25. package/plugins/@vcmap-show-case/form-inputs-example/FormInputsExample.vue +37 -1
  26. package/plugins/@vcmap-show-case/form-inputs-example/index.js +3 -0
  27. package/plugins/@vcmap-show-case/form-inputs-example/validation.js +11 -0
  28. package/plugins/@vcmap-show-case/style-input-example/styleExample.vue +0 -1
  29. package/plugins/@vcmap-show-case/vector-properties-example/index.js +40 -0
  30. package/plugins/@vcmap-show-case/vector-properties-example/package.json +5 -0
  31. package/plugins/@vcmap-show-case/vector-properties-example/vectorPropertiesExample.vue +109 -0
  32. package/plugins/@vcmap-show-case/window-tester/WindowExample.vue +27 -9
  33. package/plugins/@vcmap-show-case/window-tester/index.js +13 -1
  34. package/plugins/@vcmap-show-case/window-tester/windowExampleToggleChild.vue +11 -10
  35. package/src/actions/actionHelper.js +7 -3
  36. package/src/application/VcsApp.vue +31 -0
  37. package/src/components/form-inputs-controls/VcsChipArrayInput.vue +282 -0
  38. package/src/components/form-inputs-controls/VcsTextField.vue +12 -5
  39. package/src/components/icons/+all.js +3 -3
  40. package/src/components/lists/VcsTreeviewLeaf.vue +1 -1
  41. package/src/components/lists/VcsTreeviewSearchbar.vue +9 -4
  42. package/src/components/plugins/AbstractConfigEditor.vue +84 -0
  43. package/src/components/style/VcsImageSelector.vue +6 -5
  44. package/src/components/style/VcsTextSelector.vue +1 -1
  45. package/src/components/tables/VcsDataTable.vue +100 -13
  46. package/src/components/vector-properties/VcsVectorPropertiesComponent.vue +737 -0
  47. package/src/components/vector-properties/composables.js +93 -0
  48. package/src/contentTree/contentTreeCollection.js +3 -0
  49. package/src/featureInfo/abstractFeatureInfoView.js +3 -1
  50. package/src/featureInfo/balloonFeatureInfoView.js +3 -2
  51. package/src/featureInfo/featureInfo.js +1 -0
  52. package/src/i18n/de.js +44 -9
  53. package/src/i18n/en.js +42 -7
  54. package/src/manager/collectionManager/collectionComponent.js +1 -1
  55. package/src/manager/window/WindowComponent.vue +4 -1
  56. package/src/manager/window/WindowComponentHeader.vue +25 -13
  57. package/src/manager/window/windowManager.js +6 -2
  58. package/src/navigation/overviewMap.js +1 -1
  59. package/src/pluginHelper.js +57 -17
  60. package/src/uiConfig.js +1 -0
  61. package/src/vcsUiApp.js +45 -34
  62. package/dist/assets/index-1b09f88d.js +0 -1
  63. /package/dist/assets/{vue.d4be99.js → vue.67e80f.js} +0 -0
  64. /package/dist/assets/{vuetify.95f6c3.css → vuetify.ea3fa8.css} +0 -0
  65. /package/src/components/icons/{PolygonIcon.vue → PointIcon.vue} +0 -0
@@ -19,6 +19,16 @@
19
19
  const app = inject('vcsApp');
20
20
  const parentId = attrs['window-state'].id;
21
21
  const childId = `dynamicChild-${parentId}`;
22
+ const childComponent = {
23
+ id: childId,
24
+ parentId,
25
+ state: {
26
+ headerTitle: 'Example dynamicChild',
27
+ headerIcon: 'mdi-human-child',
28
+ },
29
+ component: WindowExampleContent,
30
+ slot: WindowSlot.DYNAMIC_CHILD,
31
+ };
22
32
 
23
33
  return {
24
34
  toggle(e) {
@@ -26,16 +36,7 @@
26
36
  app.windowManager.remove(childId);
27
37
  } else {
28
38
  e.stopPropagation();
29
- app.windowManager.add(
30
- {
31
- id: childId,
32
- parentId,
33
- headerTitle: 'Example dynamicChild',
34
- component: WindowExampleContent,
35
- slot: WindowSlot.DYNAMIC_CHILD,
36
- },
37
- owner,
38
- );
39
+ app.windowManager.add(childComponent, owner);
39
40
  }
40
41
  },
41
42
  };
@@ -284,7 +284,7 @@ export function createModalAction(actionOptions, modalComponent, app, owner) {
284
284
  /**
285
285
  * Creates an action which opens a given link in a new tab
286
286
  * @param {ActionOptions} actionOptions
287
- * @param {string} url
287
+ * @param {string|function():string} url
288
288
  * @returns {VcsAction}
289
289
  */
290
290
  export function createLinkAction(actionOptions, url) {
@@ -293,13 +293,17 @@ export function createLinkAction(actionOptions, url) {
293
293
  icon: [undefined, String],
294
294
  title: [undefined, String],
295
295
  });
296
- check(url, String);
296
+ check(url, [String, Function]);
297
297
 
298
298
  return {
299
299
  ...actionOptions,
300
300
  callback() {
301
301
  const link = document.createElement('a');
302
- link.href = url;
302
+ if (typeof url === 'string') {
303
+ link.href = url;
304
+ } else {
305
+ link.href = url();
306
+ }
303
307
  link.target = '_blank';
304
308
  link.click();
305
309
  },
@@ -94,6 +94,7 @@
94
94
  import VcsMap from './VcsMap.vue';
95
95
  import VcsNavbar from './VcsNavbar.vue';
96
96
  import {
97
+ createLinkAction,
97
98
  createMapButtonAction,
98
99
  createToggleAction,
99
100
  } from '../actions/actionHelper.js';
@@ -226,6 +227,9 @@
226
227
  state: {
227
228
  headerTitle: 'legend.title',
228
229
  headerIcon: '$vcsLegend',
230
+ infoUrl: app.getHelpUrlCallback(
231
+ '/components/contentspace.html#id_legend',
232
+ ),
229
233
  },
230
234
  slot: WindowSlot.DYNAMIC_RIGHT,
231
235
  props: { entries },
@@ -324,6 +328,29 @@
324
328
  };
325
329
  }
326
330
 
331
+ /**
332
+ * This helper function will add a help action button referencing VC Map help page to the apps NavbarManager MENU location.
333
+ * @param {VcsUiApp} app
334
+ */
335
+ export function setupHelpButton(app) {
336
+ const helpAction = createLinkAction(
337
+ {
338
+ name: 'help.title',
339
+ title: 'help.tooltip',
340
+ icon: '$vcsHelp',
341
+ },
342
+ app.getHelpUrlCallback(),
343
+ );
344
+ app.navbarManager.add(
345
+ {
346
+ id: 'helpButton',
347
+ action: helpAction,
348
+ },
349
+ vcsAppSymbol,
350
+ ButtonLocation.MENU,
351
+ );
352
+ }
353
+
327
354
  /**
328
355
  * This helper function will add a category manager button to the navbar. The category Manager
329
356
  * will only be shown if there is at least one category under management in the categoryManager.
@@ -343,6 +370,9 @@
343
370
  state: {
344
371
  headerTitle: 'categoryManager.title',
345
372
  headerIcon: '$vcsComponents',
373
+ infoUrl: app.getHelpUrlCallback(
374
+ '/components/contentspace.html#id_myWorkspace',
375
+ ),
346
376
  },
347
377
  component: CollectionManager,
348
378
  provides: {
@@ -517,6 +547,7 @@
517
547
  const mapNavbarListener = setupMapNavbar(app);
518
548
  const legendDestroy = setupLegendWindow(app);
519
549
  const settingsDestroy = setupSettingsWindow(app);
550
+ setupHelpButton(app);
520
551
  const destroyComponentsWindow = setupCategoryManagerWindow(app);
521
552
  const destroyThemingListener = setupUiConfigTheming(
522
553
  app,
@@ -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
@@ -233,8 +239,9 @@
233
239
  return attrs.value ?? '';
234
240
  }
235
241
  },
236
- set(event) {
237
- emit('input', event);
242
+ set() {
243
+ // emit is not needed, the vuetify component already emits an @input event. (forwarded listeners)
244
+ // emit('input', event);
238
245
  },
239
246
  });
240
247
  const type = computed(() => {
@@ -63,7 +63,7 @@ import PlayCircleIcon from './PlayCircleIcon.vue';
63
63
  import PlusIcon from './PlusIcon.vue';
64
64
  import PoiIcon from './PoiIcon.vue';
65
65
  import PointSelectIcon from './PointSelectIcon.vue';
66
- import PolygonIcon from './PolygonIcon.vue';
66
+ import PointIcon from './PointIcon.vue';
67
67
  import QueryIcon from './QueryIcon.vue';
68
68
  import PresentationModeIcon from './PresentationModeIcon.vue';
69
69
  import RectangleIcon from './RectangleIcon.vue';
@@ -328,8 +328,8 @@ const IconMap = {
328
328
  pointSelect: {
329
329
  component: PointSelectIcon,
330
330
  },
331
- polygon: {
332
- component: PolygonIcon,
331
+ point: {
332
+ component: PointIcon,
333
333
  },
334
334
  query: {
335
335
  component: QueryIcon,
@@ -14,7 +14,7 @@
14
14
  v-on="on"
15
15
  class="d-inline-block text-truncate"
16
16
  ref="titleElem"
17
- >{{ $t(item.title) }}</span
17
+ >{{ $t(item.title || item.name) }}</span
18
18
  >
19
19
  </template>
20
20
  </VcsTooltip>
@@ -6,7 +6,7 @@
6
6
  <v-icon class="search-icon my-0 ml-1" size="12"> $vcsSearch </v-icon>
7
7
  </slot>
8
8
 
9
- <slot>
9
+ <slot v-bind="{ ...$props, on: $listeners }">
10
10
  <v-text-field
11
11
  solo
12
12
  dense
@@ -19,7 +19,7 @@
19
19
  />
20
20
  </slot>
21
21
 
22
- <slot name="append">
22
+ <slot name="append" :has-filter="hasFilter">
23
23
  <v-icon v-if="hasFilter" class="ml-2" size="16">$vcsFilter</v-icon>
24
24
  </slot>
25
25
  </div>
@@ -111,9 +111,14 @@
111
111
  import { VIcon, VTextField } from 'vuetify/lib';
112
112
 
113
113
  /**
114
- * @description Stylized wrapper around vuetify divider
115
- * @vue-prop {number} height - Height of the component.
114
+ * @description stylized searchbar used in VcsTreeview, VcsDataTable and VcsList
116
115
  * @vue-prop {string} [placeholder='search.title'] - Placeholder will be displayed in the search field, and will be translated.
116
+ * @vue-prop {string[]} [customClasses] - CSS classes to customize style
117
+ * @vue-prop {string} [value] - The search value
118
+ * @vue-prop {boolean} [hasFilter=false] - Appends a filter icon
119
+ * @vue-data {slot} [prepend] - prepend slot overwriting search icon
120
+ * @vue-data {slot} [default] - default slot overwriting search input text field. binds all props
121
+ * @vue-data {slot} [append] - append slot overwriting filter icon. binds hasFilter prop
117
122
  */
118
123
  export default {
119
124
  name: 'VcsTreeviewSearchbar',
@@ -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>