@spaced-out/ui-design-system
Version:
Sense UI components library
157 lines (145 loc) • 4.3 kB
Flow
//@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>
);
};