@mozaic-ds/vue 0.24.0-beta.0 → 0.25.0

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.
@@ -4,52 +4,55 @@
4
4
  v-click-outside="onClickOutside"
5
5
  class="mc-dropdown"
6
6
  :class="{ 'mc-dropdown--multi': multiple }"
7
- :style="tagStyle"
7
+ :style="setStyles"
8
8
  >
9
- <m-tag
10
- v-if="multiple && tagValue.length > 0"
9
+ <MTag
10
+ v-if="multiple && listboxValue.length > 0"
11
+ :id="tagId ? tagId : `autoCompleteTag-${uuid}`"
12
+ ref="tag"
13
+ :label="setTagLabel"
14
+ :disabled="disabled"
11
15
  type="removable"
12
- :label="tagValue.length.toString() + ' ' + tagLabel"
13
16
  class="mc-dropdown__tag"
14
17
  size="s"
15
18
  @remove-tag="clearAutocomplete()"
16
19
  />
17
-
18
20
  <button
19
21
  type="button"
20
22
  class="mc-select mc-dropdown__trigger"
21
- :class="{ 'is-open': isOpen }"
22
- @click="isOpen = !isOpen"
23
+ :class="{ 'is-open': openState }"
24
+ @click="openState = !openState"
23
25
  >
24
- {{ inputValue }}
26
+ {{ buttonValue }}
25
27
  </button>
26
- <m-list-box
28
+ <MListBox
27
29
  v-model="listboxValue"
28
- :open="isOpen"
29
- :items="itemListForDropdown"
30
+ :open="openState"
31
+ :items="localItems"
30
32
  :multiple="multiple"
33
+ :empty-search-label="emptySearchLabel"
31
34
  :data-key-expr="dataKeyExpr"
32
35
  :data-text-expr="dataTextExpr"
33
36
  :data-value-expr="dataValueExpr"
34
37
  @change="onChange"
35
38
  >
36
39
  <template #item="{ item }">
37
- <slot name="item" :item="item" />
40
+ <slot name="item" :item="item"></slot>
38
41
  </template>
39
- </m-list-box>
42
+ </MListBox>
40
43
  </div>
41
44
  </template>
42
45
 
43
46
  <script>
44
- import MListBox from '../listbox/MListBox.vue';
45
47
  import MTag from '../tags/MTag.vue';
48
+ import MListBox from '../listbox/MListBox.vue';
46
49
 
47
50
  export default {
48
51
  name: 'MDropdown',
49
52
 
50
53
  components: {
51
- MListBox,
52
54
  MTag,
55
+ MListBox,
53
56
  },
54
57
 
55
58
  directives: {
@@ -73,31 +76,49 @@ export default {
73
76
  },
74
77
 
75
78
  props: {
76
- open: {
77
- type: Boolean,
78
- default: false,
79
+ // Tag Element
80
+ tagId: {
81
+ type: String,
82
+ default: null,
79
83
  },
80
84
  tagLabel: {
81
85
  type: String,
82
86
  default: '',
83
87
  },
88
+ // Input Element
84
89
  placeholder: {
85
90
  type: String,
86
91
  default: '-- Placeholder --',
87
92
  },
93
+ filter: {
94
+ type: Function,
95
+ default: null,
96
+ },
97
+ disabled: {
98
+ type: Boolean,
99
+ default: false,
100
+ },
101
+ // Listbox Element
88
102
  items: {
89
103
  type: Array,
90
- default: () => [],
91
104
  required: true,
92
105
  },
93
- multiple: {
106
+ value: {
107
+ type: [Array, String, Number],
108
+ default: undefined,
109
+ },
110
+ open: {
94
111
  type: Boolean,
95
112
  default: false,
96
113
  },
97
- disabled: {
114
+ multiple: {
98
115
  type: Boolean,
99
116
  default: false,
100
117
  },
118
+ emptySearchLabel: {
119
+ type: String,
120
+ default: 'No results found',
121
+ },
101
122
  dataKeyExpr: {
102
123
  type: String,
103
124
  default: 'id',
@@ -110,27 +131,35 @@ export default {
110
131
  type: String,
111
132
  default: 'value',
112
133
  },
113
- value: {
114
- type: Array,
115
- default: () => [],
134
+ sort: {
135
+ type: Boolean,
136
+ default: true,
137
+ },
138
+ // Global
139
+ maxWidth: {
140
+ type: String,
141
+ default: '17.875rem',
116
142
  },
117
143
  },
118
144
 
119
145
  data() {
120
146
  return {
121
- itemListForDropdown: this.items,
122
- isOpen: this.open,
147
+ uuid: Math.random(),
148
+ openState: this.open,
123
149
  tagWidth: '0px',
124
- maxWidth: '17.875rem',
125
- uuid: undefined,
126
- tagValue: '',
127
- inputValue: '',
128
- listboxValue: [],
150
+ tagValue: null,
151
+ buttonValue: this.placeholder,
152
+ localItems: null,
153
+ sortedListItems: null,
154
+ listboxValue: null,
129
155
  };
130
156
  },
131
157
 
132
158
  computed: {
133
- tagStyle() {
159
+ setTagLabel() {
160
+ return this.listboxValue.length.toString() + ' ' + this.tagLabel;
161
+ },
162
+ setStyles() {
134
163
  return {
135
164
  '--tag-width': this.tagWidth,
136
165
  '--max-width': this.maxWidth,
@@ -139,64 +168,80 @@ export default {
139
168
  },
140
169
 
141
170
  watch: {
142
- listboxValue: function (newValue) {
143
- const textToDisplay = [];
144
- const valueExpr = this.dataValueExpr;
145
- const textExpr = this.dataTextExpr;
171
+ value: {
172
+ handler: function (val) {
173
+ if (!val && this.multiple) {
174
+ this.listboxValue = [];
175
+ } else {
176
+ this.listboxValue = val;
177
+ }
178
+ },
179
+ immediate: true,
180
+ },
181
+
182
+ items: {
183
+ handler: function (val) {
184
+ this.localItems = val;
185
+ // this.clearAutocomplete();
186
+ },
187
+ immediate: true,
188
+ },
146
189
 
147
- this.tagValue = newValue;
190
+ listboxValue: function (val) {
191
+ const value = Array.isArray(val) ? val : val.toString();
192
+ const selectedItems = this.getSelectedItems(value);
148
193
 
149
- if (newValue.length) {
150
- const selectedItems = this.items.filter((item) =>
151
- newValue.includes(item[valueExpr])
152
- );
194
+ const seletedLabels = selectedItems.map(
195
+ (item) => item[this.dataTextExpr]
196
+ );
153
197
 
154
- selectedItems.forEach((item) => textToDisplay.push(item[textExpr]));
198
+ this.buttonValue = seletedLabels.join(', ');
155
199
 
156
- this.inputValue = textToDisplay.join(', ');
157
- } else {
158
- this.inputValue = this.placeholder;
200
+ if (val.length === 0) {
201
+ this.buttonValue = this.placeholder;
159
202
  }
160
- },
161
203
 
162
- tagValue: {
163
- handler: function () {
164
- this.setTagWidth();
165
- },
166
- immediate: true,
204
+ if (this.multiple) {
205
+ this.tagValue = val;
206
+ }
167
207
  },
168
208
 
169
- value: {
170
- handler: function (value) {
171
- this.listboxValue = value;
172
- },
173
- immediate: true,
209
+ tagValue: function () {
210
+ this.setTagWidth();
174
211
  },
175
- },
176
212
 
177
- mounted() {
178
- this.uuid = this._uid;
213
+ openState: function (val) {
214
+ const eventName = val ? 'open' : 'close';
215
+ this.$emit(eventName);
216
+ this.$emit('update:open', val);
217
+ },
179
218
  },
180
219
 
181
220
  methods: {
182
221
  setTagWidth() {
183
222
  this.$nextTick(() => {
184
- this.tagWidth =
185
- document && document.querySelector('.mc-dropdown__tag')
186
- ? document.querySelector('.mc-dropdown__tag').clientWidth + 8 + 'px'
187
- : '0px';
223
+ if (this.$refs.tag && this.$refs.tag.$el) {
224
+ this.tagWidth = this.$refs.tag.$el.clientWidth + 8 + 'px';
225
+ } else {
226
+ this.tagWidth = '0px';
227
+ }
188
228
  });
189
229
  },
190
230
 
191
231
  clearAutocomplete() {
192
- this.listboxValue = [];
193
- this.inputValue = '';
232
+ this.listboxValue = this.multiple ? [] : undefined;
194
233
  this.onChange();
195
234
  this.$emit('clear');
196
235
  },
197
236
 
198
237
  onClickOutside() {
199
- this.isOpen = false;
238
+ this.openState = false;
239
+
240
+ if (this.multiple && this.sort) {
241
+ this.sortItems();
242
+ } else {
243
+ this.localItems = this.items;
244
+ }
200
245
  },
201
246
 
202
247
  onChange() {
@@ -206,6 +251,34 @@ export default {
206
251
  this.onClickOutside();
207
252
  }
208
253
  },
254
+
255
+ getSelectedItems(val) {
256
+ const value = val ? val : this.listboxValue;
257
+
258
+ const selectedItems = this.items.filter((item) =>
259
+ value.includes(item[this.dataValueExpr])
260
+ );
261
+
262
+ return selectedItems;
263
+ },
264
+
265
+ sortItems() {
266
+ this.sortedListItems = this.items;
267
+ const selectedItems = this.getSelectedItems();
268
+
269
+ this.sortedListItems.sort((a, b) => {
270
+ const hasItemA = selectedItems.includes(a);
271
+ const hasItemB = selectedItems.includes(b);
272
+
273
+ if (hasItemA === hasItemB) {
274
+ return a[this.dataValueExpr] - b[this.dataValueExpr];
275
+ } else if (hasItemA < hasItemB) {
276
+ return 1;
277
+ } else {
278
+ return -1;
279
+ }
280
+ });
281
+ },
209
282
  },
210
283
  };
211
284
  </script>
@@ -221,8 +294,6 @@ export default {
221
294
  &__tag {
222
295
  position: absolute;
223
296
  top: 0;
224
- -webkit-transform: translateY(50%);
225
- -ms-transform: translateY(50%);
226
297
  transform: translateY(50%);
227
298
  }
228
299
 
@@ -1,46 +1,50 @@
1
1
  <template>
2
2
  <ul
3
- v-if="items.length > 0"
4
- ref="listbox"
5
3
  role="listbox"
6
4
  class="mc-listbox"
7
5
  aria-labelledby="listbox"
8
6
  :class="{ 'is-open': open, 'mc-listbox--multi': multiple }"
9
7
  >
10
- <li v-for="item in localItems" :key="item.id" class="mc-listbox__item">
11
- <m-icon
12
- v-if="item.icon"
13
- :name="item.icon"
14
- class="mc-listbox__icon"
15
- color="#666666"
16
- />
17
- <label
18
- :for="`listboxItem-${item[dataKeyExpr]}-${uuid}`"
19
- class="mc-listbox__label"
8
+ <template v-if="!isFiltered">
9
+ <li
10
+ v-for="(item, index) in localItems"
11
+ :key="item.id"
12
+ class="mc-listbox__item"
20
13
  >
21
- {{ item[dataTextExpr] }}
22
- </label>
23
- <input
24
- :id="`listboxItem-${item[dataKeyExpr]}-${uuid}`"
25
- ref="input"
26
- v-model="localValue"
27
- class="mc-checkbox__input mc-listbox__input"
28
- type="checkbox"
29
- :value="item[dataValueExpr]"
30
- @change="onChange"
31
- />
14
+ <MIcon
15
+ v-if="item.icon"
16
+ :name="item.icon"
17
+ class="mc-listbox__icon"
18
+ color="#666666"
19
+ />
20
+ <input
21
+ :id="setItemId(index)"
22
+ v-model="localValue"
23
+ class="mc-listbox__input"
24
+ :class="{ 'mc-checkbox__input': multiple }"
25
+ :type="multiple ? 'checkbox' : 'radio'"
26
+ :value="item[dataValueExpr]"
27
+ :name="!multiple ? `listboxradio-${uuid}` : null"
28
+ @change="onChange"
29
+ />
30
+ <label :for="setItemId(index)" class="mc-listbox__label">
31
+ <slot name="item" :item="item">
32
+ {{ item[dataTextExpr] }}
33
+ </slot>
34
+ </label>
35
+ </li>
36
+ </template>
37
+ <li v-else class="mc-listbox__item">
38
+ <span class="mc-listbox__empty">{{ emptySearchLabel }}</span>
32
39
  </li>
33
40
  </ul>
34
- <div v-else class="mc-listbox__empty">
35
- {{ emptySearchLabel }}
36
- </div>
37
41
  </template>
38
42
 
39
43
  <script>
40
44
  import MIcon from '../icon/MIcon.vue';
41
45
 
42
46
  export default {
43
- name: 'MListbox',
47
+ name: 'MListBox',
44
48
 
45
49
  components: {
46
50
  MIcon,
@@ -51,13 +55,17 @@ export default {
51
55
  },
52
56
 
53
57
  props: {
58
+ items: {
59
+ type: Array,
60
+ required: true,
61
+ },
54
62
  open: {
55
63
  type: Boolean,
56
64
  default: false,
57
65
  },
58
- items: {
59
- type: Array,
60
- default: () => [],
66
+ isFiltered: {
67
+ type: Boolean,
68
+ default: false,
61
69
  },
62
70
  multiple: {
63
71
  type: Boolean,
@@ -67,38 +75,38 @@ export default {
67
75
  type: String,
68
76
  default: 'No item matching your criteria found',
69
77
  },
70
- icon: {
71
- type: Boolean,
72
- default: false,
73
- },
74
78
  dataKeyExpr: {
75
79
  type: String,
76
80
  default: 'id',
77
81
  },
78
82
  dataTextExpr: {
79
83
  type: String,
80
- default: 'text',
84
+ default: 'label',
81
85
  },
82
86
  dataValueExpr: {
83
87
  type: String,
84
- default: 'text',
88
+ default: 'value',
85
89
  },
86
90
  value: {
87
- type: [Array, String],
91
+ type: [Array, String, Number],
88
92
  default: undefined,
89
93
  },
90
94
  },
91
95
 
92
96
  data() {
93
97
  return {
94
- localItems: null,
95
- localValue: [],
96
- selected: [],
97
- uuid: null,
98
- inputBaseId: 'listboxInput',
98
+ uuid: Math.random(),
99
+ localItems: undefined,
100
+ localValue: undefined,
99
101
  };
100
102
  },
101
103
 
104
+ computed: {
105
+ setItemId() {
106
+ return (index) => `listboxItem-${index + this.uuid}`;
107
+ },
108
+ },
109
+
102
110
  watch: {
103
111
  items: {
104
112
  handler: function (val) {
@@ -107,44 +115,15 @@ export default {
107
115
  immediate: true,
108
116
  },
109
117
  value: {
110
- handler: function (value) {
111
- this.localValue = value;
112
- },
113
- immediate: true,
114
- },
115
- localValue: {
116
- handler: function (localValue) {
117
- const inputs = this.$refs.input;
118
- if (!this.multiple && inputs) {
119
- const selectedValue = Array.from(localValue);
120
-
121
- inputs.forEach(function (input) {
122
- const listItem = input.closest('.mc-listbox__item');
123
-
124
- if (input.value == selectedValue[0]) {
125
- listItem.classList.add('is-checked');
126
- } else {
127
- listItem.classList.remove('is-checked');
128
- }
129
- });
130
- }
118
+ handler: function (val) {
119
+ this.localValue = val;
131
120
  },
132
121
  immediate: true,
133
122
  },
134
123
  },
135
124
 
136
- mounted() {
137
- this.uuid = Math.random();
138
- },
139
-
140
125
  methods: {
141
126
  onChange() {
142
- if (!this.multiple) {
143
- const currentValue = this.localValue;
144
- this.localValue = [];
145
- this.localValue = currentValue.slice(-1);
146
- }
147
-
148
127
  this.$emit('change', this.localValue);
149
128
  },
150
129
  },
@@ -153,126 +132,10 @@ export default {
153
132
 
154
133
  <style lang="scss">
155
134
  @import 'settings-tools/all-settings';
135
+ @import 'components/c.checkbox';
136
+ @import 'components/c.listbox';
156
137
 
157
- .mc-listbox {
158
- $parent: get-parent-selector(&);
159
-
160
- @include unstyle-list();
161
-
162
- background-color: $color-grey-000;
163
- border: 1px solid $color-grey-600;
164
- border-radius: 3px;
165
- position: absolute;
166
- overflow-y: auto;
167
- margin-top: 5px;
168
- margin-bottom: 0;
169
- max-height: 13.8125rem; // =221px
170
- min-width: 17.875rem; // =286px
171
- opacity: 0;
172
- visibility: hidden;
173
- width: 100%;
174
-
175
- &.is-open {
176
- opacity: 1;
177
- visibility: visible;
178
- z-index: 11;
179
- }
180
-
181
- &::-webkit-scrollbar {
182
- background-color: $color-grey-100;
183
- width: $mu025;
184
-
185
- &-thumb {
186
- background: $color-grey-600;
187
- }
188
- }
189
-
190
- &__item {
191
- align-items: center;
192
- display: flex;
193
- gap: $mu050;
194
- min-height: $mu300;
195
- padding-left: $mu075;
196
- padding-right: $mu075;
197
- position: relative;
198
- justify-content: space-between;
199
-
200
- &:not(:last-child) {
201
- border-bottom: 1px solid $color-grey-300;
202
- }
203
-
204
- &:hover {
205
- background-color: $color-grey-100;
206
- box-shadow: inset 9px 0 0 -7px $color-grey-900;
207
- }
208
- }
209
-
210
- &__flag,
211
- &__icon {
212
- width: $mu200;
213
- height: $mu200;
214
- }
215
-
216
- &__flag {
217
- @include set-font-scale('07', 'm');
218
-
219
- text-align: center;
220
- }
221
-
222
- &__empty {
223
- @include set-font-scale('04', 'm');
224
-
225
- color: $color-fields-error;
226
- display: inline-block;
227
- margin-top: $mu025;
228
- }
229
-
230
- &__label {
231
- cursor: pointer;
232
- margin-right: auto;
233
-
234
- &::after {
235
- content: '';
236
- position: absolute;
237
- inset: 0;
238
- z-index: 2;
239
- }
240
- }
241
-
242
- .is-focus {
243
- background-color: $color-grey-100;
244
- box-shadow: inset 9px 0 0 -7px $color-grey-900;
245
- }
246
-
247
- .is-disabled {
248
- cursor: not-allowed;
249
- box-shadow: none;
250
- background-color: $color-grey-200;
251
- color: $color-grey-600;
252
- pointer-events: none;
253
- }
254
-
255
- &--left {
256
- right: 0;
257
- transform: translateX(-100%);
258
- }
259
-
260
- &:not(.mc-listbox--multi) {
261
- .is-checked {
262
- background-size: $mu150;
263
- background-image: url(inline-icons(
264
- 'notification-available-24',
265
- $color-grey-900
266
- ));
267
- background-repeat: no-repeat;
268
- background-position: right $mu075 center;
269
- }
270
-
271
- #{$parent} {
272
- &__input {
273
- @include visually-hidden();
274
- }
275
- }
276
- }
138
+ .mc-listbox__empty {
139
+ margin-top: 0;
277
140
  }
278
141
  </style>
@@ -67,7 +67,7 @@
67
67
  </ul>
68
68
  </div>
69
69
  <input
70
- id="smallField"
70
+ :id="id"
71
71
  type="tel"
72
72
  class="mc-phone-number__input mc-text-input mc-text-input--m mc-field__input"
73
73
  name="phone-number-input"
@@ -126,6 +126,10 @@ export default {
126
126
  type: [String, Number],
127
127
  default: '',
128
128
  },
129
+ id: {
130
+ type: String,
131
+ default: null,
132
+ },
129
133
  maxlength: {
130
134
  type: Number,
131
135
  default: 25,
@@ -21,7 +21,7 @@
21
21
  :style="`--steps: ${steps.length}; --current: ${idx + 1};`"
22
22
  @click="isStepValidated(idx) && $emit('step-changed', step)"
23
23
  >
24
- <a v-if="step.href" :href="step.href" class="mc-stepper__link">
24
+ <a v-if="step.href && isStepValidated(idx)" :href="step.href" class="mc-stepper__link">
25
25
  <div class="mc-stepper__indicator" aria-hidden="true">
26
26
  <m-icon
27
27
  v-if="isStepValidated(idx)"