UNPKG

9.72 kBJavaScriptView Raw
1'use client';
2
3import * as React from 'react';
4import PropTypes from 'prop-types';
5import clsx from 'clsx';
6import composeClasses from '@mui/utils/composeClasses';
7import { alpha } from '@mui/system/colorManipulator';
8import rootShouldForwardProp from "../styles/rootShouldForwardProp.js";
9import { styled } from "../zero-styled/index.js";
10import memoTheme from "../utils/memoTheme.js";
11import { useDefaultProps } from "../DefaultPropsProvider/index.js";
12import ListContext from "../List/ListContext.js";
13import ButtonBase from "../ButtonBase/index.js";
14import useEnhancedEffect from "../utils/useEnhancedEffect.js";
15import useForkRef from "../utils/useForkRef.js";
16import { dividerClasses } from "../Divider/index.js";
17import { listItemIconClasses } from "../ListItemIcon/index.js";
18import { listItemTextClasses } from "../ListItemText/index.js";
19import menuItemClasses, { getMenuItemUtilityClass } from "./menuItemClasses.js";
20import { jsx as _jsx } from "react/jsx-runtime";
21export const overridesResolver = (props, styles) => {
22 const {
23 ownerState
24 } = props;
25 return [styles.root, ownerState.dense && styles.dense, ownerState.divider && styles.divider, !ownerState.disableGutters && styles.gutters];
26};
27const useUtilityClasses = ownerState => {
28 const {
29 disabled,
30 dense,
31 divider,
32 disableGutters,
33 selected,
34 classes
35 } = ownerState;
36 const slots = {
37 root: ['root', dense && 'dense', disabled && 'disabled', !disableGutters && 'gutters', divider && 'divider', selected && 'selected']
38 };
39 const composedClasses = composeClasses(slots, getMenuItemUtilityClass, classes);
40 return {
41 ...classes,
42 ...composedClasses
43 };
44};
45const MenuItemRoot = styled(ButtonBase, {
46 shouldForwardProp: prop => rootShouldForwardProp(prop) || prop === 'classes',
47 name: 'MuiMenuItem',
48 slot: 'Root',
49 overridesResolver
50})(memoTheme(({
51 theme
52}) => ({
53 ...theme.typography.body1,
54 display: 'flex',
55 justifyContent: 'flex-start',
56 alignItems: 'center',
57 position: 'relative',
58 textDecoration: 'none',
59 minHeight: 48,
60 paddingTop: 6,
61 paddingBottom: 6,
62 boxSizing: 'border-box',
63 whiteSpace: 'nowrap',
64 '&:hover': {
65 textDecoration: 'none',
66 backgroundColor: (theme.vars || theme).palette.action.hover,
67 // Reset on touch devices, it doesn't add specificity
68 '@media (hover: none)': {
69 backgroundColor: 'transparent'
70 }
71 },
72 [`&.${menuItemClasses.selected}`]: {
73 backgroundColor: theme.vars ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})` : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity),
74 [`&.${menuItemClasses.focusVisible}`]: {
75 backgroundColor: theme.vars ? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.focusOpacity}))` : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity + theme.palette.action.focusOpacity)
76 }
77 },
78 [`&.${menuItemClasses.selected}:hover`]: {
79 backgroundColor: theme.vars ? `rgba(${theme.vars.palette.primary.mainChannel} / calc(${theme.vars.palette.action.selectedOpacity} + ${theme.vars.palette.action.hoverOpacity}))` : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity + theme.palette.action.hoverOpacity),
80 // Reset on touch devices, it doesn't add specificity
81 '@media (hover: none)': {
82 backgroundColor: theme.vars ? `rgba(${theme.vars.palette.primary.mainChannel} / ${theme.vars.palette.action.selectedOpacity})` : alpha(theme.palette.primary.main, theme.palette.action.selectedOpacity)
83 }
84 },
85 [`&.${menuItemClasses.focusVisible}`]: {
86 backgroundColor: (theme.vars || theme).palette.action.focus
87 },
88 [`&.${menuItemClasses.disabled}`]: {
89 opacity: (theme.vars || theme).palette.action.disabledOpacity
90 },
91 [`& + .${dividerClasses.root}`]: {
92 marginTop: theme.spacing(1),
93 marginBottom: theme.spacing(1)
94 },
95 [`& + .${dividerClasses.inset}`]: {
96 marginLeft: 52
97 },
98 [`& .${listItemTextClasses.root}`]: {
99 marginTop: 0,
100 marginBottom: 0
101 },
102 [`& .${listItemTextClasses.inset}`]: {
103 paddingLeft: 36
104 },
105 [`& .${listItemIconClasses.root}`]: {
106 minWidth: 36
107 },
108 variants: [{
109 props: ({
110 ownerState
111 }) => !ownerState.disableGutters,
112 style: {
113 paddingLeft: 16,
114 paddingRight: 16
115 }
116 }, {
117 props: ({
118 ownerState
119 }) => ownerState.divider,
120 style: {
121 borderBottom: `1px solid ${(theme.vars || theme).palette.divider}`,
122 backgroundClip: 'padding-box'
123 }
124 }, {
125 props: ({
126 ownerState
127 }) => !ownerState.dense,
128 style: {
129 [theme.breakpoints.up('sm')]: {
130 minHeight: 'auto'
131 }
132 }
133 }, {
134 props: ({
135 ownerState
136 }) => ownerState.dense,
137 style: {
138 minHeight: 32,
139 // https://m2.material.io/components/menus#specs > Dense
140 paddingTop: 4,
141 paddingBottom: 4,
142 ...theme.typography.body2,
143 [`& .${listItemIconClasses.root} svg`]: {
144 fontSize: '1.25rem'
145 }
146 }
147 }]
148})));
149const MenuItem = /*#__PURE__*/React.forwardRef(function MenuItem(inProps, ref) {
150 const props = useDefaultProps({
151 props: inProps,
152 name: 'MuiMenuItem'
153 });
154 const {
155 autoFocus = false,
156 component = 'li',
157 dense = false,
158 divider = false,
159 disableGutters = false,
160 focusVisibleClassName,
161 role = 'menuitem',
162 tabIndex: tabIndexProp,
163 className,
164 ...other
165 } = props;
166 const context = React.useContext(ListContext);
167 const childContext = React.useMemo(() => ({
168 dense: dense || context.dense || false,
169 disableGutters
170 }), [context.dense, dense, disableGutters]);
171 const menuItemRef = React.useRef(null);
172 useEnhancedEffect(() => {
173 if (autoFocus) {
174 if (menuItemRef.current) {
175 menuItemRef.current.focus();
176 } else if (process.env.NODE_ENV !== 'production') {
177 console.error('MUI: Unable to set focus to a MenuItem whose component has not been rendered.');
178 }
179 }
180 }, [autoFocus]);
181 const ownerState = {
182 ...props,
183 dense: childContext.dense,
184 divider,
185 disableGutters
186 };
187 const classes = useUtilityClasses(props);
188 const handleRef = useForkRef(menuItemRef, ref);
189 let tabIndex;
190 if (!props.disabled) {
191 tabIndex = tabIndexProp !== undefined ? tabIndexProp : -1;
192 }
193 return /*#__PURE__*/_jsx(ListContext.Provider, {
194 value: childContext,
195 children: /*#__PURE__*/_jsx(MenuItemRoot, {
196 ref: handleRef,
197 role: role,
198 tabIndex: tabIndex,
199 component: component,
200 focusVisibleClassName: clsx(classes.focusVisible, focusVisibleClassName),
201 className: clsx(classes.root, className),
202 ...other,
203 ownerState: ownerState,
204 classes: classes
205 })
206 });
207});
208process.env.NODE_ENV !== "production" ? MenuItem.propTypes /* remove-proptypes */ = {
209 // ┌────────────────────────────── Warning ──────────────────────────────┐
210 // │ These PropTypes are generated from the TypeScript type definitions. │
211 // │ To update them, edit the d.ts file and run `pnpm proptypes`. │
212 // └─────────────────────────────────────────────────────────────────────┘
213 /**
214 * If `true`, the list item is focused during the first mount.
215 * Focus will also be triggered if the value changes from false to true.
216 * @default false
217 */
218 autoFocus: PropTypes.bool,
219 /**
220 * The content of the component.
221 */
222 children: PropTypes.node,
223 /**
224 * Override or extend the styles applied to the component.
225 */
226 classes: PropTypes.object,
227 /**
228 * @ignore
229 */
230 className: PropTypes.string,
231 /**
232 * The component used for the root node.
233 * Either a string to use a HTML element or a component.
234 */
235 component: PropTypes.elementType,
236 /**
237 * If `true`, compact vertical padding designed for keyboard and mouse input is used.
238 * The prop defaults to the value inherited from the parent Menu component.
239 * @default false
240 */
241 dense: PropTypes.bool,
242 /**
243 * @ignore
244 */
245 disabled: PropTypes.bool,
246 /**
247 * If `true`, the left and right padding is removed.
248 * @default false
249 */
250 disableGutters: PropTypes.bool,
251 /**
252 * If `true`, a 1px light border is added to the bottom of the menu item.
253 * @default false
254 */
255 divider: PropTypes.bool,
256 /**
257 * This prop can help identify which element has keyboard focus.
258 * The class name will be applied when the element gains the focus through keyboard interaction.
259 * It's a polyfill for the [CSS :focus-visible selector](https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo).
260 * The rationale for using this feature [is explained here](https://github.com/WICG/focus-visible/blob/HEAD/explainer.md).
261 * A [polyfill can be used](https://github.com/WICG/focus-visible) to apply a `focus-visible` class to other components
262 * if needed.
263 */
264 focusVisibleClassName: PropTypes.string,
265 /**
266 * @ignore
267 */
268 role: PropTypes /* @typescript-to-proptypes-ignore */.string,
269 /**
270 * If `true`, the component is selected.
271 * @default false
272 */
273 selected: PropTypes.bool,
274 /**
275 * The system prop that allows defining system overrides as well as additional CSS styles.
276 */
277 sx: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])), PropTypes.func, PropTypes.object]),
278 /**
279 * @default 0
280 */
281 tabIndex: PropTypes.number
282} : void 0;
283export default MenuItem;
\No newline at end of file