UNPKG

@spaced-out/ui-design-system

Version:
147 lines (129 loc) 3.57 kB
// @flow strict import * as React from 'react'; import classify from '../../utils/classify'; import {UnstyledButton} from '../Button'; import type {IconType} from '../Icon'; import {Icon} from '../Icon'; import {Truncate} from '../Truncate'; import css from './CollapsibleCard.module.css'; export const COLLAPSIBLE_CARD_SEMANTIC = Object.freeze({ neutral: 'neutral', success: 'success', information: 'information', warning: 'warning', danger: 'danger', }); type ClassNames = $ReadOnly<{ wrapper?: string, title?: string, content?: string, }>; export type CollapsibleCardSemanticType = $Values< typeof COLLAPSIBLE_CARD_SEMANTIC, >; export type CollapsibleCardProps = { id?: string, title: React.Node, children: React.Node, classNames?: ClassNames, isOpen?: boolean, headerIconName?: string, headerIconType?: IconType, semantic?: CollapsibleCardSemanticType, onChange?: ?( e: SyntheticEvent<HTMLElement>, isOpen: boolean, id?: string, ) => mixed, }; export type CollapsibleCardActionProps = { children: React.Node, className?: string, }; export const CollapsibleCardAction = ({ children, className, }: CollapsibleCardActionProps): React.Node => ( <div className={classify(css.actionContainer, className)}>{children}</div> ); export type CollapsibleCardContentProps = { children: React.Node, className?: string, }; export const CollapsibleCardContent = ({ children, className, }: CollapsibleCardActionProps): React.Node => ( <div className={classify(css.contentContainer, className)}>{children}</div> ); export const CollapsibleCard: React$AbstractComponent< CollapsibleCardProps, HTMLDivElement, > = React.forwardRef<CollapsibleCardProps, HTMLDivElement>( ( { id, isOpen, onChange, classNames, title, children, semantic = 'neutral', headerIconName, headerIconType = 'solid', }: CollapsibleCardProps, ref, ): React.Node => { const [collapsibleCardIsOpen, setCollapsibleCardIsOpen] = React.useState( Boolean(isOpen), ); React.useEffect(() => { setCollapsibleCardIsOpen(Boolean(isOpen)); }, [isOpen]); return ( <div data-testid="CollapsibleCard" className={classify(css.wrapper, classNames?.wrapper)} ref={ref} > <UnstyledButton className={classify( css.header, { [css.isOpen]: collapsibleCardIsOpen, }, classNames?.title, )} onClick={(e) => { onChange?.(e, !collapsibleCardIsOpen, id); setCollapsibleCardIsOpen(!collapsibleCardIsOpen); }} > <div className={css.headerContent}> {!!headerIconName && ( <div className={classify(css.iconContainer, css[semantic])}> <Icon name={headerIconName} type={headerIconType} size="small" className={css[semantic]} /> </div> )} <Truncate>{title}</Truncate> </div> {collapsibleCardIsOpen ? ( <Icon name="chevron-up" color="secondary" size="small" /> ) : ( <Icon name="chevron-down" color="secondary" size="small" /> )} </UnstyledButton> {collapsibleCardIsOpen && ( <div className={classify(css.content, classNames?.content)}> {children} </div> )} </div> ); }, );