1 | import * as React from "react";
|
2 | import {
|
3 | useSealedState,
|
4 | SealedInitialState,
|
5 | } from "reakit-utils/useSealedState";
|
6 | import {
|
7 | PopoverState,
|
8 | PopoverActions,
|
9 | PopoverInitialState,
|
10 | usePopoverState,
|
11 | PopoverStateReturn,
|
12 | } from "../Popover/PopoverState";
|
13 | import globalState from "./__globalState";
|
14 |
|
15 | export type TooltipState = Omit<PopoverState, "modal"> & {
|
16 | |
17 |
|
18 |
|
19 | unstable_timeout: number;
|
20 | };
|
21 |
|
22 | export type TooltipActions = Omit<PopoverActions, "setModal"> & {
|
23 | |
24 |
|
25 |
|
26 | unstable_setTimeout: React.Dispatch<
|
27 | React.SetStateAction<TooltipState["unstable_timeout"]>
|
28 | >;
|
29 | };
|
30 |
|
31 | export type TooltipInitialState = Omit<PopoverInitialState, "modal"> &
|
32 | Pick<Partial<TooltipState>, "unstable_timeout">;
|
33 |
|
34 | export type TooltipStateReturn = Omit<
|
35 | PopoverStateReturn,
|
36 | "modal" | "setModal"
|
37 | > &
|
38 | TooltipState &
|
39 | TooltipActions;
|
40 |
|
41 | export function useTooltipState(
|
42 | initialState: SealedInitialState<TooltipInitialState> = {}
|
43 | ): TooltipStateReturn {
|
44 | const {
|
45 | placement = "top",
|
46 | unstable_timeout: initialTimeout = 0,
|
47 | ...sealed
|
48 | } = useSealedState(initialState);
|
49 | const [timeout, setTimeout] = React.useState(initialTimeout);
|
50 | const showTimeout = React.useRef<number | null>(null);
|
51 | const hideTimeout = React.useRef<number | null>(null);
|
52 |
|
53 | const { modal, setModal, ...popover } = usePopoverState({
|
54 | ...sealed,
|
55 | placement,
|
56 | });
|
57 |
|
58 | const clearTimeouts = React.useCallback(() => {
|
59 | if (showTimeout.current !== null) {
|
60 | window.clearTimeout(showTimeout.current);
|
61 | }
|
62 | if (hideTimeout.current !== null) {
|
63 | window.clearTimeout(hideTimeout.current);
|
64 | }
|
65 | }, []);
|
66 |
|
67 | const hide = React.useCallback(() => {
|
68 | clearTimeouts();
|
69 | popover.hide();
|
70 |
|
71 |
|
72 | hideTimeout.current = window.setTimeout(() => {
|
73 | globalState.hide(popover.baseId);
|
74 | }, timeout);
|
75 | }, [clearTimeouts, popover.hide, timeout, popover.baseId]);
|
76 |
|
77 | const show = React.useCallback(() => {
|
78 | clearTimeouts();
|
79 | if (!timeout || globalState.currentTooltipId) {
|
80 |
|
81 |
|
82 | globalState.show(popover.baseId);
|
83 | popover.show();
|
84 | } else {
|
85 |
|
86 |
|
87 | globalState.show(null);
|
88 |
|
89 | showTimeout.current = window.setTimeout(() => {
|
90 | globalState.show(popover.baseId);
|
91 | popover.show();
|
92 | }, timeout);
|
93 | }
|
94 | }, [clearTimeouts, timeout, popover.show, popover.baseId]);
|
95 |
|
96 | React.useEffect(() => {
|
97 | return globalState.subscribe((id) => {
|
98 | if (id !== popover.baseId) {
|
99 | clearTimeouts();
|
100 | if (popover.visible) {
|
101 |
|
102 | popover.hide();
|
103 | }
|
104 | }
|
105 | });
|
106 | }, [popover.baseId, clearTimeouts, popover.visible, popover.hide]);
|
107 |
|
108 | React.useEffect(
|
109 | () => () => {
|
110 | clearTimeouts();
|
111 | globalState.hide(popover.baseId);
|
112 | },
|
113 | [clearTimeouts, popover.baseId]
|
114 | );
|
115 |
|
116 | return {
|
117 | ...popover,
|
118 | hide,
|
119 | show,
|
120 | unstable_timeout: timeout,
|
121 | unstable_setTimeout: setTimeout,
|
122 | };
|
123 | }
|