@carbon/react
Version:
React components for the Carbon Design System
201 lines (199 loc) • 6.32 kB
JavaScript
/**
* 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.
*/
import { usePrefix } from "../../internal/usePrefix.js";
import { Escape } from "../../internal/keyboard/keys.js";
import { match } from "../../internal/keyboard/match.js";
import { useId } from "../../internal/useId.js";
import { useWindowEvent } from "../../internal/useEvent.js";
import { Popover, PopoverContent } from "../Popover/index.js";
import classNames from "classnames";
import React, { forwardRef, useContext, useEffect, useRef, useState } from "react";
import PropTypes from "prop-types";
import { jsx } from "react/jsx-runtime";
//#region src/components/Toggletip/index.tsx
/**
* 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.
*/
/**
* Used to render the label for a Toggletip
*/
function ToggletipLabel({ as: BaseComponent = "span", children, className: customClassName, ...rest }) {
return /* @__PURE__ */ jsx(BaseComponent, {
className: classNames(`${usePrefix()}--toggletip-label`, customClassName),
...rest,
children
});
}
ToggletipLabel.propTypes = {
as: PropTypes.elementType,
children: PropTypes.node,
className: PropTypes.string
};
const ToggletipContext = React.createContext(void 0);
function useToggletip() {
return useContext(ToggletipContext);
}
/**
* Used as a container for the button and content of a toggletip. This component
* is responsible for coordinating between interactions with the button and the
* visibility of the content
*/
function Toggletip({ align, as, autoAlign, className: customClassName, children, defaultOpen = false, ...rest }) {
const ref = useRef(null);
const [open, setOpen] = useState(defaultOpen);
const prefix = usePrefix();
const id = useId();
const className = classNames(`${prefix}--toggletip`, customClassName, {
[`${prefix}--toggletip--open`]: open,
[`${prefix}--autoalign`]: autoAlign
});
const actions = {
toggle: () => {
setOpen(!open);
},
close: () => {
setOpen(false);
}
};
const value = {
buttonProps: {
"aria-expanded": open,
"aria-controls": id,
"aria-describedby": open ? id : void 0,
onClick: actions.toggle
},
contentProps: { id },
onClick: { onClick: actions.toggle }
};
const onKeyDown = (event) => {
if (open && match(event, Escape)) {
event.stopPropagation();
actions.close();
const button = ref.current?.children[0];
if (button instanceof HTMLButtonElement) button.focus();
}
};
const handleBlur = (event) => {
if (open && event.relatedTarget === null) return;
if (!event.currentTarget.contains(event.relatedTarget)) actions.close();
};
useWindowEvent("blur", () => {
if (open) actions.close();
});
useEffect(() => {
if (!open || !ref.current) return;
const targetDocument = ref.current.ownerDocument || document;
const eventType = "PointerEvent" in window ? "pointerdown" : "mousedown";
const handleOutsideClick = (event) => {
const { current } = ref;
if (!current) return;
const isInsideCurrent = (target) => target instanceof Node && current.contains(target);
if (!(isInsideCurrent(event.target) || typeof event.composedPath === "function" && event.composedPath().some(isInsideCurrent))) setOpen(false);
};
const options = { capture: true };
targetDocument.addEventListener(eventType, handleOutsideClick, options);
return () => {
targetDocument.removeEventListener(eventType, handleOutsideClick, options);
};
}, [open]);
return /* @__PURE__ */ jsx(ToggletipContext.Provider, {
value,
children: /* @__PURE__ */ jsx(Popover, {
align,
as,
caret: true,
className,
dropShadow: false,
highContrast: true,
open,
onKeyDown,
onBlur: handleBlur,
ref,
autoAlign,
...rest,
children
})
});
}
const { open, ...popoverNonOpenPropTypes } = Popover.propTypes ?? {};
Toggletip.propTypes = {
...popoverNonOpenPropTypes,
defaultOpen: PropTypes.bool
};
/**
* `ToggletipButton` controls the visibility of the Toggletip through mouse
* clicks and keyboard interactions.
*/
const ToggletipButton = forwardRef(function ToggletipButton({ children, className: customClassName, label = "Show information", as: BaseComponent, ...rest }, ref) {
const toggletip = useToggletip();
const className = classNames(`${usePrefix()}--toggletip-button`, customClassName);
const ComponentToggle = BaseComponent ?? "button";
if (ComponentToggle !== "button") return /* @__PURE__ */ jsx(ComponentToggle, {
...toggletip?.onClick,
className,
...rest,
children
});
return /* @__PURE__ */ jsx("button", {
...toggletip?.buttonProps,
"aria-label": label,
type: "button",
className,
ref,
...rest,
children
});
});
ToggletipButton.propTypes = {
children: PropTypes.node,
className: PropTypes.string,
label: PropTypes.string
};
ToggletipButton.displayName = "ToggletipButton";
/**
* `ToggletipContent` is a wrapper around `PopoverContent`. It places the
* `children` passed in as a prop inside of `PopoverContent` so that they will
* be rendered inside of the popover for this component.
*/
const ToggletipContent = forwardRef((props, ref) => {
const { children, className: customClassName } = props;
const toggletip = useToggletip();
const prefix = usePrefix();
return /* @__PURE__ */ jsx(PopoverContent, {
className: customClassName,
...toggletip?.contentProps,
ref,
children: /* @__PURE__ */ jsx("div", {
className: `${prefix}--toggletip-content`,
children
})
});
});
ToggletipContent.propTypes = {
children: PropTypes.node,
className: PropTypes.string
};
ToggletipContent.displayName = "ToggletipContent";
/**
* `ToggletipActions` is a container for one or two actions present at the base
* of a toggletip. It is used for layout of these items.
*/
function ToggletipActions({ children, className: customClassName }) {
return /* @__PURE__ */ jsx("div", {
className: classNames(`${usePrefix()}--toggletip-actions`, customClassName),
children
});
}
ToggletipActions.propTypes = {
children: PropTypes.node,
className: PropTypes.string
};
//#endregion
export { ToggletipActions, ToggletipButton, ToggletipContent, ToggletipLabel, Toggletip as default };