From 9becb4524f0fdf86427c6e0fd1d1435bc096cea3 Mon Sep 17 00:00:00 2001
From: ruru <142723369+ruru-m07@users.noreply.github.com>
Date: Fri, 23 Aug 2024 14:08:02 +0530
Subject: [PATCH 1/3] feat(components): add modal component
---
apps/www/app/playground/page.tsx | 68 ++-
apps/www/content/docs/cli.mdx | 2 +-
.../public/registry/components/button.json | 2 +-
.../www/public/registry/components/modal.json | 11 +
apps/www/public/registry/index.json | 6 +
apps/www/registry/ui.ts | 6 +
packages/cli/package.json | 2 +-
packages/cli/src/commands/init.ts | 6 +-
packages/ui/src/components/button.tsx | 2 +-
packages/ui/src/components/modal.tsx | 410 ++++++++++++++++++
pnpm-lock.yaml | 63 ++-
11 files changed, 562 insertions(+), 16 deletions(-)
create mode 100644 apps/www/public/registry/components/modal.json
create mode 100644 packages/ui/src/components/modal.tsx
diff --git a/apps/www/app/playground/page.tsx b/apps/www/app/playground/page.tsx
index 85c2bb0..45f51f2 100644
--- a/apps/www/app/playground/page.tsx
+++ b/apps/www/app/playground/page.tsx
@@ -1,6 +1,6 @@
"use client";
-import React from "react";
+import React, { useState } from "react";
import Card from "@/components/ui/card";
import { ModeToggle } from "@/components/ui/ModeToggle";
import {
@@ -40,8 +40,16 @@ import {
selectAnimationVariants,
} from "ruru-ui/components/select";
import AnimationToggle from "@/components/animationToggle";
+import Modal, { ModalProvider } from "ruru-ui/components/modal";
const Playground = () => {
+ const handleSubmit = async () => {
+ // Your submit logic here
+ console.log("Submitted");
+ // Simulate an API call or any async operation
+ await new Promise((resolve) => setTimeout(resolve, 1000));
+ };
+
return (
@@ -580,6 +588,64 @@ const Playground = () => {
))}
+
+ {/* setOpen(true)} size="small">
+ Open Modal
+ */}
+
+ {/* setOpen(false)}>
+
+
+ Create Username
+
+ Enter a unique name for your token to differentiate it from
+ other tokens and then select the scope.
+
+
+
+
+
+
+
+ */}
+ {/* setOpen(false)} variant="secondary">
+ Cancel
+
+
+ setOpen(false)}>Submit */}
+ {/* setOpen(false)}
+ variant="secondary"
+ >
+ Cancel
+
+
+ */}
+
+
+ Open Modal
+
+
+
+ Create Username
+
+ Enter a unique name for your token to differentiate it from
+ other tokens and then select the scope.
+
+
+
+
+
+
+
+ Cancel
+ Submit
+
+
+
+
+
);
diff --git a/apps/www/content/docs/cli.mdx b/apps/www/content/docs/cli.mdx
index 7611d31..07481fb 100644
--- a/apps/www/content/docs/cli.mdx
+++ b/apps/www/content/docs/cli.mdx
@@ -93,7 +93,7 @@ initialize your project and install dependencies
Options:
-y, --yes skip confirmation prompt. (default: false)
-d, --defaults use default configuration. (default: false)
- -a, --autodetact autodetact configuration by freamwork. (default: false)
+ -a, --autodetect autodetect configuration by framework. (default: false)
-c, --cwd the working directory. defaults to the current directory.
(default: "D:\\GitHub\\ruru-ui\\apps\\sink")
-h, --help display help for command
diff --git a/apps/www/public/registry/components/button.json b/apps/www/public/registry/components/button.json
index 6dba07e..06e4d60 100644
--- a/apps/www/public/registry/components/button.json
+++ b/apps/www/public/registry/components/button.json
@@ -4,7 +4,7 @@
"files": [
{
"name": "button.tsx",
- "content": "\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { Spinner } from \"./spinner\";\nimport { motion } from \"framer-motion\";\nimport { useRuru } from \"@/provider\";\n\nexport const buttonVariants = cva(\n \"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n variant: {\n default:\n \"bg-primary text-primary-foreground shadow hover:bg-primary/85 hover:shadow-md\",\n secondary:\n \"border-input border-[1.5px] bg-primary-foreground hover:bg-[#f3f3f3] dark:hover:bg-[#202020]\",\n tertiary: \"text-primary hover:bg-[#f3f3f3] dark:hover:bg-[#202020]\",\n error: \"bg-[#d93036] hover:bg-[#ff6166]\",\n warning: \"bg-[#ff990a] text-primary-foreground hover:bg-[#d27504]\",\n },\n size: {\n default: \"h-9 px-4 py-2\",\n small: \"h-8 rounded-md px-3 text-xs\",\n large: \"h-10 rounded-md px-8\",\n icon: \"size-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nexport interface ButtonProps\n extends Omit<\n React.ButtonHTMLAttributes,\n \"prefix\" | \"suffix\"\n >,\n VariantProps {\n \n asChild?: boolean;\n \n prefix?: React.ReactNode;\n \n suffix?: React.ReactNode;\n \n disabled?: boolean;\n \n loading?: boolean;\n}\n\nexport const Button = React.forwardRef(\n (\n {\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n prefix,\n suffix,\n disabled = false,\n loading = false,\n ...props\n },\n ref,\n ) => {\n const { animation } = useRuru();\n\n const Comp = asChild ? Slot : \"button\";\n\n const buttonContent = (\n \n {loading ? : null}\n {prefix ? (\n \n {prefix}\n \n ) : null}\n {props.children}\n {suffix ? (\n \n {suffix}\n \n ) : null}\n \n );\n\n return (\n \n {animation ? (\n {buttonContent} \n ) : (\n buttonContent\n )}\n
\n );\n },\n);\n\nButton.displayName = \"Button\";\n"
+ "content": "\"use client\";\n\nimport { Slot } from \"@radix-ui/react-slot\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport React from \"react\";\nimport { cn } from \"@/utils/cn\";\nimport { Spinner } from \"./spinner\";\nimport { motion } from \"framer-motion\";\nimport { useRuru } from \"@/provider\";\n\nexport const buttonVariants = cva(\n \"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50\",\n {\n variants: {\n variant: {\n default:\n \"bg-primary text-primary-foreground shadow hover:bg-primary/85 hover:shadow-md\",\n secondary:\n \"border-input border-[1.5px] bg-primary-foreground hover:bg-[#f3f3f3] dark:hover:bg-[#202020]\",\n tertiary: \"text-primary hover:bg-[#f3f3f3] dark:hover:bg-[#202020]\",\n error: \"bg-[#d93036] hover:bg-[#ff6166]\",\n warning: \"bg-[#ff990a] text-primary-foreground hover:bg-[#d27504]\",\n },\n size: {\n default: \"h-9 px-4 py-2\",\n small: \"h-8 rounded-md px-3 text-xs\",\n large: \"h-10 rounded-md px-8\",\n icon: \"size-9\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n },\n);\n\nexport interface ButtonProps\n extends Omit<\n React.ButtonHTMLAttributes,\n \"prefix\" | \"suffix\"\n >,\n VariantProps {\n \n asChild?: boolean;\n \n prefix?: React.ReactNode;\n \n suffix?: React.ReactNode;\n \n disabled?: boolean;\n \n loading?: boolean;\n}\n\nexport const Button = React.forwardRef(\n (\n {\n className,\n variant = \"default\",\n size = \"default\",\n asChild = false,\n prefix,\n suffix,\n disabled = false,\n loading = false,\n ...props\n },\n ref,\n ) => {\n const { animation } = useRuru();\n\n const Comp = asChild ? Slot : \"button\";\n\n const buttonContent = (\n \n {loading ? : null}\n {prefix ? (\n \n {prefix}\n \n ) : null}\n {props.children}\n {suffix ? (\n \n {suffix}\n \n ) : null}\n \n );\n\n return (\n \n {animation ? (\n {buttonContent} \n ) : (\n buttonContent\n )}\n
\n );\n },\n);\n\nButton.displayName = \"Button\";\n"
}
],
"type": "components:ui",
diff --git a/apps/www/public/registry/components/modal.json b/apps/www/public/registry/components/modal.json
new file mode 100644
index 0000000..83af4eb
--- /dev/null
+++ b/apps/www/public/registry/components/modal.json
@@ -0,0 +1,11 @@
+{
+ "name": "modal",
+ "files": [
+ {
+ "name": "modal.tsx",
+ "content": "import React, {\n useState,\n useContext,\n useCallback,\n ReactNode,\n createContext,\n useEffect,\n} from \"react\";\nimport {\n AnimatePresence,\n ForwardRefComponent,\n HTMLMotionProps,\n motion,\n} from \"framer-motion\";\nimport { cn } from \"@/utils/cn\";\nimport { Button, ButtonProps } from \"./button\";\n\n\nexport interface ModalContextProps {\n \n isOpen: boolean;\n \n openModal: () => void;\n \n closeModal: () => void;\n}\n\n\nexport interface ModalProps\n extends ForwardRefComponent> {\n \n children: ReactNode;\n \n onClickOutside?: () => void;\n}\n\n\nexport interface ModalActionProps extends ButtonProps {\n \n fullWidth?: boolean;\n \n onClick?: () => void | Promise;\n}\n\n\nexport interface TriggerProps extends ButtonProps {\n \n children: ReactNode;\n \n onClick?: () => void;\n \n asChild?: boolean;\n}\n\n\nexport interface DivProps\n extends React.DetailedHTMLProps<\n React.HTMLAttributes,\n HTMLDivElement\n > {}\n\n\nconst ModalContext = createContext(undefined);\n\n\nexport const ModalProvider = ({\n children,\n}: {\n children: ReactNode;\n}): React.ReactElement => {\n const [isOpen, setIsOpen] = useState(false);\n\n const openModal = useCallback(() => setIsOpen(true), []);\n const closeModal = useCallback(() => setIsOpen(false), []);\n\n return (\n \n {children}\n \n );\n};\n\n\nexport const useModal = (): ModalContextProps => {\n const context = useContext(ModalContext);\n if (!context) {\n throw new Error(\"useModal must be used within a ModalProvider\");\n }\n return context;\n};\n\n\nconst Modal = ({\n children,\n onClickOutside,\n ...props\n}: ModalProps): React.ReactElement => {\n const { isOpen, closeModal } = useModal();\n\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n closeModal();\n }\n };\n\n if (isOpen) {\n document.addEventListener(\"keydown\", handleEscape);\n } else {\n document.removeEventListener(\"keydown\", handleEscape);\n }\n\n return () => document.removeEventListener(\"keydown\", handleEscape);\n }, [isOpen, closeModal]);\n\n const modalVariants = {\n hidden: {\n opacity: 0,\n y: -50,\n rotateX: \"0deg\",\n transition: { duration: 0.15 },\n },\n visible: {\n opacity: 1,\n y: 0,\n rotateX: \"0deg\",\n transition: { duration: 0.15 },\n },\n exit: {\n opacity: 0,\n y: -50,\n rotateX: \"-10deg\",\n transition: { duration: 0.15 },\n },\n };\n\n return (\n \n {isOpen && (\n \n e.stopPropagation()}\n variants={modalVariants}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"exit\"\n >\n {children}\n \n \n )}\n \n );\n};\n\n\nModal.Trigger = ({\n children,\n onClick,\n asChild = false,\n}: TriggerProps): React.ReactElement => {\n const { openModal } = useModal();\n\n const handleClick = () => {\n openModal();\n if (onClick) {\n onClick();\n }\n };\n\n if (asChild) {\n return (\n \n {children}\n
\n );\n }\n\n return {children} ;\n};\n\n\nModal.Body = ({ children, ...props }: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Header = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Title = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n \n);\n\n\nModal.Content = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Subtitle = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Actions = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Action = ({\n fullWidth = false,\n onClick,\n className,\n ...props\n}: ModalActionProps): React.ReactElement => {\n const { closeModal } = useModal();\n\n const handleClick = async () => {\n if (onClick) {\n await onClick();\n }\n closeModal();\n };\n\n return (\n \n {props.children}\n \n );\n};\n\n\nModal.Close = (props: ModalActionProps): React.ReactElement => (\n \n {props.children}\n \n);\n\nexport default Modal;\n"
+ }
+ ],
+ "type": "components:ui",
+ "subcategory": ["button"]
+}
diff --git a/apps/www/public/registry/index.json b/apps/www/public/registry/index.json
index 859e7dc..64eee53 100644
--- a/apps/www/public/registry/index.json
+++ b/apps/www/public/registry/index.json
@@ -60,5 +60,11 @@
"dependencies": ["@radix-ui/react-tooltip"],
"files": ["tooltip.tsx"],
"type": "components:ui"
+ },
+ {
+ "name": "modal",
+ "files": ["modal.tsx"],
+ "type": "components:ui",
+ "subcategory": ["button"]
}
]
diff --git a/apps/www/registry/ui.ts b/apps/www/registry/ui.ts
index a0a720a..143c2aa 100644
--- a/apps/www/registry/ui.ts
+++ b/apps/www/registry/ui.ts
@@ -63,4 +63,10 @@ export const ui: Registry = [
dependencies: ["@radix-ui/react-tooltip"],
files: ["tooltip.tsx"],
},
+ {
+ name: "modal",
+ type: "components:ui",
+ files: ["modal.tsx"],
+ subcategory: ["button"],
+ },
];
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 31fc2b9..18aa60f 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -1,6 +1,6 @@
{
"name": "ruru-ui-cli",
- "version": "0.0.4",
+ "version": "0.0.5",
"description": "Ruru UI - CLI - add components and dependencies to your project",
"keywords": [
"cli",
diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts
index b060946..ea32064 100644
--- a/packages/cli/src/commands/init.ts
+++ b/packages/cli/src/commands/init.ts
@@ -52,7 +52,7 @@ const PROJECT_DEPENDENCIES = [
const initOptionsSchema = z.object({
defaults: z.boolean().default(false),
- autodetact: z.boolean().default(false),
+ autodetect: z.boolean().default(false),
yes: z.boolean(),
cwd: z.string(),
});
@@ -62,7 +62,7 @@ export const init = new Command()
.description("initialize your project and install dependencies")
.option("-y, --yes", "skip confirmation prompt.", false)
.option("-d, --defaults", "use default configuration.", false)
- .option("-a, --autodetact", "autodetact configuration by freamwork.", false)
+ .option("-a, --autodetect ", "autodetect configuration by framework.", false)
.option(
"-c, --cwd ",
"the working directory. defaults to the current directory.",
@@ -79,7 +79,7 @@ export const init = new Command()
const projectConfig = await getProjectConfig(cwd);
- if (options.autodetact && projectConfig) {
+ if (options.autodetect && projectConfig) {
console.log(pc.green(pc.bold("\n config found! \n ")));
const config = await promptForMinimalConfig(
diff --git a/packages/ui/src/components/button.tsx b/packages/ui/src/components/button.tsx
index b5a09f3..fea44b4 100644
--- a/packages/ui/src/components/button.tsx
+++ b/packages/ui/src/components/button.tsx
@@ -165,7 +165,7 @@ export const Button = React.forwardRef(
);
return (
-
+
{animation ? (
{buttonContent}
) : (
diff --git a/packages/ui/src/components/modal.tsx b/packages/ui/src/components/modal.tsx
new file mode 100644
index 0000000..bb08cfe
--- /dev/null
+++ b/packages/ui/src/components/modal.tsx
@@ -0,0 +1,410 @@
+import React, {
+ useState,
+ useContext,
+ useCallback,
+ ReactNode,
+ createContext,
+ useEffect,
+} from "react";
+import {
+ AnimatePresence,
+ ForwardRefComponent,
+ HTMLMotionProps,
+ motion,
+} from "framer-motion";
+import { cn } from "@/utils/cn";
+import { Button, ButtonProps } from "./button";
+
+/**
+ * Represents the props for the Modal component.
+ */
+export interface ModalContextProps {
+ /**
+ * The state of the modal.
+ *
+ * @type {boolean}
+ */
+ isOpen: boolean;
+ /**
+ *
+ * Open the modal.
+ *
+ * @returns {void}
+ */
+ openModal: () => void;
+ /**
+ *
+ * Close the modal.
+ *
+ * @returns {void}
+ */
+ closeModal: () => void;
+}
+
+/**
+ * Represents the props for the Modal component.
+ */
+export interface ModalProps
+ extends ForwardRefComponent
> {
+ /**
+ * The children of the Modal component.
+ */
+ children: ReactNode;
+ /**
+ *
+ * The function to call when the user clicks outside the modal.
+ *
+ * @returns {void}
+ */
+ onClickOutside?: () => void;
+}
+
+/**
+ * Represents the props for the Modal component.
+ */
+export interface ModalActionProps extends ButtonProps {
+ /**
+ * The children of the Modal component.
+ */
+ fullWidth?: boolean;
+ /**
+ * The function to call when the user clicks the action.
+ *
+ * @returns {void} | {Promise}
+ */
+ onClick?: () => void | Promise;
+}
+
+/**
+ * Represents the props for the Modal component.
+ */
+export interface TriggerProps extends ButtonProps {
+ /**
+ * The children of the Modal component.
+ */
+ children: ReactNode;
+ /**
+ *
+ * The function to call when the user clicks the trigger.
+ *
+ * @returns {void}
+ */
+ onClick?: () => void;
+ /**
+ *
+ * Render as child component.
+ *
+ * @default false
+ * @type {boolean}
+ */
+ asChild?: boolean;
+}
+
+/**
+ * Represents the props for the Modal component.
+ */
+export interface DivProps
+ extends React.DetailedHTMLProps<
+ React.HTMLAttributes,
+ HTMLDivElement
+ > {}
+
+/**
+ * Represents the Modal component.
+ *
+ * @param {ModalProps} props - The props for the Modal component.
+ * @returns {React.ReactElement}
+ *
+ */
+const ModalContext = createContext(undefined);
+
+/**
+ * Represents the Modal component.
+ *
+ * @param {ModalProps} props - The props for the Modal component.
+ * @returns {React.ReactElement}
+ *
+ */
+export const ModalProvider = ({
+ children,
+}: {
+ children: ReactNode;
+}): React.ReactElement => {
+ const [isOpen, setIsOpen] = useState(false);
+
+ const openModal = useCallback(() => setIsOpen(true), []);
+ const closeModal = useCallback(() => setIsOpen(false), []);
+
+ return (
+
+ {children}
+
+ );
+};
+
+/**
+ * Represents the Modal component.
+ *
+ * @returns {ModalContextProps}
+ *
+ */
+export const useModal = (): ModalContextProps => {
+ const context = useContext(ModalContext);
+ if (!context) {
+ throw new Error("useModal must be used within a ModalProvider");
+ }
+ return context;
+};
+
+/**
+ * Represents the Modal component.
+ *
+ * @param {ModalProps} props - The props for the Modal component.
+ * @returns {React.ReactElement}
+ *
+ */
+const Modal = ({
+ children,
+ onClickOutside,
+ ...props
+}: ModalProps): React.ReactElement => {
+ const { isOpen, closeModal } = useModal();
+
+ useEffect(() => {
+ const handleEscape = (event: KeyboardEvent) => {
+ if (event.key === "Escape") {
+ closeModal();
+ }
+ };
+
+ if (isOpen) {
+ document.addEventListener("keydown", handleEscape);
+ } else {
+ document.removeEventListener("keydown", handleEscape);
+ }
+
+ return () => document.removeEventListener("keydown", handleEscape);
+ }, [isOpen, closeModal]);
+
+ const modalVariants = {
+ hidden: {
+ opacity: 0,
+ y: -50,
+ rotateX: "0deg",
+ transition: { duration: 0.15 },
+ },
+ visible: {
+ opacity: 1,
+ y: 0,
+ rotateX: "0deg",
+ transition: { duration: 0.15 },
+ },
+ exit: {
+ opacity: 0,
+ y: -50,
+ rotateX: "-10deg",
+ transition: { duration: 0.15 },
+ },
+ };
+
+ return (
+
+ {isOpen && (
+
+ e.stopPropagation()}
+ variants={modalVariants}
+ initial="hidden"
+ animate="visible"
+ exit="exit"
+ >
+ {children}
+
+
+ )}
+
+ );
+};
+
+/**
+ * Represents the Modal component.
+ *
+ * @param {TriggerProps} props - The props for the Modal component.
+ * @returns {React.ReactElement}
+ */
+Modal.Trigger = ({
+ children,
+ onClick,
+ asChild = false,
+}: TriggerProps): React.ReactElement => {
+ const { openModal } = useModal();
+
+ const handleClick = () => {
+ openModal();
+ if (onClick) {
+ onClick();
+ }
+ };
+
+ if (asChild) {
+ return (
+
+ {children}
+
+ );
+ }
+
+ return {children} ;
+};
+
+/**
+ * Represents the Modal component.
+ *
+ * @param {DivProps} props - The props for the Modal component.
+ * @returns {React.ReactElement}
+ */
+Modal.Body = ({ children, ...props }: DivProps): React.ReactElement => (
+
+ {children}
+
+);
+
+/**
+ * Represents the Modal component.
+ *
+ * @param {DivProps} props - The props for the Modal component.
+ * @returns {React.ReactElement}
+ */
+Modal.Header = ({
+ children,
+ className,
+ ...props
+}: DivProps): React.ReactElement => (
+
+ {children}
+
+);
+
+/**
+ * Represents the Modal component.
+ *
+ * @param {DivProps} props - The props for the Modal component.
+ * @returns {React.ReactElement}
+ */
+Modal.Title = ({
+ children,
+ className,
+ ...props
+}: DivProps): React.ReactElement => (
+
+ {children}
+
+);
+
+/**
+ * Represents the Modal component.
+ *
+ * @param {DivProps} props - The props for the Modal component.
+ * @returns {React.ReactElement}
+ */
+Modal.Content = ({
+ children,
+ className,
+ ...props
+}: DivProps): React.ReactElement => (
+
+ {children}
+
+);
+
+/**
+ * Represents the Modal component.
+ *
+ * @param {DivProps} props - The props for the Modal component.
+ * @returns {React.ReactElement}
+ */
+Modal.Subtitle = ({
+ children,
+ className,
+ ...props
+}: DivProps): React.ReactElement => (
+
+ {children}
+
+);
+
+/**
+ * Represents the Modal component.
+ *
+ * @param {DivProps} props - The props for the Modal component.
+ * @returns {React.ReactElement}
+ */
+Modal.Actions = ({
+ children,
+ className,
+ ...props
+}: DivProps): React.ReactElement => (
+
+ {children}
+
+);
+
+/**
+ * Represents the Modal component.
+ *
+ * @param {ModalActionProps} props - The props for the Modal component.
+ * @returns {React.ReactElement}
+ */
+Modal.Action = ({
+ fullWidth = false,
+ onClick,
+ className,
+ ...props
+}: ModalActionProps): React.ReactElement => {
+ const { closeModal } = useModal();
+
+ const handleClick = async () => {
+ if (onClick) {
+ await onClick();
+ }
+ closeModal();
+ };
+
+ return (
+
+ {props.children}
+
+ );
+};
+
+/**
+ * Represents the Modal component.
+ *
+ * @param {ModalActionProps} props - The props for the Modal component.
+ * @returns {React.ReactElement}
+ */
+Modal.Close = (props: ModalActionProps): React.ReactElement => (
+
+ {props.children}
+
+);
+
+export default Modal;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f1bdaf1..f20cc7c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -371,7 +371,7 @@ importers:
version: 1.0.7(tailwindcss@3.4.4)
tsup:
specifier: ^8.1.1
- version: 8.1.1(@swc/core@1.6.13)(postcss@8.4.39)(tsx@4.17.0)(typescript@5.5.3)
+ version: 8.1.1(@swc/core@1.6.13)(postcss@8.4.39)(typescript@5.5.3)
zod:
specifier: ^3.23.8
version: 3.23.8
@@ -402,7 +402,7 @@ importers:
version: 8.4.39
postcss-cli:
specifier: ^11.0.0
- version: 11.0.0(postcss@8.4.39)(tsx@4.17.0)
+ version: 11.0.0(postcss@8.4.39)
postcss-lightningcss:
specifier: ^1.0.0
version: 1.0.0(postcss@8.4.39)
@@ -1080,6 +1080,7 @@ packages:
cpu: [ppc64]
os: [aix]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/android-arm64@0.23.0:
@@ -1088,6 +1089,7 @@ packages:
cpu: [arm64]
os: [android]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/android-arm@0.23.0:
@@ -1096,6 +1098,7 @@ packages:
cpu: [arm]
os: [android]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/android-x64@0.23.0:
@@ -1104,6 +1107,7 @@ packages:
cpu: [x64]
os: [android]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/darwin-arm64@0.23.0:
@@ -1112,6 +1116,7 @@ packages:
cpu: [arm64]
os: [darwin]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/darwin-x64@0.23.0:
@@ -1120,6 +1125,7 @@ packages:
cpu: [x64]
os: [darwin]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/freebsd-arm64@0.23.0:
@@ -1128,6 +1134,7 @@ packages:
cpu: [arm64]
os: [freebsd]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/freebsd-x64@0.23.0:
@@ -1136,6 +1143,7 @@ packages:
cpu: [x64]
os: [freebsd]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/linux-arm64@0.23.0:
@@ -1144,6 +1152,7 @@ packages:
cpu: [arm64]
os: [linux]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/linux-arm@0.23.0:
@@ -1152,6 +1161,7 @@ packages:
cpu: [arm]
os: [linux]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/linux-ia32@0.23.0:
@@ -1160,6 +1170,7 @@ packages:
cpu: [ia32]
os: [linux]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/linux-loong64@0.23.0:
@@ -1168,6 +1179,7 @@ packages:
cpu: [loong64]
os: [linux]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/linux-mips64el@0.23.0:
@@ -1176,6 +1188,7 @@ packages:
cpu: [mips64el]
os: [linux]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/linux-ppc64@0.23.0:
@@ -1184,6 +1197,7 @@ packages:
cpu: [ppc64]
os: [linux]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/linux-riscv64@0.23.0:
@@ -1192,6 +1206,7 @@ packages:
cpu: [riscv64]
os: [linux]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/linux-s390x@0.23.0:
@@ -1200,6 +1215,7 @@ packages:
cpu: [s390x]
os: [linux]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/linux-x64@0.23.0:
@@ -1208,6 +1224,7 @@ packages:
cpu: [x64]
os: [linux]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/netbsd-x64@0.23.0:
@@ -1216,6 +1233,7 @@ packages:
cpu: [x64]
os: [netbsd]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/openbsd-arm64@0.23.0:
@@ -1224,6 +1242,7 @@ packages:
cpu: [arm64]
os: [openbsd]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/openbsd-x64@0.23.0:
@@ -1232,6 +1251,7 @@ packages:
cpu: [x64]
os: [openbsd]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/sunos-x64@0.23.0:
@@ -1240,6 +1260,7 @@ packages:
cpu: [x64]
os: [sunos]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/win32-arm64@0.23.0:
@@ -1248,6 +1269,7 @@ packages:
cpu: [arm64]
os: [win32]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/win32-ia32@0.23.0:
@@ -1256,6 +1278,7 @@ packages:
cpu: [ia32]
os: [win32]
requiresBuild: true
+ dev: false
optional: true
/@esbuild/win32-x64@0.23.0:
@@ -1264,6 +1287,7 @@ packages:
cpu: [x64]
os: [win32]
requiresBuild: true
+ dev: false
optional: true
/@eslint-community/eslint-utils@4.4.0(eslint@8.57.0):
@@ -4890,6 +4914,7 @@ packages:
'@esbuild/win32-arm64': 0.23.0
'@esbuild/win32-ia32': 0.23.0
'@esbuild/win32-x64': 0.23.0
+ dev: false
/escalade@3.1.2:
resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==}
@@ -8386,7 +8411,7 @@ packages:
engines: {node: '>= 0.4'}
dev: true
- /postcss-cli@11.0.0(postcss@8.4.39)(tsx@4.17.0):
+ /postcss-cli@11.0.0(postcss@8.4.39):
resolution: {integrity: sha512-xMITAI7M0u1yolVcXJ9XTZiO9aO49mcoKQy6pCDFdMh9kGqhzLVpWxeD/32M/QBmkhcGypZFFOLNLmIW4Pg4RA==}
engines: {node: '>=18'}
hasBin: true
@@ -8400,7 +8425,7 @@ packages:
globby: 14.0.2
picocolors: 1.0.1
postcss: 8.4.39
- postcss-load-config: 5.1.0(postcss@8.4.39)(tsx@4.17.0)
+ postcss-load-config: 5.1.0(postcss@8.4.39)
postcss-reporter: 7.1.0(postcss@8.4.39)
pretty-hrtime: 1.0.3
read-cache: 1.0.0
@@ -8458,7 +8483,7 @@ packages:
postcss: 8.4.39
yaml: 2.4.5
- /postcss-load-config@5.1.0(postcss@8.4.39)(tsx@4.17.0):
+ /postcss-load-config@5.1.0(postcss@8.4.39):
resolution: {integrity: sha512-G5AJ+IX0aD0dygOE0yFZQ/huFFMSNneyfp0e3/bT05a8OfPC5FUoZRPfGijUdGOJNMewJiwzcHJXFafFzeKFVA==}
engines: {node: '>= 18'}
peerDependencies:
@@ -8475,10 +8500,31 @@ packages:
dependencies:
lilconfig: 3.1.2
postcss: 8.4.39
- tsx: 4.17.0
yaml: 2.4.5
dev: true
+ /postcss-load-config@6.0.1(postcss@8.4.39):
+ resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}
+ engines: {node: '>= 18'}
+ peerDependencies:
+ jiti: '>=1.21.0'
+ postcss: '>=8.0.9'
+ tsx: ^4.8.1
+ yaml: ^2.4.2
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+ postcss:
+ optional: true
+ tsx:
+ optional: true
+ yaml:
+ optional: true
+ dependencies:
+ lilconfig: 3.1.2
+ postcss: 8.4.39
+ dev: false
+
/postcss-load-config@6.0.1(postcss@8.4.39)(tsx@4.17.0):
resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}
engines: {node: '>= 18'}
@@ -9896,7 +9942,7 @@ packages:
/tslib@2.6.2:
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
- /tsup@8.1.1(@swc/core@1.6.13)(postcss@8.4.39)(tsx@4.17.0)(typescript@5.5.3):
+ /tsup@8.1.1(@swc/core@1.6.13)(postcss@8.4.39)(typescript@5.5.3):
resolution: {integrity: sha512-lLXP3BshJ6y/32b3tPZUB2siD2mkJ6mLzhbPOShfjogSc3aRw8MhbBV4cPKbqkbXuhsJR+c9B0W9RHMbtbXLMQ==}
engines: {node: '>=18'}
hasBin: true
@@ -9926,7 +9972,7 @@ packages:
globby: 11.1.0
joycon: 3.1.1
postcss: 8.4.39
- postcss-load-config: 6.0.1(postcss@8.4.39)(tsx@4.17.0)
+ postcss-load-config: 6.0.1(postcss@8.4.39)
resolve-from: 5.0.0
rollup: 4.18.1
source-map: 0.8.0-beta.0
@@ -10001,6 +10047,7 @@ packages:
get-tsconfig: 4.7.5
optionalDependencies:
fsevents: 2.3.3
+ dev: false
/turbo-darwin-64@2.0.9:
resolution: {integrity: sha512-owlGsOaExuVGBUfrnJwjkL1BWlvefjSKczEAcpLx4BI7Oh6ttakOi+JyomkPkFlYElRpjbvlR2gP8WIn6M/+xQ==}
From 57be1fa66a01fc90ce5dc5bc34e4a2780b908327 Mon Sep 17 00:00:00 2001
From: ruru <142723369+ruru-m07@users.noreply.github.com>
Date: Fri, 23 Aug 2024 14:18:34 +0530
Subject: [PATCH 2/3] fix(build): resolve build type errors
---
apps/www/public/registry/components/modal.json | 2 +-
packages/ui/src/components/modal.tsx | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/apps/www/public/registry/components/modal.json b/apps/www/public/registry/components/modal.json
index 83af4eb..7fa6bd3 100644
--- a/apps/www/public/registry/components/modal.json
+++ b/apps/www/public/registry/components/modal.json
@@ -3,7 +3,7 @@
"files": [
{
"name": "modal.tsx",
- "content": "import React, {\n useState,\n useContext,\n useCallback,\n ReactNode,\n createContext,\n useEffect,\n} from \"react\";\nimport {\n AnimatePresence,\n ForwardRefComponent,\n HTMLMotionProps,\n motion,\n} from \"framer-motion\";\nimport { cn } from \"@/utils/cn\";\nimport { Button, ButtonProps } from \"./button\";\n\n\nexport interface ModalContextProps {\n \n isOpen: boolean;\n \n openModal: () => void;\n \n closeModal: () => void;\n}\n\n\nexport interface ModalProps\n extends ForwardRefComponent> {\n \n children: ReactNode;\n \n onClickOutside?: () => void;\n}\n\n\nexport interface ModalActionProps extends ButtonProps {\n \n fullWidth?: boolean;\n \n onClick?: () => void | Promise;\n}\n\n\nexport interface TriggerProps extends ButtonProps {\n \n children: ReactNode;\n \n onClick?: () => void;\n \n asChild?: boolean;\n}\n\n\nexport interface DivProps\n extends React.DetailedHTMLProps<\n React.HTMLAttributes,\n HTMLDivElement\n > {}\n\n\nconst ModalContext = createContext(undefined);\n\n\nexport const ModalProvider = ({\n children,\n}: {\n children: ReactNode;\n}): React.ReactElement => {\n const [isOpen, setIsOpen] = useState(false);\n\n const openModal = useCallback(() => setIsOpen(true), []);\n const closeModal = useCallback(() => setIsOpen(false), []);\n\n return (\n \n {children}\n \n );\n};\n\n\nexport const useModal = (): ModalContextProps => {\n const context = useContext(ModalContext);\n if (!context) {\n throw new Error(\"useModal must be used within a ModalProvider\");\n }\n return context;\n};\n\n\nconst Modal = ({\n children,\n onClickOutside,\n ...props\n}: ModalProps): React.ReactElement => {\n const { isOpen, closeModal } = useModal();\n\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n closeModal();\n }\n };\n\n if (isOpen) {\n document.addEventListener(\"keydown\", handleEscape);\n } else {\n document.removeEventListener(\"keydown\", handleEscape);\n }\n\n return () => document.removeEventListener(\"keydown\", handleEscape);\n }, [isOpen, closeModal]);\n\n const modalVariants = {\n hidden: {\n opacity: 0,\n y: -50,\n rotateX: \"0deg\",\n transition: { duration: 0.15 },\n },\n visible: {\n opacity: 1,\n y: 0,\n rotateX: \"0deg\",\n transition: { duration: 0.15 },\n },\n exit: {\n opacity: 0,\n y: -50,\n rotateX: \"-10deg\",\n transition: { duration: 0.15 },\n },\n };\n\n return (\n \n {isOpen && (\n \n e.stopPropagation()}\n variants={modalVariants}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"exit\"\n >\n {children}\n \n \n )}\n \n );\n};\n\n\nModal.Trigger = ({\n children,\n onClick,\n asChild = false,\n}: TriggerProps): React.ReactElement => {\n const { openModal } = useModal();\n\n const handleClick = () => {\n openModal();\n if (onClick) {\n onClick();\n }\n };\n\n if (asChild) {\n return (\n \n {children}\n
\n );\n }\n\n return {children} ;\n};\n\n\nModal.Body = ({ children, ...props }: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Header = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Title = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n \n);\n\n\nModal.Content = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Subtitle = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Actions = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Action = ({\n fullWidth = false,\n onClick,\n className,\n ...props\n}: ModalActionProps): React.ReactElement => {\n const { closeModal } = useModal();\n\n const handleClick = async () => {\n if (onClick) {\n await onClick();\n }\n closeModal();\n };\n\n return (\n \n {props.children}\n \n );\n};\n\n\nModal.Close = (props: ModalActionProps): React.ReactElement => (\n \n {props.children}\n \n);\n\nexport default Modal;\n"
+ "content": "import React, {\n useState,\n useContext,\n useCallback,\n ReactNode,\n createContext,\n useEffect,\n} from \"react\";\nimport {\n AnimatePresence,\n ForwardRefComponent,\n HTMLMotionProps,\n motion,\n} from \"framer-motion\";\nimport { cn } from \"@/utils/cn\";\nimport { Button, ButtonProps } from \"./button\";\n\n\nexport interface ModalContextProps {\n \n isOpen: boolean;\n \n openModal: () => void;\n \n closeModal: () => void;\n}\n\n\nexport interface ModalProps\n extends Partial>> {\n \n children: ReactNode;\n \n onClickOutside?: () => void;\n}\n\n\nexport interface ModalActionProps extends ButtonProps {\n \n fullWidth?: boolean;\n \n onClick?: () => void | Promise;\n}\n\n\nexport interface TriggerProps extends ButtonProps {\n \n children: ReactNode;\n \n onClick?: () => void;\n \n asChild?: boolean;\n}\n\n\nexport interface DivProps\n extends React.DetailedHTMLProps<\n React.HTMLAttributes,\n HTMLDivElement\n > {}\n\n\nconst ModalContext = createContext(undefined);\n\n\nexport const ModalProvider = ({\n children,\n}: {\n children: ReactNode;\n}): React.ReactElement => {\n const [isOpen, setIsOpen] = useState(false);\n\n const openModal = useCallback(() => setIsOpen(true), []);\n const closeModal = useCallback(() => setIsOpen(false), []);\n\n return (\n \n {children}\n \n );\n};\n\n\nexport const useModal = (): ModalContextProps => {\n const context = useContext(ModalContext);\n if (!context) {\n throw new Error(\"useModal must be used within a ModalProvider\");\n }\n return context;\n};\n\n\nconst Modal = ({\n children,\n onClickOutside,\n ...props\n}: ModalProps): React.ReactElement => {\n const { isOpen, closeModal } = useModal();\n\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n closeModal();\n }\n };\n\n if (isOpen) {\n document.addEventListener(\"keydown\", handleEscape);\n } else {\n document.removeEventListener(\"keydown\", handleEscape);\n }\n\n return () => document.removeEventListener(\"keydown\", handleEscape);\n }, [isOpen, closeModal]);\n\n const modalVariants = {\n hidden: {\n opacity: 0,\n y: -50,\n rotateX: \"0deg\",\n transition: { duration: 0.15 },\n },\n visible: {\n opacity: 1,\n y: 0,\n rotateX: \"0deg\",\n transition: { duration: 0.15 },\n },\n exit: {\n opacity: 0,\n y: -50,\n rotateX: \"-10deg\",\n transition: { duration: 0.15 },\n },\n };\n\n return (\n \n {isOpen && (\n \n e.stopPropagation()}\n variants={modalVariants}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"exit\"\n >\n {children}\n \n \n )}\n \n );\n};\n\n\nModal.Trigger = ({\n children,\n onClick,\n asChild = false,\n}: TriggerProps): React.ReactElement => {\n const { openModal } = useModal();\n\n const handleClick = () => {\n openModal();\n if (onClick) {\n onClick();\n }\n };\n\n if (asChild) {\n return (\n \n {children}\n
\n );\n }\n\n return {children} ;\n};\n\n\nModal.Body = ({ children, ...props }: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Header = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Title = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n \n);\n\n\nModal.Content = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Subtitle = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Actions = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Action = ({\n fullWidth = false,\n onClick,\n className,\n ...props\n}: ModalActionProps): React.ReactElement => {\n const { closeModal } = useModal();\n\n const handleClick = async () => {\n if (onClick) {\n await onClick();\n }\n closeModal();\n };\n\n return (\n \n {props.children}\n \n );\n};\n\n\nModal.Close = (props: ModalActionProps): React.ReactElement => (\n \n {props.children}\n \n);\n\nexport default Modal;\n"
}
],
"type": "components:ui",
diff --git a/packages/ui/src/components/modal.tsx b/packages/ui/src/components/modal.tsx
index bb08cfe..193ac33 100644
--- a/packages/ui/src/components/modal.tsx
+++ b/packages/ui/src/components/modal.tsx
@@ -45,7 +45,7 @@ export interface ModalContextProps {
* Represents the props for the Modal component.
*/
export interface ModalProps
- extends ForwardRefComponent> {
+ extends Partial>> {
/**
* The children of the Modal component.
*/
From 67a90baf382ed7098bcc68ddc2c7afd640847559 Mon Sep 17 00:00:00 2001
From: ruru <142723369+ruru-m07@users.noreply.github.com>
Date: Fri, 23 Aug 2024 17:56:59 +0530
Subject: [PATCH 3/3] docs(component): add documentation for modal component
---
.../components/preview/Modal/customWidth.tsx | 56 ++
.../components/preview/Modal/customWidth2.tsx | 57 ++
.../www/components/preview/Modal/disabled.tsx | 55 ++
apps/www/components/preview/Modal/preview.tsx | 93 +++
.../components/preview/Modal/singleButton.tsx | 56 ++
apps/www/components/preview/Modal/trigger.tsx | 55 ++
apps/www/components/preview/Modal/usage.tsx | 60 ++
apps/www/components/preview/index.tsx | 6 +
apps/www/content/docs/cli.mdx | 2 +-
apps/www/content/docs/components/modal.mdx | 550 ++++++++++++++++++
apps/www/content/docs/components/textarea.mdx | 58 +-
.../www/public/registry/components/modal.json | 2 +-
packages/ui/src/components/modal.tsx | 19 +-
13 files changed, 1034 insertions(+), 35 deletions(-)
create mode 100644 apps/www/components/preview/Modal/customWidth.tsx
create mode 100644 apps/www/components/preview/Modal/customWidth2.tsx
create mode 100644 apps/www/components/preview/Modal/disabled.tsx
create mode 100644 apps/www/components/preview/Modal/preview.tsx
create mode 100644 apps/www/components/preview/Modal/singleButton.tsx
create mode 100644 apps/www/components/preview/Modal/trigger.tsx
create mode 100644 apps/www/components/preview/Modal/usage.tsx
create mode 100644 apps/www/content/docs/components/modal.mdx
diff --git a/apps/www/components/preview/Modal/customWidth.tsx b/apps/www/components/preview/Modal/customWidth.tsx
new file mode 100644
index 0000000..4bfbdc6
--- /dev/null
+++ b/apps/www/components/preview/Modal/customWidth.tsx
@@ -0,0 +1,56 @@
+"use client";
+
+import React from "react";
+import { Button } from "ruru-ui/components/button";
+import { Input } from "ruru-ui/components/input";
+import Modal, { ModalProvider } from "ruru-ui/components/modal";
+
+const CustomWidth = () => {
+ return (
+
+
+ Open Modal
+
+
+
+
+
+ Custom Width
+
+
+ This modal opens with a Custom Width button.
+
+
+
+
+
+
+
+
+ Cancel
+
+
+
+
+ );
+};
+
+export default CustomWidth;
diff --git a/apps/www/components/preview/Modal/customWidth2.tsx b/apps/www/components/preview/Modal/customWidth2.tsx
new file mode 100644
index 0000000..7a299c2
--- /dev/null
+++ b/apps/www/components/preview/Modal/customWidth2.tsx
@@ -0,0 +1,57 @@
+"use client";
+
+import React from "react";
+import { Button } from "ruru-ui/components/button";
+import { Input } from "ruru-ui/components/input";
+import Modal, { ModalProvider } from "ruru-ui/components/modal";
+
+const CustomWidth2 = () => {
+ return (
+
+
+ Open Modal
+
+
+
+
+
+ Custom Width
+
+
+ This modal opens with a Custom Width button.
+
+
+
+
+
+
+
+
+ Cancel
+
+ Submit
+
+
+
+ );
+};
+
+export default CustomWidth2;
diff --git a/apps/www/components/preview/Modal/disabled.tsx b/apps/www/components/preview/Modal/disabled.tsx
new file mode 100644
index 0000000..bd8a2c8
--- /dev/null
+++ b/apps/www/components/preview/Modal/disabled.tsx
@@ -0,0 +1,55 @@
+"use client";
+
+import React from "react";
+import { Button } from "ruru-ui/components/button";
+import { Input } from "ruru-ui/components/input";
+import Modal, { ModalProvider } from "ruru-ui/components/modal";
+
+const Disabled = () => {
+ return (
+
+
+ Open Modal
+
+
+
+
+
+ Disabled
+
+
+ This modal opens with a Disabled button.
+
+
+
+
+
+
+
+ Cancel
+ Submit
+
+
+
+ );
+};
+
+export default Disabled;
diff --git a/apps/www/components/preview/Modal/preview.tsx b/apps/www/components/preview/Modal/preview.tsx
new file mode 100644
index 0000000..24e46d0
--- /dev/null
+++ b/apps/www/components/preview/Modal/preview.tsx
@@ -0,0 +1,93 @@
+"use client";
+
+import React, { useState } from "react";
+import Modal, { ModalProvider } from "ruru-ui/components/modal";
+import { Input } from "ruru-ui/components/input";
+
+const Preview = () => {
+ const [isLoading, setIsLoading] = useState(false);
+ const [status, setStatus] = useState(false);
+
+ const handleSubmit = async () => {
+ setIsLoading(true);
+
+ await new Promise((resolve) => setTimeout(resolve, 2000));
+ setStatus(true);
+ setIsLoading(false);
+
+ await new Promise((resolve) => setTimeout(resolve, 500));
+
+ setStatus(false);
+ };
+
+ return (
+
+ );
+};
+
+export default Preview;
diff --git a/apps/www/components/preview/Modal/singleButton.tsx b/apps/www/components/preview/Modal/singleButton.tsx
new file mode 100644
index 0000000..15dcbb7
--- /dev/null
+++ b/apps/www/components/preview/Modal/singleButton.tsx
@@ -0,0 +1,56 @@
+"use client";
+
+import React from "react";
+import { Button } from "ruru-ui/components/button";
+import { Input } from "ruru-ui/components/input";
+import Modal, { ModalProvider } from "ruru-ui/components/modal";
+
+const SingleButton = () => {
+ return (
+
+
+ Open Modal
+
+
+
+
+
+ Single Button
+
+
+ This modal opens with a Single Button.
+
+
+
+
+
+
+
+
+ Cancel
+
+
+
+
+ );
+};
+
+export default SingleButton;
diff --git a/apps/www/components/preview/Modal/trigger.tsx b/apps/www/components/preview/Modal/trigger.tsx
new file mode 100644
index 0000000..e424323
--- /dev/null
+++ b/apps/www/components/preview/Modal/trigger.tsx
@@ -0,0 +1,55 @@
+"use client";
+
+import React from "react";
+import { Button } from "ruru-ui/components/button";
+import { Input } from "ruru-ui/components/input";
+import Modal, { ModalProvider } from "ruru-ui/components/modal";
+
+const Trigger = () => {
+ return (
+
+
+ Open Modal
+
+
+
+
+
+ Custom Trigger
+
+
+ This modal opens with a custom trigger button.
+
+
+
+
+
+
+
+ Cancel
+ Submit
+
+
+
+ );
+};
+
+export default Trigger;
diff --git a/apps/www/components/preview/Modal/usage.tsx b/apps/www/components/preview/Modal/usage.tsx
new file mode 100644
index 0000000..855c688
--- /dev/null
+++ b/apps/www/components/preview/Modal/usage.tsx
@@ -0,0 +1,60 @@
+"use client";
+
+import React, { useState } from "react";
+import { Input } from "ruru-ui/components/input";
+import Modal, { ModalProvider } from "ruru-ui/components/modal";
+
+const Usage = () => {
+ const [isLoading, setIsLoading] = useState(false);
+
+ const handleSubmit = async () => {
+ setIsLoading(true);
+
+ await new Promise((resolve) => setTimeout(resolve, 2000));
+ setIsLoading(false);
+ };
+
+ return (
+
+
+ Open Modal
+
+
+
+
+ Create Username
+
+
+ Enter a unique name for your token to differentiate it from
+ other tokens and then select the scope.
+
+
+
+
+
+
+
+ Cancel
+
+ Submit
+
+
+
+
+
+ );
+};
+
+export default Usage;
diff --git a/apps/www/components/preview/index.tsx b/apps/www/components/preview/index.tsx
index cf81028..ca12b50 100644
--- a/apps/www/components/preview/index.tsx
+++ b/apps/www/components/preview/index.tsx
@@ -27,6 +27,7 @@ import {
import { BadgePreview } from "../badgePreview";
import Tabspreview from "../tabs";
+import { default as ModalPreview } from "./Modal/preview";
export default {
button: (
@@ -163,4 +164,9 @@ export default {
),
+ modal: (
+
+
+
+ ),
} as Record
;
diff --git a/apps/www/content/docs/cli.mdx b/apps/www/content/docs/cli.mdx
index 07481fb..803e2f2 100644
--- a/apps/www/content/docs/cli.mdx
+++ b/apps/www/content/docs/cli.mdx
@@ -1,5 +1,5 @@
---
-title: Cli
+title: CLI
description: The CLI is a command-line interface that allows you to add components to your project.
---
diff --git a/apps/www/content/docs/components/modal.mdx b/apps/www/content/docs/components/modal.mdx
new file mode 100644
index 0000000..bbabba9
--- /dev/null
+++ b/apps/www/content/docs/components/modal.mdx
@@ -0,0 +1,550 @@
+---
+title: Modal
+description: The Modal component is used to display content in a modal dialog.
+preview: modal
+---
+
+
+import Modal, { ModalProvider } from "ruru-ui/components/modal";
+import { Tab, Tabs } from "fumadocs-ui/components/tabs";
+import { Tabs as Rutabs, Tab as Rutab } from "ruru-ui/components/tabs";
+import Usage from "../../../components/preview/Modal/usage.tsx";
+import Trigger from "../../../components/preview/Modal/trigger.tsx";
+import SingleButton from "../../../components/preview/Modal/singleButton.tsx";
+import Disabled from "../../../components/preview/Modal/disabled.tsx";
+import CustomWidth from "../../../components/preview/Modal/customWidth.tsx";
+import CustomWidth2 from "../../../components/preview/Modal/customWidth2.tsx";
+import Preview from "../../../components/preview/Modal/preview.tsx";
+
+## Installation
+
+
+
+
+
+```bash
+npx ruru-ui-cli@latest add modal
+```
+
+
+```bash
+pnpm dlx ruru-ui-cli@latest add modal
+```
+
+
+```bash
+npx ruru-ui-cli@latest add modal
+```
+
+
+```bash
+bunx --bun ruru-ui-cli@latest add modal
+```
+
+
+
+
+
+```package-install
+npm install ruru-ui@latest
+```
+
+
+
+
+## Usage
+
+Here is an example of how to use the `Modal` component.
+
+
+
+
+
+
+```tsx
+"use client";
+
+import React, { useState } from "react";
+import { Input } from "ruru-ui/components/input";
+import Modal, { ModalProvider } from "ruru-ui/components/modal";
+
+const Usage = () => {
+ const [isLoading, setIsLoading] = useState(false);
+
+ const handleSubmit = async () => {
+ setIsLoading(true);
+
+ await new Promise((resolve) => setTimeout(resolve, 2000));
+ setIsLoading(false);
+ };
+
+ return (
+
+
+ Open Modal
+
+
+
+
+ Create Username
+
+
+ Enter a unique name for your token to differentiate it from
+ other tokens and then select the scope.
+
+
+
+
+
+
+
+ Cancel
+
+ Submit
+
+
+
+
+
+ );
+};
+
+export default Usage;
+```
+
+
+
+
+## Example
+
+### Modal with Trigger
+
+The `Modal` component can be used with a custom trigger button.
+
+
+
+
+
+
+```tsx
+"use client";
+
+import React from "react";
+import { Button } from "ruru-ui/components/button";
+import { Input } from "ruru-ui/components/input";
+import Modal, { ModalProvider } from "ruru-ui/components/modal";
+
+const TriggerDemo = () => {
+ return (
+
+
+ Open Custom Trigger
+
+
+
+
+
+ Custom Trigger
+
+
+ This modal opens with a custom trigger button.
+
+
+
+
+
+
+
+ Cancel
+ Submit
+
+
+
+ );
+};
+
+export default TriggerDemo;
+```
+
+
+
+
+### Single button
+
+The `Modal` component can be used with a single button.
+
+
+
+
+
+
+```tsx
+"use client";
+
+import React from "react";
+import { Button } from "ruru-ui/components/button";
+import { Input } from "ruru-ui/components/input";
+import Modal, { ModalProvider } from "ruru-ui/components/modal";
+
+const SingleButton = () => {
+ return (
+
+
+ Open Custom Trigger
+
+
+
+
+
+ Custom Trigger
+
+
+ This modal opens with a custom trigger button.
+
+
+
+
+
+
+
+ Cancel
+
+
+
+ );
+};
+
+export default SingleButton;
+```
+
+
+
+### Disabled actions
+
+The `Modal` component can be used with disabled actions.
+
+
+
+
+
+
+```tsx
+"use client";
+
+import React from "react";
+import { Button } from "ruru-ui/components/button";
+import { Input } from "ruru-ui/components/input";
+import Modal, { ModalProvider } from "ruru-ui/components/modal";
+
+const Disabled = () => {
+ return (
+
+
+ Open Custom Trigger
+
+
+
+
+
+ Custom Trigger
+
+
+ This modal opens with a custom trigger button.
+
+
+
+
+
+
+
+ Cancel
+ Submit
+
+
+
+ );
+};
+
+export default Disabled;
+```
+
+
+
+### Modal with custom width
+
+The `Modal` component can be used with a custom width.
+
+
+
+
+
+
+```tsx
+"use client";
+
+import React from "react";
+import { Button } from "ruru-ui/components/button";
+import { Input } from "ruru-ui/components/input";
+import Modal, { ModalProvider } from "ruru-ui/components/modal";
+
+const CustomWidth = () => {
+ return (
+
+
+ Open Modal
+
+
+
+
+
+ Custom Width
+
+
+ This modal opens with a Custom Width button.
+
+
+
+
+
+
+
+ Cancel
+
+
+
+ );
+};
+
+export default CustomWidth;
+```
+
+
+
+
+
+
+
+
+```tsx
+"use client";
+
+import React from "react";
+import { Button } from "ruru-ui/components/button";
+import { Input } from "ruru-ui/components/input";
+import Modal, { ModalProvider } from "ruru-ui/components/modal";
+
+const CustomWidth2 = () => {
+ return (
+
+
+ Open Modal
+
+
+
+
+
+ Custom Width
+
+
+ This modal opens with a Custom Width button.
+
+
+
+
+
+
+
+
+ Cancel
+
+ Submit
+
+
+
+ );
+};
+
+export default CustomWidth2;
+```
+
+
+
+### Preview Component
+
+The component that you see on preview
+
+
+
+
+
+
+```tsx
+"use client";
+
+import React, { useState } from "react";
+import Modal, { ModalProvider } from "ruru-ui/components/modal";
+import { Input } from "ruru-ui/components/input";
+
+const Preview = () => {
+ const [isLoading, setIsLoading] = useState(false);
+ const [status, setStatus] = useState(false);
+
+ const handleSubmit = async () => {
+ setIsLoading(true);
+
+ await new Promise((resolve) => setTimeout(resolve, 2000));
+ setStatus(true);
+ setIsLoading(false);
+
+ await new Promise((resolve) => setTimeout(resolve, 500));
+
+ setStatus(false);
+ };
+
+ return (
+
+ );
+};
+
+export default Preview;
+```
+
+
+
+## Props
+
+Here's how the props table would look for the `Modal` component, formatted in the same style:
+
+## Props
+
+### Modal
+
+| Name | Type | Default | Description |
+| ----------------- | --------------------------------- | ----------- | -------------------------------------------------------------------------------------- |
+| **children** | **ReactNode** | `undefined` | The children of the Modal component. |
+| **onClickOutside**| **() => void** | `undefined` | The function to call when the user clicks outside the modal. |
+
+### ModalAction
+
+| Name | Type | Default | Description |
+| ----------------- | --------------------------------- | ----------- | -------------------------------------------------------------------------------------- |
+| **fullWidth** | **boolean** | `false` | Determines if the action button should take the full width. |
+| **onClick** | **() =\> void \| Promise\** | `undefined` | The function to call when the user clicks the action. |
+
+### Trigger
+
+| Name | Type | Default | Description |
+| ----------------- | --------------------------------- | ----------- | -------------------------------------------------------------------------------------- |
+| **children** | **ReactNode** | `undefined` | The children of the Modal component. |
+| **onClick** | **() => void** | `undefined` | The function to call when the user clicks the trigger. |
+| **asChild** | **boolean** | `false` | Render as a child component. |
+
+### DivProps
+
+| Name | Type | Default | Description |
+| ----------------- | --------------------------------- | ----------- | -------------------------------------------------------------------------------------- |
+| **className** | **string** | `""` | Additional class names for the container. |
+| **children** | **ReactNode** | `undefined` | The children of the component. |
+
+### PTagProps
+
+| Name | Type | Default | Description |
+| ----------------- | --------------------------------- | ----------- | -------------------------------------------------------------------------------------- |
+| **className** | **string** | `""` | Additional class names for the paragraph element. |
+| **children** | **ReactNode** | `undefined` | The children of the paragraph element. |
+
diff --git a/apps/www/content/docs/components/textarea.mdx b/apps/www/content/docs/components/textarea.mdx
index 0c06252..f794464 100644
--- a/apps/www/content/docs/components/textarea.mdx
+++ b/apps/www/content/docs/components/textarea.mdx
@@ -11,38 +11,38 @@ import { Tabs as Rutabs, Tab as Rutab } from "ruru-ui/components/tabs";
## Installation
-
-
-
- ```bash
- npx ruru-ui-cli@latest add textarea
- ```
-
-
- ```bash
- pnpm dlx ruru-ui-cli@latest add textarea
- ```
-
-
- ```bash
- npx ruru-ui-cli@latest add textarea
- ```
-
-
- ```bash
- bunx --bun ruru-ui-cli@latest add textarea
- ```
-
-
+
+
+
+```bash
+npx ruru-ui-cli@latest add textarea
+```
+
+
+```bash
+pnpm dlx ruru-ui-cli@latest add textarea
+```
+
+
+```bash
+npx ruru-ui-cli@latest add textarea
+```
+
+
+```bash
+bunx --bun ruru-ui-cli@latest add textarea
+```
+
+
-
-
+
+
- ```package-install
- npm install ruru-ui@latest
- ```
+```package-install
+npm install ruru-ui@latest
+```
-
+
## Usage
diff --git a/apps/www/public/registry/components/modal.json b/apps/www/public/registry/components/modal.json
index 7fa6bd3..9389d04 100644
--- a/apps/www/public/registry/components/modal.json
+++ b/apps/www/public/registry/components/modal.json
@@ -3,7 +3,7 @@
"files": [
{
"name": "modal.tsx",
- "content": "import React, {\n useState,\n useContext,\n useCallback,\n ReactNode,\n createContext,\n useEffect,\n} from \"react\";\nimport {\n AnimatePresence,\n ForwardRefComponent,\n HTMLMotionProps,\n motion,\n} from \"framer-motion\";\nimport { cn } from \"@/utils/cn\";\nimport { Button, ButtonProps } from \"./button\";\n\n\nexport interface ModalContextProps {\n \n isOpen: boolean;\n \n openModal: () => void;\n \n closeModal: () => void;\n}\n\n\nexport interface ModalProps\n extends Partial>> {\n \n children: ReactNode;\n \n onClickOutside?: () => void;\n}\n\n\nexport interface ModalActionProps extends ButtonProps {\n \n fullWidth?: boolean;\n \n onClick?: () => void | Promise;\n}\n\n\nexport interface TriggerProps extends ButtonProps {\n \n children: ReactNode;\n \n onClick?: () => void;\n \n asChild?: boolean;\n}\n\n\nexport interface DivProps\n extends React.DetailedHTMLProps<\n React.HTMLAttributes,\n HTMLDivElement\n > {}\n\n\nconst ModalContext = createContext(undefined);\n\n\nexport const ModalProvider = ({\n children,\n}: {\n children: ReactNode;\n}): React.ReactElement => {\n const [isOpen, setIsOpen] = useState(false);\n\n const openModal = useCallback(() => setIsOpen(true), []);\n const closeModal = useCallback(() => setIsOpen(false), []);\n\n return (\n \n {children}\n \n );\n};\n\n\nexport const useModal = (): ModalContextProps => {\n const context = useContext(ModalContext);\n if (!context) {\n throw new Error(\"useModal must be used within a ModalProvider\");\n }\n return context;\n};\n\n\nconst Modal = ({\n children,\n onClickOutside,\n ...props\n}: ModalProps): React.ReactElement => {\n const { isOpen, closeModal } = useModal();\n\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n closeModal();\n }\n };\n\n if (isOpen) {\n document.addEventListener(\"keydown\", handleEscape);\n } else {\n document.removeEventListener(\"keydown\", handleEscape);\n }\n\n return () => document.removeEventListener(\"keydown\", handleEscape);\n }, [isOpen, closeModal]);\n\n const modalVariants = {\n hidden: {\n opacity: 0,\n y: -50,\n rotateX: \"0deg\",\n transition: { duration: 0.15 },\n },\n visible: {\n opacity: 1,\n y: 0,\n rotateX: \"0deg\",\n transition: { duration: 0.15 },\n },\n exit: {\n opacity: 0,\n y: -50,\n rotateX: \"-10deg\",\n transition: { duration: 0.15 },\n },\n };\n\n return (\n \n {isOpen && (\n \n e.stopPropagation()}\n variants={modalVariants}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"exit\"\n >\n {children}\n \n \n )}\n \n );\n};\n\n\nModal.Trigger = ({\n children,\n onClick,\n asChild = false,\n}: TriggerProps): React.ReactElement => {\n const { openModal } = useModal();\n\n const handleClick = () => {\n openModal();\n if (onClick) {\n onClick();\n }\n };\n\n if (asChild) {\n return (\n \n {children}\n
\n );\n }\n\n return {children} ;\n};\n\n\nModal.Body = ({ children, ...props }: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Header = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Title = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n \n);\n\n\nModal.Content = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Subtitle = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Actions = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Action = ({\n fullWidth = false,\n onClick,\n className,\n ...props\n}: ModalActionProps): React.ReactElement => {\n const { closeModal } = useModal();\n\n const handleClick = async () => {\n if (onClick) {\n await onClick();\n }\n closeModal();\n };\n\n return (\n \n {props.children}\n \n );\n};\n\n\nModal.Close = (props: ModalActionProps): React.ReactElement => (\n \n {props.children}\n \n);\n\nexport default Modal;\n"
+ "content": "\"use client\";\n\nimport React, {\n useState,\n useContext,\n useCallback,\n ReactNode,\n createContext,\n useEffect,\n} from \"react\";\nimport {\n AnimatePresence,\n ForwardRefComponent,\n HTMLMotionProps,\n motion,\n} from \"framer-motion\";\nimport { cn } from \"@/utils/cn\";\nimport { Button, ButtonProps } from \"./button\";\n\n\nexport interface ModalContextProps {\n \n isOpen: boolean;\n \n openModal: () => void;\n \n closeModal: () => void;\n}\n\n\nexport interface ModalProps\n extends Partial>> {\n \n children: ReactNode;\n \n onClickOutside?: () => void;\n}\n\n\nexport interface ModalActionProps extends ButtonProps {\n \n fullWidth?: boolean;\n \n onClick?: () => void | Promise;\n}\n\n\nexport interface TriggerProps extends ButtonProps {\n \n children: ReactNode;\n \n onClick?: () => void;\n \n asChild?: boolean;\n}\n\n\nexport interface DivProps\n extends React.DetailedHTMLProps<\n React.HTMLAttributes,\n HTMLDivElement\n > {}\n\n\nexport interface PTagProps\n extends React.DetailedHTMLProps<\n React.HTMLAttributes,\n HTMLParagraphElement\n > {}\n\n\nconst ModalContext = createContext(undefined);\n\n\nexport const ModalProvider = ({\n children,\n}: {\n children: ReactNode;\n}): React.ReactElement => {\n const [isOpen, setIsOpen] = useState(false);\n\n const openModal = useCallback(() => setIsOpen(true), []);\n const closeModal = useCallback(() => setIsOpen(false), []);\n\n return (\n \n {children}\n \n );\n};\n\n\nexport const useModal = (): ModalContextProps => {\n const context = useContext(ModalContext);\n if (!context) {\n throw new Error(\"useModal must be used within a ModalProvider\");\n }\n return context;\n};\n\n\nconst Modal = ({\n children,\n onClickOutside,\n ...props\n}: ModalProps): React.ReactElement => {\n const { isOpen, closeModal } = useModal();\n\n useEffect(() => {\n const handleEscape = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n closeModal();\n }\n };\n\n if (isOpen) {\n document.addEventListener(\"keydown\", handleEscape);\n } else {\n document.removeEventListener(\"keydown\", handleEscape);\n }\n\n return () => document.removeEventListener(\"keydown\", handleEscape);\n }, [isOpen, closeModal]);\n\n const modalVariants = {\n hidden: {\n opacity: 0,\n y: -50,\n rotateX: \"0deg\",\n transition: { duration: 0.15 },\n },\n visible: {\n opacity: 1,\n y: 0,\n rotateX: \"0deg\",\n transition: { duration: 0.15 },\n },\n exit: {\n opacity: 0,\n y: -50,\n rotateX: \"-10deg\",\n transition: { duration: 0.15 },\n },\n };\n\n return (\n \n {isOpen && (\n \n e.stopPropagation()}\n variants={modalVariants}\n initial=\"hidden\"\n animate=\"visible\"\n exit=\"exit\"\n >\n {children}\n \n \n )}\n \n );\n};\n\n\nModal.Trigger = ({\n children,\n onClick,\n asChild = false,\n}: TriggerProps): React.ReactElement => {\n const { openModal } = useModal();\n\n const handleClick = () => {\n openModal();\n if (onClick) {\n onClick();\n }\n };\n\n if (asChild) {\n return (\n \n {children}\n
\n );\n }\n\n return {children} ;\n};\n\n\nModal.Body = ({ children, ...props }: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Header = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Title = ({\n children,\n className,\n ...props\n}: PTagProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Content = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Subtitle = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Actions = ({\n children,\n className,\n ...props\n}: DivProps): React.ReactElement => (\n \n {children}\n
\n);\n\n\nModal.Action = ({\n fullWidth = false,\n onClick,\n className,\n ...props\n}: ModalActionProps): React.ReactElement => {\n const { closeModal } = useModal();\n\n const handleClick = async () => {\n if (onClick) {\n await onClick();\n }\n closeModal();\n };\n\n return (\n \n {props.children}\n \n );\n};\n\n\nModal.Close = (props: ModalActionProps): React.ReactElement => (\n \n {props.children}\n \n);\n\nexport default Modal;\n"
}
],
"type": "components:ui",
diff --git a/packages/ui/src/components/modal.tsx b/packages/ui/src/components/modal.tsx
index 193ac33..017e5d7 100644
--- a/packages/ui/src/components/modal.tsx
+++ b/packages/ui/src/components/modal.tsx
@@ -1,3 +1,5 @@
+"use client";
+
import React, {
useState,
useContext,
@@ -109,6 +111,15 @@ export interface DivProps
HTMLDivElement
> {}
+/**
+ * Represents the props for the Modal component.
+ */
+export interface PTagProps
+ extends React.DetailedHTMLProps<
+ React.HTMLAttributes,
+ HTMLParagraphElement
+ > {}
+
/**
* Represents the Modal component.
*
@@ -296,17 +307,17 @@ Modal.Header = ({
/**
* Represents the Modal component.
*
- * @param {DivProps} props - The props for the Modal component.
+ * @param {PTagProps} props - The props for the Modal component.
* @returns {React.ReactElement}
*/
Modal.Title = ({
children,
className,
...props
-}: DivProps): React.ReactElement => (
-
+}: PTagProps): React.ReactElement => (
+
{children}
-
+
);
/**