Closed umar-khan closed 6 years ago
Does this help?
import React from "react";
const styles = {
position: "relative",
zIndex: 1301,
backgroundColor: "red"
};
const Navbar = () => (
<div style={styles}>Navbar: this should not be covered</div>
);
export default Navbar;
The default zIndex
provided to Drawer
component is 1200
, you can over ride it like that. :point_up:
@adeelibr Thanks for the suggestion!
That should work for the simplified example I provided, but in my actual use-case, there are several more components (eg. a sidebar) that I don't want to be covered.
I don't think it's a maintainable solution to hard-code component z-index values whenever I want a contained drawer.
I was hoping there was a way I can use the CSS API or classes to pass down styles Drawer
-> Modal
-> Backdrop
Can you provide the use case example via a code pen & i'll look into it.
Here's a sandbox which more accurately represents my use-case: CodeSandbox
I want the drawer and backdrop to only overlay the orange
section.
Preferably without having to hard-code z-index values for the other elements.
I don't see any other way other then provide a zIndex with a relative position. https://codesandbox.io/s/9jw7y3q00p
Hmm not the solution I was hoping for, but I don't see a straight-forward alternative either.
Thanks!
I have the same requirement. Can we reopen this as a feature request?
For anyone who stumbles on this page looking for an answer. I managed to do it by setting the modal container, then specifying the css positioning on the modal root, backdrop and docked drawer to absolute
:
<Drawer
variant="temporary"
open={this.props.showDrawer}
anchor="left"
onClose={this.props.toggleDrawer}
ModalProps={this.ref.current ? {container: this.ref.current} : {}}
classes={{
paperAnchorLeft: "class1",
modal: "class2"
}}
BackdropProps={{
className: "class3"
}}
>
For anyone that still wonders about the entire solution, the following worked for me.
Please note that I used document.getElementById
here and that requires that you set the ID property to that value if you want the drawer to be contained in that element. It should also be possible to use a ref
in this case, and that is preferred in my opinion.
Step 1: Set an ID attribute for the element that you want to contain the drawer elements
<div id="drawer-container" style="position: relative">
<span>Some elements</span>
</div>
Make sure that you add position: relative
to this element.
Step 2: Set correct styling and reference to container element on Drawer
element.
<Drawer
open={true}
onClose={() => {}}
PaperProps={{ style: { position: 'absolute' } }}
BackdropProps={{ style: { position: 'absolute' } }}
ModalProps={{
container: document.getElementById('drawer-container'),
style: { position: 'absolute' }
}}
variant="temporary"
>
<span>Some elements</span>
</Drawer>
The things to take away from the example above are the PaperProps
, BackdropProps
and the ModalProps
props. For them to be contained within the container element that we created in step one, these elements have to be absolutely positioned.
Also note the document.getElementById
to get a reference to the container element.
Hi ! I tried the example above, but there is this error : "document is not defined". Do you have any information about this. How should I define it ? Thanks in advance
I have the same requirement. Can we reopen this as a feature request?
Hi thanks, it worked! But when I try to set the anchor to "right" (I need to open the drawer from the right side) it opens weirdly (shaking). Any solution for that?
Hi thanks, it worked! But when I try to set the anchor to "right" (I need to open the drawer from the right side) it opens weirdly (shaking). Any solution for that?
variant="persistent"
on Drawer worked for me.
variant="persistent"
on Drawer worked for me.
Me too! Thanks @kelseyleftwich !!!
@LeroyDoornebal
thank you so much it worked
@LeroyDoornebal Although it's working fine for the most part. The only problem is with slide exit animation, the drawer slides out of the container it is contained in and goes off-screen. Whereas the expected behavior should be that the drawer should end slide exit animation inside the container it is in. Can you help me fix it?
@kelseyleftwich While this fixed the weird behavior, it also removed the backdrop which I need.
@sandeepgahlawat I'm also with the same issue as you. Did you find any solution for this?
@giovanniantonaccio yes, I did figure out a way to fix it, and below is what I did. In case you still have doubts feel free to ask.
<Box
width="100%"
height="100%"
id="drawer-container"
position="relative"
bgcolor="white"
component="div"
style={{ overflowY: "scroll", overflowX: "hidden" }}
>
<Drawer
open={props.openDrawer}
onClose={() => {}}
elevation={5}
PaperProps={{ style: { position: "absolute", width: "486px" } }}
BackdropProps={{ style: { position: "absolute" } }}
ModalProps={{
container: document.getElementById("drawer-container"),
style: { position: "absolute" },
}}
SlideProps={{
onExiting: (node) => {
node.style.webkitTransform = "scaleX(0)";
node.style.transform = "scaleX(0)";
node.style.transformOrigin = "top left ";
},
}}
>
<DrawerContent />
</Drawer>
</Box>
Thanks @sandeepgahlawat! Is solved the problem!
The solution described by @LeroyDoornebal doesn't seem to work.
Here's a code sandbox. The drawer still covers the whole page. Any advice about what I'm doing wrong?
Edit: figured it out. The drawer container needs to be visible and take up space (e.g., by setting a width and height). I've updated the code sandbox to demonstrate.
Edit 2: OK, that doesn't quite do it either. The drawer still covers the whole page upon loading the sandbox, but then moves into the correct position upon re-rendering, when the code is changed.
Edit 3: After some experimentation, it seems to work if the drawer variant is "permanent", and fail (but then fix itself after re-rendering) when "temporary".
The solution described by @LeroyDoornebal doesn't seem to work.
Here's a code sandbox. The drawer still covers the whole page. Any advice about what I'm doing wrong?
Edit: figured it out. The drawer container needs to be visible and take up space (e.g., by setting a width and height). I've updated the code sandbox to demonstrate.
Edit 2: OK, that doesn't quite do it either. The drawer still covers the whole page upon loading the sandbox, but then moves into the correct position upon re-rendering, when the code is changed.
Edit 3: After some experimentation, it seems to work if the drawer variant is "permanent", and fail (but then fix itself after re-rendering) when "temporary".
just remove variant prop and it will work perfectly
@sandeepgahlawat removing the prop will default to "temporary". https://material-ui.com/api/drawer/
It also doesn't seem to work properly with an anchor='bottom'. I modified @frankpape 's example to use a button trigger with setState
, and you can see the behavior isn't working as intended (100px blue box sliding up from the green box, and disappearing into the white on toggle off).
Can someone help me with this problem: I used similar approach but with a element higher than the screen height.
https://codesandbox.io/s/material-demo-forked-x8wi2?file=/demo.js
Works fine in Firefox. But in Chrome and Edge the browser scrolls to the top of the drawer. How can I prevent the scrolling?
@mmartinsky I have a similar problem to yours. Did you manage to fix it?
Hi ! I tried the example above, but there is this error : "document is not defined". Do you have any information about this. How should I define it ? Thanks in advance
@DorineLam I was getting the same error, so I scrapped that idea and instead did this:
<Drawer
ModalProps={{
style: {
position: 'relative',
top: 'unset',
left: 'unset',
right: 'unset',
}
}}
variant="permanent"
>
<span>Some elements</span>
</Drawer>
The Drawer desperately wants to cling to one of the outer edges of the screen, so unsetting all the absolute positioning fixed that problem for me. Now I can put this drawer inside any element I want within my app.
Oh Irony, I never got notified for updates on this issue and stumbled into the problems with my implementation myself and found my way back to this thread. In the mean time a lot of updates have been done to material ui, so probably this problem has already been solved, but for people that are still stuck on older versions, I managed to implement it slightly different to get around the weird animations when you change the anchor.
<Box style={{ overflow: 'hidden' }}>
<Backdrop open={open} style={{ zIndex: 1199, position: 'absolute' }} />
<MuiDrawer
anchor={fromSide}
open={open}
onClose={onClose}
PaperProps={{
style: {
width: width || '90%',
position: 'absolute',
maxWidth: maxWidth || 'initial',
border: 'none'
}
}}
ModalProps={{
container: document.getElementById('some-id`), // Or ref ofcourse :)
disableEnforceFocus: true,
style: { position: 'absolute' }
}}
variant="persistent"
{...props}
>
{children}
</MuiDrawer>
</Box>
The biggest issue I had with using the "permanent" variant is that it did not work out of the box with the backdrop, so I separated that by using the backdrop directly. Be aware that this implementation worked for my specific use case, maybe it gives you some ideas.
Hi thanks, it worked! But when I try to set the anchor to "right" (I need to open the drawer from the right side) it opens weirdly (shaking). Any solution for that?
@neomib did you ever figure out a way around this? This "shaking" effect is the only thing keeping this from working for me.
I'd also be interested in an officially supported way to put drawers in other components (we're using them in cards); we've bashed something together along the lines of @LeroyDoornebal's solution, which avoids the shaky animations but still gives odd animation behaviour (looks more like a fade sometimes, can't get any to slide both in and out correctly).
@elyobo how did you avoid the shaky animations? Is your drawer temporary and coming from the right?
No, using Leroy's approach above which uses a persistent drawer and a manually added backdrop. I have one coming from the left (not shaky, but seems to fade in then slide out on close) and one from the bottom (slides in but then seems to fade out on close).
@elyobo Do you mind sharing how you got the drawer to live inside the MUI Card. I can't seem to get the drawer to grab onto the card's id to use it as a container.
Here's the gist of our CardDrawer
which is used inside various Card
components. Not everything will apply (e.g. theme overrides, assumption of an app bar at the top of the card) and I don't recall exactly what was done or why at this point, and (as noted) it's far from perfect either, animations are still a bit weird.
We also haven't upgraded to v5 yet, nor even looked at the upgrade path, so YMMV.
import { useRef } from 'react'
import PropTypes from 'prop-types'
import rgba from 'hex-to-rgba'
import Backdrop from '@material-ui/core/Backdrop'
import Drawer from '@material-ui/core/Drawer'
import { makeStyles, withStyles } from '@material-ui/core/styles'
const useStyles = makeStyles(theme => ({
root: {
top: 0,
left: 0,
right: 0,
bottom: 0,
pointerEvents: 'none',
position: 'absolute',
overflow: 'hidden',
},
backdrop: {
position: ['absolute', '!important'],
backgroundColor: rgba(theme.palette.background.default, 0.67),
pointerEvents: 'auto',
zIndex: [theme.zIndex.appBar - 11, '!important'],
},
modal: {
position: ['absolute', '!important'],
zIndex: [theme.zIndex.appBar - 1, '!important'],
},
}))
const bottomAnchorStyle = ({ fullHeight }) => (
fullHeight
? { height: '100%' }
// Note: maxHeight ensures a clickable backdrop to close the drawer
: { borderTop: 'none', maxHeight: '90%' }
)
const StyledDrawer = withStyles(theme => ({
paper: {
zIndex: theme.zIndex.appBar - 10,
position: 'absolute',
pointerEvents: 'all',
},
paperAnchorLeft: ({ width }) => ({ width }),
paperAnchorRight: ({ width }) => ({ width }),
paperAnchorBottom: bottomAnchorStyle,
paperAnchorDockedLeft: ({ width }) => ({ width }),
paperAnchorDockedRight: ({ width }) => ({ width }),
paperAnchorDockedBottom: bottomAnchorStyle,
}))(({ fullHeight, width, ...props }) => <Drawer {...props} />)
const CardDrawer = ({
children,
ModalProps,
...props
}) => {
const rootRef = useRef(null)
const classes = useStyles()
const { onClose, open } = props
return (
<>
<div
ref={rootRef}
className={classes.root}
/>
<Backdrop
onClick={onClose}
open={open}
className={classes.backdrop}
/>
<StyledDrawer
ModalProps={{
...ModalProps,
className: classes.modal,
container: () => rootRef.current,
disableEnforceFocus: true,
keepMounted: true,
}}
{...props}
variant="persistent"
>
{children}
</StyledDrawer>
</>
)
}
CardDrawer.propTypes = {
children: PropTypes.node,
fullHeight: PropTypes.bool,
ModalProps: PropTypes.shape({}),
onClose: PropTypes.func,
open: PropTypes.bool.isRequired,
width: PropTypes.oneOfType([
PropTypes.number,
PropTypes.string,
]),
}
CardDrawer.defaultProps = {
children: undefined,
fullHeight: false,
ModalProps: {},
onClose: undefined,
width: 320,
}
export default CardDrawer
@giovanniantonaccio yes, I did figure out a way to fix it, and below is what I did. In case you still have doubts feel free to ask.
<Box width="100%" height="100%" id="drawer-container" position="relative" bgcolor="white" component="div" style={{ overflowY: "scroll", overflowX: "hidden" }} > <Drawer open={props.openDrawer} onClose={() => {}} elevation={5} PaperProps={{ style: { position: "absolute", width: "486px" } }} BackdropProps={{ style: { position: "absolute" } }} ModalProps={{ container: document.getElementById("drawer-container"), style: { position: "absolute" }, }} SlideProps={{ onExiting: (node) => { node.style.webkitTransform = "scaleX(0)"; node.style.transform = "scaleX(0)"; node.style.transformOrigin = "top left "; }, }} > <DrawerContent /> </Drawer> </Box>
Have anyone gotten this solution to work on NextJS (SSR) with using document.
i am trying to position a backdrop absolutely like in the examples above, just i am using Popovers as modal element.
The problem i have now is that popovers are not correctly positioned, because their position seems to be calculated from the visible area and not from the parent component coordinates. How can i fix this?
Is it possible though to have a swipeable (from bottom) drawer inside a fixed width and height container ? Basically the drawer should be triggered from bottom and it should not hover nor footer nor header and actionable only from page content.
After a lot of trial and error, finally got rid of the shaky animation (where the outer container itself seemed to be sliding up/down as well) by adding the line keepMounted: true
to ModalProps. It brings completely smooth animation to the drawer. Thanks @elyobo !!
Final code:
<Drawer className="card-drawer"
open={showDrawer}
anchor={"bottom"}
onClose={() => {
closeDrawer()
}}
PaperProps={{ style: { position: 'absolute' } }}
BackdropProps={{ style: { position: 'absolute' } }}
ModalProps={{
container: document.getElementById('container-drawer-is-within'),
style: { position: 'absolute' },
keepMounted: true, // <=============== THIS
}}
variant="temporary"
>
For some weirdly odd reason, i was mounting one of the drawers on open, which wasn't affected by shaking animation, the other drawer which was written like normal code suffered the shakiness.
it feels like a hack, but here is what I did..
const [open, setOpen] = useState(...) // usual stuff.
// render
{open && ( // <---- this trick removed the shakiness
<Drawer
open={open}
... the modal props with container ref and style as suggested
)}
Another AI suggestion that worked perfectly was to just disable Sliding on the Drawer using:
SlideProps={{ timeout: { enter: 0, exit: 0 } }}
Here's How You can implement in Typescript using React Hooks,
const containerRef = useRef<Element | (() => Element | null) | null | undefined>();
<Container className={contentStyle.container} ref={(node) => { containerRef.current = node; }} sx={{ '&.MuiContainer-root': { 'paddingLeft': 'unset', 'paddingRight': 'unset' } }}>
`<Drawer
anchor={'right'}
open={_overlayContext?.overlay?.show}
onClose={toggleOverlayDrawer('right', false)}
PaperProps={{ style: { position: 'absolute' } }}
slotProps={{ backdrop: { style: { position: 'absolute' } } }}
ModalProps={{
container: containerRef.current ? containerRef.current : null,
style: { position: 'absolute', zIndex: 1400 },
keepMounted: true,
}}
variant="temporary"
`
Hi thanks, it worked! But when I try to set the anchor to "right" (I need to open the drawer from the right side) it opens weirdly (shaking). Any solution for that?
Late to the party, although variant='persistent'
fixes the glitch for the regular Drawer
, it also disables the swipe feature on mobile for the SwipeableDrawer
, so the workaround that I found is to add the onEnter
function, which forces the drawer to start from the right.
Note: the reason behind node.clientWidth - 1
is that in the code they use <
>
comparisons so it cannot be strictly equal.
Note2: Replacing container: ...
by container: () => { ... }
will fix the error ReferenceError: document is not defined
const SwipeableDrawerStyled = emotionStyled(SwipeableDrawer)`
&.${drawerClasses.root} {
position: absolute;
overflow: hidden;
width: 100%;
height: 100%;
}
.${paperClasses.root} {
position: absolute;
width: 100%;
padding: ${PAGE_GUTTER};
::-webkit-scrollbar {
display: none;
}
}
`;
<SwipeableDrawerStyled
anchor='right'
open={isOpen}
hideBackdrop
elevation={0}
onClose={toggleDrawer}
onOpen={toggleDrawer}
ModalProps={{
// Making the container a function fixes this issue: "ReferenceError: document is not defined"
container: () => document.getElementById('root-wrapper'),
}}
SlideProps={{
onEnter(node) {
// This fixes the issue with the drawer glitching when drawer anchor is "right"
// Known issue: https://github.com/mui/material-ui/issues/11749#issuecomment-518521194
// variant='persistent' fixes the glitch issue however it disables the swipeable feature on mobile
node.style.transform = `translateX(${node.clientWidth - 1}px)`;
},
}}
For anyone that still wonders about the entire solution, the following worked for me.
Please note that I used
document.getElementById
here and that requires that you set the ID property to that value if you want the drawer to be contained in that element. It should also be possible to use aref
in this case, and that is preferred in my opinion.Step 1: Set an ID attribute for the element that you want to contain the drawer elements
<div id="drawer-container" style="position: relative"> <span>Some elements</span> </div>
Make sure that you add
position: relative
to this element.Step 2: Set correct styling and reference to container element on
Drawer
element.<Drawer open={true} onClose={() => {}} PaperProps={{ style: { position: 'absolute' } }} slotProps={{ backdrop: { style: { position: 'absolute' } } }} // <=============== Fix backdrop by slotProps ModalProps={{ container: document.getElementById('drawer-container'), style: { position: 'absolute' } }} keepMounted: true, // <=============== remove shaky animation variant="temporary" > <span>Some elements</span> </Drawer>
The things to take away from the example above are the
PaperProps
,BackdropProps
and theModalProps
props. For them to be contained within the container element that we created in step one, these elements have to be absolutely positioned.Also note the
document.getElementById
to get a reference to the container element.
I edited this code, it worked for me!
I'm trying to use a "temporary" right Drawer component, and I want the drawer and it's backdrop to be contained within the parent div.
I can't find any working examples of this, and I'm not sure how to target the Backdrop component CSS from my Drawer component.
Expected Behavior
When specifying a
container
prop for my Drawer, I expect that the drawer, and it's backdrop would be enclosed inside that container. Since this is not the case, it's unclear how to override the desired styles.It should function similar to this sidebar example
Current Behavior
The Drawer, Modal, and Backdrop components use
fixed
for their positioning, resulting in them always displaying at the top-right of the viewport.Context
I created a CodeSandbox with an example scenario.
If you click on the "Open Drawer" button,
<DrawerRight>
opens to the right of the viewport as expected. It overlays all other elements, including the<Navbar>
.The goal is to have the drawer open, but only overlay elements in it's containing element.
I attempted to override the styling with classes as shown in the docs
Here is an updated version of the above CodeSandbox with my changes. The main differences are:
<MainContent>
as container prop to<Drawer>
withStyles
to add styles based on<Drawer>
CSS APINote that the drawer now seemed to be sized correctly, but the backdrop is still covering the entire viewport. Also, the animation has become jittery, with the other elements being shifted while the drawer is opening.
Your Environment