pui9-components 1.16.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.
Files changed (60) hide show
  1. package/README.md +43 -0
  2. package/dist/demo.html +10 -0
  3. package/dist/pui9-components.common.js +81953 -0
  4. package/dist/pui9-components.common.js.map +1 -0
  5. package/dist/pui9-components.css +5 -0
  6. package/dist/pui9-components.umd.js +81963 -0
  7. package/dist/pui9-components.umd.js.map +1 -0
  8. package/dist/pui9-components.umd.min.js +308 -0
  9. package/dist/pui9-components.umd.min.js.map +1 -0
  10. package/package-lock.json +15945 -0
  11. package/package.json +78 -0
  12. package/src/App.vue +117 -0
  13. package/src/components/PuiCheckbox.vue +105 -0
  14. package/src/components/PuiCodeEditor.vue +123 -0
  15. package/src/components/PuiDateField.vue +1004 -0
  16. package/src/components/PuiField.vue +30 -0
  17. package/src/components/PuiFieldSet.vue +27 -0
  18. package/src/components/PuiFormFooter.vue +64 -0
  19. package/src/components/PuiFormFooterBtns.vue +118 -0
  20. package/src/components/PuiFormHeader.vue +25 -0
  21. package/src/components/PuiFormLoading.vue +12 -0
  22. package/src/components/PuiFormMiniAudit.vue +53 -0
  23. package/src/components/PuiMasterDetail.vue +96 -0
  24. package/src/components/PuiModalDialog.vue +87 -0
  25. package/src/components/PuiModalDialogForm.vue +205 -0
  26. package/src/components/PuiMultiSelect.vue +499 -0
  27. package/src/components/PuiNumberField.vue +503 -0
  28. package/src/components/PuiPasswordField.vue +105 -0
  29. package/src/components/PuiRadioGroup.vue +105 -0
  30. package/src/components/PuiRichTextEditor.vue +117 -0
  31. package/src/components/PuiSelect.vue +1638 -0
  32. package/src/components/PuiSelectDetailDialog.vue +106 -0
  33. package/src/components/PuiSelectTextService.vue +61 -0
  34. package/src/components/PuiSpinnerField.vue +484 -0
  35. package/src/components/PuiSwitch.vue +104 -0
  36. package/src/components/PuiTextArea.vue +203 -0
  37. package/src/components/PuiTextField.vue +272 -0
  38. package/src/dateTimeUtils.js +78 -0
  39. package/src/index.js +73 -0
  40. package/src/main.js +33 -0
  41. package/src/mixins/PuiFormComponentMixin.js +81 -0
  42. package/src/mixins/PuiMultiSelectMixin.js +106 -0
  43. package/src/mixins/PuiUtilsNumberMixin.js +19 -0
  44. package/src/plugins/vuetify.js +32 -0
  45. package/src/tests/TestAutocomplete.vue +138 -0
  46. package/src/tests/TestCodeEditor.vue +48 -0
  47. package/src/tests/TestField.vue +22 -0
  48. package/src/tests/TestFieldSet.vue +30 -0
  49. package/src/tests/TestInputCheckbox.vue +53 -0
  50. package/src/tests/TestInputDate.vue +146 -0
  51. package/src/tests/TestInputNumber.vue +77 -0
  52. package/src/tests/TestInputRadioGroup.vue +86 -0
  53. package/src/tests/TestInputSpinner.vue +77 -0
  54. package/src/tests/TestInputSwitch.vue +52 -0
  55. package/src/tests/TestInputText.vue +120 -0
  56. package/src/tests/TestInputTextArea.vue +73 -0
  57. package/src/tests/TestMultiSelect.vue +127 -0
  58. package/src/tests/TestPuiForm.vue +68 -0
  59. package/src/tests/TestRichTextEditor.vue +54 -0
  60. package/src/utils.js +148 -0
@@ -0,0 +1,205 @@
1
+ <template>
2
+ <v-layout wrap v-if="showDialog">
3
+ <v-flex xs10 md8 lg6 xl4>
4
+ <div class="text-center">
5
+ <v-dialog
6
+ :id="`${dialogName}-modal-container`"
7
+ persistent
8
+ v-model="showDialog"
9
+ :width="widthDialog"
10
+ :content-class="getDialogClasses"
11
+ v-bind="allProps"
12
+ >
13
+ <v-card class="pa-2">
14
+ <v-card-title class="pl-3 headline lighten-2" primary-title>{{ titleText }}</v-card-title>
15
+ <v-card-text>
16
+ <pui-form-header v-if="componentHeader && modalData.headerPk" showHeader>
17
+ <component :is="componentHeader" :modelPk="modalData.headerPk" />
18
+ </pui-form-header>
19
+ <v-form action ref="validateForm" class="px-4" @submit.prevent>
20
+ <slot name="message" v-bind:modalData="modalData"></slot>
21
+ </v-form>
22
+ </v-card-text>
23
+ <v-card-actions ref="actions">
24
+ <v-spacer></v-spacer>
25
+ <v-btn v-if="!disableCancel" :id="`${dialogName}-btn-cancel`" depressed @click="cancel()" :disabled="loading">{{
26
+ buttonCancel
27
+ }}</v-btn>
28
+ <v-btn color="primary" :id="`${dialogName}-btn-ok`" depressed @click="ok()" :loading="loading" :disabled="loading">{{
29
+ buttonOk
30
+ }}</v-btn>
31
+ </v-card-actions>
32
+ </v-card>
33
+ </v-dialog>
34
+ </div>
35
+ </v-flex>
36
+ </v-layout>
37
+ </template>
38
+
39
+ <script>
40
+ export default {
41
+ name: 'PuiModalDialogForm',
42
+ props: {
43
+ value: {
44
+ type: Object
45
+ },
46
+ titleText: {
47
+ type: String,
48
+ required: true
49
+ },
50
+ modelName: {
51
+ type: String,
52
+ required: true
53
+ },
54
+ dialogName: {
55
+ type: String,
56
+ required: true
57
+ },
58
+ dialogClasses: {
59
+ type: String,
60
+ default: ''
61
+ },
62
+ overflow: {
63
+ type: Boolean,
64
+ default: true
65
+ },
66
+ okText: {
67
+ type: String
68
+ },
69
+ cancelText: {
70
+ type: String
71
+ },
72
+ disableCancel: {
73
+ type: Boolean,
74
+ default: false
75
+ },
76
+ widthDialog: {
77
+ type: [String, Number],
78
+ default: 'unset'
79
+ },
80
+ onShow: {
81
+ type: Function,
82
+ default: null
83
+ },
84
+ componentHeaderName: {
85
+ type: String
86
+ },
87
+ showDialogProp: {
88
+ type: Boolean,
89
+ default: false
90
+ },
91
+ onOk: {
92
+ type: Function,
93
+ default: null
94
+ },
95
+ onCancel: {
96
+ type: Function,
97
+ default: null
98
+ }
99
+ },
100
+ data() {
101
+ return {
102
+ modalData: {},
103
+ componentHeader: null,
104
+ headerPk: {},
105
+ showDialog: false,
106
+ loading: false
107
+ };
108
+ },
109
+ methods: {
110
+ ok() {
111
+ if (this.disableCancel || (!this.loading && this.$refs.validateForm !== undefined && this.$refs.validateForm.validate())) {
112
+ if (this.onOk) {
113
+ this.loading = true;
114
+ // onOk Function handles show/hide dialog on return value
115
+ const returnOnOk = this.onOk(this.modalData);
116
+ if (returnOnOk !== undefined) {
117
+ returnOnOk.then((value) => {
118
+ this.showDialog = !value;
119
+ this.loading = false;
120
+ });
121
+ } else {
122
+ this.loading = false;
123
+ }
124
+ } else {
125
+ if (this.modalData.isAction) {
126
+ this.$puiEvents.$emit(`onPui-action-running-ended-${this.modelName}`);
127
+ }
128
+ this.showDialog = false;
129
+ }
130
+ }
131
+ },
132
+ cancel() {
133
+ if (this.onCancel) {
134
+ // onCancel Function handles show/hide dialog on return value
135
+ const returnOnCancel = this.onCancel(this.modalData);
136
+ if (returnOnCancel !== undefined) this.showDialog = !returnOnCancel;
137
+ } else {
138
+ this.showDialog = false;
139
+ }
140
+ if (this.modalData.isAction) {
141
+ this.$puiEvents.$emit(`onPui-action-running-ended-${this.modelName}`);
142
+ }
143
+ this.modalData = {};
144
+ },
145
+ loaderDynamicHeader() {
146
+ if (this.headerPk && this.componentHeaderName) {
147
+ this.componentHeader = this.componentHeaderName;
148
+ }
149
+ }
150
+ },
151
+ computed: {
152
+ getOverflow() {
153
+ return this.overflow ? '' : 'pui-modal-dialog-overflow-off';
154
+ },
155
+ buttonOk() {
156
+ return this.okText || this.$t('pui9.accept');
157
+ },
158
+ buttonCancel() {
159
+ return this.cancelText || this.$t('pui9.cancel');
160
+ },
161
+ getDialogClasses() {
162
+ return this.dialogClasses + ' ' + this.getOverflow;
163
+ },
164
+ allProps() {
165
+ return { ...this.$attrs, ...this.$props };
166
+ }
167
+ },
168
+ mounted() {
169
+ this.loaderDynamicHeader();
170
+ this.$puiEvents.$on(`pui-modalDialogForm-${this.dialogName}-${this.modelName}-show`, (data) => {
171
+ // Clean posible oldData
172
+ this.modalData = {};
173
+ for (const key in data) {
174
+ if (Object.prototype.hasOwnProperty.call(data, key)) {
175
+ // For make the container of the data reactive we use $set vue function
176
+ this.$set(this.modalData, key, data[key]);
177
+ }
178
+ }
179
+ // If not loaded from action, disable action events for transfer data, disable styles action button...
180
+ this.modalData.isAction = data.isAction ? data.isAction : false;
181
+ if (data.showDialog === undefined || data.showDialog === true) this.showDialog = true;
182
+ this.onShow && this.onShow(this.modalData);
183
+ });
184
+ },
185
+ destroyed() {
186
+ this.$puiEvents.$off(`pui-modalDialogForm-${this.dialogName}-${this.modelName}-show`);
187
+ },
188
+ watch: {
189
+ // If some data change from the modalData $emit it in case that they need on the parent component
190
+ modalData: {
191
+ handler(newValue) {
192
+ if (newValue.showDialog !== undefined) {
193
+ this.showDialog = newValue.showDialog;
194
+ }
195
+ this.$emit('input', newValue);
196
+ },
197
+ deep: true
198
+ },
199
+ showDialogProp(newValue) {
200
+ this.loading = false;
201
+ this.showDialog = newValue;
202
+ }
203
+ }
204
+ };
205
+ </script>
@@ -0,0 +1,499 @@
1
+ <template>
2
+ <v-layout wrap>
3
+ <v-flex xs12>
4
+ <v-card :flat="flat" elevation="0">
5
+ <div :class="{ 'pui-multiselect--flat': flat, 'pui-multiselect--disabled': disabled }">
6
+ <v-layout wrap>
7
+ <v-flex xs12 sm6>
8
+ <!-- slot1, TODO MAKE SLOTS IN FILTER AND SORTING DIALOGS-->
9
+ <v-flex xs12 class="pt-2">
10
+ <span class="pui-dialog__title pui-dialog__title--margin">
11
+ {{ $t(textAvailable) }}
12
+ </span>
13
+ </v-flex>
14
+ <v-flex xs12 class="pui-multiselect__searchpanel">
15
+ <pui-text-field
16
+ :placeholder="labelSearch"
17
+ v-model="searchAvailable"
18
+ clearable
19
+ prepend-inner-icon="far fa-search"
20
+ noeditable
21
+ realtime
22
+ ></pui-text-field>
23
+ </v-flex>
24
+ <v-flex xs12>
25
+ <v-list
26
+ :style="styleList"
27
+ class="overflow-y-auto pui-multiselect__itemListContainer pui-multiselect__itemListContainer-available"
28
+ >
29
+ <draggable
30
+ v-model="availableItems"
31
+ v-bind="dragOptions"
32
+ @change="onChangeAvailables"
33
+ @end="
34
+ searchSelected = '';
35
+ setSelectedItems();
36
+ "
37
+ class="pui-multiselect__item-draggable"
38
+ :class="{ 'pui-multiselect__item--tall': itemsSearchAvailable.length === 0 }"
39
+ >
40
+ <template v-for="(item, index) in itemsSearchAvailable">
41
+ <!-- He quitado esta líea del v-layout de abajo porque hacía de un tamaño muy grande al elemento
42
+ :class="{'pui-multiselect__item--tall': itemsSearchAvailable.length < 4 && index === itemsSearchAvailable.length -1}"
43
+ -->
44
+ <v-layout :key="item[itemValue]" class="pui-multiselect__item">
45
+ <v-flex xs1 d-flex align-center>
46
+ <v-list-item class="move draggable_point">
47
+ <v-icon small>far fa-ellipsis-v</v-icon>
48
+ <v-icon small>far fa-ellipsis-v</v-icon>
49
+ </v-list-item>
50
+ </v-flex>
51
+ <v-flex xs11>
52
+ <v-list-item class>
53
+ <v-list-item-content @dblclick="selectItemFromEvent(index, item)">
54
+ <v-list-item-title
55
+ class="pui-multiselect--title"
56
+ v-html="item[itemText]"
57
+ :title="item[itemText]"
58
+ ></v-list-item-title>
59
+ <v-list-item-subtitle
60
+ v-if="itemDescription"
61
+ v-html="item[itemDescription]"
62
+ ></v-list-item-subtitle>
63
+ </v-list-item-content>
64
+ <v-list-item-action>
65
+ <v-btn
66
+ small
67
+ icon
68
+ ripple
69
+ @click.native="selectItemFromEvent(index, item)"
70
+ :disabled="disabled"
71
+ >
72
+ <v-icon>far fa-plus</v-icon>
73
+ </v-btn>
74
+ </v-list-item-action>
75
+ </v-list-item>
76
+ </v-flex>
77
+ </v-layout>
78
+ </template>
79
+ </draggable>
80
+ <v-card v-show="availableItems.length === 0" flat class="pui-multiselect__itemListContainer-cardHelper pl-3 pr-3">
81
+ <div class="pui-multiselect__itemListContainer-cardHelper-text pb-1 pl-2 pr-2">
82
+ <span class="pui-multiselect__itemListContainer-cardHelper-text-title pb-3">
83
+ {{ $t('pui9.components.multiSelect.nothing_to_select') }}
84
+ </span>
85
+ <span class="pui-multiselect__itemListContainer-cardHelper-text-info">
86
+ {{ $t('pui9.components.multiSelect.multiselect_help_to_unselect') }}
87
+ </span>
88
+ </div>
89
+ </v-card>
90
+ </v-list>
91
+ </v-flex>
92
+ </v-flex>
93
+ <v-flex xs12 sm6>
94
+ <v-layout wrap>
95
+ <v-flex xs12 class="pt-2">
96
+ <span class="pui-dialog__title pui-dialog__title--margin">
97
+ {{ $t(textSelected) }}
98
+ </span>
99
+ </v-flex>
100
+ <v-flex xs12 class="pui-multiselect__searchpanel">
101
+ <pui-text-field
102
+ :placeholder="labelSearch"
103
+ v-model="searchSelected"
104
+ clearable
105
+ prepend-inner-icon="far fa-search"
106
+ noeditable
107
+ realtime
108
+ ></pui-text-field>
109
+ </v-flex>
110
+ <v-flex xs12>
111
+ <v-list
112
+ :style="styleList"
113
+ :two-line="itemDescription !== null"
114
+ class="overflow-y-auto pui-multiselect__itemListContainer"
115
+ :class="compClassItemListSelected"
116
+ >
117
+ <draggable
118
+ v-model="selectedItems"
119
+ v-bind="dragOptions"
120
+ @change="onChangeSelected"
121
+ @end="
122
+ searchAvailable = '';
123
+ setSelectedItems();
124
+ "
125
+ class="pui-multiselect__item-draggable"
126
+ :class="{ 'pui-multiselect__item--tall': itemsSearchSelected.length === 0 }"
127
+ >
128
+ <template v-for="(item, index) in itemsSearchSelected">
129
+ <!-- He quitado esta líea del v-layout de abajo porque hacía de un tamaño muy grande al elemento
130
+ :class="{'pui-multiselect__item--tall': itemsSearchSelected.length < 4 && index === itemsSearchSelected.length -1}"
131
+ -->
132
+ <v-layout :key="item[itemValue]" class="pui-multiselect__item">
133
+ <v-flex xs1 d-flex align-center>
134
+ <v-list-item class="move draggable_point">
135
+ <v-icon small>far fa-ellipsis-v</v-icon>
136
+ <v-icon small>far fa-ellipsis-v</v-icon>
137
+ </v-list-item>
138
+ </v-flex>
139
+ <v-flex xs11>
140
+ <v-list-item>
141
+ <v-list-item-content @dblclick="unselectItemFromEvent(index, item)">
142
+ <v-list-item-title
143
+ class="pui-multiselect--title"
144
+ v-html="item[itemText]"
145
+ :title="item[itemText]"
146
+ ></v-list-item-title>
147
+ <v-list-item-subtitle
148
+ v-if="itemDescription"
149
+ v-html="item[itemDescription]"
150
+ ></v-list-item-subtitle>
151
+ </v-list-item-content>
152
+ <v-list-item-action>
153
+ <v-btn
154
+ small
155
+ icon
156
+ ripple
157
+ @click.native="unselectItemFromEvent(index, item)"
158
+ :disabled="disabled"
159
+ >
160
+ <v-icon>far fa-times</v-icon>
161
+ </v-btn>
162
+ </v-list-item-action>
163
+ </v-list-item>
164
+ </v-flex>
165
+ </v-layout>
166
+ </template>
167
+ </draggable>
168
+ <v-card
169
+ v-show="selectedItems.length === 0"
170
+ flat
171
+ class="pui9-grey pui-multiselect__itemListContainer-cardHelper pl-3 pr-3"
172
+ >
173
+ <div class="pui-multiselect__itemListContainer-cardHelper-text pb-1 pl-2 pr-2">
174
+ <span class="pui-multiselect__itemListContainer-cardHelper-text-title pb-3">
175
+ {{ $t('pui9.components.multiSelect.nothing_selected') }}
176
+ </span>
177
+ <span class="pui-multiselect__itemListContainer-cardHelper-text-info">
178
+ {{ $t('pui9.components.multiSelect.multiselect_help') }}
179
+ </span>
180
+ </div>
181
+ </v-card>
182
+ </v-list>
183
+ </v-flex>
184
+ </v-layout>
185
+ </v-flex>
186
+ </v-layout>
187
+ <v-card-actions class="ma-0 pb-2 pr-2 pl-2">
188
+ <v-btn
189
+ text
190
+ outlined
191
+ :disabled="disabled || availableItems.length === 0"
192
+ class="puiToolbar-dialog__button"
193
+ @click.native="selectAllItems()"
194
+ >{{ $t('pui9.components.multiSelect.add_all') }}</v-btn
195
+ >
196
+ <v-spacer></v-spacer>
197
+ <span class="pr-2" v-show="selectedItems.length === 1">{{
198
+ `${selectedItems.length} ${$t('pui9.components.multiSelect.selected_item')}`
199
+ }}</span>
200
+ <span class="pr-2" v-show="selectedItems.length > 1">{{
201
+ `${selectedItems.length} ${$t('pui9.components.multiSelect.selected_items')}`
202
+ }}</span>
203
+ <v-btn
204
+ text
205
+ outlined
206
+ :disabled="disabled || selectedItems.length === 0"
207
+ @click.native="unselectAllItems()"
208
+ class="puiToolbar-dialog__button"
209
+ >{{ $t('pui9.components.multiSelect.empty') }}</v-btn
210
+ >
211
+ </v-card-actions>
212
+ </div>
213
+ </v-card>
214
+ </v-flex>
215
+ </v-layout>
216
+ </template>
217
+
218
+ <script>
219
+ import PuiMultiSelectMixin from '../mixins/PuiMultiSelectMixin';
220
+ import PuiTextField from './PuiTextField';
221
+ import draggable from 'vuedraggable';
222
+
223
+ export default {
224
+ name: 'PuiMultiSelect',
225
+ mixins: [PuiMultiSelectMixin],
226
+ components: {
227
+ PuiTextField,
228
+ draggable
229
+ },
230
+ /**
231
+ * !important there are more properties in the selectMixin:
232
+ * items,itemsToSelect,itemText,itemValue
233
+ */
234
+ props: {
235
+ flat: {
236
+ default: false
237
+ },
238
+ heightList: {
239
+ type: Number,
240
+ default: 300
241
+ },
242
+ itemDescription: {
243
+ type: String,
244
+ default: null
245
+ },
246
+ /**
247
+ * El texto por traducir que se muestra como titulo de los items disponibles
248
+ * Por defecto disponibles
249
+ */
250
+ textAvailable: {
251
+ type: String,
252
+ default: 'pui9.components.multiSelect.available'
253
+ },
254
+ /**
255
+ * El texto por traducir que se muestra como titulo de los items seleccionados
256
+ * Por defecto seleccionados
257
+ */
258
+ textSelected: {
259
+ type: String,
260
+ default: 'pui9.components.multiSelect.selected'
261
+ },
262
+ disabled: {
263
+ type: Boolean,
264
+ default: false
265
+ }
266
+ },
267
+ data() {
268
+ return {
269
+ searchAvailable: '',
270
+ searchSelected: '',
271
+ searchingAvailable: true,
272
+ searchingSelected: true,
273
+ dragOptions: {
274
+ group: 'multiselect',
275
+ disabled: this.disabled
276
+ },
277
+ selectedItems: [],
278
+ availableItems: []
279
+ };
280
+ },
281
+ updated() {
282
+ this.searchingAvailable = false;
283
+ this.searchingSelected = false;
284
+ },
285
+ computed: {
286
+ compClassItemListSelected() {
287
+ if (!this.selectedItems) {
288
+ return {
289
+ 'pui9-grey': true,
290
+ 'pui-multiselect__itemListContainer-selected': true
291
+ };
292
+ }
293
+ return {
294
+ 'pui9-grey': this.selectedItems.length === 0,
295
+ 'pui-multiselect__itemListContainer-selected': this.selectedItems.length === 0
296
+ };
297
+ },
298
+ itemsSearchAvailable() {
299
+ if (!this.searchAvailable) {
300
+ return this.availableItems;
301
+ }
302
+ //this.searchingAvailable = true;
303
+ return this.availableItems
304
+ .filter((item) => {
305
+ try {
306
+ const match =
307
+ item[this.itemText].toUpperCase().includes(this.searchAvailable.toUpperCase()) ||
308
+ (this.itemDescription && item[this.itemDescription].toUpperCase().includes(this.searchAvailable.toUpperCase()));
309
+ return match;
310
+ } catch (e) {
311
+ //al item le falta el texto o la descripción
312
+ return false;
313
+ }
314
+ })
315
+ .sort((a, b) => {
316
+ const aValue = a[this.itemText];
317
+ const bValue = b[this.itemText];
318
+ return aValue.localeCompare(bValue);
319
+ });
320
+ //this.searchingAvailable = false;
321
+ },
322
+ itemsSearchSelected() {
323
+ if (!this.searchSelected) {
324
+ return this.selectedItems;
325
+ }
326
+ //this.searchingSelected = true;
327
+ return this.selectedItems
328
+ .filter((item) => {
329
+ try {
330
+ const match =
331
+ item[this.itemText].toUpperCase().includes(this.searchSelected.toUpperCase()) ||
332
+ (this.itemDescription && item[this.itemDescription].toUpperCase().includes(this.searchSelected.toUpperCase()));
333
+ return match;
334
+ } catch (e) {
335
+ //al item le falta el texto o la descripción
336
+ return false;
337
+ }
338
+ })
339
+ .sort((a, b) => {
340
+ const aValue = a[this.itemText];
341
+ const bValue = b[this.itemText];
342
+ return aValue.localeCompare(bValue);
343
+ });
344
+ //this.searchingSelected = false;
345
+ },
346
+ styleList() {
347
+ return `height: ${this.heightList.toString()}px`;
348
+ },
349
+ labelSearch() {
350
+ return this.$t('pui9.components.multiSelect.search');
351
+ }
352
+ },
353
+ methods: {
354
+ setAvailableItems() {
355
+ this.availableItems = [];
356
+ this.items
357
+ .sort((item1, item2) => {
358
+ if (item1[this.itemText] > item2[this.itemText]) {
359
+ return 1;
360
+ } else if (item1[this.itemText] < item2[this.itemText]) {
361
+ return -1;
362
+ }
363
+ return 0;
364
+ })
365
+ .forEach((item) => this.availableItems.push(item));
366
+ },
367
+ selectItem(item) {
368
+ this.selectedItems.push(item);
369
+ this.availableItems.forEach((availableItem, index) => {
370
+ if (item[this.itemValue] === availableItem[this.itemValue]) {
371
+ this.availableItems.splice(index, 1);
372
+ }
373
+ });
374
+ this.$emit('input', this.selectedItems);
375
+ },
376
+ selectItemById(id, theItem) {
377
+ this.selectedItems.push(theItem);
378
+ for (let index = 0, length = this.availableItems.length; index < length; index++) {
379
+ const availableItem = this.availableItems[index];
380
+ if (availableItem[this.itemValue] === id) {
381
+ //comprobamos por referencia que el item y el availableItem no es lo mismo,
382
+ //en este caso hay que theItem que se acaba de añadir 4 lineas de código atras, y añadimos el bueno
383
+ //esto significa que theItem solo lleva los campos de la pk y no es el item "entero"
384
+ if (availableItem !== theItem) {
385
+ this.selectedItems.pop();
386
+ this.selectedItems.push(availableItem);
387
+ }
388
+ this.availableItems.splice(index, 1);
389
+ return;
390
+ }
391
+ }
392
+ },
393
+ setSelectedItems() {
394
+ this.$emit('input', this.selectedItems);
395
+ },
396
+ unselectItem(item) {
397
+ this.availableItems.push(item);
398
+ this.selectedItems.forEach((selectedItem, index) => {
399
+ if (item[this.itemValue] === selectedItem[this.itemValue]) {
400
+ this.selectedItems.splice(index, 1);
401
+ }
402
+ });
403
+ this.$emit('input', this.selectedItems);
404
+ },
405
+ selectItemFromEvent(index, item) {
406
+ if (this.disabled) {
407
+ return;
408
+ }
409
+ this.searchSelected = '';
410
+ if (this.searchAvailable) {
411
+ this.selectItem(item);
412
+ return;
413
+ }
414
+ this.availableItems.splice(index, 1);
415
+ this.selectedItems.push(item);
416
+ this.$emit('input', this.selectedItems);
417
+ },
418
+ unselectItemFromEvent(index, item) {
419
+ if (this.disabled) {
420
+ return;
421
+ }
422
+ this.searchAvailable = '';
423
+ if (this.searchSelected) {
424
+ this.unselectItem(item);
425
+ return;
426
+ }
427
+ this.selectedItems.splice(index, 1);
428
+ this.availableItems.push(item);
429
+ this.$emit('input', this.selectedItems);
430
+ },
431
+ selectAllItems() {
432
+ this.searchSelected = '';
433
+ this.searchingAvailable = true;
434
+ this.searchingSelected = true;
435
+ this.selectedItems = this.selectedItems.concat(this.availableItems);
436
+ this.availableItems = [];
437
+ this.$emit('input', this.selectedItems);
438
+ },
439
+ unselectAllItems() {
440
+ this.searchingAvailable = true;
441
+ this.searchingSelected = true;
442
+ this.searchAvailable = '';
443
+ this.availableItems = this.availableItems.concat(this.selectedItems);
444
+ this.selectedItems = [];
445
+ this.$emit('input', this.selectedItems);
446
+ },
447
+ onChangeAvailables(event) {
448
+ // due to drag and drop, if searchAvailable has value, the item selected by the user is not the item added to
449
+ // selectedItems so:
450
+ // - it has to be removed manually from selectedItems,
451
+ // - it has to be added to the availableItems one more time,
452
+ // - selected item by the user that is in itemsSearchAvailable, has to be added to selectedItems
453
+ // - and selected item by the user has to be removed from availableItems
454
+ if (event.removed && this.itemsSearchAvailable.length > 0 && this.itemsSearchAvailable.length < this.availableItems.length) {
455
+ // remove last from selectedItems because it is not the selected item by user
456
+ const removed = event.removed.element;
457
+ let removedIndex = this.selectedItems.length;
458
+ this.selectedItems.forEach((selectedItem, index) => {
459
+ if (removed[this.itemValue] === selectedItem[this.itemValue]) {
460
+ removedIndex = index;
461
+ this.selectedItems.splice(index, 1);
462
+ }
463
+ });
464
+ // push removed item in availableItems
465
+ this.availableItems.push(removed);
466
+ // add selected item by user to the selectedItems in the same position as removed previously
467
+ const added = this.itemsSearchAvailable[event.removed.oldIndex];
468
+ this.selectedItems.splice(removedIndex, 0, added);
469
+ // remove selected item from availableItems
470
+ this.availableItems.forEach((availableItem, index) => {
471
+ if (added[this.itemValue] === availableItem[this.itemValue]) {
472
+ this.availableItems.splice(index, 1);
473
+ }
474
+ });
475
+ }
476
+ },
477
+ onChangeSelected(event) {
478
+ if (event.removed && this.itemsSearchSelected.length > 0 && this.itemsSearchSelected.length < this.selectedItems.length) {
479
+ const removed = event.removed.element;
480
+ let removedIndex = this.availableItems.length;
481
+ this.availableItems.forEach((availableItem, index) => {
482
+ if (removed[this.itemValue] === availableItem[this.itemValue]) {
483
+ removedIndex = index;
484
+ this.availableItems.splice(index, 1);
485
+ }
486
+ });
487
+ this.selectedItems.splice(event.removed.oldIndex, 0, removed);
488
+ const added = this.itemsSearchSelected[event.removed.oldIndex];
489
+ this.availableItems.splice(removedIndex, 0, added);
490
+ this.selectedItems.forEach((selectedItem, index) => {
491
+ if (added[this.itemValue] === selectedItem[this.itemValue]) {
492
+ this.selectedItems.splice(index, 1);
493
+ }
494
+ });
495
+ }
496
+ }
497
+ }
498
+ };
499
+ </script>