UNPKG

@helpwave/hightide

Version:

helpwave's component and theming library

127 lines (124 loc) 3.34 kB
// src/components/user-input/Menu.tsx import { useRef } from "react"; import clsx from "clsx"; // src/hooks/useOutsideClick.ts import { useEffect } from "react"; var useOutsideClick = (refs, handler) => { useEffect(() => { const listener = (event) => { if (event.target === null) return; if (refs.some((ref) => !ref.current || ref.current.contains(event.target))) { return; } handler(); }; document.addEventListener("mousedown", listener); document.addEventListener("touchstart", listener); return () => { document.removeEventListener("mousedown", listener); document.removeEventListener("touchstart", listener); }; }, [refs, handler]); }; // src/hooks/useHoverState.ts import { useEffect as useEffect2, useState } from "react"; var defaultUseHoverStateProps = { closingDelay: 200, isDisabled: false }; var useHoverState = (props = void 0) => { const { closingDelay, isDisabled } = { ...defaultUseHoverStateProps, ...props }; const [isHovered, setIsHovered] = useState(false); const [timer, setTimer] = useState(); const onMouseEnter = () => { if (isDisabled) { return; } clearTimeout(timer); setIsHovered(true); }; const onMouseLeave = () => { if (isDisabled) { return; } setTimer(setTimeout(() => { setIsHovered(false); }, closingDelay)); }; useEffect2(() => { if (timer) { return () => { clearTimeout(timer); }; } }); useEffect2(() => { if (timer) { clearTimeout(timer); } }, [isDisabled]); return { isHovered, setIsHovered, handlers: { onMouseEnter, onMouseLeave } }; }; // src/components/user-input/Menu.tsx import { jsx, jsxs } from "react/jsx-runtime"; var MenuItem = ({ children, onClick, alignment = "left", className }) => /* @__PURE__ */ jsx( "div", { className: clsx("block px-3 py-1 bg-menu-background text-menu-text hover:brightness-90", { "text-right": alignment === "right", "text-left": alignment === "left" }, className), onClick, children } ); var Menu = ({ trigger, children, alignment = "tl", showOnHover = false, menuClassName = "" }) => { const { isHovered: isOpen, setIsHovered: setIsOpen, handlers } = useHoverState({ isDisabled: !showOnHover }); const triggerRef = useRef(null); const menuRef = useRef(null); useOutsideClick([triggerRef, menuRef], () => setIsOpen(false)); return /* @__PURE__ */ jsxs( "div", { className: "relative", ...handlers, children: [ trigger(() => setIsOpen(!isOpen), triggerRef), isOpen ? /* @__PURE__ */ jsx( "div", { ref: menuRef, onClick: (e) => e.stopPropagation(), className: clsx("absolute top-full mt-1 py-2 w-60 rounded-lg bg-menu-background text-menu-text ring-1 ring-slate-900/5 text-sm leading-6 font-semibold shadow-md z-[1]", { " top-[8px]": alignment[0] === "t", " bottom-[8px]": alignment[0] === "b", " left-[-8px]": alignment[1] === "l", " right-[-8px]": alignment[1] === "r" }, menuClassName), children } ) : null ] } ); }; export { Menu, MenuItem }; //# sourceMappingURL=Menu.mjs.map