forked from storybookjs/storybook
/
Menu.tsx
132 lines (118 loc) 路 3.26 KB
/
Menu.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import React, { FunctionComponent, useMemo, ComponentProps } from 'react';
import { styled } from '@storybook/theming';
import { WithTooltip, TooltipLinkList, Button, Icons, IconButton } from '@storybook/components';
export type MenuList = ComponentProps<typeof TooltipLinkList>['links'];
export type MenuButtonProps = ComponentProps<typeof Button> &
// FIXME: Button should extends from the native <button>
ComponentProps<'button'> & {
highlighted: boolean;
};
const sharedStyles = {
height: 10,
width: 10,
marginLeft: -5,
marginRight: -5,
display: 'block',
};
const Icon = styled(Icons)(sharedStyles, ({ theme }) => ({
color: theme.color.secondary,
}));
const Img = styled.img(sharedStyles);
const Placeholder = styled.div(sharedStyles);
export interface ListItemIconProps {
icon?: ComponentProps<typeof Icons>['icon'];
imgSrc?: string;
}
export const MenuItemIcon = ({ icon, imgSrc }: ListItemIconProps) => {
if (icon) {
return <Icon icon={icon} />;
}
if (imgSrc) {
return <Img src={imgSrc} alt="image" />;
}
return <Placeholder />;
};
export const MenuButton = styled(Button)<MenuButtonProps>(({ highlighted, theme }) => ({
position: 'relative',
overflow: 'visible',
padding: 7,
transition: 'none', // prevents button border from flashing when focused/blurred
'&:focus': {
background: theme.barBg,
boxShadow: 'none',
},
// creates a pseudo border that does not affect the box model, but is accessible in high contrast mode
'&:focus:before': {
content: '""',
position: 'absolute',
top: 0,
bottom: 0,
left: 0,
right: 0,
borderRadius: '100%',
border: `1px solid ${theme.color.secondary}`,
},
...(highlighted && {
'&:after': {
content: '""',
position: 'absolute',
top: 0,
right: 0,
width: 8,
height: 8,
borderRadius: 8,
background: theme.color.positive,
},
}),
}));
type ClickHandler = ComponentProps<typeof TooltipLinkList>['links'][number]['onClick'];
export const SidebarMenuList: FunctionComponent<{
menu: MenuList;
onHide: () => void;
}> = ({ menu, onHide }) => {
const links = useMemo(() => {
return menu.map(({ onClick, ...rest }) => ({
...rest,
onClick: ((event, item) => {
if (onClick) {
onClick(event, item);
}
onHide();
}) as ClickHandler,
}));
}, [menu]);
return <TooltipLinkList links={links} />;
};
export const SidebarMenu: FunctionComponent<{
menu: MenuList;
isHighlighted: boolean;
}> = ({ isHighlighted, menu }) => {
return (
<WithTooltip
placement="top"
trigger="click"
closeOnClick
tooltip={({ onHide }) => <SidebarMenuList onHide={onHide} menu={menu} />}
>
<MenuButton outline small containsIcon highlighted={isHighlighted} title="Shortcuts">
<Icons icon="ellipsis" />
</MenuButton>
</WithTooltip>
);
};
export const ToolbarMenu: FunctionComponent<{
menu: MenuList;
}> = ({ menu }) => {
return (
<WithTooltip
placement="bottom"
trigger="click"
closeOnClick
tooltip={({ onHide }) => <SidebarMenuList onHide={onHide} menu={menu} />}
>
<IconButton title="Shortcuts" aria-label="Shortcuts">
<Icons icon="menu" />
</IconButton>
</WithTooltip>
);
};