@rescui/select 0.20.1 → 0.21.1
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/lib/_virtual/index.css.js +12 -12
- package/lib/hooks/use-options.d.ts +2 -1
- package/lib/hooks/use-options.js +94 -32
- package/lib/index.css +190 -190
- package/lib/select-searchable.js +269 -151
- package/lib/styles/common.p.module.css.js +1 -1
- package/lib/styles/form-trigger.p.module.css.js +1 -1
- package/lib/styles/list-box.p.module.css.js +1 -1
- package/package.json +9 -9
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
var styles = {
|
|
2
|
-
"icon": "
|
|
3
|
-
"sizeXS": "
|
|
4
|
-
"sizeS": "
|
|
5
|
-
"sizeM": "
|
|
6
|
-
"sizeL": "
|
|
7
|
-
"light": "
|
|
8
|
-
"dark": "
|
|
9
|
-
"allVariants": "
|
|
10
|
-
"variant16": "
|
|
11
|
-
"variant20": "
|
|
12
|
-
"variant24": "
|
|
13
|
-
"variant28": "
|
|
2
|
+
"icon": "_icon_g46ng3_4",
|
|
3
|
+
"sizeXS": "_sizeXS_g46ng3_14",
|
|
4
|
+
"sizeS": "_sizeS_g46ng3_18",
|
|
5
|
+
"sizeM": "_sizeM_g46ng3_22",
|
|
6
|
+
"sizeL": "_sizeL_g46ng3_26",
|
|
7
|
+
"light": "_light_g46ng3_30",
|
|
8
|
+
"dark": "_dark_g46ng3_34",
|
|
9
|
+
"allVariants": "_allVariants_g46ng3_48",
|
|
10
|
+
"variant16": "_variant16_g46ng3_53",
|
|
11
|
+
"variant20": "_variant20_g46ng3_54",
|
|
12
|
+
"variant24": "_variant24_g46ng3_55",
|
|
13
|
+
"variant28": "_variant28_g46ng3_58"
|
|
14
14
|
};
|
|
15
15
|
|
|
16
16
|
export { styles as default };
|
|
@@ -3,7 +3,8 @@ import type { SelectOptionOrGroup, SelectOption, ListBoxItem } from '../types';
|
|
|
3
3
|
/**
|
|
4
4
|
* Convert an array of options with possible groups to the flat array of options, stripping them of the groups.
|
|
5
5
|
* Also creating a list for ListBox component with indexes and keys.
|
|
6
|
-
* Supports filtering by returning the setFilterString function
|
|
6
|
+
* Supports filtering by returning the setFilterString function: options that
|
|
7
|
+
* contain the filter string are kept, the ones starting with it go first.
|
|
7
8
|
* @param options
|
|
8
9
|
*/
|
|
9
10
|
export declare const useOptions: (options: SelectOptionOrGroup[]) => {
|
package/lib/hooks/use-options.js
CHANGED
|
@@ -4,20 +4,92 @@ import { isOptionAGroup } from '../utils/is-option-a-group.js';
|
|
|
4
4
|
import { getTextFromNodes } from '../utils/get-text-from-nodes.js';
|
|
5
5
|
import { getOptionValue } from '../utils/get-option-value.js';
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const RANK_ANY = 0;
|
|
8
|
+
const RANK_LABEL_START = 0;
|
|
9
|
+
const RANK_WORD_START = 1;
|
|
10
|
+
const RANK_SUBSTRING = 2;
|
|
11
|
+
const WORD_CHAR = /[\p{L}\p{N}]/u;
|
|
12
|
+
const createRankFn = substring => {
|
|
8
13
|
if (substring.length === 0) {
|
|
9
|
-
return () =>
|
|
14
|
+
return () => RANK_ANY;
|
|
10
15
|
}
|
|
11
16
|
const lowerSubstring = substring.toLowerCase();
|
|
12
|
-
return
|
|
13
|
-
|
|
17
|
+
return label => {
|
|
18
|
+
const lowerLabel = label.toLowerCase();
|
|
19
|
+
let matchIndex = lowerLabel.indexOf(lowerSubstring);
|
|
20
|
+
if (matchIndex === -1) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
if (matchIndex === 0) {
|
|
24
|
+
return RANK_LABEL_START;
|
|
25
|
+
}
|
|
26
|
+
while (matchIndex !== -1) {
|
|
27
|
+
if (!WORD_CHAR.test(lowerLabel[matchIndex - 1])) {
|
|
28
|
+
return RANK_WORD_START;
|
|
29
|
+
}
|
|
30
|
+
matchIndex = lowerLabel.indexOf(lowerSubstring, matchIndex + 1);
|
|
31
|
+
}
|
|
32
|
+
return RANK_SUBSTRING;
|
|
14
33
|
};
|
|
15
34
|
};
|
|
16
|
-
const
|
|
35
|
+
const rankOptions = (options, rankFn) => {
|
|
36
|
+
const ranked = [];
|
|
37
|
+
options.forEach(option => {
|
|
38
|
+
const rank = rankFn(getTextFromNodes(option.label));
|
|
39
|
+
if (rank !== null) {
|
|
40
|
+
ranked.push({
|
|
41
|
+
option,
|
|
42
|
+
rank
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
// Array.prototype.sort is stable, so the original order is kept within a rank
|
|
47
|
+
return ranked.sort((a, b) => a.rank - b.rank);
|
|
48
|
+
};
|
|
49
|
+
const processOptions = (options, rankFn) => {
|
|
17
50
|
const flattenedOptions = [];
|
|
18
51
|
const listBoxItems = [];
|
|
52
|
+
// Filter and rank first: options inside a group are sorted by their rank,
|
|
53
|
+
// top-level entries (options and groups) are sorted by their best rank.
|
|
54
|
+
const rankedEntries = [];
|
|
55
|
+
options.forEach((optionOrGroup, arrayIndex) => {
|
|
56
|
+
if (isOptionAGroup(optionOrGroup)) {
|
|
57
|
+
const group = optionOrGroup;
|
|
58
|
+
if (!group.options || group.options.length === 0) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const rankedGroupOptions = rankOptions(group.options, rankFn);
|
|
62
|
+
if (rankedGroupOptions.length > 0) {
|
|
63
|
+
rankedEntries.push({
|
|
64
|
+
type: 'group',
|
|
65
|
+
group,
|
|
66
|
+
arrayIndex,
|
|
67
|
+
options: rankedGroupOptions.map(_ref => {
|
|
68
|
+
let {
|
|
69
|
+
option
|
|
70
|
+
} = _ref;
|
|
71
|
+
return option;
|
|
72
|
+
}),
|
|
73
|
+
rank: rankedGroupOptions[0].rank
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const option = optionOrGroup;
|
|
79
|
+
const rank = rankFn(getTextFromNodes(option.label));
|
|
80
|
+
if (rank !== null) {
|
|
81
|
+
rankedEntries.push({
|
|
82
|
+
type: 'option',
|
|
83
|
+
option,
|
|
84
|
+
rank
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
rankedEntries.sort((a, b) => a.rank - b.rank);
|
|
89
|
+
// Indexes are assigned after sorting to match the order of flattenedOptions
|
|
19
90
|
let index = 0;
|
|
20
91
|
const createOptionItem = option => {
|
|
92
|
+
flattenedOptions.push(option);
|
|
21
93
|
return {
|
|
22
94
|
...option,
|
|
23
95
|
index: index++,
|
|
@@ -25,12 +97,12 @@ const processOptions = (options, filterFn) => {
|
|
|
25
97
|
key: getOptionValue(option)
|
|
26
98
|
};
|
|
27
99
|
};
|
|
28
|
-
|
|
29
|
-
if (
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
100
|
+
rankedEntries.forEach(entry => {
|
|
101
|
+
if (entry.type === 'group') {
|
|
102
|
+
const {
|
|
103
|
+
group,
|
|
104
|
+
arrayIndex
|
|
105
|
+
} = entry;
|
|
34
106
|
// Render as Group (with or without label)
|
|
35
107
|
const groupLabelText = group.label ? getTextFromNodes(group.label) : '';
|
|
36
108
|
// Use a special prefix that's unlikely to collide with user values
|
|
@@ -38,24 +110,13 @@ const processOptions = (options, filterFn) => {
|
|
|
38
110
|
const listBoxGroupItem = {
|
|
39
111
|
...group,
|
|
40
112
|
type: 'group',
|
|
41
|
-
options:
|
|
113
|
+
options: entry.options.map(createOptionItem),
|
|
42
114
|
key: groupKey
|
|
43
115
|
};
|
|
44
|
-
|
|
45
|
-
if (filterFn(getTextFromNodes(option.label))) {
|
|
46
|
-
flattenedOptions.push(option);
|
|
47
|
-
listBoxGroupItem.options.push(createOptionItem(option));
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
if (listBoxGroupItem.options.length > 0) {
|
|
51
|
-
listBoxItems.push(listBoxGroupItem);
|
|
52
|
-
}
|
|
116
|
+
listBoxItems.push(listBoxGroupItem);
|
|
53
117
|
return;
|
|
54
118
|
}
|
|
55
|
-
|
|
56
|
-
flattenedOptions.push(optionOrGroup);
|
|
57
|
-
listBoxItems.push(createOptionItem(optionOrGroup));
|
|
58
|
-
}
|
|
119
|
+
listBoxItems.push(createOptionItem(entry.option));
|
|
59
120
|
});
|
|
60
121
|
return {
|
|
61
122
|
flattenedOptions,
|
|
@@ -65,7 +126,8 @@ const processOptions = (options, filterFn) => {
|
|
|
65
126
|
/**
|
|
66
127
|
* Convert an array of options with possible groups to the flat array of options, stripping them of the groups.
|
|
67
128
|
* Also creating a list for ListBox component with indexes and keys.
|
|
68
|
-
* Supports filtering by returning the setFilterString function
|
|
129
|
+
* Supports filtering by returning the setFilterString function: options that
|
|
130
|
+
* contain the filter string are kept, the ones starting with it go first.
|
|
69
131
|
* @param options
|
|
70
132
|
*/
|
|
71
133
|
const useOptions = options => {
|
|
@@ -73,18 +135,18 @@ const useOptions = options => {
|
|
|
73
135
|
const [filterString, setFilterString] = useState("");
|
|
74
136
|
let t0;
|
|
75
137
|
if ($[0] !== filterString) {
|
|
76
|
-
t0 =
|
|
138
|
+
t0 = createRankFn(filterString);
|
|
77
139
|
$[0] = filterString;
|
|
78
140
|
$[1] = t0;
|
|
79
141
|
} else {
|
|
80
142
|
t0 = $[1];
|
|
81
143
|
}
|
|
82
|
-
const
|
|
144
|
+
const rankFn = t0;
|
|
83
145
|
let t1;
|
|
84
|
-
if ($[2] !==
|
|
85
|
-
t1 = processOptions(options,
|
|
86
|
-
$[2] =
|
|
87
|
-
$[3] =
|
|
146
|
+
if ($[2] !== options || $[3] !== rankFn) {
|
|
147
|
+
t1 = processOptions(options, rankFn);
|
|
148
|
+
$[2] = options;
|
|
149
|
+
$[3] = rankFn;
|
|
88
150
|
$[4] = t1;
|
|
89
151
|
} else {
|
|
90
152
|
t1 = $[4];
|