@carbon/react
Version:
React components for the Carbon Design System
159 lines (152 loc) • 4.89 kB
JavaScript
/**
* 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.
*/
import { extends as _extends } from '../../_virtual/_rollupPluginBabelHelpers.js';
import { ChevronRight } from '@carbon/icons-react';
import cx from 'classnames';
import PropTypes from 'prop-types';
import React, { useState, useContext } from 'react';
import '../Text/index.js';
import { Escape } from '../../internal/keyboard/keys.js';
import { match } from '../../internal/keyboard/match.js';
import { useId } from '../../internal/useId.js';
import { deprecate } from '../../prop-types/deprecate.js';
import { usePrefix } from '../../internal/usePrefix.js';
import { AccordionContext } from './AccordionProvider.js';
import { Text } from '../Text/Text.js';
const defaultRenderToggle = props => /*#__PURE__*/React.createElement("button", _extends({
type: "button"
}, props));
function AccordionItem({
children,
className: customClassName = '',
open = false,
onHeadingClick,
renderExpando = defaultRenderToggle,
// remove renderExpando in next major release
renderToggle,
title = 'title',
disabled: controlledDisabled,
handleAnimationEnd,
...rest
}) {
const [isOpen, setIsOpen] = useState(open);
const [prevIsOpen, setPrevIsOpen] = useState(open);
const accordionState = useContext(AccordionContext);
const disabledIsControlled = typeof controlledDisabled === 'boolean';
const disabled = disabledIsControlled ? controlledDisabled : accordionState.disabled;
const id = useId('accordion-item');
const prefix = usePrefix();
const className = cx({
[`${prefix}--accordion__item`]: true,
[`${prefix}--accordion__item--active`]: isOpen && !disabled,
[`${prefix}--accordion__item--disabled`]: disabled,
[customClassName]: !!customClassName
});
const Toggle = renderToggle || renderExpando; // remove renderExpando in next major release
const content = React.useCallback(node => {
if (!node) {
return;
}
if (isOpen) {
// accordion closes
node.style.maxBlockSize = '';
}
}, [isOpen]);
if (open !== prevIsOpen) {
setIsOpen(open);
setPrevIsOpen(open);
}
// When the AccordionItem heading is clicked, toggle the open state of the
// panel
function onClick(event) {
const nextValue = !isOpen;
setIsOpen(nextValue);
if (onHeadingClick) {
// TODO: normalize signature, potentially:
// onHeadingClick :: (event: Event, state: { isOpen: Boolean }) => any
onHeadingClick({
isOpen: nextValue,
event
});
}
}
// If the AccordionItem is open, and the user hits the ESC key, then close it
function onKeyDown(event) {
if (isOpen && match(event, Escape)) {
setIsOpen(false);
}
}
function onAnimationEnd(event) {
if (handleAnimationEnd) {
handleAnimationEnd(event);
}
}
return /*#__PURE__*/React.createElement("li", _extends({
className: className
}, rest), /*#__PURE__*/React.createElement(Toggle, {
disabled: disabled,
"aria-controls": id,
"aria-expanded": isOpen,
className: `${prefix}--accordion__heading`,
onClick: onClick,
onKeyDown: onKeyDown,
type: "button"
}, /*#__PURE__*/React.createElement(ChevronRight, {
className: `${prefix}--accordion__arrow`
}), /*#__PURE__*/React.createElement(Text, {
as: "div",
className: `${prefix}--accordion__title`
}, title)), /*#__PURE__*/React.createElement("div", {
ref: content,
className: `${prefix}--accordion__wrapper`,
onTransitionEnd: onAnimationEnd
}, /*#__PURE__*/React.createElement("div", {
id: id,
className: `${prefix}--accordion__content`
}, children)));
}
AccordionItem.propTypes = {
/**
* Provide the contents of your AccordionItem
*/
children: PropTypes.node,
/**
* Specify an optional className to be applied to the container node
*/
className: PropTypes.string,
/**
* Specify whether an individual AccordionItem should be disabled
*/
disabled: PropTypes.bool,
/**
* The handler of the massaged `click` event.
*/
onClick: PropTypes.func,
/**
* The handler of the massaged `click` event on the heading.
*/
onHeadingClick: PropTypes.func,
/**
* `true` to open the expand.
*/
open: PropTypes.bool,
/**
* The callback function to render the expand button.
* Can be a React component class.
*/
renderExpando: deprecate(PropTypes.func, 'The `renderExpando` prop has been deprecated and will be removed in the next major release of Carbon. Use the `renderToggle` prop instead.'),
/**
* The callback function to render the expand button.
* Can be a React component class.
*/
renderToggle: PropTypes.func,
/**
* The accordion title.
*/
title: PropTypes.node
};
export { AccordionItem as default };