UNPKG

@carbon/react

Version:

React components for the Carbon Design System

297 lines (287 loc) 9.34 kB
/** * Copyright IBM Corp. 2016, 2023 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ 'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js'); var cx = require('classnames'); var PropTypes = require('prop-types'); var React = require('react'); var index = require('../Popover/index.js'); var keys = require('../../internal/keyboard/keys.js'); var match = require('../../internal/keyboard/match.js'); var useEvent = require('../../internal/useEvent.js'); var useId = require('../../internal/useId.js'); var usePrefix = require('../../internal/usePrefix.js'); /** * Used to render the label for a Toggletip */ function ToggletipLabel({ as: BaseComponent = 'span', children, className: customClassName, ...rest }) { const prefix = usePrefix.usePrefix(); const className = cx(`${prefix}--toggletip-label`, customClassName); // eslint-disable-next-line @typescript-eslint/no-explicit-any -- https://github.com/carbon-design-system/carbon/issues/20452 const BaseComponentAsAny = BaseComponent; return /*#__PURE__*/React.createElement(BaseComponentAsAny, _rollupPluginBabelHelpers.extends({ className: className }, rest), children); } ToggletipLabel.propTypes = { /** * Provide a custom element or component to render the top-level node for the * component. */ as: PropTypes.elementType, /** * Custom children to be rendered as the content of the label */ children: PropTypes.node, /** * Provide a custom class name to be added to the outermost node in the * component */ className: PropTypes.string }; // Used to coordinate accessibility props between button and content along with // the actions to open and close the toggletip const ToggletipContext = /*#__PURE__*/React.createContext(undefined); function useToggletip() { return React.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 = React.useRef(null); const [open, setOpen] = React.useState(defaultOpen); const prefix = usePrefix.usePrefix(); const id = useId.useId(); const className = cx(`${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 : undefined, onClick: actions.toggle }, contentProps: { id }, onClick: { onClick: actions.toggle } }; const onKeyDown = event => { if (open && match.match(event, keys.Escape)) { event.stopPropagation(); actions.close(); // If the menu is closed while focus is still inside the menu, it should return to the trigger button (#12922) const button = ref.current?.children[0]; if (button instanceof HTMLButtonElement) { button.focus(); } } }; const handleBlur = event => { // Do not close if the menu itself is clicked, should only close on focus out if (open && event.relatedTarget === null) { return; } if (!event.currentTarget.contains(event.relatedTarget)) { // The menu should be closed when focus leaves the `Toggletip` (#12922) actions.close(); } }; // If the `Toggletip` is the last focusable item in the tab order, it should also close when the browser window loses focus (#12922) useEvent.useWindowEvent('blur', () => { if (open) { actions.close(); } }); React.useEffect(() => { if (!ref.current) return; const targetDocument = ref.current.ownerDocument || document; const eventType = 'PointerEvent' in window ? 'pointerdown' : 'mousedown'; const handleOutsideClick = event => { const node = event.target; // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- https://github.com/carbon-design-system/carbon/issues/20452 if (open && node && !ref.current.contains(node)) { setOpen(false); } }; const options = { capture: true }; targetDocument.addEventListener(eventType, handleOutsideClick, options); return () => { targetDocument.removeEventListener(eventType, handleOutsideClick, options); }; }, [open]); return /*#__PURE__*/React.createElement(ToggletipContext.Provider, { value: value }, /*#__PURE__*/React.createElement(index.Popover, _rollupPluginBabelHelpers.extends({ align: align, as: as, caret: true, className: className, dropShadow: false, highContrast: true, open: open, onKeyDown: onKeyDown, onBlur: handleBlur, ref: ref, autoAlign: autoAlign }, rest), children)); } // Get all the properties from Popover except for "open". // The Typescript types for PropTypes are really messed up so we need lots of // casting. It will be great when we can finally get rid of PropTypes altogether. // eslint-disable-next-line @typescript-eslint/no-unused-vars -- https://github.com/carbon-design-system/carbon/issues/20452 const { open, ...popoverNonOpenPropTypes } = index.Popover.propTypes ?? {}; Toggletip.propTypes = { // Has all of Popover's PropTypes except for "open". ...popoverNonOpenPropTypes, /** * Specify if the toggletip should be open by default */ defaultOpen: PropTypes.bool }; /** * `ToggletipButton` controls the visibility of the Toggletip through mouse * clicks and keyboard interactions. */ const ToggletipButton = /*#__PURE__*/React.forwardRef(function ToggletipButton({ children, className: customClassName, label = 'Show information', as: BaseComponent, ...rest }, ref) { const toggletip = useToggletip(); const prefix = usePrefix.usePrefix(); const className = cx(`${prefix}--toggletip-button`, customClassName); // eslint-disable-next-line @typescript-eslint/no-explicit-any -- https://github.com/carbon-design-system/carbon/issues/20452 const ComponentToggle = BaseComponent ?? 'button'; if (ComponentToggle !== 'button') { return /*#__PURE__*/React.createElement(ComponentToggle, _rollupPluginBabelHelpers.extends({}, toggletip?.onClick, { className: className }, rest), children); } return /*#__PURE__*/React.createElement("button", _rollupPluginBabelHelpers.extends({}, toggletip?.buttonProps, { "aria-label": label, type: "button", className: className, ref: ref }, rest), children); }); ToggletipButton.propTypes = { /** * Custom children to be rendered as the content of the label */ children: PropTypes.node, /** * Provide a custom class name to be added to the outermost node in the * component */ className: PropTypes.string, /** * Provide an accessible label for this button */ label: PropTypes.string }; ToggletipButton.displayName = 'ToggletipButton'; const frFn = React.forwardRef; /** * `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 = frFn((props, ref) => { const { children, className: customClassName } = props; const toggletip = useToggletip(); const prefix = usePrefix.usePrefix(); return /*#__PURE__*/React.createElement(index.PopoverContent, _rollupPluginBabelHelpers.extends({ className: customClassName }, toggletip?.contentProps, { ref: ref }), /*#__PURE__*/React.createElement("div", { className: `${prefix}--toggletip-content` }, children)); }); ToggletipContent.propTypes = { /** * Custom children to be rendered as the content of the label */ children: PropTypes.node, /** * Provide a custom class name to be added to the outermost node in the * component */ 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 }) { const prefix = usePrefix.usePrefix(); const className = cx(`${prefix}--toggletip-actions`, customClassName); return /*#__PURE__*/React.createElement("div", { className: className }, children); } ToggletipActions.propTypes = { /** * Custom children to be rendered as the content of the label */ children: PropTypes.node, /** * Provide a custom class name to be added to the outermost node in the * component */ className: PropTypes.string }; exports.Toggletip = Toggletip; exports.ToggletipActions = ToggletipActions; exports.ToggletipButton = ToggletipButton; exports.ToggletipContent = ToggletipContent; exports.ToggletipLabel = ToggletipLabel; exports.default = Toggletip;