Skip to content

Commit

Permalink
[MenuUnstyled] Create MenuUnstyled and useMenu (#30961)
Browse files Browse the repository at this point in the history
Co-authored-by: danilo leal <daniloleal09@gmail.com>
  • Loading branch information
michaldudak and danilo-leal committed Mar 7, 2022
1 parent 0809cd6 commit 1aafc52
Show file tree
Hide file tree
Showing 52 changed files with 2,559 additions and 99 deletions.
181 changes: 181 additions & 0 deletions docs/data/base/components/menu/MenuSimple.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import * as React from 'react';
import MenuUnstyled from '@mui/base/MenuUnstyled';
import MenuItemUnstyled, {
menuItemUnstyledClasses,
} from '@mui/base/MenuItemUnstyled';
import { buttonUnstyledClasses } from '@mui/base/ButtonUnstyled';
import PopperUnstyled from '@mui/base/PopperUnstyled';
import { styled } from '@mui/system';

const blue = {
100: '#DAECFF',
200: '#99CCF3',
400: '#3399FF',
500: '#007FFF',
600: '#0072E5',
900: '#003A75',
};

const grey = {
100: '#E7EBF0',
200: '#E0E3E7',
300: '#CDD2D7',
400: '#B2BAC2',
500: '#A0AAB4',
600: '#6F7E8C',
700: '#3E5060',
800: '#2D3843',
900: '#1A2027',
};

const StyledListbox = styled('ul')(
({ theme }) => `
font-family: IBM Plex Sans, sans-serif;
font-size: 0.875rem;
box-sizing: border-box;
padding: 5px;
margin: 10px 0;
min-width: 200px;
background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'};
border: 1px solid ${theme.palette.mode === 'dark' ? grey[800] : grey[300]};
border-radius: 0.75em;
color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]};
overflow: auto;
outline: 0px;
`,
);

const StyledMenuItem = styled(MenuItemUnstyled)(
({ theme }) => `
list-style: none;
padding: 8px;
border-radius: 0.45em;
cursor: default;
&:last-of-type {
border-bottom: none;
}
&.${menuItemUnstyledClasses.focusVisible} {
outline: 3px solid ${theme.palette.mode === 'dark' ? blue[600] : blue[200]};
background-color: ${theme.palette.mode === 'dark' ? grey[800] : grey[100]};
color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]};
}
&.${menuItemUnstyledClasses.disabled} {
color: ${theme.palette.mode === 'dark' ? grey[700] : grey[400]};
}
&:hover:not(.${menuItemUnstyledClasses.disabled}) {
background-color: ${theme.palette.mode === 'dark' ? grey[800] : grey[100]};
color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]};
}
`,
);

const TriggerButton = styled('button')(
({ theme }) => `
font-family: IBM Plex Sans, sans-serif;
font-size: 0.875rem;
box-sizing: border-box;
min-height: calc(1.5em + 22px);
min-width: 200px;
background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'};
border: 1px solid ${theme.palette.mode === 'dark' ? grey[800] : grey[300]};
border-radius: 0.75em;
margin: 0.5em;
padding: 10px;
text-align: left;
line-height: 1.5;
color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]};
&:hover {
background: ${theme.palette.mode === 'dark' ? '' : grey[100]};
border-color: ${theme.palette.mode === 'dark' ? grey[700] : grey[400]};
}
&.${buttonUnstyledClasses.focusVisible} {
outline: 3px solid ${theme.palette.mode === 'dark' ? blue[600] : blue[100]};
}
&::after {
content: '▾';
float: right;
}
`,
);

const Popper = styled(PopperUnstyled)`
z-index: 1;
`;

export default function UnstyledMenuSimple() {
const [anchorEl, setAnchorEl] = React.useState(null);
const isOpen = Boolean(anchorEl);
const buttonRef = React.useRef(null);
const menuActions = React.useRef(null);

const handleButtonClick = (event) => {
if (isOpen) {
setAnchorEl(null);
} else {
setAnchorEl(event.currentTarget);
}
};

const handleButtonKeyDown = (event) => {
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
event.preventDefault();
setAnchorEl(event.currentTarget);
if (event.key === 'ArrowUp') {
menuActions.current?.highlightLastItem();
}
}
};

const close = () => {
setAnchorEl(null);
buttonRef.current.focus();
};

const createHandleMenuClick = (menuItem) => {
return () => {
// eslint-disable-next-line no-console
console.log(`Clicked on ${menuItem}`);
close();
};
};

return (
<div>
<TriggerButton
type="button"
onClick={handleButtonClick}
onKeyDown={handleButtonKeyDown}
ref={buttonRef}
aria-controls={isOpen ? 'simple-menu' : undefined}
aria-expanded={isOpen || undefined}
aria-haspopup="menu"
>
Language
</TriggerButton>

<MenuUnstyled
actions={menuActions}
open={isOpen}
onClose={close}
anchorEl={anchorEl}
components={{ Root: Popper, Listbox: StyledListbox }}
componentsProps={{ listbox: { id: 'simple-menu' } }}
>
<StyledMenuItem onClick={createHandleMenuClick('English')}>
English
</StyledMenuItem>
<StyledMenuItem onClick={createHandleMenuClick('中文')}>中文</StyledMenuItem>
<StyledMenuItem onClick={createHandleMenuClick('Português')}>
Português
</StyledMenuItem>
</MenuUnstyled>
</div>
);
}
181 changes: 181 additions & 0 deletions docs/data/base/components/menu/MenuSimple.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import * as React from 'react';
import MenuUnstyled, { MenuUnstyledActions } from '@mui/base/MenuUnstyled';
import MenuItemUnstyled, {
menuItemUnstyledClasses,
} from '@mui/base/MenuItemUnstyled';
import { buttonUnstyledClasses } from '@mui/base/ButtonUnstyled';
import PopperUnstyled from '@mui/base/PopperUnstyled';
import { styled } from '@mui/system';

const blue = {
100: '#DAECFF',
200: '#99CCF3',
400: '#3399FF',
500: '#007FFF',
600: '#0072E5',
900: '#003A75',
};

const grey = {
100: '#E7EBF0',
200: '#E0E3E7',
300: '#CDD2D7',
400: '#B2BAC2',
500: '#A0AAB4',
600: '#6F7E8C',
700: '#3E5060',
800: '#2D3843',
900: '#1A2027',
};

const StyledListbox = styled('ul')(
({ theme }) => `
font-family: IBM Plex Sans, sans-serif;
font-size: 0.875rem;
box-sizing: border-box;
padding: 5px;
margin: 10px 0;
min-width: 200px;
background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'};
border: 1px solid ${theme.palette.mode === 'dark' ? grey[800] : grey[300]};
border-radius: 0.75em;
color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]};
overflow: auto;
outline: 0px;
`,
);

const StyledMenuItem = styled(MenuItemUnstyled)(
({ theme }) => `
list-style: none;
padding: 8px;
border-radius: 0.45em;
cursor: default;
&:last-of-type {
border-bottom: none;
}
&.${menuItemUnstyledClasses.focusVisible} {
outline: 3px solid ${theme.palette.mode === 'dark' ? blue[600] : blue[200]};
background-color: ${theme.palette.mode === 'dark' ? grey[800] : grey[100]};
color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]};
}
&.${menuItemUnstyledClasses.disabled} {
color: ${theme.palette.mode === 'dark' ? grey[700] : grey[400]};
}
&:hover:not(.${menuItemUnstyledClasses.disabled}) {
background-color: ${theme.palette.mode === 'dark' ? grey[800] : grey[100]};
color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]};
}
`,
);

const TriggerButton = styled('button')(
({ theme }) => `
font-family: IBM Plex Sans, sans-serif;
font-size: 0.875rem;
box-sizing: border-box;
min-height: calc(1.5em + 22px);
min-width: 200px;
background: ${theme.palette.mode === 'dark' ? grey[900] : '#fff'};
border: 1px solid ${theme.palette.mode === 'dark' ? grey[800] : grey[300]};
border-radius: 0.75em;
margin: 0.5em;
padding: 10px;
text-align: left;
line-height: 1.5;
color: ${theme.palette.mode === 'dark' ? grey[300] : grey[900]};
&:hover {
background: ${theme.palette.mode === 'dark' ? '' : grey[100]};
border-color: ${theme.palette.mode === 'dark' ? grey[700] : grey[400]};
}
&.${buttonUnstyledClasses.focusVisible} {
outline: 3px solid ${theme.palette.mode === 'dark' ? blue[600] : blue[100]};
}
&::after {
content: '▾';
float: right;
}
`,
);

const Popper = styled(PopperUnstyled)`
z-index: 1;
`;

export default function UnstyledMenuSimple() {
const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null);
const isOpen = Boolean(anchorEl);
const buttonRef = React.useRef<HTMLButtonElement>(null);
const menuActions = React.useRef<MenuUnstyledActions>(null);

const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {
if (isOpen) {
setAnchorEl(null);
} else {
setAnchorEl(event.currentTarget);
}
};

const handleButtonKeyDown = (event: React.KeyboardEvent<HTMLButtonElement>) => {
if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
event.preventDefault();
setAnchorEl(event.currentTarget);
if (event.key === 'ArrowUp') {
menuActions.current?.highlightLastItem();
}
}
};

const close = () => {
setAnchorEl(null);
buttonRef.current!.focus();
};

const createHandleMenuClick = (menuItem: string) => {
return () => {
// eslint-disable-next-line no-console
console.log(`Clicked on ${menuItem}`);
close();
};
};

return (
<div>
<TriggerButton
type="button"
onClick={handleButtonClick}
onKeyDown={handleButtonKeyDown}
ref={buttonRef}
aria-controls={isOpen ? 'simple-menu' : undefined}
aria-expanded={isOpen || undefined}
aria-haspopup="menu"
>
Language
</TriggerButton>

<MenuUnstyled
actions={menuActions}
open={isOpen}
onClose={close}
anchorEl={anchorEl}
components={{ Root: Popper, Listbox: StyledListbox }}
componentsProps={{ listbox: { id: 'simple-menu' } }}
>
<StyledMenuItem onClick={createHandleMenuClick('English')}>
English
</StyledMenuItem>
<StyledMenuItem onClick={createHandleMenuClick('中文')}>中文</StyledMenuItem>
<StyledMenuItem onClick={createHandleMenuClick('Português')}>
Português
</StyledMenuItem>
</MenuUnstyled>
</div>
);
}

0 comments on commit 1aafc52

Please sign in to comment.