@mozaic-ds/vue 0.34.2-beta.0 → 0.35.0-beta.1

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.34.2-beta.0",
3
+ "version": "0.35.0-beta.1",
4
4
  "description": "Vue.js implementation of Mozaic Design System",
5
5
  "author": "Adeo - Mozaic Design System",
6
6
  "scripts": {
@@ -25,15 +25,15 @@
25
25
  "dependencies": {
26
26
  "@mozaic-ds/css-dev-tools": "1.50.0",
27
27
  "@mozaic-ds/icons": "1.49.0",
28
- "@mozaic-ds/styles": "1.50.0",
28
+ "@mozaic-ds/styles": "1.51.0",
29
29
  "@mozaic-ds/web-fonts": "1.22.0",
30
- "core-js": "^3.26.1",
30
+ "core-js": "^3.27.0",
31
31
  "libphonenumber-js": "^1.10.15",
32
32
  "vue": "^2.6.14",
33
33
  "vue-country-flag": "2.3.2"
34
34
  },
35
35
  "devDependencies": {
36
- "@babel/core": "^7.20.5",
36
+ "@babel/core": "^7.20.7",
37
37
  "@babel/eslint-parser": "^7.19.1",
38
38
  "@rushstack/eslint-patch": "^1.2.0",
39
39
  "@vue/cli-plugin-babel": "~5.0.8",
@@ -41,14 +41,14 @@
41
41
  "@vue/compiler-sfc": "^3.2.45",
42
42
  "@vue/eslint-config-prettier": "^7.0.0",
43
43
  "babel-eslint": "^10.1.0",
44
- "eslint": "^8.29.0",
44
+ "eslint": "^8.30.0",
45
45
  "eslint-config-prettier": "^8.5.0",
46
46
  "eslint-plugin-vue": "^9.8.0",
47
- "postcss": "^8.4.19",
47
+ "postcss": "^8.4.20",
48
48
  "postcss-loader": "^7.0.2",
49
49
  "postcss-scss": "^4.0.6",
50
50
  "prettier": "^2.8.1",
51
- "sass": "^1.56.2",
51
+ "sass": "^1.57.1",
52
52
  "sass-loader": "^13.2.0",
53
53
  "vue-template-compiler": "^2.7.14"
54
54
  },
@@ -2,7 +2,7 @@
2
2
  <div
3
3
  v-click-outside="closeListBox"
4
4
  class="mc-autocomplete"
5
- :class="{ 'mc-autocomplete--multi': multiple }"
5
+ :class="classObject"
6
6
  :style="setStyles"
7
7
  >
8
8
  <div class="mc-autocomplete__main">
@@ -17,14 +17,14 @@
17
17
  size="s"
18
18
  @remove-tag="clearListbox()"
19
19
  />
20
- <MLoader v-if="loading" class="mc-autocomplete__loader" size="s" />
21
20
  <MTextInput
22
21
  :id="id"
23
22
  v-model="inputValue"
24
23
  :placeholder="placeholder"
25
- :is-invalid="invalid"
24
+ :is-invalid="isInvalid"
26
25
  :disabled="disabled"
27
26
  :size="size"
27
+ :root-class="getInvalidRootClass"
28
28
  text-input-field-class="mc-autocomplete__trigger"
29
29
  icon-position="left"
30
30
  icon="DisplaySearch48"
@@ -32,6 +32,7 @@
32
32
  @input="onInput"
33
33
  @click="openState = true"
34
34
  />
35
+ <MLoader v-if="loading" class="mc-autocomplete__loader" size="s" />
35
36
  <button
36
37
  v-if="hasValues"
37
38
  type="button"
@@ -43,6 +44,7 @@
43
44
  </button>
44
45
  </div>
45
46
  <MListBox
47
+ v-if="!isFiltered"
46
48
  v-model="listboxValue"
47
49
  :open="openState"
48
50
  :items="localItems"
@@ -51,7 +53,6 @@
51
53
  :data-key-expr="dataKeyExpr"
52
54
  :data-text-expr="dataTextExpr"
53
55
  :data-value-expr="dataValueExpr"
54
- :is-filtered="isFiltered"
55
56
  :max-width="maxWidth"
56
57
  @change="onChangeListbox"
57
58
  >
@@ -59,6 +60,9 @@
59
60
  <slot name="item" :item="item"></slot>
60
61
  </template>
61
62
  </MListBox>
63
+ <div v-else class="mc-autocomplete__error">
64
+ {{ emptySearchLabel }}
65
+ </div>
62
66
  </div>
63
67
  </template>
64
68
 
@@ -211,6 +215,12 @@ export default {
211
215
  },
212
216
 
213
217
  computed: {
218
+ classObject() {
219
+ return {
220
+ 'mc-autocomplete--multi': this.multiple,
221
+ 'mc-autocomplete--clearable': this.hasValues,
222
+ };
223
+ },
214
224
  setStyles() {
215
225
  return {
216
226
  '--autocomplete-tag-width': this.tagWidth,
@@ -223,6 +233,13 @@ export default {
223
233
  hasValues() {
224
234
  return this.inputValue?.length > 0;
225
235
  },
236
+ isInvalid() {
237
+ return this.invalid || this.isFiltered;
238
+ },
239
+ getInvalidRootClass() {
240
+ const invalidClass = this.isInvalid ? 'is-invalid' : '';
241
+ return invalidClass;
242
+ },
226
243
  },
227
244
 
228
245
  watch: {
@@ -232,6 +249,13 @@ export default {
232
249
  },
233
250
  immediate: true,
234
251
  },
252
+ localItems: {
253
+ handler: function (val) {
254
+ // this.localItems = val;
255
+ this.isFiltered = !val.length;
256
+ this.$emit('list-filtered', val);
257
+ },
258
+ },
235
259
  input: {
236
260
  handler: function (val) {
237
261
  this.inputValue = val;
@@ -248,18 +272,20 @@ export default {
248
272
  },
249
273
  immediate: true,
250
274
  },
251
- listboxValue: function (val) {
252
- if (!this.multiple) {
253
- const selectedItems = this.getSelectedItems(val);
254
- const seletedLabels = selectedItems.map(
255
- (item) => item[this.dataTextExpr]
256
- );
275
+ listboxValue: {
276
+ handler: function (val) {
277
+ if (!this.multiple) {
278
+ const selectedItems = this.getSelectedItems(val);
279
+ const seletedLabels = selectedItems.map(
280
+ (item) => item[this.dataTextExpr]
281
+ );
257
282
 
258
- this.inputValue = seletedLabels.join(', ');
259
- this.$emit('update:input', this.inputValue);
260
- } else {
261
- this.tagValue = val;
262
- }
283
+ this.inputValue = seletedLabels.join(', ');
284
+ } else {
285
+ this.tagValue = val;
286
+ }
287
+ },
288
+ immediate: true,
263
289
  },
264
290
  tagValue: function () {
265
291
  this.setTagWidth();
@@ -300,18 +326,24 @@ export default {
300
326
  filterList(value) {
301
327
  if (value.length && this.filter) {
302
328
  this.filter(value);
303
- } else if (value.length && this.filterOnType) {
304
- this.localItems = this.items.filter((item) =>
305
- item[this.dataTextExpr].toUpperCase().includes(value.toUpperCase())
306
- );
307
-
308
- this.isFiltered = !this.localItems.length;
309
329
  } else {
310
- this.localItems = this.items;
311
- this.isFiltered = !this.localItems.length;
312
- }
330
+ if (!this.filterOnType) {
331
+ return;
332
+ }
333
+
334
+ if (value.length) {
335
+ this.localItems = this.items.filter((item) =>
336
+ item[this.dataTextExpr].toUpperCase().includes(value.toUpperCase())
337
+ );
338
+
339
+ // this.isFiltered = !this.localItems.length;
340
+ } else {
341
+ this.localItems = this.items;
342
+ // this.isFiltered = !this.localItems.length;
343
+ }
313
344
 
314
- this.$emit('list-filtered', this.localItems);
345
+ // this.$emit('list-filtered', this.localItems);
346
+ }
315
347
  },
316
348
  closeListBox() {
317
349
  this.openState = false;
@@ -375,6 +407,18 @@ export default {
375
407
  @import 'components/c.autocomplete';
376
408
 
377
409
  .mc-autocomplete {
410
+ &__main {
411
+ position: relative;
412
+ }
413
+
414
+ &__error {
415
+ @include set-font-scale('04', 'm');
416
+
417
+ color: $color-fields-error;
418
+ display: inline-block;
419
+ margin-top: $mu025;
420
+ }
421
+
378
422
  .mc-autocomplete__trigger {
379
423
  padding-right: $mu300;
380
424
  }
@@ -6,37 +6,32 @@
6
6
  :class="{ 'is-open': open, 'mc-listbox--multi': multiple }"
7
7
  :style="{ '--listbox-width': maxWidth }"
8
8
  >
9
- <template v-if="!isFiltered">
10
- <li
11
- v-for="(item, index) in localItems"
12
- :key="item.id"
13
- class="mc-listbox__item"
14
- >
15
- <MIcon
16
- v-if="item.icon"
17
- :name="item.icon"
18
- class="mc-listbox__icon"
19
- color="#666666"
20
- />
21
- <input
22
- :id="setItemId(index)"
23
- v-model="localValue"
24
- class="mc-listbox__input"
25
- :class="{ 'mc-checkbox__input': multiple }"
26
- :type="multiple ? 'checkbox' : 'radio'"
27
- :value="item[dataValueExpr]"
28
- :name="!multiple ? `listboxradio-${uuid}` : null"
29
- @change="onChange"
30
- />
31
- <label :for="setItemId(index)" class="mc-listbox__label">
32
- <slot name="item" :item="item">
33
- {{ item[dataTextExpr] }}
34
- </slot>
35
- </label>
36
- </li>
37
- </template>
38
- <li v-else class="mc-listbox__item">
39
- <span class="mc-listbox__empty">{{ emptySearchLabel }}</span>
9
+ <li
10
+ v-for="(item, index) in localItems"
11
+ :key="item.id"
12
+ class="mc-listbox__item"
13
+ >
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>
40
35
  </li>
41
36
  </ul>
42
37
  </template>
@@ -64,18 +59,10 @@ export default {
64
59
  type: Boolean,
65
60
  default: false,
66
61
  },
67
- isFiltered: {
68
- type: Boolean,
69
- default: false,
70
- },
71
62
  multiple: {
72
63
  type: Boolean,
73
64
  default: false,
74
65
  },
75
- emptySearchLabel: {
76
- type: String,
77
- default: 'No item matching your criteria found',
78
- },
79
66
  dataKeyExpr: {
80
67
  type: String,
81
68
  default: 'id',
@@ -9,7 +9,7 @@
9
9
  ref="trigger"
10
10
  type="button"
11
11
  class="mc-listbox-options__trigger"
12
- @click="onClickTrigger"
12
+ @click="onSwitch($event)"
13
13
  >
14
14
  <m-icon name="DisplayOptions24" :color="triggerIconColor" />
15
15
  <span class="mc-listbox-options__trigger-label">{{ triggerLabel }}</span>
@@ -17,7 +17,11 @@
17
17
  <div
18
18
  ref="listbox"
19
19
  class="mc-listbox-options__container"
20
- :class="{ 'is-open': isOpen, 'align-right': position == 'right' }"
20
+ :class="{
21
+ 'is-open': isOpen,
22
+ 'align-right': position == 'right',
23
+ 'align-top': displayTop,
24
+ }"
21
25
  role="listbox"
22
26
  aria-labelledby="listbox"
23
27
  >
@@ -107,6 +111,7 @@ export default {
107
111
  data() {
108
112
  return {
109
113
  isOpen: this.open,
114
+ displayTop: false,
110
115
  posTop: '0px',
111
116
  hasDataTable: false,
112
117
  };
@@ -154,21 +159,26 @@ export default {
154
159
  onClickOutside() {
155
160
  this.isOpen = false;
156
161
  },
162
+ onSwitch(e) {
163
+ if (!this.isOpen) {
164
+ if (this.$refs.listbox.clientHeight + e.clientY >= window.innerHeight) {
165
+ this.displayTop = true;
166
+ }
167
+
168
+ if (this.hasDataTable) {
169
+ const buttonSizes = this.$refs.trigger.getBoundingClientRect();
170
+ this.posTop = buttonSizes.top + buttonSizes.height;
171
+ }
172
+ }
173
+ this.isOpen = !this.isOpen;
174
+ },
157
175
  onClickItem(item, listIndex, itemIndex) {
158
176
  const valToEmit = Object.assign(
159
177
  { listIndex: listIndex, itemIndex: itemIndex },
160
178
  item
161
179
  );
162
- this.$emit('update:itemSelected', valToEmit);
163
- },
164
- onClickTrigger() {
165
- this.isOpen = !this.isOpen;
166
-
167
- if (this.isOpen && this.hasDataTable) {
168
- const buttonHeight = this.$refs.trigger.getBoundingClientRect().height;
169
- this.posTop =
170
- this.$refs.trigger.getBoundingClientRect().top + buttonHeight;
171
- }
180
+ this.$emit('update:itemSelected', valToEmit); // TODO: deprecated
181
+ this.$emit('item-clicked', valToEmit);
172
182
  },
173
183
  handleScroll() {
174
184
  this.isOpen = false;
@@ -218,8 +228,16 @@ export default {
218
228
  z-index: 11;
219
229
  }
220
230
 
231
+ &.align-top {
232
+ transform: translate(0, calc(-100% - #{$mu150}));
233
+ }
234
+
221
235
  &.align-right {
222
- transform: translateX(calc(-100% + #{$mu150}));
236
+ transform: translate(calc(-100% + #{$mu150}), 0);
237
+
238
+ &.align-top {
239
+ transform: translate(calc(-100% + #{$mu150}), calc(-100% - #{$mu150}));
240
+ }
223
241
  }
224
242
  }
225
243
 
@@ -3,7 +3,7 @@
3
3
  v-if="icon"
4
4
  key="icon-input"
5
5
  class="mc-left-icon-input"
6
- :class="cssFieldElementClass"
6
+ :class="[cssFieldElementClass, rootClass]"
7
7
  >
8
8
  <m-text-input-icon :icon="icon" />
9
9
  <m-text-input-field
@@ -49,6 +49,10 @@ export default {
49
49
  type: String,
50
50
  default: null,
51
51
  },
52
+ rootClass: {
53
+ type: String,
54
+ default: null,
55
+ },
52
56
  textInputFieldClass: {
53
57
  type: String,
54
58
  default: null,