shadcn-ui / ui

Beautifully designed components that you can copy and paste into your apps. Accessible. Customizable. Open Source.
https://ui.shadcn.com
MIT License
72.52k stars 4.4k forks source link

Sheet portal still displaying content in the body #2495

Closed T3mpu5 closed 8 months ago

T3mpu5 commented 9 months ago

I may have this wrong but does the SheetPortal allow the content to display within a div rather than the body? This is how I understand it from the Dialog docs. I'm passing the portal element through but it seems to still be mounting to the body.

return (
        domReady ?
            <div className='flex justify-end'>
                <Sheet>
                    <SheetTrigger asChild>
                        <Button variant="outline"><ShoppingCart className='pr-2' />£{cartData.total}</Button>
                    </SheetTrigger>
                    <SheetPortal container={portal}>
                        <SheetContent>
                            <SheetHeader>
                                <SheetTitle>Cart</SheetTitle>
                                <SheetDescription>
                                    Total: £{cartData.total}
                                </SheetDescription>
                            </SheetHeader>
                            {cartData.items.map(item => (
                                <div className='grid grid-cols-4 gap-4'>
                                    <div className='col-span-3'>
                                        <h3>{item.name}</h3>
                                        <p>Quantity: {item.quantity}</p>
                                        <p>£{item.price}</p>
                                    </div>
                                    <Button variant="destructive" className='col-span-1' onClick={(e) => handleDeleteCartItem(e, item)}>Delete</Button>
                                </div>
                            ))}
                            <SheetFooter>
                                <SheetClose asChild>
                                    <Button type="submit">Save changes</Button>
                                </SheetClose>
                            </SheetFooter>
                        </SheetContent>
                    </SheetPortal>
                </Sheet>
            </div>
            :
            null
    );
The portal:
```
const ShopUI: React.FC<ShopUIProps> = ({ shopData, cartData, setCartData }) => {
const [container, setContainer] = useState<HTMLDivElement | null>(null);

return (
    <div className="bg-zinc-700 p-4 rounded-lg shadow-lg max-w-3xl h-[80vh]">
        <div ref={setContainer}></div>
        <h3 className="text-lg text-white font-medium mb-2">SHOP</h3>
        <span>
            <Cart cartData={cartData} setCartData={setCartData} portal={container} />
        </span>
        <ScrollArea className='h-[85%] w-full pr-2 mt-2'>
            <div className="grid grid-cols-4 gap-4">
                {shopData.items.map(item => (
                    <Item key={item.id} item={item} cartData={cartData} setCartData={setCartData} />
                ))}
            </div>
        </ScrollArea>
    </div>
);

}



Expected output:
![image](https://github.com/shadcn-ui/ui/assets/39499405/8e728679-3d54-43da-8651-937faf6ba161)
V-Mokhun commented 9 months ago

Could you reproduce the code? It would be much easier to help that way. But from the first glance I'd say that you should use useRef instead of useState and assign it to your div

shadcn commented 8 months ago

This issue has been automatically closed because it received no activity for a while. If you think it was closed by accident, please leave a comment. Thank you.

SahilMahadwar commented 7 months ago

@T3mpu5 were you able to resolve this?

T3mpu5 commented 7 months ago

No I didn't. I ended up using ant design instead

SukiDivine commented 6 months ago

I had the same problem, passed the container element, like this: <SheetContent side="right" container={document.getElementById('YourDivID')}> and updated my shadcn component to add the container inside it (sheet.tsx) <SheetPortal container={props?.container}> sorry if this comment is useless, this was just my case

LucasLoe commented 3 months ago

Is there any solution to this issue? I tried different combinations following the Dialog docs (not working), however didn't succeed. If anybody solved that: I would be really happy to hear about it. @SukiDivine did you solve it? If yes, can you elaborate please?

evilive3000 commented 2 months ago

The portal works as expected, but the problem is caused by the styling.

const sheetVariants = cva(
  "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
  {
    variants: {
      side: {
        top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
        bottom:
          "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
        left: "inset-y-0 left-0  h-full w-3/4 border-r data-[state=closed]:slide-out-to-left  data-[state=open]:slide-in-from-left  sm:max-w-sm",
        right:
          "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
      },
    },
    defaultVariants: {
      side: "right",
    },
  },
);

Your SheetContent has the style "fixed inset-y-0", which makes it cover the full screen as if it were rendered in 'document.body'.

I had the same problem, passed the container element, like this: <SheetContent side="right" container={document.getElementById('YourDivID')}> and updated my shadcn component to add the container inside it (sheet.tsx) <SheetPortal container={props?.container}> sorry if this comment is useless, this was just my case

In my case, I achieved the desired result by adding the styles transform: translateZ(0) (tailwind: transform transform-z-0) to the container (#YourDivID). This creates a new stacking context and forces the element with fixed positioning to be contained within the specified container.

However, you might want to try solving the issue using more conventional methods. You could give the container a position: relative property and change the positioning of the element itself from fixed to absolute.

Both approaches can work, but the second method is generally more predictable and widely supported across browsers. Choose the one that best fits your specific use case and layout requirements.

scobbe commented 2 months ago

@LucasLoe, per @SukiDivine's suggestion, I was unblocked by extending the code to accept a container like this:

my-code.tsx

  const VIEWER_CONTAINER_ID = "viewer";

  const [container, setContainer] = useState<HTMLElement | null>(null);
  useEffect(() => {
    // ensure container is set after render
    setContainer(document.getElementById(VIEWER_CONTAINER_ID));
  }, []);

  return (
    <div className="relative h-full w-full" id={VIEWER_CONTAINER_ID}>
      <ContentWithTabs />
      <Sheet
        modal={false}
        ...
      >
        <SheetContent
          container={container}
          side={"bottom"}
          ...
        >
          {"content"}
        </SheetContent>
      </Sheet>
    </div>
  );

sheet.tsx

interface SheetContentProps
  extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
    VariantProps<typeof sheetVariants> {
  container?: HTMLElement | null;
}

const SheetContent = React.forwardRef<
  React.ElementRef<typeof SheetPrimitive.Content>,
  SheetContentProps
>(({ side = "right", className, children, container, ...props }, ref) => (
  <SheetPortal container={container}>
    <SheetOverlay />
    <SheetPrimitive.Content
      ref={ref}
      className={cn(sheetVariants({ side }), className)}
      {...props}
    >
      <SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-white transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-slate-950 focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-slate-100 dark:ring-offset-slate-950 dark:focus:ring-slate-300 dark:data-[state=open]:bg-slate-800">
        <Cross2Icon className="h-4 w-4" />
        <span className="sr-only">Close</span>
      </SheetPrimitive.Close>
      {children}
    </SheetPrimitive.Content>
  </SheetPortal>
));
SheetContent.displayName = SheetPrimitive.Content.displayName;
yosefebrahimi7 commented 1 month ago

hello friends I could not solve this problem. Is it possible to provide an online example? thank you