@mskcc/carbon-react
Version:
Carbon react components for the MSKCC DSM
170 lines (163 loc) • 5.4 kB
JavaScript
/**
* MSKCC 2021, 2024
*/
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__default, { useState, useContext } from 'react';
import '../Text/index.js';
import { useId } from '../../internal/useId.js';
import { usePrefix } from '../../internal/usePrefix.js';
import { AccordionContext } from './AccordionProvider.js';
import { Text } from '../Text/Text.js';
import { match } from '../../internal/keyboard/match.js';
import { Escape } from '../../internal/keyboard/keys.js';
function AccordionItem(_ref) {
let {
children,
className: customClassName = '',
open = false,
onHeadingClick,
renderToggle,
title = 'title',
disabled: controlledDisabled,
handleAnimationEnd,
openIcon,
closeIcon,
iconOnlyToggle = false,
headingClassName,
titleClassName,
contentClassName,
...rest
} = _ref;
const [isOpen, setIsOpen] = useState(open);
const [prevIsOpen, setPrevIsOpen] = useState(open);
const [animation, setAnimation] = useState('');
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,
[`${prefix}--accordion__item--${animation}`]: animation,
[`${prefix}--accordion__item--disabled`]: disabled,
'msk-accordion-item--toggle-icon-only': iconOnlyToggle,
[customClassName]: !!customClassName
});
const defaultRenderToggle = props => !iconOnlyToggle ? /*#__PURE__*/React__default.createElement("button", _extends({
type: "button"
}, props)) : /*#__PURE__*/React__default.createElement("div", props);
const Toggle = renderToggle || defaultRenderToggle;
if (open !== prevIsOpen) {
setAnimation(isOpen ? 'collapsing' : 'expanding');
setIsOpen(open);
setPrevIsOpen(open);
}
// When the AccordionItem heading is clicked, toggle the open state of the
// panel
function onClick(event) {
const nextValue = !isOpen;
setAnimation(isOpen ? 'collapsing' : 'expanding');
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);
}
setAnimation('');
}
function onIconClick(event) {
if (iconOnlyToggle) {
onClick(event);
}
}
// Determine which icon to use
let IconComponent;
if (openIcon && closeIcon) {
IconComponent = /*#__PURE__*/React__default.createElement("button", {
className: "msk-accordion-item--expand-icon",
onClick: iconOnlyToggle ? onIconClick : undefined // Attach onClick if iconOnlyToggle is true
}, isOpen ? closeIcon : openIcon);
} else {
IconComponent = /*#__PURE__*/React__default.createElement(ChevronRight, {
onClick: iconOnlyToggle ? onIconClick : undefined,
className: `${prefix}--accordion__arrow`
});
}
return /*#__PURE__*/React__default.createElement("li", _extends({
className: className
}, rest, {
onAnimationEnd: onAnimationEnd
}), /*#__PURE__*/React__default.createElement(Toggle, {
disabled: disabled,
"aria-controls": id,
"aria-expanded": isOpen,
className: cx(headingClassName, `${prefix}--accordion__heading`),
onClick: !iconOnlyToggle ? onClick : undefined // Attach onClick if iconOnlyToggle is false
,
onKeyDown: onKeyDown,
type: !iconOnlyToggle ? 'button' : undefined
}, IconComponent, /*#__PURE__*/React__default.createElement(Text, {
as: "div",
className: cx(titleClassName, `${prefix}--accordion__title`)
}, title)), /*#__PURE__*/React__default.createElement("div", {
id: id,
className: cx(contentClassName, `${prefix}--accordion__content`)
}, children));
}
AccordionItem.propTypes = {
/**
* Provide the contents of your AccordionItem
*/
children: PropTypes.node,
headingClassName: PropTypes.node,
titleClassName: PropTypes.node,
contentClassName: 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.
*/
renderToggle: PropTypes.func,
/**
* The accordion title.
*/
title: PropTypes.node
};
export { AccordionItem as default };