@mozaic-ds/vue 0.24.0-beta.0 → 0.24.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mozaic-ds/vue",
3
- "version": "0.24.0-beta.0",
3
+ "version": "0.24.0",
4
4
  "description": "Vue.js implementation of Mozaic Design System",
5
5
  "author": "Adeo - Mozaic Design System",
6
6
  "scripts": {
@@ -25,7 +25,7 @@
25
25
  "dependencies": {
26
26
  "@mozaic-ds/css-dev-tools": "1.38.0",
27
27
  "@mozaic-ds/icons": "1.41.0",
28
- "@mozaic-ds/styles": "1.41.0",
28
+ "@mozaic-ds/styles": "1.42.0",
29
29
  "@mozaic-ds/web-fonts": "1.22.0",
30
30
  "core-js": "^3.25.0",
31
31
  "libphonenumber-js": "^1.10.13",
@@ -3,47 +3,56 @@
3
3
  v-click-outside="onClickOutside"
4
4
  class="mc-autocomplete"
5
5
  :class="{ 'mc-autocomplete--multi': multiple }"
6
- :style="tagStyle"
6
+ :style="setStyles"
7
7
  >
8
- <m-tag
9
- v-if="multiple && tagValue.length > 0"
8
+ <MTag
9
+ v-if="multiple && listboxValue.length > 0"
10
+ :id="tagId ? tagId : `autocompleteTag-${uuid}`"
11
+ ref="tag"
12
+ :label="setTagLabel"
13
+ :disabled="disabled"
10
14
  type="removable"
11
- :label="tagValue.length.toString() + ' ' + tagLabel"
12
15
  class="mc-autocomplete__tag"
13
16
  size="s"
14
17
  @remove-tag="clearAutocomplete()"
15
18
  />
16
- <m-text-input
19
+ <MLoader v-if="loading" class="mc-autocomplete__loader" size="s" />
20
+ <MTextInput
21
+ :id="id"
17
22
  v-model="inputValue"
18
23
  :placeholder="placeholder"
19
- :is-invalid="itemListForDropdown.length === 0"
24
+ :is-invalid="invalid"
25
+ :disabled="disabled"
20
26
  text-input-field-class="mc-autocomplete__trigger"
21
27
  icon-position="left"
22
28
  icon="DisplaySearch48"
23
29
  autocomplete="off"
24
- @input="filterList"
25
- @click="isOpen = true"
30
+ type="search"
31
+ @input="onInput"
32
+ @click="openState = true"
26
33
  />
27
- <m-list-box
34
+ <MListBox
28
35
  v-model="listboxValue"
29
- :open="isOpen"
30
- :items="itemListForDropdown"
36
+ :open="openState"
37
+ :items="localItems"
31
38
  :multiple="multiple"
32
39
  :empty-search-label="emptySearchLabel"
33
40
  :data-key-expr="dataKeyExpr"
34
41
  :data-text-expr="dataTextExpr"
35
42
  :data-value-expr="dataValueExpr"
43
+ :is-filtered="isFiltered"
36
44
  @change="onChange"
37
45
  >
38
46
  <template #item="{ item }">
39
- <slot name="item" :item="item" />
47
+ <slot name="item" :item="item"></slot>
40
48
  </template>
41
- </m-list-box>
49
+ </MListBox>
42
50
  </div>
43
51
  </template>
44
52
 
45
53
  <script>
46
54
  import MTag from '../tags/MTag.vue';
55
+ import MLoader from '../loader/MLoader.vue';
47
56
  import MTextInput from '../textinput/MTextInput.vue';
48
57
  import MListBox from '../listbox/MListBox.vue';
49
58
 
@@ -51,9 +60,10 @@ export default {
51
60
  name: 'MAutocomplete',
52
61
 
53
62
  components: {
54
- MListBox,
55
63
  MTag,
64
+ MLoader,
56
65
  MTextInput,
66
+ MListBox,
57
67
  },
58
68
 
59
69
  directives: {
@@ -77,34 +87,68 @@ export default {
77
87
  },
78
88
 
79
89
  props: {
80
- open: {
81
- type: Boolean,
82
- default: false,
90
+ // Tag Element
91
+ tagId: {
92
+ type: String,
93
+ default: null,
83
94
  },
84
95
  tagLabel: {
85
96
  type: String,
86
97
  default: '',
87
98
  },
99
+ // Input Element
100
+ id: {
101
+ type: String,
102
+ default: null,
103
+ },
104
+ input: {
105
+ type: String,
106
+ default: null,
107
+ },
88
108
  placeholder: {
89
109
  type: String,
90
- default: '',
110
+ default: null,
111
+ },
112
+ filter: {
113
+ type: Function,
114
+ default: null,
115
+ },
116
+ filterOnType: {
117
+ type: Boolean,
118
+ default: true,
91
119
  },
120
+ disabled: {
121
+ type: Boolean,
122
+ default: false,
123
+ },
124
+ invalid: {
125
+ type: Boolean,
126
+ default: false,
127
+ },
128
+ loading: {
129
+ type: Boolean,
130
+ default: false,
131
+ },
132
+ // Listbox Element
92
133
  items: {
93
134
  type: Array,
94
- default: () => [],
95
135
  required: true,
96
136
  },
97
- multiple: {
137
+ value: {
138
+ type: [Array, String, Number],
139
+ default: undefined,
140
+ },
141
+ open: {
98
142
  type: Boolean,
99
143
  default: false,
100
144
  },
101
- filter: {
102
- type: Function,
103
- default: null,
145
+ multiple: {
146
+ type: Boolean,
147
+ default: false,
104
148
  },
105
149
  emptySearchLabel: {
106
150
  type: String,
107
- default: 'No item matching your criteria found',
151
+ default: 'No results found',
108
152
  },
109
153
  dataKeyExpr: {
110
154
  type: String,
@@ -118,27 +162,36 @@ export default {
118
162
  type: String,
119
163
  default: 'value',
120
164
  },
121
- value: {
122
- type: Array,
123
- default: () => [],
165
+ sort: {
166
+ type: Boolean,
167
+ default: true,
168
+ },
169
+ // Global
170
+ maxWidth: {
171
+ type: String,
172
+ default: '17.875rem',
124
173
  },
125
174
  },
126
175
 
127
176
  data() {
128
177
  return {
129
- itemListForDropdown: this.items,
130
- isOpen: this.open,
178
+ uuid: Math.random(),
179
+ openState: this.open,
131
180
  tagWidth: '0px',
132
- maxWidth: '17.875rem',
133
- uuid: undefined,
134
- tagValue: '',
135
- inputValue: '',
136
- listboxValue: [],
181
+ tagValue: null,
182
+ inputValue: null,
183
+ localItems: null,
184
+ sortedListItems: null,
185
+ listboxValue: null,
186
+ isFiltered: null,
137
187
  };
138
188
  },
139
189
 
140
190
  computed: {
141
- tagStyle() {
191
+ setTagLabel() {
192
+ return this.listboxValue.length.toString() + ' ' + this.tagLabel;
193
+ },
194
+ setStyles() {
142
195
  return {
143
196
  '--tag-width': this.tagWidth,
144
197
  '--max-width': this.maxWidth,
@@ -147,56 +200,71 @@ export default {
147
200
  },
148
201
 
149
202
  watch: {
150
- listboxValue: function (newValue) {
151
- const textToDisplay = [];
152
- const valueExpr = this.dataValueExpr;
153
- const textExpr = this.dataTextExpr;
203
+ value: {
204
+ handler: function (val) {
205
+ if (!val && this.multiple) {
206
+ this.listboxValue = [];
207
+ } else {
208
+ this.listboxValue = val;
209
+ }
210
+ },
211
+ immediate: true,
212
+ },
154
213
 
155
- this.tagValue = newValue;
214
+ items: {
215
+ handler: function (val) {
216
+ this.localItems = val;
217
+ // this.clearAutocomplete();
218
+ },
219
+ immediate: true,
220
+ },
156
221
 
157
- if (newValue.length) {
158
- const selectedItems = this.items.filter((item) =>
159
- newValue.includes(item[valueExpr])
160
- );
222
+ listboxValue: function (val) {
223
+ const value = Array.isArray(val) ? val : val.toString();
224
+ const selectedItems = this.getSelectedItems(value);
225
+
226
+ const seletedLabels = selectedItems.map(
227
+ (item) => item[this.dataTextExpr]
228
+ );
161
229
 
162
- selectedItems.forEach((item) => textToDisplay.push(item[textExpr]));
230
+ this.inputValue = seletedLabels.join(', ');
163
231
 
164
- this.inputValue = textToDisplay.join(', ');
232
+ if (this.multiple) {
233
+ this.tagValue = val;
165
234
  }
166
235
  },
167
236
 
168
- tagValue: {
169
- handler: function () {
170
- this.setTagWidth();
171
- },
172
- immediate: true,
237
+ tagValue: function () {
238
+ this.setTagWidth();
173
239
  },
174
240
 
175
- value: {
176
- handler: function (value) {
177
- this.listboxValue = value;
241
+ openState: function (val) {
242
+ const eventName = val ? 'open' : 'close';
243
+ this.$emit(eventName);
244
+ this.$emit('update:open', val);
245
+ },
246
+
247
+ input: {
248
+ handler: function (val) {
249
+ this.inputValue = val;
178
250
  },
179
251
  immediate: true,
180
252
  },
181
253
  },
182
254
 
183
- mounted() {
184
- this.uuid = this._uid;
185
- },
186
-
187
255
  methods: {
188
256
  setTagWidth() {
189
257
  this.$nextTick(() => {
190
- this.tagWidth =
191
- document && document.querySelector('.mc-autocomplete__tag')
192
- ? document.querySelector('.mc-autocomplete__tag').clientWidth + 'px'
193
- : '0px';
258
+ if (this.$refs.tag && this.$refs.tag.$el) {
259
+ this.tagWidth = this.$refs.tag.$el.clientWidth + 'px';
260
+ } else {
261
+ this.tagWidth = '0px';
262
+ }
194
263
  });
195
264
  },
196
265
 
197
266
  clearAutocomplete() {
198
- this.listboxValue = [];
199
- this.inputValue = '';
267
+ this.listboxValue = this.multiple ? [] : undefined;
200
268
  this.onChange();
201
269
  this.$emit('clear');
202
270
  },
@@ -204,18 +272,27 @@ export default {
204
272
  filterList(value) {
205
273
  if (value.length && this.filter) {
206
274
  this.filter(value);
207
- } else if (value.length) {
208
- this.itemListForDropdown = this.itemListForDropdown.filter((item) =>
275
+ } else if (value.length && this.filterOnType) {
276
+ this.localItems = this.items.filter((item) =>
209
277
  item[this.dataTextExpr].toUpperCase().includes(value.toUpperCase())
210
278
  );
279
+
280
+ this.isFiltered = !this.localItems.length;
211
281
  } else {
212
- this.itemListForDropdown = this.items;
282
+ this.localItems = this.items;
283
+ this.isFiltered = !this.localItems.length;
213
284
  }
214
- this.$emit('list-filtered', this.itemListForDropdown);
285
+ this.$emit('list-filtered', this.localItems);
215
286
  },
216
287
 
217
288
  onClickOutside() {
218
- this.isOpen = false;
289
+ this.openState = false;
290
+
291
+ if (this.multiple && this.sort) {
292
+ this.sortItems();
293
+ } else {
294
+ this.localItems = this.items;
295
+ }
219
296
  },
220
297
 
221
298
  onChange() {
@@ -225,13 +302,51 @@ export default {
225
302
  this.onClickOutside();
226
303
  }
227
304
  },
305
+
306
+ getSelectedItems(val) {
307
+ const value = val ? val : this.listboxValue;
308
+
309
+ const selectedItems = this.items.filter((item) =>
310
+ value.includes(item[this.dataValueExpr])
311
+ );
312
+
313
+ return selectedItems;
314
+ },
315
+
316
+ sortItems() {
317
+ this.sortedListItems = this.items;
318
+ const selectedItems = this.getSelectedItems();
319
+
320
+ this.sortedListItems.sort((a, b) => {
321
+ const hasItemA = selectedItems.includes(a);
322
+ const hasItemB = selectedItems.includes(b);
323
+
324
+ if (hasItemA === hasItemB) {
325
+ return a[this.dataValueExpr] - b[this.dataValueExpr];
326
+ } else if (hasItemA < hasItemB) {
327
+ return 1;
328
+ } else {
329
+ return -1;
330
+ }
331
+ });
332
+
333
+ this.localItems = this.sortedListItems;
334
+ },
335
+
336
+ handleInputValue(value) {
337
+ this.$emit('update:input', value);
338
+ },
339
+
340
+ onInput(value) {
341
+ this.handleInputValue(value);
342
+ this.filterList(value);
343
+ },
228
344
  },
229
345
  };
230
346
  </script>
231
347
 
232
348
  <style lang="scss">
233
349
  @import 'settings-tools/all-settings';
234
- @import 'components/c.checkbox';
235
350
  @import 'components/c.autocomplete';
236
351
 
237
352
  .mc-autocomplete {
@@ -240,10 +355,15 @@ export default {
240
355
  &__tag {
241
356
  position: absolute;
242
357
  top: 0;
243
- -webkit-transform: translateY(50%);
244
- -ms-transform: translateY(50%);
245
358
  transform: translateY(50%);
246
359
  }
360
+
361
+ &__loader {
362
+ position: absolute;
363
+ right: $mu075;
364
+ top: $mu075;
365
+ z-index: 10;
366
+ }
247
367
  }
248
368
 
249
369
  .mc-autocomplete--multi .mc-autocomplete__trigger {