From 0a6aed906f76690f8a6e3e026cd30dbf991ef148 Mon Sep 17 00:00:00 2001 From: Mikkel Laursen Date: Mon, 19 Apr 2021 17:03:05 -0600 Subject: [PATCH] refactor(tooltip): Cleaned up some useTooltip code --- packages/tooltip/src/useTooltip.ts | 97 +++++++++++++++--------------- 1 file changed, 47 insertions(+), 50 deletions(-) diff --git a/packages/tooltip/src/useTooltip.ts b/packages/tooltip/src/useTooltip.ts index aba1c8db67..f87e291602 100644 --- a/packages/tooltip/src/useTooltip.ts +++ b/packages/tooltip/src/useTooltip.ts @@ -13,15 +13,19 @@ import { import cn from "classnames"; import { TransitionHooks, useFixedPositioning } from "@react-md/transition"; import { - HorizontalPosition, + ABOVE_CENTER_ANCHOR, + BELOW_CENTER_ANCHOR, + CENTER_LEFT_ANCHOR, + CENTER_RIGHT_ANCHOR, HoverModeEventHandlers, HoverModeOnlyReturnValue, + PositionAnchor, + SimplePosition, unitToNumber, useHoverMode, useOnUnmount, UserInteractionMode, useUserInteractionMode, - VerticalPosition, } from "@react-md/utils"; import { @@ -39,11 +43,27 @@ import { useTooltipPosition, } from "./useTooltipPosition"; +/** @internal */ +function getAnchor(position: SimplePosition): PositionAnchor { + switch (position) { + case "above": + return ABOVE_CENTER_ANCHOR; + case "below": + return BELOW_CENTER_ANCHOR; + case "left": + return CENTER_LEFT_ANCHOR; + case "right": + return CENTER_RIGHT_ANCHOR; + default: + throw new Error(`Invalid position: ${position}`); + } +} + /** * @internal * @remarks \@since 2.8.0 */ -export type TooltipInitiated = UserInteractionMode | null; +export type TooltipInitiatedBy = UserInteractionMode | null; /** @remarks \@since 2.8.0 */ export type TooltipTouchEventHandlers = Pick< @@ -302,7 +322,7 @@ export function useTooltip({ onEntered, onExited, disableSwapping, - disableHoverMode, + disableHoverMode: disabled, disableAutoSpacing = process.env.NODE_ENV === "test", }: TooltipHookOptions): TooltipHookReturnValue { const containerRef = useRef(null); @@ -311,54 +331,52 @@ export function useTooltip({ defaultPosition, threshold, }); - const horizontal = position === "left" || position === "right"; const mode = useUserInteractionMode(); - const [touched, setTouched] = useState(false); - const initiated = useRef(null); + const [initiatedBy, setInitiatedBy] = useState(null); const windowFocusEvent = useRef(false); const timeout = useRef(undefined); const { visible, setVisible, handlers: mouseHandlers, + disableHoverMode, ...others } = useHoverMode({ - disabled: disableHoverMode, + disabled, onMouseEnter: (event) => { onMouseEnter?.(event); - if (initiated.current !== null) { + if (initiatedBy !== null) { event.stopPropagation(); return; } - initiated.current = "mouse"; containerRef.current = event.currentTarget; updatePosition(event.currentTarget); + setInitiatedBy("mouse"); }, onMouseLeave: (event) => { onMouseLeave?.(event); - if (initiated.current !== "mouse") { + if (initiatedBy !== "mouse") { event.stopPropagation(); return; } - initiated.current = null; + setInitiatedBy(null); }, }); const hide = useCallback(() => { - initiated.current = null; window.clearTimeout(timeout.current); setVisible(false); + setInitiatedBy(null); }, [setVisible]); const onBlur = (event: FocusEvent): void => { propOnBlur?.(event); - if (initiated.current !== "keyboard") { + if (initiatedBy !== "keyboard") { return; } - window.clearTimeout(timeout.current); hide(); }; const onFocus = (event: FocusEvent): void => { @@ -371,11 +389,11 @@ export function useTooltip({ return; } - if (mode !== "keyboard" || initiated.current !== null) { + if (mode !== "keyboard" || initiatedBy !== null) { return; } - initiated.current = "keyboard"; + setInitiatedBy("keyboard"); window.clearTimeout(timeout.current); containerRef.current = event.currentTarget; updatePosition(event.currentTarget); @@ -387,11 +405,10 @@ export function useTooltip({ const onKeyDown = (event: KeyboardEvent): void => { propOnKeyDown?.(event); - if (initiated.current !== "keyboard" || event.key !== "Escape") { + if (initiatedBy !== "keyboard" || event.key !== "Escape") { return; } - window.clearTimeout(timeout.current); hide(); }; @@ -402,8 +419,7 @@ export function useTooltip({ return; } - initiated.current = "touch"; - setTouched(true); + setInitiatedBy("touch"); window.clearTimeout(timeout.current); timeout.current = window.setTimeout(() => { setVisible(true); @@ -453,43 +469,27 @@ export function useTooltip({ }; }, [hide, mode]); useEffect(() => { - if (mode !== "touch") { + if (initiatedBy !== "touch") { return; } + window.addEventListener("touchmove", hide); window.addEventListener("touchend", hide); return () => { + window.removeEventListener("touchmove", hide); window.removeEventListener("touchend", hide); }; - }, [hide, mode]); - useEffect(() => { - if (!touched) { - return; - } - - // need to cancel the tooltip appearance if a touchmove event occurs before - // the tooltip appears since it means the page will scroll - const handler = (): void => { - initiated.current = null; - window.clearTimeout(timeout.current); - setTouched(false); - setVisible(false); - }; + }, [hide, initiatedBy, setVisible]); - window.addEventListener("touchmove", handler); - return () => { - window.removeEventListener("touchmove", handler); - }; - }, [setVisible, touched]); + useOnUnmount(() => { + window.clearTimeout(timeout.current); + }); const { updateStyle: _u, ...positionProps } = useFixedPositioning({ style, - anchor: { - x: horizontal ? (position as HorizontalPosition) : "center", - y: horizontal ? "center" : (position as VerticalPosition), - }, + anchor: getAnchor(position), disableSwapping: disableSwapping ?? !!determinedPosition, - fixedTo: containerRef.current, + fixedTo: containerRef, getOptions: (node) => { let tooltipSpacing = dense ? denseSpacing : spacing; /* istanbul ignore next */ @@ -516,10 +516,6 @@ export function useTooltip({ onExited, }); - useOnUnmount(() => { - window.clearTimeout(timeout.current); - }); - const tooltipHandlers: Required> = { ...mouseHandlers, onFocus, @@ -550,5 +546,6 @@ export function useTooltip({ handlers: tooltipHandlers, elementProps, tooltipProps, + disableHoverMode, }; }