UNPKG

@lobehub/ui

Version:

Lobe UI is an open-source UI component library for building AIGC web apps

169 lines (166 loc) 5.7 kB
'use client'; import { useNativeButton } from "../hooks/useNativeButton.mjs"; import { useIsClient } from "../hooks/useIsClient.mjs"; import { placementMap } from "../utils/placement.mjs"; import { TooltipArrowIcon } from "./ArrowIcon.mjs"; import TooltipContent_default from "./TooltipContent.mjs"; import { useTooltipPortalContainer } from "./TooltipPortal.mjs"; import { styles } from "./style.mjs"; import { cloneElement, isValidElement, memo, useCallback, useMemo, useState } from "react"; import { jsx, jsxs } from "react/jsx-runtime"; import { cx } from "antd-style"; import { mergeProps } from "@base-ui/react/merge-props"; import { Tooltip } from "@base-ui/react/tooltip"; import { mergeRefs } from "react-merge-refs"; //#region src/Tooltip/TooltipStandalone.tsx const DEFAULT_OPEN_DELAY = 400; const DEFAULT_CLOSE_DELAY = 100; /** * Tooltip component - displays small contextual hints on hover/focus * Compatible with Ant Design Tooltip-like API (subset) */ const TooltipStandalone = memo(({ children, title, arrow = true, className, classNames, closeDelay, defaultOpen = false, disabled = false, getPopupContainer, hotkey, hotkeyProps, mouseEnterDelay, mouseLeaveDelay, onOpenChange, open, openDelay, placement = "top", portalled = true, styles: styleProps, zIndex, ref: refProp }) => { const isClient = useIsClient(); const [uncontrolledOpen, setUncontrolledOpen] = useState(Boolean(defaultOpen)); const resolvedOpen = disabled ? false : open ?? uncontrolledOpen; const handleOpenChange = useCallback((nextOpen) => { if (disabled && nextOpen) return; onOpenChange?.(nextOpen); if (open === void 0) setUncontrolledOpen(nextOpen); }, [ disabled, onOpenChange, open ]); const resolvedOpenDelay = useMemo(() => { if (openDelay !== void 0) return openDelay; if (mouseEnterDelay !== void 0) return mouseEnterDelay * 1e3; return DEFAULT_OPEN_DELAY; }, [mouseEnterDelay, openDelay]); const resolvedCloseDelay = useMemo(() => { if (closeDelay !== void 0) return closeDelay; if (mouseLeaveDelay !== void 0) return mouseLeaveDelay * 1e3; return DEFAULT_CLOSE_DELAY; }, [closeDelay, mouseLeaveDelay]); const placementConfig = placementMap[placement] ?? placementMap.top; const baseSideOffset = arrow ? 8 : 6; const portalContainer = useTooltipPortalContainer(); const { isNativeButtonTriggerElement } = useNativeButton({ children }); const resolvedClassNames = useMemo(() => ({ arrow: cx(styles.arrow, classNames?.arrow), popup: cx(styles.popup, className, classNames?.root, classNames?.container), positioner: styles.positioner, viewport: cx(styles.viewport, classNames?.content) }), [ className, classNames?.arrow, classNames?.container, classNames?.content, classNames?.root ]); const resolvedStyleProps = useMemo(() => { if (typeof styleProps === "function") return void 0; return styleProps; }, [styleProps]); const resolvedStyles = useMemo(() => ({ arrow: resolvedStyleProps?.arrow, popup: { ...resolvedStyleProps?.root, ...resolvedStyleProps?.container }, positioner: { zIndex: zIndex ?? 1100 }, viewport: resolvedStyleProps?.content }), [resolvedStyleProps, zIndex]); const triggerElement = useMemo(() => { const triggerProps = { closeDelay: resolvedCloseDelay, delay: resolvedOpenDelay, disabled }; if (isValidElement(children)) return /* @__PURE__ */ jsx(Tooltip.Trigger, { ...triggerProps, render: (props) => { const resolvedProps = (() => { if (isNativeButtonTriggerElement) return props; const { type, ref: triggerRef, ...restProps } = props; return restProps; })(); return cloneElement(children, { ...mergeProps(children.props, resolvedProps), ref: mergeRefs([ children.ref, props.ref, refProp ]) }); } }); return /* @__PURE__ */ jsx(Tooltip.Trigger, { ...triggerProps, ref: refProp, children }); }, [ children, disabled, isNativeButtonTriggerElement, refProp, resolvedCloseDelay, resolvedOpenDelay ]); const customContainer = useMemo(() => { if (!getPopupContainer || !isClient) return void 0; }, [getPopupContainer, isClient]); const popup = useMemo(() => /* @__PURE__ */ jsx(Tooltip.Positioner, { align: placementConfig.align, className: resolvedClassNames.positioner, "data-placement": placement, side: placementConfig.side, sideOffset: baseSideOffset, style: resolvedStyles.positioner, children: /* @__PURE__ */ jsxs(Tooltip.Popup, { className: resolvedClassNames.popup, style: resolvedStyles.popup, children: [arrow && /* @__PURE__ */ jsx(Tooltip.Arrow, { className: resolvedClassNames.arrow, style: resolvedStyles.arrow, children: TooltipArrowIcon }), /* @__PURE__ */ jsx("div", { className: resolvedClassNames.viewport, style: resolvedStyles.viewport, children: /* @__PURE__ */ jsx(TooltipContent_default, { hotkey, hotkeyProps, title }) })] }) }), [ arrow, baseSideOffset, hotkey, hotkeyProps, placement, placementConfig.align, placementConfig.side, resolvedClassNames, resolvedStyles, title ]); if (title == null && !hotkey) return children; const resolvedPortalContainer = customContainer ?? portalContainer; return /* @__PURE__ */ jsxs(Tooltip.Root, { defaultOpen, disabled, onOpenChange: handleOpenChange, open: resolvedOpen, children: [triggerElement, portalled ? resolvedPortalContainer ? /* @__PURE__ */ jsx(Tooltip.Portal, { container: resolvedPortalContainer, children: popup }) : null : popup] }); }); TooltipStandalone.displayName = "TooltipStandalone"; //#endregion export { TooltipStandalone }; //# sourceMappingURL=TooltipStandalone.mjs.map