From 3d2fb8adca9d7ea3d4ea46998a207bcf5aa04aa1 Mon Sep 17 00:00:00 2001 From: Marija Najdova Date: Fri, 8 Sep 2023 15:07:34 +0200 Subject: [PATCH] [joy-ui][Drawer] Add Drawer component (#38169) --- .../joy/components/drawer/DrawerAnchor.js | 72 ++++ .../joy/components/drawer/DrawerAnchor.tsx | 80 ++++ .../data/joy/components/drawer/DrawerBasic.js | 51 +++ .../joy/components/drawer/DrawerBasic.tsx | 56 +++ .../components/drawer/DrawerCloseButton.js | 22 + .../components/drawer/DrawerCloseButton.tsx | 22 + .../drawer/DrawerCloseButton.tsx.preview | 7 + .../joy/components/drawer/DrawerFilters.js | 272 ++++++++++++ .../joy/components/drawer/DrawerFilters.tsx | 273 ++++++++++++ .../drawer/DrawerMobileNavigation.js | 99 +++++ .../drawer/DrawerMobileNavigation.tsx | 99 +++++ .../joy/components/drawer/DrawerScrollable.js | 55 +++ .../components/drawer/DrawerScrollable.tsx | 55 +++ docs/data/joy/components/drawer/DrawerSize.js | 71 ++++ .../data/joy/components/drawer/DrawerSize.tsx | 79 ++++ .../joy/components/drawer/DrawerTransition.js | 48 +++ .../components/drawer/DrawerTransition.tsx | 48 +++ .../data/joy/components/drawer/DrawerUsage.js | 121 ++++++ docs/data/joy/components/drawer/drawer.md | 70 ++- docs/data/joy/pages.ts | 2 +- docs/data/joy/pagesApi.js | 1 + docs/pages/base-ui/api/use-modal.json | 5 +- docs/pages/joy-ui/api/drawer.js | 19 + docs/pages/joy-ui/api/drawer.json | 115 +++++ docs/pages/joy-ui/api/modal.json | 3 +- docs/src/modules/components/DemoSandbox.js | 37 +- .../drawer-content/drawer-content.json | 90 ++++ .../api-docs-joy/drawer/drawer.json | 135 ++++++ .../api-docs-joy/modal/modal.json | 5 + .../src/unstable_useModal/useModal.ts | 4 +- .../src/unstable_useModal/useModal.types.ts | 2 +- packages/mui-joy/src/Drawer/Drawer.test.tsx | 69 +++ packages/mui-joy/src/Drawer/Drawer.tsx | 401 ++++++++++++++++++ packages/mui-joy/src/Drawer/DrawerProps.ts | 78 ++++ packages/mui-joy/src/Drawer/drawerClasses.ts | 66 +++ packages/mui-joy/src/Drawer/index.ts | 5 + packages/mui-joy/src/Modal/Modal.tsx | 54 +-- packages/mui-joy/src/Modal/ModalProps.ts | 63 +-- packages/mui-joy/src/Modal/modalClasses.ts | 8 +- packages/mui-joy/src/index.ts | 3 + 40 files changed, 2681 insertions(+), 84 deletions(-) create mode 100644 docs/data/joy/components/drawer/DrawerAnchor.js create mode 100644 docs/data/joy/components/drawer/DrawerAnchor.tsx create mode 100644 docs/data/joy/components/drawer/DrawerBasic.js create mode 100644 docs/data/joy/components/drawer/DrawerBasic.tsx create mode 100644 docs/data/joy/components/drawer/DrawerCloseButton.js create mode 100644 docs/data/joy/components/drawer/DrawerCloseButton.tsx create mode 100644 docs/data/joy/components/drawer/DrawerCloseButton.tsx.preview create mode 100644 docs/data/joy/components/drawer/DrawerFilters.js create mode 100644 docs/data/joy/components/drawer/DrawerFilters.tsx create mode 100644 docs/data/joy/components/drawer/DrawerMobileNavigation.js create mode 100644 docs/data/joy/components/drawer/DrawerMobileNavigation.tsx create mode 100644 docs/data/joy/components/drawer/DrawerScrollable.js create mode 100644 docs/data/joy/components/drawer/DrawerScrollable.tsx create mode 100644 docs/data/joy/components/drawer/DrawerSize.js create mode 100644 docs/data/joy/components/drawer/DrawerSize.tsx create mode 100644 docs/data/joy/components/drawer/DrawerTransition.js create mode 100644 docs/data/joy/components/drawer/DrawerTransition.tsx create mode 100644 docs/data/joy/components/drawer/DrawerUsage.js create mode 100644 docs/pages/joy-ui/api/drawer.js create mode 100644 docs/pages/joy-ui/api/drawer.json create mode 100644 docs/translations/api-docs-joy/drawer-content/drawer-content.json create mode 100644 docs/translations/api-docs-joy/drawer/drawer.json create mode 100644 packages/mui-joy/src/Drawer/Drawer.test.tsx create mode 100644 packages/mui-joy/src/Drawer/Drawer.tsx create mode 100644 packages/mui-joy/src/Drawer/DrawerProps.ts create mode 100644 packages/mui-joy/src/Drawer/drawerClasses.ts create mode 100644 packages/mui-joy/src/Drawer/index.ts diff --git a/docs/data/joy/components/drawer/DrawerAnchor.js b/docs/data/joy/components/drawer/DrawerAnchor.js new file mode 100644 index 00000000000000..0523d6d4ea6ade --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerAnchor.js @@ -0,0 +1,72 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; +import Drawer from '@mui/joy/Drawer'; +import ButtonGroup from '@mui/joy/ButtonGroup'; +import Button from '@mui/joy/Button'; +import List from '@mui/joy/List'; +import Divider from '@mui/joy/Divider'; +import ListItem from '@mui/joy/ListItem'; +import ListItemButton from '@mui/joy/ListItemButton'; + +export default function DrawerAnchor() { + const [state, setState] = React.useState({ + top: false, + left: false, + bottom: false, + right: false, + }); + + const toggleDrawer = (anchor, open) => (event) => { + if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) { + return; + } + + setState({ ...state, [anchor]: open }); + }; + + const list = (anchor) => ( + + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text) => ( + + {text} + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text) => ( + + {text} + + ))} + + + ); + + return ( + + + {['left', 'right', 'top', 'bottom'].map((anchor) => ( + + ))} + + {['left', 'right', 'top', 'bottom'].map((anchor) => ( + + {list(anchor)} + + ))} + + ); +} diff --git a/docs/data/joy/components/drawer/DrawerAnchor.tsx b/docs/data/joy/components/drawer/DrawerAnchor.tsx new file mode 100644 index 00000000000000..677ad8db09533f --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerAnchor.tsx @@ -0,0 +1,80 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; +import Drawer from '@mui/joy/Drawer'; +import ButtonGroup from '@mui/joy/ButtonGroup'; +import Button from '@mui/joy/Button'; +import List from '@mui/joy/List'; +import Divider from '@mui/joy/Divider'; +import ListItem from '@mui/joy/ListItem'; +import ListItemButton from '@mui/joy/ListItemButton'; + +type Anchor = 'top' | 'left' | 'bottom' | 'right'; + +export default function DrawerAnchor() { + const [state, setState] = React.useState({ + top: false, + left: false, + bottom: false, + right: false, + }); + + const toggleDrawer = + (anchor: Anchor, open: boolean) => + (event: React.KeyboardEvent | React.MouseEvent) => { + if ( + event.type === 'keydown' && + ((event as React.KeyboardEvent).key === 'Tab' || + (event as React.KeyboardEvent).key === 'Shift') + ) { + return; + } + + setState({ ...state, [anchor]: open }); + }; + + const list = (anchor: Anchor) => ( + + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text) => ( + + {text} + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text) => ( + + {text} + + ))} + + + ); + + return ( + + + {(['left', 'right', 'top', 'bottom'] as const).map((anchor) => ( + + ))} + + {(['left', 'right', 'top', 'bottom'] as const).map((anchor) => ( + + {list(anchor)} + + ))} + + ); +} diff --git a/docs/data/joy/components/drawer/DrawerBasic.js b/docs/data/joy/components/drawer/DrawerBasic.js new file mode 100644 index 00000000000000..9c103b988a6ad7 --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerBasic.js @@ -0,0 +1,51 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; +import Drawer from '@mui/joy/Drawer'; +import Button from '@mui/joy/Button'; +import List from '@mui/joy/List'; +import Divider from '@mui/joy/Divider'; +import ListItem from '@mui/joy/ListItem'; +import ListItemButton from '@mui/joy/ListItemButton'; + +export default function DrawerBasic() { + const [open, setOpen] = React.useState(false); + + const toggleDrawer = (inOpen) => (event) => { + if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) { + return; + } + + setOpen(inOpen); + }; + + return ( + + + + + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text) => ( + + {text} + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text) => ( + + {text} + + ))} + + + + + ); +} diff --git a/docs/data/joy/components/drawer/DrawerBasic.tsx b/docs/data/joy/components/drawer/DrawerBasic.tsx new file mode 100644 index 00000000000000..6bcc4a39727756 --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerBasic.tsx @@ -0,0 +1,56 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; +import Drawer from '@mui/joy/Drawer'; +import Button from '@mui/joy/Button'; +import List from '@mui/joy/List'; +import Divider from '@mui/joy/Divider'; +import ListItem from '@mui/joy/ListItem'; +import ListItemButton from '@mui/joy/ListItemButton'; + +export default function DrawerBasic() { + const [open, setOpen] = React.useState(false); + + const toggleDrawer = + (inOpen: boolean) => (event: React.KeyboardEvent | React.MouseEvent) => { + if ( + event.type === 'keydown' && + ((event as React.KeyboardEvent).key === 'Tab' || + (event as React.KeyboardEvent).key === 'Shift') + ) { + return; + } + + setOpen(inOpen); + }; + + return ( + + + + + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text) => ( + + {text} + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text) => ( + + {text} + + ))} + + + + + ); +} diff --git a/docs/data/joy/components/drawer/DrawerCloseButton.js b/docs/data/joy/components/drawer/DrawerCloseButton.js new file mode 100644 index 00000000000000..6d10e1ae93b587 --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerCloseButton.js @@ -0,0 +1,22 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; +import Button from '@mui/joy/Button'; +import Drawer from '@mui/joy/Drawer'; +import DialogTitle from '@mui/joy/DialogTitle'; +import ModalClose from '@mui/joy/ModalClose'; + +export default function DrawerCloseButton() { + const [open, setOpen] = React.useState(false); + + return ( + + + setOpen(false)}> + + Title + + + ); +} diff --git a/docs/data/joy/components/drawer/DrawerCloseButton.tsx b/docs/data/joy/components/drawer/DrawerCloseButton.tsx new file mode 100644 index 00000000000000..6d10e1ae93b587 --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerCloseButton.tsx @@ -0,0 +1,22 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; +import Button from '@mui/joy/Button'; +import Drawer from '@mui/joy/Drawer'; +import DialogTitle from '@mui/joy/DialogTitle'; +import ModalClose from '@mui/joy/ModalClose'; + +export default function DrawerCloseButton() { + const [open, setOpen] = React.useState(false); + + return ( + + + setOpen(false)}> + + Title + + + ); +} diff --git a/docs/data/joy/components/drawer/DrawerCloseButton.tsx.preview b/docs/data/joy/components/drawer/DrawerCloseButton.tsx.preview new file mode 100644 index 00000000000000..1ade8a1795e0b7 --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerCloseButton.tsx.preview @@ -0,0 +1,7 @@ + + setOpen(false)}> + + Title + \ No newline at end of file diff --git a/docs/data/joy/components/drawer/DrawerFilters.js b/docs/data/joy/components/drawer/DrawerFilters.js new file mode 100644 index 00000000000000..77ad4f04716a29 --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerFilters.js @@ -0,0 +1,272 @@ +import * as React from 'react'; +import AspectRatio from '@mui/joy/AspectRatio'; +import Box from '@mui/joy/Box'; +import Drawer from '@mui/joy/Drawer'; +import Button from '@mui/joy/Button'; +import Card from '@mui/joy/Card'; +import CardContent from '@mui/joy/CardContent'; +import Checkbox from '@mui/joy/Checkbox'; +import DialogTitle from '@mui/joy/DialogTitle'; +import DialogContent from '@mui/joy/DialogContent'; +import ModalClose from '@mui/joy/ModalClose'; +import Divider from '@mui/joy/Divider'; +import FormControl from '@mui/joy/FormControl'; +import FormLabel from '@mui/joy/FormLabel'; +import FormHelperText from '@mui/joy/FormHelperText'; +import List from '@mui/joy/List'; +import ListItem from '@mui/joy/ListItem'; +import Stack from '@mui/joy/Stack'; +import RadioGroup from '@mui/joy/RadioGroup'; +import Radio from '@mui/joy/Radio'; +import Sheet from '@mui/joy/Sheet'; +import Switch from '@mui/joy/Switch'; +import Typography from '@mui/joy/Typography'; +import TuneIcon from '@mui/icons-material/TuneRounded'; +import HomeRoundedIcon from '@mui/icons-material/HomeRounded'; +import ApartmentRoundedIcon from '@mui/icons-material/ApartmentRounded'; +import MeetingRoomRoundedIcon from '@mui/icons-material/MeetingRoomRounded'; +import HotelRoundedIcon from '@mui/icons-material/HotelRounded'; +import Done from '@mui/icons-material/Done'; + +export default function DrawerFilters() { + const [open, setOpen] = React.useState(false); + const [type, setType] = React.useState('Guesthouse'); + const [amenities, setAmenities] = React.useState([0, 6]); + + return ( + + + setOpen(false)} + slotProps={{ + content: { + sx: { + bgcolor: 'transparent', + p: { md: 3, sm: 0 }, + boxShadow: 'none', + }, + }, + }} + > + + Filters + + + + Property Type + { + setType(event.target.value); + }} + > + + {[ + { + name: 'House', + icon: , + }, + { + name: 'Apartment', + icon: , + }, + { + name: 'Guesthouse', + icon: , + }, + { + name: 'Hotel', + icon: , + }, + ].map((item) => ( + + + {item.icon} + {item.name} + + + + ))} + + + + + + Amenities + +
+ + {[ + 'Wifi', + 'Washer', + 'Air Conditioner', + 'Kitchen', + 'Dryer', + 'Heating', + 'Dedicated Workspace', + ].map((item, index) => { + const selected = amenities.includes(index); + return ( + + +
{selected && }
+
+ + setAmenities((prev) => { + const set = new Set([...prev, index]); + if (!event.target.checked) { + set.delete(index); + } + + return [...set]; + }) + } + slotProps={{ + action: { + sx: { + '&:hover': { + bgcolor: 'transparent', + }, + }, + }, + }} + /> +
+ ); + })} +
+
+ + + Booking Options + + + + Instant Book + + Listings you can book without waiting for host approval + + + + + + + + Self Check-in + + Easy access to the property when you arrive + + + + + + + + Superhost + + Stay with top tier recognized hosts + + + + +
+ + + + + + +
+
+
+ ); +} diff --git a/docs/data/joy/components/drawer/DrawerFilters.tsx b/docs/data/joy/components/drawer/DrawerFilters.tsx new file mode 100644 index 00000000000000..e1427ccd6a4880 --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerFilters.tsx @@ -0,0 +1,273 @@ +import * as React from 'react'; +import AspectRatio from '@mui/joy/AspectRatio'; +import Box from '@mui/joy/Box'; +import Drawer from '@mui/joy/Drawer'; +import Button from '@mui/joy/Button'; +import Card from '@mui/joy/Card'; +import CardContent from '@mui/joy/CardContent'; +import Checkbox from '@mui/joy/Checkbox'; +import DialogTitle from '@mui/joy/DialogTitle'; +import DialogContent from '@mui/joy/DialogContent'; +import ModalClose from '@mui/joy/ModalClose'; +import Divider from '@mui/joy/Divider'; +import FormControl from '@mui/joy/FormControl'; +import FormLabel from '@mui/joy/FormLabel'; +import FormHelperText from '@mui/joy/FormHelperText'; +import List from '@mui/joy/List'; +import ListItem from '@mui/joy/ListItem'; +import Stack from '@mui/joy/Stack'; +import RadioGroup from '@mui/joy/RadioGroup'; +import Radio from '@mui/joy/Radio'; +import Sheet from '@mui/joy/Sheet'; +import Switch from '@mui/joy/Switch'; +import Typography from '@mui/joy/Typography'; +import TuneIcon from '@mui/icons-material/TuneRounded'; +import HomeRoundedIcon from '@mui/icons-material/HomeRounded'; +import ApartmentRoundedIcon from '@mui/icons-material/ApartmentRounded'; +import MeetingRoomRoundedIcon from '@mui/icons-material/MeetingRoomRounded'; +import HotelRoundedIcon from '@mui/icons-material/HotelRounded'; +import Done from '@mui/icons-material/Done'; + +export default function DrawerFilters() { + const [open, setOpen] = React.useState(false); + const [type, setType] = React.useState('Guesthouse'); + const [amenities, setAmenities] = React.useState([0, 6]); + + return ( + + + setOpen(false)} + slotProps={{ + content: { + sx: { + bgcolor: 'transparent', + p: { md: 3, sm: 0 }, + boxShadow: 'none', + }, + }, + }} + > + + Filters + + + + + Property Type + { + setType(event.target.value); + }} + > + + {[ + { + name: 'House', + icon: , + }, + { + name: 'Apartment', + icon: , + }, + { + name: 'Guesthouse', + icon: , + }, + { + name: 'Hotel', + icon: , + }, + ].map((item) => ( + + + {item.icon} + {item.name} + + + + ))} + + + + + + Amenities + +
+ + {[ + 'Wifi', + 'Washer', + 'Air Conditioner', + 'Kitchen', + 'Dryer', + 'Heating', + 'Dedicated Workspace', + ].map((item, index) => { + const selected = amenities.includes(index); + return ( + + +
{selected && }
+
+ + setAmenities((prev) => { + const set = new Set([...prev, index]); + if (!event.target.checked) { + set.delete(index); + } + // @ts-ignore + return [...set]; + }) + } + slotProps={{ + action: { + sx: { + '&:hover': { + bgcolor: 'transparent', + }, + }, + }, + }} + /> +
+ ); + })} +
+
+ + + Booking Options + + + + Instant Book + + Listings you can book without waiting for host approval + + + + + + + + Self Check-in + + Easy access to the property when you arrive + + + + + + + + Superhost + + Stay with top tier recognized hosts + + + + +
+ + + + + + +
+
+
+ ); +} diff --git a/docs/data/joy/components/drawer/DrawerMobileNavigation.js b/docs/data/joy/components/drawer/DrawerMobileNavigation.js new file mode 100644 index 00000000000000..711a9617796a61 --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerMobileNavigation.js @@ -0,0 +1,99 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; +import IconButton from '@mui/joy/IconButton'; +import Drawer from '@mui/joy/Drawer'; +import Input from '@mui/joy/Input'; +import List from '@mui/joy/List'; +import ListItemButton from '@mui/joy/ListItemButton'; +import Typography from '@mui/joy/Typography'; +import ModalClose from '@mui/joy/ModalClose'; +import Menu from '@mui/icons-material/Menu'; +import Search from '@mui/icons-material/Search'; + +export default function DrawerMobileNavigation() { + const [open, setOpen] = React.useState(false); + + return ( + + setOpen(true)}> + + + setOpen(false)}> + + + close + + + + div': { justifyContent: 'center' }, + }} + > + Home + About + Works + Studio + Contact + + + } + slotProps={{ + input: { + 'aria-label': 'Search anything', + }, + }} + sx={{ + borderRadius: 0, + borderBottom: '2px solid', + borderColor: 'neutral.outlinedBorder', + '&:hover': { + borderColor: 'neutral.outlinedHoverBorder', + }, + '&::before': { + border: '1px solid var(--Input-focusedHighlight)', + transform: 'scaleX(0)', + left: 0, + right: 0, + bottom: '-2px', + top: 'unset', + transition: 'transform .15s cubic-bezier(0.1,0.9,0.2,1)', + borderRadius: 0, + }, + '&:focus-within::before': { + transform: 'scaleX(1)', + }, + }} + /> + + We made honest design for your business. Check out our works. + + + + + ); +} diff --git a/docs/data/joy/components/drawer/DrawerMobileNavigation.tsx b/docs/data/joy/components/drawer/DrawerMobileNavigation.tsx new file mode 100644 index 00000000000000..711a9617796a61 --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerMobileNavigation.tsx @@ -0,0 +1,99 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; +import IconButton from '@mui/joy/IconButton'; +import Drawer from '@mui/joy/Drawer'; +import Input from '@mui/joy/Input'; +import List from '@mui/joy/List'; +import ListItemButton from '@mui/joy/ListItemButton'; +import Typography from '@mui/joy/Typography'; +import ModalClose from '@mui/joy/ModalClose'; +import Menu from '@mui/icons-material/Menu'; +import Search from '@mui/icons-material/Search'; + +export default function DrawerMobileNavigation() { + const [open, setOpen] = React.useState(false); + + return ( + + setOpen(true)}> + + + setOpen(false)}> + + + close + + + + div': { justifyContent: 'center' }, + }} + > + Home + About + Works + Studio + Contact + + + } + slotProps={{ + input: { + 'aria-label': 'Search anything', + }, + }} + sx={{ + borderRadius: 0, + borderBottom: '2px solid', + borderColor: 'neutral.outlinedBorder', + '&:hover': { + borderColor: 'neutral.outlinedHoverBorder', + }, + '&::before': { + border: '1px solid var(--Input-focusedHighlight)', + transform: 'scaleX(0)', + left: 0, + right: 0, + bottom: '-2px', + top: 'unset', + transition: 'transform .15s cubic-bezier(0.1,0.9,0.2,1)', + borderRadius: 0, + }, + '&:focus-within::before': { + transform: 'scaleX(1)', + }, + }} + /> + + We made honest design for your business. Check out our works. + + + + + ); +} diff --git a/docs/data/joy/components/drawer/DrawerScrollable.js b/docs/data/joy/components/drawer/DrawerScrollable.js new file mode 100644 index 00000000000000..3c6e018d7c27e5 --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerScrollable.js @@ -0,0 +1,55 @@ +import * as React from 'react'; +import Avatar from '@mui/joy/Avatar'; +import Box from '@mui/joy/Box'; +import Button from '@mui/joy/Button'; +import Drawer from '@mui/joy/Drawer'; +import DialogTitle from '@mui/joy/DialogTitle'; +import DialogContent from '@mui/joy/DialogContent'; +import List from '@mui/joy/List'; +import ListItem from '@mui/joy/ListItem'; +import ListItemButton from '@mui/joy/ListItemButton'; +import Typography from '@mui/joy/Typography'; +import ModalClose from '@mui/joy/ModalClose'; + +export default function DrawerScrollable() { + const [open, setOpen] = React.useState(false); + + return ( + + + setOpen(false)}> + + Title + + + {[...new Array(100)].map((_, index) => ( + + setOpen(false)}> + Item {index} + + + ))} + + + + +
+ Username + joined 20 Jun 2023 +
+
+
+
+ ); +} diff --git a/docs/data/joy/components/drawer/DrawerScrollable.tsx b/docs/data/joy/components/drawer/DrawerScrollable.tsx new file mode 100644 index 00000000000000..3c6e018d7c27e5 --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerScrollable.tsx @@ -0,0 +1,55 @@ +import * as React from 'react'; +import Avatar from '@mui/joy/Avatar'; +import Box from '@mui/joy/Box'; +import Button from '@mui/joy/Button'; +import Drawer from '@mui/joy/Drawer'; +import DialogTitle from '@mui/joy/DialogTitle'; +import DialogContent from '@mui/joy/DialogContent'; +import List from '@mui/joy/List'; +import ListItem from '@mui/joy/ListItem'; +import ListItemButton from '@mui/joy/ListItemButton'; +import Typography from '@mui/joy/Typography'; +import ModalClose from '@mui/joy/ModalClose'; + +export default function DrawerScrollable() { + const [open, setOpen] = React.useState(false); + + return ( + + + setOpen(false)}> + + Title + + + {[...new Array(100)].map((_, index) => ( + + setOpen(false)}> + Item {index} + + + ))} + + + + +
+ Username + joined 20 Jun 2023 +
+
+
+
+ ); +} diff --git a/docs/data/joy/components/drawer/DrawerSize.js b/docs/data/joy/components/drawer/DrawerSize.js new file mode 100644 index 00000000000000..a7afeef26c2ead --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerSize.js @@ -0,0 +1,71 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; +import Drawer from '@mui/joy/Drawer'; +import ButtonGroup from '@mui/joy/ButtonGroup'; +import Button from '@mui/joy/Button'; +import List from '@mui/joy/List'; +import Divider from '@mui/joy/Divider'; +import ListItem from '@mui/joy/ListItem'; +import ListItemButton from '@mui/joy/ListItemButton'; + +export default function DrawerSize() { + const [state, setState] = React.useState({ + sm: false, + md: false, + lg: false, + }); + + const toggleDrawer = (size, open) => (event) => { + if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) { + return; + } + + setState({ ...state, [size]: open }); + }; + + const list = (size) => ( + + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text) => ( + + {text} + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text) => ( + + {text} + + ))} + + + ); + + return ( + + + {['sm', 'md', 'lg'].map((size) => ( + + ))} + + {['sm', 'md', 'lg'].map((size) => ( + + {list(size)} + + ))} + + ); +} diff --git a/docs/data/joy/components/drawer/DrawerSize.tsx b/docs/data/joy/components/drawer/DrawerSize.tsx new file mode 100644 index 00000000000000..b111a0f6040471 --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerSize.tsx @@ -0,0 +1,79 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; +import Drawer from '@mui/joy/Drawer'; +import ButtonGroup from '@mui/joy/ButtonGroup'; +import Button from '@mui/joy/Button'; +import List from '@mui/joy/List'; +import Divider from '@mui/joy/Divider'; +import ListItem from '@mui/joy/ListItem'; +import ListItemButton from '@mui/joy/ListItemButton'; + +type Size = 'sm' | 'md' | 'lg'; + +export default function DrawerSize() { + const [state, setState] = React.useState({ + sm: false, + md: false, + lg: false, + }); + + const toggleDrawer = + (size: Size, open: boolean) => + (event: React.KeyboardEvent | React.MouseEvent) => { + if ( + event.type === 'keydown' && + ((event as React.KeyboardEvent).key === 'Tab' || + (event as React.KeyboardEvent).key === 'Shift') + ) { + return; + } + + setState({ ...state, [size]: open }); + }; + + const list = (size: Size) => ( + + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text) => ( + + {text} + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text) => ( + + {text} + + ))} + + + ); + + return ( + + + {(['sm', 'md', 'lg'] as const).map((size) => ( + + ))} + + {(['sm', 'md', 'lg'] as const).map((size) => ( + + {list(size)} + + ))} + + ); +} diff --git a/docs/data/joy/components/drawer/DrawerTransition.js b/docs/data/joy/components/drawer/DrawerTransition.js new file mode 100644 index 00000000000000..5c1f113f86aebd --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerTransition.js @@ -0,0 +1,48 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; +import Drawer from '@mui/joy/Drawer'; +import Button from '@mui/joy/Button'; +import List from '@mui/joy/List'; +import Divider from '@mui/joy/Divider'; +import ListItem from '@mui/joy/ListItem'; +import ListItemButton from '@mui/joy/ListItemButton'; + +export default function DrawerTransition() { + const [open, setOpen] = React.useState(false); + + return ( + + + setOpen(false)} + sx={{ + '--Drawer-transitionDuration': open ? '0.4s' : '0.2s', + '--Drawer-transitionFunction': open + ? 'cubic-bezier(0.79,0.14,0.15,0.86)' + : 'cubic-bezier(0.77,0,0.18,1)', + }} + > + + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text) => ( + + {text} + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text) => ( + + {text} + + ))} + + + + + ); +} diff --git a/docs/data/joy/components/drawer/DrawerTransition.tsx b/docs/data/joy/components/drawer/DrawerTransition.tsx new file mode 100644 index 00000000000000..5c1f113f86aebd --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerTransition.tsx @@ -0,0 +1,48 @@ +import * as React from 'react'; +import Box from '@mui/joy/Box'; +import Drawer from '@mui/joy/Drawer'; +import Button from '@mui/joy/Button'; +import List from '@mui/joy/List'; +import Divider from '@mui/joy/Divider'; +import ListItem from '@mui/joy/ListItem'; +import ListItemButton from '@mui/joy/ListItemButton'; + +export default function DrawerTransition() { + const [open, setOpen] = React.useState(false); + + return ( + + + setOpen(false)} + sx={{ + '--Drawer-transitionDuration': open ? '0.4s' : '0.2s', + '--Drawer-transitionFunction': open + ? 'cubic-bezier(0.79,0.14,0.15,0.86)' + : 'cubic-bezier(0.77,0,0.18,1)', + }} + > + + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text) => ( + + {text} + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text) => ( + + {text} + + ))} + + + + + ); +} diff --git a/docs/data/joy/components/drawer/DrawerUsage.js b/docs/data/joy/components/drawer/DrawerUsage.js new file mode 100644 index 00000000000000..36060dacf1556b --- /dev/null +++ b/docs/data/joy/components/drawer/DrawerUsage.js @@ -0,0 +1,121 @@ +import * as React from 'react'; +import Button from '@mui/joy/Button'; +import Box from '@mui/joy/Box'; +import Drawer from '@mui/joy/Drawer'; +import ButtonGroup from '@mui/joy/ButtonGroup'; +import List from '@mui/joy/List'; +import Divider from '@mui/joy/Divider'; +import ListItem from '@mui/joy/ListItem'; +import ListItemButton from '@mui/joy/ListItemButton'; +import ListItemDecorator from '@mui/joy/ListItemDecorator'; +import ListItemContent from '@mui/joy/ListItemContent'; +import InboxIcon from '@mui/icons-material/MoveToInbox'; +import MailIcon from '@mui/icons-material/Mail'; +import JoyUsageDemo from 'docs/src/modules/components/JoyUsageDemo'; + +function Demo(props) { + const [state, setState] = React.useState({ + top: false, + left: false, + bottom: false, + right: false, + }); + + const toggleDrawer = (anchor, open) => (event) => { + if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) { + return; + } + + setState({ ...state, [anchor]: open }); + }; + + const list = (anchor) => ( + + + {['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => ( + + + + {index % 2 === 0 ? : } + + {text} + + + ))} + + + + {['All mail', 'Trash', 'Spam'].map((text, index) => ( + + + + {index % 2 === 0 ? : } + + {text} + + + ))} + + + ); + + return ( + + + {['left', 'right', 'top', 'bottom'].map((anchor) => ( + + ))} + + {['left', 'right', 'top', 'bottom'].map((anchor) => ( + + {list(anchor)} + + ))} + + ); +} + +export default function DrawerUsage() { + return ( + } + /> + ); +} diff --git a/docs/data/joy/components/drawer/drawer.md b/docs/data/joy/components/drawer/drawer.md index 0c1e6dcb852e3c..59ef89646a1d99 100644 --- a/docs/data/joy/components/drawer/drawer.md +++ b/docs/data/joy/components/drawer/drawer.md @@ -1,6 +1,7 @@ --- productId: joy-ui title: React Drawer component +components: Drawer githubLabel: 'component: drawer' --- @@ -8,19 +9,62 @@ githubLabel: 'component: drawer'

Navigation drawers provide quick access to other destinations in your app without removing the user out of context.

-:::info -The Joy UI Drawer component is still in development. -If you're in need of it, please upvote [**this GitHub issue**](https://github.com/mui/material-ui/issues/36292) to help us prioritize the next batch of new components. -::: +## Introduction -## Using the Modal component +{{"demo": "DrawerUsage.js", "hideToolbar": true, "bg": "gradient"}} -In the meantime, you can build your own Drawer using Joy UI's [Modal](/joy-ui/react-modal/) and [Sheet](/joy-ui/react-sheet/) components as a starting point. -The demo below shows how to do it. +## Basic - +The navigation drawers can toggle open or closed. Closed by default, the drawer opens temporarily above all other content until a section is selected. + +The Drawer can be cancelled by clicking the overlay or pressing the Esc key. +It closes when an item is selected, handled by controlling the `open` prop. + +{{"demo": "DrawerBasic.js"}} + +## Customization + +### Anchor + +You can use the `anchor` prop for specifying where the drawer should appear from. + +{{"demo": "DrawerAnchor.js"}} + +### Close button + +Use the ModalClose component to add a close button to the drawer. + +{{"demo": "DrawerCloseButton.js"}} + +### Size + +The `size` prop allows you to adjust the size of the drawer. + +{{"demo": "DrawerSize.js"}} + +### Transition + +Set these CSS variables to the `sx` prop to the transition of the drawer: + +- `--Drawer-transitionFunction`: the [transition function](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-timing-function), default is `ease`. +- `--Drawer-transitionDuration`: the [duration of the transition](https://developer.mozilla.org/en-US/docs/Web/CSS/transition-duration), default is `0.3s`. + +{{"demo": "DrawerTransition.js"}} + +### Scrollable content + +Use the DialogContent component to create a scrollable content inside the drawer. + +{{"demo": "DrawerScrollable.js"}} + +## Common examples + +### Mobile navigation + +{{"demo": "DrawerMobileNavigation.js"}} + +### Filter drawer + +To create an inset panel, set the background and padding of the Drawer's content slot and then create a full height wrapper using [Sheet](/joy-ui/react-sheet/) or Box component. + +{{"demo": "DrawerFilters.js"}} diff --git a/docs/data/joy/pages.ts b/docs/data/joy/pages.ts index dd12a9c6d097b8..93ea42e037a70a 100644 --- a/docs/data/joy/pages.ts +++ b/docs/data/joy/pages.ts @@ -90,7 +90,7 @@ const pages: readonly MuiPage[] = [ subheader: 'navigation', children: [ { pathname: '/joy-ui/react-breadcrumbs' }, - { pathname: '/joy-ui/react-drawer', planned: true }, + { pathname: '/joy-ui/react-drawer', newFeature: true }, { pathname: '/joy-ui/react-link' }, { pathname: '/joy-ui/react-menu' }, { pathname: '/joy-ui/react-tabs' }, diff --git a/docs/data/joy/pagesApi.js b/docs/data/joy/pagesApi.js index 781a7501e2c445..792274682953a3 100644 --- a/docs/data/joy/pagesApi.js +++ b/docs/data/joy/pagesApi.js @@ -28,6 +28,7 @@ module.exports = [ { pathname: '/joy-ui/api/dialog-content' }, { pathname: '/joy-ui/api/dialog-title' }, { pathname: '/joy-ui/api/divider' }, + { pathname: '/joy-ui/api/drawer' }, { pathname: '/joy-ui/api/form-control' }, { pathname: '/joy-ui/api/form-helper-text' }, { pathname: '/joy-ui/api/form-label' }, diff --git a/docs/pages/base-ui/api/use-modal.json b/docs/pages/base-ui/api/use-modal.json index d7fe1e226f3af0..494620611cab21 100644 --- a/docs/pages/base-ui/api/use-modal.json +++ b/docs/pages/base-ui/api/use-modal.json @@ -1,7 +1,10 @@ { "parameters": { "children": { - "type": { "name": "React.ReactElement", "description": "React.ReactElement" }, + "type": { + "name": "React.ReactElement | undefined | null", + "description": "React.ReactElement | undefined | null" + }, "required": true }, "open": { "type": { "name": "boolean", "description": "boolean" }, "required": true }, diff --git a/docs/pages/joy-ui/api/drawer.js b/docs/pages/joy-ui/api/drawer.js new file mode 100644 index 00000000000000..6b3204d9cb678d --- /dev/null +++ b/docs/pages/joy-ui/api/drawer.js @@ -0,0 +1,19 @@ +import * as React from 'react'; +import ApiPage from 'docs/src/modules/components/ApiPage'; +import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations'; +import jsonPageContent from './drawer.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ; +} + +Page.getInitialProps = () => { + const req = require.context('docs/translations/api-docs-joy/drawer', false, /drawer.*.json$/); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/joy-ui/api/drawer.json b/docs/pages/joy-ui/api/drawer.json new file mode 100644 index 00000000000000..a65854716887fd --- /dev/null +++ b/docs/pages/joy-ui/api/drawer.json @@ -0,0 +1,115 @@ +{ + "props": { + "open": { "type": { "name": "bool" }, "required": true }, + "anchor": { + "type": { + "name": "enum", + "description": "'bottom'
| 'left'
| 'right'
| 'top'" + }, + "default": "'left'" + }, + "color": { + "type": { + "name": "enum", + "description": "'danger'
| 'neutral'
| 'primary'
| 'success'
| 'warning'" + }, + "default": "'neutral'", + "additionalInfo": { "joy-color": true } + }, + "component": { "type": { "name": "elementType" } }, + "container": { "type": { "name": "union", "description": "HTML element
| func" } }, + "disableAutoFocus": { "type": { "name": "bool" }, "default": "false" }, + "disableEnforceFocus": { "type": { "name": "bool" }, "default": "false" }, + "disableEscapeKeyDown": { "type": { "name": "bool" }, "default": "false" }, + "disablePortal": { "type": { "name": "bool" }, "default": "false" }, + "disableRestoreFocus": { "type": { "name": "bool" }, "default": "false" }, + "disableScrollLock": { "type": { "name": "bool" }, "default": "false" }, + "hideBackdrop": { "type": { "name": "bool" }, "default": "false" }, + "invertedColors": { "type": { "name": "bool" }, "default": "false" }, + "onClose": { + "type": { "name": "func" }, + "signature": { + "type": "function(event: object, reason: string) => void", + "describedArgs": ["event", "reason"] + } + }, + "size": { + "type": { "name": "enum", "description": "'sm'
| 'md'
| 'lg'" }, + "default": "'md'", + "additionalInfo": { "joy-size": true } + }, + "slotProps": { + "type": { + "name": "shape", + "description": "{ backdrop?: func
| object, content?: func
| object, root?: func
| object }" + }, + "default": "{}" + }, + "slots": { + "type": { + "name": "shape", + "description": "{ backdrop?: elementType, content?: elementType, root?: elementType }" + }, + "default": "{}", + "additionalInfo": { "slotsApi": true } + }, + "variant": { + "type": { + "name": "enum", + "description": "'outlined'
| 'plain'
| 'soft'
| 'solid'" + }, + "default": "'plain'", + "additionalInfo": { "joy-variant": true } + } + }, + "name": "Drawer", + "imports": ["import Drawer from '@mui/joy/Drawer';", "import { Drawer } from '@mui/joy';"], + "styles": { "classes": [], "globalClasses": {}, "name": "MuiDrawer" }, + "slots": [ + { + "name": "root", + "description": "The component that renders the root.", + "default": "'div'", + "class": ".MuiDrawer-root" + }, + { + "name": "backdrop", + "description": "The component that renders the backdrop.", + "default": "'div'", + "class": ".MuiDrawer-backdrop" + }, + { + "name": "content", + "description": "The component that renders the content of the drawer.", + "default": "'div'", + "class": ".MuiDrawer-content" + } + ], + "classes": { + "classes": [ + "colorContext", + "colorDanger", + "colorNeutral", + "colorPrimary", + "colorSuccess", + "colorWarning", + "hidden", + "sizeLg", + "sizeMd", + "sizeSm", + "variantOutlined", + "variantPlain", + "variantSoft", + "variantSolid" + ], + "globalClasses": {} + }, + "spread": true, + "themeDefaultProps": false, + "muiName": "JoyDrawer", + "forwardsRefTo": "HTMLDivElement", + "filename": "/packages/mui-joy/src/Drawer/Drawer.tsx", + "inheritance": null, + "demos": "", + "cssComponent": false +} diff --git a/docs/pages/joy-ui/api/modal.json b/docs/pages/joy-ui/api/modal.json index 338a0614101f6b..dd9f011204cad4 100644 --- a/docs/pages/joy-ui/api/modal.json +++ b/docs/pages/joy-ui/api/modal.json @@ -41,7 +41,7 @@ }, "name": "Modal", "imports": ["import Modal from '@mui/joy/Modal';", "import { Modal } from '@mui/joy';"], - "styles": { "classes": ["root", "backdrop"], "globalClasses": {}, "name": "MuiModal" }, + "styles": { "classes": [], "globalClasses": {}, "name": "MuiModal" }, "slots": [ { "name": "root", @@ -56,6 +56,7 @@ "class": ".MuiModal-backdrop" } ], + "classes": { "classes": ["hidden"], "globalClasses": {} }, "spread": true, "themeDefaultProps": false, "muiName": "JoyModal", diff --git a/docs/src/modules/components/DemoSandbox.js b/docs/src/modules/components/DemoSandbox.js index a5305fb8e95476..b3ad8bb8187e95 100644 --- a/docs/src/modules/components/DemoSandbox.js +++ b/docs/src/modules/components/DemoSandbox.js @@ -9,6 +9,7 @@ import createCache from '@emotion/cache'; import { CacheProvider } from '@emotion/react'; import { StyleSheetManager } from 'styled-components'; import { jssPreset, StylesProvider } from '@mui/styles'; +import { CssVarsProvider, extendTheme } from '@mui/joy/styles'; import { useTheme, styled, createTheme, ThemeProvider } from '@mui/material/styles'; import rtl from 'jss-rtl'; import DemoErrorBoundary from 'docs/src/modules/components/DemoErrorBoundary'; @@ -16,8 +17,12 @@ import { useTranslate } from 'docs/src/modules/utils/i18n'; import { getDesignTokens } from 'docs/src/modules/brandingTheme'; import { highDensity } from 'docs/src/modules/components/ThemeContext'; +const iframeDefaultJoyTheme = extendTheme({ + cssVarPrefix: 'demo-iframe', +}); + function FramedDemo(props) { - const { children, document } = props; + const { children, document, productId } = props; const theme = useTheme(); React.useEffect(() => { @@ -47,6 +52,16 @@ function FramedDemo(props) { const getWindow = React.useCallback(() => document.defaultView, [document]); + const Wrapper = productId === 'joy-ui' ? CssVarsProvider : React.Fragment; + const wrapperProps = + productId === 'joy-ui' + ? { + documentNode: document, + colorSchemeNode: document.documentElement, + theme: iframeDefaultJoyTheme, + } + : {}; + return ( - {React.cloneElement(children, { - window: getWindow, - })} + + {React.cloneElement(children, { + window: getWindow, + })} + @@ -65,6 +82,7 @@ function FramedDemo(props) { FramedDemo.propTypes = { children: PropTypes.node, document: PropTypes.object.isRequired, + productId: PropTypes.string, }; const Iframe = styled('iframe')(({ theme }) => ({ @@ -72,11 +90,11 @@ const Iframe = styled('iframe')(({ theme }) => ({ flexGrow: 1, height: 400, border: 0, - boxShadow: (theme.vars || theme).shadows[1], + boxShadow: (theme.vars || theme)?.shadows?.[1], })); function DemoIframe(props) { - const { children, name, ...other } = props; + const { children, name, productId, ...other } = props; /** * @type {import('react').Ref} */ @@ -106,7 +124,9 @@ function DemoIframe(props) {