@mozaic-ds/vue 0.20.0-beta.1 → 0.20.0-beta.4

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.20.0-beta.1",
3
+ "version": "0.20.0-beta.4",
4
4
  "description": "Vue.js implementation of Mozaic Design System",
5
5
  "author": "Adeo - Mozaic Design System",
6
6
  "scripts": {
@@ -27,9 +27,6 @@
27
27
  "@mozaic-ds/icons": "1.34.0",
28
28
  "@mozaic-ds/styles": "1.33.0",
29
29
  "@mozaic-ds/web-fonts": "1.22.0",
30
- "@vue/composition-api": "^1.6.2",
31
- "@vueuse/components": "^8.6.0",
32
- "@vueuse/core": "^8.6.0",
33
30
  "core-js": "^3.18.3",
34
31
  "libphonenumber-js": "1.9.50",
35
32
  "postcss-scss": "^4.0.1",
@@ -1,65 +1,48 @@
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 }"
7
+ :style="tagStyle"
6
8
  >
7
9
  <m-tag
8
- v-if="multiple && modelValue.length > 0"
9
- id="tag"
10
+ v-if="multiple && tagValue.length > 1"
10
11
  type="removable"
11
- :label="selectedItems.length.toString() + ' ' + labelTag"
12
+ :label="tagValue.length.toString() + ' ' + tagLabel"
12
13
  class="mc-autocomplete__tag"
13
14
  size="s"
14
- @remove-tag="removeElementsFromList()"
15
+ @remove-tag="clearAutocomplete()"
15
16
  />
16
17
  <m-text-input
17
- v-model="state.itemDisplayed"
18
+ v-model="inputValue"
18
19
  :placeholder="placeholder"
19
- class="mc-autocomplete__trigger"
20
+ text-input-field-class="mc-autocomplete__trigger"
20
21
  icon-position="left"
21
22
  icon="DisplaySearch48"
22
23
  autocomplete="off"
23
- :style="{ width: boxWidth + 'px' }"
24
- @update:modelValue="updateDropdown"
25
- @change="$emit('input-change', $event.target.value)"
26
- @click="state.open = true"
24
+ @input="filterList"
25
+ @click="isOpen = true"
27
26
  />
28
27
  <m-list-box
29
- ref="listbox"
30
- v-model="state.selected"
31
- :open="state.open"
32
- :items="sort ? orderedItems() : state.itemListForDropdown"
28
+ v-model="listboxValue"
29
+ :open="isOpen"
30
+ :items="itemListForDropdown"
33
31
  :multiple="multiple"
34
32
  :empty-search-label="emptySearchLabel"
35
- :style="{ width: boxWidth + 'px' }"
36
33
  :data-key-expr="dataKeyExpr"
37
34
  :data-text-expr="dataTextExpr"
38
35
  :data-value-expr="dataValueExpr"
39
- @update:modelValue="(selected) => updateList(selected)"
40
- @close-list-box="state.open = false"
36
+ @change="onChange"
41
37
  >
42
38
  <template #item="{ item }">
43
- <slot name="item" :item="item"> </slot>
39
+ <slot name="item" :item="item" />
44
40
  </template>
45
41
  </m-list-box>
46
42
  </div>
47
43
  </template>
48
44
 
49
45
  <script>
50
- // import Vue from 'vue';
51
- // import VueCompositionAPI from '@vue/composition-api';
52
- // Vue.use(VueCompositionAPI);
53
-
54
- import {
55
- ref,
56
- reactive,
57
- onMounted,
58
- computed,
59
- nextTick,
60
- } from '@vue/composition-api';
61
-
62
- import { onClickOutside } from '@vueuse/core';
63
46
  import MListBox from '../listbox/MListBox.vue';
64
47
  import MTag from '../tags/MTag.vue';
65
48
  import MTextInput from '../textinput/MTextInput.vue';
@@ -73,19 +56,47 @@ export default {
73
56
  MTextInput,
74
57
  },
75
58
 
59
+ directives: {
60
+ 'click-outside': {
61
+ bind(el, binding, vnode) {
62
+ el.clickOutsideEvent = (event) => {
63
+ if (!(el === event.target || el.contains(event.target))) {
64
+ vnode.context[binding.expression](event);
65
+ }
66
+ };
67
+ document.body.addEventListener('click', el.clickOutsideEvent);
68
+ },
69
+ unbind(el) {
70
+ document.body.removeEventListener('click', el.clickOutsideEvent);
71
+ },
72
+ },
73
+ },
74
+
75
+ model: {
76
+ event: 'change',
77
+ },
78
+
76
79
  props: {
77
- multiple: {
80
+ open: {
78
81
  type: Boolean,
79
82
  default: false,
80
83
  },
84
+ tagLabel: {
85
+ type: String,
86
+ default: 'voitures',
87
+ },
81
88
  placeholder: {
82
89
  type: String,
83
90
  default: '',
84
91
  },
85
92
  items: {
86
93
  type: Array,
87
- required: true,
88
94
  default: () => [],
95
+ required: true,
96
+ },
97
+ multiple: {
98
+ type: Boolean,
99
+ default: false,
89
100
  },
90
101
  filter: {
91
102
  type: Function,
@@ -95,18 +106,6 @@ export default {
95
106
  type: String,
96
107
  default: 'No item matching your criteria found',
97
108
  },
98
- sort: {
99
- type: Boolean,
100
- default: false,
101
- },
102
- labelTag: {
103
- type: String,
104
- default: '',
105
- },
106
- modelValue: {
107
- type: Array,
108
- default: () => [],
109
- },
110
109
  dataKeyExpr: {
111
110
  type: String,
112
111
  default: 'id',
@@ -115,119 +114,118 @@ export default {
115
114
  type: String,
116
115
  default: 'text',
117
116
  },
118
- dataSelectedExpr: {
119
- type: String,
120
- default: 'selected',
121
- },
122
117
  dataValueExpr: {
123
118
  type: String,
124
119
  default: 'text',
125
120
  },
121
+ value: {
122
+ type: Array,
123
+ default: () => [],
124
+ },
126
125
  },
127
126
 
128
- emits: ['update:modelValue', 'list-removed', 'list-filtered', 'input-change'],
129
-
130
- setup(props, { emit }) {
131
- const autocomplete = ref(null);
132
- const listbox = ref(null);
133
-
134
- const state = reactive({
135
- open: false,
136
- itemListForDropdown: props.items,
137
- selected: [],
127
+ data() {
128
+ return {
129
+ itemListForDropdown: this.items,
130
+ selected: this.items,
138
131
  itemDisplayed: '',
132
+ isOpen: this.open,
139
133
  tagWidth: '0px',
140
- });
141
-
142
- onMounted(() => {
143
- manageTag();
144
- });
145
-
146
- const selectedItems = computed(() =>
147
- state.selected.filter((item) => {
148
- return item[props.dataValueExpr];
149
- })
150
- );
151
-
152
- const orderedItems = () => {
153
- // Order by selected then by id
154
- return Array.from(state.itemListForDropdown).sort((a, b) => {
155
- if (a.selected === b.selected) {
156
- return a.id - b.id;
157
- } else if (a.selected < b.selected) {
158
- return 1;
159
- } else {
160
- return -1;
161
- }
162
- });
134
+ uuid: undefined,
135
+ tagValue: '',
136
+ inputValue: '',
137
+ listboxValue: [],
163
138
  };
139
+ },
164
140
 
165
- const boxWidth = computed(() =>
166
- autocomplete.value ? autocomplete.value.clientWidth : ''
167
- );
168
-
169
- onClickOutside(listbox, () => (state.open = false));
141
+ computed: {
142
+ tagStyle() {
143
+ return {
144
+ '--tag-width': this.tagWidth,
145
+ };
146
+ },
147
+ },
170
148
 
171
- const updateList = (list) => {
172
- if (!props.multiple && list) {
173
- state.itemDisplayed = list[0].value;
149
+ watch: {
150
+ listboxValue: function (newValue) {
151
+ if (newValue.length === 1) {
152
+ const valueToDisplay = this.items.find(
153
+ (item) => item.value == newValue
154
+ );
155
+ this.inputValue = valueToDisplay.text;
174
156
  } else {
175
- state.open = true;
176
- // manageTag();
157
+ this.inputValue = '';
158
+ this.tagValue = newValue;
177
159
  }
178
- state.selected = list;
179
- emit('update:modelValue', list);
180
- };
160
+ },
181
161
 
182
- const removeElementsFromList = () => {
183
- state.selected = [];
184
- // manageTag();
185
- emit('update:modelValue', state.selected);
186
- emit('list-removed');
187
- };
162
+ tagValue: {
163
+ handler: function () {
164
+ this.setTagWidth();
165
+ },
166
+ immediate: true,
167
+ },
188
168
 
189
- const updateDropdown = (value) => {
190
- if (value.length && props.filter) {
191
- props.filter(value);
192
- } else if (value.length) {
193
- state.itemListForDropdown = props.items.filter((item) =>
194
- item[props.dataTextExpr].toUpperCase().includes(value.toUpperCase())
195
- );
196
- } else {
197
- state.itemListForDropdown = props.items;
198
- }
199
- emit('update:modelValue', state.selected);
200
- emit('list-filtered', state.itemListForDropdown);
201
- };
169
+ value: {
170
+ handler: function (value) {
171
+ this.listboxValue = value;
172
+ },
173
+ immediate: true,
174
+ },
175
+ },
176
+
177
+ mounted() {
178
+ this.uuid = this._uid;
179
+ },
202
180
 
203
- const manageTag = () => {
204
- nextTick(() => {
205
- state.tagWidth =
181
+ methods: {
182
+ setTagWidth() {
183
+ this.$nextTick(() => {
184
+ this.tagWidth =
206
185
  document && document.querySelector('.mc-autocomplete__tag')
207
186
  ? document.querySelector('.mc-autocomplete__tag').clientWidth + 'px'
208
187
  : '0px';
209
188
  });
210
- };
189
+ },
211
190
 
212
- return {
213
- autocomplete,
214
- listbox,
215
- state,
216
- selectedItems,
217
- orderedItems,
218
- boxWidth,
219
- updateList,
220
- removeElementsFromList,
221
- updateDropdown,
222
- manageTag,
223
- };
191
+ clearAutocomplete() {
192
+ this.listboxValue = [];
193
+ this.onChange();
194
+ this.$emit('clear');
195
+ },
196
+
197
+ filterList(value) {
198
+ if (value.length && this.filter) {
199
+ this.filter(value);
200
+ } else if (value.length) {
201
+ this.itemListForDropdown = this.itemListForDropdown.filter((item) =>
202
+ item.text.toUpperCase().includes(value.toUpperCase())
203
+ );
204
+ } else {
205
+ this.itemListForDropdown = this.items;
206
+ }
207
+ this.$emit('list-filtered', this.itemListForDropdown);
208
+ },
209
+
210
+ onClickOutside() {
211
+ this.isOpen = false;
212
+ },
213
+
214
+ onChange() {
215
+ this.$emit('change', this.listboxValue);
216
+ },
224
217
  },
225
218
  };
219
+
220
+ // TODO:
221
+ // - boxWidth ?
222
+ // - dataSelectedExpr ?
226
223
  </script>
227
224
 
228
225
  <style lang="scss">
229
- @import 'settings-tools/_all-settings';
230
- @import 'components/_c.autocomplete';
226
+ @import 'settings-tools/all-settings';
227
+ @import 'components/c.checkbox';
228
+ @import 'components/c.autocomplete';
231
229
 
232
230
  .mc-autocomplete--multi .mc-autocomplete__trigger {
233
231
  padding-left: calc(2.9375rem + var(--tag-width));
@@ -1,17 +1,13 @@
1
1
  <template>
2
2
  <ul
3
- v-if="state.selectableItems.length > 0"
3
+ v-if="items.length > 0"
4
4
  ref="listbox"
5
5
  role="listbox"
6
- class="mc-listbox mc-listbox--multi"
6
+ class="mc-listbox"
7
7
  aria-labelledby="listbox"
8
- :class="{ 'is-open': open }"
8
+ :class="{ 'is-open': open, 'mc-listbox--multi': multiple }"
9
9
  >
10
- <li
11
- v-for="item in state.selectableItems"
12
- :key="item[dataKeyExpr]"
13
- class="mc-listbox__item"
14
- >
10
+ <li v-for="item in selectableItems" :key="item.id" class="mc-listbox__item">
15
11
  <slot name="item" :item="item">
16
12
  <label
17
13
  :for="`checkbox-dropdown-${item[dataKeyExpr]}-${uuid}`"
@@ -19,32 +15,29 @@
19
15
  >{{ item[dataTextExpr] }}
20
16
  </label>
21
17
  </slot>
22
- <m-checkbox
18
+ <input
23
19
  :id="`checkbox-dropdown-${item[dataKeyExpr]}-${uuid}`"
20
+ ref="input"
21
+ v-model="localValue"
22
+ class="mc-checkbox__input mc-listbox__input"
24
23
  :class="{ hideCheckbox: !multiple }"
25
- class="mc-listbox__input"
26
- :checked="updateModelValue(true, item)"
27
- @update:modelValue="(v) => updateList(v, item[dataValueExpr])"
24
+ type="checkbox"
25
+ :value="item.value"
26
+ @change="onChange"
28
27
  />
29
28
  </li>
30
29
  </ul>
31
- <div v-else class="mc-listbox__empty">
30
+ <div v-else class="mc-list-box__empty">
32
31
  {{ emptySearchLabel }}
33
32
  </div>
34
33
  </template>
35
34
 
36
35
  <script>
37
- // import Vue from 'vue';
38
- // import VueCompositionAPI from '@vue/composition-api';
39
- // Vue.use(VueCompositionAPI);
40
- import { reactive, watch, ref } from '@vue/composition-api';
41
- import MCheckbox from '../checkbox/MCheckbox.vue';
42
-
43
36
  export default {
44
37
  name: 'MListbox',
45
38
 
46
- components: {
47
- MCheckbox,
39
+ model: {
40
+ event: 'change',
48
41
  },
49
42
 
50
43
  props: {
@@ -80,73 +73,203 @@ export default {
80
73
  type: String,
81
74
  default: 'text',
82
75
  },
83
- modelValue: {
84
- type: Array,
85
- default: () => [],
76
+ value: {
77
+ type: [Array, String],
78
+ default: undefined,
86
79
  },
87
80
  },
88
81
 
89
- emits: ['update:itemSelected', 'close-list-box', 'update:modelValue'],
90
-
91
- setup(props, { emit }) {
92
- const listbox = ref(null);
93
-
94
- let uuid = Math.random();
95
-
96
- const state = reactive({
97
- selectableItems: props.items,
82
+ data() {
83
+ return {
84
+ selectableItems: null,
85
+ localValue: [],
98
86
  selected: [],
99
- });
87
+ uuid: null,
88
+ inputBaseId: 'listboxInput',
89
+ };
90
+ },
100
91
 
101
- const updateList = (checked, value) => {
102
- if (!props.multiple) {
103
- emit('update:modelValue', [{ value }]);
104
- emit('close-list-box');
105
- return;
106
- }
92
+ watch: {
93
+ items: {
94
+ handler: function (val) {
95
+ this.selectableItems = val;
96
+ },
97
+ immediate: true,
98
+ },
99
+ value: {
100
+ handler: function (value) {
101
+ this.localValue = value;
102
+ },
103
+ immediate: true,
104
+ },
105
+ localValue: {
106
+ handler: function (localValue) {
107
+ const inputs = this.$refs.input;
108
+ if (!this.multiple && inputs) {
109
+ const selectedValue = Array.from(localValue);
107
110
 
108
- if (checked) {
109
- state.selected = [...state.selected, { [props.dataValueExpr]: value }];
110
- } else {
111
- state.selected = state.selected.filter(
112
- (item) => item[props.dataValueExpr] !== value
113
- );
114
- }
115
- emit('update:modelValue', state.selected);
116
- };
111
+ inputs.forEach(function (input) {
112
+ const listItem = input.closest('.mc-listbox__item');
117
113
 
118
- const updateModelValue = (checked, value) => {
119
- state.selected = props.modelValue;
120
- if (state.selected) {
121
- return (
122
- state.selected.filter(
123
- (item) => item[props.dataValueExpr] === value[props.dataValueExpr]
124
- ).length > 0
125
- );
126
- } else {
127
- return undefined;
128
- }
129
- };
114
+ if (input.value == selectedValue[0]) {
115
+ listItem.classList.add('is-checked');
116
+ } else {
117
+ listItem.classList.remove('is-checked');
118
+ }
119
+ });
120
+ }
121
+ },
122
+ immediate: true,
123
+ },
124
+ },
125
+
126
+ mounted() {
127
+ this.uuid = Math.random();
128
+ },
130
129
 
131
- watch(
132
- () => props.items,
133
- (nextItems) => {
134
- state.selectableItems = nextItems;
130
+ methods: {
131
+ onChange() {
132
+ if (!this.multiple) {
133
+ const currentValue = this.localValue;
134
+ this.localValue = [];
135
+ this.localValue = currentValue.slice(-1);
135
136
  }
136
- );
137
137
 
138
- return {
139
- listbox,
140
- uuid,
141
- state,
142
- updateList,
143
- updateModelValue,
144
- };
138
+ this.$emit('change', this.localValue);
139
+ },
145
140
  },
146
141
  };
147
142
  </script>
148
143
 
149
144
  <style lang="scss">
150
- @import 'settings-tools/_all-settings';
151
- @import 'components/_c.listbox';
145
+ @import 'settings-tools/all-settings';
146
+
147
+ .mc-listbox {
148
+ $parent: get-parent-selector(&);
149
+
150
+ @include unstyle-list();
151
+
152
+ background-color: $color-grey-000;
153
+ border: 1px solid $color-grey-600;
154
+ border-radius: 3px;
155
+ position: absolute;
156
+ overflow-y: auto;
157
+ margin-top: 5px;
158
+ margin-bottom: 0;
159
+ // max-height: 13.5rem;
160
+ // min-width: 18rem;
161
+ opacity: 0;
162
+ visibility: hidden;
163
+ width: 100%;
164
+
165
+ &.is-open {
166
+ opacity: 1;
167
+ visibility: visible;
168
+ z-index: 11;
169
+ }
170
+
171
+ &::-webkit-scrollbar {
172
+ background-color: $color-grey-100;
173
+ width: $mu025;
174
+
175
+ &-thumb {
176
+ background: $color-grey-600;
177
+ }
178
+ }
179
+
180
+ &__item {
181
+ align-items: center;
182
+ display: flex;
183
+ gap: $mu050;
184
+ min-height: $mu300;
185
+ padding-left: $mu075;
186
+ padding-right: $mu075;
187
+ position: relative;
188
+ justify-content: space-between;
189
+
190
+ &:not(:last-child) {
191
+ border-bottom: 1px solid $color-grey-300;
192
+ }
193
+
194
+ &:hover {
195
+ background-color: $color-grey-100;
196
+ box-shadow: inset 9px 0 0 -7px $color-grey-900;
197
+ }
198
+ }
199
+
200
+ &__flag,
201
+ &__icon {
202
+ width: $mu200;
203
+ height: $mu200;
204
+ }
205
+
206
+ &__flag {
207
+ @include set-font-scale('07', 'm');
208
+
209
+ text-align: center;
210
+ }
211
+
212
+ &__empty {
213
+ align-items: center;
214
+ border: 1px solid $color-grey-600;
215
+ border-radius: get-border-radius('m');
216
+ padding: $mu100 $mu050;
217
+ }
218
+
219
+ &__label {
220
+ cursor: pointer;
221
+
222
+ &::after {
223
+ content: '';
224
+ position: absolute;
225
+ inset: 0;
226
+ z-index: 2;
227
+ }
228
+ }
229
+
230
+ &__input {
231
+ position: absolute;
232
+ right: $mu075;
233
+ }
234
+
235
+ .is-focus {
236
+ background-color: $color-grey-100;
237
+ box-shadow: inset 9px 0 0 -7px $color-grey-900;
238
+ }
239
+
240
+ .is-disabled {
241
+ cursor: not-allowed;
242
+ box-shadow: none;
243
+ background-color: $color-grey-200;
244
+ color: $color-grey-600;
245
+ pointer-events: none;
246
+ }
247
+
248
+ &--left {
249
+ right: 0;
250
+ transform: translateX(-100%);
251
+ }
252
+
253
+ &:not(.mc-listbox--multi) {
254
+ .is-checked {
255
+ background-size: $mu150;
256
+ background-image: url(inline-icons(
257
+ 'notification-available-24',
258
+ $color-grey-900
259
+ ));
260
+ background-repeat: no-repeat;
261
+ background-position: right $mu075 center;
262
+ }
263
+
264
+ #{$parent} {
265
+ &__input {
266
+ @include visually-hidden();
267
+ }
268
+ }
269
+ }
270
+ }
271
+
272
+ .hideCheckbox {
273
+ opacity: 0;
274
+ }
152
275
  </style>
@@ -19,6 +19,7 @@
19
19
  :value="rateId"
20
20
  class="mc-stars-input__radio"
21
21
  :required="required"
22
+ @click="$emit('star-clicked', index + 1)"
22
23
  />
23
24
 
24
25
  <label