@mozaic-ds/vue 0.21.1 → 0.22.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.21.1",
3
+ "version": "0.22.0",
4
4
  "description": "Vue.js implementation of Mozaic Design System",
5
5
  "author": "Adeo - Mozaic Design System",
6
6
  "scripts": {
@@ -23,9 +23,9 @@
23
23
  "postinstall.js"
24
24
  ],
25
25
  "dependencies": {
26
- "@mozaic-ds/css-dev-tools": "1.29.1",
27
- "@mozaic-ds/icons": "1.34.0",
28
- "@mozaic-ds/styles": "1.33.0",
26
+ "@mozaic-ds/css-dev-tools": "^1.38.0",
27
+ "@mozaic-ds/icons": "1.40.0",
28
+ "@mozaic-ds/styles": "^1.39.1",
29
29
  "@mozaic-ds/web-fonts": "1.22.0",
30
30
  "core-js": "^3.18.3",
31
31
  "libphonenumber-js": "1.9.50",
@@ -1,70 +1,103 @@
1
1
  <template>
2
2
  <div
3
3
  ref="autocomplete"
4
+ v-click-outside="onClickOutside"
4
5
  class="mc-autocomplete"
5
6
  :class="{ 'mc-autocomplete--multi': multiple }"
6
7
  :style="tagStyle"
7
- @keyup.esc="isOpen = true"
8
8
  >
9
9
  <m-tag
10
- v-if="multiple && selectedItems().length > 0"
11
- id="tag"
10
+ v-if="multiple && tagValue.length > 1"
12
11
  type="removable"
13
- :label="selectedItems().length.toString() + ' ' + labelTag"
12
+ :label="tagValue.length.toString() + ' ' + tagLabel"
14
13
  class="mc-autocomplete__tag"
15
14
  size="s"
16
- @remove-tag="removeElementsFromList()"
15
+ @remove-tag="clearAutocomplete()"
17
16
  />
18
17
  <m-text-input
19
- v-model="itemDisplayed"
18
+ v-model="inputValue"
20
19
  :placeholder="placeholder"
20
+ :is-invalid="itemListForDropdown.length === 0"
21
21
  text-input-field-class="mc-autocomplete__trigger"
22
22
  icon-position="left"
23
23
  icon="DisplaySearch48"
24
24
  autocomplete="off"
25
- :style="{ width: boxWidth + 'px' }"
26
- @input="filerList"
25
+ @input="filterList"
27
26
  @click="isOpen = true"
28
27
  />
29
28
  <m-list-box
29
+ v-model="listboxValue"
30
30
  :open="isOpen"
31
- :items="sort ? orderedItems() : itemListForDropdown"
31
+ :items="itemListForDropdown"
32
32
  :multiple="multiple"
33
33
  :empty-search-label="emptySearchLabel"
34
- :style="{ width: boxWidth + 'px' }"
35
- @update:itemSelected="updateList"
36
- @close-list-box="isOpen = false"
34
+ :data-key-expr="dataKeyExpr"
35
+ :data-text-expr="dataTextExpr"
36
+ :data-value-expr="dataValueExpr"
37
+ @change="onChange"
37
38
  >
38
39
  <template #item="{ item }">
39
- <slot name="item" :item="item"> </slot>
40
+ <slot name="item" :item="item" />
40
41
  </template>
41
42
  </m-list-box>
42
43
  </div>
43
44
  </template>
44
45
 
45
46
  <script>
46
- import MTextInput from '../textinput/MTextInput.vue';
47
- import MTag from '../tags/MTag.vue';
48
47
  import MListBox from '../listbox/MListBox.vue';
48
+ import MTag from '../tags/MTag.vue';
49
+ import MTextInput from '../textinput/MTextInput.vue';
49
50
 
50
51
  export default {
51
52
  name: 'MAutocomplete',
52
53
 
53
- components: { MListBox, MTag, MTextInput },
54
+ components: {
55
+ MListBox,
56
+ MTag,
57
+ MTextInput,
58
+ },
59
+
60
+ directives: {
61
+ 'click-outside': {
62
+ bind(el, binding, vnode) {
63
+ el.clickOutsideEvent = (event) => {
64
+ if (!(el === event.target || el.contains(event.target))) {
65
+ vnode.context[binding.expression](event);
66
+ }
67
+ };
68
+ document.body.addEventListener('click', el.clickOutsideEvent);
69
+ },
70
+ unbind(el) {
71
+ document.body.removeEventListener('click', el.clickOutsideEvent);
72
+ },
73
+ },
74
+ },
75
+
76
+ model: {
77
+ event: 'change',
78
+ },
54
79
 
55
80
  props: {
56
- multiple: {
81
+ open: {
57
82
  type: Boolean,
58
83
  default: false,
59
84
  },
85
+ tagLabel: {
86
+ type: String,
87
+ default: '',
88
+ },
60
89
  placeholder: {
61
90
  type: String,
62
91
  default: '',
63
92
  },
64
93
  items: {
65
94
  type: Array,
66
- required: true,
67
95
  default: () => [],
96
+ required: true,
97
+ },
98
+ multiple: {
99
+ type: Boolean,
100
+ default: false,
68
101
  },
69
102
  filter: {
70
103
  type: Function,
@@ -74,48 +107,85 @@ export default {
74
107
  type: String,
75
108
  default: 'No item matching your criteria found',
76
109
  },
77
- sort: {
78
- type: Boolean,
79
- default: false,
110
+ dataKeyExpr: {
111
+ type: String,
112
+ default: 'id',
80
113
  },
81
- labelTag: {
114
+ dataTextExpr: {
82
115
  type: String,
83
- default: '',
116
+ default: 'label',
84
117
  },
85
- open: {
86
- type: Boolean,
87
- default: false,
118
+ dataValueExpr: {
119
+ type: String,
120
+ default: 'value',
121
+ },
122
+ value: {
123
+ type: Array,
124
+ default: () => [],
88
125
  },
89
126
  },
127
+
90
128
  data() {
91
129
  return {
92
- itemListForDropdown: this.$props.items,
93
- selected: this.$props.items,
130
+ itemListForDropdown: this.items,
131
+ selected: this.items,
94
132
  itemDisplayed: '',
95
- isOpen: this.$props.open,
133
+ isOpen: this.open,
96
134
  tagWidth: '0px',
97
- boxWidth: '288px',
135
+ maxWidth: '17.875rem',
136
+ uuid: undefined,
137
+ tagValue: '',
138
+ inputValue: '',
139
+ listboxValue: [],
98
140
  };
99
141
  },
142
+
100
143
  computed: {
101
144
  tagStyle() {
102
145
  return {
103
146
  '--tag-width': this.tagWidth,
147
+ '--max-width': this.maxWidth,
104
148
  };
105
149
  },
106
- boxStyle() {
107
- return {
108
- '--box-width': this.boxWidth,
109
- };
150
+ },
151
+
152
+ watch: {
153
+ listboxValue: function (newValue) {
154
+ const valueExpr = this.dataValueExpr;
155
+ const textExpr = this.dataTextExpr;
156
+
157
+ if (newValue.length === 1) {
158
+ const valueToDisplay = this.items.find(
159
+ (item) => item[valueExpr] == newValue
160
+ );
161
+ this.inputValue = valueToDisplay[textExpr];
162
+ } else {
163
+ this.inputValue = '';
164
+ this.tagValue = newValue;
165
+ }
166
+ },
167
+
168
+ tagValue: {
169
+ handler: function () {
170
+ this.setTagWidth();
171
+ },
172
+ immediate: true,
173
+ },
174
+
175
+ value: {
176
+ handler: function (value) {
177
+ this.listboxValue = value;
178
+ },
179
+ immediate: true,
110
180
  },
111
181
  },
182
+
112
183
  mounted() {
113
- this.selectedItems();
114
- this.tagWidthCalcul();
115
- this.boxWidthCalcul();
184
+ this.uuid = this._uid;
116
185
  },
186
+
117
187
  methods: {
118
- tagWidthCalcul() {
188
+ setTagWidth() {
119
189
  this.$nextTick(() => {
120
190
  this.tagWidth =
121
191
  document && document.querySelector('.mc-autocomplete__tag')
@@ -123,74 +193,45 @@ export default {
123
193
  : '0px';
124
194
  });
125
195
  },
126
- selectedItems() {
127
- return this.selected.filter((item) => {
128
- return item.selected;
129
- });
130
- },
131
- orderedItems() {
132
- this.itemListForDropdown.sort((a, b) => {
133
- if (a.selected === b.selected) {
134
- return a.id - b.id;
135
- } else if (a.selected < b.selected) {
136
- return 1;
137
- } else {
138
- return -1;
139
- }
140
- });
141
- },
142
- updateList(list) {
143
- if (!this.$props.multiple && list) {
144
- this.itemDisplayed = list[0].text;
145
- } else {
146
- this.isOpen = true;
147
- this.selectedItems();
148
- }
149
- this.itemListForDropdown.forEach((elem) => {
150
- if (elem.id === list.id) {
151
- elem.selected = false;
152
- }
153
- });
154
- this.tagWidthCalcul();
155
- this.$emit(
156
- 'update:modelValue',
157
- this.$props.multiple ? this.selectedItems().value : list
158
- );
159
- },
160
- removeElementsFromList() {
161
- this.itemListForDropdown.forEach((elem) => {
162
- elem.selected = false;
163
- });
164
- this.selectedItems();
165
- this.tagWidthCalcul();
166
- this.$emit('list-removed');
196
+
197
+ clearAutocomplete() {
198
+ this.listboxValue = [];
199
+ this.onChange();
200
+ this.$emit('clear');
167
201
  },
168
- filerList(value) {
169
- if (value.length && this.$props.filter) {
170
- this.$props.filter(value);
202
+
203
+ filterList(value) {
204
+ if (value.length && this.filter) {
205
+ this.filter(value);
171
206
  } else if (value.length) {
172
207
  this.itemListForDropdown = this.itemListForDropdown.filter((item) =>
173
- item.text.toUpperCase().includes(value.toUpperCase())
208
+ item[this.dataTextExpr].toUpperCase().includes(value.toUpperCase())
174
209
  );
175
210
  } else {
176
- this.itemListForDropdown = this.$props.items;
211
+ this.itemListForDropdown = this.items;
177
212
  }
178
213
  this.$emit('list-filtered', this.itemListForDropdown);
179
214
  },
180
- boxWidthCalcul() {
181
- this.$nextTick(() => {
182
- this.boxWidth = document.querySelector('.mc-autocomplete').clientWidth;
183
- console.log(this.boxWidth);
184
- });
185
- return;
215
+
216
+ onClickOutside() {
217
+ this.isOpen = false;
218
+ },
219
+
220
+ onChange() {
221
+ this.$emit('change', this.listboxValue);
186
222
  },
187
223
  },
188
224
  };
189
225
  </script>
190
226
 
191
227
  <style lang="scss">
192
- @import 'settings-tools/_all-settings';
193
- @import 'components/_c.autocomplete';
228
+ @import 'settings-tools/all-settings';
229
+ @import 'components/c.checkbox';
230
+ @import 'components/c.autocomplete';
231
+
232
+ .mc-autocomplete {
233
+ max-width: var(--max-width);
234
+ }
194
235
 
195
236
  .mc-autocomplete--multi .mc-autocomplete__trigger {
196
237
  padding-left: calc(2.9375rem + var(--tag-width));
@@ -22,6 +22,7 @@
22
22
  v-bind="option"
23
23
  :key="option.id ? option.id : option.value"
24
24
  class="mc-field__item"
25
+ :disabled="disabled"
25
26
  :checked="value ? value.includes(option.value) : undefined"
26
27
  @change="(v) => onChange(v, option.value)"
27
28
  >
@@ -124,6 +125,13 @@ export default {
124
125
  type: Boolean,
125
126
  default: false,
126
127
  },
128
+ /**
129
+ * Disabled
130
+ */
131
+ disabled: {
132
+ type: Boolean,
133
+ default: false,
134
+ },
127
135
  },
128
136
 
129
137
  data() {
@@ -7,35 +7,48 @@
7
7
  aria-labelledby="listbox"
8
8
  :class="{ 'is-open': open, 'mc-listbox--multi': multiple }"
9
9
  >
10
- <li
11
- v-for="item in selectableItems"
12
- :key="item.id"
13
- class="mc-listbox__item"
14
- @change="$emit('update:itemSelected', item)"
15
- @click.self="updateList(item.id, item.text, !item.selected, true)"
16
- >
17
- <slot name="item" :item="item">
18
- <span class="mc-listbox__text">{{ item.text }} </span>
19
- </slot>
20
- <m-checkbox
21
- v-if="multiple"
22
- :id="`checkbox-dropdown-${item.id}`"
23
- v-model="selectableItems.find((elem) => elem.id === item.id).selected"
24
- class="mc-listbox__input"
25
- @change="updateList(item.id, item.text, !item.selected, $e)"
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"
20
+ >
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"
26
31
  />
27
32
  </li>
28
33
  </ul>
29
- <div v-else class="mc-list-box__empty">
34
+ <div v-else class="mc-listbox__empty">
30
35
  {{ emptySearchLabel }}
31
36
  </div>
32
37
  </template>
38
+
33
39
  <script>
34
- import MCheckbox from '../checkbox/MCheckbox.vue';
40
+ import MIcon from '../icon/MIcon.vue';
41
+
35
42
  export default {
36
43
  name: 'MListbox',
37
44
 
38
- components: { MCheckbox },
45
+ components: {
46
+ MIcon,
47
+ },
48
+
49
+ model: {
50
+ event: 'change',
51
+ },
39
52
 
40
53
  props: {
41
54
  open: {
@@ -58,49 +71,208 @@ export default {
58
71
  type: Boolean,
59
72
  default: false,
60
73
  },
74
+ dataKeyExpr: {
75
+ type: String,
76
+ default: 'id',
77
+ },
78
+ dataTextExpr: {
79
+ type: String,
80
+ default: 'text',
81
+ },
82
+ dataValueExpr: {
83
+ type: String,
84
+ default: 'text',
85
+ },
86
+ value: {
87
+ type: [Array, String],
88
+ default: undefined,
89
+ },
61
90
  },
91
+
62
92
  data() {
63
93
  return {
64
- selectableItems: null,
94
+ localItems: null,
95
+ localValue: [],
65
96
  selected: [],
97
+ uuid: null,
98
+ inputBaseId: 'listboxInput',
66
99
  };
67
100
  },
101
+
68
102
  watch: {
69
103
  items: {
70
104
  handler: function (val) {
71
- this.selectableItems = val;
105
+ this.localItems = val;
106
+ },
107
+ immediate: true,
108
+ },
109
+ 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
+ }
72
131
  },
73
132
  immediate: true,
74
133
  },
75
134
  },
76
- methods: {
77
- updateList(id, text, value, isCheckboxUpdate) {
78
- if (!this.multiple) {
79
- this.$emit('update:itemSelected', [{ id, selected: value, text }]);
80
135
 
81
- this.$emit('close-list-box');
82
- return;
83
- }
136
+ mounted() {
137
+ this.uuid = Math.random();
138
+ },
84
139
 
85
- if (
86
- isCheckboxUpdate &&
87
- this.selectableItems.find((item) => item.id === id)
88
- ) {
89
- this.selectableItems.find((item) => item.id === id).selected = value;
140
+ methods: {
141
+ onChange() {
142
+ if (!this.multiple) {
143
+ const currentValue = this.localValue;
144
+ this.localValue = [];
145
+ this.localValue = currentValue.slice(-1);
90
146
  }
91
147
 
92
- if (value) {
93
- this.selected = [...this.selected, { id, selected: value, text }];
94
- } else {
95
- this.selected = this.selected.filter((item) => item.id !== id);
96
- }
97
- this.$emit('update:itemSelected', this.selectableItems);
148
+ this.$emit('change', this.localValue);
98
149
  },
99
150
  },
100
151
  };
101
152
  </script>
102
153
 
103
154
  <style lang="scss">
104
- @import 'settings-tools/_all-settings';
105
- @import 'components/_c.listbox';
155
+ @import 'settings-tools/all-settings';
156
+
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
+ }
277
+ }
106
278
  </style>
@@ -7,7 +7,7 @@
7
7
  icon-position="left"
8
8
  :aria-label="decrementAriaLabel"
9
9
  :aria-controls="id"
10
- :disabled="currentValue === valuemin"
10
+ :disabled="disabled ? disabled : currentValue === valuemin"
11
11
  :size="small ? 's' : null"
12
12
  tabindex="-1"
13
13
  type="button"
@@ -26,6 +26,7 @@
26
26
  :aria-valuemax="valuemax"
27
27
  :placeholder="placeholder"
28
28
  :size="small ? 's' : null"
29
+ :disabled="disabled"
29
30
  role="spinbutton"
30
31
  @input="handle"
31
32
  @keypress="integerOnly && formatValue($event)"
@@ -39,7 +40,7 @@
39
40
  icon-position="right"
40
41
  :aria-label="incrementAriaLabel"
41
42
  :aria-controls="id"
42
- :disabled="currentValue === valuemax"
43
+ :disabled="disabled ? disabled : currentValue === valuemax"
43
44
  :size="small ? 's' : null"
44
45
  tabindex="-1"
45
46
  type="button"
@@ -105,6 +106,10 @@ export default {
105
106
  type: Boolean,
106
107
  default: false,
107
108
  },
109
+ disabled: {
110
+ type: Boolean,
111
+ default: false,
112
+ },
108
113
  },
109
114
 
110
115
  data() {