selectic 3.1.0 → 3.1.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/dist/selectic.common.js +110 -22
- package/dist/selectic.esm.js +110 -22
- package/doc/breakingChanges.md +8 -0
- package/doc/changeIcons.md +8 -2
- package/package.json +1 -1
- package/src/ExtendedList.tsx +5 -5
- package/src/Icon.tsx +2 -2
- package/src/List.tsx +0 -4
- package/src/Store.tsx +161 -25
- package/src/index.tsx +2 -2
- package/src/tools.ts +13 -0
- package/test/Store/Store_creation.spec.js +12 -12
- package/test/Store/Store_props.spec.js +612 -595
- package/test/Store/changeIcons.spec.js +1 -1
- package/test/Store/commit.spec.js +56 -47
- package/test/Store/selectGroup.spec.js +278 -271
- package/test/Store/toggleSelectAll.spec.js +6 -1
- package/test/helper.js +4 -0
- package/test/tools.js +5 -1
- package/types/ExtendedList.d.ts +5 -5
- package/types/List.d.ts +0 -3
- package/types/Store.d.ts +15 -2
- package/types/index.d.ts +2 -2
- package/types/tools.d.ts +4 -0
package/dist/selectic.common.js
CHANGED
|
@@ -138,12 +138,25 @@ function compareOptions(oldOptions, newOptions) {
|
|
|
138
138
|
});
|
|
139
139
|
});
|
|
140
140
|
}
|
|
141
|
+
let displayLog = false;
|
|
142
|
+
function debug(fName, step, ...args) {
|
|
143
|
+
if (!displayLog) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
console.log('--%s-- [%s]', fName, step, ...args);
|
|
147
|
+
}
|
|
148
|
+
/** Enable logs for debugging */
|
|
149
|
+
debug.enable = (display) => {
|
|
150
|
+
displayLog = display;
|
|
151
|
+
};
|
|
141
152
|
|
|
142
153
|
/* File Purpose:
|
|
143
154
|
* It keeps and computes all states at a single place.
|
|
144
155
|
* Every inner components of Selectic should communicate with this file to
|
|
145
156
|
* change or to get states.
|
|
146
157
|
*/
|
|
158
|
+
/* For debugging */
|
|
159
|
+
debug.enable(false);
|
|
147
160
|
/* }}} */
|
|
148
161
|
/* {{{ Static */
|
|
149
162
|
function changeTexts$1(texts) {
|
|
@@ -178,12 +191,19 @@ let messages = {
|
|
|
178
191
|
let defaultFamilyIcon = 'selectic';
|
|
179
192
|
let icons = {};
|
|
180
193
|
let closePreviousSelectic;
|
|
194
|
+
/**
|
|
195
|
+
* Time to wait before considering there is no other requests.
|
|
196
|
+
* This time is await only if there is already a requested request.
|
|
197
|
+
*/
|
|
198
|
+
const DEBOUNCE_REQUEST = 250;
|
|
181
199
|
/* }}} */
|
|
182
200
|
let uid = 0;
|
|
183
201
|
class SelecticStore {
|
|
184
202
|
constructor(props = {}) {
|
|
185
203
|
/* Do not need reactivity */
|
|
186
204
|
this.requestId = 0;
|
|
205
|
+
this.requestSearchId = 0; /* Used for search request */
|
|
206
|
+
this.isRequesting = false;
|
|
187
207
|
this._uid = ++uid;
|
|
188
208
|
/* {{{ Props */
|
|
189
209
|
const defaultProps = {
|
|
@@ -299,6 +319,7 @@ class SelecticStore {
|
|
|
299
319
|
this.data.cacheItem.clear();
|
|
300
320
|
this.setAutomaticClose();
|
|
301
321
|
this.commit('isOpen', false);
|
|
322
|
+
this.clearDisplay();
|
|
302
323
|
this.buildAllOptions(true);
|
|
303
324
|
this.buildSelectedOptions();
|
|
304
325
|
}, { deep: true });
|
|
@@ -389,6 +410,7 @@ class SelecticStore {
|
|
|
389
410
|
/* {{{ public methods */
|
|
390
411
|
commit(name, value) {
|
|
391
412
|
const oldValue = this.state[name];
|
|
413
|
+
debug('commit', 'start', name, value, 'oldValue:', oldValue);
|
|
392
414
|
if (oldValue === value) {
|
|
393
415
|
return;
|
|
394
416
|
}
|
|
@@ -397,8 +419,7 @@ class SelecticStore {
|
|
|
397
419
|
case 'searchText':
|
|
398
420
|
this.state.offsetItem = 0;
|
|
399
421
|
this.state.activeItemIdx = -1;
|
|
400
|
-
this.
|
|
401
|
-
this.state.totalFilteredOptions = Infinity;
|
|
422
|
+
this.clearDisplay();
|
|
402
423
|
if (value) {
|
|
403
424
|
this.buildFilteredOptions();
|
|
404
425
|
}
|
|
@@ -446,6 +467,7 @@ class SelecticStore {
|
|
|
446
467
|
}
|
|
447
468
|
break;
|
|
448
469
|
}
|
|
470
|
+
debug('commit', '(done)', name);
|
|
449
471
|
}
|
|
450
472
|
setAutomaticChange() {
|
|
451
473
|
this.state.status.automaticChange = true;
|
|
@@ -631,14 +653,14 @@ class SelecticStore {
|
|
|
631
653
|
this.state.status.errorMessage = '';
|
|
632
654
|
}
|
|
633
655
|
clearCache(forceReset = false) {
|
|
656
|
+
debug('clearCache', 'start', forceReset);
|
|
634
657
|
const isPartial = vue.unref(this.isPartial);
|
|
635
658
|
const total = isPartial ? Infinity : 0;
|
|
636
659
|
this.data.cacheItem.clear();
|
|
637
660
|
this.state.allOptions = [];
|
|
638
661
|
this.state.totalAllOptions = total;
|
|
639
662
|
this.state.totalDynOptions = total;
|
|
640
|
-
this.
|
|
641
|
-
this.state.totalFilteredOptions = Infinity;
|
|
663
|
+
this.clearDisplay();
|
|
642
664
|
this.state.status.errorMessage = '';
|
|
643
665
|
this.state.status.hasChanged = false;
|
|
644
666
|
if (forceReset) {
|
|
@@ -770,6 +792,13 @@ class SelecticStore {
|
|
|
770
792
|
this.checkAutoSelect();
|
|
771
793
|
}
|
|
772
794
|
}
|
|
795
|
+
/** Reset the display cache in order to rebuild it */
|
|
796
|
+
clearDisplay() {
|
|
797
|
+
debug('clearDisplay', 'start');
|
|
798
|
+
this.state.filteredOptions = [];
|
|
799
|
+
this.state.totalFilteredOptions = Infinity;
|
|
800
|
+
}
|
|
801
|
+
/** rebuild the state filteredOptions to normalize their values */
|
|
773
802
|
updateFilteredOptions() {
|
|
774
803
|
if (!this.data.doNotUpdate) {
|
|
775
804
|
this.state.filteredOptions = this.buildItems(this.state.filteredOptions);
|
|
@@ -819,7 +848,7 @@ class SelecticStore {
|
|
|
819
848
|
});
|
|
820
849
|
return listOptions;
|
|
821
850
|
}
|
|
822
|
-
|
|
851
|
+
/** This method is for the computed property elementOptions */
|
|
823
852
|
getElementOptions() {
|
|
824
853
|
const options = deepClone(this.props.childOptions, ['data']);
|
|
825
854
|
const childOptions = [];
|
|
@@ -850,7 +879,9 @@ class SelecticStore {
|
|
|
850
879
|
});
|
|
851
880
|
return childOptions;
|
|
852
881
|
}
|
|
882
|
+
/** Generate the list of all options by combining the 3 option lists */
|
|
853
883
|
buildAllOptions(keepFetched = false, stopFetch = false) {
|
|
884
|
+
debug('buildAllOptions', 'start', 'keepFetched', keepFetched, 'stopFetch', stopFetch);
|
|
854
885
|
const allOptions = [];
|
|
855
886
|
let listOptions = [];
|
|
856
887
|
let elementOptions = [];
|
|
@@ -922,8 +953,6 @@ class SelecticStore {
|
|
|
922
953
|
}
|
|
923
954
|
}
|
|
924
955
|
if (!stopFetch) {
|
|
925
|
-
this.state.filteredOptions = [];
|
|
926
|
-
this.state.totalFilteredOptions = Infinity;
|
|
927
956
|
this.buildFilteredOptions().then(() => {
|
|
928
957
|
/* XXX: To recompute for strict mode and auto-select */
|
|
929
958
|
this.assertCorrectValue();
|
|
@@ -939,18 +968,21 @@ class SelecticStore {
|
|
|
939
968
|
const options = this.filterOptions(allOptions, search);
|
|
940
969
|
this.setFilteredOptions(options);
|
|
941
970
|
}
|
|
971
|
+
debug('buildAllOptions', 'end', 'allOptions:', this.state.allOptions.length, 'totalAllOptions:', this.state.totalAllOptions);
|
|
942
972
|
}
|
|
943
973
|
async buildFilteredOptions() {
|
|
944
|
-
|
|
974
|
+
const state = this.state;
|
|
975
|
+
if (!state.isOpen) {
|
|
945
976
|
/* Do not try to fetch anything while the select is not open */
|
|
946
977
|
return;
|
|
947
978
|
}
|
|
948
|
-
const allOptions =
|
|
949
|
-
const search =
|
|
950
|
-
const totalAllOptions =
|
|
979
|
+
const allOptions = state.allOptions;
|
|
980
|
+
const search = state.searchText;
|
|
981
|
+
const totalAllOptions = state.totalAllOptions;
|
|
951
982
|
const allOptionsLength = allOptions.length;
|
|
952
|
-
let filteredOptionsLength =
|
|
983
|
+
let filteredOptionsLength = state.filteredOptions.length;
|
|
953
984
|
const hasAllItems = vue.unref(this.hasAllItems);
|
|
985
|
+
debug('buildFilteredOptions', 'start', 'hasAllItems:', hasAllItems, 'allOptions', allOptions.length, 'search:', search, 'filteredOptionsLength:', filteredOptionsLength);
|
|
954
986
|
if (hasAllItems) {
|
|
955
987
|
/* Everything has already been fetched and stored in filteredOptions */
|
|
956
988
|
return;
|
|
@@ -967,16 +999,17 @@ class SelecticStore {
|
|
|
967
999
|
return;
|
|
968
1000
|
}
|
|
969
1001
|
/* When we only have partial options */
|
|
970
|
-
const offsetItem =
|
|
1002
|
+
const offsetItem = state.offsetItem;
|
|
971
1003
|
const marginSize = vue.unref(this.marginSize);
|
|
972
1004
|
const endIndex = offsetItem + marginSize;
|
|
1005
|
+
debug('buildFilteredOptions', 'partial options', 'offsetItem:', offsetItem, 'marginSize:', marginSize, 'filteredOptionsLength', filteredOptionsLength);
|
|
973
1006
|
if (endIndex <= filteredOptionsLength) {
|
|
974
1007
|
return;
|
|
975
1008
|
}
|
|
976
1009
|
if (!search && endIndex <= allOptionsLength) {
|
|
977
|
-
this.setFilteredOptions(this.buildGroupItems(allOptions), false, totalAllOptions +
|
|
1010
|
+
this.setFilteredOptions(this.buildGroupItems(allOptions), false, totalAllOptions + state.groups.size);
|
|
978
1011
|
const isPartial = vue.unref(this.isPartial);
|
|
979
|
-
if (isPartial &&
|
|
1012
|
+
if (isPartial && state.totalDynOptions === Infinity) {
|
|
980
1013
|
this.fetchData();
|
|
981
1014
|
}
|
|
982
1015
|
return;
|
|
@@ -989,6 +1022,7 @@ class SelecticStore {
|
|
|
989
1022
|
return;
|
|
990
1023
|
}
|
|
991
1024
|
}
|
|
1025
|
+
debug('buildFilteredOptions', 'end', '(will call fetchData)', this.state.filteredOptions.length);
|
|
992
1026
|
await this.fetchData();
|
|
993
1027
|
}
|
|
994
1028
|
async buildSelectedOptions() {
|
|
@@ -1042,6 +1076,39 @@ class SelecticStore {
|
|
|
1042
1076
|
state.selectedOptions = items[0];
|
|
1043
1077
|
}
|
|
1044
1078
|
}
|
|
1079
|
+
async fetchRequest(fetchCallback, search, offset, limit) {
|
|
1080
|
+
const searchRqId = ++this.requestSearchId;
|
|
1081
|
+
if (!search) {
|
|
1082
|
+
++this.requestId;
|
|
1083
|
+
}
|
|
1084
|
+
const requestId = this.requestId;
|
|
1085
|
+
debug('fetchRequest', 'start', 'search:', search, 'offset:', offset, 'limit:', limit, 'requestId:', requestId, 'requestSearchId:', searchRqId, 'isRequesting:', this.isRequesting);
|
|
1086
|
+
if (this.isRequesting) {
|
|
1087
|
+
debug('fetchRequest', `await ${DEBOUNCE_REQUEST}ms`);
|
|
1088
|
+
/* debounce the call to avoid sending too much requests */
|
|
1089
|
+
await new Promise((resolve) => {
|
|
1090
|
+
setTimeout(resolve, DEBOUNCE_REQUEST);
|
|
1091
|
+
});
|
|
1092
|
+
/* Check if there are other requested requests, in such case drop this one */
|
|
1093
|
+
if (requestId !== this.requestId || (search && searchRqId !== this.requestSearchId)) {
|
|
1094
|
+
debug('fetchRequest', '××deprecated××', requestId, searchRqId);
|
|
1095
|
+
return false;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
this.isRequesting = true;
|
|
1099
|
+
const response = await fetchCallback(search, offset, limit);
|
|
1100
|
+
/* Check if request is obsolete */
|
|
1101
|
+
if (requestId !== this.requestId || (search && searchRqId !== this.requestSearchId)) {
|
|
1102
|
+
debug('fetchRequest', '×××deprecated×××', requestId, searchRqId);
|
|
1103
|
+
return false;
|
|
1104
|
+
}
|
|
1105
|
+
this.isRequesting = false;
|
|
1106
|
+
const deprecated = searchRqId !== this.requestSearchId;
|
|
1107
|
+
debug('fetchRequest', 'end', response.result.length, response.total, deprecated);
|
|
1108
|
+
return Object.assign(Object.assign({}, response), {
|
|
1109
|
+
/* this is to fulfill the cache */
|
|
1110
|
+
deprecated: deprecated });
|
|
1111
|
+
}
|
|
1045
1112
|
async fetchData() {
|
|
1046
1113
|
const state = this.state;
|
|
1047
1114
|
const labels = this.data.labels;
|
|
@@ -1063,9 +1130,14 @@ class SelecticStore {
|
|
|
1063
1130
|
const offset = filteredOptionsLength - this.nbGroups(state.filteredOptions) - dynOffset;
|
|
1064
1131
|
const nbItems = endIndex - offset;
|
|
1065
1132
|
const limit = Math.ceil(nbItems / pageSize) * pageSize;
|
|
1133
|
+
debug('fetchData', 'start', 'search:', search, 'offset:', offset, 'limit:', limit);
|
|
1066
1134
|
try {
|
|
1067
|
-
const
|
|
1068
|
-
|
|
1135
|
+
const response = await this.fetchRequest(fetchCallback, search, offset, limit);
|
|
1136
|
+
if (!response) {
|
|
1137
|
+
debug('fetchData', '×× deprecated ××', search, offset, limit);
|
|
1138
|
+
return;
|
|
1139
|
+
}
|
|
1140
|
+
const { total: rTotal, result, deprecated } = response;
|
|
1069
1141
|
let total = rTotal;
|
|
1070
1142
|
let errorMessage = '';
|
|
1071
1143
|
/* Assert result is correctly formatted */
|
|
@@ -1083,11 +1155,12 @@ class SelecticStore {
|
|
|
1083
1155
|
if (!search) {
|
|
1084
1156
|
/* update cache */
|
|
1085
1157
|
state.totalDynOptions = total;
|
|
1086
|
-
const old = state.dynOptions.splice(offset,
|
|
1158
|
+
const old = state.dynOptions.splice(offset, result.length, ...result);
|
|
1087
1159
|
if (compareOptions(old, result)) {
|
|
1088
1160
|
/* Added options are the same as previous ones.
|
|
1089
1161
|
* Stop fetching to avoid infinite loop
|
|
1090
1162
|
*/
|
|
1163
|
+
debug('fetchData', 'no new values');
|
|
1091
1164
|
if (!vue.unref(this.hasFetchedAllItems)) {
|
|
1092
1165
|
/* Display error if all items are not fetch
|
|
1093
1166
|
* We can have the case where old value and result
|
|
@@ -1095,14 +1168,21 @@ class SelecticStore {
|
|
|
1095
1168
|
* total is 0 */
|
|
1096
1169
|
errorMessage = labels.wrongQueryResult;
|
|
1097
1170
|
}
|
|
1098
|
-
setTimeout(() =>
|
|
1171
|
+
setTimeout(() => {
|
|
1172
|
+
debug('fetchData', 'before buildAllOptions (stopped)', 'offsetItem:', this.state.offsetItem, 'allOptions:', this.state.allOptions.length);
|
|
1173
|
+
this.buildAllOptions(true, true);
|
|
1174
|
+
}, 0);
|
|
1099
1175
|
}
|
|
1100
1176
|
else {
|
|
1101
|
-
setTimeout(() =>
|
|
1177
|
+
setTimeout(() => {
|
|
1178
|
+
debug('fetchData', 'before buildAllOptions', 'offsetItem:', this.state.offsetItem, 'allOptions:', this.state.allOptions.length);
|
|
1179
|
+
this.buildAllOptions(true);
|
|
1180
|
+
}, 0);
|
|
1102
1181
|
}
|
|
1103
1182
|
}
|
|
1104
|
-
/* Check request is not obsolete */
|
|
1105
|
-
if (
|
|
1183
|
+
/* Check request (without search) is not obsolete */
|
|
1184
|
+
if (deprecated) {
|
|
1185
|
+
debug('fetchData', '××× deprecated ×××', search, offset, limit);
|
|
1106
1186
|
return;
|
|
1107
1187
|
}
|
|
1108
1188
|
if (!search) {
|
|
@@ -1131,14 +1211,17 @@ class SelecticStore {
|
|
|
1131
1211
|
}
|
|
1132
1212
|
catch (e) {
|
|
1133
1213
|
state.status.errorMessage = e.message;
|
|
1214
|
+
debug('fetchData', 'error', e.message);
|
|
1134
1215
|
if (!search) {
|
|
1135
1216
|
state.totalDynOptions = 0;
|
|
1136
1217
|
this.buildAllOptions(true, true);
|
|
1137
1218
|
}
|
|
1138
1219
|
}
|
|
1139
1220
|
this.state.status.searching = false;
|
|
1221
|
+
debug('fetchData', 'end');
|
|
1140
1222
|
}
|
|
1141
1223
|
filterOptions(options, search) {
|
|
1224
|
+
debug('filterOptions', 'start', 'options:', options.length, 'search:', search);
|
|
1142
1225
|
if (!search) {
|
|
1143
1226
|
return this.buildGroupItems(options);
|
|
1144
1227
|
}
|
|
@@ -1149,6 +1232,7 @@ class SelecticStore {
|
|
|
1149
1232
|
addStaticFilteredOptions(fromDynamic = false) {
|
|
1150
1233
|
const search = this.state.searchText;
|
|
1151
1234
|
let continueLoop = fromDynamic;
|
|
1235
|
+
debug('addStaticFilteredOptions', 'start', 'fromDynamic:', fromDynamic, 'optionBehaviorOperation:', this.state.optionBehaviorOperation);
|
|
1152
1236
|
if (this.state.optionBehaviorOperation !== 'sort') {
|
|
1153
1237
|
return;
|
|
1154
1238
|
}
|
|
@@ -1174,6 +1258,7 @@ class SelecticStore {
|
|
|
1174
1258
|
}
|
|
1175
1259
|
this.setFilteredOptions(options, true);
|
|
1176
1260
|
}
|
|
1261
|
+
debug('addStaticFilteredOptions', 'end');
|
|
1177
1262
|
}
|
|
1178
1263
|
buildSelectedItems(ids) {
|
|
1179
1264
|
return this.buildItems(ids.map((id) => {
|
|
@@ -1215,6 +1300,7 @@ class SelecticStore {
|
|
|
1215
1300
|
}
|
|
1216
1301
|
buildGroupItems(options, previousItem) {
|
|
1217
1302
|
let previousGroupId = previousItem && previousItem.group;
|
|
1303
|
+
debug('buildGroupItems', 'start', 'options:', options.length, 'previousGroupId:', previousGroupId);
|
|
1218
1304
|
const list = this.buildItems(options).reduce((items, item) => {
|
|
1219
1305
|
if (item.group !== previousGroupId) {
|
|
1220
1306
|
const groupId = item.group;
|
|
@@ -1231,6 +1317,7 @@ class SelecticStore {
|
|
|
1231
1317
|
items.push(item);
|
|
1232
1318
|
return items;
|
|
1233
1319
|
}, []);
|
|
1320
|
+
debug('buildGroupItems', 'end', list.length);
|
|
1234
1321
|
return list;
|
|
1235
1322
|
}
|
|
1236
1323
|
buildOptionBehavior(optionBehavior, state) {
|
|
@@ -1348,6 +1435,7 @@ class SelecticStore {
|
|
|
1348
1435
|
}
|
|
1349
1436
|
/** assign new value to the filteredOptions and apply change depending on it */
|
|
1350
1437
|
setFilteredOptions(options, add = false, length = 0) {
|
|
1438
|
+
debug('setFilteredOptions', 'start', 'options:', options.length, 'add', add, 'length', length);
|
|
1351
1439
|
if (!add) {
|
|
1352
1440
|
this.state.filteredOptions = options;
|
|
1353
1441
|
this.state.totalFilteredOptions = length || options.length;
|
package/dist/selectic.esm.js
CHANGED
|
@@ -134,12 +134,25 @@ function compareOptions(oldOptions, newOptions) {
|
|
|
134
134
|
});
|
|
135
135
|
});
|
|
136
136
|
}
|
|
137
|
+
let displayLog = false;
|
|
138
|
+
function debug(fName, step, ...args) {
|
|
139
|
+
if (!displayLog) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
console.log('--%s-- [%s]', fName, step, ...args);
|
|
143
|
+
}
|
|
144
|
+
/** Enable logs for debugging */
|
|
145
|
+
debug.enable = (display) => {
|
|
146
|
+
displayLog = display;
|
|
147
|
+
};
|
|
137
148
|
|
|
138
149
|
/* File Purpose:
|
|
139
150
|
* It keeps and computes all states at a single place.
|
|
140
151
|
* Every inner components of Selectic should communicate with this file to
|
|
141
152
|
* change or to get states.
|
|
142
153
|
*/
|
|
154
|
+
/* For debugging */
|
|
155
|
+
debug.enable(false);
|
|
143
156
|
/* }}} */
|
|
144
157
|
/* {{{ Static */
|
|
145
158
|
function changeTexts$1(texts) {
|
|
@@ -174,12 +187,19 @@ let messages = {
|
|
|
174
187
|
let defaultFamilyIcon = 'selectic';
|
|
175
188
|
let icons = {};
|
|
176
189
|
let closePreviousSelectic;
|
|
190
|
+
/**
|
|
191
|
+
* Time to wait before considering there is no other requests.
|
|
192
|
+
* This time is await only if there is already a requested request.
|
|
193
|
+
*/
|
|
194
|
+
const DEBOUNCE_REQUEST = 250;
|
|
177
195
|
/* }}} */
|
|
178
196
|
let uid = 0;
|
|
179
197
|
class SelecticStore {
|
|
180
198
|
constructor(props = {}) {
|
|
181
199
|
/* Do not need reactivity */
|
|
182
200
|
this.requestId = 0;
|
|
201
|
+
this.requestSearchId = 0; /* Used for search request */
|
|
202
|
+
this.isRequesting = false;
|
|
183
203
|
this._uid = ++uid;
|
|
184
204
|
/* {{{ Props */
|
|
185
205
|
const defaultProps = {
|
|
@@ -295,6 +315,7 @@ class SelecticStore {
|
|
|
295
315
|
this.data.cacheItem.clear();
|
|
296
316
|
this.setAutomaticClose();
|
|
297
317
|
this.commit('isOpen', false);
|
|
318
|
+
this.clearDisplay();
|
|
298
319
|
this.buildAllOptions(true);
|
|
299
320
|
this.buildSelectedOptions();
|
|
300
321
|
}, { deep: true });
|
|
@@ -385,6 +406,7 @@ class SelecticStore {
|
|
|
385
406
|
/* {{{ public methods */
|
|
386
407
|
commit(name, value) {
|
|
387
408
|
const oldValue = this.state[name];
|
|
409
|
+
debug('commit', 'start', name, value, 'oldValue:', oldValue);
|
|
388
410
|
if (oldValue === value) {
|
|
389
411
|
return;
|
|
390
412
|
}
|
|
@@ -393,8 +415,7 @@ class SelecticStore {
|
|
|
393
415
|
case 'searchText':
|
|
394
416
|
this.state.offsetItem = 0;
|
|
395
417
|
this.state.activeItemIdx = -1;
|
|
396
|
-
this.
|
|
397
|
-
this.state.totalFilteredOptions = Infinity;
|
|
418
|
+
this.clearDisplay();
|
|
398
419
|
if (value) {
|
|
399
420
|
this.buildFilteredOptions();
|
|
400
421
|
}
|
|
@@ -442,6 +463,7 @@ class SelecticStore {
|
|
|
442
463
|
}
|
|
443
464
|
break;
|
|
444
465
|
}
|
|
466
|
+
debug('commit', '(done)', name);
|
|
445
467
|
}
|
|
446
468
|
setAutomaticChange() {
|
|
447
469
|
this.state.status.automaticChange = true;
|
|
@@ -627,14 +649,14 @@ class SelecticStore {
|
|
|
627
649
|
this.state.status.errorMessage = '';
|
|
628
650
|
}
|
|
629
651
|
clearCache(forceReset = false) {
|
|
652
|
+
debug('clearCache', 'start', forceReset);
|
|
630
653
|
const isPartial = unref(this.isPartial);
|
|
631
654
|
const total = isPartial ? Infinity : 0;
|
|
632
655
|
this.data.cacheItem.clear();
|
|
633
656
|
this.state.allOptions = [];
|
|
634
657
|
this.state.totalAllOptions = total;
|
|
635
658
|
this.state.totalDynOptions = total;
|
|
636
|
-
this.
|
|
637
|
-
this.state.totalFilteredOptions = Infinity;
|
|
659
|
+
this.clearDisplay();
|
|
638
660
|
this.state.status.errorMessage = '';
|
|
639
661
|
this.state.status.hasChanged = false;
|
|
640
662
|
if (forceReset) {
|
|
@@ -766,6 +788,13 @@ class SelecticStore {
|
|
|
766
788
|
this.checkAutoSelect();
|
|
767
789
|
}
|
|
768
790
|
}
|
|
791
|
+
/** Reset the display cache in order to rebuild it */
|
|
792
|
+
clearDisplay() {
|
|
793
|
+
debug('clearDisplay', 'start');
|
|
794
|
+
this.state.filteredOptions = [];
|
|
795
|
+
this.state.totalFilteredOptions = Infinity;
|
|
796
|
+
}
|
|
797
|
+
/** rebuild the state filteredOptions to normalize their values */
|
|
769
798
|
updateFilteredOptions() {
|
|
770
799
|
if (!this.data.doNotUpdate) {
|
|
771
800
|
this.state.filteredOptions = this.buildItems(this.state.filteredOptions);
|
|
@@ -815,7 +844,7 @@ class SelecticStore {
|
|
|
815
844
|
});
|
|
816
845
|
return listOptions;
|
|
817
846
|
}
|
|
818
|
-
|
|
847
|
+
/** This method is for the computed property elementOptions */
|
|
819
848
|
getElementOptions() {
|
|
820
849
|
const options = deepClone(this.props.childOptions, ['data']);
|
|
821
850
|
const childOptions = [];
|
|
@@ -846,7 +875,9 @@ class SelecticStore {
|
|
|
846
875
|
});
|
|
847
876
|
return childOptions;
|
|
848
877
|
}
|
|
878
|
+
/** Generate the list of all options by combining the 3 option lists */
|
|
849
879
|
buildAllOptions(keepFetched = false, stopFetch = false) {
|
|
880
|
+
debug('buildAllOptions', 'start', 'keepFetched', keepFetched, 'stopFetch', stopFetch);
|
|
850
881
|
const allOptions = [];
|
|
851
882
|
let listOptions = [];
|
|
852
883
|
let elementOptions = [];
|
|
@@ -918,8 +949,6 @@ class SelecticStore {
|
|
|
918
949
|
}
|
|
919
950
|
}
|
|
920
951
|
if (!stopFetch) {
|
|
921
|
-
this.state.filteredOptions = [];
|
|
922
|
-
this.state.totalFilteredOptions = Infinity;
|
|
923
952
|
this.buildFilteredOptions().then(() => {
|
|
924
953
|
/* XXX: To recompute for strict mode and auto-select */
|
|
925
954
|
this.assertCorrectValue();
|
|
@@ -935,18 +964,21 @@ class SelecticStore {
|
|
|
935
964
|
const options = this.filterOptions(allOptions, search);
|
|
936
965
|
this.setFilteredOptions(options);
|
|
937
966
|
}
|
|
967
|
+
debug('buildAllOptions', 'end', 'allOptions:', this.state.allOptions.length, 'totalAllOptions:', this.state.totalAllOptions);
|
|
938
968
|
}
|
|
939
969
|
async buildFilteredOptions() {
|
|
940
|
-
|
|
970
|
+
const state = this.state;
|
|
971
|
+
if (!state.isOpen) {
|
|
941
972
|
/* Do not try to fetch anything while the select is not open */
|
|
942
973
|
return;
|
|
943
974
|
}
|
|
944
|
-
const allOptions =
|
|
945
|
-
const search =
|
|
946
|
-
const totalAllOptions =
|
|
975
|
+
const allOptions = state.allOptions;
|
|
976
|
+
const search = state.searchText;
|
|
977
|
+
const totalAllOptions = state.totalAllOptions;
|
|
947
978
|
const allOptionsLength = allOptions.length;
|
|
948
|
-
let filteredOptionsLength =
|
|
979
|
+
let filteredOptionsLength = state.filteredOptions.length;
|
|
949
980
|
const hasAllItems = unref(this.hasAllItems);
|
|
981
|
+
debug('buildFilteredOptions', 'start', 'hasAllItems:', hasAllItems, 'allOptions', allOptions.length, 'search:', search, 'filteredOptionsLength:', filteredOptionsLength);
|
|
950
982
|
if (hasAllItems) {
|
|
951
983
|
/* Everything has already been fetched and stored in filteredOptions */
|
|
952
984
|
return;
|
|
@@ -963,16 +995,17 @@ class SelecticStore {
|
|
|
963
995
|
return;
|
|
964
996
|
}
|
|
965
997
|
/* When we only have partial options */
|
|
966
|
-
const offsetItem =
|
|
998
|
+
const offsetItem = state.offsetItem;
|
|
967
999
|
const marginSize = unref(this.marginSize);
|
|
968
1000
|
const endIndex = offsetItem + marginSize;
|
|
1001
|
+
debug('buildFilteredOptions', 'partial options', 'offsetItem:', offsetItem, 'marginSize:', marginSize, 'filteredOptionsLength', filteredOptionsLength);
|
|
969
1002
|
if (endIndex <= filteredOptionsLength) {
|
|
970
1003
|
return;
|
|
971
1004
|
}
|
|
972
1005
|
if (!search && endIndex <= allOptionsLength) {
|
|
973
|
-
this.setFilteredOptions(this.buildGroupItems(allOptions), false, totalAllOptions +
|
|
1006
|
+
this.setFilteredOptions(this.buildGroupItems(allOptions), false, totalAllOptions + state.groups.size);
|
|
974
1007
|
const isPartial = unref(this.isPartial);
|
|
975
|
-
if (isPartial &&
|
|
1008
|
+
if (isPartial && state.totalDynOptions === Infinity) {
|
|
976
1009
|
this.fetchData();
|
|
977
1010
|
}
|
|
978
1011
|
return;
|
|
@@ -985,6 +1018,7 @@ class SelecticStore {
|
|
|
985
1018
|
return;
|
|
986
1019
|
}
|
|
987
1020
|
}
|
|
1021
|
+
debug('buildFilteredOptions', 'end', '(will call fetchData)', this.state.filteredOptions.length);
|
|
988
1022
|
await this.fetchData();
|
|
989
1023
|
}
|
|
990
1024
|
async buildSelectedOptions() {
|
|
@@ -1038,6 +1072,39 @@ class SelecticStore {
|
|
|
1038
1072
|
state.selectedOptions = items[0];
|
|
1039
1073
|
}
|
|
1040
1074
|
}
|
|
1075
|
+
async fetchRequest(fetchCallback, search, offset, limit) {
|
|
1076
|
+
const searchRqId = ++this.requestSearchId;
|
|
1077
|
+
if (!search) {
|
|
1078
|
+
++this.requestId;
|
|
1079
|
+
}
|
|
1080
|
+
const requestId = this.requestId;
|
|
1081
|
+
debug('fetchRequest', 'start', 'search:', search, 'offset:', offset, 'limit:', limit, 'requestId:', requestId, 'requestSearchId:', searchRqId, 'isRequesting:', this.isRequesting);
|
|
1082
|
+
if (this.isRequesting) {
|
|
1083
|
+
debug('fetchRequest', `await ${DEBOUNCE_REQUEST}ms`);
|
|
1084
|
+
/* debounce the call to avoid sending too much requests */
|
|
1085
|
+
await new Promise((resolve) => {
|
|
1086
|
+
setTimeout(resolve, DEBOUNCE_REQUEST);
|
|
1087
|
+
});
|
|
1088
|
+
/* Check if there are other requested requests, in such case drop this one */
|
|
1089
|
+
if (requestId !== this.requestId || (search && searchRqId !== this.requestSearchId)) {
|
|
1090
|
+
debug('fetchRequest', '××deprecated××', requestId, searchRqId);
|
|
1091
|
+
return false;
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
this.isRequesting = true;
|
|
1095
|
+
const response = await fetchCallback(search, offset, limit);
|
|
1096
|
+
/* Check if request is obsolete */
|
|
1097
|
+
if (requestId !== this.requestId || (search && searchRqId !== this.requestSearchId)) {
|
|
1098
|
+
debug('fetchRequest', '×××deprecated×××', requestId, searchRqId);
|
|
1099
|
+
return false;
|
|
1100
|
+
}
|
|
1101
|
+
this.isRequesting = false;
|
|
1102
|
+
const deprecated = searchRqId !== this.requestSearchId;
|
|
1103
|
+
debug('fetchRequest', 'end', response.result.length, response.total, deprecated);
|
|
1104
|
+
return Object.assign(Object.assign({}, response), {
|
|
1105
|
+
/* this is to fulfill the cache */
|
|
1106
|
+
deprecated: deprecated });
|
|
1107
|
+
}
|
|
1041
1108
|
async fetchData() {
|
|
1042
1109
|
const state = this.state;
|
|
1043
1110
|
const labels = this.data.labels;
|
|
@@ -1059,9 +1126,14 @@ class SelecticStore {
|
|
|
1059
1126
|
const offset = filteredOptionsLength - this.nbGroups(state.filteredOptions) - dynOffset;
|
|
1060
1127
|
const nbItems = endIndex - offset;
|
|
1061
1128
|
const limit = Math.ceil(nbItems / pageSize) * pageSize;
|
|
1129
|
+
debug('fetchData', 'start', 'search:', search, 'offset:', offset, 'limit:', limit);
|
|
1062
1130
|
try {
|
|
1063
|
-
const
|
|
1064
|
-
|
|
1131
|
+
const response = await this.fetchRequest(fetchCallback, search, offset, limit);
|
|
1132
|
+
if (!response) {
|
|
1133
|
+
debug('fetchData', '×× deprecated ××', search, offset, limit);
|
|
1134
|
+
return;
|
|
1135
|
+
}
|
|
1136
|
+
const { total: rTotal, result, deprecated } = response;
|
|
1065
1137
|
let total = rTotal;
|
|
1066
1138
|
let errorMessage = '';
|
|
1067
1139
|
/* Assert result is correctly formatted */
|
|
@@ -1079,11 +1151,12 @@ class SelecticStore {
|
|
|
1079
1151
|
if (!search) {
|
|
1080
1152
|
/* update cache */
|
|
1081
1153
|
state.totalDynOptions = total;
|
|
1082
|
-
const old = state.dynOptions.splice(offset,
|
|
1154
|
+
const old = state.dynOptions.splice(offset, result.length, ...result);
|
|
1083
1155
|
if (compareOptions(old, result)) {
|
|
1084
1156
|
/* Added options are the same as previous ones.
|
|
1085
1157
|
* Stop fetching to avoid infinite loop
|
|
1086
1158
|
*/
|
|
1159
|
+
debug('fetchData', 'no new values');
|
|
1087
1160
|
if (!unref(this.hasFetchedAllItems)) {
|
|
1088
1161
|
/* Display error if all items are not fetch
|
|
1089
1162
|
* We can have the case where old value and result
|
|
@@ -1091,14 +1164,21 @@ class SelecticStore {
|
|
|
1091
1164
|
* total is 0 */
|
|
1092
1165
|
errorMessage = labels.wrongQueryResult;
|
|
1093
1166
|
}
|
|
1094
|
-
setTimeout(() =>
|
|
1167
|
+
setTimeout(() => {
|
|
1168
|
+
debug('fetchData', 'before buildAllOptions (stopped)', 'offsetItem:', this.state.offsetItem, 'allOptions:', this.state.allOptions.length);
|
|
1169
|
+
this.buildAllOptions(true, true);
|
|
1170
|
+
}, 0);
|
|
1095
1171
|
}
|
|
1096
1172
|
else {
|
|
1097
|
-
setTimeout(() =>
|
|
1173
|
+
setTimeout(() => {
|
|
1174
|
+
debug('fetchData', 'before buildAllOptions', 'offsetItem:', this.state.offsetItem, 'allOptions:', this.state.allOptions.length);
|
|
1175
|
+
this.buildAllOptions(true);
|
|
1176
|
+
}, 0);
|
|
1098
1177
|
}
|
|
1099
1178
|
}
|
|
1100
|
-
/* Check request is not obsolete */
|
|
1101
|
-
if (
|
|
1179
|
+
/* Check request (without search) is not obsolete */
|
|
1180
|
+
if (deprecated) {
|
|
1181
|
+
debug('fetchData', '××× deprecated ×××', search, offset, limit);
|
|
1102
1182
|
return;
|
|
1103
1183
|
}
|
|
1104
1184
|
if (!search) {
|
|
@@ -1127,14 +1207,17 @@ class SelecticStore {
|
|
|
1127
1207
|
}
|
|
1128
1208
|
catch (e) {
|
|
1129
1209
|
state.status.errorMessage = e.message;
|
|
1210
|
+
debug('fetchData', 'error', e.message);
|
|
1130
1211
|
if (!search) {
|
|
1131
1212
|
state.totalDynOptions = 0;
|
|
1132
1213
|
this.buildAllOptions(true, true);
|
|
1133
1214
|
}
|
|
1134
1215
|
}
|
|
1135
1216
|
this.state.status.searching = false;
|
|
1217
|
+
debug('fetchData', 'end');
|
|
1136
1218
|
}
|
|
1137
1219
|
filterOptions(options, search) {
|
|
1220
|
+
debug('filterOptions', 'start', 'options:', options.length, 'search:', search);
|
|
1138
1221
|
if (!search) {
|
|
1139
1222
|
return this.buildGroupItems(options);
|
|
1140
1223
|
}
|
|
@@ -1145,6 +1228,7 @@ class SelecticStore {
|
|
|
1145
1228
|
addStaticFilteredOptions(fromDynamic = false) {
|
|
1146
1229
|
const search = this.state.searchText;
|
|
1147
1230
|
let continueLoop = fromDynamic;
|
|
1231
|
+
debug('addStaticFilteredOptions', 'start', 'fromDynamic:', fromDynamic, 'optionBehaviorOperation:', this.state.optionBehaviorOperation);
|
|
1148
1232
|
if (this.state.optionBehaviorOperation !== 'sort') {
|
|
1149
1233
|
return;
|
|
1150
1234
|
}
|
|
@@ -1170,6 +1254,7 @@ class SelecticStore {
|
|
|
1170
1254
|
}
|
|
1171
1255
|
this.setFilteredOptions(options, true);
|
|
1172
1256
|
}
|
|
1257
|
+
debug('addStaticFilteredOptions', 'end');
|
|
1173
1258
|
}
|
|
1174
1259
|
buildSelectedItems(ids) {
|
|
1175
1260
|
return this.buildItems(ids.map((id) => {
|
|
@@ -1211,6 +1296,7 @@ class SelecticStore {
|
|
|
1211
1296
|
}
|
|
1212
1297
|
buildGroupItems(options, previousItem) {
|
|
1213
1298
|
let previousGroupId = previousItem && previousItem.group;
|
|
1299
|
+
debug('buildGroupItems', 'start', 'options:', options.length, 'previousGroupId:', previousGroupId);
|
|
1214
1300
|
const list = this.buildItems(options).reduce((items, item) => {
|
|
1215
1301
|
if (item.group !== previousGroupId) {
|
|
1216
1302
|
const groupId = item.group;
|
|
@@ -1227,6 +1313,7 @@ class SelecticStore {
|
|
|
1227
1313
|
items.push(item);
|
|
1228
1314
|
return items;
|
|
1229
1315
|
}, []);
|
|
1316
|
+
debug('buildGroupItems', 'end', list.length);
|
|
1230
1317
|
return list;
|
|
1231
1318
|
}
|
|
1232
1319
|
buildOptionBehavior(optionBehavior, state) {
|
|
@@ -1344,6 +1431,7 @@ class SelecticStore {
|
|
|
1344
1431
|
}
|
|
1345
1432
|
/** assign new value to the filteredOptions and apply change depending on it */
|
|
1346
1433
|
setFilteredOptions(options, add = false, length = 0) {
|
|
1434
|
+
debug('setFilteredOptions', 'start', 'options:', options.length, 'add', add, 'length', length);
|
|
1347
1435
|
if (!add) {
|
|
1348
1436
|
this.state.filteredOptions = options;
|
|
1349
1437
|
this.state.totalFilteredOptions = length || options.length;
|