@vcmap/ui 5.0.0-rc.15 → 5.0.0-rc.17

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 (91) hide show
  1. package/build/buildHelpers.js +7 -1
  2. package/config/base.config.json +7 -45
  3. package/config/dev.config.json +5 -1
  4. package/config/www.config.json +14 -13
  5. package/dist/assets/{cesium.2e288a.js → cesium.41de56.js} +0 -0
  6. package/dist/assets/cesium.js +1 -1
  7. package/dist/assets/{core.8014d3.js → core.af84e3.js} +6077 -4544
  8. package/dist/assets/core.js +1 -1
  9. package/dist/assets/{index.3f74fa92.js → index.5b773cad.js} +1 -1
  10. package/dist/assets/{ol.31c3a5.js → ol.5c7490.js} +0 -0
  11. package/dist/assets/ol.js +1 -1
  12. package/dist/assets/ui.dffe32.css +1 -0
  13. package/dist/assets/{ui.36f84f.js → ui.dffe32.js} +7243 -6234
  14. package/dist/assets/ui.js +1 -1
  15. package/dist/assets/{vue.a39c10.js → vue.25da17.js} +0 -0
  16. package/dist/assets/vue.js +2 -2
  17. package/dist/assets/{vuetify.378637.css → vuetify.e4ece7.css} +1 -1
  18. package/dist/assets/{vuetify.378637.js → vuetify.e4ece7.js} +5 -2
  19. package/dist/assets/vuetify.js +2 -2
  20. package/dist/index.html +1 -1
  21. package/index.html +77 -0
  22. package/index.js +18 -3
  23. package/package.json +4 -2
  24. package/plugins/@vcmap/create-link/fallbackCreateLink.vue +4 -1
  25. package/plugins/@vcmap/create-link/index.js +4 -1
  26. package/plugins/@vcmap/pluginExample/exampleActions.js +45 -0
  27. package/plugins/@vcmap/pluginExample/index.js +26 -2
  28. package/plugins/@vcmap/pluginExample/pluginExampleComponent.vue +77 -42
  29. package/plugins/@vcmap/search-nominatim/nominatim.js +1 -1
  30. package/plugins/@vcmap/theme-changer/ThemeChangerComponent.vue +4 -2
  31. package/plugins/categoryTest/Categories.vue +27 -13
  32. package/plugins/categoryTest/Category.vue +7 -1
  33. package/plugins/categoryTest/index.js +1 -1
  34. package/plugins/notifier/index.js +31 -0
  35. package/plugins/notifier/notifierTester.vue +88 -0
  36. package/plugins/package.json +1 -1
  37. package/plugins/test/allIconsComponent.vue +5 -5
  38. package/plugins/test/emptyComponent.vue +1 -1
  39. package/plugins/test/index.js +27 -3
  40. package/plugins/test/myCustomHeader.vue +9 -1
  41. package/plugins/test/testList.vue +290 -0
  42. package/plugins/test/vcsContent.vue +1 -1
  43. package/plugins/test/windowManagerExample.vue +12 -7
  44. package/plugins/wizardExample/index.js +41 -0
  45. package/plugins/wizardExample/wizardExample.vue +77 -0
  46. package/src/actions/actionHelper.js +10 -9
  47. package/src/application/VcsApp.vue +43 -34
  48. package/src/components/form-inputs-controls/VcsCheckbox.vue +1 -0
  49. package/src/components/form-inputs-controls/VcsFormSection.vue +23 -15
  50. package/src/components/form-inputs-controls/VcsSelect.vue +33 -1
  51. package/src/components/form-inputs-controls/VcsTextField.vue +11 -3
  52. package/src/components/form-inputs-controls/VcsWizard.vue +133 -0
  53. package/src/components/imageElementInjector.vue +22 -0
  54. package/src/components/lists/VcsList.vue +468 -0
  55. package/src/components/lists/VcsTreeview.vue +1 -2
  56. package/src/components/lists/VcsTreeviewLeaf.vue +18 -50
  57. package/src/components/lists/VcsTreeviewSearchbar.vue +1 -23
  58. package/src/components/tables/VcsTable.vue +13 -1
  59. package/src/contentTree/LayerTree.vue +1 -1
  60. package/src/contentTree/contentTreeCollection.js +9 -0
  61. package/src/contentTree/contentTreeItem.js +13 -13
  62. package/src/contentTree/layerContentTreeItem.js +1 -1
  63. package/src/contentTree/subContentTreeItem.js +1 -1
  64. package/src/contentTree/vcsObjectContentTreeItem.js +1 -1
  65. package/src/featureInfo/BalloonComponent.vue +13 -8
  66. package/src/featureInfo/balloonFeatureInfoView.js +16 -22
  67. package/src/featureInfo/balloonHelper.js +26 -5
  68. package/src/featureInfo/featureInfo.js +14 -2
  69. package/src/featureInfo/featureInfoInteraction.js +1 -1
  70. package/src/i18n/de.js +13 -1
  71. package/src/i18n/en.js +13 -1
  72. package/src/icons/+all.js +4 -0
  73. package/src/icons/WandIcon.vue +63 -0
  74. package/src/manager/categoryManager/CategoryComponent.vue +115 -0
  75. package/src/manager/categoryManager/CategoryComponentList.vue +57 -0
  76. package/src/manager/categoryManager/CategoryManager.vue +35 -0
  77. package/src/manager/categoryManager/categoryManager.js +251 -165
  78. package/src/manager/contextMenu/contextMenuManager.js +8 -2
  79. package/src/manager/window/WindowComponent.vue +51 -70
  80. package/src/manager/window/WindowComponentHeader.vue +81 -13
  81. package/src/manager/window/WindowManager.vue +54 -30
  82. package/src/manager/window/windowHelper.js +341 -0
  83. package/src/manager/window/windowManager.js +173 -151
  84. package/src/navigation/overviewMap.js +10 -9
  85. package/src/notifier/notifier.js +120 -0
  86. package/src/notifier/notifierComponent.vue +84 -0
  87. package/src/styles/variables.scss +19 -3
  88. package/src/vcsUiApp.js +26 -2
  89. package/src/vuePlugins/vuetify.js +2 -0
  90. package/dist/assets/ui.36f84f.css +0 -1
  91. package/src/manager/categoryManager/ComponentsManager.vue +0 -30
@@ -51,6 +51,9 @@
51
51
  component: EmptyComponent,
52
52
  headerComponent: MyCustomHeader,
53
53
  slot: WindowSlot.DYNAMIC_RIGHT,
54
+ props: {
55
+ sample: 'A window property',
56
+ },
54
57
  },
55
58
  {
56
59
  id: 'dynamicRight2',
@@ -85,7 +88,7 @@
85
88
  {
86
89
  id: 'position1',
87
90
  state: {
88
- headerTitle: 'Example position1',
91
+ headerTitle: 'Example position1 relative',
89
92
  },
90
93
  component: VcsContent,
91
94
  position: {
@@ -98,15 +101,17 @@
98
101
  {
99
102
  id: 'position2',
100
103
  state: {
101
- hideHeader: true,
102
- headerTitle: 'Example position2',
104
+ hideHeader: false,
105
+ headerTitle: 'Example position2 absolute',
103
106
  },
104
107
  component: EmptyComponent,
105
108
  position: {
106
- left: '35%',
107
- right: '35%',
108
- top: '40%',
109
- bottom: '20%',
109
+ left: '200px',
110
+ top: '300px',
111
+ minHeight: '250px',
112
+ maxHeight: '500px',
113
+ minWidth: '400px',
114
+ maxWidth: '1000px',
110
115
  },
111
116
  },
112
117
  ];
@@ -0,0 +1,41 @@
1
+ import { createToggleAction, ButtonLocation, WindowSlot } from '@vcmap/ui';
2
+ import WizardExample from './wizardExample.vue';
3
+
4
+ export default async () => {
5
+ return {
6
+ name: 'wizardExample',
7
+ onVcsAppMounted(app) {
8
+ const { action, destroy } = createToggleAction(
9
+ {
10
+ name: 'wizardExample',
11
+ icon: '$vcsLegend',
12
+ title: 'VCS Wizard Example',
13
+ },
14
+ {
15
+ id: 'wizard-example',
16
+ component: WizardExample,
17
+ slot: WindowSlot.DYNAMIC_LEFT,
18
+ state: {
19
+ headerTitle: 'VCS Wizard',
20
+ headerIcon: '$vcsWand',
21
+ styles: { width: '350px', height: 'auto' },
22
+ },
23
+ },
24
+ app.windowManager,
25
+ 'wizardExample',
26
+ );
27
+ app.navbarManager.add(
28
+ { id: 'wizard-example', action },
29
+ 'wizardExample',
30
+ ButtonLocation.TOOL,
31
+ );
32
+ this._destroyAction = destroy;
33
+ },
34
+ destroy() {
35
+ if (this._destroyAction) {
36
+ this._destroyAction();
37
+ this._destroyAction = null;
38
+ }
39
+ },
40
+ };
41
+ };
@@ -0,0 +1,77 @@
1
+ <template>
2
+ <VcsWizard
3
+ v-model="step"
4
+ >
5
+ <v-stepper-step
6
+ step="1"
7
+ editable
8
+ :complete="step > 1"
9
+ >
10
+ This is the first step
11
+ </v-stepper-step>
12
+ <v-stepper-content
13
+ step="1"
14
+ >
15
+ <v-sheet>
16
+ This is the content of the first step.
17
+ </v-sheet>
18
+ <VcsButton
19
+ @click="increaseStep()"
20
+ class="my-2"
21
+ >
22
+ Next
23
+ </VcsButton>
24
+ </v-stepper-content>
25
+ <v-stepper-step
26
+ :rules="[() => false]"
27
+ step="2"
28
+ editable
29
+ >
30
+ This is the second step
31
+ </v-stepper-step>
32
+ <v-stepper-content
33
+ step="2"
34
+ >
35
+ <VcsSelect
36
+ :items="['this', 'is', 'a', 'test']"
37
+ label="Select"
38
+ class="my-2"
39
+ />
40
+ <VcsButton
41
+ @click="decreaseStep()"
42
+ >
43
+ Back
44
+ </VcsButton>
45
+ </v-stepper-content>
46
+ </VcsWizard>
47
+ </template>
48
+
49
+ <script>
50
+ import { VcsWizard, VcsButton, VcsSelect } from '@vcmap/ui';
51
+ import { VStepperStep, VStepperContent, VSheet } from 'vuetify/lib';
52
+ import { ref } from 'vue';
53
+
54
+ export default {
55
+ name: 'WizardExample',
56
+ components: {
57
+ VcsWizard,
58
+ VStepperStep,
59
+ VStepperContent,
60
+ VSheet,
61
+ VcsButton,
62
+ VcsSelect,
63
+ },
64
+ setup() {
65
+ const step = ref(1);
66
+ return {
67
+ step,
68
+ increaseStep() { step.value += 1; },
69
+ decreaseStep() { step.value -= 1; },
70
+ };
71
+ },
72
+ };
73
+ </script>
74
+
75
+ <style scoped>
76
+
77
+ </style>
@@ -4,7 +4,8 @@ import { Collection, Extent, MapCollection, mercatorProjection, Viewpoint } from
4
4
  import { Feature } from 'ol';
5
5
  import { reactive, ref } from 'vue';
6
6
  import { vcsAppSymbol } from '../pluginHelper.js';
7
- import { getWindowPositionOptions, WindowSlot } from '../manager/window/windowManager.js';
7
+ import { WindowSlot } from '../manager/window/windowManager.js';
8
+ import { getWindowPositionOptions } from '../manager/window/windowHelper.js';
8
9
  import SearchComponent from '../search/searchComponent.vue';
9
10
 
10
11
  /**
@@ -189,11 +190,11 @@ export function createOverviewMapAction(overviewMap, windowComponent, windowMana
189
190
  * at the clicked position (the actions position) by default, unless the window component already has a position set.
190
191
  * @param {ActionOptions} actionOptions
191
192
  * @param {WindowComponentOptions} modalComponent
192
- * @param {WindowManager} windowManager
193
+ * @param {VcsUiApp} app
193
194
  * @param {string|symbol} owner
194
195
  * @returns {{action: VcsAction, destroy: Function}}
195
196
  */
196
- export function createModalAction(actionOptions, modalComponent, windowManager, owner) {
197
+ export function createModalAction(actionOptions, modalComponent, app, owner) {
197
198
  check(actionOptions, {
198
199
  name: String,
199
200
  icon: [undefined, String],
@@ -216,7 +217,7 @@ export function createModalAction(actionOptions, modalComponent, windowManager,
216
217
  left: 0,
217
218
  right: 0,
218
219
  });
219
- elem.onclick = () => { windowManager.remove(id); };
220
+ elem.onclick = () => { app.windowManager.remove(id); };
220
221
  document.body.appendChild(elem);
221
222
  }
222
223
  };
@@ -235,20 +236,20 @@ export function createModalAction(actionOptions, modalComponent, windowManager,
235
236
  if (!this.active) {
236
237
  this.active = true;
237
238
  const { left, top, width } = event.currentTarget.getBoundingClientRect();
238
- const position = getWindowPositionOptions(left + width, top);
239
+ const position = getWindowPositionOptions(left + width, top, app.maps.target);
239
240
  const state = { ...modalComponent?.state, hideHeader: true };
240
- windowManager.add({ position, ...modalComponent, id, state }, owner);
241
- addModal(windowManager.componentIds.length - 2);
241
+ app.windowManager.add({ position, ...modalComponent, id, state }, owner);
242
+ addModal(app.windowManager.componentIds.length - 2);
242
243
  } else {
243
244
  this.active = false;
244
- windowManager.remove(id);
245
+ app.windowManager.remove(id);
245
246
  }
246
247
  return null;
247
248
  },
248
249
  };
249
250
 
250
251
  const listeners = [
251
- windowManager.removed.addEventListener(({ id: windowId }) => {
252
+ app.windowManager.removed.addEventListener(({ id: windowId }) => {
252
253
  if (windowId === id) {
253
254
  action.active = false;
254
255
  removeModal();
@@ -20,6 +20,7 @@
20
20
  <MapNavigation />
21
21
  <ToolboxManagerComponent />
22
22
  <WindowManagerComponent />
23
+ <NotifierComponent />
23
24
  </v-container>
24
25
  <v-footer absolute v-if="$vuetify.breakpoint.smAndUp" min-height="22px">
25
26
  <VcsAttributionsFooter :entries="attributionEntries" :attribution-action="attributionAction" />
@@ -73,6 +74,7 @@
73
74
  } from 'vue';
74
75
  import { getVcsAppById } from '@vcmap/core';
75
76
  import { VContainer, VFooter } from 'vuetify/lib';
77
+ import { getLogger } from '@vcsuite/logger';
76
78
  import WindowManagerComponent from '../manager/window/WindowManager.vue';
77
79
  import ToolboxManagerComponent from '../manager/toolbox/ToolboxManager.vue';
78
80
  import { ButtonLocation } from '../manager/navbarManager.js';
@@ -83,7 +85,7 @@
83
85
  import MapNavigation from '../navigation/mapNavigation.vue';
84
86
  import VcsSettings from './VcsSettings.vue';
85
87
  import { WindowSlot } from '../manager/window/windowManager.js';
86
- import ComponentsManager from '../manager/categoryManager/ComponentsManager.vue';
88
+ import CategoryManager from '../manager/categoryManager/CategoryManager.vue';
87
89
  import { defaultPrimaryColor } from '../vuePlugins/vuetify.js';
88
90
  import VcsLegend from '../legend/vcsLegend.vue';
89
91
  import { getLegendEntries } from '../legend/legendHelper.js';
@@ -91,6 +93,7 @@
91
93
  import VcsButton from '../components/buttons/VcsButton.vue';
92
94
  import VcsAttributions from './VcsAttributions.vue';
93
95
  import { getAttributions } from './attributionsHelper.js';
96
+ import NotifierComponent from '../notifier/notifierComponent.vue';
94
97
 
95
98
  /**
96
99
  * You should call this function in the component providing the vcsUiApp to your
@@ -101,17 +104,23 @@
101
104
  * @returns {function():void}
102
105
  */
103
106
  export function setupPluginMountedListeners(app) {
104
- [...app.plugins].forEach((plugin) => {
107
+ /**
108
+ * wrapped execution of onVcsAppMounted hook
109
+ * @param {VcsPlugin} plugin
110
+ */
111
+ function onVcsAppMounted(plugin) {
105
112
  if (plugin.onVcsAppMounted) {
106
- plugin.onVcsAppMounted(app);
113
+ try {
114
+ plugin.onVcsAppMounted(app);
115
+ } catch (e) {
116
+ getLogger('VcsUiApp').error(`Error in plugin ${plugin.name} onVcsAppMounted hook`, e);
117
+ }
107
118
  }
108
- });
119
+ }
109
120
 
110
- return app.plugins.added.addEventListener((plugin) => {
111
- if (plugin.onVcsAppMounted) {
112
- plugin.onVcsAppMounted(app);
113
- }
114
- });
121
+ [...app.plugins].forEach(onVcsAppMounted);
122
+
123
+ return app.plugins.added.addEventListener(onVcsAppMounted);
115
124
  }
116
125
 
117
126
  /**
@@ -267,56 +276,54 @@
267
276
  }
268
277
 
269
278
  /**
270
- * This helper function will add a Components manager button to the navbar. The Components Manager
279
+ * This helper function will add a category manager button to the navbar. The category Manager
271
280
  * will only be shown if there is at least one category under management in the categoryManager.
272
281
  * @param {VcsUiApp} app
273
282
  * @returns {function():void}
274
283
  */
275
- export function setupComponentsWindow(app) {
276
- const { action: componentsManagerAction, destroy: destroyComponentsManagerAction } = createToggleAction(
284
+ export function setupCategoryManagerWindow(app) {
285
+ const id = 'category-manager';
286
+ const { action: categoryManagerAction, destroy } = createToggleAction(
277
287
  {
278
- name: 'components-manager',
288
+ name: id,
279
289
  icon: '$vcsComponents',
280
- title: 'components.tooltip',
290
+ title: 'categoryManager.tooltip',
281
291
  },
282
292
  {
283
- id: 'component-manager',
293
+ id,
284
294
  state: {
285
- headerTitle: 'components.title',
295
+ headerTitle: 'categoryManager.title',
286
296
  headerIcon: '$vcsComponents',
287
297
  },
288
- component: ComponentsManager,
298
+ component: CategoryManager,
289
299
  slot: WindowSlot.STATIC,
290
300
  },
291
301
  app.windowManager,
292
302
  vcsAppSymbol,
293
303
  );
294
304
 
295
- // only show Components Window if we have at least one managed Category
296
- if (app.categoryManager.items.value.length > 0) {
297
- app.navbarManager.add(
298
- { id: 'component-manager', action: componentsManagerAction },
299
- vcsAppSymbol,
300
- ButtonLocation.CONTENT,
301
- );
302
- }
303
- watch(app.categoryManager.items.value, () => {
304
- if (app.categoryManager.items.value.length > 0) {
305
- if (!app.navbarManager.has('component-manager')) {
305
+ const setupCategories = () => {
306
+ if (app.categoryManager.componentIds.length > 0) {
307
+ if (!app.navbarManager.has(id)) {
306
308
  app.navbarManager.add(
307
- { id: 'component-manager', action: componentsManagerAction },
309
+ { id, action: categoryManagerAction },
308
310
  vcsAppSymbol,
309
311
  ButtonLocation.CONTENT,
310
312
  );
311
313
  }
312
314
  } else {
313
- app.windowManager.remove('component-manager');
314
- app.navbarManager.remove('component-manager');
315
+ app.windowManager.remove(id);
316
+ app.navbarManager.remove(id);
315
317
  }
316
- });
318
+ };
319
+ const addedListener = app.categoryManager.added.addEventListener(setupCategories);
320
+ const removedListener = app.categoryManager.removed.addEventListener(setupCategories);
321
+ setupCategories();
317
322
 
318
323
  return () => {
319
- destroyComponentsManagerAction();
324
+ destroy();
325
+ addedListener();
326
+ removedListener();
320
327
  };
321
328
  }
322
329
 
@@ -405,6 +412,7 @@
405
412
  ToolboxManagerComponent,
406
413
  VContainer,
407
414
  VFooter,
415
+ NotifierComponent,
408
416
  },
409
417
  props: {
410
418
  appId: {
@@ -422,7 +430,7 @@
422
430
  const mapNavbarListener = setupMapNavbar(app);
423
431
  const legendDestroy = setupLegendWindow(app);
424
432
  const settingsDestroy = setupSettingsWindow(app);
425
- const destroyComponentsWindow = setupComponentsWindow(app);
433
+ const destroyComponentsWindow = setupCategoryManagerWindow(app);
426
434
  const destroyThemingListener = setupUiConfigTheming(app, getCurrentInstance().proxy.$vuetify);
427
435
  const { attributionEntries, attributionAction, destroyAttributions } = setupAttributions(app);
428
436
 
@@ -430,6 +438,7 @@
430
438
  onMounted(() => {
431
439
  pluginMountedListener = setupPluginMountedListeners(app);
432
440
  app.maps.setTarget(mapId);
441
+ app.mounted.raiseEvent(mapId);
433
442
  });
434
443
 
435
444
  onUnmounted(() => {
@@ -7,6 +7,7 @@
7
7
  <template #activator="{ on, attrs }">
8
8
  <span v-on="on">
9
9
  <v-checkbox
10
+ :input-value="$attrs.value"
10
11
  on-icon="$vcsCheckboxChecked"
11
12
  off-icon="$vcsCheckbox"
12
13
  class="vcs-checkbox"
@@ -1,11 +1,12 @@
1
1
  <template>
2
2
  <section class="vcs-form-section">
3
- <slot name="title">
3
+ <slot name="header" :heading="heading" :actions="actions">
4
4
  <article class="pa-2 accent">
5
5
  <div class="form-section-header d-flex justify-space-between align-center">
6
- <strong class="caption">{{ $t(title) }}</strong>
6
+ <strong class="caption">{{ $t(heading) }}</strong>
7
7
  <VcsActionButtonList
8
8
  :actions="actions"
9
+ :overflow-count="actionButtonListOverflowCount"
9
10
  small
10
11
  />
11
12
  </div>
@@ -30,20 +31,21 @@
30
31
 
31
32
 
32
33
  <script>
33
- import { computed, ref } from 'vue';
34
+ import { computed, reactive } from 'vue';
34
35
  import { VAlert } from 'vuetify/lib';
35
36
  import VcsActionButtonList from '../buttons/VcsActionButtonList.vue';
36
37
 
37
38
  /**
38
39
  * @description
39
40
  * Stylized form section with action buttons
40
- * @vue-data {slot} [#title] - slot to override form section header
41
+ * @vue-data {slot} [#header] - slot to override form section header
41
42
  * @vue-data {slot} [#default] - slot with the section content
42
43
  * @vue-data {slot} [#help] - Slot to specify html based help. Gets precedence over helpText prop.
43
- * @vue-prop {string} title - Title to be displayed, will be translated.
44
- * @vue-prop {Array<VcsAction>} titleActions - Icons to be displayed on the right side
44
+ * @vue-prop {string} heading - Title of the section to be displayed, will be translated.
45
+ * @vue-prop {Array<VcsAction>} headerActions - Icons to be displayed on the right side
46
+ * @vue-prop {number} [actionButtonListOverflowCount] - overflow count to use for action lists in the title and items
45
47
  * @vue-prop {string} [helpText] - Optional help text. Must be plain string. Use 'help' slot for html based help texts. Help slot has precedence over helpText prop.
46
- * @vue-computed {Array<VcsAction>} actions - Returns title actions extended by a help action, if help prop is passed or help slot is used.
48
+ * @vue-computed {Array<VcsAction>} actions - Returns header actions extended by a help action, if help prop is passed or help slot is used.
47
49
  */
48
50
  export default {
49
51
  name: 'VcsFormSection',
@@ -52,35 +54,41 @@
52
54
  VAlert,
53
55
  },
54
56
  props: {
55
- title: {
57
+ heading: {
56
58
  type: String,
57
59
  default: undefined,
58
60
  },
59
- titleActions: {
61
+ headerActions: {
60
62
  type: Array,
61
63
  default: () => ([]),
62
64
  },
65
+ actionButtonListOverflowCount: {
66
+ type: Number,
67
+ required: false,
68
+ default: undefined,
69
+ },
63
70
  helpText: {
64
71
  type: String,
65
72
  default: undefined,
66
73
  },
67
74
  },
68
75
  setup(props, { slots }) {
69
- const showHelp = ref(false);
70
- const helpAction = {
76
+ const helpAction = reactive({
71
77
  name: 'help',
72
78
  title: 'components.vcsFormSection.help',
79
+ active: false,
73
80
  icon: 'mdi-help-circle',
74
- callback: () => { showHelp.value = !showHelp.value; },
75
- };
81
+ callback() { this.active = !this.active; },
82
+ });
83
+ const showHelp = computed(() => helpAction.active);
76
84
  /**
77
85
  * @type {ComputedRef<VcsAction>}
78
86
  */
79
87
  const actions = computed(() => {
80
88
  if (props.helpText || (slots.help && slots.help().length > 0)) {
81
- return [helpAction, ...props.titleActions];
89
+ return [helpAction, ...props.headerActions];
82
90
  }
83
- return props.titleActions;
91
+ return props.headerActions;
84
92
  });
85
93
 
86
94
  return {
@@ -17,11 +17,21 @@
17
17
  :outlined="isOutlined"
18
18
  :dense="isDense"
19
19
  :height="isDense ? 24 : 32"
20
+ :item-text="item => $t(getText(item))"
20
21
  :class="$attrs.color === 'primary' ? 'primary--select' : ''"
21
22
  v-bind="{...$attrs, ...attrs}"
22
23
  v-on="{...$listeners, ...on}"
23
24
  @update:error="setError"
24
- />
25
+ >
26
+ <template #selection="{ item, index }">
27
+ <span v-if="index === 0" class="text-truncate">
28
+ {{ $t(getText(item)) }}
29
+ </span>
30
+ <span v-if="index === 1" class="text-no-wrap grey--text text-caption">
31
+ (+{{ $attrs.value.length - 1 }})
32
+ </span>
33
+ </template>
34
+ </v-select>
25
35
  </span>
26
36
  </template>
27
37
  </VcsTooltip>
@@ -46,6 +56,15 @@
46
56
  }
47
57
  }
48
58
  }
59
+ .v-select{
60
+ &.v-select--is-multi{
61
+ ::v-deep {
62
+ .v-select__selections{
63
+ flex-wrap: nowrap;
64
+ }
65
+ }
66
+ }
67
+ }
49
68
  </style>
50
69
  <script>
51
70
 
@@ -55,11 +74,13 @@
55
74
 
56
75
  /**
57
76
  * @description Stylized wrapper around {@link https://vuetifyjs.com/en/api/v-select/ |vuetify select}.
77
+ * Translates the items text if it is an i18n string.
58
78
  * Provides two height options depending on "dense" property:
59
79
  * - if dense is set true (default), height is 24 px
60
80
  * - if dense is set false, height is 32 px
61
81
  * Provides VcsTooltip to show error messages
62
82
  * @vue-prop {('bottom' | 'left' | 'top' | 'right')} [tooltipPosition='right'] - Position of the error tooltip.
83
+ * @vue-prop {Function} itemText - A function that is applied to each item and should return the item's text value.
63
84
  * @vue-computed {boolean} isDense - Whether size of select is dense.
64
85
  * @vue-computed {boolean} isOutlined - Select is outlined on either hover, focus or error, if not disabled.
65
86
  */
@@ -74,6 +95,10 @@
74
95
  type: String,
75
96
  default: 'right',
76
97
  },
98
+ itemText: {
99
+ type: Function,
100
+ default: undefined,
101
+ },
77
102
  },
78
103
  data() {
79
104
  return {
@@ -95,6 +120,13 @@
95
120
  const rules = [...this.$attrs.rules].concat(this.$attrs.errorMessages);
96
121
  this.errorMessage = validate(rules, this.$attrs.value).join('\n');
97
122
  },
123
+ getText(item) {
124
+ if (this.itemText) {
125
+ return this.itemText(item);
126
+ } else {
127
+ return item?.text ?? item;
128
+ }
129
+ },
98
130
  },
99
131
  };
100
132
  </script>
@@ -11,7 +11,8 @@
11
11
  :max-width="200"
12
12
  >
13
13
  <template #activator="{ attrs }">
14
- <v-text-field
14
+ <component
15
+ :is="inputComponent"
15
16
  ref="textFieldRef"
16
17
  hide-details
17
18
  :dense="isDense"
@@ -23,7 +24,7 @@
23
24
  v-bind="{...$attrs, ...attrs}"
24
25
  v-on="{...$listeners}"
25
26
  :height="isDense ? 24 : 32"
26
- class="ma-0 pb-1 pt-1 primary--placeholder"
27
+ class="ma-0 pb-1 pt-1 primary--placeholder align-center"
27
28
  :class="$attrs.color === 'primary' ? 'primary--textfield' : ''"
28
29
  />
29
30
  </template>
@@ -51,7 +52,7 @@
51
52
  </style>
52
53
 
53
54
  <script>
54
- import { VTextField } from 'vuetify/lib';
55
+ import { VTextField, VFileInput } from 'vuetify/lib';
55
56
  import VcsTooltip from '../notification/VcsTooltip.vue';
56
57
 
57
58
  /**
@@ -74,6 +75,7 @@
74
75
  components: {
75
76
  VcsTooltip,
76
77
  VTextField,
78
+ VFileInput,
77
79
  },
78
80
  props: {
79
81
  tooltipPosition: {
@@ -92,6 +94,12 @@
92
94
  };
93
95
  },
94
96
  computed: {
97
+ inputComponent() {
98
+ if (this.$attrs.type === 'file') {
99
+ return 'VFileInput';
100
+ }
101
+ return 'VTextField';
102
+ },
95
103
  isClearable() {
96
104
  return (this.$attrs.clearable !== undefined && this.$attrs.clearable !== false) &&
97
105
  (this.hover || this.focus || this.isError);