Files
AFFiNE-Mirror/packages/frontend/component/src/ui/menu/menu.stories.tsx
2023-12-04 08:32:19 +00:00

204 lines
4.1 KiB
TypeScript

import { InformationIcon } from '@blocksuite/icons';
import type { Meta, StoryFn } from '@storybook/react';
import { type ReactNode, useCallback, useState } from 'react';
import { Button } from '../button';
import { Tooltip } from '../tooltip';
import {
Menu,
MenuIcon,
MenuItem,
type MenuItemProps,
type MenuProps,
MenuSeparator,
MenuSub,
MenuTrigger,
} from '.';
export default {
title: 'UI/Menu',
component: Menu,
} satisfies Meta<typeof Menu>;
const Template: StoryFn<MenuProps> = args => (
<Menu
{...args}
contentOptions={{
style: {
width: '500px',
},
}}
>
<MenuTrigger>menu trigger</MenuTrigger>
</Menu>
);
interface Items {
label: ReactNode;
type?: MenuItemProps['type'];
preFix?: MenuItemProps['preFix'];
disabled?: boolean;
divider?: boolean;
subItems?: Items[];
block?: boolean;
}
const items: Items[] = [
{
label: 'default menu item 1',
},
{
label: 'menu item with icon',
preFix: (
<Tooltip content="Use `MenuIcon` to wrap your icon and choose `preFix` or `endFix`">
<MenuIcon>
<InformationIcon />
</MenuIcon>
</Tooltip>
),
},
{
label: (
<Tooltip
align="start"
content="Write, Draw, and Plan All at Once Notion Open Source Alternative One
hyper-fused platform for wildly creative minds"
>
<span>
Write, Draw, and Plan All at Once Notion Open Source Alternative One
hyper-fused platform for wildly creative minds
</span>
</Tooltip>
),
block: true,
},
{
label: 'default disabled menu item',
disabled: true,
},
{
label: 'danger menu item',
type: 'danger',
block: true,
preFix: (
<Tooltip content="Use `MenuIcon` to wrap your icon and choose `preFix` or `endFix`">
<MenuIcon>
<InformationIcon />
</MenuIcon>
</Tooltip>
),
},
{
label: 'warning menu item',
type: 'warning',
divider: true,
},
{
label: 'menu item with sub menu',
subItems: [
{
label: 'sub menu item 1',
},
{
label: 'sub menu item 1',
},
],
},
{
label: 'menu item with deep sub menu',
subItems: [
{
label: 'sub menu item 1',
},
{
label: 'sub menu with sub',
subItems: [
{
label: 'sub menu item 2-1',
},
{
label: 'sub menu item 2-2',
},
],
},
],
},
];
export const Default: StoryFn<MenuProps> = Template.bind(undefined);
const ItemRender = ({ label, divider, subItems, ...otherProps }: Items) => {
const onSelect = useCallback(() => {
console.log('value', label);
}, [label]);
if (subItems) {
return (
<>
<MenuSub
items={subItems.map((props, i) => (
<ItemRender key={i} {...props} />
))}
triggerOptions={otherProps}
>
{label}
</MenuSub>
{divider ? <MenuSeparator /> : null}
</>
);
}
return (
<>
<MenuItem onSelect={onSelect} {...otherProps}>
{label}
</MenuItem>
{divider ? <MenuSeparator /> : null}
</>
);
};
Default.args = {
items: items.map((props, i) => {
return <ItemRender key={i} {...props} />;
}),
};
const selectList = [
{ name: 'AFFiNE', value: '1' },
{ name: 'blocksuite', value: '2' },
{ name: 'octobase', value: '3' },
{ name: 'virgo', value: '4' },
];
const SelectItems = ({
selectedValue,
onSelect,
}: {
selectedValue: string;
onSelect: (value: string) => void;
}) => {
return selectList.map(({ name, value }) => (
<MenuItem
key={value}
selected={selectedValue === value}
onSelect={() => onSelect(value)}
>
{name}
</MenuItem>
));
};
const AsSelectTemplate: StoryFn<MenuProps> = () => {
const [value, setValue] = useState('1');
const name = selectList.find(item => item.value === value)?.name;
return (
<Menu items={<SelectItems selectedValue={value} onSelect={setValue} />}>
<Button>selected: {name}</Button>
</Menu>
);
};
export const AsSelect: StoryFn<MenuProps> = AsSelectTemplate.bind({});