@mozaic-ds/vue 0.23.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.23.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,31 +25,31 @@
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
- "core-js": "^3.18.3",
31
- "libphonenumber-js": "1.9.50",
32
- "postcss-scss": "^4.0.1",
30
+ "core-js": "^3.25.0",
31
+ "libphonenumber-js": "^1.10.13",
32
+ "postcss-scss": "^4.0.4",
33
33
  "vue": "^2.6.14",
34
34
  "vue-country-flag": "2.3.2"
35
35
  },
36
36
  "devDependencies": {
37
- "@babel/core": "^7.15.8",
38
- "@babel/eslint-parser": "^7.15.8",
37
+ "@babel/core": "^7.18.13",
38
+ "@babel/eslint-parser": "^7.18.9",
39
39
  "@vue/cli-plugin-babel": "~5.0.4",
40
40
  "@vue/cli-service": "~5.0.4",
41
41
  "@vue/compiler-sfc": "^3.2.20",
42
42
  "@vue/eslint-config-prettier": "^6.0.0",
43
43
  "babel-eslint": "^10.1.0",
44
- "eslint": "^7.30.0",
45
- "eslint-config-prettier": "^8.3.0",
46
- "eslint-plugin-vue": "^7.19.1",
47
- "postcss": "^8.3.9",
44
+ "eslint": "^8.23.0",
45
+ "eslint-config-prettier": "^8.5.0",
46
+ "eslint-plugin-vue": "^9.4.0",
47
+ "postcss": "^8.4.16",
48
48
  "postcss-loader": "^4.3.0",
49
- "prettier": "^2.4.1",
50
- "sass": "^1.42.1",
49
+ "prettier": "^2.7.1",
50
+ "sass": "^1.54.7",
51
51
  "sass-loader": "^10.2.0",
52
- "vue-template-compiler": "^2.6.14"
52
+ "vue-template-compiler": "^2.7.10"
53
53
  },
54
54
  "bugs": {
55
55
  "url": "https://github.com/adeo/mozaic-vue/issues"
@@ -1,60 +1,69 @@
1
1
  <template>
2
2
  <div
3
- ref="autocomplete"
4
3
  v-click-outside="onClickOutside"
5
4
  class="mc-autocomplete"
6
5
  :class="{ 'mc-autocomplete--multi': multiple }"
7
- :style="tagStyle"
6
+ :style="setStyles"
8
7
  >
9
- <m-tag
10
- 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"
11
14
  type="removable"
12
- :label="tagValue.length.toString() + ' ' + tagLabel"
13
15
  class="mc-autocomplete__tag"
14
16
  size="s"
15
17
  @remove-tag="clearAutocomplete()"
16
18
  />
17
- <m-text-input
19
+ <MLoader v-if="loading" class="mc-autocomplete__loader" size="s" />
20
+ <MTextInput
21
+ :id="id"
18
22
  v-model="inputValue"
19
23
  :placeholder="placeholder"
20
- :is-invalid="itemListForDropdown.length === 0"
24
+ :is-invalid="invalid"
25
+ :disabled="disabled"
21
26
  text-input-field-class="mc-autocomplete__trigger"
22
27
  icon-position="left"
23
28
  icon="DisplaySearch48"
24
29
  autocomplete="off"
25
- @input="filterList"
26
- @click="isOpen = true"
30
+ type="search"
31
+ @input="onInput"
32
+ @click="openState = true"
27
33
  />
28
- <m-list-box
34
+ <MListBox
29
35
  v-model="listboxValue"
30
- :open="isOpen"
31
- :items="itemListForDropdown"
36
+ :open="openState"
37
+ :items="localItems"
32
38
  :multiple="multiple"
33
39
  :empty-search-label="emptySearchLabel"
34
40
  :data-key-expr="dataKeyExpr"
35
41
  :data-text-expr="dataTextExpr"
36
42
  :data-value-expr="dataValueExpr"
43
+ :is-filtered="isFiltered"
37
44
  @change="onChange"
38
45
  >
39
46
  <template #item="{ item }">
40
- <slot name="item" :item="item" />
47
+ <slot name="item" :item="item"></slot>
41
48
  </template>
42
- </m-list-box>
49
+ </MListBox>
43
50
  </div>
44
51
  </template>
45
52
 
46
53
  <script>
47
- import MListBox from '../listbox/MListBox.vue';
48
54
  import MTag from '../tags/MTag.vue';
55
+ import MLoader from '../loader/MLoader.vue';
49
56
  import MTextInput from '../textinput/MTextInput.vue';
57
+ import MListBox from '../listbox/MListBox.vue';
50
58
 
51
59
  export default {
52
60
  name: 'MAutocomplete',
53
61
 
54
62
  components: {
55
- MListBox,
56
63
  MTag,
64
+ MLoader,
57
65
  MTextInput,
66
+ MListBox,
58
67
  },
59
68
 
60
69
  directives: {
@@ -78,34 +87,68 @@ export default {
78
87
  },
79
88
 
80
89
  props: {
81
- open: {
82
- type: Boolean,
83
- default: false,
90
+ // Tag Element
91
+ tagId: {
92
+ type: String,
93
+ default: null,
84
94
  },
85
95
  tagLabel: {
86
96
  type: String,
87
97
  default: '',
88
98
  },
99
+ // Input Element
100
+ id: {
101
+ type: String,
102
+ default: null,
103
+ },
104
+ input: {
105
+ type: String,
106
+ default: null,
107
+ },
89
108
  placeholder: {
90
109
  type: String,
91
- default: '',
110
+ default: null,
92
111
  },
112
+ filter: {
113
+ type: Function,
114
+ default: null,
115
+ },
116
+ filterOnType: {
117
+ type: Boolean,
118
+ default: true,
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
93
133
  items: {
94
134
  type: Array,
95
- default: () => [],
96
135
  required: true,
97
136
  },
98
- multiple: {
137
+ value: {
138
+ type: [Array, String, Number],
139
+ default: undefined,
140
+ },
141
+ open: {
99
142
  type: Boolean,
100
143
  default: false,
101
144
  },
102
- filter: {
103
- type: Function,
104
- default: null,
145
+ multiple: {
146
+ type: Boolean,
147
+ default: false,
105
148
  },
106
149
  emptySearchLabel: {
107
150
  type: String,
108
- default: 'No item matching your criteria found',
151
+ default: 'No results found',
109
152
  },
110
153
  dataKeyExpr: {
111
154
  type: String,
@@ -119,27 +162,36 @@ export default {
119
162
  type: String,
120
163
  default: 'value',
121
164
  },
122
- value: {
123
- type: Array,
124
- default: () => [],
165
+ sort: {
166
+ type: Boolean,
167
+ default: true,
168
+ },
169
+ // Global
170
+ maxWidth: {
171
+ type: String,
172
+ default: '17.875rem',
125
173
  },
126
174
  },
127
175
 
128
176
  data() {
129
177
  return {
130
- itemListForDropdown: this.items,
131
- isOpen: this.open,
178
+ uuid: Math.random(),
179
+ openState: this.open,
132
180
  tagWidth: '0px',
133
- maxWidth: '17.875rem',
134
- uuid: undefined,
135
- tagValue: '',
136
- inputValue: '',
137
- listboxValue: [],
181
+ tagValue: null,
182
+ inputValue: null,
183
+ localItems: null,
184
+ sortedListItems: null,
185
+ listboxValue: null,
186
+ isFiltered: null,
138
187
  };
139
188
  },
140
189
 
141
190
  computed: {
142
- tagStyle() {
191
+ setTagLabel() {
192
+ return this.listboxValue.length.toString() + ' ' + this.tagLabel;
193
+ },
194
+ setStyles() {
143
195
  return {
144
196
  '--tag-width': this.tagWidth,
145
197
  '--max-width': this.maxWidth,
@@ -148,56 +200,71 @@ export default {
148
200
  },
149
201
 
150
202
  watch: {
151
- listboxValue: function (newValue) {
152
- const textToDisplay = [];
153
- const valueExpr = this.dataValueExpr;
154
- 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
+ },
213
+
214
+ items: {
215
+ handler: function (val) {
216
+ this.localItems = val;
217
+ // this.clearAutocomplete();
218
+ },
219
+ immediate: true,
220
+ },
155
221
 
156
- this.tagValue = newValue;
222
+ listboxValue: function (val) {
223
+ const value = Array.isArray(val) ? val : val.toString();
224
+ const selectedItems = this.getSelectedItems(value);
157
225
 
158
- if (newValue.length) {
159
- const selectedItems = this.items.filter((item) =>
160
- newValue.includes(item[valueExpr])
161
- );
226
+ const seletedLabels = selectedItems.map(
227
+ (item) => item[this.dataTextExpr]
228
+ );
162
229
 
163
- selectedItems.forEach((item) => textToDisplay.push(item[textExpr]));
230
+ this.inputValue = seletedLabels.join(', ');
164
231
 
165
- this.inputValue = textToDisplay.join(', ');
232
+ if (this.multiple) {
233
+ this.tagValue = val;
166
234
  }
167
235
  },
168
236
 
169
- tagValue: {
170
- handler: function () {
171
- this.setTagWidth();
172
- },
173
- immediate: true,
237
+ tagValue: function () {
238
+ this.setTagWidth();
174
239
  },
175
240
 
176
- value: {
177
- handler: function (value) {
178
- 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;
179
250
  },
180
251
  immediate: true,
181
252
  },
182
253
  },
183
254
 
184
- mounted() {
185
- this.uuid = this._uid;
186
- },
187
-
188
255
  methods: {
189
256
  setTagWidth() {
190
257
  this.$nextTick(() => {
191
- this.tagWidth =
192
- document && document.querySelector('.mc-autocomplete__tag')
193
- ? document.querySelector('.mc-autocomplete__tag').clientWidth + 'px'
194
- : '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
+ }
195
263
  });
196
264
  },
197
265
 
198
266
  clearAutocomplete() {
199
- this.listboxValue = [];
200
- this.inputValue = '';
267
+ this.listboxValue = this.multiple ? [] : undefined;
201
268
  this.onChange();
202
269
  this.$emit('clear');
203
270
  },
@@ -205,18 +272,27 @@ export default {
205
272
  filterList(value) {
206
273
  if (value.length && this.filter) {
207
274
  this.filter(value);
208
- } else if (value.length) {
209
- this.itemListForDropdown = this.itemListForDropdown.filter((item) =>
275
+ } else if (value.length && this.filterOnType) {
276
+ this.localItems = this.items.filter((item) =>
210
277
  item[this.dataTextExpr].toUpperCase().includes(value.toUpperCase())
211
278
  );
279
+
280
+ this.isFiltered = !this.localItems.length;
212
281
  } else {
213
- this.itemListForDropdown = this.items;
282
+ this.localItems = this.items;
283
+ this.isFiltered = !this.localItems.length;
214
284
  }
215
- this.$emit('list-filtered', this.itemListForDropdown);
285
+ this.$emit('list-filtered', this.localItems);
216
286
  },
217
287
 
218
288
  onClickOutside() {
219
- 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
+ }
220
296
  },
221
297
 
222
298
  onChange() {
@@ -226,13 +302,51 @@ export default {
226
302
  this.onClickOutside();
227
303
  }
228
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
+ },
229
344
  },
230
345
  };
231
346
  </script>
232
347
 
233
348
  <style lang="scss">
234
349
  @import 'settings-tools/all-settings';
235
- @import 'components/c.checkbox';
236
350
  @import 'components/c.autocomplete';
237
351
 
238
352
  .mc-autocomplete {
@@ -241,10 +355,15 @@ export default {
241
355
  &__tag {
242
356
  position: absolute;
243
357
  top: 0;
244
- -webkit-transform: translateY(50%);
245
- -ms-transform: translateY(50%);
246
358
  transform: translateY(50%);
247
359
  }
360
+
361
+ &__loader {
362
+ position: absolute;
363
+ right: $mu075;
364
+ top: $mu075;
365
+ z-index: 10;
366
+ }
248
367
  }
249
368
 
250
369
  .mc-autocomplete--multi .mc-autocomplete__trigger {