ui icon indicating copy to clipboard operation
ui copied to clipboard

Drawer position property missing

Open MilanObrenovic opened this issue 1 year ago • 13 comments

How about having the option to change position of the drawer? I think the drawer is a very cool thing especially with the drag & swipe effect to close it. I think this would be perfect for mobile side navigation bar. So instead of forcing this drawer to be shown from the bottom of the screen, how about using it as a side navbar, where it appears from left side with menu items? This way anyone can swipe to close the navigation on mobile

Example: exactly like this https://mui.com/material-ui/react-drawer/#swipeable

Open this left sidebar in mobile mode and try to swipe it from right to left to close the navigation

MilanObrenovic avatar Jan 28 '24 15:01 MilanObrenovic

it's not messing check this https://codesandbox.io/p/devbox/drawer-direction-right-n338ml?file=%2Fapp%2Fmy-drawer.tsx%3A7%2C29-7%2C34

kal07 avatar Jan 28 '24 17:01 kal07

it's not messing check this https://codesandbox.io/p/devbox/drawer-direction-right-n338ml?file=%2Fapp%2Fmy-drawer.tsx%3A7%2C29-7%2C34

you confused me a bit but yeah, it indeed is missing. What is vaul? you provided an example using some other UI package that is not related to shadcn.

and yeah i just tried that example as well and it still doesnt work. i cannot scroll vertically if there are too many navigation menu items. i can only scroll after adding overflow-y-scroll to Drawer.Content, but then the other serious problem that happens is you can't swipe to close the drawer anymore

MilanObrenovic avatar Jan 28 '24 21:01 MilanObrenovic

vaul is the library that shadcn used to build his drawer I think now I can understand your probleme and I think I can help you if you give me an example in a sandbox

kal07 avatar Jan 28 '24 21:01 kal07

how about using it as a side navbar, where it appears from left side with menu items? This way anyone can swipe to close the navigation on mobile the example that I shared was for this question

kal07 avatar Jan 28 '24 21:01 kal07

vaul is the library that shadcn used to build his drawer I think now I can understand your probleme and I think I can help you if you give me an example in a sandbox

i already gave you an example in the original post. check the MUI library's SwipeableDrawer component. Try to swipe it on mobile from right to left and see how it works perfectly fine. That same drawer is used in this template: https://react-material.fusetheme.com/

Test it on mobile yourself. That is the best UX i have seen yet for the navigation sidebar menu. I want to achieve that same effect with shadcn. Is it possible or not? @shadcn

MilanObrenovic avatar Jan 28 '24 22:01 MilanObrenovic

how about using it as a side navbar, where it appears from left side with menu items? This way anyone can swipe to close the navigation on mobile the example that I shared was for this question

correct. this is what shadcn's drawer can also do as it turns out, after i modified it a bit. but i forgot to add the part where i need this drawer to have the content scrollable in case there are too many items. but if it gets scrollable then the drag/swipe effect of the drawer no longer works. can you solve this?

MilanObrenovic avatar Jan 28 '24 22:01 MilanObrenovic

@MilanObrenovic

You can use ScrollArea component from @shadcn to have a scroll and here is the demo

import { ScrollArea } from '@/components/ui/scroll-area';

export function DrawerDemo() {
	return (
		<Drawer direction='right'>
			<DrawerTrigger asChild>
				<Button variant='outline'>Open Drawer</Button>
			</DrawerTrigger>
			<DrawerContent className='h-screen top-0 right-0 left-auto mt-0 w-[500px] rounded-none'>
				<ScrollArea className='h-screen'>
					<div className='mx-auto w-full p-5'>
						<DrawerHeader>
							<DrawerTitle>Theme Color Options</DrawerTitle>
							<DrawerDescription>
								* Selected option will be applied to all layout elements (navbar, toolbar, etc.). You can also create your own theme options and color
								schemes.
							</DrawerDescription>
						</DrawerHeader>
						<div className='p-4 pb-0 space-y-4'>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 1</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 2</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 3</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 4</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 4</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 5</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 6</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 7</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 8</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 9</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 10</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 11</p>
							</div>
						</div>
					</div>
				</ScrollArea>
			</DrawerContent>
		</Drawer>
	);
}

If you want to the top bar not to display then you should pass a prop to the DrawerContent and update the component as below

interface DrawerContentProps extends React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content> {
	showBar?: boolean;
}

const DrawerContent = React.forwardRef<React.ElementRef<typeof DrawerPrimitive.Content>, DrawerContentProps>(
	({ className, children, showBar = true, ...props }, ref) => (
		<DrawerPortal>
			<DrawerOverlay />
			<DrawerPrimitive.Content
				ref={ref}
				className={cn('fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background', className)}
				{...props}
			>
				{showBar ? <div className='mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted' /> : null}
				{children}
			</DrawerPrimitive.Content>
		</DrawerPortal>
	),
);
DrawerContent.displayName = 'DrawerContent';

I don't know why the animation is not being recorded properly but it works 😅

https://github.com/shadcn-ui/ui/assets/150527559/10644fa1-1400-48ac-9694-a95d7ffe7796

Here is the repo for the issue fix https://github.com/imopbuilder/shadcn-ui-issue-fixes/blob/main/src/components/pages/client.tsx

imopbuilder avatar Jan 29 '24 04:01 imopbuilder

Thank you @imopbuilder your exemple was exactly what i needed

Last-Autumn-Leaf avatar Jan 30 '24 06:01 Last-Autumn-Leaf

@MilanObrenovic

You can use ScrollArea component from @shadcn to have a scroll and here is the demo

import { ScrollArea } from '@/components/ui/scroll-area';

export function DrawerDemo() {
	return (
		<Drawer direction='right'>
			<DrawerTrigger asChild>
				<Button variant='outline'>Open Drawer</Button>
			</DrawerTrigger>
			<DrawerContent className='h-screen top-0 right-0 left-auto mt-0 w-[500px] rounded-none'>
				<ScrollArea className='h-screen'>
					<div className='mx-auto w-full p-5'>
						<DrawerHeader>
							<DrawerTitle>Theme Color Options</DrawerTitle>
							<DrawerDescription>
								* Selected option will be applied to all layout elements (navbar, toolbar, etc.). You can also create your own theme options and color
								schemes.
							</DrawerDescription>
						</DrawerHeader>
						<div className='p-4 pb-0 space-y-4'>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 1</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 2</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 3</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 4</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 4</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 5</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 6</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 7</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 8</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 9</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 10</p>
							</div>
							<div className='bg-muted flex items-center justify-center rounded-lg h-32'>
								<p>Image 11</p>
							</div>
						</div>
					</div>
				</ScrollArea>
			</DrawerContent>
		</Drawer>
	);
}

If you want to the top bar not to display then you should pass a prop to the DrawerContent and update the component as below

interface DrawerContentProps extends React.ComponentPropsWithoutRef<typeof DrawerPrimitive.Content> {
	showBar?: boolean;
}

const DrawerContent = React.forwardRef<React.ElementRef<typeof DrawerPrimitive.Content>, DrawerContentProps>(
	({ className, children, showBar = true, ...props }, ref) => (
		<DrawerPortal>
			<DrawerOverlay />
			<DrawerPrimitive.Content
				ref={ref}
				className={cn('fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background', className)}
				{...props}
			>
				{showBar ? <div className='mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted' /> : null}
				{children}
			</DrawerPrimitive.Content>
		</DrawerPortal>
	),
);
DrawerContent.displayName = 'DrawerContent';

I don't know why the animation is not being recorded properly but it works 😅

shadcn-ui-issue-.2602-1.mp4 Here is the repo for the issue fix https://github.com/imopbuilder/shadcn-ui-issue-fixes/blob/main/src/components/pages/client.tsx

no, you did NOT fix ANYTHING here. have you even read my post or the reason for the issue i've created?

  1. open the drawer, try to swipe it to close it --> it works
  2. open the drawer, scroll, now try to swipe it --> it does NOT work

especially on mobile.

So I will repeat again:

I want to be able to scroll within the drawer AND ALSO be able to swipe to close it.

MilanObrenovic avatar Jan 30 '24 17:01 MilanObrenovic

@MilanObrenovic

I will try it on mobile and figure out the issue

imopbuilder avatar Jan 31 '24 00:01 imopbuilder

@MilanObrenovic

I will try it on mobile and figure out the issue

i'll illustrate the problem in more details. use this code and test it yourself:

<Drawer direction={"left"}>
  <DrawerTrigger asChild>
    <Button variant="outline">Open Drawer</Button>
  </DrawerTrigger>
  <DrawerContent className="h-full w-[35%]">
    <DrawerHeader className="text-left">
      <DrawerTitle>Citation</DrawerTitle>
      <DrawerDescription>
        Make sure to check if the given answer is align with the original
        source.
      </DrawerDescription>
    </DrawerHeader>
    <Separator />
    <ScrollArea className="overflow-auto p-4 break-all">
      {Array.from({ length: 10000 }, (_, index) => index + 1)}
    </ScrollArea>
    <Separator />
    <DrawerFooter className="pt-2">
      <p className="text-sm italic">
        Thank you for <strong>diligently</strong> double checking!
      </p>
    </DrawerFooter>
  </DrawerContent>
</Drawer>
  1. imagine that you have a left-side navigation menu
  2. open the drawer
  3. swipe from Right to Left => it works
  4. open the drawer again
  5. scroll
  6. swipe from Right to Left => it does NOT work <--- FIX THIS

MilanObrenovic avatar Jan 31 '24 15:01 MilanObrenovic

@MilanObrenovic

I have raised an issue with package vaul, We should wait for the author to resolve

imopbuilder avatar Jan 31 '24 16:01 imopbuilder

@MilanObrenovic

I have raised an issue with package vaul, We should wait for the author to resolve

great, thank you. now we're finally on the same page. i think this problem is due to implementation of the drawer, so only the package maintainer should be able to fix it, since modifying through css, or changing the html structure, does not fix it.

more than likely the scroll area overrides the swipe functionality, so instead of being able to swipe, you can only scroll. this problem has been solved by MUI library, more specifically this https://mui.com/material-ui/react-drawer/#swipeable component, the SwipeableDrawer. Try it on mobile and see how flawlessly you can scroll AND swipe to close the sidebar

MUI has proven it's possible to do it, so it's up to vaul or shadcn to fix this issue as well

MilanObrenovic avatar Jan 31 '24 16:01 MilanObrenovic

@MilanObrenovic ...have you made any progress? Need to do the same thing in my app, you do with your drawer (open from left, close to the left)

jspm2013 avatar Feb 04 '24 16:02 jspm2013

@MilanObrenovic ...have you made any progress? Need to do the same thing in my app, you do with your drawer (open from left, close to the left)

they fixed this bug in the latest update of vaul package, check https://github.com/emilkowalski/vaul/issues/243#issuecomment-1925446818

MilanObrenovic avatar Feb 04 '24 20:02 MilanObrenovic

@MilanObrenovic I'm using Shadcn Drawer component (brand new app), and it appears I still have an issue like this? If I scroll on my phone on a page that has Drawer Triggers, when the drawer opens, I cannot close it by swiping down. Any thoughts?

Tigatok avatar Jul 12 '24 00:07 Tigatok

@MilanObrenovic I think you should work a bit on your communication. This as well as vaul is open source software maintained by people in their free time without getting paid for it. You did not even bother to create a proper reproduction highlighting your issue, and when someone tried to help you instead of starting with a "thank you", you complain about that not immediately solving your issue or understanding you.

Your way is not motivating people to help, I certainly wouldn't bother to provide a technical solution for you with the way you post here. I think this gives a good starting point if you feel like reading further.

matzeso avatar Jul 22 '24 09:07 matzeso

It can be done with the help of props, the list of props can be found here

For me the below code worked. It opens the drawer from the right side of the screen.

<DrawerTrigger asChild>
  <Button variant="outline">Open Drawer</Button>
</DrawerTrigger>
<DrawerContent className="top-0 mt-0 ml-[50%] rounded-t-none rounded-[10px]">
  <div className="mx-auto w-full max-w-sm">
    <DrawerHeader>
      <DrawerTitle>Move Goal</DrawerTitle>
      <DrawerDescription>Set your daily activity goal.</DrawerDescription>
    </DrawerHeader>
    <div className="p-4 pb-0">
      <div className="flex items-center justify-center space-x-2"></div>
      <div className="mt-3 h-[120px]"></div>
    </div>
    <DrawerFooter>
      <Button>Submit</Button>
      <DrawerClose asChild>
        <Button variant="outline">Cancel</Button>
      </DrawerClose>
    </DrawerFooter>
  </div>
</DrawerContent>
</Drawer>

<Drawer direction="right"> opens it from right <DrawerContent className="top-0 mt-0 ml-[50%] rounded-t-none rounded-[10px]"> will make adjustment to it's top and margin on the left, ml-[50%] will make it half of the screen size (horizontally). If you reduce the margin, it'll increase the drawer width. If you update the below marked line directly in the Drawer component file, you'll be able to move the grabber to left and vertically centre

image

Please note: I haven't optimised it for mobile devices (smaller screens). You can play with it to meet your needs.

SaurabhBorse avatar Jul 24 '24 18:07 SaurabhBorse