@zohodesk/hooks
Version:
Unified Component Library - Hooks
114 lines (104 loc) • 3.29 kB
JavaScript
import { useState, useEffect, useRef } from 'react';
import { useFloating, shift, flip, arrow, offset } from '@floating-ui/react-dom';
import selectn from 'selectn';
import { useThemeByComponent } from "../themeprovider/ThemeProvider";
import useEscapeEffect from "../../custom-hooks/useEscapeEffect";
import debounce from "../../utils/debounce";
import useCommonReducer from "../../libraries/reducerHandler";
import tooltipReducer, { initialState } from "./Tooltip.reducers";
import { reset, setTitle } from "./Tooltip.actions";
import { getClosestTitleElement } from "./utils";
import { DATA_TITLE_ATTR, TITLE_ATTR, DATA_TITLE_ISHTML, TOOLTIP_DELAY, COMPONENT_NAME, ARROW_PLACEMENT } from "./Tooltip.constants";
export default function useTooltip(_ref) {
let {
container,
reducer,
offsetOption,
placement: tooltipPlacement
} = _ref;
const {
state,
dispatch
} = useCommonReducer(tooltipReducer, reducer, initialState);
const arrowReference = useRef(null);
const {
x,
y,
reference,
floating,
strategy: position,
middlewareData,
placement
} = useFloating({
placement: tooltipPlacement,
middleware: [offset(offsetOption), flip(), shift(), arrow({
element: arrowReference
})]
});
const {
title,
isHtmlTitle
} = state;
useEscapeEffect(() => {
handleReset();
}, [title]);
const componentThemeProps = useThemeByComponent(COMPONENT_NAME);
function handleReset() {
if (title) {
dispatch(reset());
}
}
function mouseHover(e) {
// For handling nested tooltip avoid call multiple times
if (e.isTooltipHanlde) {
handleReset();
return null;
}
const element = e.target;
const titleElement = getClosestTitleElement(element);
if (titleElement) {
const title = titleElement.getAttribute(DATA_TITLE_ATTR) || titleElement.getAttribute(TITLE_ATTR);
if (title != '') {
titleElement.setAttribute(DATA_TITLE_ATTR, title);
/* reason for removing title attribute avoiding default browser tooltip */
titleElement.removeAttribute(TITLE_ATTR);
}
const isHtmlTitle = titleElement.getAttribute(DATA_TITLE_ISHTML);
if (isHtmlTitle) {
dispatch(setTitle(title, isHtmlTitle == 'true'));
} else {
dispatch(setTitle(title));
}
e.isTooltipHanlde = true;
reference(titleElement);
} else {
handleReset();
}
}
const mouseHoverDebounced = debounce(mouseHover, TOOLTIP_DELAY);
useEffect(() => {
container.addEventListener('mouseover', mouseHoverDebounced);
container.addEventListener('mouseleave', handleReset);
container.addEventListener('click', handleReset);
return () => {
container.removeEventListener('mouseover', mouseHoverDebounced);
container.removeEventListener('mouseleave', handleReset);
container.removeEventListener('click', handleReset);
};
}, [title, container]);
const arrowX = selectn('arrow.x', middlewareData);
const arrowY = selectn('arrow.y', middlewareData);
return {
title,
isHtmlTitle,
floating,
position,
x,
y,
arrowReference,
arrowX,
arrowY,
arrowPlacement: ARROW_PLACEMENT[placement.split('-')[0]],
componentThemeProps
};
}