-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathexample.tsx
121 lines (104 loc) Β· 2.43 KB
/
example.tsx
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { createContext, useMemo, useCallback, useRef, useState } from 'react';
import type { ReactNode, PropsWithChildren, KeyboardEvent } from 'react';
interface TabsContextShape {
selectedTab: string | null;
setSelectedTab: (tab: string) => void;
tabsPrefix: string;
}
const TabsContext = createContext<TabsContextShape>({
tabsPrefix: '',
selectedTab: null,
setSelectedTab: (tab: string) => {
throw new Error('Should be used with TabsContext.Provider');
},
});
const Tabs = ({
children,
defaultSelectedTab,
}: PropsWithChildren<{
defaultSelectedTab: string;
}>) => {
const [selectedTab, setSelectedTab] = useState(defaultSelectedTab);
const value = useMemo(
() => ({
selectedTab,
setSelectedTab,
}),
[selectedTab, setSelectedTab]
);
return <TabsContext.Provider value={value}>{children}</TabsContext.Provider>;
};
export const TabList = ({
children,
label,
}: PropsWithChildren<{
label: string;
}>) => {
const listRef = useRef<HTMLDivElement>(null);
const handleKeyDown = useCallback((event: KeyboardEvent) => {
if (!listRef.current) return;
const $tabs = [
...listRef.current.querySelectorAll('[role="tab"]:not([disabled])'),
];
const index = $tabs.indexOf(document.activeElement as HTMLElement);
if (index < 0) return;
switch (event.key) {
case 'ArrowUp':
case 'ArrowLeft': {
const next = (index - 1 + $tabs.length) % $tabs.length;
$tabs[next]?.focus();
break;
}
case 'ArrowDown':
case 'ArrowRight': {
const next = (index + 1 + $tabs.length) % $tabs.length;
$tabs[next]?.focus();
break;
}
}
}, []);
return (
<div
ref={listRef}
role='tablist'
aria-label={label}
onKeyDown={handleKeyDown}
>
{children}
</div>
);
};
export const Tab = ({
children,
tab,
}: PropsWithChildren<{
tab: string;
}>) => {
const { selectedTab, setSelectedTab, tabsPrefix } = useContext(TabsContext);
return (
<button
type='button'
role='tab'
aria-selected={selectedTab === tab}
aria-controls={`tab-${tabsPrefix}-tabpanel-${tab}`}
onClick={() => setSelectedTab(tab)}
tabIndex={selectedTab === tab ? 0 : -1}
>
{children}
</button>
);
};
export const TabPanel = ({
children,
tab,
}: PropsWithChildren<{
tab: string;
}>) => {
const { selectedTab, tabsPrefix } = useContext(TabsContext);
if (selectedTab !== tab) return null;
return (
<div role='tabpanel' tabIndex='0' id={`tab-${tabsPrefix}-tabpanel-${tab}`}>
{children}
</div>
);
};