diff --git a/app/components/customButton/customButton.tsx b/app/components/customButton/customButton.tsx new file mode 100644 index 00000000..0ab3e47e --- /dev/null +++ b/app/components/customButton/customButton.tsx @@ -0,0 +1,117 @@ +import { Link } from "@remix-run/react"; +import { LoaderCircle } from "lucide-react"; +import React from "react"; + +type Variant = + | "default" + | "primary" + | "destructive" + | "subtle" + | "loading" + | "outline" + | "secondary" + | "ghost" + | "link"; + +type Size = "default" | "sm" | "lg" | "link" | "icon" | "circle"; + +interface ButtonProperties { + leftIcon?: React.ReactElement; + rightIcon?: React.ReactElement; + isLoading?: boolean; + href?: string; + variant?: Variant; + size?: Size; + icon?: React.ReactNode; + children?: React.ReactNode; + isIconOnly?: boolean; + isLeftIconVisible?: boolean; + isRightIconVisible?: boolean; + isDisabled?: boolean; + ariaLabel?: string; + onClick?: () => void; + className?: string; +} + +const Button: React.FC = ({ + leftIcon, + rightIcon, + isLoading, + href, + variant = "default", + size = "default", + icon, + children, + isLeftIconVisible = true, + isRightIconVisible = true, + isDisabled, + ariaLabel, + onClick, + className = "", +}) => { + const baseClasses = + "inline-flex items-center justify-center px-[16px] py-[8px] gap-[8px] text-[14px] font-medium rounded-md"; + const variantClasses = { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + primary: "bg-primary text-[#FFFFFF] hover:bg-[#F97316]", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + subtle: "bg-[#F1F5F9] hover:bg-[#E2E8F0]", + loading: "bg-[#0F172A] text-[#FFFFFF]", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }; + const sizeClasses = { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + link: "h-auto px-0", + icon: "h-10 w-10 !px-0", + circle: "h-[40px] w-[40px] !rounded-full !px-0", + }; + const loadingClasses = "opacity-75 cursor-not-allowed"; + const buttonClasses = `${baseClasses} ${variantClasses[variant]} ${sizeClasses[size]} ${className} ${isLoading ? loadingClasses : ""}`; + + const content = ( + <> + {isLoading && } + {isLeftIconVisible && leftIcon && !isLoading && ( + {leftIcon} + )} + {children || icon} + {isRightIconVisible && rightIcon && ( + {rightIcon} + )} + + ); + + if (href) { + return ( + + {content} + + ); + } + + return ( + + ); +}; + +export default Button; diff --git a/app/components/ui/footerCookieConsent.tsx b/app/components/ui/footerCookieConsent.tsx index bdb74902..826f1341 100644 --- a/app/components/ui/footerCookieConsent.tsx +++ b/app/components/ui/footerCookieConsent.tsx @@ -1,5 +1,3 @@ -import type { FC } from "react"; - import { Button } from "./button"; const handleAccept = () => { @@ -14,7 +12,7 @@ const handleSettings = () => { console.log("Cookie settings opened"); }; -const FooterCookieConsent: FC = () => { +const FooterCookieConsent: React.FC = () => { return (
diff --git a/guides/ButtonUsage.md b/guides/ButtonUsage.md new file mode 100644 index 00000000..0de12b02 --- /dev/null +++ b/guides/ButtonUsage.md @@ -0,0 +1,74 @@ +# Button Component Usage Guide + +The `Button` component is a versatile and customizable button component that supports various styles, sizes, and additional features. This guide provides detailed instructions on how to use the `Button` component. + +## Props + +- **leftIcon**: `React.ReactElement` (Optional) + - An element to be displayed on the left side of the button. + +- **rightIcon**: `React.ReactElement` (Optional) + - An element to be displayed on the right side of the button. + +- **isLoading**: `boolean` (Optional) + - If `true`, a loading spinner is displayed, and the button is disabled. + +- **href**: `string` (Optional) + - A URL that converts the button into a link. + +- **variant**: `Variant` (Optional, default: `"default"`) + - The style variant of the button. Options include: + - `"default"` + - `"primary"` + - `"destructive"` + - `"subtle"` + - `"loading"` + - `"outline"` + - `"secondary"` + - `"ghost"` + - `"link"` + +- **size**: `Size` (Optional, default: `"default"`) + - The size of the button. Options include: + - `"default"` + - `"sm"` + - `"lg"` + - `"link"` + - `"icon"` + - `"circle"` + +- **icon**: `React.ReactNode` (Optional) + - An icon or element to be displayed inside the button when `isIconOnly` is `true`. + +- **children**: `React.ReactNode` (Optional) + - The content of the button. + +- **isIconOnly**: `boolean` (Optional) + - If `true`, the button is styled as an icon-only button. + +- **isLeftIconVisible**: `boolean` (Optional, default: `true`) + - If `false`, the left icon will not be displayed. + +- **isRightIconVisible**: `boolean` (Optional, default: `true`) + - If `false`, the right icon will not be displayed. + +- **isDisabled**: `boolean` (Optional) + - If `true`, the button is disabled. + +- **ariaLabel**: `string` (Optional) + - An accessible label for the button. + +- **onClick**: `() => void` (Optional) + - A function to handle the button's `onClick` event. + +- **className**: `string` (Optional) + - Additional CSS classes for the button. + +## Examples + +### Basic Button + +```tsx + diff --git a/guides/useButton.tsx b/guides/useButton.tsx new file mode 100644 index 00000000..3b85466a --- /dev/null +++ b/guides/useButton.tsx @@ -0,0 +1,102 @@ +import { Mail, Plus } from "lucide-react"; +import { useState } from "react"; + +import Button from "~/components/customButton/customButton"; + +export default function Index() { + const [isLoading, setIsLoading] = useState(false); + + const enterLoading = () => { + setIsLoading(true); + setTimeout(() => { + setIsLoading(false); + }, 2000); + }; + + return ( +
+
+
+ + + + + + + + + + + + + + + + +
+ +
+ + +
+
+
+ ); +}