You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
4.6 KiB

import contains from 'dom-helpers/contains';
import * as React from 'react';
import { cloneElement, useCallback, useRef } from 'react';
import useTimeout from '@restart/hooks/useTimeout';
import warning from 'warning';
import { useUncontrolledProp } from 'uncontrollable';
import useMergedRefs from '@restart/hooks/useMergedRefs';
import Overlay from './Overlay';
import safeFindDOMNode from './safeFindDOMNode';
import { jsx as _jsx } from "react/jsx-runtime";
import { Fragment as _Fragment } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
function normalizeDelay(delay) {
return delay && typeof delay === 'object' ? delay : {
show: delay,
hide: delay
};
} // Simple implementation of mouseEnter and mouseLeave.
// React's built version is broken: https://github.com/facebook/react/issues/4251
// for cases when the trigger is disabled and mouseOut/Over can cause flicker
// moving from one child element to another.
function handleMouseOverOut( // eslint-disable-next-line @typescript-eslint/no-shadow
handler, args, relatedNative) {
const [e] = args;
const target = e.currentTarget;
const related = e.relatedTarget || e.nativeEvent[relatedNative];
if ((!related || related !== target) && !contains(target, related)) {
handler(...args);
}
}
const defaultProps = {
defaultShow: false,
trigger: ['hover', 'focus']
};
function OverlayTrigger({
trigger,
overlay,
children,
popperConfig = {},
show: propsShow,
defaultShow = false,
onToggle,
delay: propsDelay,
placement,
flip = placement && placement.indexOf('auto') !== -1,
...props
}) {
const triggerNodeRef = useRef(null);
const mergedRef = useMergedRefs(triggerNodeRef, children.ref);
const timeout = useTimeout();
const hoverStateRef = useRef('');
const [show, setShow] = useUncontrolledProp(propsShow, defaultShow, onToggle);
const delay = normalizeDelay(propsDelay);
const {
onFocus,
onBlur,
onClick
} = typeof children !== 'function' ? React.Children.only(children).props : {};
const attachRef = r => {
mergedRef(safeFindDOMNode(r));
};
const handleShow = useCallback(() => {
timeout.clear();
hoverStateRef.current = 'show';
if (!delay.show) {
setShow(true);
return;
}
timeout.set(() => {
if (hoverStateRef.current === 'show') setShow(true);
}, delay.show);
}, [delay.show, setShow, timeout]);
const handleHide = useCallback(() => {
timeout.clear();
hoverStateRef.current = 'hide';
if (!delay.hide) {
setShow(false);
return;
}
timeout.set(() => {
if (hoverStateRef.current === 'hide') setShow(false);
}, delay.hide);
}, [delay.hide, setShow, timeout]);
const handleFocus = useCallback((...args) => {
handleShow();
onFocus == null ? void 0 : onFocus(...args);
}, [handleShow, onFocus]);
const handleBlur = useCallback((...args) => {
handleHide();
onBlur == null ? void 0 : onBlur(...args);
}, [handleHide, onBlur]);
const handleClick = useCallback((...args) => {
setShow(!show);
onClick == null ? void 0 : onClick(...args);
}, [onClick, setShow, show]);
const handleMouseOver = useCallback((...args) => {
handleMouseOverOut(handleShow, args, 'fromElement');
}, [handleShow]);
const handleMouseOut = useCallback((...args) => {
handleMouseOverOut(handleHide, args, 'toElement');
}, [handleHide]);
const triggers = trigger == null ? [] : [].concat(trigger);
const triggerProps = {
ref: attachRef
};
if (triggers.indexOf('click') !== -1) {
triggerProps.onClick = handleClick;
}
if (triggers.indexOf('focus') !== -1) {
triggerProps.onFocus = handleFocus;
triggerProps.onBlur = handleBlur;
}
if (triggers.indexOf('hover') !== -1) {
process.env.NODE_ENV !== "production" ? warning(triggers.length > 1, '[react-bootstrap] Specifying only the `"hover"` trigger limits the visibility of the overlay to just mouse users. Consider also including the `"focus"` trigger so that touch and keyboard only users can see the overlay as well.') : void 0;
triggerProps.onMouseOver = handleMouseOver;
triggerProps.onMouseOut = handleMouseOut;
}
return /*#__PURE__*/_jsxs(_Fragment, {
children: [typeof children === 'function' ? children(triggerProps) : /*#__PURE__*/cloneElement(children, triggerProps), /*#__PURE__*/_jsx(Overlay, { ...props,
show: show,
onHide: handleHide,
flip: flip,
placement: placement,
popperConfig: popperConfig,
target: triggerNodeRef.current,
children: overlay
})]
});
}
OverlayTrigger.defaultProps = defaultProps;
export default OverlayTrigger;