UNPKG

@carbon/ibm-products

Version:

Carbon for IBM Products

210 lines (201 loc) 7.65 kB
/** * Copyright IBM Corp. 2020, 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. */ 'use strict'; var _rollupPluginBabelHelpers = require('../../_virtual/_rollupPluginBabelHelpers.js'); var React = require('react'); var pconsole = require('../../global/js/utils/pconsole.js'); var index = require('../../_virtual/index.js'); var cx = require('classnames'); var icons = require('@carbon/react/icons'); var react = require('@carbon/react'); var devtools = require('../../global/js/utils/devtools.js'); var settings = require('../../settings.js'); var reactDom = require('react-dom'); var CoachmarkHeader = require('../Coachmark/CoachmarkHeader.js'); var useIsomorphicEffect = require('../../global/js/hooks/useIsomorphicEffect.js'); // Carbon and package components we use. /* TODO: @import(s) of carbon components and other package components. */ // The block part of our conventional BEM class names (blockClass__E--M). const blockClass = `${settings.pkg.prefix}--coachmark-stacked-home`; const overlayClass = `${settings.pkg.prefix}--coachmark-overlay`; const componentName = 'CoachmarkStackHome'; /** * DO NOT USE. This component is for the exclusive use * of other Onboarding components. */ exports.CoachmarkStackHome = /*#__PURE__*/React.forwardRef((_ref, ref) => { let { className, description, isOpen, renderMedia, navLinkLabels, onClickNavItem, onClose, portalTarget, closeButtonLabel, title, tooltipAlign, ...rest } = _ref; const buttonFocusRef = React.useRef(null); const [linkFocusIndex, setLinkFocusIndex] = React.useState(0); const navItemRefs = React.useRef([]); const [overflowStates, setOverflowStates] = React.useState([]); React.useEffect(() => { setTimeout(() => { if (isOpen && buttonFocusRef.current) { buttonFocusRef.current.focus(); } }, 100); }, [isOpen]); const portalNode = React.useRef(null); useIsomorphicEffect.useIsomorphicEffect(() => { portalNode.current = portalTarget ? document?.querySelector(portalTarget) ?? document?.querySelector('body') : document?.querySelector('body'); }, [portalTarget]); if (!navLinkLabels) { return pconsole.default.warn(`${componentName} is an Onboarding internal component and is not intended for general use.`); } const itemRefHandler = (index, node) => { if (node && navItemRefs.current[index] !== node) { const isOverflowing = node.scrollWidth > node.clientWidth; navItemRefs.current[index] = node; setOverflowStates(prev => { const newState = [...prev]; newState[index] = isOverflowing; return newState; }); } }; function renderNavLink(index, label) { let ref = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; const isOverflowing = overflowStates[index] ?? false; return /*#__PURE__*/React.createElement("li", { key: index, ref: node => { itemRefHandler(index, node); } }, /*#__PURE__*/React.createElement(react.Button, { kind: "ghost", size: "sm", onClick: () => { setLinkFocusIndex(index); onClickNavItem(index + 1); }, ref: ref }, isOverflowing ? /*#__PURE__*/React.createElement(react.Tooltip, { highContrast: false, label: label, align: tooltipAlign, className: `${blockClass}__navLinkLabels-tooltip` }, /*#__PURE__*/React.createElement("span", { className: `${blockClass}__navLinkLabels-text` }, label)) : label)); } return portalNode?.current ? /*#__PURE__*/reactDom.createPortal(/*#__PURE__*/React.createElement("div", _rollupPluginBabelHelpers.extends({}, rest, { className: cx(blockClass, className), ref: ref }, devtools.getDevtoolsProps(componentName)), /*#__PURE__*/React.createElement(CoachmarkHeader.CoachmarkHeader, { onClose: () => { setLinkFocusIndex(0); onClose(); } }), /*#__PURE__*/React.createElement("div", { className: `${overlayClass}__body` }, /*#__PURE__*/React.createElement("div", { className: `${overlayClass}-element` }, !renderMedia && /*#__PURE__*/React.createElement(icons.Idea, { size: 20, className: `${blockClass}__icon-idea` }), renderMedia && /*#__PURE__*/React.createElement("div", { className: `${blockClass}__element-stepped-media` }, renderMedia({ playStep: 0 })), /*#__PURE__*/React.createElement("div", { className: `${overlayClass}-element__content` }, title && /*#__PURE__*/React.createElement("h2", { className: `${overlayClass}-element__title` }, title), description && /*#__PURE__*/React.createElement("p", { className: `${overlayClass}-element__body` }, description)), /*#__PURE__*/React.createElement("ul", { className: `${blockClass}__nav-links` }, navLinkLabels.map((label, index) => { if (index === linkFocusIndex) { return renderNavLink(index, label, buttonFocusRef); } return renderNavLink(index, label); })), closeButtonLabel && /*#__PURE__*/React.createElement("div", { className: `${overlayClass}__footer` }, /*#__PURE__*/React.createElement(react.Button, { size: "sm", onClick: () => { setLinkFocusIndex(0); onClose(); } }, closeButtonLabel))))), portalNode?.current) : null; }); // Return a placeholder if not released and not enabled by feature flag exports.CoachmarkStackHome = settings.pkg.checkComponentEnabled(exports.CoachmarkStackHome, componentName); // The display name of the component, used by React. Note that displayName // is used in preference to relying on function.name. exports.CoachmarkStackHome.displayName = componentName; // The types and DocGen commentary for the component props, // in alphabetical order (for consistency). // See https://www.npmjs.com/package/prop-types#usage. exports.CoachmarkStackHome.propTypes = { /** * Optional class name for this component. */ className: index.default.string, /** * The label for the button that will close the stack */ closeButtonLabel: index.default.string, /** * The description of the Coachmark. */ description: index.default.node.isRequired, /** * If the stack home is open. */ isOpen: index.default.bool.isRequired, /** * The labels used to link to the stackable Coachmarks. */ navLinkLabels: index.default.arrayOf(index.default.string.isRequired).isRequired, /** * For internal use only by CoachmarkStack and CoachmarkStackHome. */ onClickNavItem: index.default.func.isRequired, /** * Function to call when the stack closes. */ onClose: index.default.func.isRequired, /** * By default, the CoachmarkStackHome will be appended to the end of `document.body`. * The CoachmarkStackHome will remain persistent as the user navigates the app until * the user closes the CoachmarkStackHome. * * Alternatively, the app developer can tightly couple the CoachmarkStackHome to a DOM * element or other component by specifying a CSS selector. The CoachmarkStackHome will * remain visible as long as that element remains visible or mounted. When the * element is hidden or component is unmounted, the CoachmarkStackHome will disappear. */ portalTarget: index.default.string, /** * Optional prop to render any media like images or animated media. */ renderMedia: index.default.func, /** * The title of the Coachmark. */ title: index.default.string.isRequired, /** * Label's tooltip position */ tooltipAlign: index.default.oneOf(['top', 'bottom']) };