Skip to content

Commit 7e5d31c

Browse files
committed
fix(ui): fix focus when press ArrowRight
1 parent 057f26a commit 7e5d31c

File tree

10 files changed

+103
-41
lines changed

10 files changed

+103
-41
lines changed

packages/ui/src/components/auto-complete/AutoComplete.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,11 @@ function AutoComplete<T extends DAutoCompleteItem>(
188188
onFocusChange={(key) => {
189189
switch (key) {
190190
case 'next':
191-
changeFocusItem(vsRef.current?.scrollByStep(1));
191+
changeFocusItem(vsRef.current?.scrollToStep(1));
192192
break;
193193

194194
case 'prev':
195-
changeFocusItem(vsRef.current?.scrollByStep(-1));
195+
changeFocusItem(vsRef.current?.scrollToStep(-1));
196196
break;
197197

198198
case 'first':

packages/ui/src/components/cascader/List.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,11 @@ function List<V extends DId, T extends DCascaderItem<V>>(
8484
};
8585
switch (key) {
8686
case 'next':
87-
focusNode(vsRef.current?.scrollByStep(1));
87+
focusNode(vsRef.current?.scrollToStep(1));
8888
break;
8989

9090
case 'prev':
91-
focusNode(vsRef.current?.scrollByStep(-1));
91+
focusNode(vsRef.current?.scrollToStep(-1));
9292
break;
9393

9494
case 'first':

packages/ui/src/components/dropdown/Dropdown.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { DSeparator } from '../separator';
1717
import { DGroup } from './Group';
1818
import { DItem } from './Item';
1919
import { DSub } from './Sub';
20-
import { checkEnableItem, getSameLevelItems } from './utils';
20+
import { checkEnableItem, getSameLevelEnableItems } from './utils';
2121

2222
export interface DDropdownRef {
2323
updatePosition: () => void;
@@ -233,7 +233,7 @@ function Dropdown<ID extends DId, T extends DDropdownItem<ID>>(
233233

234234
if (isFocus) {
235235
handleKeyDown = (e) => {
236-
const sameLevelItems = getSameLevelItems((nth(subParents, -1)?.children as T[]) ?? dList);
236+
const sameLevelItems = getSameLevelEnableItems((nth(subParents, -1)?.children as T[]) ?? dList);
237237
const focusItem = (val?: T) => {
238238
if (val) {
239239
setFocusIds(subParents.map((parentItem) => parentItem.id).concat([val.id]));
@@ -288,7 +288,7 @@ function Dropdown<ID extends DId, T extends DDropdownItem<ID>>(
288288
if (itemType === 'sub') {
289289
addPopupId(itemId);
290290
if (children) {
291-
const newFocusItem = nth(getSameLevelItems(children), 0);
291+
const newFocusItem = nth(getSameLevelEnableItems(children), 0);
292292
if (newFocusItem) {
293293
setFocusIds(newSubParents.map((parentItem) => parentItem.id).concat([newFocusItem.id]));
294294
}

packages/ui/src/components/dropdown/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export function checkEnableItem<ID extends DId, T extends DDropdownItem<ID>>(ite
55
return (item.type === 'item' || item.type === 'sub') && !item.disabled;
66
}
77

8-
export function getSameLevelItems<ID extends DId, T extends DDropdownItem<ID>>(arr: T[]) {
8+
export function getSameLevelEnableItems<ID extends DId, T extends DDropdownItem<ID>>(arr: T[]) {
99
const items: T[] = [];
1010
const reduceArr = (arr: T[]) => {
1111
for (const item of arr) {

packages/ui/src/components/menu/Menu.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { useComponentConfig, usePrefixConfig } from '../root';
1515
import { DGroup } from './Group';
1616
import { DItem } from './Item';
1717
import { DSub } from './Sub';
18-
import { checkEnableItem, getSameLevelItems } from './utils';
18+
import { checkEnableItem, getSameLevelEnableItems } from './utils';
1919

2020
export interface DMenuRef {
2121
updatePosition: () => void;
@@ -236,7 +236,7 @@ function Menu<ID extends DId, T extends DMenuItem<ID>>(props: DMenuProps<ID, T>,
236236

237237
if (isFocus) {
238238
handleKeyDown = (e) => {
239-
const sameLevelItems = getSameLevelItems((nth(subParents, -1)?.children as T[]) ?? dList);
239+
const sameLevelItems = getSameLevelEnableItems((nth(subParents, -1)?.children as T[]) ?? dList);
240240
const focusItem = (val?: T) => {
241241
if (val) {
242242
setFocusIds(subParents.map((parentItem) => parentItem.id).concat([val.id]));
@@ -251,7 +251,7 @@ function Menu<ID extends DId, T extends DMenuItem<ID>>(props: DMenuProps<ID, T>,
251251
addPopupId(itemId);
252252
}
253253
if (children) {
254-
const newFocusItem = nth(getSameLevelItems(children), 0);
254+
const newFocusItem = nth(getSameLevelEnableItems(children), 0);
255255
if (newFocusItem) {
256256
setFocusIds(_subParents.map((parentItem) => parentItem.id).concat([newFocusItem.id]));
257257
}
@@ -425,7 +425,7 @@ function Menu<ID extends DId, T extends DMenuItem<ID>>(props: DMenuProps<ID, T>,
425425
setFocusIds(subParents.map((parentItem) => parentItem.id).concat([itemId]));
426426

427427
if (dMode === 'vertical') {
428-
const sameLevelItems = getSameLevelItems((nth(subParents, -1)?.children as T[]) ?? dList);
428+
const sameLevelItems = getSameLevelEnableItems((nth(subParents, -1)?.children as T[]) ?? dList);
429429
handleSubExpand(sameLevelItems);
430430
}
431431
}

packages/ui/src/components/menu/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export function checkEnableItem<ID extends DId, T extends DMenuItem<ID>>(item: T
55
return (item.type === 'item' || item.type === 'sub') && !item.disabled;
66
}
77

8-
export function getSameLevelItems<ID extends DId, T extends DMenuItem<ID>>(arr: T[]) {
8+
export function getSameLevelEnableItems<ID extends DId, T extends DMenuItem<ID>>(arr: T[]) {
99
const items: T[] = [];
1010
const reduceArr = (arr: T[]) => {
1111
for (const item of arr) {

packages/ui/src/components/select/Select.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -441,11 +441,11 @@ function Select<V extends DId, T extends DSelectItem<V>>(
441441
onFocusChange={(key) => {
442442
switch (key) {
443443
case 'next':
444-
changeFocusItem(vsRef.current?.scrollByStep(1));
444+
changeFocusItem(vsRef.current?.scrollToStep(1));
445445
break;
446446

447447
case 'prev':
448-
changeFocusItem(vsRef.current?.scrollByStep(-1));
448+
changeFocusItem(vsRef.current?.scrollToStep(-1));
449449
break;
450450

451451
case 'first':

packages/ui/src/components/tree/Panel.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ function Panel<V extends DId, T extends DTreeItem<V>>(
7878

7979
switch (key) {
8080
case 'next':
81-
focusNode(vsRef.current?.scrollByStep(1));
81+
focusNode(vsRef.current?.scrollToStep(1));
8282
break;
8383

8484
case 'prev':
85-
focusNode(vsRef.current?.scrollByStep(-1));
85+
focusNode(vsRef.current?.scrollToStep(-1));
8686
break;
8787

8888
case 'first':
@@ -105,7 +105,7 @@ function Panel<V extends DId, T extends DTreeItem<V>>(
105105
case 'next-level':
106106
if (!dFocusItem.isLeaf) {
107107
if (isExpand) {
108-
focusNode(vsRef.current?.scrollByStep(1));
108+
focusNode(vsRef.current?.scrollToNested());
109109
} else {
110110
onExpandChange(dFocusItem);
111111
}

packages/ui/src/components/tree/SearchPanel.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ function SearchPanel<V extends DId, T extends DTreeItem<V>>(
6565
};
6666
switch (key) {
6767
case 'next':
68-
focusItem(vsRef.current?.scrollByStep(1));
68+
focusItem(vsRef.current?.scrollToStep(1));
6969
break;
7070

7171
case 'prev':
72-
focusItem(vsRef.current?.scrollByStep(-1));
72+
focusItem(vsRef.current?.scrollToStep(-1));
7373
break;
7474

7575
case 'first':

packages/ui/src/components/virtual-scroll/VirtualScroll.tsx

Lines changed: 83 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ export type DVirtualScrollPerformance<T> = Pick<
1818

1919
export interface DVirtualScrollRef<T> {
2020
scrollToItem: (item: T) => void;
21-
scrollByStep: (step: number) => T | undefined;
21+
scrollToStep: (step: 1 | -1) => T | undefined;
22+
scrollToNested: () => T | undefined;
2223
scrollToStart: () => T | undefined;
2324
scrollToEnd: () => T | undefined;
2425
}
@@ -92,8 +93,8 @@ function VirtualScroll<T>(props: DVirtualScrollProps<T>, ref: React.ForwardedRef
9293
let firstFocusableItem: T | undefined;
9394
let lastFocusableItem: T | undefined;
9495

95-
const items = new Map<DId, { item: T; accSize: number; nestedSize: number }>();
96-
const reduceArr = (arr: T[]) => {
96+
const items = new Map<DId, { item: T; level: number; accSize: number; nestedSize: number }>();
97+
const reduceArr = (arr: T[], level = 0) => {
9798
let size = 0;
9899
for (const item of arr) {
99100
if (checkFocusable(item)) {
@@ -103,20 +104,22 @@ function VirtualScroll<T>(props: DVirtualScrollProps<T>, ref: React.ForwardedRef
103104
}
104105
}
105106

106-
size += getItemSize(item);
107-
accSize += getItemSize(item);
107+
const key = dItemKey(item);
108+
const itemSize = getItemSize(item);
109+
size += itemSize;
110+
accSize += itemSize;
108111

109-
const data = { item, accSize, nestedSize: 0 };
110-
items.set(dItemKey(item), data);
112+
const data = { item, level, accSize, nestedSize: 0 };
113+
items.set(key, data);
111114

112115
const nestedData = dItemNested?.(item);
113-
if (nestedData && nestedData.list && (isUndefined(dExpands) || dExpands.has(dItemKey(item)))) {
116+
if (nestedData && nestedData.list && (isUndefined(dExpands) || dExpands.has(key))) {
114117
if (nestedData.list.length === 0) {
115118
data.nestedSize = nestedData.emptySize ?? 0;
116119
size += data.nestedSize;
117120
accSize += data.nestedSize;
118121
} else {
119-
data.nestedSize = reduceArr(nestedData.list);
122+
data.nestedSize = reduceArr(nestedData.list, level + 1);
120123
size += data.nestedSize;
121124
}
122125
}
@@ -198,7 +201,7 @@ function VirtualScroll<T>(props: DVirtualScrollProps<T>, ref: React.ForwardedRef
198201
childrenList = getList(nestedList.length === 0 ? [EMPTY] : nestedList, parent.concat([item as T]));
199202
} else {
200203
childrenList = dataRef.current.listCache.get(key) ?? [];
201-
if (dExpands.has(dItemKey(item as T))) {
204+
if (dExpands.has(key)) {
202205
childrenList = getList(nestedList.length === 0 ? [EMPTY] : nestedList, parent.concat([item as T]));
203206
dataRef.current.listCache.set(key, childrenList);
204207
}
@@ -324,10 +327,10 @@ function VirtualScroll<T>(props: DVirtualScrollProps<T>, ref: React.ForwardedRef
324327
return lastFocusableItem;
325328
});
326329

327-
const scrollByStep = useEventCallback((step: number) => {
330+
const scrollToStep = useEventCallback((step: 1 | -1) => {
328331
if (!isUndefined(paddingSize)) {
329332
if (isUndefined(dFocusItem)) {
330-
return step > 0 ? scrollToStart() : scrollToEnd();
333+
return;
331334
}
332335

333336
let findItem: T | undefined;
@@ -337,27 +340,28 @@ function VirtualScroll<T>(props: DVirtualScrollProps<T>, ref: React.ForwardedRef
337340
let index = -1;
338341
let findIndex = -1;
339342
const accSizeList = [];
343+
const focusKey = dItemKey(dFocusItem);
340344
for (const iterator of itemsMap) {
341345
index += 1;
342-
if (dItemKey(iterator[1].item) === dItemKey(dFocusItem)) {
346+
if (iterator[0] === focusKey) {
343347
findIndex = index;
344348
}
345349
accSizeList.push(iterator[1]);
346350
}
347351

348352
if (findIndex !== -1) {
349-
if (step < 0) {
350-
for (let index = findIndex - 1, n = 0; n < accSizeList.length; index--, n++) {
351-
const accSizeItem = nth(accSizeList, index);
353+
if (step === 1) {
354+
for (let index = findIndex + 1, n = 0; n < accSizeList.length; index++, n++) {
355+
const accSizeItem = nth(accSizeList, index % accSizeList.length);
352356
if (accSizeItem && checkFocusable(accSizeItem.item)) {
353357
findItem = accSizeItem.item;
354358
offsetSize = [accSizeItem.accSize - getItemSize(findItem) + paddingSize, accSizeItem.accSize + paddingSize];
355359
break;
356360
}
357361
}
358362
} else {
359-
for (let index = findIndex + 1, n = 0; n < accSizeList.length; index++, n++) {
360-
const accSizeItem = nth(accSizeList, index % accSizeList.length);
363+
for (let index = findIndex - 1, n = 0; n < accSizeList.length; index--, n++) {
364+
const accSizeItem = nth(accSizeList, index);
361365
if (accSizeItem && checkFocusable(accSizeItem.item)) {
362366
findItem = accSizeItem.item;
363367
offsetSize = [accSizeItem.accSize - getItemSize(findItem) + paddingSize, accSizeItem.accSize + paddingSize];
@@ -375,7 +379,7 @@ function VirtualScroll<T>(props: DVirtualScrollProps<T>, ref: React.ForwardedRef
375379
} else if (offsetSize[0] > listElScrollPosition + listElClientSize) {
376380
scrollTo(offsetSize[1] - listElClientSize + paddingSize);
377381
} else {
378-
if (step > 0) {
382+
if (step === 1) {
379383
if (offsetSize[1] > listElScrollPosition + listElClientSize) {
380384
scrollTo(offsetSize[1] - listElClientSize + paddingSize);
381385
}
@@ -392,15 +396,73 @@ function VirtualScroll<T>(props: DVirtualScrollProps<T>, ref: React.ForwardedRef
392396
}
393397
});
394398

399+
const scrollToNested = useEventCallback(() => {
400+
if (!isUndefined(paddingSize)) {
401+
if (isUndefined(dFocusItem)) {
402+
return;
403+
}
404+
405+
let findItem: T | undefined;
406+
let offsetSize: [number, number] | undefined;
407+
408+
if (listRef.current) {
409+
let index = -1;
410+
let findIndex = -1;
411+
let level = 0;
412+
const accSizeList = [];
413+
const focusKey = dItemKey(dFocusItem);
414+
for (const iterator of itemsMap) {
415+
index += 1;
416+
if (iterator[0] === focusKey) {
417+
findIndex = index;
418+
level = iterator[1].level;
419+
}
420+
accSizeList.push(iterator[1]);
421+
}
422+
423+
if (findIndex !== -1) {
424+
for (let index = findIndex + 1; index < accSizeList.length; index++) {
425+
const accSizeItem = accSizeList[index];
426+
if (accSizeItem.level <= level) {
427+
return;
428+
}
429+
if (checkFocusable(accSizeItem.item)) {
430+
findItem = accSizeItem.item;
431+
offsetSize = [accSizeItem.accSize - getItemSize(findItem) + paddingSize, accSizeItem.accSize + paddingSize];
432+
break;
433+
}
434+
}
435+
}
436+
437+
if (!isUndefined(offsetSize)) {
438+
const listElScrollPosition = listRef.current[dHorizontal ? 'scrollLeft' : 'scrollTop'];
439+
const listElClientSize = listRef.current[dHorizontal ? 'clientWidth' : 'clientHeight'];
440+
if (listElScrollPosition > offsetSize[1]) {
441+
scrollTo(offsetSize[0] - paddingSize);
442+
} else if (offsetSize[0] > listElScrollPosition + listElClientSize) {
443+
scrollTo(offsetSize[1] - listElClientSize + paddingSize);
444+
} else {
445+
if (offsetSize[1] > listElScrollPosition + listElClientSize) {
446+
scrollTo(offsetSize[1] - listElClientSize + paddingSize);
447+
}
448+
}
449+
}
450+
}
451+
452+
return findItem;
453+
}
454+
});
455+
395456
useImperativeHandle(
396457
ref,
397458
() => ({
398459
scrollToItem,
399-
scrollByStep,
460+
scrollToStep,
461+
scrollToNested,
400462
scrollToStart,
401463
scrollToEnd,
402464
}),
403-
[scrollByStep, scrollToEnd, scrollToStart, scrollToItem]
465+
[scrollToItem, scrollToStep, scrollToNested, scrollToStart, scrollToEnd]
404466
);
405467

406468
return children({

0 commit comments

Comments
 (0)