nishant-design-system
Version:
Sense UI components library
176 lines (159 loc) • 4.31 kB
Flow
// @flow strict
import * as React from 'react';
import {classify} from '../../utils/classify';
import type {IconType} from '../Icon/Icon';
import {MenuOptionButton} from './MenuOptionButton';
import css from './Menu.module.css';
type ClassNames = $ReadOnly<{
wrapper?: string,
option?: string,
groupTitle?: string,
optionTextContainer?: string,
optionTextLabel?: string,
}>;
export type MenuOption = {
key: string,
label?: string,
secondaryLabel?: string,
customComponent?: React.Node,
iconLeft?: string,
iconLeftType?: IconType,
iconRight?: string,
iconRightType?: IconType,
disabled?: boolean,
optionSize?: MenuSizeTypes,
optionVariant?: MenuOptionsVariant,
};
// Render first available option set
export type BaseMenuProps = {
onSelect?: (option: MenuOption) => mixed,
selectedOption?: ?MenuOption,
optionsVariant?: MenuOptionsVariant,
selectedKeys?: Array<string>,
classNames?: ClassNames,
size?: MenuSizeTypes,
width?: string,
menuDisabled?: boolean,
isFluid?: boolean,
};
export type MenuOptionTypes = {
options?: Array<MenuOption>,
composeOptions?: Array<Array<MenuOption>>,
groupTitleOptions?: Array<MenuGroupTitleOption>,
};
export type MenuSizeTypes = 'medium' | 'small';
export type MenuOptionsVariant = 'checkbox' | 'radio' | 'normal';
export type MenuGroupTitleOption = {
groupTitle?: React.Node,
options?: Array<MenuOption>,
showLineDivider?: boolean,
};
export type MenuProps = {
...BaseMenuProps,
...MenuOptionTypes,
};
const RenderOption = ({
options,
composeOptions,
groupTitleOptions,
classNames,
...restProps
}: MenuProps): React.Node => {
if (options && Array.isArray(options) && options.length) {
return (
<>
{options.map((option) => (
<React.Fragment key={option.key}>
<MenuOptionButton
option={option}
classNames={classNames}
{...restProps}
/>
</React.Fragment>
))}
</>
);
}
if (
composeOptions &&
Array.isArray(composeOptions) &&
composeOptions.length
) {
return (
<>
{composeOptions.map((composeMenuOptions, index) => (
// eslint-disable-next-line react/no-array-index-key
<span className={css.menuDivider} key={index}>
{composeMenuOptions.map((option) => (
<React.Fragment key={option.key}>
<MenuOptionButton
option={option}
classNames={classNames}
{...restProps}
/>
</React.Fragment>
))}
</span>
))}
</>
);
}
if (
groupTitleOptions &&
Array.isArray(groupTitleOptions) &&
groupTitleOptions.length
) {
return (
<>
{groupTitleOptions.map((optionsGroup, index) => (
// eslint-disable-next-line react/no-array-index-key
<React.Fragment key={index}>
{!!optionsGroup.groupTitle && (
<div
className={classify(
css.groupTitleWrapper,
classNames?.groupTitle,
)}
>
{optionsGroup.groupTitle}
</div>
)}
{optionsGroup.options?.map((option) => (
<React.Fragment key={option.key}>
<MenuOptionButton
option={option}
classNames={classNames}
{...restProps}
/>
</React.Fragment>
))}
</React.Fragment>
))}
</>
);
}
return <></>;
};
export const Menu: React$AbstractComponent<MenuProps, HTMLDivElement> =
React.forwardRef<MenuProps, HTMLDivElement>(
(props: MenuProps, ref): React.Node => {
const {classNames, size = 'medium', width, isFluid = true} = props;
return (
<div
className={classify(
css.menuCard,
{
[css.fluid]: isFluid,
[css.medium]: size === 'medium',
[css.small]: size === 'small',
},
classNames?.wrapper,
)}
style={{width}}
ref={ref}
>
<RenderOption {...props} />
</div>
);
},
);