UNPKG

2.45 kBPlain TextView Raw
1import * as React from "react";
2import { createComponent } from "reakit-system/createComponent";
3import { createHook } from "reakit-system/createHook";
4import { useLiveRef } from "reakit-utils/useLiveRef";
5import {
6 CompositeItemOptions,
7 CompositeItemHTMLProps,
8 useCompositeItem,
9} from "../Composite/CompositeItem";
10import { TabStateReturn } from "./TabState";
11import { TAB_KEYS } from "./__keys";
12
13export type TabOptions = CompositeItemOptions &
14 Pick<Partial<TabStateReturn>, "manual"> &
15 Pick<TabStateReturn, "panels" | "selectedId" | "select">;
16
17export type TabHTMLProps = CompositeItemHTMLProps;
18
19export type TabProps = TabOptions & TabHTMLProps;
20
21function useTabPanelId(options: TabOptions) {
22 return React.useMemo(
23 () =>
24 options.panels?.find((panel) => panel.groupId === options.id)?.id ||
25 undefined,
26 [options.panels, options.id]
27 );
28}
29
30export const useTab = createHook<TabOptions, TabHTMLProps>({
31 name: "Tab",
32 compose: useCompositeItem,
33 keys: TAB_KEYS,
34
35 useOptions({ focusable = true, ...options }) {
36 return { focusable, ...options };
37 },
38
39 useProps(
40 options,
41 { onClick: htmlOnClick, onFocus: htmlOnFocus, ...htmlProps }
42 ) {
43 const selected = options.selectedId === options.id;
44 const tabPanelId = useTabPanelId(options);
45 const onClickRef = useLiveRef(htmlOnClick);
46 const onFocusRef = useLiveRef(htmlOnFocus);
47
48 const onClick = React.useCallback(
49 (event: React.MouseEvent) => {
50 onClickRef.current?.(event);
51 if (event.defaultPrevented) return;
52 if (options.disabled) return;
53 if (!options.id) return;
54 if (selected) return;
55 options.select?.(options.id);
56 },
57 [options.disabled, selected, options.select, options.id]
58 );
59
60 const onFocus = React.useCallback(
61 (event: React.FocusEvent) => {
62 onFocusRef.current?.(event);
63 if (event.defaultPrevented) return;
64 if (options.disabled) return;
65 if (options.manual) return;
66 if (!options.id) return;
67 if (selected) return;
68 options.select?.(options.id);
69 },
70 [options.id, options.disabled, options.manual, selected, options.select]
71 );
72
73 return {
74 role: "tab",
75 "aria-selected": selected,
76 "aria-controls": tabPanelId,
77 onClick,
78 onFocus,
79 ...htmlProps,
80 };
81 },
82});
83
84export const Tab = createComponent({
85 as: "button",
86 memo: true,
87 useHook: useTab,
88});