@rainbow-oh/yee-c 0.6.4 → 0.8.0
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/cjs/Button/style/index.less +5 -4
- package/dist/cjs/Drawer/drawer.js +1 -1
- package/dist/cjs/Drawer/interface.d.ts +1 -1
- package/dist/cjs/Drawer/style/index.less +1 -0
- package/dist/cjs/Form/form-store.d.ts +2 -1
- package/dist/cjs/Form/form-store.js +32 -17
- package/dist/cjs/List/demo/virtual.d.ts +3 -0
- package/dist/cjs/List/demo/virtual.js +53 -0
- package/dist/cjs/List/hooks/useListKeyboard.d.ts +28 -0
- package/dist/cjs/List/hooks/useListKeyboard.js +188 -0
- package/dist/cjs/List/interface.d.ts +29 -2
- package/dist/cjs/List/list.js +141 -21
- package/dist/cjs/List/style/index.less +24 -0
- package/dist/cjs/Select/demo/columns.d.ts +3 -0
- package/dist/cjs/Select/demo/columns.js +62 -0
- package/dist/cjs/Select/hooks/useSelectKeyboard.d.ts +13 -1
- package/dist/cjs/Select/hooks/useSelectKeyboard.js +81 -10
- package/dist/cjs/Select/interface.d.ts +30 -3
- package/dist/cjs/Select/option.js +4 -3
- package/dist/cjs/Select/options.js +90 -8
- package/dist/cjs/Select/select.js +32 -8
- package/dist/cjs/Select/style/index.less +14 -0
- package/dist/cjs/Selector/interface.d.ts +4 -0
- package/dist/cjs/Selector/selector.js +5 -3
- package/dist/cjs/Table/hooks/useColumns.js +1 -1
- package/dist/cjs/Table/interface.d.ts +1 -1
- package/dist/cjs/Table/style/index.less +35 -11
- package/dist/cjs/Table/table.js +57 -7
- package/dist/cjs/Table/tbody/cell.js +1 -1
- package/dist/cjs/Table/util/header.d.ts +3 -1
- package/dist/cjs/Table/util/header.js +30 -3
- package/dist/cjs/Tree/demo/draggable.js +2 -0
- package/dist/cjs/Tree/interface.d.ts +46 -23
- package/dist/cjs/Tree/tree-node.d.ts +1 -1
- package/dist/cjs/Tree/tree-node.js +30 -23
- package/dist/cjs/Tree/tree.d.ts +13 -12
- package/dist/cjs/Tree/tree.js +452 -227
- package/dist/cjs/Tree/utils/tree-tools.d.ts +43 -10
- package/dist/cjs/Tree/utils/tree-tools.js +129 -91
- package/dist/cjs/locale/context.d.ts +5 -4
- package/dist/cjs/locale/context.js +15 -8
- package/dist/cjs/locale/interface.d.ts +20 -0
- package/dist/cjs/locale/util.d.ts +3 -3
- package/dist/cjs/style/color.css +3 -0
- package/dist/cjs/style/size.css +2 -1
- package/dist/cjs/style/themes.css +1 -1
- package/dist/cjs/style/variables.css +6 -2
- package/dist/cjs/utils/isEqual.js +5 -7
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/Button/style/index.less +5 -4
- package/dist/esm/Drawer/drawer.js +2 -1
- package/dist/esm/Drawer/interface.d.ts +1 -1
- package/dist/esm/Drawer/style/index.less +1 -0
- package/dist/esm/Form/form-store.d.ts +2 -1
- package/dist/esm/Form/form-store.js +40 -19
- package/dist/esm/List/demo/virtual.d.ts +3 -0
- package/dist/esm/List/demo/virtual.js +22 -0
- package/dist/esm/List/hooks/useListKeyboard.d.ts +28 -0
- package/dist/esm/List/hooks/useListKeyboard.js +173 -0
- package/dist/esm/List/interface.d.ts +29 -2
- package/dist/esm/List/list.js +171 -14
- package/dist/esm/List/style/index.less +24 -0
- package/dist/esm/Select/demo/columns.d.ts +3 -0
- package/dist/esm/Select/demo/columns.js +36 -0
- package/dist/esm/Select/hooks/useSelectKeyboard.d.ts +13 -1
- package/dist/esm/Select/hooks/useSelectKeyboard.js +91 -14
- package/dist/esm/Select/interface.d.ts +30 -3
- package/dist/esm/Select/option.js +6 -2
- package/dist/esm/Select/options.js +114 -32
- package/dist/esm/Select/select.js +40 -11
- package/dist/esm/Select/style/index.less +14 -0
- package/dist/esm/Selector/interface.d.ts +4 -0
- package/dist/esm/Selector/selector.js +10 -4
- package/dist/esm/Table/hooks/useColumns.js +1 -1
- package/dist/esm/Table/interface.d.ts +1 -1
- package/dist/esm/Table/style/index.less +35 -11
- package/dist/esm/Table/table.js +54 -2
- package/dist/esm/Table/tbody/cell.js +1 -1
- package/dist/esm/Table/util/header.d.ts +3 -1
- package/dist/esm/Table/util/header.js +30 -2
- package/dist/esm/Tree/demo/draggable.js +2 -0
- package/dist/esm/Tree/interface.d.ts +46 -23
- package/dist/esm/Tree/tree-node.d.ts +1 -1
- package/dist/esm/Tree/tree-node.js +26 -31
- package/dist/esm/Tree/tree.d.ts +13 -12
- package/dist/esm/Tree/tree.js +572 -163
- package/dist/esm/Tree/utils/tree-tools.d.ts +43 -10
- package/dist/esm/Tree/utils/tree-tools.js +219 -104
- package/dist/esm/locale/context.d.ts +5 -4
- package/dist/esm/locale/context.js +25 -9
- package/dist/esm/locale/interface.d.ts +20 -0
- package/dist/esm/locale/util.d.ts +3 -3
- package/dist/esm/style/color.css +3 -0
- package/dist/esm/style/size.css +2 -1
- package/dist/esm/style/themes.css +1 -1
- package/dist/esm/style/variables.css +6 -2
- package/dist/esm/utils/isEqual.js +5 -7
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/umd/yee-c.min.css +4 -4
- package/dist/umd/yee-c.min.js +1 -1
- package/package.json +1 -1
- package/src/Button/style/index.less +5 -4
- package/src/Drawer/drawer.tsx +1 -1
- package/src/Drawer/interface.ts +1 -0
- package/src/Drawer/style/index.less +1 -0
- package/src/Form/form-store.ts +42 -15
- package/src/List/demo/virtual.tsx +20 -0
- package/src/List/hooks/useListKeyboard.ts +235 -0
- package/src/List/index.en-US.md +6 -0
- package/src/List/index.zh-CN.md +6 -0
- package/src/List/interface.ts +29 -2
- package/src/List/list.tsx +157 -19
- package/src/List/style/index.less +24 -0
- package/src/Select/demo/columns.tsx +35 -0
- package/src/Select/hooks/useSelectKeyboard.ts +112 -15
- package/src/Select/index.en-US.md +4 -0
- package/src/Select/index.zh-CN.md +4 -0
- package/src/Select/interface.ts +31 -4
- package/src/Select/option.tsx +9 -3
- package/src/Select/options.tsx +119 -26
- package/src/Select/select.tsx +41 -8
- package/src/Select/style/index.less +14 -0
- package/src/Selector/interface.ts +4 -0
- package/src/Selector/selector.tsx +8 -3
- package/src/Table/hooks/useColumns.ts +1 -1
- package/src/Table/interface.ts +1 -1
- package/src/Table/style/index.less +35 -11
- package/src/Table/table.tsx +60 -1
- package/src/Table/tbody/cell.tsx +1 -1
- package/src/Table/util/header.ts +48 -2
- package/src/Tree/demo/draggable.tsx +2 -0
- package/src/Tree/interface.ts +46 -23
- package/src/Tree/tree-node.tsx +26 -29
- package/src/Tree/tree.tsx +573 -274
- package/src/Tree/utils/tree-tools.ts +231 -125
- package/src/locale/_README.md +22 -13
- package/src/locale/context.tsx +33 -16
- package/src/locale/interface.ts +24 -0
- package/src/locale/util.ts +6 -6
- package/src/style/color.css +3 -0
- package/src/style/size.css +2 -1
- package/src/utils/isEqual.ts +5 -7
- package/src/version.ts +1 -1
|
@@ -6,18 +6,42 @@
|
|
|
6
6
|
list-style: none;
|
|
7
7
|
background-color: var(--yee-list-bg-color);
|
|
8
8
|
|
|
9
|
+
&:focus {
|
|
10
|
+
outline: none;
|
|
11
|
+
}
|
|
12
|
+
|
|
9
13
|
&-item {
|
|
10
14
|
padding: var(--yee-list-item-padding);
|
|
11
15
|
|
|
12
16
|
&:hover:not(&-disabled) {
|
|
13
17
|
background-color: var(--yee-list-item-bg-hover-color);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
&-disabled {
|
|
14
21
|
cursor: not-allowed;
|
|
22
|
+
opacity: 0.5;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
&-clickable:not(&-disabled) {
|
|
26
|
+
cursor: pointer;
|
|
15
27
|
}
|
|
16
28
|
|
|
17
29
|
&-focused {
|
|
18
30
|
background-color: var(--yee-list-item-bg-hover-color);
|
|
19
31
|
}
|
|
20
32
|
}
|
|
33
|
+
|
|
34
|
+
// Multi-column grid layout (non-virtual mode).
|
|
35
|
+
// Column count driven by CSS custom property --yee-list-columns set inline.
|
|
36
|
+
&-grid {
|
|
37
|
+
display: grid;
|
|
38
|
+
grid-template-columns: repeat(var(--yee-list-columns, 1), 1fr);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Virtual multi-column: each row is already a grid via inline style.
|
|
42
|
+
&-grid-row {
|
|
43
|
+
align-items: center;
|
|
44
|
+
}
|
|
21
45
|
}
|
|
22
46
|
|
|
23
47
|
@{pls}-bordered {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Select } from "../..";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
var options = Array.from({
|
|
4
|
+
length: 24
|
|
5
|
+
}, function (_, i) {
|
|
6
|
+
return {
|
|
7
|
+
label: "Option ".concat(i + 1),
|
|
8
|
+
value: "opt-".concat(i + 1)
|
|
9
|
+
};
|
|
10
|
+
});
|
|
11
|
+
export default (function () {
|
|
12
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
13
|
+
style: {
|
|
14
|
+
display: 'flex',
|
|
15
|
+
flexDirection: 'column',
|
|
16
|
+
gap: 24
|
|
17
|
+
}
|
|
18
|
+
}, /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("p", null, "3 columns:"), /*#__PURE__*/React.createElement(Select, {
|
|
19
|
+
columns: 3,
|
|
20
|
+
popupWidth: 360,
|
|
21
|
+
options: options,
|
|
22
|
+
placeholder: "Select an option",
|
|
23
|
+
style: {
|
|
24
|
+
width: 200
|
|
25
|
+
}
|
|
26
|
+
})), /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("p", null, "4 columns with multiple selection:"), /*#__PURE__*/React.createElement(Select, {
|
|
27
|
+
columns: 4,
|
|
28
|
+
popupWidth: 480,
|
|
29
|
+
mode: "multiple",
|
|
30
|
+
options: options,
|
|
31
|
+
placeholder: "Select options",
|
|
32
|
+
style: {
|
|
33
|
+
width: 300
|
|
34
|
+
}
|
|
35
|
+
})));
|
|
36
|
+
});
|
|
@@ -7,6 +7,18 @@ interface UseSelectKeyboardParams {
|
|
|
7
7
|
}>;
|
|
8
8
|
open: boolean;
|
|
9
9
|
selectedKeys: Array<string | number>;
|
|
10
|
+
/**
|
|
11
|
+
* Number of columns in multi-column mode.
|
|
12
|
+
* Affects ArrowLeft/ArrowRight and ArrowUp/ArrowDown behavior.
|
|
13
|
+
* @default 1
|
|
14
|
+
*/
|
|
15
|
+
columns?: number;
|
|
16
|
+
/**
|
|
17
|
+
* Whether loose value matching is enabled.
|
|
18
|
+
* Affects initial focus resolution when selectedKeys types differ from option values.
|
|
19
|
+
* @default false
|
|
20
|
+
*/
|
|
21
|
+
looseMatch?: boolean;
|
|
10
22
|
onSelect: (value: string | number) => void;
|
|
11
23
|
onClose?: () => void;
|
|
12
24
|
onOpenChange?: (open: boolean) => void;
|
|
@@ -18,7 +30,7 @@ interface UseSelectKeyboardParams {
|
|
|
18
30
|
*/
|
|
19
31
|
scrollToIndex?: (index: number) => void;
|
|
20
32
|
}
|
|
21
|
-
export default function useSelectKeyboard({ options, open, selectedKeys, onSelect, onClose, onOpenChange, containerRef, scrollToIndex, }: UseSelectKeyboardParams): {
|
|
33
|
+
export default function useSelectKeyboard({ options, open, selectedKeys, columns, looseMatch, onSelect, onClose, onOpenChange, containerRef, scrollToIndex, }: UseSelectKeyboardParams): {
|
|
22
34
|
focusedKey: string | number;
|
|
23
35
|
onKeyDown: (event: React.KeyboardEvent) => void;
|
|
24
36
|
};
|
|
@@ -10,6 +10,10 @@ export default function useSelectKeyboard(_ref) {
|
|
|
10
10
|
var options = _ref.options,
|
|
11
11
|
open = _ref.open,
|
|
12
12
|
selectedKeys = _ref.selectedKeys,
|
|
13
|
+
_ref$columns = _ref.columns,
|
|
14
|
+
columns = _ref$columns === void 0 ? 1 : _ref$columns,
|
|
15
|
+
_ref$looseMatch = _ref.looseMatch,
|
|
16
|
+
looseMatch = _ref$looseMatch === void 0 ? false : _ref$looseMatch,
|
|
13
17
|
onSelect = _ref.onSelect,
|
|
14
18
|
onClose = _ref.onClose,
|
|
15
19
|
onOpenChange = _ref.onOpenChange,
|
|
@@ -63,9 +67,15 @@ export default function useSelectKeyboard(_ref) {
|
|
|
63
67
|
var optionElements = container.querySelectorAll('[role="option"]');
|
|
64
68
|
var targetElement = optionElements[index];
|
|
65
69
|
if (targetElement) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
// Use manual scroll calculation instead of scrollIntoView to avoid
|
|
71
|
+
// scrolling the entire page (popup is rendered in a portal).
|
|
72
|
+
var containerRect = container.getBoundingClientRect();
|
|
73
|
+
var targetRect = targetElement.getBoundingClientRect();
|
|
74
|
+
if (targetRect.bottom > containerRect.bottom) {
|
|
75
|
+
container.scrollTop += targetRect.bottom - containerRect.bottom;
|
|
76
|
+
} else if (targetRect.top < containerRect.top) {
|
|
77
|
+
container.scrollTop -= containerRect.top - targetRect.top;
|
|
78
|
+
}
|
|
69
79
|
}
|
|
70
80
|
};
|
|
71
81
|
|
|
@@ -73,12 +83,29 @@ export default function useSelectKeyboard(_ref) {
|
|
|
73
83
|
var handleArrowUp = function handleArrowUp() {
|
|
74
84
|
var focusedIndex = getFocusedIndex();
|
|
75
85
|
var nextIndex;
|
|
76
|
-
if (
|
|
77
|
-
//
|
|
78
|
-
|
|
86
|
+
if (columns > 1) {
|
|
87
|
+
// Multi-column: move up one row (jump by `columns` items)
|
|
88
|
+
if (focusedIndex === -1) {
|
|
89
|
+
nextIndex = findNextEnabledIndex(options.length - 1, -1);
|
|
90
|
+
} else {
|
|
91
|
+
var target = focusedIndex - columns;
|
|
92
|
+
if (target >= 0) {
|
|
93
|
+
nextIndex = options[target].disabled ? findNextEnabledIndex(target, -1) : target;
|
|
94
|
+
} else {
|
|
95
|
+
var _options$wrapTarget;
|
|
96
|
+
// Wrap to bottom: find the same column in the last row
|
|
97
|
+
var lastRowStart = Math.floor((options.length - 1) / columns) * columns;
|
|
98
|
+
var col = focusedIndex % columns;
|
|
99
|
+
var wrapTarget = Math.min(lastRowStart + col, options.length - 1);
|
|
100
|
+
nextIndex = (_options$wrapTarget = options[wrapTarget]) !== null && _options$wrapTarget !== void 0 && _options$wrapTarget.disabled ? findNextEnabledIndex(wrapTarget, -1) : wrapTarget;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
79
103
|
} else {
|
|
80
|
-
|
|
81
|
-
|
|
104
|
+
if (focusedIndex === -1) {
|
|
105
|
+
nextIndex = findNextEnabledIndex(options.length - 1, -1);
|
|
106
|
+
} else {
|
|
107
|
+
nextIndex = findNextEnabledIndex(focusedIndex, -1);
|
|
108
|
+
}
|
|
82
109
|
}
|
|
83
110
|
if (nextIndex !== -1) {
|
|
84
111
|
var nextValue = options[nextIndex].value;
|
|
@@ -91,12 +118,28 @@ export default function useSelectKeyboard(_ref) {
|
|
|
91
118
|
var handleArrowDown = function handleArrowDown() {
|
|
92
119
|
var focusedIndex = getFocusedIndex();
|
|
93
120
|
var nextIndex;
|
|
94
|
-
if (
|
|
95
|
-
//
|
|
96
|
-
|
|
121
|
+
if (columns > 1) {
|
|
122
|
+
// Multi-column: move down one row (jump by `columns` items)
|
|
123
|
+
if (focusedIndex === -1) {
|
|
124
|
+
nextIndex = findNextEnabledIndex(0, 1);
|
|
125
|
+
} else {
|
|
126
|
+
var target = focusedIndex + columns;
|
|
127
|
+
if (target < options.length) {
|
|
128
|
+
nextIndex = options[target].disabled ? findNextEnabledIndex(target, 1) : target;
|
|
129
|
+
} else {
|
|
130
|
+
var _options$wrapTarget2;
|
|
131
|
+
// Wrap to top: find the same column in the first row
|
|
132
|
+
var col = focusedIndex % columns;
|
|
133
|
+
var wrapTarget = col;
|
|
134
|
+
nextIndex = (_options$wrapTarget2 = options[wrapTarget]) !== null && _options$wrapTarget2 !== void 0 && _options$wrapTarget2.disabled ? findNextEnabledIndex(wrapTarget, 1) : wrapTarget;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
97
137
|
} else {
|
|
98
|
-
|
|
99
|
-
|
|
138
|
+
if (focusedIndex === -1) {
|
|
139
|
+
nextIndex = findNextEnabledIndex(0, 1);
|
|
140
|
+
} else {
|
|
141
|
+
nextIndex = findNextEnabledIndex(focusedIndex, 1);
|
|
142
|
+
}
|
|
100
143
|
}
|
|
101
144
|
if (nextIndex !== -1) {
|
|
102
145
|
var nextValue = options[nextIndex].value;
|
|
@@ -105,6 +148,28 @@ export default function useSelectKeyboard(_ref) {
|
|
|
105
148
|
}
|
|
106
149
|
};
|
|
107
150
|
|
|
151
|
+
// Handle ArrowLeft key (multi-column: move to previous item)
|
|
152
|
+
var handleArrowLeft = function handleArrowLeft() {
|
|
153
|
+
var focusedIndex = getFocusedIndex();
|
|
154
|
+
if (focusedIndex <= 0) return;
|
|
155
|
+
var nextIndex = findNextEnabledIndex(focusedIndex, -1);
|
|
156
|
+
if (nextIndex !== -1) {
|
|
157
|
+
setFocusedKey(options[nextIndex].value);
|
|
158
|
+
scrollOptionIntoView(nextIndex);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
// Handle ArrowRight key (multi-column: move to next item)
|
|
163
|
+
var handleArrowRight = function handleArrowRight() {
|
|
164
|
+
var focusedIndex = getFocusedIndex();
|
|
165
|
+
if (focusedIndex >= options.length - 1) return;
|
|
166
|
+
var nextIndex = findNextEnabledIndex(focusedIndex, 1);
|
|
167
|
+
if (nextIndex !== -1) {
|
|
168
|
+
setFocusedKey(options[nextIndex].value);
|
|
169
|
+
scrollOptionIntoView(nextIndex);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
108
173
|
// Handle Enter key
|
|
109
174
|
var handleEnter = function handleEnter() {
|
|
110
175
|
var currentFocusedKey = latestFocusedKey.current;
|
|
@@ -143,6 +208,18 @@ export default function useSelectKeyboard(_ref) {
|
|
|
143
208
|
event.preventDefault();
|
|
144
209
|
handleArrowDown();
|
|
145
210
|
break;
|
|
211
|
+
case 'ArrowLeft':
|
|
212
|
+
if (columns > 1) {
|
|
213
|
+
event.preventDefault();
|
|
214
|
+
handleArrowLeft();
|
|
215
|
+
}
|
|
216
|
+
break;
|
|
217
|
+
case 'ArrowRight':
|
|
218
|
+
if (columns > 1) {
|
|
219
|
+
event.preventDefault();
|
|
220
|
+
handleArrowRight();
|
|
221
|
+
}
|
|
222
|
+
break;
|
|
146
223
|
case 'Enter':
|
|
147
224
|
event.preventDefault();
|
|
148
225
|
handleEnter();
|
|
@@ -162,7 +239,7 @@ export default function useSelectKeyboard(_ref) {
|
|
|
162
239
|
var focusedValue = '';
|
|
163
240
|
if ((selectedKeys === null || selectedKeys === void 0 ? void 0 : selectedKeys.length) > 0) {
|
|
164
241
|
var firstSelected = options === null || options === void 0 ? void 0 : options.find(function (opt) {
|
|
165
|
-
return opt.value === selectedKeys[0];
|
|
242
|
+
return looseMatch ? String(opt.value) === String(selectedKeys[0]) : opt.value === selectedKeys[0];
|
|
166
243
|
});
|
|
167
244
|
if (firstSelected && !firstSelected.disabled) {
|
|
168
245
|
focusedValue = firstSelected.value;
|
|
@@ -126,10 +126,27 @@ export interface SelectProps extends DataAttributeProps, Pick<SelectorProps, 'si
|
|
|
126
126
|
* @default 200
|
|
127
127
|
*/
|
|
128
128
|
listHeight?: number;
|
|
129
|
+
/**
|
|
130
|
+
* Number of columns in the dropdown panel.
|
|
131
|
+
* When greater than 1, options are displayed in a grid layout.
|
|
132
|
+
* @default 1
|
|
133
|
+
*/
|
|
134
|
+
columns?: number;
|
|
135
|
+
/**
|
|
136
|
+
* Fixed width of the dropdown popup in pixels.
|
|
137
|
+
* When set, the popup will not stretch to match the trigger width.
|
|
138
|
+
* Useful with multi-column layout to provide enough horizontal space.
|
|
139
|
+
*/
|
|
140
|
+
popupWidth?: number;
|
|
141
|
+
/**
|
|
142
|
+
* Enable loose value matching. When enabled, value comparison uses
|
|
143
|
+
* `String(a) === String(b)` instead of strict equality (`===`).
|
|
144
|
+
* Resolves type mismatch when options use string values but the
|
|
145
|
+
* controlled value is a number (or vice versa).
|
|
146
|
+
* @default false
|
|
147
|
+
*/
|
|
148
|
+
looseMatch?: boolean;
|
|
129
149
|
}
|
|
130
|
-
/**
|
|
131
|
-
* Props for the internal Option component (renders a single dropdown option).
|
|
132
|
-
*/
|
|
133
150
|
export interface OptionProps {
|
|
134
151
|
/**
|
|
135
152
|
* Display label
|
|
@@ -165,6 +182,7 @@ export interface OptionsContextValue {
|
|
|
165
182
|
selectedKeys: Array<string | number>;
|
|
166
183
|
focusedKey: string | number;
|
|
167
184
|
multiple: boolean;
|
|
185
|
+
looseMatch: boolean;
|
|
168
186
|
onSelect: (key: string | number, e?: React.MouseEvent | React.KeyboardEvent) => void;
|
|
169
187
|
}
|
|
170
188
|
/**
|
|
@@ -223,6 +241,15 @@ export interface OptionsProps extends Omit<React.HTMLAttributes<HTMLDivElement>,
|
|
|
223
241
|
virtualApiRef?: React.MutableRefObject<{
|
|
224
242
|
scrollToIndex: (index: number) => void;
|
|
225
243
|
} | null>;
|
|
244
|
+
/**
|
|
245
|
+
* Number of columns in the dropdown grid layout.
|
|
246
|
+
* @default 1
|
|
247
|
+
*/
|
|
248
|
+
columns?: number;
|
|
249
|
+
/**
|
|
250
|
+
* Whether loose value matching is enabled
|
|
251
|
+
*/
|
|
252
|
+
looseMatch?: boolean;
|
|
226
253
|
/**
|
|
227
254
|
* Callback when an option is selected
|
|
228
255
|
*/
|
|
@@ -18,6 +18,7 @@ var Option = function Option(props) {
|
|
|
18
18
|
selectedKeys = _useContext.selectedKeys,
|
|
19
19
|
focusedKey = _useContext.focusedKey,
|
|
20
20
|
multiple = _useContext.multiple,
|
|
21
|
+
looseMatch = _useContext.looseMatch,
|
|
21
22
|
onSelect = _useContext.onSelect;
|
|
22
23
|
var handleClick = function handleClick(e) {
|
|
23
24
|
if (!disabled) {
|
|
@@ -25,9 +26,12 @@ var Option = function Option(props) {
|
|
|
25
26
|
}
|
|
26
27
|
};
|
|
27
28
|
var htmlTitle = title ? title : typeof label === 'string' ? label : undefined;
|
|
28
|
-
var selected = selectedKeys.
|
|
29
|
+
var selected = looseMatch ? selectedKeys.some(function (k) {
|
|
30
|
+
return String(k) === String(value);
|
|
31
|
+
}) : selectedKeys.includes(value);
|
|
32
|
+
var focused = looseMatch ? String(focusedKey) === String(value) : focusedKey === value;
|
|
29
33
|
return /*#__PURE__*/React.createElement("div", {
|
|
30
|
-
className: clsx("".concat(prefixCls, "-option"), _defineProperty(_defineProperty(_defineProperty({}, "".concat(prefixCls, "-option-selected"), selected), "".concat(prefixCls, "-option-focused"),
|
|
34
|
+
className: clsx("".concat(prefixCls, "-option"), _defineProperty(_defineProperty(_defineProperty({}, "".concat(prefixCls, "-option-selected"), selected), "".concat(prefixCls, "-option-focused"), focused), "".concat(prefixCls, "-option-disabled"), disabled)),
|
|
31
35
|
"aria-label": "option",
|
|
32
36
|
onClick: handleClick,
|
|
33
37
|
onKeyDown: function onKeyDown(e) {
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
+
var _excluded = ["prefixCls", "options", "popupClassName", "popupStyle", "selectedKeys", "focusedKey", "dataTestId", "multiple", "virtual", "itemHeight", "listHeight", "virtualApiRef", "columns", "looseMatch", "onSelect"];
|
|
3
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
4
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
5
|
+
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
6
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : String(i); }
|
|
7
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
2
8
|
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
|
3
9
|
function _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }
|
|
4
10
|
function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }
|
|
@@ -26,20 +32,23 @@ var Options = /*#__PURE__*/forwardRef(function (props, ref) {
|
|
|
26
32
|
_props$listHeight = props.listHeight,
|
|
27
33
|
listHeight = _props$listHeight === void 0 ? 200 : _props$listHeight,
|
|
28
34
|
virtualApiRef = props.virtualApiRef,
|
|
35
|
+
_props$columns = props.columns,
|
|
36
|
+
columns = _props$columns === void 0 ? 1 : _props$columns,
|
|
37
|
+
_props$looseMatch = props.looseMatch,
|
|
38
|
+
looseMatch = _props$looseMatch === void 0 ? false : _props$looseMatch,
|
|
29
39
|
onSelect = props.onSelect,
|
|
30
40
|
rest = _objectWithoutProperties(props, _excluded);
|
|
41
|
+
var multiColumn = columns > 1;
|
|
31
42
|
var rootRef = useRef(null);
|
|
32
|
-
// Forward the popup root element to the parent (Select uses it as the
|
|
33
|
-
// keyboard-nav containerRef) while keeping a stable RefObject for the
|
|
34
|
-
// effects below.
|
|
35
43
|
useImperativeHandle(ref, function () {
|
|
36
44
|
return rootRef.current;
|
|
37
45
|
}, []);
|
|
38
46
|
|
|
39
|
-
//
|
|
40
|
-
//
|
|
47
|
+
// In multi-column virtual mode, each "row" contains `columns` options.
|
|
48
|
+
// The virtual list tracks rows, not individual options.
|
|
49
|
+
var rowCount = multiColumn ? Math.ceil(options.length / columns) : options.length;
|
|
41
50
|
var _useVirtualList = useVirtualList({
|
|
42
|
-
itemCount:
|
|
51
|
+
itemCount: virtual ? rowCount : 0,
|
|
43
52
|
itemHeight: itemHeight
|
|
44
53
|
}),
|
|
45
54
|
start = _useVirtualList.start,
|
|
@@ -48,37 +57,34 @@ var Options = /*#__PURE__*/forwardRef(function (props, ref) {
|
|
|
48
57
|
totalHeight = _useVirtualList.totalHeight,
|
|
49
58
|
viewportRef = _useVirtualList.viewportRef,
|
|
50
59
|
onScroll = _useVirtualList.onScroll,
|
|
51
|
-
|
|
60
|
+
_scrollToIndex = _useVirtualList.scrollToIndex;
|
|
52
61
|
|
|
53
|
-
// Expose the virtual scroll API to the parent
|
|
54
|
-
//
|
|
62
|
+
// Expose the virtual scroll API to the parent.
|
|
63
|
+
// In multi-column mode, the parent passes a flat option index, so we
|
|
64
|
+
// convert it to a row index before scrolling.
|
|
55
65
|
useEffect(function () {
|
|
56
66
|
if (!virtual || !virtualApiRef) return;
|
|
57
67
|
virtualApiRef.current = {
|
|
58
|
-
scrollToIndex: scrollToIndex
|
|
68
|
+
scrollToIndex: function scrollToIndex(flatIndex) {
|
|
69
|
+
var rowIndex = multiColumn ? Math.floor(flatIndex / columns) : flatIndex;
|
|
70
|
+
_scrollToIndex(rowIndex);
|
|
71
|
+
}
|
|
59
72
|
};
|
|
60
73
|
return function () {
|
|
61
74
|
if (virtualApiRef.current) {
|
|
62
75
|
virtualApiRef.current = null;
|
|
63
76
|
}
|
|
64
77
|
};
|
|
65
|
-
}, [virtual, virtualApiRef,
|
|
78
|
+
}, [virtual, virtualApiRef, _scrollToIndex, multiColumn, columns]);
|
|
66
79
|
|
|
67
|
-
// Reset
|
|
68
|
-
// filtering via search), otherwise the viewport may sit at an offset
|
|
69
|
-
// where none of the new results are visible.
|
|
80
|
+
// Reset scroll position when options change.
|
|
70
81
|
useEffect(function () {
|
|
71
82
|
if (virtual) {
|
|
72
|
-
|
|
83
|
+
_scrollToIndex(0);
|
|
73
84
|
}
|
|
74
|
-
}, [options, virtual,
|
|
85
|
+
}, [options, virtual, _scrollToIndex]);
|
|
75
86
|
|
|
76
|
-
// Bring the focused option into view
|
|
77
|
-
// - virtual mode: drive the virtual viewport via scrollToIndex.
|
|
78
|
-
// - default mode: scrollIntoView on the option's DOM node.
|
|
79
|
-
// Options owns this (rather than the keyboard hook) so it also covers the
|
|
80
|
-
// initial open, where the keyboard effect can fire before the popup is
|
|
81
|
-
// mounted / the virtual API ref is wired up.
|
|
87
|
+
// Bring the focused option into view.
|
|
82
88
|
useEffect(function () {
|
|
83
89
|
if (options.length === 0) return;
|
|
84
90
|
if (focusedKey === '') return;
|
|
@@ -87,7 +93,8 @@ var Options = /*#__PURE__*/forwardRef(function (props, ref) {
|
|
|
87
93
|
});
|
|
88
94
|
if (focusedIndex < 0) return;
|
|
89
95
|
if (virtual) {
|
|
90
|
-
|
|
96
|
+
var rowIndex = multiColumn ? Math.floor(focusedIndex / columns) : focusedIndex;
|
|
97
|
+
_scrollToIndex(rowIndex);
|
|
91
98
|
return;
|
|
92
99
|
}
|
|
93
100
|
var container = rootRef.current;
|
|
@@ -95,21 +102,94 @@ var Options = /*#__PURE__*/forwardRef(function (props, ref) {
|
|
|
95
102
|
var optionElements = container.querySelectorAll('[role="option"]');
|
|
96
103
|
var targetElement = optionElements[focusedIndex];
|
|
97
104
|
if (targetElement) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
105
|
+
// Use manual scroll calculation instead of scrollIntoView to avoid
|
|
106
|
+
// scrolling the entire page (the popup is rendered in a portal).
|
|
107
|
+
var containerRect = container.getBoundingClientRect();
|
|
108
|
+
var targetRect = targetElement.getBoundingClientRect();
|
|
109
|
+
if (targetRect.bottom > containerRect.bottom) {
|
|
110
|
+
container.scrollTop += targetRect.bottom - containerRect.bottom;
|
|
111
|
+
} else if (targetRect.top < containerRect.top) {
|
|
112
|
+
container.scrollTop -= containerRect.top - targetRect.top;
|
|
113
|
+
}
|
|
101
114
|
}
|
|
102
|
-
}, [focusedKey, virtual, options,
|
|
115
|
+
}, [focusedKey, virtual, options, _scrollToIndex, rootRef, multiColumn, columns]);
|
|
103
116
|
var isEmpty = options.length === 0;
|
|
104
117
|
var ctxValue = {
|
|
105
118
|
prefixCls: prefixCls,
|
|
106
119
|
selectedKeys: selectedKeys,
|
|
107
120
|
focusedKey: focusedKey,
|
|
108
121
|
multiple: multiple,
|
|
122
|
+
looseMatch: looseMatch,
|
|
109
123
|
onSelect: onSelect
|
|
110
124
|
};
|
|
111
125
|
|
|
112
|
-
// ----- Virtual
|
|
126
|
+
// ----- Virtual + multi-column: render rows of N options -----
|
|
127
|
+
if (virtual && multiColumn && !isEmpty) {
|
|
128
|
+
var visibleRows = [];
|
|
129
|
+
for (var rowIdx = start; rowIdx < end; rowIdx++) {
|
|
130
|
+
var rowOptions = [];
|
|
131
|
+
for (var col = 0; col < columns; col++) {
|
|
132
|
+
var flatIdx = rowIdx * columns + col;
|
|
133
|
+
if (flatIdx < options.length) {
|
|
134
|
+
rowOptions.push({
|
|
135
|
+
option: options[flatIdx],
|
|
136
|
+
flatIdx: flatIdx
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
visibleRows.push({
|
|
141
|
+
rowIdx: rowIdx,
|
|
142
|
+
rowOptions: rowOptions
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return /*#__PURE__*/React.createElement("div", _extends({}, rest, {
|
|
146
|
+
className: clsx("".concat(prefixCls, "-popup"), "".concat(prefixCls, "-popup-virtual"), popupClassName),
|
|
147
|
+
style: popupStyle,
|
|
148
|
+
ref: rootRef,
|
|
149
|
+
tabIndex: -1
|
|
150
|
+
}), /*#__PURE__*/React.createElement(OptionsCtx.Provider, {
|
|
151
|
+
value: ctxValue
|
|
152
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
153
|
+
className: "".concat(prefixCls, "-popup-viewport"),
|
|
154
|
+
ref: viewportRef,
|
|
155
|
+
onScroll: onScroll,
|
|
156
|
+
style: {
|
|
157
|
+
maxHeight: listHeight,
|
|
158
|
+
overflowY: 'auto'
|
|
159
|
+
}
|
|
160
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
161
|
+
style: {
|
|
162
|
+
height: totalHeight,
|
|
163
|
+
position: 'relative'
|
|
164
|
+
}
|
|
165
|
+
}, visibleRows.map(function (_ref, ri) {
|
|
166
|
+
var rowIdx = _ref.rowIdx,
|
|
167
|
+
rowOptions = _ref.rowOptions;
|
|
168
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
169
|
+
key: rowIdx,
|
|
170
|
+
className: "".concat(prefixCls, "-popup-grid-row"),
|
|
171
|
+
style: {
|
|
172
|
+
position: 'absolute',
|
|
173
|
+
top: 0,
|
|
174
|
+
left: 0,
|
|
175
|
+
right: 0,
|
|
176
|
+
height: itemHeight,
|
|
177
|
+
transform: "translateY(".concat(offsetY + ri * itemHeight, "px)"),
|
|
178
|
+
display: 'grid',
|
|
179
|
+
gridTemplateColumns: "repeat(".concat(columns, ", 1fr)")
|
|
180
|
+
}
|
|
181
|
+
}, rowOptions.map(function (_ref2) {
|
|
182
|
+
var option = _ref2.option,
|
|
183
|
+
flatIdx = _ref2.flatIdx;
|
|
184
|
+
return /*#__PURE__*/React.createElement(Option, _extends({}, option, {
|
|
185
|
+
key: option.value,
|
|
186
|
+
dataTestId: "".concat(dataTestId, "-").concat(flatIdx)
|
|
187
|
+
}));
|
|
188
|
+
}));
|
|
189
|
+
})))));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ----- Virtual single-column: render visible window -----
|
|
113
193
|
if (virtual && !isEmpty) {
|
|
114
194
|
var visibleOptions = options.slice(start, end);
|
|
115
195
|
return /*#__PURE__*/React.createElement("div", _extends({}, rest, {
|
|
@@ -148,10 +228,12 @@ var Options = /*#__PURE__*/forwardRef(function (props, ref) {
|
|
|
148
228
|
})))));
|
|
149
229
|
}
|
|
150
230
|
|
|
151
|
-
// ----- Default mode
|
|
231
|
+
// ----- Default mode (non-virtual) -----
|
|
152
232
|
return /*#__PURE__*/React.createElement("div", _extends({}, rest, {
|
|
153
|
-
className: clsx("".concat(prefixCls, "-popup"), popupClassName),
|
|
154
|
-
style: popupStyle,
|
|
233
|
+
className: clsx("".concat(prefixCls, "-popup"), _defineProperty({}, "".concat(prefixCls, "-popup-grid"), multiColumn), popupClassName),
|
|
234
|
+
style: _objectSpread(_objectSpread({}, popupStyle), multiColumn ? {
|
|
235
|
+
'--yee-select-columns': columns
|
|
236
|
+
} : undefined),
|
|
155
237
|
ref: rootRef,
|
|
156
238
|
tabIndex: -1
|
|
157
239
|
}), isEmpty ? /*#__PURE__*/React.createElement("div", {
|