Skip to content

Commit feb012d

Browse files
committed
feat: improve keyboard navigation when reach maxCount
1 parent e9b59f9 commit feb012d

File tree

1 file changed

+66
-3
lines changed

1 file changed

+66
-3
lines changed

src/OptionList.tsx

Lines changed: 66 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,53 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
206206
setActiveKey(nextActiveKey);
207207
}, [open, searchValue]);
208208

209+
const getNextMatchingNode = (
210+
nodes: EventDataNode<any>[],
211+
currentKey: Key | null,
212+
direction: 'next' | 'prev' = 'next',
213+
): EventDataNode<any> | null => {
214+
const availableNodes: EventDataNode<any>[] = [];
215+
216+
const collectNodes = (nodeList: EventDataNode<any>[]) => {
217+
nodeList.forEach(node => {
218+
if (!node.disabled && node.selectable !== false) {
219+
// only collect selected nodes
220+
if (displayValues?.some(v => v.value === node[fieldNames.value])) {
221+
availableNodes.push(node);
222+
}
223+
}
224+
if (node[fieldNames.children]) {
225+
collectNodes(node[fieldNames.children]);
226+
}
227+
});
228+
};
229+
230+
collectNodes(nodes);
231+
232+
if (availableNodes.length === 0) {
233+
return null;
234+
}
235+
236+
// if no current selected node, return first available node
237+
if (!currentKey) {
238+
return availableNodes[0];
239+
}
240+
241+
const currentIndex = availableNodes.findIndex(node => node[fieldNames.value] === currentKey);
242+
243+
// if current node is not in available nodes list, return first node
244+
if (currentIndex === -1) {
245+
return availableNodes[0];
246+
}
247+
248+
const nextIndex =
249+
direction === 'next'
250+
? (currentIndex + 1) % availableNodes.length
251+
: (currentIndex - 1 + availableNodes.length) % availableNodes.length;
252+
253+
return availableNodes[nextIndex];
254+
};
255+
209256
// ========================= Keyboard =========================
210257
React.useImperativeHandle(ref, () => ({
211258
scrollTo: treeRef.current?.scrollTo,
@@ -217,7 +264,18 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
217264
case KeyCode.DOWN:
218265
case KeyCode.LEFT:
219266
case KeyCode.RIGHT:
220-
treeRef.current?.onKeyDown(event as React.KeyboardEvent<HTMLDivElement>);
267+
if (isOverMaxCount) {
268+
event.preventDefault();
269+
const direction = which === KeyCode.UP || which === KeyCode.LEFT ? 'prev' : 'next';
270+
const nextNode = getNextMatchingNode(memoTreeData, activeKey, direction);
271+
if (nextNode) {
272+
setActiveKey(nextNode[fieldNames.value]);
273+
// ensure scroll to visible area
274+
treeRef.current?.scrollTo({ key: nextNode[fieldNames.value] });
275+
}
276+
} else {
277+
treeRef.current?.onKeyDown(event as React.KeyboardEvent<HTMLDivElement>);
278+
}
221279
break;
222280

223281
// >>> Select item
@@ -251,10 +309,15 @@ const OptionList: React.ForwardRefRenderFunction<ReviseRefOptionListProps> = (_,
251309
);
252310

253311
const onActiveChange = (key: Key) => {
254-
if (isOverMaxCount && !displayValues?.some(v => v.value === key)) {
312+
if (!isOverMaxCount) {
313+
setActiveKey(key);
255314
return;
256315
}
257-
setActiveKey(key);
316+
317+
const nextNode = getNextMatchingNode(memoTreeData, key);
318+
if (nextNode) {
319+
setActiveKey(nextNode[fieldNames.value]);
320+
}
258321
};
259322

260323
// ========================== Render ==========================

0 commit comments

Comments
 (0)