UNPKG

@vectara/vectara-ui

Version:

Vectara's design system, codified as a React and Sass component library

77 lines (76 loc) 3.72 kB
var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; import { useState, cloneElement } from "react"; import { Tooltip } from "react-tooltip"; import { useVuiContext } from "../context/Context"; import { VuiPortal } from "../portal/Portal"; import { VuiText } from "../typography/Text"; import { VuiTextColor } from "../typography/TextColor"; const generateTooltipId = () => { return `tooltip-${Math.random().toString(36).slice(2, 9)}`; }; // Naturally focusable elements that don't need tabIndex. const FOCUSABLE_ELEMENTS = ["a", "button", "input", "select", "textarea"]; // Determine if the element needs tabIndex to be keyboard accessible. const needsTabIndex = (element) => { var _a; if (!element || typeof element !== "object" || !("type" in element)) { return false; } const child = element; const elementType = typeof child.type === "string" ? child.type.toLowerCase() : ""; // Don't add tabIndex if element is naturally focusable. if (FOCUSABLE_ELEMENTS.includes(elementType)) { return false; } // Don't add tabIndex if it already has one. if (((_a = child.props) === null || _a === void 0 ? void 0 : _a.tabIndex) !== undefined) { return false; } return true; }; export const VuiTooltip = (_a) => { var { children, darkTheme, position, tip, usePortal } = _a, rest = __rest(_a, ["children", "darkTheme", "position", "tip", "usePortal"]); const { getThemeStyle } = useVuiContext(); const [tooltipId] = useState(generateTooltipId()); const target = cloneElement(children, Object.assign(Object.assign({ "data-tooltip-id": tooltipId }, (needsTabIndex(children) && { tabIndex: 0 })), rest)); // Tooltips can be used in a dark-themed component, so we need to explicitly set // the light theme class in order to enable having a different theme than the // parent. const style = getThemeStyle(darkTheme ? "dark" : "light"); const content = typeof tip === "string" ? (_jsx(VuiText, Object.assign({ size: "xs" }, { children: _jsx("p", { children: _jsx(VuiTextColor, Object.assign({ color: "empty" }, { children: tip })) }) }))) : (tip); const tooltip = (_jsx(Tooltip, Object.assign({ id: tooltipId, offset: 10, className: "vuiTooltip", style: style, opacity: 1, place: position }, { children: content }))); return (_jsxs(_Fragment, { children: [target, usePortal ? _jsx(VuiPortal, { children: tooltip }) : tooltip] })); }; // This is a workaround for the issue with ResizeObserver in ReactTooltip. // Without this, uncaught runtime errors are thrown: "ResizeObserver loop // completed with undelivered notifications." // See https://github.com/ReactTooltip/react-tooltip/issues/1104 const debounce = (callback, delay) => { let tid; return function (...args) { // eslint-disable-next-line no-restricted-globals const ctx = self; tid && clearTimeout(tid); tid = setTimeout(() => { callback.apply(ctx, args); }, delay); }; }; const _ = window.ResizeObserver; window.ResizeObserver = class ResizeObserver extends _ { constructor(callback) { callback = debounce(callback, 20); super(callback); } };