@carbon/ibm-products
Version:
Carbon for IBM Products
223 lines (214 loc) • 8.43 kB
JavaScript
/**
* 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.
*/
import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
import { Button, IconButton } from '@carbon/react';
import { Idea, CaretLeft, CaretRight, Close } from '@carbon/react/icons';
import React__default, { useRef, useState } from 'react';
import { purple70, blue90 } from '../../node_modules/@carbon/colors/es/index.js';
import { Carousel } from '../Carousel/Carousel.js';
import '../Carousel/CarouselItem.js';
import PropTypes from '../../_virtual/index.js';
import cx from 'classnames';
import { getDevtoolsProps } from '../../global/js/utils/devtools.js';
import { pkg } from '../../settings.js';
import uuidv4 from '../../global/js/utils/uuidv4.js';
var _CaretLeft, _CaretRight, _Close;
// The block part of our conventional BEM class names (blockClass__E--M).
const blockClass = `${pkg.prefix}--guidebanner`;
const componentName = 'Guidebanner';
const defaults = {
collapsible: false,
withLeftGutter: false,
// Labels
closeIconDescription: 'Close',
collapseButtonLabel: 'Read less',
expandButtonLabel: 'Read more',
nextIconDescription: 'Next',
previousIconDescription: 'Back'
};
/**
* The guide banner sits at the top of a page, or page-level tab,
* to introduce foundational concepts related to the page's content.
*/
let Guidebanner = /*#__PURE__*/React__default.forwardRef((_ref, ref) => {
let {
children,
className,
collapsible = defaults.collapsible,
onClose,
withLeftGutter = defaults.withLeftGutter,
// Labels
closeIconDescription = defaults.closeIconDescription,
collapseButtonLabel = defaults.collapseButtonLabel,
expandButtonLabel = defaults.expandButtonLabel,
nextIconDescription = defaults.nextIconDescription,
previousIconDescription = defaults.previousIconDescription,
title,
...rest
} = _ref;
const scrollRef = useRef(null);
const toggleRef = useRef(null);
const [scrollPosition, setScrollPosition] = useState(0);
const [showNavigation, setShowNavigation] = useState(false);
const [isCollapsed, setIsCollapsed] = useState(collapsible ? true : false);
const handleClickToggle = () => {
setIsCollapsed(prevState => !prevState);
};
const carouselContentId = `${uuidv4()}--carousel-content-id`;
return /*#__PURE__*/React__default.createElement("div", _extends({}, rest, {
"aria-owns": !isCollapsed ? carouselContentId : undefined,
className: cx(blockClass, className, collapsible && `${blockClass}__collapsible`, isCollapsed && `${blockClass}__collapsible-collapsed`, withLeftGutter && `${blockClass}__with-left-gutter`),
ref: ref
}, getDevtoolsProps(componentName)), /*#__PURE__*/React__default.createElement(Idea, {
size: 20,
className: `${blockClass}__icon-idea`
}), /*#__PURE__*/React__default.createElement("div", {
className: `${blockClass}__title`
}, title), /*#__PURE__*/React__default.createElement(Carousel, {
id: carouselContentId,
className: `${blockClass}__carousel`
// These colors are to match the Carousel's faded edges
// against the Guidebanner's gradient background.
,
fadedEdgeColor: {
left: blue90,
right: purple70
},
ref: scrollRef,
onChangeIsScrollable: value => {
setShowNavigation(value);
},
onScroll: scrollPercent => {
setScrollPosition(scrollPercent);
},
isScrollMode: true
}, children), /*#__PURE__*/React__default.createElement("div", {
className: cx([collapsible || showNavigation ? `${blockClass}__navigation` : null])
}, collapsible && /*#__PURE__*/React__default.createElement(Button, {
kind: "ghost",
size: "md",
className: `${blockClass}__toggle-button`,
onClick: handleClickToggle,
ref: toggleRef,
"aria-controls": !isCollapsed ? carouselContentId : undefined,
"aria-expanded": !isCollapsed
}, isCollapsed ? expandButtonLabel : collapseButtonLabel), showNavigation && /*#__PURE__*/React__default.createElement(React__default.Fragment, null, /*#__PURE__*/React__default.createElement("span", {
className: cx(`${blockClass}__back-button`, [scrollPosition === 0 ? `${blockClass}__back-button--disabled` : null])
}, /*#__PURE__*/React__default.createElement(IconButton, {
align: "top",
disabled: scrollPosition === 0,
kind: "ghost",
label: previousIconDescription,
onClick: () => {
scrollRef.current.scrollPrev();
},
size: "md"
}, _CaretLeft || (_CaretLeft = /*#__PURE__*/React__default.createElement(CaretLeft, {
size: 16
})))), /*#__PURE__*/React__default.createElement("span", {
className: cx(`${blockClass}__next-button`, [scrollPosition === 1 ? `${blockClass}__next-button--disabled` : null])
}, /*#__PURE__*/React__default.createElement(IconButton, {
align: "top-right",
disabled: scrollPosition === 1,
kind: "ghost",
label: nextIconDescription,
onClick: () => {
scrollRef.current.scrollNext();
},
size: "md"
}, _CaretRight || (_CaretRight = /*#__PURE__*/React__default.createElement(CaretRight, {
size: 16
})))))), onClose && /*#__PURE__*/React__default.createElement("span", {
className: `${blockClass}__close-button`
}, /*#__PURE__*/React__default.createElement(IconButton, {
align: "bottom-end",
kind: "ghost",
label: closeIconDescription,
onClick: onClose,
size: "md"
}, _Close || (_Close = /*#__PURE__*/React__default.createElement(Close, {
size: 16
})))));
});
// Return a placeholder if not released and not enabled by feature flag
Guidebanner = pkg.checkComponentEnabled(Guidebanner, componentName);
// The display name of the component, used by React. Note that displayName
// is used in preference to relying on function.name.
Guidebanner.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.
Guidebanner.propTypes = {
/**
* Provide the contents of the Guidebanner.
* One or more GuidebannerElement components are required.
*/
children: (props, propName) => {
let error;
const prop = props[propName];
if (!prop) {
error = new Error('`Guidebanner` requires one or more children of type `GuidebannerElement`.');
}
React__default.Children.forEach(prop, child => {
if (child.type.displayName !== 'GuidebannerElement') {
// If child element is not `GuidebannerElement`, then show:
// Carbon Products component's `displayName` (child.type.displayName) or
// React component's `name` (child.type.name) or
// HTML element's tag name (child.type).
error = new Error(`\`Guidebanner\` only accepts children of type \`GuidebannerElement\`, found \`${child.type?.displayName || child.type?.name || child.type}\` instead.`);
}
});
return error;
},
/**
* Provide an optional class to be applied to the containing node.
*/
className: PropTypes.string,
/**
* Tooltip text and aria label for the Close button icon.
*/
closeIconDescription: PropTypes.string,
/**
* Text label for the Collapse button.
*/
collapseButtonLabel: PropTypes.string,
/**
* When true, the Guidebanner will initialize in a collapsed state,
* showing the title and the Expand button.
*
* When expanded, it will show the GuidebannerElement child components and the Collapse button.
*/
collapsible: PropTypes.bool,
/**
* Text label for the Expand button.
*/
expandButtonLabel: PropTypes.string,
/**
* Tooltip text and aria label for the Next button icon.
*/
nextIconDescription: PropTypes.string,
/**
* If defined, a Close button will render in the top-right corner and a
* callback function will be triggered when button is clicked.
*/
onClose: PropTypes.func,
/**
* Tooltip text and aria label for the Back button icon.
*/
previousIconDescription: PropTypes.string,
/**
* Title text.
*/
title: PropTypes.string.isRequired,
/**
* If true, insert 1 rem of "space" on the left of the component.
* This will allow the component's content to line up with other
* content on the page under special circumstances.
*/
withLeftGutter: PropTypes.bool
};
export { Guidebanner };