@mozaic-ds/vue 0.19.1 → 0.20.0-beta.2

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,14 +1,14 @@
1
1
  {
2
2
  "name": "@mozaic-ds/vue",
3
- "version": "0.19.1",
3
+ "version": "0.20.0-beta.2",
4
4
  "description": "Vue.js implementation of Mozaic Design System",
5
5
  "author": "Adeo - Mozaic Design System",
6
6
  "scripts": {
7
7
  "serve": "vue-cli-service serve",
8
8
  "build": "vue-cli-service build ./src/index.js",
9
9
  "lint": "vue-cli-service lint",
10
- "build:bundle": "vue-cli-service build --target lib --name mozaic-vue ./src/index.js",
11
- "build:bundleAdeo": "vue-cli-service build --target lib --formats umd --mode adeo --filename mozaic-vue.adeo ./src/index.js --no-clean",
10
+ "build:bundle": "vue-cli-service build --target lib --inline-vue --name mozaic-vue ./src/index.js",
11
+ "build:bundleAdeo": "vue-cli-service build --mode adeo --target lib --formats umd --inline-vue --filename mozaic-vue.adeo ./src/index.js --no-clean",
12
12
  "postinstall": "node postinstall.js",
13
13
  "prepublishOnly": "npm run-script build:bundle && npm run-script build:bundleAdeo",
14
14
  "publish:beta": "npm publish --access public --tag alpha",
@@ -27,6 +27,8 @@
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/core": "^8.6.0",
30
32
  "core-js": "^3.18.3",
31
33
  "libphonenumber-js": "1.9.50",
32
34
  "postcss-scss": "^4.0.1",
@@ -3,37 +3,41 @@
3
3
  ref="autocomplete"
4
4
  class="mc-autocomplete"
5
5
  :class="{ 'mc-autocomplete--multi': multiple }"
6
- :style="tagStyle"
7
- @keyup.esc="isOpen = true"
8
6
  >
9
7
  <m-tag
10
- v-if="multiple && selectedItems().length > 0"
8
+ v-if="multiple && modelValue.length > 0"
11
9
  id="tag"
12
10
  type="removable"
13
- :label="selectedItems().length.toString() + ' ' + labelTag"
11
+ :label="selectedItems.length.toString() + ' ' + labelTag"
14
12
  class="mc-autocomplete__tag"
15
13
  size="s"
16
14
  @remove-tag="removeElementsFromList()"
17
15
  />
18
16
  <m-text-input
19
- v-model="itemDisplayed"
17
+ v-model="state.itemDisplayed"
20
18
  :placeholder="placeholder"
21
- text-input-field-class="mc-autocomplete__trigger"
19
+ class="mc-autocomplete__trigger"
22
20
  icon-position="left"
23
21
  icon="DisplaySearch48"
24
22
  autocomplete="off"
25
23
  :style="{ width: boxWidth + 'px' }"
26
- @input="filerList"
27
- @click="isOpen = true"
24
+ @update:modelValue="updateDropdown"
25
+ @change="$emit('input-change', $event.target.value)"
26
+ @click="state.open = true"
28
27
  />
29
28
  <m-list-box
30
- :open="isOpen"
31
- :items="sort ? orderedItems() : itemListForDropdown"
29
+ ref="listbox"
30
+ v-model="state.selected"
31
+ :open="state.open"
32
+ :items="sort ? orderedItems() : state.itemListForDropdown"
32
33
  :multiple="multiple"
33
34
  :empty-search-label="emptySearchLabel"
34
35
  :style="{ width: boxWidth + 'px' }"
35
- @update:itemSelected="updateList"
36
- @close-list-box="isOpen = false"
36
+ :data-key-expr="dataKeyExpr"
37
+ :data-text-expr="dataTextExpr"
38
+ :data-value-expr="dataValueExpr"
39
+ @update:modelValue="(selected) => updateList(selected)"
40
+ @close-list-box="state.open = false"
37
41
  >
38
42
  <template #item="{ item }">
39
43
  <slot name="item" :item="item"> </slot>
@@ -43,14 +47,26 @@
43
47
  </template>
44
48
 
45
49
  <script>
46
- import MTextInput from '../textinput/MTextInput.vue';
47
- import MTag from '../tags/MTag.vue';
50
+ import {
51
+ ref,
52
+ reactive,
53
+ onMounted,
54
+ computed,
55
+ nextTick,
56
+ } from '@vue/composition-api';
57
+ import { onClickOutside } from '@vueuse/core';
48
58
  import MListBox from '../listbox/MListBox.vue';
59
+ import MTag from '../tags/MTag.vue';
60
+ import MTextInput from '../textinput/MTextInput.vue';
49
61
 
50
62
  export default {
51
63
  name: 'MAutocomplete',
52
64
 
53
- components: { MListBox, MTag, MTextInput },
65
+ components: {
66
+ MListBox,
67
+ MTag,
68
+ MTextInput,
69
+ },
54
70
 
55
71
  props: {
56
72
  multiple: {
@@ -82,54 +98,55 @@ export default {
82
98
  type: String,
83
99
  default: '',
84
100
  },
85
- open: {
86
- type: Boolean,
87
- default: false,
101
+ modelValue: {
102
+ type: Array,
103
+ default: () => [],
88
104
  },
89
- },
90
- data() {
91
- return {
92
- itemListForDropdown: this.$props.items,
93
- selected: this.$props.items,
94
- itemDisplayed: '',
95
- isOpen: this.$props.open,
96
- tagWidth: '0px',
97
- boxWidth: '288px',
98
- };
99
- },
100
- computed: {
101
- tagStyle() {
102
- return {
103
- '--tag-width': this.tagWidth,
104
- };
105
+ dataKeyExpr: {
106
+ type: String,
107
+ default: 'id',
105
108
  },
106
- boxStyle() {
107
- return {
108
- '--box-width': this.boxWidth,
109
- };
109
+ dataTextExpr: {
110
+ type: String,
111
+ default: 'text',
110
112
  },
111
- },
112
- mounted() {
113
- this.selectedItems();
114
- this.tagWidthCalcul();
115
- this.boxWidthCalcul();
116
- },
117
- methods: {
118
- tagWidthCalcul() {
119
- this.$nextTick(() => {
120
- this.tagWidth =
121
- document && document.querySelector('.mc-autocomplete__tag')
122
- ? document.querySelector('.mc-autocomplete__tag').clientWidth + 'px'
123
- : '0px';
124
- });
113
+ dataSelectedExpr: {
114
+ type: String,
115
+ default: 'selected',
125
116
  },
126
- selectedItems() {
127
- return this.selected.filter((item) => {
128
- return item.selected;
129
- });
117
+ dataValueExpr: {
118
+ type: String,
119
+ default: 'text',
130
120
  },
131
- orderedItems() {
132
- this.itemListForDropdown.sort((a, b) => {
121
+ },
122
+
123
+ emits: ['update:modelValue', 'list-removed', 'list-filtered', 'input-change'],
124
+
125
+ setup(props, { emit }) {
126
+ const autocomplete = ref(null);
127
+ const listbox = ref(null);
128
+
129
+ const state = reactive({
130
+ open: false,
131
+ itemListForDropdown: props.items,
132
+ selected: [],
133
+ itemDisplayed: '',
134
+ tagWidth: '0px',
135
+ });
136
+
137
+ onMounted(() => {
138
+ manageTag();
139
+ });
140
+
141
+ const selectedItems = computed(() =>
142
+ state.selected.filter((item) => {
143
+ return item[props.dataValueExpr];
144
+ })
145
+ );
146
+
147
+ const orderedItems = () => {
148
+ // Order by selected then by id
149
+ return Array.from(state.itemListForDropdown).sort((a, b) => {
133
150
  if (a.selected === b.selected) {
134
151
  return a.id - b.id;
135
152
  } else if (a.selected < b.selected) {
@@ -138,52 +155,67 @@ export default {
138
155
  return -1;
139
156
  }
140
157
  });
141
- },
142
- updateList(list) {
143
- if (!this.$props.multiple && list) {
144
- this.itemDisplayed = list[0].text;
158
+ };
159
+
160
+ const boxWidth = computed(() =>
161
+ autocomplete.value ? autocomplete.value.clientWidth : ''
162
+ );
163
+
164
+ onClickOutside(listbox, () => (state.open = false));
165
+
166
+ const updateList = (list) => {
167
+ if (!props.multiple && list) {
168
+ state.itemDisplayed = list[0].value;
145
169
  } else {
146
- this.isOpen = true;
147
- this.selectedItems();
170
+ state.open = true;
171
+ manageTag();
148
172
  }
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');
167
- },
168
- filerList(value) {
169
- if (value.length && this.$props.filter) {
170
- this.$props.filter(value);
173
+ state.selected = list;
174
+ emit('update:modelValue', list);
175
+ };
176
+
177
+ const removeElementsFromList = () => {
178
+ state.selected = [];
179
+ manageTag();
180
+ emit('update:modelValue', state.selected);
181
+ emit('list-removed');
182
+ };
183
+
184
+ const updateDropdown = (value) => {
185
+ if (value.length && props.filter) {
186
+ props.filter(value);
171
187
  } else if (value.length) {
172
- this.itemListForDropdown = this.itemListForDropdown.filter((item) =>
173
- item.text.toUpperCase().includes(value.toUpperCase())
188
+ state.itemListForDropdown = props.items.filter((item) =>
189
+ item[props.dataTextExpr].toUpperCase().includes(value.toUpperCase())
174
190
  );
175
191
  } else {
176
- this.itemListForDropdown = this.$props.items;
192
+ state.itemListForDropdown = props.items;
177
193
  }
178
- this.$emit('list-filtered', this.itemListForDropdown);
179
- },
180
- boxWidthCalcul() {
181
- this.$nextTick(() => {
182
- this.boxWidth = document.querySelector('.mc-autocomplete').clientWidth;
183
- console.log(this.boxWidth);
194
+ emit('update:modelValue', state.selected);
195
+ emit('list-filtered', state.itemListForDropdown);
196
+ };
197
+
198
+ const manageTag = () => {
199
+ nextTick(() => {
200
+ state.tagWidth =
201
+ document && document.querySelector('.mc-autocomplete__tag')
202
+ ? document.querySelector('.mc-autocomplete__tag').clientWidth + 'px'
203
+ : '0px';
184
204
  });
185
- return;
186
- },
205
+ };
206
+
207
+ return {
208
+ autocomplete,
209
+ listbox,
210
+ state,
211
+ selectedItems,
212
+ orderedItems,
213
+ boxWidth,
214
+ updateList,
215
+ removeElementsFromList,
216
+ updateDropdown,
217
+ manageTag,
218
+ };
187
219
  },
188
220
  };
189
221
  </script>
@@ -1,41 +1,48 @@
1
1
  <template>
2
2
  <ul
3
- v-if="items.length > 0"
3
+ v-if="state.selectableItems.length > 0"
4
4
  ref="listbox"
5
5
  role="listbox"
6
- class="mc-listbox"
6
+ class="mc-listbox mc-listbox--multi"
7
7
  aria-labelledby="listbox"
8
- :class="{ 'is-open': open, 'mc-listbox--multi': multiple }"
8
+ :class="{ 'is-open': open }"
9
9
  >
10
10
  <li
11
- v-for="item in selectableItems"
12
- :key="item.id"
11
+ v-for="item in state.selectableItems"
12
+ :key="item[dataKeyExpr]"
13
13
  class="mc-listbox__item"
14
- @change="$emit('update:itemSelected', item)"
15
- @click.self="updateList(item.id, item.text, !item.selected, true)"
16
14
  >
17
15
  <slot name="item" :item="item">
18
- <span class="mc-listbox__text">{{ item.text }} </span>
16
+ <label
17
+ :for="`checkbox-dropdown-${item[dataKeyExpr]}-${uuid}`"
18
+ class="mc-listbox__label"
19
+ >{{ item[dataTextExpr] }}
20
+ </label>
19
21
  </slot>
20
22
  <m-checkbox
21
- v-if="multiple"
22
- :id="`checkbox-dropdown-${item.id}`"
23
- v-model="selectableItems.find((elem) => elem.id === item.id).selected"
23
+ :id="`checkbox-dropdown-${item[dataKeyExpr]}-${uuid}`"
24
+ :class="{ hideCheckbox: !multiple }"
24
25
  class="mc-listbox__input"
25
- @change="updateList(item.id, item.text, !item.selected, $e)"
26
+ :checked="updateModelValue(true, item)"
27
+ @update:modelValue="(v) => updateList(v, item[dataValueExpr])"
26
28
  />
27
29
  </li>
28
30
  </ul>
29
- <div v-else class="mc-list-box__empty">
31
+ <div v-else class="mc-listbox__empty">
30
32
  {{ emptySearchLabel }}
31
33
  </div>
32
34
  </template>
35
+
33
36
  <script>
37
+ import { reactive, watch, ref } from '@vue/composition-api';
34
38
  import MCheckbox from '../checkbox/MCheckbox.vue';
39
+
35
40
  export default {
36
41
  name: 'MListbox',
37
42
 
38
- components: { MCheckbox },
43
+ components: {
44
+ MCheckbox,
45
+ },
39
46
 
40
47
  props: {
41
48
  open: {
@@ -58,44 +65,80 @@ export default {
58
65
  type: Boolean,
59
66
  default: false,
60
67
  },
61
- },
62
- data() {
63
- return {
64
- selectableItems: null,
65
- selected: [],
66
- };
67
- },
68
- watch: {
69
- items: {
70
- handler: function (val) {
71
- this.selectableItems = val;
72
- },
73
- immediate: true,
68
+ dataKeyExpr: {
69
+ type: String,
70
+ default: 'id',
71
+ },
72
+ dataTextExpr: {
73
+ type: String,
74
+ default: 'text',
75
+ },
76
+ dataValueExpr: {
77
+ type: String,
78
+ default: 'text',
79
+ },
80
+ modelValue: {
81
+ type: Array,
82
+ default: () => [],
74
83
  },
75
84
  },
76
- methods: {
77
- updateList(id, text, value, isCheckboxUpdate) {
78
- if (!this.multiple) {
79
- this.$emit('update:itemSelected', [{ id, selected: value, text }]);
80
85
 
81
- this.$emit('close-list-box');
86
+ emits: ['update:itemSelected', 'close-list-box', 'update:modelValue'],
87
+
88
+ setup(props, { emit }) {
89
+ const listbox = ref(null);
90
+
91
+ let uuid = Math.random();
92
+
93
+ const state = reactive({
94
+ selectableItems: props.items,
95
+ selected: [],
96
+ });
97
+
98
+ const updateList = (checked, value) => {
99
+ if (!props.multiple) {
100
+ emit('update:modelValue', [{ value }]);
101
+ emit('close-list-box');
82
102
  return;
83
103
  }
84
104
 
85
- if (
86
- isCheckboxUpdate &&
87
- this.selectableItems.find((item) => item.id === id)
88
- ) {
89
- this.selectableItems.find((item) => item.id === id).selected = value;
105
+ if (checked) {
106
+ state.selected = [...state.selected, { [props.dataValueExpr]: value }];
107
+ } else {
108
+ state.selected = state.selected.filter(
109
+ (item) => item[props.dataValueExpr] !== value
110
+ );
90
111
  }
112
+ emit('update:modelValue', state.selected);
113
+ };
91
114
 
92
- if (value) {
93
- this.selected = [...this.selected, { id, selected: value, text }];
115
+ const updateModelValue = (checked, value) => {
116
+ state.selected = props.modelValue;
117
+ if (state.selected) {
118
+ return (
119
+ state.selected.filter(
120
+ (item) => item[props.dataValueExpr] === value[props.dataValueExpr]
121
+ ).length > 0
122
+ );
94
123
  } else {
95
- this.selected = this.selected.filter((item) => item.id !== id);
124
+ return undefined;
96
125
  }
97
- this.$emit('update:itemSelected', this.selectableItems);
98
- },
126
+ };
127
+
128
+ watch(
129
+ () => props.items,
130
+ (nextItems) => {
131
+ state.selectableItems = nextItems;
132
+ }
133
+ );
134
+
135
+ return {
136
+ listbox,
137
+ uuid,
138
+ state,
139
+ updateList,
140
+ updateModelValue,
141
+ };
99
142
  },
100
143
  };
101
144
  </script>