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/doc/breakingChanges.md
CHANGED
|
@@ -7,6 +7,14 @@ version which want to upgrade them to latest version.
|
|
|
7
7
|
|
|
8
8
|
**This is not something you have to read to understand and to use Selectic.**
|
|
9
9
|
|
|
10
|
+
## 3.0.x → 3.1.x
|
|
11
|
+
|
|
12
|
+
Selectic no more depends on Font-awesome. It embeds its own icons (from Material Design Icons).
|
|
13
|
+
|
|
14
|
+
It is still possible to use Font-awesome icons (or from any other libraries).
|
|
15
|
+
|
|
16
|
+
Read [the documentation section related to changing icons](./changeIcons.md) for more information on how to handle them.
|
|
17
|
+
|
|
10
18
|
## 1.3.x → 3.x
|
|
11
19
|
|
|
12
20
|
### Vue2 → Vue3
|
package/doc/changeIcons.md
CHANGED
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
There are some icons in selectic. But sometimes it is useful to change them (because you want to use yours or the same of your favorite theme).
|
|
6
6
|
|
|
7
7
|
There are 3 ways to changes these icons:
|
|
8
|
-
* Call the static `changeIcons()` method. It changes icons for all selectic components.
|
|
8
|
+
* Call the static `changeIcons(icons, iconFamily)` method. It changes icons for all selectic components.
|
|
9
9
|
* Change the `icons` property. It changes icons only for the component.
|
|
10
10
|
* Change the `iconFamily` property. It changes the icons origin only for the component.
|
|
11
|
-
* Call the `changeIcons()` method on the component. It changes icons only for the component.
|
|
11
|
+
* Call the `changeIcons(icons, iconFamily)` method on the component. It changes icons only for the component.
|
|
12
12
|
|
|
13
13
|
_Changes made locally take precedence over changes made globally_.
|
|
14
14
|
|
|
@@ -116,3 +116,9 @@ this.$refs.selectic.changeIcons({
|
|
|
116
116
|
* <span class="fa fa-thumbs-o-up"></span>
|
|
117
117
|
*/
|
|
118
118
|
```
|
|
119
|
+
|
|
120
|
+
It is possible to change only the icon family:
|
|
121
|
+
```javascript
|
|
122
|
+
// change icons for all selectic components to use FA-6 icons
|
|
123
|
+
Selectic.changeIcons(null, 'font-awesome-6');
|
|
124
|
+
```
|
package/package.json
CHANGED
package/src/ExtendedList.tsx
CHANGED
|
@@ -13,13 +13,13 @@ import Icon from './Icon';
|
|
|
13
13
|
|
|
14
14
|
export interface Props {
|
|
15
15
|
store: Store;
|
|
16
|
-
width
|
|
16
|
+
width?: number;
|
|
17
17
|
|
|
18
18
|
/* positions of the main element related to current window */
|
|
19
|
-
elementTop
|
|
20
|
-
elementBottom
|
|
21
|
-
elementLeft
|
|
22
|
-
elementRight
|
|
19
|
+
elementTop?: number;
|
|
20
|
+
elementBottom?: number;
|
|
21
|
+
elementLeft?: number;
|
|
22
|
+
elementRight?: number;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/* list estimation height
|
package/src/Icon.tsx
CHANGED
|
@@ -30,7 +30,7 @@ export default class Icon extends Vue<Props> {
|
|
|
30
30
|
private store: Store;
|
|
31
31
|
|
|
32
32
|
@Prop()
|
|
33
|
-
private icon:
|
|
33
|
+
private icon: string;
|
|
34
34
|
|
|
35
35
|
@Prop()
|
|
36
36
|
private spin?: boolean;
|
|
@@ -42,7 +42,7 @@ export default class Icon extends Vue<Props> {
|
|
|
42
42
|
/* {{{ computed */
|
|
43
43
|
|
|
44
44
|
private get rawIconValue(): IconValue {
|
|
45
|
-
const key = this.icon;
|
|
45
|
+
const key = this.icon as IconKey;
|
|
46
46
|
const icon = this.store.data.icons[key];
|
|
47
47
|
|
|
48
48
|
if (icon === undefined) {
|
package/src/List.tsx
CHANGED
package/src/Store.tsx
CHANGED
|
@@ -5,7 +5,16 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { reactive, watch, unref, computed, ComputedRef } from 'vue';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
assignObject,
|
|
10
|
+
compareOptions,
|
|
11
|
+
convertToRegExp,
|
|
12
|
+
debug,
|
|
13
|
+
deepClone,
|
|
14
|
+
} from './tools';
|
|
15
|
+
|
|
16
|
+
/* For debugging */
|
|
17
|
+
debug.enable(false);
|
|
9
18
|
|
|
10
19
|
/* {{{ Types definitions */
|
|
11
20
|
|
|
@@ -52,8 +61,21 @@ export interface GroupValue {
|
|
|
52
61
|
text: string;
|
|
53
62
|
}
|
|
54
63
|
|
|
64
|
+
export type RequestResult = {
|
|
65
|
+
/** The total number of expecting options.
|
|
66
|
+
* Needed to know if there are more items to fetch, and to size the scrollbar.
|
|
67
|
+
*/
|
|
68
|
+
total: number;
|
|
69
|
+
|
|
70
|
+
/** The list of the options. */
|
|
71
|
+
result: OptionValue[];
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/** false means that the request is deprecated */
|
|
75
|
+
type FetchResult = RequestResult & { deprecated: boolean; } | false;
|
|
76
|
+
|
|
55
77
|
export type FetchCallback = (_search: string, _offsetItem: number, _pageSize: number)
|
|
56
|
-
=> Promise<
|
|
78
|
+
=> Promise<RequestResult>;
|
|
57
79
|
|
|
58
80
|
export type GetCallback = (_ids: OptionId[])
|
|
59
81
|
=> Promise<OptionValue[]>;
|
|
@@ -438,6 +460,12 @@ let icons: PartialIcons = {
|
|
|
438
460
|
|
|
439
461
|
let closePreviousSelectic: undefined | voidCaller;
|
|
440
462
|
|
|
463
|
+
/**
|
|
464
|
+
* Time to wait before considering there is no other requests.
|
|
465
|
+
* This time is await only if there is already a requested request.
|
|
466
|
+
*/
|
|
467
|
+
const DEBOUNCE_REQUEST = 250;
|
|
468
|
+
|
|
441
469
|
/* }}} */
|
|
442
470
|
|
|
443
471
|
let uid = 0;
|
|
@@ -452,6 +480,8 @@ export default class SelecticStore {
|
|
|
452
480
|
|
|
453
481
|
/* Do not need reactivity */
|
|
454
482
|
private requestId: number = 0;
|
|
483
|
+
private requestSearchId: number = 0; /* Used for search request */
|
|
484
|
+
private isRequesting: boolean = false;
|
|
455
485
|
private cacheRequest: Map<string, Promise<OptionValue[]>>;
|
|
456
486
|
private closeSelectic: () => void;
|
|
457
487
|
|
|
@@ -612,6 +642,7 @@ export default class SelecticStore {
|
|
|
612
642
|
this.data.cacheItem.clear();
|
|
613
643
|
this.setAutomaticClose();
|
|
614
644
|
this.commit('isOpen', false);
|
|
645
|
+
this.clearDisplay();
|
|
615
646
|
this.buildAllOptions(true);
|
|
616
647
|
this.buildSelectedOptions();
|
|
617
648
|
}, { deep: true });
|
|
@@ -734,6 +765,8 @@ export default class SelecticStore {
|
|
|
734
765
|
{
|
|
735
766
|
const oldValue = this.state[name];
|
|
736
767
|
|
|
768
|
+
debug('commit', 'start', name, value, 'oldValue:', oldValue);
|
|
769
|
+
|
|
737
770
|
if (oldValue === value) {
|
|
738
771
|
return;
|
|
739
772
|
}
|
|
@@ -744,8 +777,8 @@ export default class SelecticStore {
|
|
|
744
777
|
case 'searchText':
|
|
745
778
|
this.state.offsetItem = 0;
|
|
746
779
|
this.state.activeItemIdx = -1;
|
|
747
|
-
this.
|
|
748
|
-
|
|
780
|
+
this.clearDisplay();
|
|
781
|
+
|
|
749
782
|
if (value) {
|
|
750
783
|
this.buildFilteredOptions();
|
|
751
784
|
} else {
|
|
@@ -793,6 +826,7 @@ export default class SelecticStore {
|
|
|
793
826
|
}
|
|
794
827
|
break;
|
|
795
828
|
}
|
|
829
|
+
debug('commit', '(done)', name);
|
|
796
830
|
}
|
|
797
831
|
|
|
798
832
|
public setAutomaticChange() {
|
|
@@ -1014,6 +1048,7 @@ export default class SelecticStore {
|
|
|
1014
1048
|
}
|
|
1015
1049
|
|
|
1016
1050
|
public clearCache(forceReset = false) {
|
|
1051
|
+
debug('clearCache', 'start', forceReset);
|
|
1017
1052
|
const isPartial = unref(this.isPartial);
|
|
1018
1053
|
const total = isPartial ? Infinity : 0;
|
|
1019
1054
|
|
|
@@ -1023,8 +1058,7 @@ export default class SelecticStore {
|
|
|
1023
1058
|
this.state.totalAllOptions = total;
|
|
1024
1059
|
this.state.totalDynOptions = total;
|
|
1025
1060
|
|
|
1026
|
-
this.
|
|
1027
|
-
this.state.totalFilteredOptions = Infinity;
|
|
1061
|
+
this.clearDisplay();
|
|
1028
1062
|
|
|
1029
1063
|
this.state.status.errorMessage = '';
|
|
1030
1064
|
this.state.status.hasChanged = false;
|
|
@@ -1183,6 +1217,14 @@ export default class SelecticStore {
|
|
|
1183
1217
|
}
|
|
1184
1218
|
}
|
|
1185
1219
|
|
|
1220
|
+
/** Reset the display cache in order to rebuild it */
|
|
1221
|
+
private clearDisplay() {
|
|
1222
|
+
debug('clearDisplay', 'start');
|
|
1223
|
+
this.state.filteredOptions = [];
|
|
1224
|
+
this.state.totalFilteredOptions = Infinity;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
/** rebuild the state filteredOptions to normalize their values */
|
|
1186
1228
|
private updateFilteredOptions() {
|
|
1187
1229
|
if (!this.data.doNotUpdate) {
|
|
1188
1230
|
this.state.filteredOptions = this.buildItems(this.state.filteredOptions);
|
|
@@ -1243,7 +1285,7 @@ export default class SelecticStore {
|
|
|
1243
1285
|
return listOptions;
|
|
1244
1286
|
}
|
|
1245
1287
|
|
|
1246
|
-
|
|
1288
|
+
/** This method is for the computed property elementOptions */
|
|
1247
1289
|
private getElementOptions(): OptionValue[] {
|
|
1248
1290
|
const options = deepClone(this.props.childOptions, ['data']);
|
|
1249
1291
|
const childOptions: OptionValue[] = [];
|
|
@@ -1282,7 +1324,9 @@ export default class SelecticStore {
|
|
|
1282
1324
|
return childOptions;
|
|
1283
1325
|
}
|
|
1284
1326
|
|
|
1327
|
+
/** Generate the list of all options by combining the 3 option lists */
|
|
1285
1328
|
private buildAllOptions(keepFetched = false, stopFetch = false) {
|
|
1329
|
+
debug('buildAllOptions', 'start', 'keepFetched', keepFetched, 'stopFetch', stopFetch);
|
|
1286
1330
|
const allOptions: OptionValue[] = [];
|
|
1287
1331
|
let listOptions: OptionValue[] = [];
|
|
1288
1332
|
let elementOptions: OptionValue[] = [];
|
|
@@ -1360,9 +1404,6 @@ export default class SelecticStore {
|
|
|
1360
1404
|
}
|
|
1361
1405
|
|
|
1362
1406
|
if (!stopFetch) {
|
|
1363
|
-
this.state.filteredOptions = [];
|
|
1364
|
-
this.state.totalFilteredOptions = Infinity;
|
|
1365
|
-
|
|
1366
1407
|
this.buildFilteredOptions().then(() => {
|
|
1367
1408
|
/* XXX: To recompute for strict mode and auto-select */
|
|
1368
1409
|
this.assertCorrectValue();
|
|
@@ -1377,21 +1418,27 @@ export default class SelecticStore {
|
|
|
1377
1418
|
const options = this.filterOptions(allOptions, search);
|
|
1378
1419
|
this.setFilteredOptions(options);
|
|
1379
1420
|
}
|
|
1421
|
+
|
|
1422
|
+
debug('buildAllOptions', 'end', 'allOptions:', this.state.allOptions.length, 'totalAllOptions:', this.state.totalAllOptions);
|
|
1380
1423
|
}
|
|
1381
1424
|
|
|
1382
1425
|
private async buildFilteredOptions() {
|
|
1383
|
-
|
|
1426
|
+
const state = this.state;
|
|
1427
|
+
|
|
1428
|
+
if (!state.isOpen) {
|
|
1384
1429
|
/* Do not try to fetch anything while the select is not open */
|
|
1385
1430
|
return;
|
|
1386
1431
|
}
|
|
1387
1432
|
|
|
1388
|
-
const allOptions =
|
|
1389
|
-
const search =
|
|
1390
|
-
const totalAllOptions =
|
|
1433
|
+
const allOptions = state.allOptions;
|
|
1434
|
+
const search = state.searchText;
|
|
1435
|
+
const totalAllOptions = state.totalAllOptions;
|
|
1391
1436
|
const allOptionsLength = allOptions.length;
|
|
1392
|
-
let filteredOptionsLength =
|
|
1437
|
+
let filteredOptionsLength = state.filteredOptions.length;
|
|
1393
1438
|
const hasAllItems = unref(this.hasAllItems);
|
|
1394
1439
|
|
|
1440
|
+
debug('buildFilteredOptions', 'start', 'hasAllItems:', hasAllItems, 'allOptions', allOptions.length, 'search:', search, 'filteredOptionsLength:', filteredOptionsLength);
|
|
1441
|
+
|
|
1395
1442
|
if (hasAllItems) {
|
|
1396
1443
|
/* Everything has already been fetched and stored in filteredOptions */
|
|
1397
1444
|
return;
|
|
@@ -1412,18 +1459,22 @@ export default class SelecticStore {
|
|
|
1412
1459
|
|
|
1413
1460
|
/* When we only have partial options */
|
|
1414
1461
|
|
|
1415
|
-
const offsetItem =
|
|
1462
|
+
const offsetItem = state.offsetItem;
|
|
1416
1463
|
const marginSize = unref(this.marginSize);
|
|
1417
1464
|
const endIndex = offsetItem + marginSize;
|
|
1418
1465
|
|
|
1466
|
+
|
|
1467
|
+
debug('buildFilteredOptions', 'partial options', 'offsetItem:',offsetItem, 'marginSize:',marginSize, 'filteredOptionsLength', filteredOptionsLength);
|
|
1468
|
+
|
|
1419
1469
|
if (endIndex <= filteredOptionsLength) {
|
|
1420
1470
|
return;
|
|
1421
1471
|
}
|
|
1422
1472
|
|
|
1423
1473
|
if (!search && endIndex <= allOptionsLength) {
|
|
1424
|
-
this.setFilteredOptions(this.buildGroupItems(allOptions), false, totalAllOptions +
|
|
1474
|
+
this.setFilteredOptions(this.buildGroupItems(allOptions), false, totalAllOptions + state.groups.size);
|
|
1475
|
+
|
|
1425
1476
|
const isPartial = unref(this.isPartial);
|
|
1426
|
-
if (isPartial &&
|
|
1477
|
+
if (isPartial && state.totalDynOptions === Infinity) {
|
|
1427
1478
|
this.fetchData();
|
|
1428
1479
|
}
|
|
1429
1480
|
return;
|
|
@@ -1439,6 +1490,7 @@ export default class SelecticStore {
|
|
|
1439
1490
|
}
|
|
1440
1491
|
}
|
|
1441
1492
|
|
|
1493
|
+
debug('buildFilteredOptions', 'end', '(will call fetchData)', this.state.filteredOptions.length);
|
|
1442
1494
|
await this.fetchData();
|
|
1443
1495
|
}
|
|
1444
1496
|
|
|
@@ -1502,6 +1554,53 @@ export default class SelecticStore {
|
|
|
1502
1554
|
}
|
|
1503
1555
|
}
|
|
1504
1556
|
|
|
1557
|
+
private async fetchRequest(fetchCallback: FetchCallback, search: string, offset: number, limit: number): Promise<FetchResult> {
|
|
1558
|
+
const searchRqId = ++this.requestSearchId;
|
|
1559
|
+
|
|
1560
|
+
if (!search) {
|
|
1561
|
+
++this.requestId
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
const requestId = this.requestId;
|
|
1565
|
+
|
|
1566
|
+
debug('fetchRequest', 'start', 'search:', search, 'offset:', offset, 'limit:', limit, 'requestId:', requestId, 'requestSearchId:', searchRqId, 'isRequesting:', this.isRequesting);
|
|
1567
|
+
|
|
1568
|
+
if (this.isRequesting) {
|
|
1569
|
+
debug('fetchRequest', `await ${DEBOUNCE_REQUEST}ms`);
|
|
1570
|
+
|
|
1571
|
+
/* debounce the call to avoid sending too much requests */
|
|
1572
|
+
await new Promise((resolve) => {
|
|
1573
|
+
setTimeout(resolve, DEBOUNCE_REQUEST);
|
|
1574
|
+
});
|
|
1575
|
+
|
|
1576
|
+
/* Check if there are other requested requests, in such case drop this one */
|
|
1577
|
+
if (requestId !== this.requestId || (search && searchRqId !== this.requestSearchId)) {
|
|
1578
|
+
debug('fetchRequest', '××deprecated××', requestId, searchRqId);
|
|
1579
|
+
return false;
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
this.isRequesting = true;
|
|
1584
|
+
const response = await fetchCallback(search, offset, limit);
|
|
1585
|
+
|
|
1586
|
+
/* Check if request is obsolete */
|
|
1587
|
+
if (requestId !== this.requestId || (search && searchRqId !== this.requestSearchId)) {
|
|
1588
|
+
debug('fetchRequest', '×××deprecated×××', requestId, searchRqId);
|
|
1589
|
+
return false;
|
|
1590
|
+
}
|
|
1591
|
+
|
|
1592
|
+
this.isRequesting = false;
|
|
1593
|
+
|
|
1594
|
+
const deprecated = searchRqId !== this.requestSearchId;
|
|
1595
|
+
debug('fetchRequest', 'end', response.result.length, response.total, deprecated);
|
|
1596
|
+
|
|
1597
|
+
return {
|
|
1598
|
+
...response,
|
|
1599
|
+
/* this is to fulfill the cache */
|
|
1600
|
+
deprecated: deprecated,
|
|
1601
|
+
};
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1505
1604
|
private async fetchData() {
|
|
1506
1605
|
const state = this.state;
|
|
1507
1606
|
const labels = this.data.labels;
|
|
@@ -1528,9 +1627,18 @@ export default class SelecticStore {
|
|
|
1528
1627
|
const nbItems = endIndex - offset;
|
|
1529
1628
|
const limit = Math.ceil(nbItems / pageSize) * pageSize;
|
|
1530
1629
|
|
|
1630
|
+
debug('fetchData', 'start', 'search:', search, 'offset:', offset, 'limit:', limit);
|
|
1631
|
+
|
|
1531
1632
|
try {
|
|
1532
|
-
const
|
|
1533
|
-
|
|
1633
|
+
const response = await this.fetchRequest(fetchCallback, search, offset, limit);
|
|
1634
|
+
|
|
1635
|
+
if (!response) {
|
|
1636
|
+
debug('fetchData', '×× deprecated ××', search, offset, limit);
|
|
1637
|
+
return;
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
const {total: rTotal, result, deprecated} = response;
|
|
1641
|
+
|
|
1534
1642
|
let total = rTotal;
|
|
1535
1643
|
let errorMessage = '';
|
|
1536
1644
|
|
|
@@ -1552,11 +1660,15 @@ export default class SelecticStore {
|
|
|
1552
1660
|
if (!search) {
|
|
1553
1661
|
/* update cache */
|
|
1554
1662
|
state.totalDynOptions = total;
|
|
1555
|
-
const old = state.dynOptions.splice(offset,
|
|
1663
|
+
const old = state.dynOptions.splice(offset, result.length, ...result);
|
|
1664
|
+
|
|
1556
1665
|
if (compareOptions(old, result)) {
|
|
1557
1666
|
/* Added options are the same as previous ones.
|
|
1558
1667
|
* Stop fetching to avoid infinite loop
|
|
1559
1668
|
*/
|
|
1669
|
+
|
|
1670
|
+
debug('fetchData', 'no new values');
|
|
1671
|
+
|
|
1560
1672
|
if (!unref(this.hasFetchedAllItems)) {
|
|
1561
1673
|
/* Display error if all items are not fetch
|
|
1562
1674
|
* We can have the case where old value and result
|
|
@@ -1564,14 +1676,22 @@ export default class SelecticStore {
|
|
|
1564
1676
|
* total is 0 */
|
|
1565
1677
|
errorMessage = labels.wrongQueryResult;
|
|
1566
1678
|
}
|
|
1567
|
-
|
|
1679
|
+
|
|
1680
|
+
setTimeout(() => {
|
|
1681
|
+
debug('fetchData', 'before buildAllOptions (stopped)', 'offsetItem:', this.state.offsetItem, 'allOptions:', this.state.allOptions.length);
|
|
1682
|
+
this.buildAllOptions(true, true)
|
|
1683
|
+
}, 0);
|
|
1568
1684
|
} else {
|
|
1569
|
-
setTimeout(() =>
|
|
1685
|
+
setTimeout(() => {
|
|
1686
|
+
debug('fetchData', 'before buildAllOptions', 'offsetItem:', this.state.offsetItem, 'allOptions:', this.state.allOptions.length);
|
|
1687
|
+
this.buildAllOptions(true)
|
|
1688
|
+
}, 0);
|
|
1570
1689
|
}
|
|
1571
1690
|
}
|
|
1572
1691
|
|
|
1573
|
-
/* Check request is not obsolete */
|
|
1574
|
-
if (
|
|
1692
|
+
/* Check request (without search) is not obsolete */
|
|
1693
|
+
if (deprecated) {
|
|
1694
|
+
debug('fetchData', '××× deprecated ×××', search, offset, limit);
|
|
1575
1695
|
return;
|
|
1576
1696
|
}
|
|
1577
1697
|
|
|
@@ -1604,6 +1724,8 @@ export default class SelecticStore {
|
|
|
1604
1724
|
state.status.errorMessage = errorMessage;
|
|
1605
1725
|
} catch (e) {
|
|
1606
1726
|
state.status.errorMessage = (e as Error).message;
|
|
1727
|
+
debug('fetchData', 'error', (e as Error).message);
|
|
1728
|
+
|
|
1607
1729
|
if (!search) {
|
|
1608
1730
|
state.totalDynOptions = 0;
|
|
1609
1731
|
this.buildAllOptions(true, true);
|
|
@@ -1611,9 +1733,13 @@ export default class SelecticStore {
|
|
|
1611
1733
|
}
|
|
1612
1734
|
|
|
1613
1735
|
this.state.status.searching = false;
|
|
1736
|
+
|
|
1737
|
+
debug('fetchData', 'end');
|
|
1614
1738
|
}
|
|
1615
1739
|
|
|
1616
1740
|
private filterOptions(options: OptionValue[], search: string): OptionItem[] {
|
|
1741
|
+
debug('filterOptions', 'start', 'options:', options.length, 'search:', search);
|
|
1742
|
+
|
|
1617
1743
|
if (!search) {
|
|
1618
1744
|
return this.buildGroupItems(options);
|
|
1619
1745
|
}
|
|
@@ -1629,6 +1755,8 @@ export default class SelecticStore {
|
|
|
1629
1755
|
const search = this.state.searchText;
|
|
1630
1756
|
let continueLoop = fromDynamic;
|
|
1631
1757
|
|
|
1758
|
+
debug('addStaticFilteredOptions', 'start', 'fromDynamic:', fromDynamic, 'optionBehaviorOperation:', this.state.optionBehaviorOperation);
|
|
1759
|
+
|
|
1632
1760
|
if (this.state.optionBehaviorOperation !== 'sort') {
|
|
1633
1761
|
return;
|
|
1634
1762
|
}
|
|
@@ -1655,6 +1783,8 @@ export default class SelecticStore {
|
|
|
1655
1783
|
}
|
|
1656
1784
|
this.setFilteredOptions(options, true);
|
|
1657
1785
|
}
|
|
1786
|
+
|
|
1787
|
+
debug('addStaticFilteredOptions', 'end');
|
|
1658
1788
|
}
|
|
1659
1789
|
|
|
1660
1790
|
private buildSelectedItems(ids: OptionId[]): OptionItem[] {
|
|
@@ -1709,6 +1839,8 @@ export default class SelecticStore {
|
|
|
1709
1839
|
{
|
|
1710
1840
|
let previousGroupId = previousItem && previousItem.group;
|
|
1711
1841
|
|
|
1842
|
+
debug('buildGroupItems', 'start', 'options:', options.length, 'previousGroupId:', previousGroupId);
|
|
1843
|
+
|
|
1712
1844
|
const list = this.buildItems(options).reduce((items: OptionItem[], item) => {
|
|
1713
1845
|
if (item.group !== previousGroupId) {
|
|
1714
1846
|
const groupId = item.group as StrictOptionId;
|
|
@@ -1730,6 +1862,8 @@ export default class SelecticStore {
|
|
|
1730
1862
|
return items;
|
|
1731
1863
|
}, []);
|
|
1732
1864
|
|
|
1865
|
+
debug('buildGroupItems', 'end', list.length);
|
|
1866
|
+
|
|
1733
1867
|
return list;
|
|
1734
1868
|
}
|
|
1735
1869
|
|
|
@@ -1871,6 +2005,8 @@ export default class SelecticStore {
|
|
|
1871
2005
|
|
|
1872
2006
|
/** assign new value to the filteredOptions and apply change depending on it */
|
|
1873
2007
|
private setFilteredOptions(options: OptionItem[], add = false, length = 0) {
|
|
2008
|
+
debug('setFilteredOptions', 'start', 'options:', options.length, 'add', add, 'length', length);
|
|
2009
|
+
|
|
1874
2010
|
if (!add) {
|
|
1875
2011
|
this.state.filteredOptions = options;
|
|
1876
2012
|
this.state.totalFilteredOptions = length || options.length;
|
package/src/index.tsx
CHANGED
|
@@ -206,11 +206,11 @@ export interface Props {
|
|
|
206
206
|
iconFamily?: IconFamily;
|
|
207
207
|
|
|
208
208
|
/** If enabled, it resets the dynamic cache when selectic opens */
|
|
209
|
-
noCache?:
|
|
209
|
+
noCache?: boolean;
|
|
210
210
|
|
|
211
211
|
/** If true, the component opens (at start or if it is closed).
|
|
212
212
|
* If false, the components closes (if it is opened). */
|
|
213
|
-
open?:
|
|
213
|
+
open?: boolean;
|
|
214
214
|
|
|
215
215
|
/** Props which is not expected to change during the life time of the
|
|
216
216
|
* component.
|
package/src/tools.ts
CHANGED
|
@@ -117,3 +117,16 @@ export function compareOptions(oldOptions: OptionValue[], newOptions: OptionValu
|
|
|
117
117
|
});
|
|
118
118
|
});
|
|
119
119
|
}
|
|
120
|
+
|
|
121
|
+
let displayLog = false;
|
|
122
|
+
export function debug(fName: string, step: string, ...args: any[]) {
|
|
123
|
+
if (!displayLog) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
console.log('--%s-- [%s]', fName, step, ...args);
|
|
128
|
+
}
|
|
129
|
+
/** Enable logs for debugging */
|
|
130
|
+
debug.enable = (display: boolean) => {
|
|
131
|
+
displayLog = display;
|
|
132
|
+
};
|
|
@@ -788,7 +788,7 @@ tape.test('Store creation', (subT) => {
|
|
|
788
788
|
|
|
789
789
|
/* Load data */
|
|
790
790
|
store.commit('isOpen', true);
|
|
791
|
-
await _.nextVueTick(store, spy.promise);
|
|
791
|
+
await _.nextVueTick(store, spy.promise, sleep(0) /* await request resolution */);
|
|
792
792
|
store.commit('isOpen', false);
|
|
793
793
|
await sleep(0);
|
|
794
794
|
|
|
@@ -1343,7 +1343,7 @@ tape.test('Store creation', (subT) => {
|
|
|
1343
1343
|
await sleep(0);
|
|
1344
1344
|
t.is(store1.state.filteredOptions.length, 0);
|
|
1345
1345
|
command1.fetch();
|
|
1346
|
-
await _.nextVueTick(store1, spy1.promise);
|
|
1346
|
+
await _.nextVueTick(store1, spy1.promise, sleep(0) /* await request resolution */);
|
|
1347
1347
|
|
|
1348
1348
|
t.is(store1.state.filteredOptions.length, 4);
|
|
1349
1349
|
|
|
@@ -1385,7 +1385,7 @@ tape.test('Store creation', (subT) => {
|
|
|
1385
1385
|
store1.commit('isOpen', true);
|
|
1386
1386
|
await sleep(0);
|
|
1387
1387
|
command1.fetch();
|
|
1388
|
-
await _.nextVueTick(store1, spy1.promise);
|
|
1388
|
+
await _.nextVueTick(store1, spy1.promise, sleep(0) /* await request resolution */);
|
|
1389
1389
|
|
|
1390
1390
|
t.is(store1.state.filteredOptions.length, 3, 'should contain the groups');
|
|
1391
1391
|
|
|
@@ -1405,7 +1405,7 @@ tape.test('Store creation', (subT) => {
|
|
|
1405
1405
|
store2.commit('isOpen', true);
|
|
1406
1406
|
await sleep(0);
|
|
1407
1407
|
command2.fetch();
|
|
1408
|
-
await _.nextVueTick(store2, spy2.promise);
|
|
1408
|
+
await _.nextVueTick(store2, spy2.promise, sleep(0) /* await request resolution */);
|
|
1409
1409
|
|
|
1410
1410
|
t.is(store2.state.filteredOptions.length, 4);
|
|
1411
1411
|
|
|
@@ -1425,7 +1425,7 @@ tape.test('Store creation', (subT) => {
|
|
|
1425
1425
|
store3.commit('isOpen', true);
|
|
1426
1426
|
await sleep(0);
|
|
1427
1427
|
command3.fetch();
|
|
1428
|
-
await _.nextVueTick(store3, spy3.promise);
|
|
1428
|
+
await _.nextVueTick(store3, spy3.promise, sleep(0) /* await request resolution */);
|
|
1429
1429
|
|
|
1430
1430
|
t.is(store3.state.filteredOptions.length, 2);
|
|
1431
1431
|
t.end();
|
|
@@ -1449,7 +1449,7 @@ tape.test('Store creation', (subT) => {
|
|
|
1449
1449
|
store1.commit('isOpen', true);
|
|
1450
1450
|
await sleep(0);
|
|
1451
1451
|
command1.fetch();
|
|
1452
|
-
await _.nextVueTick(store1, spy1.promise);
|
|
1452
|
+
await _.nextVueTick(store1, spy1.promise, sleep(0) /* await request resolution */);
|
|
1453
1453
|
|
|
1454
1454
|
t.is(store1.state.filteredOptions.length, 3);
|
|
1455
1455
|
|
|
@@ -1470,7 +1470,7 @@ tape.test('Store creation', (subT) => {
|
|
|
1470
1470
|
store2.commit('isOpen', true);
|
|
1471
1471
|
await sleep(0);
|
|
1472
1472
|
command2.fetch();
|
|
1473
|
-
await _.nextVueTick(store2, spy2.promise);
|
|
1473
|
+
await _.nextVueTick(store2, spy2.promise, sleep(0) /* await request resolution */);
|
|
1474
1474
|
|
|
1475
1475
|
t.is(store2.state.filteredOptions.length, 4);
|
|
1476
1476
|
|
|
@@ -1491,7 +1491,7 @@ tape.test('Store creation', (subT) => {
|
|
|
1491
1491
|
store3.commit('isOpen', true);
|
|
1492
1492
|
await sleep(0);
|
|
1493
1493
|
command3.fetch();
|
|
1494
|
-
await _.nextVueTick(store3, spy3.promise);
|
|
1494
|
+
await _.nextVueTick(store3, spy3.promise, sleep(0) /* await request resolution */);
|
|
1495
1495
|
|
|
1496
1496
|
t.is(store3.state.filteredOptions.length, 2);
|
|
1497
1497
|
t.end();
|
|
@@ -1540,7 +1540,7 @@ tape.test('Store creation', (subT) => {
|
|
|
1540
1540
|
await sleep(0);
|
|
1541
1541
|
t.is(store1.state.filteredOptions.length, 0);
|
|
1542
1542
|
command1.fetch();
|
|
1543
|
-
await _.nextVueTick(store1, spy1.promise);
|
|
1543
|
+
await _.nextVueTick(store1, spy1.promise, sleep(0) /* await request resolution */);
|
|
1544
1544
|
|
|
1545
1545
|
t.is(store1.state.filteredOptions.length, 9);
|
|
1546
1546
|
t.end();
|
|
@@ -1565,7 +1565,7 @@ tape.test('Store creation', (subT) => {
|
|
|
1565
1565
|
t.is(store.state.filteredOptions.length, 5); // previous options should be displayed
|
|
1566
1566
|
t.true(toHaveBeenCalledWith(spy, ['', 0, 100])); // dynamic index should always start at 0
|
|
1567
1567
|
command.fetch();
|
|
1568
|
-
await _.nextVueTick(store, spy.promise);
|
|
1568
|
+
await _.nextVueTick(store, spy.promise, sleep(0) /* await request resolution */);
|
|
1569
1569
|
|
|
1570
1570
|
t.is(store.state.filteredOptions.length, 9); // finally all options should be displayed
|
|
1571
1571
|
t.end();
|
|
@@ -1590,7 +1590,7 @@ tape.test('Store creation', (subT) => {
|
|
|
1590
1590
|
t.true(toHaveBeenCalledWith(spy1, ['', 0, 100]));
|
|
1591
1591
|
|
|
1592
1592
|
command1.fetch();
|
|
1593
|
-
await _.nextVueTick(store1, spy1.promise);
|
|
1593
|
+
await _.nextVueTick(store1, spy1.promise, sleep(0) /* await request resolution */);
|
|
1594
1594
|
|
|
1595
1595
|
t.is(store1.state.allOptions.length, 104);
|
|
1596
1596
|
t.is(store1.state.filteredOptions.length, 104);
|
|
@@ -1675,7 +1675,7 @@ tape.test('Store creation', (subT) => {
|
|
|
1675
1675
|
t.true(toHaveBeenCalled(spy));
|
|
1676
1676
|
|
|
1677
1677
|
command.fetch();
|
|
1678
|
-
await _.nextVueTick(store, spy.promise);
|
|
1678
|
+
await _.nextVueTick(store, spy.promise, sleep(0) /* await request resolution */);
|
|
1679
1679
|
|
|
1680
1680
|
t.is(store.state.isOpen, true);
|
|
1681
1681
|
t.is(store.state.filteredOptions.length, 4);
|