ecklf / tailwindcss-radix

Utilities and variants for styling Radix state
https://tailwindcss-radix.vercel.app
MIT License
2.11k stars 67 forks source link

Feat: Drawer component #39

Closed thmsmtylr closed 7 months ago

thmsmtylr commented 1 year ago

I've created a Drawer component based off of the Radix Dialog component something I think the Radix library is missing. Do you have any interest in adding this component? Here's a rough sketch of how it would look:

drawer.tsx:

import { Transition } from "@headlessui/react";
import * as DialogPrimitive from "@radix-ui/react-dialog";
import { Cross1Icon } from "@radix-ui/react-icons";
import { clsx } from "clsx";
import React, { Fragment } from "react";
import Button from "./shared/button";

type Position = "left" | "right";

export interface DrawerProps {
  title?: string;
  description?: string;
  position?: Position;
  isOpen: boolean;
  onOpenChange: () => void;
}

const Drawer = (props: DrawerProps) => {
  const {
    title = "Drawer",
    position = "right",
    description,
    isOpen,
    onOpenChange,
  } = props;
  return (
    <DialogPrimitive.Root open={isOpen} onOpenChange={onOpenChange}>
      <DialogPrimitive.Portal forceMount>
        <Transition.Root show={isOpen}>
          <Transition.Child
            as={Fragment}
            enter="ease-in-out duration-300"
            enterFrom="opacity-0"
            enterTo="opacity-100"
            leave="ease-in-out duration-300"
            leaveFrom="opacity-100"
            leaveTo="opacity-0"
          >
            <DialogPrimitive.Overlay className="fixed inset-0 bg-black/50" />
          </Transition.Child>
          <Transition.Child
            as={Fragment}
            enter="transform transition ease-in-out duration-300"
            enterFrom="translate-x-full"
            enterTo="translate-x-0"
            leave="transform transition ease-in-out duration-300"
            leaveFrom="translate-x-0"
            leaveTo="translate-x-full"
          >
            <DialogPrimitive.Content
              forceMount
              className={clsx(
                position === "right" ? "right-0" : "left-0",
                "fixed top-0 flex h-full w-screen max-w-md flex-col overflow-y-scroll bg-gray-800 py-6 shadow-xl"
              )}
            >
              <DialogPrimitive.Title className="mb-2 px-4 text-sm font-medium text-gray-900 dark:text-gray-100">
                {title}
              </DialogPrimitive.Title>
              {description && (
                <DialogPrimitive.Description className="px-4 text-sm font-normal text-gray-700 dark:text-gray-400">
                  {description}
                </DialogPrimitive.Description>
              )}
              <div className="mt-[25px] flex justify-end px-4">
                <DialogPrimitive.Close
                  className={clsx(
                    "inline-flex select-none justify-center rounded-md px-4 py-2 text-sm font-medium",
                    "bg-purple-600 text-white hover:bg-purple-700 dark:bg-purple-700 dark:text-gray-100 dark:hover:bg-purple-600",
                    "border border-transparent",
                    "focus:outline-none focus-visible:ring focus-visible:ring-purple-500 focus-visible:ring-opacity-75"
                  )}
                >
                  Save changes
                </DialogPrimitive.Close>
              </div>
              <DialogPrimitive.Close className="absolute top-6 right-3.5 inline-flex items-center justify-center rounded-full p-1 focus:outline-none focus-visible:ring focus-visible:ring-purple-500 focus-visible:ring-opacity-75">
                <Cross1Icon className="h-4 w-4 text-gray-500 hover:text-gray-700 dark:text-gray-500 dark:hover:text-gray-400" />
              </DialogPrimitive.Close>
            </DialogPrimitive.Content>
          </Transition.Child>
        </Transition.Root>
      </DialogPrimitive.Portal>
    </DialogPrimitive.Root>
  );
};

export { Drawer };

index.tsx:

const DrawerWithState = () => {
  const [drawerOpen, setDrawerOpen] = useState<boolean>(false);

  const handleDrawerToggle = () => {
    setDrawerOpen(!drawerOpen);
  };

  return (
    <>
      <Button onClick={handleDrawerToggle}>Click</Button>
      <Drawer
        isOpen={drawerOpen}
        onOpenChange={handleDrawerToggle}
        description="Navigation drawers provide access to destinations in your app. Side sheets are surfaces containing supplementary content that are anchored to the left or right edge of the screen."
      />
    </>
  );
};

{
  label: "Drawer",
  link: `${REPO_URL}/components/drawer.tsx`,
  component: <DrawerWithState />,
},

screenshots:

Screen Shot 2023-03-16 at 1 42 05 pm Screen Shot 2023-03-16 at 1 43 21 pm

I have the changes on a PR locally. I'd very much like to contribute to your library moving forward 👍 . Please let me know your thoughts and feedback? Thanks!

ecklf commented 7 months ago

Much appreciated, but I'll stick to the official Radix primitives for the demo. We have things like shadcn/ui now, which is a fully built collection of components.