UNPKG

@spaced-out/ui-design-system

Version:
157 lines (145 loc) 4.3 kB
//@flow strict import * as React from 'react'; import classify from '../../utils/classify'; import {FocusManagerWithArrowKeyNavigation} from '../FocusManagerWithArrowKeyNavigation'; import type {IconSize, IconType} from '../Icon'; import {Icon} from '../Icon'; import type {StatusSemanticType} from '../StatusIndicator'; import {StatusIndicator} from '../StatusIndicator'; import {ButtonTextMedium, TEXT_COLORS} from '../Text'; import css from './SubMenu.module.css'; type ClassNames = $ReadOnly<{ wrapper?: string, groupHeader?: string, menuGroup?: string, }>; export type SubMenuGroupProps = { children?: React.Node, groupTitle?: string, groupIcon?: string, groupIconSize?: IconSize, groupIconType?: IconType, collapsible?: boolean, disabled?: boolean, onChange?: (value: string) => mixed, selectedMenuKey?: string, status?: StatusSemanticType, classNames?: ClassNames, }; export const SubMenuGroup = ({ children, groupTitle, groupIcon, groupIconSize, groupIconType, collapsible = false, disabled = false, onChange, selectedMenuKey, status, classNames, }: SubMenuGroupProps): React.Node => { const isSubMenuGroupChildSelected = React.Children.toArray(children).some( (child) => child.props.menuKey === selectedMenuKey, ); const [collapsed, setCollapsed] = React.useState( !isSubMenuGroupChildSelected, ); const collapseHandler = () => { collapsible && setCollapsed(!collapsed); }; const onMenuItemChangeHandler = (value) => { onChange && onChange(value); }; const onKeyDownHandlerHeader = (e) => { if (e.key === 'Enter') { collapseHandler(); } }; const childrenWithProps = React.Children.map(children, (child) => { if (React.isValidElement(child)) { const {disabled: disabledChild} = child.props; return React.cloneElement(child, { selectedMenuKey, onChange: onMenuItemChangeHandler, disabled: disabledChild || disabled, }); } return child; }); React.useEffect(() => { setCollapsed(!isSubMenuGroupChildSelected); }, [selectedMenuKey]); return ( <div className={classify(css.subMenuGroupWrapper, classNames?.wrapper)}> <div className={classify( css.subMenuGroupHeader, {[css.collapsible]: collapsible}, {[css.highlightedMenu]: isSubMenuGroupChildSelected}, {[css.collapsed]: !collapsed}, classNames?.groupHeader, )} onClick={collapseHandler} onKeyDown={onKeyDownHandlerHeader} tabIndex={disabled ? '-1' : 0} > <div className={css.groupTitleWrapper}> {(!!groupIcon || !!status) && ( <div className={css.groupIconWrapper}> {!!groupIcon && ( <Icon color={TEXT_COLORS.inverseSecondary} name={groupIcon} type={groupIconType} size={groupIconSize} className={css.groupIcon} /> )} {!!status && collapsed && ( <div className={classify(css.statusIndicatorWrapper)}> <StatusIndicator status={status} className={css.statusIndicatorWrapper} /> </div> )} </div> )} <ButtonTextMedium color={TEXT_COLORS.inverseSecondary} className={css.groupTitle} > {groupTitle} </ButtonTextMedium> </div> {collapsible && ( <Icon color={TEXT_COLORS.inverseSecondary} name={collapsed ? 'chevron-down' : 'chevron-up'} size="small" /> )} </div> <div className={classify( css.subMenuGroup, { [css.collapsed]: collapsible && collapsed, }, classNames?.menuGroup, )} > <FocusManagerWithArrowKeyNavigation classNames={{ wrapper: classify(css.subMenuFocusWrapper), }} focusItemOnOpen={true} loop={true} > {childrenWithProps} </FocusManagerWithArrowKeyNavigation> </div> </div> ); };