@wonderflow/react-components
Version:
UI components from Wonderflow's Wanda design system
113 lines (112 loc) • 4.79 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import tkns from '@wonderflow/tokens/platforms/web/tokens.json';
import { useFocusWithin, useKeyPress } from 'ahooks';
import clsx from 'clsx';
import { domMax, LazyMotion, m, } from 'framer-motion';
import { Children, cloneElement, forwardRef, isValidElement, useEffect, useMemo, useRef, useState, } from 'react';
import { createPortal } from 'react-dom';
import { mergeRefs } from 'react-merge-refs';
import { usePopperTooltip } from 'react-popper-tooltip';
import { useUIDSeed } from 'react-uid';
import { isBrowser } from '../../utils/browser';
import * as styles from './popover.module.css';
const cssEasingToArray = (cssEasing) => {
const [x1, y1, x2, y2] = cssEasing.replace(/[^0-9.,]+/g, '').split(',').map(i => parseFloat(i));
return [x1, y1, x2, y2];
};
const PopoverAnimation = {
visible: {
y: 0,
opacity: 1,
transition: {
ease: cssEasingToArray(tkns.easing.entrance),
duration: 0.1,
},
},
hidden: {
y: -5,
opacity: 0,
transition: {
ease: cssEasingToArray(tkns.easing.exit),
duration: 0.1,
},
},
};
export const Popover = forwardRef(({ children, trigger, offset = 8, placement = 'auto-start', open, disabled, closeOnOutsideClick = true, closeOnInsideClick = false, className, matchTriggerWidth, onOpenChange, root, ...otherProps }, forwardedRef) => {
const seedID = useUIDSeed();
const [isOpen, setIsOpen] = useState(false);
const popoverContainerRef = useRef(null);
const sameWidth = useMemo(() => ({
name: 'sameWidth',
enabled: true,
phase: 'beforeWrite',
requires: ['computeStyles'],
fn: ({ state }) => {
const s = state;
if (matchTriggerWidth)
s.styles.popper.width = `${s.rects.reference.width}px`;
},
effect: ({ state }) => {
const s = state;
const referenceElement = s.elements.reference;
if (matchTriggerWidth)
s.elements.popper.style.width = `${referenceElement.offsetWidth ?? 0}px`;
},
}), [matchTriggerWidth]);
const handleCloseOnClickInside = (e) => {
e.stopPropagation();
if (isOpen && closeOnInsideClick) {
setIsOpen(false);
}
};
const { getTooltipProps, setTooltipRef, setTriggerRef,
// eslint-disable-next-line @typescript-eslint/naming-convention
visible, } = usePopperTooltip({
delayShow: 0,
delayHide: 0,
trigger: !disabled ? ['click'] : null,
visible: isOpen,
closeOnTriggerHidden: true,
closeOnOutsideClick,
onVisibleChange: (state) => {
onOpenChange?.(state);
setIsOpen(state);
},
}, {
placement,
modifiers: [
sameWidth,
{
name: 'offset',
options: {
offset: [0, offset],
},
},
],
});
const isFocusWithin = useFocusWithin(popoverContainerRef, {
onBlur: (e) => {
if (e.relatedTarget && visible) {
setIsOpen(true);
}
},
});
useKeyPress('esc', () => setIsOpen(false));
useEffect(() => {
if (open !== undefined)
setIsOpen(open);
}, [open]);
return (_jsxs("div", { ref: mergeRefs([popoverContainerRef, forwardedRef]), className: clsx(styles.Popover, className), "data-popover-has-focus": isFocusWithin, children: [isValidElement(trigger) && cloneElement(trigger, {
ref: setTriggerRef,
id: seedID('popover-trigger'),
key: seedID('popover-trigger'),
'aria-haspopup': 'true',
'aria-controls': seedID('popover-dialog'),
'aria-expanded': isOpen,
...otherProps,
}), isBrowser() && visible && createPortal(_jsx("div", { "aria-hidden": true, "data-testid": "tooltip", ref: setTooltipRef, role: "tooltip", id: seedID('tooltip-content'), ...getTooltipProps({ className: styles.PopUp }), onClick: e => handleCloseOnClickInside(e), children: _jsx(LazyMotion, { features: domMax, strict: true, children: _jsx(m.div, { variants: PopoverAnimation, initial: "hidden", animate: "visible", exit: "hidden", children: _jsx("div", { children: Children.map(children, child => isValidElement(child) && cloneElement(child, {
id: seedID('popover-dialog'),
'aria-labelledby': seedID('popover-trigger'),
})) }) }) }) }, seedID('tooltip-content')), root ?? document.body)] }));
});
Popover.displayName = 'Popover';