UNPKG

@zendeskgarden/container-tooltip

Version:

Containers relating to tooltip in the Garden Design System

155 lines (149 loc) 5.04 kB
/** * Copyright Zendesk, Inc. * * Use of this source code is governed under the Apache License, Version 2.0 * found at http://www.apache.org/licenses/LICENSE-2.0. */ 'use strict'; var React = require('react'); var containerUtilities = require('@zendeskgarden/container-utilities'); var PropTypes = require('prop-types'); const useTooltip = _ref => { let { delayMilliseconds = 500, id, isVisible, triggerRef } = _ref; const _id = containerUtilities.useId(id); const [visibility, setVisibility] = React.useState(isVisible); const [isTriggerPopupExpanded, setIsTriggerPopupExpanded] = React.useState(false); const isMounted = React.useRef(false); const openTooltipTimeoutId = React.useRef(); const closeTooltipTimeoutId = React.useRef(); const openTooltip = React.useCallback(function (delayMs) { if (delayMs === void 0) { delayMs = delayMilliseconds; } clearTimeout(closeTooltipTimeoutId.current); const timerId = setTimeout(() => { if (isMounted.current) { setVisibility(true); } }, delayMs); openTooltipTimeoutId.current = Number(timerId); }, [delayMilliseconds]); const closeTooltip = React.useCallback(function (delayMs) { if (delayMs === void 0) { delayMs = delayMilliseconds; } clearTimeout(openTooltipTimeoutId.current); const timerId = setTimeout(() => { if (isMounted.current) { setVisibility(false); } }, delayMs); closeTooltipTimeoutId.current = Number(timerId); }, [delayMilliseconds]); React.useEffect(() => { isMounted.current = true; return () => { isMounted.current = false; }; }, []); React.useEffect(() => { return () => { clearTimeout(openTooltipTimeoutId.current); clearTimeout(closeTooltipTimeoutId.current); }; }, [closeTooltipTimeoutId, openTooltipTimeoutId]); React.useEffect(() => { const triggerElement = triggerRef?.current?.getAttribute('aria-haspopup') === 'true' ? triggerRef.current : null; const updateTriggerPopupExpandedState = () => { if (triggerElement) { setIsTriggerPopupExpanded(triggerElement.getAttribute('aria-expanded') === 'true'); } }; const mutationObserver = new MutationObserver(updateTriggerPopupExpandedState); if (triggerElement) { mutationObserver.observe(triggerElement, { attributes: true, attributeFilter: ['aria-expanded'] }); } updateTriggerPopupExpandedState(); return () => mutationObserver.disconnect(); }, [triggerRef]); const getTriggerProps = React.useCallback(function (_temp) { let { tabIndex = 0, onMouseEnter, onMouseLeave, onFocus, onBlur, onKeyDown, ...other } = _temp === void 0 ? {} : _temp; return { tabIndex, onMouseEnter: containerUtilities.composeEventHandlers(onMouseEnter, () => openTooltip()), onMouseLeave: containerUtilities.composeEventHandlers(onMouseLeave, () => closeTooltip()), onFocus: containerUtilities.composeEventHandlers(onFocus, () => openTooltip()), onBlur: containerUtilities.composeEventHandlers(onBlur, () => closeTooltip(0)), onKeyDown: containerUtilities.composeEventHandlers(onKeyDown, event => { if (event.key === containerUtilities.KEYS.ESCAPE && visibility) { closeTooltip(0); } }), 'aria-describedby': _id, 'data-garden-container-id': 'containers.tooltip', 'data-garden-container-version': '2.0.0', ref: triggerRef, ...other }; }, [_id, closeTooltip, openTooltip, triggerRef, visibility]); const getTooltipProps = React.useCallback(function (_temp2) { let { role = 'tooltip', onMouseEnter, onMouseLeave, ...other } = _temp2 === void 0 ? {} : _temp2; return { role, onMouseEnter: containerUtilities.composeEventHandlers(onMouseEnter, () => openTooltip()), onMouseLeave: containerUtilities.composeEventHandlers(onMouseLeave, () => closeTooltip()), 'aria-hidden': !visibility || isTriggerPopupExpanded, id: _id, ...other }; }, [_id, closeTooltip, openTooltip, visibility, isTriggerPopupExpanded]); return React.useMemo(() => ({ isVisible: visibility && !isTriggerPopupExpanded, getTooltipProps, getTriggerProps, openTooltip, closeTooltip }), [closeTooltip, getTooltipProps, getTriggerProps, openTooltip, visibility, isTriggerPopupExpanded]); }; const TooltipContainer = props => { const { children, render = children, ...options } = props; return React.createElement(React.Fragment, null, render(useTooltip(options))); }; TooltipContainer.defaultProps = { delayMilliseconds: 500 }; TooltipContainer.propTypes = { children: PropTypes.func, render: PropTypes.func, delayMilliseconds: PropTypes.number, id: PropTypes.string, isVisible: PropTypes.bool, triggerRef: PropTypes.any.isRequired }; exports.TooltipContainer = TooltipContainer; exports.useTooltip = useTooltip;