UNPKG

2.49 kBPlain TextView Raw
1import * as React from "react";
2import {
3 SealedInitialState,
4 useSealedState,
5} from "reakit-utils/useSealedState";
6import {
7 useCompositeState,
8 CompositeState,
9 CompositeActions,
10 CompositeInitialState,
11} from "../Composite/CompositeState";
12
13export type TabState = CompositeState & {
14 /**
15 * The current selected tab's `id`.
16 */
17 selectedId?: TabState["currentId"];
18 /**
19 * Lists all the panels.
20 */
21 panels: TabState["items"];
22 /**
23 * Whether the tab selection should be manual.
24 */
25 manual: boolean;
26};
27
28export type TabActions = CompositeActions & {
29 /**
30 * Moves into and selects a tab by its `id`.
31 */
32 select: TabActions["move"];
33 /**
34 * Sets `selectedId`.
35 */
36 setSelectedId: TabActions["setCurrentId"];
37 /**
38 * Registers a tab panel.
39 */
40 registerPanel: TabActions["registerItem"];
41 /**
42 * Unregisters a tab panel.
43 */
44 unregisterPanel: TabActions["unregisterItem"];
45};
46
47export type TabInitialState = CompositeInitialState &
48 Partial<Pick<TabState, "selectedId" | "manual">>;
49
50export type TabStateReturn = TabState & TabActions;
51
52export function useTabState(
53 initialState: SealedInitialState<TabInitialState> = {}
54): TabStateReturn {
55 const {
56 selectedId: initialSelectedId,
57 loop = true,
58 manual = false,
59 ...sealed
60 } = useSealedState(initialState);
61
62 const composite = useCompositeState({
63 loop,
64 currentId: initialSelectedId,
65 ...sealed,
66 });
67 const panels = useCompositeState();
68 const [selectedId, setSelectedId] = React.useState(initialSelectedId);
69
70 const select = React.useCallback(
71 (id: string) => {
72 composite.move(id);
73 setSelectedId(id);
74 },
75 [composite.move]
76 );
77
78 // If selectedId is not set, use the currentId. It's still possible to have
79 // no selected tab with useTabState({ selectedId: null });
80 React.useEffect(() => {
81 if (selectedId === null) return;
82 const selectedItem = composite.items.find((item) => item.id === selectedId);
83 if (selectedItem) return;
84 if (composite.currentId) {
85 setSelectedId(composite.currentId);
86 }
87 }, [selectedId, composite.items, composite.currentId]);
88
89 return {
90 ...composite,
91 selectedId,
92 panels: panels.items,
93 manual,
94 select,
95 setSelectedId,
96 registerPanel: React.useCallback((panel) => panels.registerItem(panel), [
97 panels.registerItem,
98 ]),
99 unregisterPanel: React.useCallback((id) => panels.unregisterItem(id), [
100 panels.unregisterItem,
101 ]),
102 };
103}