UNPKG

@carbon/react

Version:

React components for the Carbon Design System

201 lines (199 loc) 6.67 kB
/** * Copyright IBM Corp. 2016, 2026 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ const require_runtime = require("../../_virtual/_rolldown/runtime.js"); const require_usePrefix = require("../../internal/usePrefix.js"); const require_keys = require("../../internal/keyboard/keys.js"); const require_match = require("../../internal/keyboard/match.js"); const require_useIsomorphicEffect = require("../../internal/useIsomorphicEffect.js"); const require_useId = require("../../internal/useId.js"); const require_index = require("../Popover/index.js"); const require_useDelayedState = require("../../internal/useDelayedState.js"); const require_useNoInteractiveChildren = require("../../internal/useNoInteractiveChildren.js"); let classnames = require("classnames"); classnames = require_runtime.__toESM(classnames); let react = require("react"); react = require_runtime.__toESM(react); let prop_types = require("prop-types"); prop_types = require_runtime.__toESM(prop_types); let react_jsx_runtime = require("react/jsx-runtime"); //#region src/components/Tooltip/Tooltip.tsx /** * Copyright IBM Corp. 2016, 2025 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ /** * Event types that trigger a "drag" to stop. */ const DRAG_STOP_EVENT_TYPES = new Set([ "mouseup", "touchend", "touchcancel" ]); const Tooltip = react.default.forwardRef(({ as, align = "top", className: customClassName, children, label, description, enterDelayMs = 100, leaveDelayMs = 300, defaultOpen = false, closeOnActivation = false, dropShadow = false, highContrast = true, ...rest }, ref) => { const tooltipRef = (0, react.useRef)(null); const [open, setOpen] = require_useDelayedState.useDelayedState(defaultOpen); const [isDragging, setIsDragging] = (0, react.useState)(false); const [focusByMouse, setFocusByMouse] = (0, react.useState)(false); const [isPointerIntersecting, setIsPointerIntersecting] = (0, react.useState)(false); const id = require_useId.useId("tooltip"); const prefix = require_usePrefix.usePrefix(); const child = react.default.Children.only(children); const { "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy } = child?.props ?? {}; const hasLabel = !!label; const triggerProps = { onFocus: () => !focusByMouse && setOpen(true), onBlur: () => { setOpen(false); setFocusByMouse(false); }, onClick: () => closeOnActivation && setOpen(false), onMouseEnter, onMouseLeave, onMouseDown, onMouseMove, onTouchStart: onDragStart, "aria-labelledby": ariaLabelledBy ?? (hasLabel ? id : void 0), "aria-describedby": ariaDescribedBy ?? (!hasLabel ? id : void 0) }; function getChildEventHandlers(childProps) { const eventHandlerFunctions = Object.keys(triggerProps).filter((prop) => prop.startsWith("on")); const eventHandlers = {}; eventHandlerFunctions.forEach((functionName) => { eventHandlers[functionName] = (evt) => { triggerProps[functionName](evt); if (childProps?.[functionName]) childProps?.[functionName](evt); }; }); return eventHandlers; } const onKeyDown = (0, react.useCallback)((event) => { if (open && require_match.match(event, require_keys.Escape)) { event.stopPropagation(); setOpen(false); } if (open && closeOnActivation && (require_match.match(event, require_keys.Enter) || require_match.match(event, require_keys.Space))) setOpen(false); }, [ closeOnActivation, open, setOpen ]); require_useIsomorphicEffect.default(() => { if (!open) return; function handleKeyDown(event) { if (require_match.match(event, require_keys.Escape)) onKeyDown(event); } document.addEventListener("keydown", handleKeyDown); return () => { document.removeEventListener("keydown", handleKeyDown); }; }, [open, onKeyDown]); function onMouseEnter() { if (!rest?.onMouseEnter) { setIsPointerIntersecting(true); setOpen(true, enterDelayMs); } } function onMouseDown() { setFocusByMouse(true); onDragStart(); } function onMouseLeave() { setIsPointerIntersecting(false); if (isDragging) return; setOpen(false, leaveDelayMs); } function onMouseMove(evt) { if (evt.buttons === 1) setIsDragging(true); else setIsDragging(false); } function onDragStart() { setIsDragging(true); } const onDragStop = (0, react.useCallback)(() => { setIsDragging(false); if (!isPointerIntersecting) setOpen(false, leaveDelayMs); }, [ isPointerIntersecting, leaveDelayMs, setOpen ]); require_useNoInteractiveChildren.useNoInteractiveChildren(tooltipRef, "The Tooltip component must have no interactive content rendered by the`label` or `description` prop"); (0, react.useEffect)(() => { if (isDragging) DRAG_STOP_EVENT_TYPES.forEach((eventType) => { document.addEventListener(eventType, onDragStop); }); return () => { DRAG_STOP_EVENT_TYPES.forEach((eventType) => { document.removeEventListener(eventType, onDragStop); }); }; }, [isDragging, onDragStop]); return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(require_index.Popover, { as, ref, ...rest, align, className: (0, classnames.default)(`${prefix}--tooltip`, customClassName), dropShadow, highContrast, onKeyDown, onMouseLeave, open, children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: `${prefix}--tooltip-trigger__wrapper`, children: typeof child !== "undefined" ? react.default.cloneElement(child, { ...triggerProps, ...getChildEventHandlers(child.props) }) : null }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_index.PopoverContent, { "aria-hidden": open ? "false" : "true", className: `${prefix}--tooltip-content`, id, onMouseEnter, role: "tooltip", children: label || description })] }); }); Tooltip.propTypes = { align: prop_types.default.oneOf([ "top", "top-left", "top-right", "bottom", "bottom-left", "bottom-right", "left", "left-bottom", "left-top", "right", "right-bottom", "right-top", "top-start", "top-end", "bottom-start", "bottom-end", "left-end", "left-start", "right-end", "right-start" ]), children: prop_types.default.node, className: prop_types.default.string, closeOnActivation: prop_types.default.bool, defaultOpen: prop_types.default.bool, description: prop_types.default.node, dropShadow: prop_types.default.bool, enterDelayMs: prop_types.default.number, highContrast: prop_types.default.bool, label: prop_types.default.node, leaveDelayMs: prop_types.default.number }; //#endregion exports.Tooltip = Tooltip;