1 | import * as React from "react";
|
2 | import { createComponent } from "reakit-system/createComponent";
|
3 | import { createHook } from "reakit-system/createHook";
|
4 | import { useLiveRef } from "reakit-utils/useLiveRef";
|
5 | import {
|
6 | CompositeItemOptions,
|
7 | CompositeItemHTMLProps,
|
8 | useCompositeItem,
|
9 | } from "../Composite/CompositeItem";
|
10 | import { TabStateReturn } from "./TabState";
|
11 | import { TAB_KEYS } from "./__keys";
|
12 |
|
13 | export type TabOptions = CompositeItemOptions &
|
14 | Pick<Partial<TabStateReturn>, "manual"> &
|
15 | Pick<TabStateReturn, "panels" | "selectedId" | "select">;
|
16 |
|
17 | export type TabHTMLProps = CompositeItemHTMLProps;
|
18 |
|
19 | export type TabProps = TabOptions & TabHTMLProps;
|
20 |
|
21 | function 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 |
|
30 | export 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 |
|
84 | export const Tab = createComponent({
|
85 | as: "button",
|
86 | memo: true,
|
87 | useHook: useTab,
|
88 | });
|