@zendeskgarden/container-tooltip
Version:
Containers relating to tooltip in the Garden Design System
155 lines (149 loc) • 5.04 kB
JavaScript
/**
* 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.
*/
;
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;