Skip to content

Commit

Permalink
[docs][joy-ui] Add TS demo for Menu Bar (#38308)
Browse files Browse the repository at this point in the history
  • Loading branch information
sai6855 authored Aug 4, 2023
1 parent baaa347 commit cddfa95
Show file tree
Hide file tree
Showing 2 changed files with 267 additions and 18 deletions.
22 changes: 4 additions & 18 deletions docs/data/joy/components/menu/MenuToolbarExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,7 @@ import MenuButton from '@mui/joy/MenuButton';
const MenuBarButton = React.forwardRef(
({ children, menu, open, onOpen, onKeyDown, ...props }, ref) => {
return (
<Dropdown
open={open}
onOpenChange={() => {
onOpen();
}}
>
<Dropdown open={open} onOpenChange={onOpen}>
<MenuButton
{...props}
slots={{ root: ListItemButton }}
Expand Down Expand Up @@ -148,11 +143,8 @@ export default function MenuToolbarExample() {
}}
menu={
<Menu
onClose={(event) => {
onClose={() => {
menus.current[0]?.focus();
if (event && event.relatedTarget !== menus.current[0]) {
setMenuIndex(null);
}
}}
>
<ListItem nested>
Expand Down Expand Up @@ -196,11 +188,8 @@ export default function MenuToolbarExample() {
}}
menu={
<Menu
onClose={(event) => {
onClose={() => {
menus.current[1]?.focus();
if (event && event.relatedTarget !== menus.current[1]) {
setMenuIndex(null);
}
}}
>
<ListItem nested>
Expand Down Expand Up @@ -240,11 +229,8 @@ export default function MenuToolbarExample() {
}}
menu={
<Menu
onClose={(event) => {
onClose={() => {
menus.current[2]?.focus();
if (event && event.relatedTarget !== menus.current[2]) {
setMenuIndex(null);
}
}}
>
<MenuItem {...itemProps}>Select All {renderShortcut('⌘ A')}</MenuItem>
Expand Down
263 changes: 263 additions & 0 deletions docs/data/joy/components/menu/MenuToolbarExample.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
import * as React from 'react';
import Menu from '@mui/joy/Menu';
import MenuItem, { menuItemClasses } from '@mui/joy/MenuItem';
import List from '@mui/joy/List';
import ListItem from '@mui/joy/ListItem';
import ListItemButton from '@mui/joy/ListItemButton';
import ListDivider from '@mui/joy/ListDivider';
import Typography, { typographyClasses } from '@mui/joy/Typography';
import Dropdown, { DropdownProps } from '@mui/joy/Dropdown';
import MenuButton from '@mui/joy/MenuButton';
import { Theme } from '@mui/joy';

type MenuBarButtonProps = Pick<DropdownProps, 'children' | 'open'> & {
onOpen: DropdownProps['onOpenChange'];
onKeyDown: React.KeyboardEventHandler;
menu: JSX.Element;
onMouseEnter: React.MouseEventHandler;
};

const MenuBarButton = React.forwardRef(
(
{ children, menu, open, onOpen, onKeyDown, ...props }: MenuBarButtonProps,
ref: React.ForwardedRef<HTMLButtonElement>,
) => {
return (
<Dropdown open={open} onOpenChange={onOpen}>
<MenuButton
{...props}
slots={{ root: ListItemButton }}
ref={ref}
role="menuitem"
variant={open ? 'soft' : 'plain'}
>
{children}
</MenuButton>
{React.cloneElement(menu, {
slotProps: {
listbox: {
id: `toolbar-example-menu-${children}`,
'aria-label': children,
},
},
placement: 'bottom-start',
disablePortal: false,
variant: 'soft',
sx: (theme: Theme) => ({
width: 288,
boxShadow: '0 2px 8px 0px rgba(0 0 0 / 0.38)',
'--List-padding': 'var(--ListDivider-gap)',
'--ListItem-minHeight': '32px',
[`&& .${menuItemClasses.root}`]: {
transition: 'none',
'&:hover': {
...theme.variants.solid.primary,
[`& .${typographyClasses.root}`]: {
color: 'inherit',
},
},
},
}),
})}
</Dropdown>
);
},
);

export default function MenuToolbarExample() {
const menus = React.useRef<Array<HTMLButtonElement>>([]);
const [menuIndex, setMenuIndex] = React.useState<null | number>(null);

const renderShortcut = (text: string) => (
<Typography level="body-sm" textColor="text.tertiary" ml="auto">
{text}
</Typography>
);

const openNextMenu = () => {
if (typeof menuIndex === 'number') {
if (menuIndex === menus.current.length - 1) {
setMenuIndex(0);
} else {
setMenuIndex(menuIndex + 1);
}
}
};

const openPreviousMenu = () => {
if (typeof menuIndex === 'number') {
if (menuIndex === 0) {
setMenuIndex(menus.current.length - 1);
} else {
setMenuIndex(menuIndex - 1);
}
}
};

const handleKeyDown = (event: React.KeyboardEvent) => {
if (event.key === 'ArrowRight') {
openNextMenu();
}
if (event.key === 'ArrowLeft') {
openPreviousMenu();
}
};

const createHandleButtonKeyDown =
(index: number) => (event: React.KeyboardEvent) => {
if (event.key === 'ArrowRight') {
if (index === menus.current.length - 1) {
menus.current[0]?.focus();
} else {
menus.current[index + 1]?.focus();
}
}
if (event.key === 'ArrowLeft') {
if (index === 0) {
menus.current[menus.current.length]?.focus();
} else {
menus.current[index - 1]?.focus();
}
}
};

const itemProps = {
onClick: () => setMenuIndex(null),
onKeyDown: handleKeyDown,
};

return (
<List
orientation="horizontal"
aria-label="Example application menu bar"
role="menubar"
data-joy-color-scheme="dark"
sx={{
bgcolor: 'background.body',
borderRadius: '4px',
maxWidth: 'fit-content',
}}
>
<ListItem>
<MenuBarButton
open={menuIndex === 0}
onOpen={() => {
setMenuIndex((prevMenuIndex) => (prevMenuIndex === null ? 0 : null));
}}
onKeyDown={createHandleButtonKeyDown(0)}
onMouseEnter={() => {
if (typeof menuIndex === 'number') {
setMenuIndex(0);
}
}}
ref={(instance) => {
menus.current[0] = instance!;
}}
menu={
<Menu
onClose={() => {
menus.current[0]?.focus();
}}
>
<ListItem nested>
<List aria-label="New">
<MenuItem {...itemProps}>New File</MenuItem>
<MenuItem {...itemProps}>
New Text File... {renderShortcut('⌥ ⌘ N')}
</MenuItem>
<MenuItem {...itemProps}>
New Window {renderShortcut('⇧ ⌘ N')}
</MenuItem>
</List>
</ListItem>
<ListDivider />
<ListItem nested>
<List aria-label="Open">
<MenuItem {...itemProps}>Open {renderShortcut('⌘ O')}</MenuItem>
<MenuItem {...itemProps}>Open Folder</MenuItem>
</List>
</ListItem>
</Menu>
}
>
File
</MenuBarButton>
</ListItem>
<ListItem>
<MenuBarButton
open={menuIndex === 1}
onOpen={() => {
setMenuIndex((prevMenuIndex) => (prevMenuIndex === null ? 1 : null));
}}
onKeyDown={createHandleButtonKeyDown(1)}
onMouseEnter={() => {
if (typeof menuIndex === 'number') {
setMenuIndex(1);
}
}}
ref={(instance) => {
menus.current[1] = instance!;
}}
menu={
<Menu
onClose={() => {
menus.current[1]?.focus();
}}
>
<ListItem nested>
<List aria-label="Time travel">
<MenuItem {...itemProps}>Undo {renderShortcut('⌘ Z')}</MenuItem>
<MenuItem {...itemProps}>Redo {renderShortcut('⇧ ⌘ Z')}</MenuItem>
</List>
</ListItem>
<ListDivider />
<ListItem nested>
<List aria-label="Tool">
<MenuItem {...itemProps}>Cut {renderShortcut('⌘ X')}</MenuItem>
<MenuItem {...itemProps}>Copy {renderShortcut('⌘ Z')}</MenuItem>
<MenuItem {...itemProps}>Paste {renderShortcut('⌘ V')}</MenuItem>
</List>
</ListItem>
</Menu>
}
>
Edit
</MenuBarButton>
</ListItem>
<ListItem>
<MenuBarButton
open={menuIndex === 2}
onOpen={() => {
setMenuIndex((prevMenuIndex) => (prevMenuIndex === null ? 2 : null));
}}
onKeyDown={createHandleButtonKeyDown(2)}
onMouseEnter={() => {
if (typeof menuIndex === 'number') {
setMenuIndex(2);
}
}}
ref={(instance) => {
menus.current[2] = instance!;
}}
menu={
<Menu
onClose={() => {
menus.current[2]?.focus();
}}
>
<MenuItem {...itemProps}>Select All {renderShortcut('⌘ A')}</MenuItem>
<MenuItem {...itemProps}>
Expand Selection {renderShortcut('⌃ ⇧ ⌘ →')}
</MenuItem>
<MenuItem {...itemProps}>
Shrink Selection {renderShortcut('⌃ ⇧ ⌘ ←')}
</MenuItem>
</Menu>
}
>
Selection
</MenuBarButton>
</ListItem>
</List>
);
}

0 comments on commit cddfa95

Please sign in to comment.