pui9-components 1.17.3 → 2.0.7

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 (43) hide show
  1. package/dist/pui9-components.common.js +75007 -52512
  2. package/dist/pui9-components.css +9 -2
  3. package/package-lock.json +6651 -6827
  4. package/package.json +22 -26
  5. package/src/components/PuiCauDialog.vue +192 -0
  6. package/src/components/PuiCheckbox.vue +25 -4
  7. package/src/components/PuiCodeEditor.vue +2 -1
  8. package/src/components/PuiDateField.vue +653 -614
  9. package/src/components/PuiField.vue +1 -1
  10. package/src/components/PuiFileUpload.vue +275 -0
  11. package/src/components/PuiFileUploadGroup.vue +241 -0
  12. package/src/components/PuiFilter.vue +54 -52
  13. package/src/components/PuiFilterGroup.vue +179 -179
  14. package/src/components/PuiFilterRule.vue +481 -162
  15. package/src/components/PuiFormFooter.vue +5 -38
  16. package/src/components/PuiFormTooltip.vue +50 -0
  17. package/src/components/PuiMasterDetail.vue +18 -11
  18. package/src/components/PuiModalDialog.vue +4 -1
  19. package/src/components/PuiModalDialogForm.vue +5 -1
  20. package/src/components/PuiMultiSelect.vue +383 -280
  21. package/src/components/PuiNumberField.vue +216 -285
  22. package/src/components/PuiRichTextEditor.vue +43 -44
  23. package/src/components/PuiSelect.vue +382 -355
  24. package/src/components/PuiSelectDetailDialog.vue +10 -3
  25. package/src/components/PuiSelectorList.vue +169 -0
  26. package/src/components/PuiSort.vue +98 -0
  27. package/src/components/PuiSpinnerField.vue +101 -121
  28. package/src/components/PuiTextField.vue +374 -258
  29. package/src/index.js +8 -0
  30. package/src/main.js +1 -0
  31. package/src/mixins/PuiFormComponentMixin.js +2 -8
  32. package/src/mixins/PuiSortMixin.js +136 -0
  33. package/src/mixins/PuiUtilsNumberMixin.js +15 -5
  34. package/src/plugins/vuetify.js +2 -1
  35. package/src/utils.js +10 -0
  36. package/dist/demo.html +0 -10
  37. package/dist/pui9-components.common.js.map +0 -1
  38. package/dist/pui9-components.umd.js +0 -86432
  39. package/dist/pui9-components.umd.js.map +0 -1
  40. package/dist/pui9-components.umd.min.js +0 -308
  41. package/dist/pui9-components.umd.min.js.map +0 -1
  42. package/src/mixins/PuiFilterMixin.js +0 -157
  43. package/src/mixins/PuiMultiSelectMixin.js +0 -106
@@ -2,95 +2,78 @@
2
2
  <v-layout wrap class="pui-multiselect">
3
3
  <v-flex xs12>
4
4
  <v-card :flat="flat" elevation="0">
5
- <div :class="{ 'pui-multiselect--flat': flat, 'pui-multiselect--disabled': disabled }">
5
+ <v-container class="pb-2 mb-n2" :class="{ 'pui-multiselect--flat': flat, 'pui-multiselect--disabled': disabled }">
6
6
  <v-layout wrap>
7
7
  <v-flex xs12 sm6>
8
8
  <!-- slot1, TODO MAKE SLOTS IN FILTER AND SORTING DIALOGS-->
9
- <v-flex xs12 class="pt-2">
9
+ <v-flex xs12 class="pa-2 pt-0 pl-1">
10
10
  <span class="pui-dialog__title pui-dialog__title--margin">
11
11
  {{ $t(textAvailable) }}
12
12
  </span>
13
13
  </v-flex>
14
14
  <v-flex xs12 class="pui-multiselect__searchpanel">
15
15
  <pui-text-field
16
- :placeholder="labelSearch"
17
- v-model="searchAvailable"
16
+ :placeholder="$t('pui9.components.multiSelect.search')"
17
+ v-model="searchTextAvailableItems"
18
18
  clearable
19
19
  prepend-inner-icon="far fa-search"
20
20
  noeditable
21
21
  realtime
22
22
  ></pui-text-field>
23
23
  </v-flex>
24
- <v-flex xs12>
24
+ <v-flex xs12 class="py-0 px-1">
25
25
  <v-list
26
- :style="styleList"
27
- class="overflow-y-auto pui-multiselect__itemListContainer pui-multiselect__itemListContainer-available"
26
+ :style="`height: ${heightList}px`"
27
+ :two-line="itemDescription !== null"
28
+ class="overflow-y-auto pui-multiselect__itemListContainer"
28
29
  >
29
30
  <draggable
30
31
  v-model="availableItems"
31
32
  v-bind="dragOptions"
32
- @change="onChangeAvailables"
33
- @end="
34
- searchSelected = '';
35
- setSelectedItems();
36
- "
33
+ @change="onChangeAvailableItems"
37
34
  class="pui-multiselect__item-draggable"
38
- :class="{ 'pui-multiselect__item--tall': itemsSearchAvailable.length === 0 }"
35
+ :class="{ 'pui-multiselect__item--tall': filteredAvailableItems.length === 0 }"
39
36
  >
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
- x-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-tooltip top v-if="itemTooltip">
75
- <template v-slot:activator="{ on, attrs }">
76
- <v-btn
77
- x-small
78
- icon
79
- ripple
80
- v-bind="attrs"
81
- v-on="on"
82
- :disabled="disabled"
83
- >
84
- <v-icon>far fa-info-circle</v-icon>
85
- </v-btn>
86
- </template>
87
- <span v-html="item[itemTooltip]"/>
88
- </v-tooltip>
89
- </v-list-item-action>
90
- </v-list-item>
91
- </v-flex>
92
- </v-layout>
93
- </template>
37
+ <v-layout
38
+ v-for="(item, index) in filteredAvailableItems"
39
+ :key="`${index}__${item.itemText}__${item.itemValue}`"
40
+ class="pui-multiselect__item"
41
+ >
42
+ <v-flex xs1 d-flex align-center>
43
+ <v-list-item class="move draggable_point">
44
+ <v-icon small>far fa-ellipsis-v</v-icon>
45
+ <v-icon small>far fa-ellipsis-v</v-icon>
46
+ </v-list-item>
47
+ </v-flex>
48
+ <v-flex xs11>
49
+ <v-list-item class>
50
+ <v-list-item-content @dblclick="selectItemFromClick(item)">
51
+ <v-list-item-title
52
+ class="pui-multiselect--title"
53
+ v-html="item[itemText]"
54
+ :title="item[itemText]"
55
+ ></v-list-item-title>
56
+ <v-list-item-subtitle
57
+ v-if="itemDescription"
58
+ v-html="item[itemDescription]"
59
+ ></v-list-item-subtitle>
60
+ </v-list-item-content>
61
+ <v-list-item-action>
62
+ <v-btn x-small icon ripple @click.native="selectItemFromClick(item)" :disabled="disabled">
63
+ <v-icon>far fa-plus</v-icon>
64
+ </v-btn>
65
+ <v-tooltip class="overflow-y-hidden" max-width="30%" max-height="30%" top v-if="itemTooltip">
66
+ <template v-slot:activator="{ on, attrs }">
67
+ <v-btn x-small icon ripple v-bind="attrs" v-on="on" :disabled="disabled">
68
+ <v-icon>far fa-info-circle</v-icon>
69
+ </v-btn>
70
+ </template>
71
+ <div class="tooltipText">{{ itemTooltip }}</div>
72
+ </v-tooltip>
73
+ </v-list-item-action>
74
+ </v-list-item>
75
+ </v-flex>
76
+ </v-layout>
94
77
  </draggable>
95
78
  <v-card v-show="availableItems.length === 0" flat class="pui-multiselect__itemListContainer-cardHelper pl-3 pr-3">
96
79
  <div class="pui-multiselect__itemListContainer-cardHelper-text pb-1 pl-2 pr-2">
@@ -107,24 +90,24 @@
107
90
  </v-flex>
108
91
  <v-flex xs12 sm6>
109
92
  <v-layout wrap>
110
- <v-flex xs12 class="pt-2">
93
+ <v-flex xs12 class="pa-2 pt-0 pl-1">
111
94
  <span class="pui-dialog__title pui-dialog__title--margin">
112
95
  {{ $t(textSelected) }}
113
96
  </span>
114
97
  </v-flex>
115
98
  <v-flex xs12 class="pui-multiselect__searchpanel">
116
99
  <pui-text-field
117
- :placeholder="labelSearch"
118
- v-model="searchSelected"
100
+ :placeholder="$t('pui9.components.multiSelect.search')"
101
+ v-model="searchTextSelectedItems"
119
102
  clearable
120
103
  prepend-inner-icon="far fa-search"
121
104
  noeditable
122
105
  realtime
123
106
  ></pui-text-field>
124
107
  </v-flex>
125
- <v-flex xs12>
108
+ <v-flex xs12 class="py-0 px-1">
126
109
  <v-list
127
- :style="styleList"
110
+ :style="`height: ${heightList}px`"
128
111
  :two-line="itemDescription !== null"
129
112
  class="overflow-y-auto pui-multiselect__itemListContainer"
130
113
  :class="compClassItemListSelected"
@@ -132,68 +115,56 @@
132
115
  <draggable
133
116
  v-model="selectedItems"
134
117
  v-bind="dragOptions"
135
- @change="onChangeSelected"
136
- @end="
137
- searchAvailable = '';
138
- setSelectedItems();
139
- "
118
+ @change="onChangeSelectedItems"
140
119
  class="pui-multiselect__item-draggable"
141
- :class="{ 'pui-multiselect__item--tall': itemsSearchSelected.length === 0 }"
120
+ :class="{ 'pui-multiselect__item--tall': filteredSelectedItems.length === 0 }"
142
121
  >
143
- <template v-for="(item, index) in itemsSearchSelected">
144
- <!-- He quitado esta líea del v-layout de abajo porque hacía de un tamaño muy grande al elemento
145
- :class="{'pui-multiselect__item--tall': itemsSearchSelected.length < 4 && index === itemsSearchSelected.length -1}"
146
- -->
147
- <v-layout :key="item[itemValue]" class="pui-multiselect__item">
148
- <v-flex xs1 d-flex align-center>
149
- <v-list-item class="move draggable_point">
150
- <v-icon small>far fa-ellipsis-v</v-icon>
151
- <v-icon small>far fa-ellipsis-v</v-icon>
152
- </v-list-item>
153
- </v-flex>
154
- <v-flex xs11>
155
- <v-list-item>
156
- <v-list-item-content @dblclick="unselectItemFromEvent(index, item)">
157
- <v-list-item-title
158
- class="pui-multiselect--title"
159
- v-html="item[itemText]"
160
- :title="item[itemText]"
161
- ></v-list-item-title>
162
- <v-list-item-subtitle
163
- v-if="itemDescription"
164
- v-html="item[itemDescription]"
165
- ></v-list-item-subtitle>
166
- </v-list-item-content>
167
- <v-list-item-action>
168
- <v-btn
169
- x-small
170
- icon
171
- ripple
172
- @click.native="unselectItemFromEvent(index, item)"
173
- :disabled="disabled"
174
- >
175
- <v-icon>far fa-times</v-icon>
176
- </v-btn>
177
- <v-tooltip top v-if="itemTooltip">
178
- <template v-slot:activator="{ on, attrs }">
179
- <v-btn
180
- x-small
181
- icon
182
- ripple
183
- v-bind="attrs"
184
- v-on="on"
185
- :disabled="disabled"
186
- >
187
- <v-icon>far fa-info-circle</v-icon>
188
- </v-btn>
189
- </template>
190
- <span v-html="item[itemTooltip]"/>
191
- </v-tooltip>
192
- </v-list-item-action>
193
- </v-list-item>
194
- </v-flex>
195
- </v-layout>
196
- </template>
122
+ <v-layout
123
+ v-for="(item, index) in filteredSelectedItems"
124
+ :key="`${index}__${item.itemText}__${item.itemValue}`"
125
+ class="pui-multiselect__item"
126
+ >
127
+ <v-flex xs1 d-flex align-center>
128
+ <v-list-item class="move draggable_point">
129
+ <v-icon small>far fa-ellipsis-v</v-icon>
130
+ <v-icon small>far fa-ellipsis-v</v-icon>
131
+ </v-list-item>
132
+ </v-flex>
133
+ <v-flex xs11>
134
+ <v-list-item>
135
+ <v-list-item-content @dblclick="unselectItemFromClick(item)">
136
+ <v-list-item-title
137
+ class="pui-multiselect--title"
138
+ v-html="item[itemText]"
139
+ :title="item[itemText]"
140
+ ></v-list-item-title>
141
+ <v-list-item-subtitle
142
+ v-if="itemDescription"
143
+ v-html="item[itemDescription]"
144
+ ></v-list-item-subtitle>
145
+ </v-list-item-content>
146
+ <v-list-item-action>
147
+ <v-btn
148
+ x-small
149
+ icon
150
+ ripple
151
+ @click.native="unselectItemFromClick(item)"
152
+ :disabled="disabled"
153
+ >
154
+ <v-icon>far fa-times</v-icon>
155
+ </v-btn>
156
+ <v-tooltip top v-if="itemTooltip">
157
+ <template v-slot:activator="{ on, attrs }">
158
+ <v-btn x-small icon ripple v-bind="attrs" v-on="on" :disabled="disabled">
159
+ <v-icon>far fa-info-circle</v-icon>
160
+ </v-btn>
161
+ </template>
162
+ <span v-html="item[itemTooltip]" />
163
+ </v-tooltip>
164
+ </v-list-item-action>
165
+ </v-list-item>
166
+ </v-flex>
167
+ </v-layout>
197
168
  </draggable>
198
169
  <v-card
199
170
  v-show="selectedItems.length === 0"
@@ -219,15 +190,16 @@
219
190
  text
220
191
  outlined
221
192
  :disabled="disabled || availableItems.length === 0"
222
- class="puiToolbar-dialog__button"
193
+ class="puiToolbar-dialog__button ml-n1 mt-3 mr-2"
223
194
  @click.native="selectAllItems()"
224
195
  >{{ $t('pui9.components.multiSelect.add_all') }}</v-btn
225
196
  >
197
+ <span class="pl-2 mt-3">{{ `${availableItems.length} ${$t('pui9.components.multiSelect.available').toLowerCase()}` }}</span>
226
198
  <v-spacer></v-spacer>
227
- <span class="pr-2" v-show="selectedItems.length === 1">{{
199
+ <span class="pr-2 mt-3" v-show="selectedItems.length === 1">{{
228
200
  `${selectedItems.length} ${$t('pui9.components.multiSelect.selected_item')}`
229
201
  }}</span>
230
- <span class="pr-2" v-show="selectedItems.length > 1">{{
202
+ <span class="pr-2 mt-3" v-show="selectedItems.length > 1">{{
231
203
  `${selectedItems.length} ${$t('pui9.components.multiSelect.selected_items')}`
232
204
  }}</span>
233
205
  <v-btn
@@ -235,33 +207,63 @@
235
207
  outlined
236
208
  :disabled="disabled || selectedItems.length === 0"
237
209
  @click.native="unselectAllItems()"
238
- class="puiToolbar-dialog__button"
210
+ class="puiToolbar-dialog__button mr-n1 mt-3"
239
211
  >{{ $t('pui9.components.multiSelect.empty') }}</v-btn
240
212
  >
241
213
  </v-card-actions>
242
- </div>
214
+ </v-container>
243
215
  </v-card>
244
216
  </v-flex>
245
217
  </v-layout>
246
218
  </template>
247
219
 
248
220
  <script>
249
- import PuiMultiSelectMixin from '../mixins/PuiMultiSelectMixin';
221
+ import { isEqual } from 'lodash';
250
222
  import PuiTextField from './PuiTextField';
251
223
  import draggable from 'vuedraggable';
252
224
 
253
225
  export default {
254
226
  name: 'PuiMultiSelect',
255
- mixins: [PuiMultiSelectMixin],
256
227
  components: {
257
228
  PuiTextField,
258
229
  draggable
259
230
  },
260
- /**
261
- * !important there are more properties in PuiMultiSelectMixin:
262
- * items,itemsToSelect,itemText,itemValue
263
- */
264
231
  props: {
232
+ value: {
233
+ required: false
234
+ },
235
+ /* Array of Objects provided by the parent component */
236
+ items: {
237
+ type: Array,
238
+ default: null,
239
+ required: true
240
+ },
241
+ /**
242
+ * Los items seleccionados que se pasan de inicio, se pasa en la directiva v-model al utilizar este componente
243
+ * puede ser un array de Items igual que en la propiedad :items, o un array con los id's solamente
244
+ */
245
+ itemsToSelect: {
246
+ type: Array,
247
+ default: () => []
248
+ },
249
+ // reactividad de los items seleccionados, solamente actualiza si viene true
250
+ trackItemsToSelect: {
251
+ type: Boolean,
252
+ default: false
253
+ },
254
+ /* Type: String
255
+ The items text property name */
256
+ itemText: {
257
+ type: String,
258
+ //type: [String, Array, Function],
259
+ default: 'text'
260
+ },
261
+ /* Type: String
262
+ The items id property name */
263
+ itemValue: {
264
+ type: String,
265
+ default: 'value'
266
+ },
265
267
  flat: {
266
268
  default: false
267
269
  },
@@ -300,22 +302,20 @@ export default {
300
302
  },
301
303
  data() {
302
304
  return {
303
- searchAvailable: '',
304
- searchSelected: '',
305
- searchingAvailable: true,
306
- searchingSelected: true,
305
+ itemTextMultiple: false,
306
+ itemValueMultiple: false,
307
+ itemTextNested: false,
308
+ itemValueNested: false,
309
+ searchTextAvailableItems: '',
310
+ searchTextSelectedItems: '',
307
311
  dragOptions: {
308
312
  group: 'multiselect',
309
313
  disabled: this.disabled
310
314
  },
311
- selectedItems: [],
312
- availableItems: []
315
+ availableItems: [],
316
+ selectedItems: []
313
317
  };
314
318
  },
315
- updated() {
316
- this.searchingAvailable = false;
317
- this.searchingSelected = false;
318
- },
319
319
  computed: {
320
320
  compClassItemListSelected() {
321
321
  if (!this.selectedItems) {
@@ -329,205 +329,308 @@ export default {
329
329
  'pui-multiselect__itemListContainer-selected': this.selectedItems.length === 0
330
330
  };
331
331
  },
332
- itemsSearchAvailable() {
333
- if (!this.searchAvailable) {
332
+ filteredAvailableItems() {
333
+ if (!this.searchTextAvailableItems) {
334
334
  return this.availableItems;
335
335
  }
336
- //this.searchingAvailable = true;
336
+
337
337
  return this.availableItems
338
338
  .filter((item) => {
339
339
  try {
340
340
  const match =
341
- item[this.itemText].toUpperCase().includes(this.searchAvailable.toUpperCase()) ||
342
- (this.itemDescription && item[this.itemDescription].toUpperCase().includes(this.searchAvailable.toUpperCase()));
341
+ item[this.itemText].toUpperCase().includes(this.searchTextAvailableItems.toUpperCase()) ||
342
+ (this.itemDescription && item[this.itemDescription].toUpperCase().includes(this.searchTextAvailableItems.toUpperCase()));
343
343
  return match;
344
344
  } catch (e) {
345
345
  //al item le falta el texto o la descripción
346
346
  return false;
347
347
  }
348
- })
349
- .sort((a, b) => {
350
- const aValue = a[this.itemText];
351
- const bValue = b[this.itemText];
352
- return aValue.localeCompare(bValue);
353
348
  });
354
- //this.searchingAvailable = false;
355
349
  },
356
- itemsSearchSelected() {
357
- if (!this.searchSelected) {
350
+ filteredSelectedItems() {
351
+ if (!this.searchTextSelectedItems) {
358
352
  return this.selectedItems;
359
353
  }
360
- //this.searchingSelected = true;
354
+
361
355
  return this.selectedItems
362
356
  .filter((item) => {
363
357
  try {
364
358
  const match =
365
- item[this.itemText].toUpperCase().includes(this.searchSelected.toUpperCase()) ||
366
- (this.itemDescription && item[this.itemDescription].toUpperCase().includes(this.searchSelected.toUpperCase()));
359
+ item[this.itemText].toUpperCase().includes(this.searchTextSelectedItems.toUpperCase()) ||
360
+ (this.itemDescription && item[this.itemDescription].toUpperCase().includes(this.searchTextSelectedItems.toUpperCase()));
367
361
  return match;
368
362
  } catch (e) {
369
363
  //al item le falta el texto o la descripción
370
364
  return false;
371
365
  }
372
- })
373
- .sort((a, b) => {
374
- const aValue = a[this.itemText];
375
- const bValue = b[this.itemText];
376
- return aValue.localeCompare(bValue);
377
366
  });
378
- //this.searchingSelected = false;
379
367
  },
380
- styleList() {
381
- return `height: ${this.heightList.toString()}px`;
368
+ selected() {
369
+ return this.selectedItems.length > 0;
370
+ }
371
+ },
372
+ watch: {
373
+ items(newValue, oldValue) {
374
+ if (JSON.stringify(newValue) !== JSON.stringify(oldValue)) {
375
+ this.checkItemText();
376
+ this.checkItemValue();
377
+
378
+ this.setAvailableItems();
379
+ this.setSelectedItems();
380
+ }
382
381
  },
383
- labelSearch() {
384
- return this.$t('pui9.components.multiSelect.search');
382
+ itemsToSelect: {
383
+ handler() {
384
+ if (this.trackItemsToSelect) {
385
+ this.setAvailableItems();
386
+ this.setSelectedItems();
387
+ }
388
+ },
389
+ deep: true
385
390
  }
386
391
  },
392
+ mounted() {
393
+ this.checkItemText();
394
+ this.checkItemValue();
395
+
396
+ this.setAvailableItems();
397
+ this.setSelectedItems();
398
+ },
387
399
  methods: {
400
+ checkItemText() {
401
+ this.itemTextMultiple = Array.isArray(this.itemText);
402
+
403
+ if (this.itemTextMultiple === false) {
404
+ this.itemTextNested = this.checkForNestedFields(this.itemText);
405
+ }
406
+ },
407
+ checkItemValue() {
408
+ this.itemValueMultiple = Array.isArray(this.itemValue);
409
+
410
+ if (this.itemValueMultiple === false) {
411
+ this.itemValueNested = this.checkForNestedFields(this.itemValue);
412
+ }
413
+ },
414
+ checkForNestedFields(stringField) {
415
+ if (stringField instanceof Function) {
416
+ return false;
417
+ }
418
+
419
+ return stringField.split('.').length > 1;
420
+ },
421
+ // order items by itemText alphabetically
422
+ orderItems(items) {
423
+ return items.sort((a, b) => {
424
+ const aValue = a[this.itemText] ? a[this.itemText] : '';
425
+ const bValue = b[this.itemText] ? b[this.itemText] : '';
426
+ return aValue.localeCompare(bValue);
427
+ });
428
+ },
388
429
  setAvailableItems() {
389
- this.availableItems = [];
390
- this.items
391
- .sort((item1, item2) => {
392
- if (item1[this.itemText] > item2[this.itemText]) {
393
- return 1;
394
- } else if (item1[this.itemText] < item2[this.itemText]) {
395
- return -1;
396
- }
397
- return 0;
398
- })
399
- .forEach((item) => this.availableItems.push(item));
430
+ this.availableItems = [...this.items];
400
431
  },
401
- selectItem(item) {
402
- this.selectedItems.push(item);
403
- this.availableItems.forEach((availableItem, index) => {
404
- if (item[this.itemValue] === availableItem[this.itemValue]) {
432
+ setSelectedItems() {
433
+ this.selectedItems = [];
434
+
435
+ if (!this.itemsToSelect || this.itemsToSelect.length === 0) {
436
+ return;
437
+ }
438
+
439
+ for (let i = 0, length = this.itemsToSelect.length; i < length; i++) {
440
+ // this.selectItem(this.itemsToSelect[i]);
441
+ this.selectItemFromItemValue(this.itemsToSelect[i]);
442
+ }
443
+ },
444
+ // solo compara los valores de los items, se usa en setSelectItems
445
+ selectItemFromItemValue(item) {
446
+ for (let index = 0, length = this.availableItems.length; index < length; index++) {
447
+ const availableItem = this.availableItems[index];
448
+ if (availableItem[this.itemValue] === item[this.itemValue]) {
449
+ this.selectedItems.push({ ...item });
405
450
  this.availableItems.splice(index, 1);
451
+ return;
406
452
  }
407
- });
408
- this.$emit('input', this.selectedItems);
453
+ }
409
454
  },
410
- selectItemById(id, theItem) {
411
- this.selectedItems.push(theItem);
455
+ // compara la equivalencia del objeto
456
+ selectItem(item) {
412
457
  for (let index = 0, length = this.availableItems.length; index < length; index++) {
413
458
  const availableItem = this.availableItems[index];
414
- if (availableItem[this.itemValue] === id) {
415
- //comprobamos por referencia que el item y el availableItem no es lo mismo,
416
- //en este caso hay que theItem que se acaba de añadir 4 lineas de código atras, y añadimos el bueno
417
- //esto significa que theItem solo lleva los campos de la pk y no es el item "entero"
418
- if (availableItem !== theItem) {
419
- this.selectedItems.pop();
420
- this.selectedItems.push(availableItem);
421
- }
459
+ if (isEqual(availableItem, item)) {
460
+ this.selectedItems.push({ ...item });
422
461
  this.availableItems.splice(index, 1);
423
462
  return;
424
463
  }
425
464
  }
426
465
  },
427
- setSelectedItems() {
428
- this.$emit('input', this.selectedItems);
429
- },
430
466
  unselectItem(item) {
431
- this.availableItems.push(item);
432
- this.selectedItems.forEach((selectedItem, index) => {
433
- if (item[this.itemValue] === selectedItem[this.itemValue]) {
467
+ for (let index = 0, length = this.selectedItems.length; index < length; index++) {
468
+ const selectedItem = this.selectedItems[index];
469
+ if (isEqual(selectedItem, item)) {
470
+ this.availableItems.push({ ...item });
434
471
  this.selectedItems.splice(index, 1);
472
+ return;
435
473
  }
436
- });
437
- this.$emit('input', this.selectedItems);
474
+ }
438
475
  },
439
- selectItemFromEvent(index, item) {
476
+ selectItemFromClick(item) {
440
477
  if (this.disabled) {
441
478
  return;
442
479
  }
443
- this.searchSelected = '';
444
- if (this.searchAvailable) {
445
- this.selectItem(item);
446
- return;
447
- }
448
- this.availableItems.splice(index, 1);
449
- this.selectedItems.push(item);
480
+
481
+ this.selectItem(item);
450
482
  this.$emit('input', this.selectedItems);
451
483
  },
452
- unselectItemFromEvent(index, item) {
484
+ unselectItemFromClick(item) {
453
485
  if (this.disabled) {
454
486
  return;
455
487
  }
456
- this.searchAvailable = '';
457
- if (this.searchSelected) {
458
- this.unselectItem(item);
459
- return;
460
- }
461
- this.selectedItems.splice(index, 1);
462
- this.availableItems.push(item);
488
+
489
+ this.unselectItem(item);
463
490
  this.$emit('input', this.selectedItems);
464
491
  },
465
492
  selectAllItems() {
466
- this.searchSelected = '';
467
- this.searchingAvailable = true;
468
- this.searchingSelected = true;
493
+ this.searchTextSelectedItems = '';
469
494
  this.selectedItems = this.selectedItems.concat(this.availableItems);
470
495
  this.availableItems = [];
471
496
  this.$emit('input', this.selectedItems);
472
497
  },
473
498
  unselectAllItems() {
474
- this.searchingAvailable = true;
475
- this.searchingSelected = true;
476
- this.searchAvailable = '';
499
+ this.searchTextAvailableItems = '';
477
500
  this.availableItems = this.availableItems.concat(this.selectedItems);
478
501
  this.selectedItems = [];
479
502
  this.$emit('input', this.selectedItems);
480
503
  },
481
- onChangeAvailables(event) {
482
- // due to drag and drop, if searchAvailable has value, the item selected by the user is not the item added to
483
- // selectedItems so:
484
- // - it has to be removed manually from selectedItems,
485
- // - it has to be added to the availableItems one more time,
486
- // - selected item by the user that is in itemsSearchAvailable, has to be added to selectedItems
487
- // - and selected item by the user has to be removed from availableItems
488
- if (event.removed && this.itemsSearchAvailable.length > 0 && this.itemsSearchAvailable.length < this.availableItems.length) {
489
- // remove last from selectedItems because it is not the selected item by user
490
- const removed = event.removed.element;
491
- let removedIndex = this.selectedItems.length;
492
- this.selectedItems.forEach((selectedItem, index) => {
493
- if (removed[this.itemValue] === selectedItem[this.itemValue]) {
494
- removedIndex = index;
495
- this.selectedItems.splice(index, 1);
496
- }
497
- });
498
- // push removed item in availableItems
499
- this.availableItems.push(removed);
500
- // add selected item by user to the selectedItems in the same position as removed previously
501
- const added = this.itemsSearchAvailable[event.removed.oldIndex];
502
- this.selectedItems.splice(removedIndex, 0, added);
503
- // remove selected item from availableItems
504
- this.availableItems.forEach((availableItem, index) => {
505
- if (added[this.itemValue] === availableItem[this.itemValue]) {
506
- this.availableItems.splice(index, 1);
504
+
505
+ // due to drag and drop, if searchTextAvailableItems or searchTextSelectedItems have values,
506
+ // the item selected by the user is not the real item so we need to disapply the operations manually
507
+ // and apply them again transforming the indexes of the filtered items to the real items
508
+
509
+ onChangeAvailableItems(event) {
510
+ if (event.moved) {
511
+ if (this.filteredAvailableItems.length > 0 && this.filteredAvailableItems.length < this.availableItems.length) {
512
+ // deshacer movimiento con indices equivocados
513
+ this.availableItems.splice(event.moved.newIndex, 1);
514
+ this.availableItems.splice(event.moved.oldIndex, 0, event.moved.element);
515
+
516
+ // realizar verdadero movimiento
517
+ const realItem = this.filteredAvailableItems[event.moved.oldIndex];
518
+ const realOldIndex = this.availableItems.indexOf(realItem);
519
+ this.availableItems.splice(realOldIndex, 1);
520
+
521
+ const previousItem = this.filteredAvailableItems[event.moved.newIndex - 1];
522
+ const realNewIndex = this.availableItems.indexOf(previousItem) + 1;
523
+ this.availableItems.splice(realNewIndex, 0, realItem);
524
+ }
525
+
526
+ this.$emit('input', this.selectedItems);
527
+ }
528
+
529
+ if (event.removed) {
530
+ if (this.filteredAvailableItems.length > 0 && this.filteredAvailableItems.length < this.availableItems.length) {
531
+ // deshacer el borrado de availableItems
532
+ this.availableItems.splice(event.removed.oldIndex, 0, event.removed.element);
533
+ // borrar el verdadero item
534
+ const realItem = this.filteredAvailableItems[event.removed.oldIndex];
535
+ for (let index = 0, length = this.availableItems.length; index < length; index++) {
536
+ const availableItem = this.availableItems[index];
537
+ if (isEqual(availableItem, realItem)) {
538
+ console.log('deleting', this.availableItems[index][this.itemValue]);
539
+ this.availableItems.splice(index, 1);
540
+ break;
541
+ }
507
542
  }
508
- });
543
+ // añadir el verdadero item a partir de la posición guardada previamente (added ejecuta antes que removed)
544
+ console.log('newIndex', this.newIndex);
545
+
546
+ const previousItem = this.filteredSelectedItems[this.newIndex - 1];
547
+ const realNewIndex = this.selectedItems.indexOf(previousItem) + 1;
548
+ this.selectedItems.splice(realNewIndex, 0, realItem);
549
+ }
550
+ this.$emit('input', this.selectedItems);
551
+ }
552
+
553
+ if (event.added) {
554
+ if (this.filteredSelectedItems.length > 0 && this.filteredSelectedItems.length < this.selectedItems.length) {
555
+ // deshacer el añadido de availableItems
556
+ this.availableItems.splice(event.added.newIndex, 1);
557
+ // guardar la posición donde se desea insertar el nuevo item
558
+ this.newIndex = event.added.newIndex;
559
+ }
560
+ this.$emit('input', this.selectedItems);
509
561
  }
510
562
  },
511
- onChangeSelected(event) {
512
- if (event.removed && this.itemsSearchSelected.length > 0 && this.itemsSearchSelected.length < this.selectedItems.length) {
513
- const removed = event.removed.element;
514
- let removedIndex = this.availableItems.length;
515
- this.availableItems.forEach((availableItem, index) => {
516
- if (removed[this.itemValue] === availableItem[this.itemValue]) {
517
- removedIndex = index;
518
- this.availableItems.splice(index, 1);
519
- }
520
- });
521
- this.selectedItems.splice(event.removed.oldIndex, 0, removed);
522
- const added = this.itemsSearchSelected[event.removed.oldIndex];
523
- this.availableItems.splice(removedIndex, 0, added);
524
- this.selectedItems.forEach((selectedItem, index) => {
525
- if (added[this.itemValue] === selectedItem[this.itemValue]) {
526
- this.selectedItems.splice(index, 1);
563
+ onChangeSelectedItems(event) {
564
+ if (event.moved) {
565
+ if (this.filteredSelectedItems.length > 0 && this.filteredSelectedItems.length < this.selectedItems.length) {
566
+ // deshacer movimiento con indices equivocados
567
+ this.selectedItems.splice(event.moved.newIndex, 1);
568
+ this.selectedItems.splice(event.moved.oldIndex, 0, event.moved.element);
569
+
570
+ // realizar verdadero movimiento
571
+ const realItem = this.filteredSelectedItems[event.moved.oldIndex];
572
+ const realOldIndex = this.selectedItems.indexOf(realItem);
573
+ this.selectedItems.splice(realOldIndex, 1);
574
+
575
+ const previousItem = this.filteredSelectedItems[event.moved.newIndex - 1];
576
+ const realNewIndex = this.selectedItems.indexOf(previousItem) + 1;
577
+ this.selectedItems.splice(realNewIndex, 0, realItem);
578
+ }
579
+
580
+ this.$emit('input', this.selectedItems);
581
+ }
582
+
583
+ if (event.removed) {
584
+ if (this.filteredSelectedItems.length > 0 && this.filteredSelectedItems.length < this.selectedItems.length) {
585
+ // deshacer el borrado de selectedItems
586
+ this.selectedItems.splice(event.removed.oldIndex, 0, event.removed.element);
587
+ // borrar el verdadero item
588
+ const realItem = this.filteredSelectedItems[event.removed.oldIndex];
589
+ for (let index = 0, length = this.selectedItems.length; index < length; index++) {
590
+ const selectedItem = this.selectedItems[index];
591
+ if (isEqual(selectedItem, realItem)) {
592
+ console.log('deleting', this.selectedItems[index][this.itemValue]);
593
+ this.selectedItems.splice(index, 1);
594
+ break;
595
+ }
527
596
  }
528
- });
597
+ // añadir el verdadero item a partir de la posición guardada previamente (added ejecuta antes que removed)
598
+ const previousItem = this.filteredAvailableItems[this.newIndex - 1];
599
+ const realNewIndex = this.availableItems.indexOf(previousItem) + 1;
600
+ this.availableItems.splice(realNewIndex, 0, realItem);
601
+ }
602
+ this.$emit('input', this.selectedItems);
603
+ }
604
+
605
+ if (event.added) {
606
+ if (this.filteredAvailableItems.length > 0 && this.filteredAvailableItems.length < this.availableItems.length) {
607
+ // deshacer el añadido de selectedItems
608
+ this.selectedItems.splice(event.added.newIndex, 1);
609
+ // guardar la posición donde se desea insertar el nuevo item
610
+ this.newIndex = event.added.newIndex;
611
+ }
612
+ this.$emit('input', this.selectedItems);
529
613
  }
614
+ },
615
+ getItemIndexFromFilteredIndex(filteredIndex, filteredList, list) {
616
+ const filteredItem = filteredList[filteredIndex];
617
+ for (let index = 0, length = list.length; index < length; index++) {
618
+ const item = list[index];
619
+ if (isEqual(item, filteredItem)) {
620
+ return index;
621
+ }
622
+ }
623
+ return -1;
530
624
  }
531
625
  }
532
626
  };
533
627
  </script>
628
+
629
+ <style scoped>
630
+ .tooltipText {
631
+ word-break: break-all;
632
+ max-width: 100% !important;
633
+ max-height: 100% !important;
634
+ font: normal;
635
+ }
636
+ </style>