-
Notifications
You must be signed in to change notification settings - Fork 78
Expand file tree
/
Copy pathDrawerMenuItem.tsx
More file actions
187 lines (174 loc) · 5.72 KB
/
DrawerMenuItem.tsx
File metadata and controls
187 lines (174 loc) · 5.72 KB
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
import {
FocusZone,
FontIcon,
IFontIconProps,
IIconProps,
IRawStyle,
IStackStyles,
IStyle,
mergeStyles,
Stack,
Text
} from '@fluentui/react';
import React from 'react';
import { useTheme } from '../../theming/FluentThemeProvider';
import { BaseCustomStyles } from '../../types';
import { submitWithKeyboard } from '../utils/keyboardNavigation';
/**
* Props for the DrawerMenuItem
*
* @internal
*/
export interface _DrawerMenuItemProps {
onItemClick?: (ev?: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>, itemKey?: string) => void;
itemKey: string;
/** Text that shows at the start of the menu item after any icon supplied */
text?: string;
/** Text that shows at the end of the menu item before any secondaryIcon is supplied */
secondaryText?: string;
/** A component that shows at the end of the menu item before any secondaryIcon is supplied */
secondaryComponent?: JSX.Element;
/** Icon shown at the start of the menu item (before the menu item text) */
iconProps?: IIconProps;
/**
* Icon shown at the end of the menu item.
* By default if this component has subMenuProps, this icon is the RightChevron.
*/
secondaryIconProps?: IIconProps;
styles?: BaseCustomStyles;
subMenuProps?: _DrawerMenuItemProps[];
/**
* Whether the menu item is disabled
* @defaultvalue false
*/
disabled?: boolean;
/**
* A unique id set for the standard HTML id attibute
*/
id?: string;
/**
* Property to set the focus since this is the first item in the menu
*/
shouldFocusOnMount?: boolean;
/**
* Custom JSX item injection for custom mobile view button on drawers
*/
onRendererContent?: () => JSX.Element;
/**
* Aria label for the menu item
*/
ariaLabel?: string;
/**
* Dismiss the drawer menu when the button is clicked
*/
dismissDrawer?: boolean;
}
/**
* Maps the individual item in menuProps.items passed in the {@link DrawerMenu} into a UI component.
*
* @private
*/
export const DrawerMenuItem = (props: _DrawerMenuItemProps): JSX.Element => {
const theme = useTheme();
const onClick = (ev?: React.MouseEvent<HTMLElement> | React.KeyboardEvent<HTMLElement>): void =>
props.onItemClick && props.onItemClick(ev, props.itemKey);
const onKeyPress = (ev: React.KeyboardEvent<HTMLElement>): void => onClick && submitWithKeyboard(ev, onClick);
const secondaryIcon = props.secondaryIconProps ? (
<MenuItemIcon {...props.secondaryIconProps} />
) : props.subMenuProps ? (
<MenuItemIcon iconName="ChevronRight" />
) : undefined;
return (
<FocusZone shouldFocusOnMount={props.shouldFocusOnMount}>
<Stack
tabIndex={0}
role="menuitem"
horizontal
className={mergeStyles(
drawerMenuItemRootStyles(theme.palette.neutralLight, theme.fonts.small),
props.disabled ? disabledDrawerMenuItemRootStyles(theme.palette.neutralQuaternaryAlt) : undefined,
props.styles?.root
)}
onKeyPress={props.disabled ? undefined : onKeyPress}
onClick={props.disabled ? undefined : onClick}
tokens={menuItemChildrenGap}
id={props.id}
aria-label={props.ariaLabel}
>
{props.iconProps && (
<Stack.Item
role="presentation"
styles={props.disabled ? { root: { color: theme.palette.neutralTertiaryAlt } } : undefined}
>
<MenuItemIcon {...props.iconProps} />
</Stack.Item>
)}
<Stack.Item styles={drawerMenuItemTextStyles} grow>
<Text styles={props.disabled ? { root: { color: theme.palette.neutralTertiaryAlt } } : undefined}>
{props.text}
</Text>
</Stack.Item>
{props.secondaryText && (
<Stack.Item styles={drawerMenuItemTextStyles} className={mergeStyles(secondaryTextStyles)}>
<Text
styles={{
root: { color: props.disabled ? theme.palette.neutralTertiaryAlt : theme.palette.neutralSecondary }
}}
>
{props.secondaryText}
</Text>
</Stack.Item>
)}
{props.secondaryComponent && <Stack.Item>{props.secondaryComponent}</Stack.Item>}
{secondaryIcon && <Stack.Item>{secondaryIcon}</Stack.Item>}
</Stack>
</FocusZone>
);
};
const MenuItemIcon = (props: IFontIconProps): JSX.Element => (
<FontIcon className={mergeStyles(iconStyles)} {...props} />
);
const menuItemChildrenGap = { childrenGap: '0.5rem' };
const drawerMenuItemRootStyles = (hoverBackground: string, fontSize: IRawStyle): IStyle => ({
...fontSize,
height: '3rem',
lineHeight: '3rem',
padding: '0rem 0.75rem',
cursor: 'pointer',
':hover, :focus': {
background: hoverBackground
}
});
const disabledDrawerMenuItemRootStyles = (background: string): IStyle => ({
pointerEvents: 'none',
background: background,
':hover, :focus': {
background: background
}
});
/** Ensure long text entries appropriately show ellipsis instead of wrapping to a new line or showing a scrollbar */
const drawerMenuItemTextStyles: IStackStyles = {
root: {
overflow: 'hidden',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
}
};
const iconStyles: IStyle = {
// Vertically center icons in the menu item. Using line-height does not work for centering fluent SVG icons.
display: 'flex',
alignItems: 'center',
height: '100%',
// This can be removed when we upgrade to fluent-react-icons v2 (that removes the inner span element)
' span': {
display: 'flex',
alignItems: 'center',
height: '100%'
}
};
const secondaryTextStyles: IStyle = {
// limit width for secondaryText in the menu item so it does not overlap with text on left.
maxWidth: '50%'
};