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

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 (102) hide show
  1. package/build/buildCesium.js +7 -0
  2. package/config/dev.config.json +4 -0
  3. package/dist/assets/cesium/ThirdParty/Workers/basis_transcoder.js +21 -0
  4. package/dist/assets/cesium/ThirdParty/Workers/draco_decoder_nodejs.js +117 -0
  5. package/dist/assets/cesium/ThirdParty/Workers/package.json +1 -0
  6. package/dist/assets/cesium/ThirdParty/Workers/pako_deflate.min.js +2 -0
  7. package/dist/assets/cesium/ThirdParty/Workers/pako_inflate.min.js +2 -0
  8. package/dist/assets/cesium/ThirdParty/Workers/z-worker-pako.js +1 -0
  9. package/dist/assets/cesium/ThirdParty/basis_transcoder.wasm +0 -0
  10. package/dist/assets/cesium/ThirdParty/draco_decoder.wasm +0 -0
  11. package/dist/assets/cesium/ThirdParty/google-earth-dbroot-parser.js +8337 -0
  12. package/dist/assets/{cesium.305b7c.js → cesium.82fdbe.js} +4 -4
  13. package/dist/assets/cesium.js +1 -1
  14. package/dist/assets/{core.f3d6d4.js → core.df069a.js} +2 -2
  15. package/dist/assets/core.js +1 -1
  16. package/dist/assets/index-1cff371d.js +1 -0
  17. package/dist/assets/ol.js +1 -1
  18. package/dist/assets/style/icon-marker-blue.534e37.png +0 -0
  19. package/dist/assets/style/icon-marker-green.0b6a92.png +0 -0
  20. package/dist/assets/style/icon-marker-o-blue.7b6d62.png +0 -0
  21. package/dist/assets/style/icon-marker-o-green.c863c0.png +0 -0
  22. package/dist/assets/style/icon-marker-o-red.93ff58.png +0 -0
  23. package/dist/assets/style/icon-marker-o.036477.png +0 -0
  24. package/dist/assets/style/icon-marker-red.313d03.png +0 -0
  25. package/dist/assets/style/icon-marker.70960f.png +0 -0
  26. package/dist/assets/style/icon-pin-blue.7be369.png +0 -0
  27. package/dist/assets/style/icon-pin-green.cbb935.png +0 -0
  28. package/dist/assets/style/icon-pin-red.3f25b2.png +0 -0
  29. package/dist/assets/style/icon-pin.b7ce77.png +0 -0
  30. package/dist/assets/{ui.74022f.css → ui.3ed7ff.css} +2 -2
  31. package/dist/assets/ui.3ed7ff.js +14763 -0
  32. package/dist/assets/ui.js +1 -1
  33. package/dist/assets/vue.js +2 -2
  34. package/dist/assets/{vuetify.30486f.js → vuetify.614278.js} +1 -1
  35. package/dist/assets/vuetify.js +2 -2
  36. package/dist/index.html +1 -1
  37. package/index.js +17 -1
  38. package/package.json +1 -1
  39. package/plugins/@vcmap/create-link/index.js +8 -8
  40. package/plugins/@vcmap-show-case/collection-manager-example/index.js +6 -1
  41. package/plugins/@vcmap-show-case/form-inputs-example/FormInputsExample.vue +36 -8
  42. package/plugins/@vcmap-show-case/form-inputs-example/exampleActions.js +0 -19
  43. package/plugins/@vcmap-show-case/form-inputs-example/index.js +1 -2
  44. package/plugins/@vcmap-show-case/style-input-example/README.md +4 -0
  45. package/plugins/@vcmap-show-case/style-input-example/index.js +42 -0
  46. package/plugins/@vcmap-show-case/style-input-example/package.json +5 -0
  47. package/plugins/@vcmap-show-case/style-input-example/styleExample.vue +191 -0
  48. package/plugins/@vcmap-show-case/window-tester/WindowExample.vue +12 -13
  49. package/plugins/@vcmap-show-case/window-tester/windowExampleToggleChild.vue +44 -0
  50. package/public/assets/style/icon-marker-blue.png +0 -0
  51. package/public/assets/style/icon-marker-green.png +0 -0
  52. package/public/assets/style/icon-marker-o-blue.png +0 -0
  53. package/public/assets/style/icon-marker-o-green.png +0 -0
  54. package/public/assets/style/icon-marker-o-red.png +0 -0
  55. package/public/assets/style/icon-marker-o.png +0 -0
  56. package/public/assets/style/icon-marker-red.png +0 -0
  57. package/public/assets/style/icon-marker.png +0 -0
  58. package/public/assets/style/icon-pin-blue.png +0 -0
  59. package/public/assets/style/icon-pin-green.png +0 -0
  60. package/public/assets/style/icon-pin-red.png +0 -0
  61. package/public/assets/style/icon-pin.png +0 -0
  62. package/src/actions/actionHelper.js +1 -0
  63. package/src/application/VcsApp.vue +7 -1
  64. package/src/components/buttons/VcsActionButtonList.vue +1 -0
  65. package/src/components/form-inputs-controls/VcsCheckbox.vue +3 -2
  66. package/src/components/form-inputs-controls/VcsFormSection.vue +59 -9
  67. package/src/components/form-inputs-controls/VcsLabel.vue +10 -0
  68. package/src/components/form-inputs-controls/VcsRadioGrid.vue +175 -0
  69. package/src/components/form-inputs-controls/VcsSelect.vue +3 -0
  70. package/src/components/form-inputs-controls/VcsTextField.vue +12 -0
  71. package/src/components/icons/+all.js +4 -0
  72. package/src/components/icons/EditVerticesIcon.vue +39 -0
  73. package/src/components/lists/VcsActionList.vue +2 -0
  74. package/src/components/style/MenuWrapper.vue +138 -0
  75. package/src/components/style/VcsFillMenu.vue +61 -0
  76. package/src/components/style/VcsFillSelector.vue +45 -0
  77. package/src/components/style/VcsImageMenu.vue +84 -0
  78. package/src/components/style/VcsImageSelector.vue +609 -0
  79. package/src/components/style/VcsStrokeMenu.vue +73 -0
  80. package/src/components/style/VcsStrokeSelector.vue +87 -0
  81. package/src/components/style/VcsTextMenu.vue +81 -0
  82. package/src/components/style/VcsTextSelector.vue +271 -0
  83. package/src/components/style/VcsVectorStyleComponent.vue +119 -0
  84. package/src/components/style/composables.js +84 -0
  85. package/src/contentTree/contentTreeCollection.js +1 -0
  86. package/src/i18n/de.js +51 -17
  87. package/src/i18n/en.js +56 -22
  88. package/src/legend/vcsLegend.vue +7 -5
  89. package/src/manager/collectionManager/CollectionComponent.vue +9 -17
  90. package/src/manager/collectionManager/CollectionComponentList.vue +22 -9
  91. package/src/manager/collectionManager/CollectionManager.vue +20 -1
  92. package/src/manager/collectionManager/collectionComponent.js +11 -0
  93. package/src/manager/window/WindowComponent.vue +2 -1
  94. package/src/manager/window/WindowManager.vue +23 -9
  95. package/src/manager/window/windowHelper.js +76 -4
  96. package/src/manager/window/windowManager.js +38 -6
  97. package/src/vcsUiApp.js +1 -0
  98. package/dist/assets/index-f94d5be3.js +0 -1
  99. package/dist/assets/ui.74022f.js +0 -13466
  100. /package/dist/assets/{ol.39cc05.js → ol.90a5d0.js} +0 -0
  101. /package/dist/assets/{vue.9b8c6e.js → vue.537ff3.js} +0 -0
  102. /package/dist/assets/{vuetify.30486f.css → vuetify.614278.css} +0 -0
Binary file
@@ -69,6 +69,7 @@ export function createToggleAction(
69
69
  name: String,
70
70
  icon: [undefined, String],
71
71
  title: [undefined, String],
72
+ hasUpdate: [undefined, Boolean],
72
73
  });
73
74
  check(windowComponent, { id: String });
74
75
  check(owner, [String, vcsAppSymbol]);
@@ -154,6 +154,11 @@
154
154
  CesiumMap: '$vcs3d',
155
155
  ObliqueMap: '$vcsObliqueView',
156
156
  };
157
+ const mapBtnWeight = {
158
+ OpenlayersMap: 3,
159
+ CesiumMap: 2,
160
+ ObliqueMap: 1,
161
+ };
157
162
 
158
163
  const mapButtonActionDestroy = {};
159
164
  const setupMap = ({ className, name }) => {
@@ -173,6 +178,7 @@
173
178
  {
174
179
  id: `mapButton-${name}`,
175
180
  action,
181
+ weight: mapBtnWeight[className],
176
182
  },
177
183
  vcsAppSymbol,
178
184
  ButtonLocation.MAP,
@@ -342,7 +348,7 @@
342
348
  provides: {
343
349
  collectionManager: app.categoryManager,
344
350
  },
345
- slot: WindowSlot.STATIC,
351
+ slot: WindowSlot.DYNAMIC_LEFT,
346
352
  },
347
353
  app.windowManager,
348
354
  vcsAppSymbol,
@@ -9,6 +9,7 @@
9
9
  :icon="btn.icon"
10
10
  :active="btn.active"
11
11
  :disabled="btn.disabled"
12
+ :has-update="btn.hasUpdate"
12
13
  :background="btn.background"
13
14
  @click.stop="btn.callback($event)"
14
15
  v-bind="{ ...$attrs }"
@@ -43,8 +43,9 @@
43
43
  label.v-label.error--text {
44
44
  animation: none;
45
45
  }
46
- .v-icon,
47
- .v-label {
46
+
47
+ .primary--text,
48
+ .v-label:not(.v-label--is-disabled) {
48
49
  &.theme--light {
49
50
  color: map-get($shades, 'black') !important;
50
51
  &.error--text {
@@ -1,29 +1,49 @@
1
1
  <template>
2
2
  <section class="vcs-form-section">
3
3
  <slot name="header" :heading="heading" :actions="actions">
4
- <article class="pa-2 base lighten-3">
5
- <div
6
- class="form-section-header d-flex justify-space-between align-center"
7
- >
8
- <strong>{{ $t(heading) }}</strong>
4
+ <div class="vcs-form-section-header d-flex base lighten-3">
5
+ <div class="d-flex justify-space-between w-full">
6
+ <div class="d-flex align-center" :class="{ 'px-2': !expandable }">
7
+ <v-btn
8
+ :ripple="false"
9
+ dense
10
+ plain
11
+ icon
12
+ small
13
+ text
14
+ :disabled="disabled"
15
+ elevation="0"
16
+ @click="open = !open"
17
+ v-if="expandable"
18
+ >
19
+ <v-icon>{{
20
+ open ? 'mdi-chevron-down' : 'mdi-chevron-right'
21
+ }}</v-icon>
22
+ </v-btn>
23
+ <strong :class="{ 'text--disabled': disabled }">{{
24
+ $t(heading)
25
+ }}</strong>
26
+ </div>
9
27
  <VcsActionButtonList
10
28
  :actions="actions"
11
29
  :overflow-count="actionButtonListOverflowCount"
30
+ class="pa-2"
12
31
  />
13
32
  </div>
14
- </article>
33
+ </div>
15
34
  </slot>
16
35
  <VcsHelp :text="helpText" :show="showHelp" class="base lighten-4">
17
36
  <slot name="help" />
18
37
  </VcsHelp>
19
- <article class="section-content">
38
+ <article class="section-content" v-if="showContent">
20
39
  <slot />
21
40
  </article>
22
41
  </section>
23
42
  </template>
24
43
 
25
44
  <script>
26
- import { computed, reactive } from 'vue';
45
+ import { computed, reactive, ref } from 'vue';
46
+ import { VBtn, VIcon } from 'vuetify/lib';
27
47
  import VcsActionButtonList from '../buttons/VcsActionButtonList.vue';
28
48
  import VcsHelp from '../notification/VcsHelp.vue';
29
49
 
@@ -34,6 +54,9 @@
34
54
  * @vue-data {slot} [#default] - slot with the section content
35
55
  * @vue-data {slot} [#help] - Slot to specify html based help. Gets precedence over helpText prop.
36
56
  * @vue-prop {string} heading - Title of the section to be displayed, will be translated.
57
+ * @vue-prop {boolean} [expandable=false] - If true, section can be toggled.
58
+ * @vue-prop {boolean} [startOpen=false] - If section starts open.
59
+ * @vue-prop {boolean} [disabled=false] - If expansion is disabled. Title is rendered in disabled style.
37
60
  * @vue-prop {Array<VcsAction>} headerActions - Icons to be displayed on the right side
38
61
  * @vue-prop {number} [actionButtonListOverflowCount] - overflow count to use for action lists in the title and items
39
62
  * @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.
@@ -42,6 +65,8 @@
42
65
  export default {
43
66
  name: 'VcsFormSection',
44
67
  components: {
68
+ VBtn,
69
+ VIcon,
45
70
  VcsActionButtonList,
46
71
  VcsHelp,
47
72
  },
@@ -50,6 +75,18 @@
50
75
  type: String,
51
76
  default: undefined,
52
77
  },
78
+ expandable: {
79
+ type: Boolean,
80
+ default: false,
81
+ },
82
+ startOpen: {
83
+ type: Boolean,
84
+ default: false,
85
+ },
86
+ disabled: {
87
+ type: Boolean,
88
+ default: false,
89
+ },
53
90
  headerActions: {
54
91
  type: Array,
55
92
  default: () => [],
@@ -65,6 +102,13 @@
65
102
  },
66
103
  },
67
104
  setup(props, { slots }) {
105
+ const open = ref(props.startOpen);
106
+ const showContent = computed(() => {
107
+ if (props.expandable) {
108
+ return open.value;
109
+ }
110
+ return true;
111
+ });
68
112
  const helpAction = reactive({
69
113
  name: 'help',
70
114
  title: 'components.vcsFormSection.help',
@@ -86,6 +130,8 @@
86
130
  });
87
131
 
88
132
  return {
133
+ open,
134
+ showContent,
89
135
  showHelp,
90
136
  actions,
91
137
  };
@@ -93,7 +139,11 @@
93
139
  };
94
140
  </script>
95
141
 
96
- <style scoped>
142
+ <style lang="scss" scoped>
143
+ @import '../../styles/shades.scss';
144
+ .vcs-form-section-header {
145
+ height: 40px;
146
+ }
97
147
  .v-alert--text:before {
98
148
  background-color: transparent;
99
149
  }
@@ -4,6 +4,7 @@
4
4
  class="vcs-label"
5
5
  :class="{
6
6
  'vcs-label-dense': dense,
7
+ 'vcs-label-required': required,
7
8
  'px-1': dense,
8
9
  'px-2': !dense,
9
10
  }"
@@ -22,6 +23,10 @@
22
23
  .vcs-label-dense {
23
24
  line-height: $line-height-dense;
24
25
  }
26
+ .vcs-label-required:after {
27
+ content: ' *';
28
+ color: var(--v-error-base);
29
+ }
25
30
  </style>
26
31
  <script>
27
32
  /**
@@ -29,6 +34,7 @@
29
34
  * pass the label text as innerHtml
30
35
  * @vue-prop {string} htmlFor - an id reference the label is meant for
31
36
  * @vue-prop {boolean} [dense=true] - default line height is 32px (dense). If set false, height is 40px.
37
+ * @vue-prop {boolean} [required=false] - Marks an input field as required by adding an asterisk after the label.
32
38
  */
33
39
  export default {
34
40
  name: 'VcsLabel',
@@ -41,6 +47,10 @@
41
47
  type: Boolean,
42
48
  default: true,
43
49
  },
50
+ required: {
51
+ type: Boolean,
52
+ default: false,
53
+ },
44
54
  },
45
55
  };
46
56
  </script>
@@ -0,0 +1,175 @@
1
+ <template>
2
+ <VcsTooltip
3
+ :tooltip-position="tooltipPosition"
4
+ :tooltip="errorMessage"
5
+ color="error"
6
+ :max-width="200"
7
+ >
8
+ <template #activator="{ on, attrs }">
9
+ <v-container v-on="on" class="py-0" :class="isDense ? 'px-1' : 'px-2'">
10
+ <v-radio-group
11
+ ref="radioGroup"
12
+ hide-details
13
+ class="vcs-radio-group"
14
+ :ripple="false"
15
+ :dense="isDense"
16
+ v-bind="{ ...$attrs, ...attrs }"
17
+ v-on="{ ...$listeners, ...on }"
18
+ >
19
+ <v-row no-gutters class="justify-center">
20
+ <v-col
21
+ v-for="(item, idx) in items"
22
+ :key="idx"
23
+ cols="1"
24
+ class="mx-2"
25
+ >
26
+ <v-row no-gutters>
27
+ <v-col>
28
+ <div
29
+ class="d-flex justify-center label-wrapper pt-1"
30
+ :class="$attrs.disabled ? 'disabled' : ''"
31
+ >
32
+ <slot name="label" :value="item[itemValue]" :src="item.src">
33
+ <img
34
+ :src="item.src"
35
+ :alt="item[itemValue]"
36
+ class="image"
37
+ />
38
+ </slot>
39
+ </div>
40
+ </v-col>
41
+ </v-row>
42
+ <v-row no-gutters>
43
+ <v-col>
44
+ <v-radio
45
+ :value="item[itemValue]"
46
+ :ripple="false"
47
+ class="ma-0"
48
+ :class="isDense ? 'vcs-radio-dense' : 'vcs-radio'"
49
+ :dense="isDense"
50
+ />
51
+ </v-col>
52
+ </v-row>
53
+ </v-col>
54
+ </v-row>
55
+ </v-radio-group>
56
+ </v-container>
57
+ </template>
58
+ </VcsTooltip>
59
+ </template>
60
+
61
+ <script>
62
+ import { computed, ref } from 'vue';
63
+ import { VContainer, VRow, VCol, VRadioGroup, VRadio } from 'vuetify/lib';
64
+ import { VcsTooltip } from '@vcmap/ui';
65
+ import { useErrorSync } from './composables.js';
66
+
67
+ /**
68
+ * @description Stylized wrapper around {@link https://vuetifyjs.com/en/api/v-radio-group/ |vuetify radio group} which places icons or raster src files above the radio buttons as labels and arranges them in a grid.
69
+ * @vue-prop {{value: string, src: string}[]} items - The items to be displayed in the grid. The src is the path to the raster image.
70
+ * @vue-prop {('bottom' | 'left' | 'top' | 'right')} [tooltipPosition='right'] - Position of the error tooltip.
71
+ * @vue-prop {string} itemValue - The key of the provided item objects that contains the value.
72
+ * @vue-data {slot} [#label] - slot to display labels where the src does not contain a path to an img but e.g. the name of an icon. Then an v-icon can be used in the slot.
73
+ */
74
+ export default {
75
+ name: 'VcsRadioGrid',
76
+ components: {
77
+ VContainer,
78
+ VRow,
79
+ VCol,
80
+ VRadioGroup,
81
+ VRadio,
82
+ VcsTooltip,
83
+ },
84
+ props: {
85
+ items: {
86
+ type: Array,
87
+ required: true,
88
+ },
89
+ tooltipPosition: {
90
+ type: String,
91
+ default: 'right',
92
+ },
93
+ itemValue: {
94
+ type: String,
95
+ default: 'value',
96
+ },
97
+ },
98
+ setup(props, { attrs }) {
99
+ const radioGroup = ref();
100
+
101
+ const errorMessage = useErrorSync(radioGroup);
102
+ return {
103
+ isDense: computed(() => attrs.dense !== false),
104
+ radioGroup,
105
+ errorMessage,
106
+ };
107
+ },
108
+ model: {
109
+ event: 'change',
110
+ },
111
+ };
112
+ </script>
113
+
114
+ <style lang="scss" scoped>
115
+ // TODO: scss for radio buttons should be moved to own file.
116
+ @import '../../styles/vcsFont.scss';
117
+ @import '../../styles/shades.scss';
118
+ .v-input--radio-group--column .v-radio:not(:last-child):not(:only-child) {
119
+ margin-bottom: 0;
120
+ }
121
+ .v-input {
122
+ &.vcs-radio-group {
123
+ ::v-deep {
124
+ margin-top: 0;
125
+ padding-top: 0;
126
+ label.v-label,
127
+ .v-icon.v-icon {
128
+ font-size: $vcs-font-size;
129
+ color: inherit;
130
+ &.theme--light {
131
+ color: map-get($shades, 'black') !important;
132
+ &.error--text {
133
+ color: var(--v-error-base) !important;
134
+ }
135
+ }
136
+ &.theme--dark {
137
+ color: map-get($shades, 'white') !important;
138
+ &.error--text {
139
+ color: var(--v-error-base) !important;
140
+ }
141
+ }
142
+ }
143
+ .v-radio:not(:last-child):not(:only-child) {
144
+ margin-bottom: 0;
145
+ }
146
+ .v-input--selection-controls__input {
147
+ margin: 0;
148
+ }
149
+ label.v-label.error--text {
150
+ animation: none;
151
+ }
152
+ }
153
+ }
154
+ }
155
+ .vcs-radio {
156
+ height: 40px;
157
+ align-items: center;
158
+ }
159
+ .vcs-radio-dense {
160
+ height: 32px;
161
+ align-items: center;
162
+ }
163
+ .image {
164
+ max-height: 24px;
165
+ max-width: 24px;
166
+ height: auto;
167
+ width: auto;
168
+ }
169
+ .label-wrapper {
170
+ width: 24px;
171
+ }
172
+ .disabled {
173
+ opacity: 0.3;
174
+ }
175
+ </style>
@@ -40,6 +40,9 @@
40
40
  (+{{ $attrs.value.length - 1 }})
41
41
  </span>
42
42
  </template>
43
+ <template v-for="(_, slot) of $scopedSlots" #[slot]="scope">
44
+ <slot :name="slot" v-bind="scope" />
45
+ </template>
43
46
  </v-select>
44
47
  </span>
45
48
  </template>
@@ -24,6 +24,7 @@
24
24
  v-bind="{ ...$attrs, ...attrs }"
25
25
  v-on="{ ...$listeners, ...on }"
26
26
  :height="isDense ? 24 : 32"
27
+ :rules="rules"
27
28
  class="py-1 primary--placeholder align-center"
28
29
  :class="{
29
30
  'remove-outline': !isOutlined,
@@ -244,6 +245,16 @@
244
245
  }
245
246
  });
246
247
 
248
+ const rules = computed(() => {
249
+ if (attrs.type === 'number' && Array.isArray(attrs.rules)) {
250
+ return attrs.rules.map((rule) => {
251
+ return (value) => rule(parseFloat(value));
252
+ });
253
+ } else {
254
+ return attrs.rules;
255
+ }
256
+ });
257
+
247
258
  function onEscape(event) {
248
259
  textFieldRef.value.blur();
249
260
  emit('input', textFieldRef.value.initialValue);
@@ -268,6 +279,7 @@
268
279
  isOutlined,
269
280
  visibleValue,
270
281
  type,
282
+ rules,
271
283
  onEscape,
272
284
  textFieldRef,
273
285
  errorMessage,
@@ -22,6 +22,7 @@ import ComponentsIcon from './ComponentsIcon.vue';
22
22
  import ConeIcon from './ConeIcon.vue';
23
23
  import DimensionsHouseIcon from './DimensionsHouseIcon.vue';
24
24
  import EditIcon from './EditIcon.vue';
25
+ import EditVerticesIcon from './EditVerticesIcon.vue';
25
26
  import ElevationProfileIcon from './ElevationProfileIcon.vue';
26
27
  import ExportAreaIcon from './ExportAreaIcon.vue';
27
28
  import ExportFlightIcon from './ExportFlightIcon.vue';
@@ -204,6 +205,9 @@ const IconMap = {
204
205
  edit: {
205
206
  component: EditIcon,
206
207
  },
208
+ editVertices: {
209
+ component: EditVerticesIcon,
210
+ },
207
211
  elevationProfile: {
208
212
  component: ElevationProfileIcon,
209
213
  },
@@ -0,0 +1,39 @@
1
+ <template>
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="23.907"
5
+ height="24"
6
+ viewBox="0 0 23.907 24"
7
+ >
8
+ <g id="icon-16-edit_vertex" transform="translate(0 0)">
9
+ <path
10
+ id="Path_840"
11
+ data-name="Path 840"
12
+ d="M-5.217-90.456l1.376,1.3,6.355-6.38L1.138-96.81Z"
13
+ transform="translate(5.217 109.281)"
14
+ fill="currentColor"
15
+ />
16
+ <path
17
+ id="Path_841"
18
+ data-name="Path 841"
19
+ d="M2.9-100.708A2.818,2.818,0,0,0-.074-97.655,2.827,2.827,0,0,0,2.9-94.778,2.8,2.8,0,0,0,5.88-97.655,2.853,2.853,0,0,0,2.9-100.708Z"
20
+ transform="translate(7.204 107.775)"
21
+ fill="currentColor"
22
+ />
23
+ <path
24
+ id="Path_842"
25
+ data-name="Path 842"
26
+ d="M3.806-99.513l1.5,1.426,6.3-6.355-1.363-1.363Z"
27
+ transform="translate(8.703 105.805)"
28
+ fill="currentColor"
29
+ />
30
+ <path
31
+ id="Path_843"
32
+ data-name="Path 843"
33
+ d="M4.221-96.244,8.771-85.5l1.871-4.384,4.4-1.958Z"
34
+ transform="translate(8.863 109.5)"
35
+ fill="currentColor"
36
+ />
37
+ </g>
38
+ </svg>
39
+ </template>
@@ -53,6 +53,7 @@
53
53
  * @property {string} [icon] - icon rendered on the button. If no icon provided, item is rendered in overflow
54
54
  * @property {Function} callback - callback function is triggered when the button is clicked
55
55
  * @property {boolean} [active=false] - optional state of button. If active, button is rendered in primary color
56
+ * @property {boolean} [hasUpdate=false] - optional hasUpdate of button. If true, a yellow notification is rendered next to the button
56
57
  * @property {boolean} [background=false] - optional background state. If active and background, button is rendered in primary color outlined
57
58
  * @property {boolean} [disabled=false] - optional flag to indicate that the action is disabled
58
59
  */
@@ -68,6 +69,7 @@
68
69
  callback: Function,
69
70
  active: [undefined, Boolean],
70
71
  background: [undefined, Boolean],
72
+ hasUpdate: [undefined, Boolean],
71
73
  disabled: [undefined, Boolean],
72
74
  };
73
75
 
@@ -0,0 +1,138 @@
1
+ <template>
2
+ <v-sheet>
3
+ <div class="d-flex align-center py-1">
4
+ <VcsCheckbox
5
+ :value="!!value"
6
+ @change="handleCheckbox"
7
+ :disabled="disabled"
8
+ />
9
+ <v-menu
10
+ :close-on-content-click="false"
11
+ :close-on-click="false"
12
+ v-model="isMenuOpen"
13
+ :absolute="true"
14
+ :disabled="!value || disabled"
15
+ min-width="300"
16
+ max-width="300"
17
+ >
18
+ <template #activator="{ on, attrs }">
19
+ <VcsTooltip :tooltip="name">
20
+ <template #activator="{ on: tooltipOn, attrs: tooltipAttrs }">
21
+ <v-card
22
+ rounded
23
+ height="24px"
24
+ width="32px"
25
+ v-bind="{ ...attrs, ...tooltipAttrs }"
26
+ v-on="{ ...on, ...tooltipOn }"
27
+ class="tiled-background"
28
+ >
29
+ <slot name="preview" />
30
+ </v-card>
31
+ </template>
32
+ </VcsTooltip>
33
+ </template>
34
+ <VcsFormSection
35
+ :heading="name"
36
+ :header-actions="[
37
+ {
38
+ name: 'reset',
39
+ title: 'components.style.reset',
40
+ icon: '$vcsReturn',
41
+ callback: () => {
42
+ reset();
43
+ },
44
+ },
45
+ {
46
+ name: 'close',
47
+ title: 'components.close',
48
+ icon: 'mdi-close-thick',
49
+ callback: () => {
50
+ close();
51
+ },
52
+ },
53
+ ]"
54
+ >
55
+ <slot name="content" />
56
+ </VcsFormSection>
57
+ </v-menu>
58
+ <span class="ml-2">{{ $t(name) }}</span>
59
+ </div>
60
+ </v-sheet>
61
+ </template>
62
+
63
+ <script>
64
+ import { ref } from 'vue';
65
+ import { VSheet, VMenu, VCard } from 'vuetify/lib';
66
+ import { VcsFormSection, VcsTooltip, VcsCheckbox } from '@vcmap/ui';
67
+
68
+ /**
69
+ * @description A wrapper for style components, that provides:
70
+ * - a 32 x 24 px preview
71
+ * - a checkbox that emits null when unchecked and the value of the valueDefault prop when checked again. If the vauleDefault is undefined or null, the valueFallback will be emitted.
72
+ * - a menu that pops up when clicking the preview. It has also a reset button that emits the value of the valueDefault prop when clicked
73
+ * @vue-prop {Object} [value] - Style options that are modelled by the checkbox.
74
+ * @vue-prop {string} name - The name that is displayed in the header of the menu and in the tooltip of the preview.
75
+ * @vue-prop {Object} [valueDefault] - The default Options, that are applied when clicking reset or setting the checkbox from null to true.
76
+ * @vue-prop {Object} valueFallback - The fallback Options, in case the valueDefault is null or undefined. These are applied when the checkbox is checked again and the default value is null. The fallback value must not be null or undefined.
77
+ */
78
+ export default {
79
+ name: 'MenuWrapper',
80
+ components: {
81
+ VSheet,
82
+ VMenu,
83
+ VCard,
84
+ VcsFormSection,
85
+ VcsTooltip,
86
+ VcsCheckbox,
87
+ },
88
+ props: {
89
+ value: {
90
+ default: undefined,
91
+ required: false,
92
+ type: Object,
93
+ },
94
+ valueDefault: {
95
+ default: undefined,
96
+ type: Object,
97
+ },
98
+ valueFallback: {
99
+ required: true,
100
+ type: Object,
101
+ },
102
+ name: {
103
+ required: true,
104
+ type: String,
105
+ },
106
+ disabled: {
107
+ default: false,
108
+ type: Boolean,
109
+ },
110
+ },
111
+ setup(props, { emit }) {
112
+ const isMenuOpen = ref(false);
113
+
114
+ return {
115
+ isMenuOpen,
116
+ reset() {
117
+ emit('input', props.valueDefault);
118
+ },
119
+ close() {
120
+ isMenuOpen.value = false;
121
+ },
122
+ handleCheckbox(value) {
123
+ emit(
124
+ 'input',
125
+ value ? props.valueDefault || props.valueFallback : null,
126
+ );
127
+ },
128
+ };
129
+ },
130
+ };
131
+ </script>
132
+
133
+ <style scoped>
134
+ .tiled-background {
135
+ background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGElEQVQYlWNgYGCQwoKxgqGgcJA5h3yFAAs8BRWVSwooAAAAAElFTkSuQmCC)
136
+ repeat;
137
+ }
138
+ </style>