From aefbf432a06f2d478d6fd2d6989af2d21872c50a Mon Sep 17 00:00:00 2001 From: Xcoder2023 Date: Sun, 21 Jul 2024 00:53:45 -0800 Subject: [PATCH 1/3] feat: create reusable button component --- app/components/customButton/customButton.tsx | 117 +++++++++++++++++++ guides/ButtonUsage.md | 74 ++++++++++++ guides/useButton.tsx | 102 ++++++++++++++++ 3 files changed, 293 insertions(+) create mode 100644 app/components/customButton/customButton.tsx create mode 100644 guides/ButtonUsage.md create mode 100644 guides/useButton.tsx 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/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 ( +
+
+
+ + + + + + + + + + + + + + + + +
+ +
+ + +
+
+
+ ); +} From 947152044638aabab3fceae83cc2644e641edeeb Mon Sep 17 00:00:00 2001 From: Xcoder2023 Date: Sun, 21 Jul 2024 08:08:51 -0800 Subject: [PATCH 2/3] feat/isuue-29-create-reusable-button-component resolved eslint in footerCookieConsent file --- app/components/ui/footerCookieConsent.tsx | 34 ++++++++++++----------- app/components/ui/switch.tsx | 25 ++++++++--------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/app/components/ui/footerCookieConsent.tsx b/app/components/ui/footerCookieConsent.tsx index d2d8c98b..826f1341 100644 --- a/app/components/ui/footerCookieConsent.tsx +++ b/app/components/ui/footerCookieConsent.tsx @@ -1,30 +1,32 @@ import { Button } from "./button"; -const FooterCookieConsent: React.FC = () => { - const handleAccept = () => { - console.log("Cookie consent accepted"); - }; +const handleAccept = () => { + console.log("Cookie consent accepted"); +}; - const handleReject = () => { - console.log("Cookie consent rejected"); - }; +const handleReject = () => { + console.log("Cookie consent rejected"); +}; - const handleSettings = () => { - console.log("Cookie settings opened"); - }; +const handleSettings = () => { + console.log("Cookie settings opened"); +}; +const FooterCookieConsent: React.FC = () => { return ( -
-
-

- We Value your Privacy +

+
+

+ + We Value your Privacy + Our website uses cookies to enhance your browsing experience, provide personalized content, and analyze site traffic. By clicking - "Accept All", you consent to our use of cookies. + "Accept All", you consent to our use of cookies.

-
+
diff --git a/app/components/ui/switch.tsx b/app/components/ui/switch.tsx index c2e357b1..b0ec578a 100644 --- a/app/components/ui/switch.tsx +++ b/app/components/ui/switch.tsx @@ -1,29 +1,28 @@ -"use client" +"use client"; -import * as React from "react" -import * as SwitchPrimitives from "@radix-ui/react-switch" - -import { cn } from "app/lib/utils/cn" +import * as SwitchPrimitives from "@radix-ui/react-switch"; +import { cn } from "app/lib/utils/cn"; +import * as React from "react"; const Switch = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( +>(({ className, ...properties }, reference) => ( -)) -Switch.displayName = SwitchPrimitives.Root.displayName +)); +Switch.displayName = SwitchPrimitives.Root.displayName; -export { Switch } +export { Switch }; From 3c622aae732a299bf457026bb127fee115a9d683 Mon Sep 17 00:00:00 2001 From: Xcoder2023 Date: Sun, 21 Jul 2024 14:26:31 -0800 Subject: [PATCH 3/3] feat/issue-29-create-reusable-button-component update --- app/components/ui/footerCookieConsent.tsx | 2 -- app/components/ui/switch.tsx | 8 -------- 2 files changed, 10 deletions(-) diff --git a/app/components/ui/footerCookieConsent.tsx b/app/components/ui/footerCookieConsent.tsx index 05ea5b74..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 = () => { diff --git a/app/components/ui/switch.tsx b/app/components/ui/switch.tsx index cf318564..683bd717 100644 --- a/app/components/ui/switch.tsx +++ b/app/components/ui/switch.tsx @@ -2,13 +2,6 @@ import * as SwitchPrimitives from "@radix-ui/react-switch"; import { cn } from "app/lib/utils/cn"; -<<<<<<< HEAD -import * as React from "react"; - -const Switch = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef -======= import { forwardRef, type ComponentPropsWithoutRef, @@ -18,7 +11,6 @@ import { const Switch = forwardRef< ElementRef, ComponentPropsWithoutRef ->>>>>>> 40fd46af9dffa35b1451106ed619c5d16eb07dcc >(({ className, ...properties }, reference) => (