Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added drawer component #2202

Closed
wants to merge 2 commits into from
Closed

feat: added drawer component #2202

wants to merge 2 commits into from

Conversation

1111mp
Copy link
Contributor

@1111mp 1111mp commented Jan 3, 2024

📝 Description

Added Drawer component.

Learned about some user feature requests from #2134 [Feature Request] Some Components Feature Suggestions, so I started to implement them from the simplest components (Because it can almost reuse Modal’s design and code).

⛳️ Current behavior (updates)

There is currently no support for the Drawer component.

🚀 New behavior

We can have the Drawer component.

💣 Is this a breaking change (Yes/No):

No. Supports the new Drawer component, which will not have any impact on other components.

📝 Additional Information

Screen Recording 2024-01-02 at 00 15 42

There is currently some work yet to be completed, such as Drawer documentation and usage examples. Because I don’t know whether such code complies with the specifications of the warehouse, I initiated a PR in advance to confirm this. If all goes well I will continue with the rest of the work.

Feel free to point out my questions at any time. Of course, if you already have Drawer-related implementation plans or other questions, please let me know and I will close this PR. Thank you.

I have read the contributing guidelines and code of conduct document.

Signed-off-by: The1111mp <The1111mp@outlook.com>
Copy link

changeset-bot bot commented Jan 3, 2024

🦋 Changeset detected

Latest commit: d6cc47f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@nextui-org/drawer Major
@nextui-org/theme Minor
@nextui-org/react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link

vercel bot commented Jan 3, 2024

@1111mp is attempting to deploy a commit to the NextUI Team on Vercel.

A member of the Team first needs to authorize it.

Copy link

vercel bot commented Jan 3, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
nextui-storybook-v2 ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 4, 2024 0:43am

Signed-off-by: The1111mp <The1111mp@outlook.com>
@jrgarciadev
Copy link
Member

Hey @1111mp, thank you so much for your contribution! 🚀. I suggest using the Modal component as the base component, given that the Drawer logic and structure are basically the same. We would primarily need to adjust the motionProps and introduce modifications to the size and placement variants.

@1111mp
Copy link
Contributor Author

1111mp commented Jan 4, 2024

@jrgarciadev I understand what you mean, and it should be so. Or this way, there is no need to develop additional Drawer components, but just add a Drawer example based on the Modal component.

I completed the following code locally and it also works well:

import {
  Modal,
  ModalContent,
  ModalHeader,
  ModalBody,
  ModalFooter,
  ModalProps,
  useDisclosure,
} from "@nextui-org/react";

type DrawerProps = Omit<ModalProps, "placement" | "scrollBehavior"> & {
  placement?: "top" | "right" | "bottom" | "left";
  scrollBehavior?: "inside" | "outside";
};

const Drawer: React.FC<DrawerProps> = ({
  placement = "right",
  scrollBehavior = "inside",
  size = "md",
  motionProps: drawerMotionProps,
  ...props
}) => {
  const {isOpen, onOpen, onOpenChange} = useDisclosure({defaultOpen: false});

  const motionProps = React.useMemo(() => {
    if (drawerMotionProps !== void 0) return drawerMotionProps;

    const key = placement === "left" || placement === "right" ? "x" : "y";

    return {
      variants: {
        enter: {
          [key]: 0,
          transition: {
            [key]: {
              bounce: 0,
              duration: 0.3,
              ease: TRANSITION_EASINGS.ease,
            },
          },
        },
        exit: {
          [key]: placement === "top" || placement === "left" ? "-100%" : "100%",
          transition: {
            [key]: {
              bounce: 0,
              duration: 0.3,
              ease: TRANSITION_EASINGS.ease,
            },
          },
        },
      },
    };
  }, [placement, drawerMotionProps]);

  const base = useMemo(() => {
    const sizeSource = {
      xs: "max-h-[20rem]",
      sm: "max-h-[24rem]",
      md: "max-h-[28rem]",
      lg: "max-h-[32rem]",
      xl: "max-h-[36rem]",
      "2xl": "max-h-[42rem]",
      "3xl": "max-h-[48rem]",
      "4xl": "max-h-[56rem]",
      "5xl": "max-h-[64rem]",
      full: "max-h-full",
    };
    switch (placement) {
      case "right": {
        return `absolute inset-y-0 right-0 m-0 sm:m-0 max-h-[none] overflow-y-auto rounded-r-none`;
      }
      case "left": {
        return `absolute inset-y-0 left-0 m-0 sm:m-0 max-h-[none] overflow-y-auto rounded-l-none`;
      }
      case "top": {
        return `absolute inset-x-0 top-0 m-0 sm:m-0 max-w-[none] ${sizeSource[size]} overflow-y-auto rounded-t-none`;
      }
      case "bottom": {
        return `absolute inset-x-0 bottom-0 m-0 sm:m-0 max-w-[none] ${sizeSource[size]} overflow-y-auto rounded-b-none`;
      }
    }
  }, [placement, size]);

  return (
    <>
      <Button onPress={onOpen}>Open Modal</Button>
      <Modal
        isOpen={isOpen}
        onOpenChange={onOpenChange}
        {...props}
        size={size}
        scrollBehavior={scrollBehavior}
        classNames={{
          base: base,
        }}
        motionProps={motionProps}
      >
        <ModalContent>
          {(onClose) => (
            <>
              <ModalHeader className="flex flex-col gap-1">Log in</ModalHeader>
              <ModalBody>
                <p>
                  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar risus non
                  risus hendrerit venenatis. Pellentesque sit amet hendrerit risus, sed porttitor
                  quam.
                </p>
                <p>
                  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar risus non
                  risus hendrerit venenatis. Pellentesque sit amet hendrerit risus, sed porttitor
                  quam.
                </p>
                <p>
                  Magna exercitation reprehenderit magna aute tempor cupidatat consequat elit dolor
                  adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis. Velit duis sit
                  officia eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt
                  nisi consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa
                  deserunt nostrud ad veniam.
                </p>
                <p>
                  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar risus non
                  risus hendrerit venenatis. Pellentesque sit amet hendrerit risus, sed porttitor
                  quam. Magna exercitation reprehenderit magna aute tempor cupidatat consequat elit
                  dolor adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis. Velit duis
                  sit officia eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit
                  incididunt nisi consectetur esse laborum eiusmod pariatur proident Lorem eiusmod
                  et. Culpa deserunt nostrud ad veniam.
                </p>
                <p>
                  Mollit dolor eiusmod sunt ex incididunt cillum quis. Velit duis sit officia
                  eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt nisi
                  consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa
                  deserunt nostrud ad veniam. Lorem ipsum dolor sit amet, consectetur adipiscing
                  elit. Nullam pulvinar risus non risus hendrerit venenatis. Pellentesque sit amet
                  hendrerit risus, sed porttitor quam. Magna exercitation reprehenderit magna aute
                  tempor cupidatat consequat elit dolor adipisicing. Mollit dolor eiusmod sunt ex
                  incididunt cillum quis. Velit duis sit officia eiusmod Lorem aliqua enim laboris
                  do dolor eiusmod. Et mollit incididunt nisi consectetur esse laborum eiusmod
                  pariatur proident Lorem eiusmod et. Culpa deserunt nostrud ad veniam.
                </p>
                <p>
                  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar risus non
                  risus hendrerit venenatis. Pellentesque sit amet hendrerit risus, sed porttitor
                  quam.
                </p>
                <p>
                  Magna exercitation reprehenderit magna aute tempor cupidatat consequat elit dolor
                  adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis. Velit duis sit
                  officia eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt
                  nisi consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa
                  deserunt nostrud ad veniam.
                </p>
                <p>
                  Mollit dolor eiusmod sunt ex incididunt cillum quis. Velit duis sit officia
                  eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt nisi
                  consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa
                  deserunt nostrud ad veniam. Lorem ipsum dolor sit amet, consectetur adipiscing
                  elit. Nullam pulvinar risus non risus hendrerit venenatis. Pellentesque sit amet
                  hendrerit risus, sed porttitor quam. Magna exercitation reprehenderit magna aute
                  tempor cupidatat consequat elit dolor adipisicing. Mollit dolor eiusmod sunt ex
                  incididunt cillum quis. Velit duis sit officia eiusmod Lorem aliqua enim laboris
                  do dolor eiusmod. Et mollit incididunt nisi consectetur esse laborum eiusmod
                  pariatur proident Lorem eiusmod et. Culpa deserunt nostrud ad veniam.
                </p>
              </ModalBody>
              <ModalFooter>
                <Button color="danger" variant="flat" onPress={onClose}>
                  Close
                </Button>
                <Button color="primary" onPress={onClose}>
                  Sign in
                </Button>
              </ModalFooter>
            </>
          )}
        </ModalContent>
      </Modal>
    </>
  );
};

// usage in storybook
export const Default = {
  render: Drawer,

  args: {
    // ...defaultProps,
    // size: "full",
    placement: "top",
    scrollBehavior: "outside",
  },
};

Just need to do some class style coverage for the Drawer use case.

So should the final solution be to add a Drawer usage example to Modal's documentation?

@jrgarciadev
Copy link
Member

Nice you could tested it, the final solution should be creating the Drawer component structure by based on the Modal, you'll
Need to put the drawer styles inside the theme folder to allow tailwindcss recognize the styles correctly

@1111mp
Copy link
Contributor Author

1111mp commented Jan 5, 2024

I ended up doing the following:

  • created packages/components/modal/drawer.tsx
import {ReactNode, useMemo} from "react";
import {forwardRef} from "@nextui-org/system";

import {UseModalProps} from "./use-modal";
import Modal from "./modal";
import {TRANSITION_EASINGS} from "@nextui-org/framer-transitions";
import {drawer} from "@nextui-org/theme";
import {clsx} from "@nextui-org/shared-utils";

export type DrawerProps = Omit<UseModalProps, "placement" | "scrollBehavior"> & {
  /**
   * The content of the modal. Usually the ModalContent
   */
  children: ReactNode;
  placement?: "top" | "right" | "bottom" | "left";
  scrollBehavior?: "inside" | "outside";
};

const Drawer = forwardRef<"div", DrawerProps>(
  (
    {
      className,
      classNames,
      placement = "right",
      scrollBehavior = "inside",
      size = "md",
      motionProps: drawerMotionProps,
      children,
      ...props
    },
    ref,
  ) => {
    const motionProps = useMemo(() => {
      if (drawerMotionProps !== void 0) return drawerMotionProps;

      const key = placement === "left" || placement === "right" ? "x" : "y";

      return {
        variants: {
          enter: {
            [key]: 0,
            transition: {
              [key]: {
                bounce: 0,
                duration: 0.3,
                ease: TRANSITION_EASINGS.ease,
              },
            },
          },
          exit: {
            [key]: placement === "top" || placement === "left" ? "-100%" : "100%",
            transition: {
              [key]: {
                bounce: 0,
                duration: 0.3,
                ease: TRANSITION_EASINGS.ease,
              },
            },
          },
        },
      };
    }, [placement, drawerMotionProps]);

    const baseStyles = clsx(classNames?.base, className);

    const slots = useMemo(
      () =>
        drawer({
          size,
          placement,
        }),
      [size, placement],
    );

    const base = slots.base({class: clsx(baseStyles, {})});

    console.log(base);

    return (
      <Modal
        ref={ref}
        {...props}
        size={size}
        scrollBehavior={scrollBehavior}
        classNames={{
          ...classNames,
          base: base,
        }}
        motionProps={motionProps}
      >
        {children}
      </Modal>
    );
  },
);

Drawer.displayName = "NextUI.Drawer";

export default Drawer;
  • packages/components/modal/index.ts
// ...
import Drawer from "./drawer";

// ...

// add export Drawer
export {Modal, Drawer, ModalContent, ModalHeader, ModalBody, ModalFooter};
  • drawer styles @nextui-org/theme/components/modal.ts
// .....

const drawer = tv({
  slots: {
    base: ["absolute", "m-0", "sm:m-0", "overflow-y-auto"],
  },
  variants: {
    size: {
      xs: {
        base: "max-w-xs max-h-[20rem]",
      },
      sm: {
        base: "max-w-sm max-h-[24rem]",
      },
      md: {
        base: "max-w-md max-h-[28rem]",
      },
      lg: {
        base: "max-w-lg max-h-[32rem]",
      },
      xl: {
        base: "max-w-xl max-h-[36rem]",
      },
      "2xl": {
        base: "max-w-2xl max-h-[42rem]",
      },
      "3xl": {
        base: "max-w-3xl max-h-[48rem]",
      },
      "4xl": {
        base: "max-w-4xl max-h-[56rem]",
      },
      "5xl": {
        base: "max-w-5xl max-h-[64rem]",
      },
      full: {
        base: "max-w-full max-h-full h-[100dvh] !rounded-none",
      },
    },
    placement: {
      top: {
        base: "inset-x-0 top-0 max-w-[none] rounded-t-none",
      },
      right: {
        base: "inset-y-0 right-0 max-h-[none] rounded-r-none",
      },
      bottom: {
        base: "inset-x-0 bottom-0 max-w-[none] rounded-b-none",
      },
      left: {
        base: "inset-y-0 left-0 max-h-[none] rounded-l-none",
      },
    },
  },
});

// ...

export {modal, drawer};

This is all the code of the Drawer component.

Usage:

import {
  Modal,
  Drawer,
  ModalContent,
  ModalHeader,
  ModalBody,
  ModalFooter,
  ModalProps,
  useDisclosure,
} from "@nextui-org/modal";

const App = () => {
  const {isOpen, onOpen, onOpenChange} = useDisclosure({defaultOpen: false});

  return (
    <>
      <Button onPress={onOpen}>Open Modal</Button>
      <Drawer
        isOpen={isOpen}
        onOpenChange={onOpenChange}
        scrollBehavior="inside"
        size="lg"
        placement="right"
        // classNames={{
        //   base: "w-[378px]",
        // }}
      >
        <ModalContent>
          {(onClose) => (
            <>
              <ModalHeader className="flex flex-col gap-1">Log in</ModalHeader>
              <ModalBody>
                <p>
                  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar risus non
                  risus hendrerit venenatis. Pellentesque sit amet hendrerit risus, sed porttitor
                  quam.
                </p>
                <p>
                  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar risus non
                  risus hendrerit venenatis. Pellentesque sit amet hendrerit risus, sed porttitor
                  quam.
                </p>
                <p>
                  Magna exercitation reprehenderit magna aute tempor cupidatat consequat elit dolor
                  adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis. Velit duis sit
                  officia eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt
                  nisi consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa
                  deserunt nostrud ad veniam.
                </p>
                <p>
                  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar risus non
                  risus hendrerit venenatis. Pellentesque sit amet hendrerit risus, sed porttitor
                  quam. Magna exercitation reprehenderit magna aute tempor cupidatat consequat elit
                  dolor adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis. Velit duis
                  sit officia eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit
                  incididunt nisi consectetur esse laborum eiusmod pariatur proident Lorem eiusmod
                  et. Culpa deserunt nostrud ad veniam.
                </p>
                <p>
                  Mollit dolor eiusmod sunt ex incididunt cillum quis. Velit duis sit officia
                  eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt nisi
                  consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa
                  deserunt nostrud ad veniam. Lorem ipsum dolor sit amet, consectetur adipiscing
                  elit. Nullam pulvinar risus non risus hendrerit venenatis. Pellentesque sit amet
                  hendrerit risus, sed porttitor quam. Magna exercitation reprehenderit magna aute
                  tempor cupidatat consequat elit dolor adipisicing. Mollit dolor eiusmod sunt ex
                  incididunt cillum quis. Velit duis sit officia eiusmod Lorem aliqua enim laboris
                  do dolor eiusmod. Et mollit incididunt nisi consectetur esse laborum eiusmod
                  pariatur proident Lorem eiusmod et. Culpa deserunt nostrud ad veniam.
                </p>
                <p>
                  Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam pulvinar risus non
                  risus hendrerit venenatis. Pellentesque sit amet hendrerit risus, sed porttitor
                  quam.
                </p>
                <p>
                  Magna exercitation reprehenderit magna aute tempor cupidatat consequat elit dolor
                  adipisicing. Mollit dolor eiusmod sunt ex incididunt cillum quis. Velit duis sit
                  officia eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt
                  nisi consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa
                  deserunt nostrud ad veniam.
                </p>
                <p>
                  Mollit dolor eiusmod sunt ex incididunt cillum quis. Velit duis sit officia
                  eiusmod Lorem aliqua enim laboris do dolor eiusmod. Et mollit incididunt nisi
                  consectetur esse laborum eiusmod pariatur proident Lorem eiusmod et. Culpa
                  deserunt nostrud ad veniam. Lorem ipsum dolor sit amet, consectetur adipiscing
                  elit. Nullam pulvinar risus non risus hendrerit venenatis. Pellentesque sit amet
                  hendrerit risus, sed porttitor quam. Magna exercitation reprehenderit magna aute
                  tempor cupidatat consequat elit dolor adipisicing. Mollit dolor eiusmod sunt ex
                  incididunt cillum quis. Velit duis sit officia eiusmod Lorem aliqua enim laboris
                  do dolor eiusmod. Et mollit incididunt nisi consectetur esse laborum eiusmod
                  pariatur proident Lorem eiusmod et. Culpa deserunt nostrud ad veniam.
                </p>
              </ModalBody>
              <ModalFooter>
                <Button color="danger" variant="flat" onPress={onClose}>
                  Close
                </Button>
                <Button color="primary" onPress={onClose}>
                  Sign in
                </Button>
              </ModalFooter>
            </>
          )}
        </ModalContent>
      </Drawer>
    </>
  );
};

I tested it locally and it works fine. I don't know if this way of supporting Drawer components meets your expectations. Please feel free to let me know if you have any questions.

@1111mp 1111mp closed this Jan 6, 2024
@jrgarciadev
Copy link
Member

Hey @1111mp why did you close this?, I'm still reviewing your suggestion, you're on the right track

@1111mp
Copy link
Contributor Author

1111mp commented Jan 10, 2024

I'm very sorry that I didn't reply in time. I've rearranged the code locally and added a UseAsDrawer story to the storybook.

But I found that previous commit records may pollute the main branch in the future, so I decided to reopen a PR #2223. Please understand. This way you can quickly synchronize your code and perform testing and evaluation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants