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

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 (51) hide show
  1. package/build/bundle.js +2 -1
  2. package/config/base.config.json +22 -0
  3. package/config/www.config.json +1 -1
  4. package/dist/assets/cesium/Workers/cesiumWorkerBootstrapper.js +1 -1
  5. package/dist/assets/cesium/Workers/package.js +1 -1
  6. package/dist/assets/cesium/Workers/transferTypedArrayTest.js +1 -1
  7. package/dist/assets/{cesium.82fdbe.js → cesium.16590b.js} +29788 -29589
  8. package/dist/assets/cesium.js +1 -1
  9. package/dist/assets/{core.df069a.js → core.74da2a.js} +4330 -4231
  10. package/dist/assets/core.js +1 -1
  11. package/dist/assets/index-cb070eff.js +1 -0
  12. package/dist/assets/{ol.90a5d0.js → ol.50a512.js} +11405 -11126
  13. package/dist/assets/ol.js +1 -1
  14. package/dist/assets/{ui.3ed7ff.css → ui.d3054c.css} +2 -2
  15. package/dist/assets/{ui.3ed7ff.js → ui.d3054c.js} +2811 -2711
  16. package/dist/assets/ui.js +1 -1
  17. package/dist/assets/vue.js +2 -2
  18. package/dist/assets/{vuetify.614278.js → vuetify.946bd8.js} +1 -1
  19. package/dist/assets/vuetify.js +2 -2
  20. package/dist/index.html +1 -1
  21. package/index.js +1 -0
  22. package/lib/olLib.js +6 -0
  23. package/package.json +4 -4
  24. package/plugins/@vcmap-show-case/window-tester/WindowExample.vue +27 -9
  25. package/plugins/@vcmap-show-case/window-tester/index.js +13 -1
  26. package/plugins/@vcmap-show-case/window-tester/windowExampleToggleChild.vue +11 -10
  27. package/src/actions/actionHelper.js +7 -3
  28. package/src/application/VcsApp.vue +31 -0
  29. package/src/components/form-inputs-controls/VcsTextField.vue +3 -2
  30. package/src/components/icons/+all.js +3 -3
  31. package/src/components/lists/VcsTreeviewLeaf.vue +1 -1
  32. package/src/components/lists/VcsTreeviewSearchbar.vue +9 -4
  33. package/src/components/tables/VcsDataTable.vue +100 -13
  34. package/src/contentTree/contentTreeCollection.js +22 -11
  35. package/src/featureInfo/abstractFeatureInfoView.js +3 -1
  36. package/src/featureInfo/balloonFeatureInfoView.js +3 -2
  37. package/src/featureInfo/featureInfo.js +1 -0
  38. package/src/i18n/de.js +4 -0
  39. package/src/i18n/en.js +4 -0
  40. package/src/manager/collectionManager/CollectionComponent.vue +6 -1
  41. package/src/manager/collectionManager/collectionComponent.js +6 -0
  42. package/src/manager/window/WindowComponent.vue +4 -1
  43. package/src/manager/window/WindowComponentHeader.vue +25 -13
  44. package/src/manager/window/windowManager.js +6 -2
  45. package/src/navigation/overviewMap.js +1 -1
  46. package/src/uiConfig.js +1 -0
  47. package/src/vcsUiApp.js +29 -8
  48. package/dist/assets/index-1cff371d.js +0 -1
  49. /package/dist/assets/{vue.537ff3.js → vue.30740e.js} +0 -0
  50. /package/dist/assets/{vuetify.614278.css → vuetify.946bd8.css} +0 -0
  51. /package/src/components/icons/{PolygonIcon.vue → PointIcon.vue} +0 -0
package/dist/assets/ui.js CHANGED
@@ -1 +1 @@
1
- export * from "./ui.3ed7ff.js";
1
+ export * from "./ui.d3054c.js";
@@ -1,5 +1,5 @@
1
- export * from "./vue.537ff3.js";
2
- import { default as f } from "./vue.537ff3.js";
1
+ export * from "./vue.30740e.js";
2
+ import { default as f } from "./vue.30740e.js";
3
3
  export {
4
4
  f as default
5
5
  };
@@ -13,7 +13,7 @@ function loadCss(href) {
13
13
  elem.onerror = reject;
14
14
  document.head.appendChild(elem);
15
15
  });
16
- } await loadCss('./assets/vuetify.614278.css');import v from "./vue.537ff3.js";
16
+ } await loadCss('./assets/vuetify.946bd8.css');import v from "./vue.30740e.js";
17
17
  const Ne = v.extend().extend({
18
18
  name: "themeable",
19
19
  provide() {
@@ -1,5 +1,5 @@
1
- export * from "./vuetify.614278.js";
2
- import { default as f } from "./vuetify.614278.js";
1
+ export * from "./vuetify.946bd8.js";
2
+ import { default as f } from "./vuetify.946bd8.js";
3
3
  export {
4
4
  f as default
5
5
  };
package/dist/index.html CHANGED
@@ -29,7 +29,7 @@
29
29
  />
30
30
  <link rel="icon" type="image/svg+xml" href="./assets/favicon.d5ec97.svg" />
31
31
 
32
- <script type="module" crossorigin src="./assets/index-1cff371d.js"></script>
32
+ <script type="module" crossorigin src="./assets/index-cb070eff.js"></script>
33
33
  </head>
34
34
  <body style="height: 100vh">
35
35
  <noscript>
package/index.js CHANGED
@@ -139,6 +139,7 @@ export { default as VcsCompass } from './src/navigation/vcsCompass.vue';
139
139
  export { default as VcsZoomButton } from './src/navigation/vcsZoomButton.vue';
140
140
 
141
141
  export { createVueI18n, setupI18n } from './src/vuePlugins/i18n.js';
142
+ export { i18nPluginSymbol } from './src/i18n/i18nCollection.js';
142
143
  export {
143
144
  createVuetify,
144
145
  vuetify,
package/lib/olLib.js CHANGED
@@ -50,6 +50,7 @@ export {default as ol$ViewHint} from 'ol/ViewHint';
50
50
  export {default as ol$ViewProperty} from 'ol/ViewProperty';
51
51
  export {binarySearch as ol$array$binarySearch} from 'ol/array';
52
52
  export {ascending as ol$array$ascending} from 'ol/array';
53
+ export {descending as ol$array$descending} from 'ol/array';
53
54
  export {linearFindNearest as ol$array$linearFindNearest} from 'ol/array';
54
55
  export {reverseSubArray as ol$array$reverseSubArray} from 'ol/array';
55
56
  export {extend as ol$array$extend} from 'ol/array';
@@ -124,6 +125,8 @@ export {preventDefault as ol$events$Event$preventDefault} from 'ol/events/Event'
124
125
  export {default as ol$events$Event} from 'ol/events/Event';
125
126
  export {default as ol$events$EventType} from 'ol/events/EventType';
126
127
  export {default as ol$events$Key} from 'ol/events/Key';
128
+ export {SnapEventType as ol$events$SnapEvent$SnapEventType} from 'ol/events/SnapEvent';
129
+ export {SnapEvent as ol$events$SnapEvent$SnapEvent} from 'ol/events/SnapEvent';
127
130
  export {default as ol$events$Target} from 'ol/events/Target';
128
131
  export {all as ol$events$condition$all} from 'ol/events/condition';
129
132
  export {altKeyOnly as ol$events$condition$altKeyOnly} from 'ol/events/condition';
@@ -139,6 +142,7 @@ export {singleClick as ol$events$condition$singleClick} from 'ol/events/conditio
139
142
  export {doubleClick as ol$events$condition$doubleClick} from 'ol/events/condition';
140
143
  export {noModifierKeys as ol$events$condition$noModifierKeys} from 'ol/events/condition';
141
144
  export {platformModifierKeyOnly as ol$events$condition$platformModifierKeyOnly} from 'ol/events/condition';
145
+ export {platformModifierKey as ol$events$condition$platformModifierKey} from 'ol/events/condition';
142
146
  export {shiftKeyOnly as ol$events$condition$shiftKeyOnly} from 'ol/events/condition';
143
147
  export {targetNotEditable as ol$events$condition$targetNotEditable} from 'ol/events/condition';
144
148
  export {mouseOnly as ol$events$condition$mouseOnly} from 'ol/events/condition';
@@ -507,6 +511,7 @@ export {register as ol$proj$proj4$register} from 'ol/proj/proj4';
507
511
  export {setEPSGLookup as ol$proj$proj4$setEPSGLookup} from 'ol/proj/proj4';
508
512
  export {getEPSGLookup as ol$proj$proj4$getEPSGLookup} from 'ol/proj/proj4';
509
513
  export {fromEPSGCode as ol$proj$proj4$fromEPSGCode} from 'ol/proj/proj4';
514
+ export {epsgLookupMapTiler as ol$proj$proj4$epsgLookupMapTiler} from 'ol/proj/proj4';
510
515
  export {clear as ol$proj$projections$clear} from 'ol/proj/projections';
511
516
  export {get as ol$proj$projections$get} from 'ol/proj/projections';
512
517
  export {add as ol$proj$projections$add} from 'ol/proj/projections';
@@ -751,6 +756,7 @@ export {expressionToGlsl as ol$style$expressions$expressionToGlsl} from 'ol/styl
751
756
  export {uniformNameForVariable as ol$style$expressions$uniformNameForVariable} from 'ol/style/expressions';
752
757
  export {PALETTE_TEXTURE_ARRAY as ol$style$expressions$PALETTE_TEXTURE_ARRAY} from 'ol/style/expressions';
753
758
  export {toStyle as ol$style$flat$toStyle} from 'ol/style/flat';
759
+ export {createDefaultStyle as ol$style$flat$createDefaultStyle} from 'ol/style/flat';
754
760
  export {SymbolType as ol$style$literal$SymbolType} from 'ol/style/literal';
755
761
  export {createOrUpdate as ol$tilecoord$createOrUpdate} from 'ol/tilecoord';
756
762
  export {getKeyZXY as ol$tilecoord$getKeyZXY} from 'ol/tilecoord';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vcmap/ui",
3
- "version": "5.0.0-rc.27",
3
+ "version": "5.0.0-rc.29",
4
4
  "author": "Virtual City Systems",
5
5
  "license": "MIT",
6
6
  "scripts": {
@@ -44,9 +44,9 @@
44
44
  "vue-i18n": "^8.24.1"
45
45
  },
46
46
  "peerDependencies": {
47
- "@vcmap-cesium/engine": "~2.4.1",
48
- "@vcmap/core": "^5.0.0-rc.35",
49
- "ol": "~7.4.0",
47
+ "@vcmap-cesium/engine": "~2.4.3",
48
+ "@vcmap/core": "^5.0.0-rc.37",
49
+ "ol": "~7.5.1",
50
50
  "vue": "~2.7.3",
51
51
  "vuetify": "~2.6.7"
52
52
  },
@@ -35,14 +35,16 @@
35
35
  const exampleWindows = [
36
36
  {
37
37
  id: 'dynamicLeft',
38
- headerTitle: 'Example dynamicLeft',
38
+ state: {
39
+ headerTitle: ['windowTester.title', '-', 'Example dynamicLeft'],
40
+ },
39
41
  component: WindowExampleToggleChild,
40
42
  slot: WindowSlot.DYNAMIC_LEFT,
41
43
  },
42
44
  {
43
45
  id: 'dynamicLeft2',
44
46
  state: {
45
- headerTitle: 'Example dynamicLeft2',
47
+ headerTitle: ['windowTester.title', '-', 'Example dynamicLeft2'],
46
48
  },
47
49
  component: WindowExampleToggleChild,
48
50
  slot: WindowSlot.DYNAMIC_LEFT,
@@ -50,7 +52,11 @@
50
52
  {
51
53
  id: 'dynamicLeft2 large',
52
54
  state: {
53
- headerTitle: 'Example dynamicLeft2 with 1000px width',
55
+ headerTitle: [
56
+ 'windowTester.title',
57
+ '-',
58
+ 'Example dynamicLeft2 with 1000px width',
59
+ ],
54
60
  },
55
61
  component: WindowExampleToggleChild,
56
62
  slot: WindowSlot.DYNAMIC_LEFT,
@@ -61,7 +67,7 @@
61
67
  {
62
68
  id: 'dynamicRight',
63
69
  state: {
64
- headerTitle: 'Example dynamicRight',
70
+ headerTitle: ['windowTester.title', '-', 'Example dynamicRight'],
65
71
  },
66
72
  component: WindowExampleContent,
67
73
  headerComponent: MyCustomHeader,
@@ -73,7 +79,7 @@
73
79
  {
74
80
  id: 'dynamicRight2',
75
81
  state: {
76
- headerTitle: 'Example dynamicRight2',
82
+ headerTitle: ['windowTester.title', '-', 'Example dynamicRight2'],
77
83
  },
78
84
  component: WindowExampleContent,
79
85
  slot: WindowSlot.DYNAMIC_RIGHT,
@@ -81,7 +87,7 @@
81
87
  {
82
88
  id: 'static',
83
89
  state: {
84
- headerTitle: 'Example static',
90
+ headerTitle: ['windowTester.title', '-', 'Example static'],
85
91
  styles: { 'background-color': 'red' },
86
92
  },
87
93
  component: WindowExampleToggleChild,
@@ -90,7 +96,11 @@
90
96
  {
91
97
  id: 'static2',
92
98
  state: {
93
- headerTitle: 'Example static2 With TestClass',
99
+ headerTitle: [
100
+ 'windowTester.title',
101
+ '-',
102
+ 'Example static2 With TestClass',
103
+ ],
94
104
  classes: {
95
105
  vcsTest: computed(() => {
96
106
  return showTestClass.value;
@@ -103,7 +113,11 @@
103
113
  {
104
114
  id: 'position1',
105
115
  state: {
106
- headerTitle: 'Example position1 relative',
116
+ headerTitle: [
117
+ 'windowTester.title',
118
+ '-',
119
+ 'Example position1 relative',
120
+ ],
107
121
  },
108
122
  component: WindowExampleToggleChild,
109
123
  position: {
@@ -117,7 +131,11 @@
117
131
  id: 'position2',
118
132
  state: {
119
133
  hideHeader: false,
120
- headerTitle: 'Example position2 absolute',
134
+ headerTitle: [
135
+ 'windowTester.title',
136
+ '-',
137
+ 'Example position2 absolute',
138
+ ],
121
139
  },
122
140
  component: WindowExampleToggleChild,
123
141
  position: {
@@ -25,7 +25,7 @@ export default async function windowTester() {
25
25
  {
26
26
  id: 'window-tester',
27
27
  state: {
28
- headerTitle: 'Window Tester',
28
+ headerTitle: 'windowTester.title',
29
29
  },
30
30
  component: windowExample,
31
31
  position: {
@@ -58,6 +58,18 @@ export default async function windowTester() {
58
58
  );
59
59
  this._destroyAction = [destroy, destroyToolboxData];
60
60
  },
61
+ i18n: {
62
+ de: {
63
+ windowTester: {
64
+ title: 'Fenster Tester',
65
+ },
66
+ },
67
+ en: {
68
+ windowTester: {
69
+ title: 'Window Tester',
70
+ },
71
+ },
72
+ },
61
73
  destroy() {
62
74
  if (this._destroyActions) {
63
75
  this._destroyActions.forEach((cb) => cb());
@@ -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,
@@ -233,8 +233,9 @@
233
233
  return attrs.value ?? '';
234
234
  }
235
235
  },
236
- set(event) {
237
- emit('input', event);
236
+ set() {
237
+ // emit is not needed, the vuetify component already emits an @input event. (forwarded listeners)
238
+ // emit('input', event);
238
239
  },
239
240
  });
240
241
  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',
@@ -4,7 +4,18 @@
4
4
  v-if="showSearchbar"
5
5
  :placeholder="$t(searchbarPlaceholder)"
6
6
  v-model="search"
7
- />
7
+ @input="handleSearch"
8
+ >
9
+ <template #prepend="scope">
10
+ <slot name="prepend" v-bind="scope" />
11
+ </template>
12
+ <template #default="scope">
13
+ <slot v-bind="scope" />
14
+ </template>
15
+ <template #append="scope">
16
+ <slot name="append" v-bind="scope" />
17
+ </template>
18
+ </VcsTreeviewSearchbar>
8
19
  <v-data-table
9
20
  dense
10
21
  :headers="translatedHeaders"
@@ -22,14 +33,20 @@
22
33
  $t('components.vcsDataTable.noResultsPlaceholder')
23
34
  "
24
35
  :single-select="singleSelect"
36
+ :server-items-length="serverItemsLength"
25
37
  hide-default-footer
26
38
  v-bind="$attrs"
27
39
  v-on="$listeners"
28
40
  class="vcs-table rounded-0"
41
+ @update:options="(o) => $emit('update:items', { ...o, search })"
29
42
  >
30
43
  <!-- eslint-disable-next-line -->
31
44
  <template v-for="(_, slot) of $scopedSlots" #[slot]="scope">
32
- <slot :name="slot" v-bind="scope" />
45
+ <slot
46
+ v-if="!['prepend', 'default', 'append'].includes(slot)"
47
+ :name="slot"
48
+ v-bind="scope"
49
+ />
33
50
  </template>
34
51
  <!-- eslint-disable-next-line -->
35
52
  <template v-slot:header.data-table-select="{ props, on }">
@@ -54,10 +71,11 @@
54
71
  </div>
55
72
  </template>
56
73
  <!-- eslint-disable-next-line -->
57
- <template v-slot:item.data-table-select="{ isSelected, select, index }">
74
+ <template #item.data-table-select="{ isSelected, select, item, index }">
58
75
  <div @mouseover="hovering = index" @mouseout="hovering = null">
59
76
  <v-icon
60
77
  v-if="isSelected"
78
+ :disabled="item.disabled"
61
79
  @click="select(!isSelected)"
62
80
  class="vcs-select-icon"
63
81
  >
@@ -67,17 +85,23 @@
67
85
  v-else-if="
68
86
  hovering === index || (!singleSelect && value.length > 0)
69
87
  "
88
+ :disabled="item.disabled"
70
89
  @click="select(!isSelected)"
71
90
  class="vcs-select-icon"
72
91
  >
73
92
  mdi-circle-outline
74
93
  </v-icon>
75
- <v-icon v-else @click="select(!isSelected)" class="vcs-select-icon">
94
+ <v-icon
95
+ v-else
96
+ :disabled="item.disabled"
97
+ @click="select(!isSelected)"
98
+ class="vcs-select-icon"
99
+ >
76
100
  mdi-circle-small
77
101
  </v-icon>
78
102
  </div>
79
103
  </template>
80
- <template #footer v-if="items.length > itemsPerPageRef">
104
+ <template #footer v-if="showFooter">
81
105
  <v-divider />
82
106
  <v-container class="pa-2 vcs-pagination-bar">
83
107
  <v-row dense no-gutters justify="center" class="align-center">
@@ -143,14 +167,31 @@
143
167
  import VcsTreeviewSearchbar from '../lists/VcsTreeviewSearchbar.vue';
144
168
  import VcsButton from '../buttons/VcsButton.vue';
145
169
 
170
+ /**
171
+ * @typedef {Object} UpdateItemsEvent
172
+ * @property {number} page
173
+ * @property {number} itemsPerPage
174
+ * @property {string[]} sortBy
175
+ * @property {boolean[]} sortDesc
176
+ * @property {string[]} groupBy
177
+ * @property {boolean[]} groupDesc
178
+ * @property {boolean} multiSort
179
+ * @property {boolean} mustSort
180
+ * @property {string} search
181
+ */
182
+
146
183
  /**
147
184
  * @description A wrapper around {@link https://vuetifyjs.com/en/api/v-data-table/#props v-data-table } with custom pagination
185
+ * Passes all slots to v-data-table and 'prepend', 'default' and 'append' slots to VcsSearchbar
148
186
  * @vue-prop {Array<Object>} items - array of items, where each item must provide a unique key
149
187
  * @vue-prop {string} itemKey - the key property, which is unique on all items.
188
+ * @vue-prop {number} serverItemsLength - number of total items on a backend. Used for server-side pagination.
189
+ * @vue-prop {number} serverPagesLength - number of total pages on a backend. Used for server-side pagination.
150
190
  * @vue-prop {Array<{text: string, value: string}>} [headers] - optional array defining column names. Text will be translated
151
191
  * @vue-prop {boolean} [showSearchbar=true] - whether to show searchbar
152
192
  * @vue-prop {string} [searchbarPlaceholder] - placeholder for searchbar
153
193
  * @vue-prop {boolean} [singleSelect=false]
194
+ * @vue-event {UpdateItemsEvent} update:items - Emits when one of the options properties is updated or on search input. Can be used to update items via API call to a server.
154
195
  * @vue-computed {Array<TableItem>} filteredItems - array of items with search filter applied on. If search string is empty, same as items array.
155
196
  * @vue-computed {Array<import("vuetify").DataTableHeader>} translatedHeaders - array of translated header items.
156
197
  * @vue-computed {number} numberOfItems - number of filtered items (depending on search).
@@ -187,6 +228,14 @@
187
228
  type: String,
188
229
  required: true,
189
230
  },
231
+ serverItemsLength: {
232
+ type: Number,
233
+ default: -1,
234
+ },
235
+ serverPagesLength: {
236
+ type: Number,
237
+ default: -1,
238
+ },
190
239
  itemsPerPage: {
191
240
  type: Number,
192
241
  default: 10,
@@ -212,7 +261,7 @@
212
261
  default: () => [],
213
262
  },
214
263
  },
215
- setup(props) {
264
+ setup(props, { attrs, emit }) {
216
265
  const vm = getCurrentInstance().proxy;
217
266
  const hovering = ref(null);
218
267
  /**
@@ -230,12 +279,15 @@
230
279
  if (filter) {
231
280
  const q = filter.toLocaleLowerCase();
232
281
  return Object.values(item).some((i) => {
233
- const content = i.toString();
234
- const translated = vm.$t(content);
235
- return (
236
- translated.toLowerCase().includes(q) ||
237
- content.toLowerCase().includes(q)
238
- );
282
+ if (i) {
283
+ const content = i.toString();
284
+ const translated = vm.$t(content);
285
+ return (
286
+ translated.toLowerCase().includes(q) ||
287
+ content.toLowerCase().includes(q)
288
+ );
289
+ }
290
+ return false;
239
291
  });
240
292
  }
241
293
  return true;
@@ -249,7 +301,12 @@
249
301
  handleFilter(item.value, search.value, item),
250
302
  ),
251
303
  );
252
- const numberOfItems = computed(() => filteredItems.value.length);
304
+ const numberOfItems = computed(() => {
305
+ if (props.serverItemsLength > -1) {
306
+ return props.serverItemsLength;
307
+ }
308
+ return filteredItems.value.length;
309
+ });
253
310
  const totalNumber = computed(() => props.items.length);
254
311
 
255
312
  /**
@@ -267,6 +324,9 @@
267
324
  */
268
325
  const itemsPerPageRef = ref(props.itemsPerPage);
269
326
  const numberOfPages = computed(() => {
327
+ if (props.serverPagesLength > -1) {
328
+ return props.serverPagesLength;
329
+ }
270
330
  return Math.ceil(numberOfItems.value / itemsPerPageRef.value);
271
331
  });
272
332
  /**
@@ -281,6 +341,31 @@
281
341
  return last < numberOfItems.value ? last : numberOfItems.value;
282
342
  });
283
343
 
344
+ const handleSearch = () => {
345
+ page.value = 1;
346
+ const { sortBy, sortDesc, groupBy, groupDesc, multiSort, mustSort } =
347
+ attrs;
348
+ // attrs of v-data-table cannot be accessed outside this component but are necessary for server-side pagination and search
349
+ // hence all relevant variables are emitted
350
+ emit('update:items', {
351
+ page: page.value,
352
+ itemsPerPage: itemsPerPageRef.value,
353
+ sortBy,
354
+ sortDesc,
355
+ groupBy,
356
+ groupDesc,
357
+ multiSort,
358
+ mustSort,
359
+ search: search.value,
360
+ });
361
+ };
362
+
363
+ const showFooter = computed(
364
+ () =>
365
+ props.items.length > itemsPerPageRef.value ||
366
+ props.serverItemsLength > itemsPerPageRef.value,
367
+ );
368
+
284
369
  return {
285
370
  hovering,
286
371
  search,
@@ -306,7 +391,9 @@
306
391
  itemsPerPageRef.value = number;
307
392
  },
308
393
  handleFilter,
394
+ handleSearch,
309
395
  translatedHeaders,
396
+ showFooter,
310
397
  };
311
398
  },
312
399
  };