blob: ed271aa375ed8c0c4078408e99caa319094ecce7 (
plain) (
blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
|
import React from "react";
import CompletionItem from "./CompletionItem";
import CompletionTitle from "./CompletionTitle";
interface Item {
icon?: string;
caption?: string;
url?: string;
}
interface Group {
name: string;
items: Item[];
}
interface Props {
select: number;
size: number;
completions: Group[];
}
const Completion: React.FC<Props> = ({ select, size, completions }) => {
const [viewOffset, setViewOffset] = React.useState(0);
const [prevSelect, setPrevSelect] = React.useState(-1);
React.useEffect(() => {
if (select === prevSelect) {
return;
}
const viewSelect = (() => {
let index = 0;
for (let i = 0; i < completions.length; ++i) {
++index;
const g = completions[i];
if (select + i + 1 < index + g.items.length) {
return select + i + 1;
}
index += g.items.length;
}
return -1;
})();
const nextViewOffset = (() => {
if (prevSelect < select) {
return Math.max(viewOffset, viewSelect - size + 1);
} else if (prevSelect > select) {
return Math.min(viewOffset, viewSelect);
}
return 0;
})();
setPrevSelect(select);
setViewOffset(nextViewOffset);
}, [select]);
let itemIndex = 0;
let viewIndex = 0;
const groups: Array<JSX.Element> = [];
completions.forEach((group, groupIndex) => {
const items = [];
const title = (
<CompletionTitle
id={`title-${groupIndex}`}
key={`group-${groupIndex}`}
shown={viewOffset <= viewIndex && viewIndex < viewOffset + size}
title={group.name}
/>
);
++viewIndex;
for (const item of group.items) {
items.push(
<CompletionItem
shown={viewOffset <= viewIndex && viewIndex < viewOffset + size}
key={`item-${itemIndex}`}
icon={item.icon}
caption={item.caption}
url={item.url}
highlight={itemIndex === select}
aria-selected={itemIndex === select}
role="menuitem"
/>
);
++viewIndex;
++itemIndex;
}
groups.push(
<div
key={`group-${groupIndex}`}
role="group"
aria-describedby={`title-${groupIndex}`}
>
{title}
<ul>{items}</ul>
</div>
);
});
return <div role="menu">{groups}</div>;
};
export default Completion;
|