Added buttons

This commit is contained in:
2025-02-26 21:10:29 -05:00
parent d1b0a499a8
commit 1499f4055f
11 changed files with 633 additions and 48 deletions

View File

@@ -12,6 +12,7 @@ import RaidLayoutPage from "./pages/protected/RaidLayoutPage";
import HomePage from "./pages/public/HomePage"; import HomePage from "./pages/public/HomePage";
import LoginPage from "./pages/public/LoginPage"; import LoginPage from "./pages/public/LoginPage";
import SignupPage from "./pages/public/SignupPage"; import SignupPage from "./pages/public/SignupPage";
import TestPage from "./pages/public/TestPage";
import { ProtectedRoute } from "./providers/AuthProvider"; import { ProtectedRoute } from "./providers/AuthProvider";
import ErrorBoundary from "./providers/ErrorBoundary"; import ErrorBoundary from "./providers/ErrorBoundary";
@@ -27,6 +28,10 @@ const routes = createBrowserRouter([
path: "/", path: "/",
element: <HomePage/> element: <HomePage/>
}, },
{
path: "/test",
element: <TestPage/>
},
{ {
path: "/login", path: "/login",
element: <LoginPage/> element: <LoginPage/>

View File

@@ -0,0 +1,68 @@
import clsx from "clsx";
export type ButtonRounding = "none" | "sm" | "md" | "lg" | "full";
export type ButtonShape = "vertical" | "horizontal" | "square";
export type ButtonSizeType = "xsm" | "sm" | "md" | "lg" | "xl";
export type ButtonVariant = "solid" | "outline" | "ghost" | "outline-ghost" | "icon";
export interface ButtonProps extends React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>{
rounding?: ButtonRounding;
shape?: ButtonShape;
size?: ButtonSizeType;
variant?: ButtonVariant;
}
export default function Button(props: ButtonProps){
const {
rounding = "lg",
shape = "vertical",
size = "md"
} = props;
const buttonProps = {...props};
delete buttonProps.rounding;
delete buttonProps.shape;
delete buttonProps.size;
delete buttonProps.variant;
return (
<button
{...props}
className={clsx(
props.className,
//Rounding
{
"rounded-none": rounding === "none",
"rounded-sm": rounding === "sm",
"rounded-md": rounding === "md",
"rounded-lg": rounding === "lg",
"rounded-full": rounding === "full"
},
//Shape & Size
{
//Square
"p-0": size === "xsm" && shape === "square",
"p-1": size === "sm" && shape === "square",
"p-2": size === "md" && shape === "square",
"p-3": size === "lg" && shape === "square",
"p-4": size === "xl" && shape === "square",
//Horizontal
"px-1 py-0": size === "xsm" && shape === "horizontal",
"px-2 py-1": size === "sm" && shape === "horizontal",
"px-4 py-2": size === "md" && shape === "horizontal",
"px-6 py-3": size === "lg" && shape === "horizontal",
"px-8 py-4": size === "xl" && shape === "horizontal",
//Vertical
"px-0 py-1": size === "xsm" && shape === "vertical",
"px-1 py-2": size === "sm" && shape === "vertical",
"px-2 py-4": size === "md" && shape === "vertical",
"px-3 py-6": size === "lg" && shape === "vertical",
"px-4 py-8": size === "xl" && shape === "vertical",
}
)}
/>
);
}

View File

@@ -0,0 +1,39 @@
import clsx from "clsx";
import Button, { ButtonProps } from "./Button";
export default function DangerButton(props: ButtonProps){
const {
variant = "solid"
} = props;
return (
<Button
{...props}
className={clsx(
props.className,
//Background
{
"bg-transparent": variant === "outline" || variant === "icon",
"bg-red-600 hover:bg-red-700 active:bg-red-800": variant === "solid",
"bg-transparent hover:bg-red-600 active:bg-red-700": variant === "ghost" || variant === "outline-ghost"
},
//Text
{
"text-white": variant === "solid",
"text-red-600 hover:text-red-700 active:text-red-800": variant === "outline" || variant === "icon",
"text-red-600 hover:text-white active:text-white": variant === "ghost" || variant === "outline-ghost"
},
//Outline
{
"outline-none": variant === "ghost" || variant === "icon",
"outline outline-red-600 hover:outline-red-700 active:outline-red-800": variant === "solid" || variant === "outline",
"outline hover:outline-red-600 active:outline-red-700": variant === "outline-ghost"
}
)}
>
{props.children}
</Button>
);
}

View File

@@ -1,18 +1,39 @@
import clsx from "clsx"; import clsx from "clsx";
import { ComponentProps } from "react"; import Button, { ButtonProps } from "./Button";
export default function PrimaryButton(props: ButtonProps){
const {
variant = "solid"
} = props;
export default function PrimaryButton(props: ComponentProps<"button">){
return ( return (
<button <Button
{...props} {...props}
className={clsx( className={clsx(
props.className, props.className,
"rounded-lg py-2 px-4", //Background
"bg-blue-500 dark:bg-blue-600 text-white" {
"bg-transparent": variant === "outline" || variant === "icon",
"bg-blue-600 hover:bg-blue-700 active:bg-blue-800": variant === "solid",
"bg-transparent hover:bg-blue-600 active:bg-blue-700": variant === "ghost" || variant === "outline-ghost"
},
//Text
{
"text-white": variant === "solid",
"text-blue-600 hover:text-blue-700 active:text-blue-800": variant === "outline" || variant === "icon",
"text-blue-600 hover:text-white active:text-white": variant === "ghost" || variant === "outline-ghost"
},
//Outline
{
"outline-none": variant === "ghost" || variant === "icon",
"outline outline-blue-600 hover:outline-blue-700 active:outline-blue-800": variant === "solid" || variant === "outline",
"outline hover:outline-blue-600 active:outline-blue-700": variant === "outline-ghost"
}
)} )}
> >
{props.children} {props.children}
</button> </Button>
); );
} }

View File

@@ -0,0 +1,39 @@
import clsx from "clsx";
import Button, { ButtonProps } from "./Button";
export default function SecondaryButton(props: ButtonProps){
const {
variant = "solid"
} = props;
return (
<Button
{...props}
className={clsx(
props.className,
//Background
{
"bg-transparent": variant === "outline" || variant === "icon",
"bg-neutral-500 hover:bg-neutral-600 active:bg-neutral-700": variant === "solid",
"bg-transparent hover:bg-neutral-500 active:bg-neutral-600": variant === "ghost" || variant === "outline-ghost"
},
//Text
{
"text-white": variant === "solid",
"text-neutral-500 hover:text-neutral-600 active:text-neutral-700": variant === "outline" || variant === "icon",
"text-neutral-500 hover:text-white active:text-white": variant === "ghost" || variant === "outline-ghost"
},
//Outline
{
"outline-none": variant === "ghost" || variant === "icon",
"outline outline-neutral-500 hover:outline-neutral-600 active:outline-neutral-700": variant === "solid" || variant === "outline",
"outline hover:outline-neutral-500 active:outline-neutral-600": variant === "outline-ghost"
}
)}
>
{props.children}
</Button>
);
}

View File

@@ -0,0 +1,39 @@
import clsx from "clsx";
import Button, { ButtonProps } from "./Button";
export default function SuccessButton(props: ButtonProps){
const {
variant = "solid"
} = props;
return (
<Button
{...props}
className={clsx(
props.className,
//Background
{
"bg-transparent": variant === "outline" || variant === "icon",
"bg-green-600 hover:bg-green-700 active:bg-green-800": variant === "solid",
"bg-transparent hover:bg-green-600 active:bg-green-700": variant === "ghost" || variant === "outline-ghost"
},
//Text
{
"text-white": variant === "solid",
"text-green-600 hover:text-green-700 active:text-green-800": variant === "outline" || variant === "icon",
"text-green-600 hover:text-white active:text-white": variant === "ghost" || variant === "outline-ghost"
},
//Outline
{
"outline-none": variant === "ghost" || variant === "icon",
"outline outline-green-600 hover:outline-green-700 active:outline-green-800": variant === "solid" || variant === "outline",
"outline hover:outline-green-600 active:outline-green-700": variant === "outline-ghost"
}
)}
>
{props.children}
</Button>
);
}

View File

@@ -0,0 +1,39 @@
import clsx from "clsx";
import Button, { ButtonProps } from "./Button";
export default function TertiaryButton(props: ButtonProps){
const {
variant = "solid"
} = props;
return (
<Button
{...props}
className={clsx(
props.className,
//Background
{
"bg-transparent": variant === "outline" || variant === "icon",
"bg-purple-600 hover:bg-purple-700 active:bg-purple-800": variant === "solid",
"bg-transparent hover:bg-purple-600 active:bg-purple-700": variant === "ghost" || variant === "outline-ghost"
},
//Text
{
"text-white": variant === "solid",
"text-purple-600 hover:text-purple-700 active:text-purple-800": variant === "outline" || variant === "icon",
"text-purple-600 hover:text-white active:text-white": variant === "ghost" || variant === "outline-ghost"
},
//Outline
{
"outline-none": variant === "ghost" || variant === "icon",
"outline outline-purple-600 hover:outline-purple-700 active:outline-purple-800": variant === "solid" || variant === "outline",
"outline hover:outline-purple-600 active:outline-purple-700": variant === "outline-ghost"
}
)}
>
{props.children}
</Button>
);
}

View File

@@ -0,0 +1,39 @@
import clsx from "clsx";
import Button, { ButtonProps } from "./Button";
export default function WarningButton(props: ButtonProps){
const {
variant = "solid"
} = props;
return (
<Button
{...props}
className={clsx(
props.className,
//Background
{
"bg-transparent": variant === "outline" || variant === "icon",
"bg-yellow-400 hover:bg-yellow-500 active:bg-yellow-600": variant === "solid",
"bg-transparent hover:bg-yellow-400 active:bg-yellow-500": variant === "ghost" || variant === "outline-ghost"
},
//Text
{
"text-black": variant === "solid",
"text-yellow-400 hover:text-yellow-500 active:text-yellow-600": variant === "outline" || variant === "icon",
"text-yellow-400 hover:text-black active:text-black": variant === "ghost" || variant === "outline-ghost"
},
//Outline
{
"outline-none": variant === "ghost" || variant === "icon",
"outline outline-yellow-400 hover:outline-yellow-500 active:outline-yellow-600": variant === "solid" || variant === "outline",
"outline hover:outline-yellow-400 active:outline-yellow-500": variant === "outline-ghost"
}
)}
>
{props.children}
</Button>
);
}

View File

@@ -6,6 +6,10 @@ const publicLinks = [
{ {
name: "Home", name: "Home",
path: "/" path: "/"
},
{
name: "Test",
path: "/test"
} }
]; ];

View File

@@ -1,49 +1,7 @@
import Modal from "@/components/modal/Modal";
import ModalBody from "@/components/modal/ModalBody";
import ModalFooter from "@/components/modal/ModalFooter";
import ModalHeader from "@/components/modal/ModalHeader";
import { useState } from "react";
export default function HomePage(){ export default function HomePage(){
const [ displayModal, setDisplayModal ] = useState(false);
const showModal = () => {
setDisplayModal(true);
}
const hideModal = () => {
setDisplayModal(false);
}
return ( return (
<main> <main>
<div>Home Page</div> <div>Home Page</div>
<button
onClick={showModal}
>
Modal
</button>
<Modal
display={displayModal}
close={hideModal}
backgroundType="lighten"
className="bg-(--bg-color) text-(--text-color)"
>
<ModalHeader
close={hideModal}
>
Header
</ModalHeader>
<ModalBody>
Body
</ModalBody>
<ModalFooter>
Footer
</ModalFooter>
</Modal>
</main> </main>
); );
} }

View File

@@ -0,0 +1,334 @@
import { ButtonShape, ButtonSizeType, ButtonVariant } from "@/components/button/Button";
import DangerButton from "@/components/button/DangerButton";
import PrimaryButton from "@/components/button/PrimaryButton";
import SecondaryButton from "@/components/button/SecondaryButton";
import SuccessButton from "@/components/button/SuccessButton";
import TertiaryButton from "@/components/button/TertiaryButton";
import WarningButton from "@/components/button/WarningButton";
export default function TestPage(){
const sizes: ButtonSizeType[] = ["xsm", "sm", "md", "lg", "xl"];
const variants: ButtonVariant[] = ["solid", "icon", "outline", "outline-ghost", "ghost"];
const shapes: ButtonShape[] = ["horizontal", "square", "vertical"];
return (
<main
className="flex flex-col items-center justify-center gap-4"
>
{/* Primary Button */}
<div
className="flex flex-row items-center justify-center gap-4"
>
{
sizes.map((size, cnt) => (
<PrimaryButton
key={cnt}
size={size}
variant="solid"
shape="horizontal"
rounding="lg"
>
Primary {size}
</PrimaryButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
variants.map((variant, cnt) => (
<PrimaryButton
key={cnt}
size="md"
variant={variant}
shape="horizontal"
rounding="lg"
>
Primary {variant}
</PrimaryButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
shapes.map((shape, cnt) => (
<PrimaryButton
key={cnt}
size="md"
variant="solid"
shape={shape}
rounding="lg"
>
Primary {shape}
</PrimaryButton>
))
}
</div>
{/* Secondary Button */}
<div
className="flex flex-row items-center justify-center gap-4"
>
{
sizes.map((size, cnt) => (
<SecondaryButton
key={cnt}
size={size}
variant="solid"
shape="horizontal"
rounding="lg"
>
Secondary {size}
</SecondaryButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
variants.map((variant, cnt) => (
<SecondaryButton
key={cnt}
size="md"
variant={variant}
shape="horizontal"
rounding="lg"
>
Secondary {variant}
</SecondaryButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
shapes.map((shape, cnt) => (
<SecondaryButton
key={cnt}
size="md"
variant="solid"
shape={shape}
rounding="lg"
>
Secondary {shape}
</SecondaryButton>
))
}
</div>
{/* Tertiary Button */}
<div
className="flex flex-row items-center justify-center gap-4"
>
{
sizes.map((size, cnt) => (
<TertiaryButton
key={cnt}
size={size}
variant="solid"
shape="horizontal"
rounding="lg"
>
Tertiary {size}
</TertiaryButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
variants.map((variant, cnt) => (
<TertiaryButton
key={cnt}
size="md"
variant={variant}
shape="horizontal"
rounding="lg"
>
Tertiary {variant}
</TertiaryButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
shapes.map((shape, cnt) => (
<TertiaryButton
key={cnt}
size="md"
variant="solid"
shape={shape}
rounding="lg"
>
Tertiary {shape}
</TertiaryButton>
))
}
</div>
{/* Success Button */}
<div
className="flex flex-row items-center justify-center gap-4"
>
{
sizes.map((size, cnt) => (
<SuccessButton
key={cnt}
size={size}
variant="solid"
shape="horizontal"
rounding="lg"
>
Success {size}
</SuccessButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
variants.map((variant, cnt) => (
<SuccessButton
key={cnt}
size="md"
variant={variant}
shape="horizontal"
rounding="lg"
>
Success {variant}
</SuccessButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
shapes.map((shape, cnt) => (
<SuccessButton
key={cnt}
size="md"
variant="solid"
shape={shape}
rounding="lg"
>
Success {shape}
</SuccessButton>
))
}
</div>
{/* Warning Button */}
<div
className="flex flex-row items-center justify-center gap-4"
>
{
sizes.map((size, cnt) => (
<WarningButton
key={cnt}
size={size}
variant="solid"
shape="horizontal"
rounding="lg"
>
Warning {size}
</WarningButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
variants.map((variant, cnt) => (
<WarningButton
key={cnt}
size="md"
variant={variant}
shape="horizontal"
rounding="lg"
>
Warning {variant}
</WarningButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
shapes.map((shape, cnt) => (
<WarningButton
key={cnt}
size="md"
variant="solid"
shape={shape}
rounding="lg"
>
Warning {shape}
</WarningButton>
))
}
</div>
{/* Danger Button */}
<div
className="flex flex-row items-center justify-center gap-4"
>
{
sizes.map((size, cnt) => (
<DangerButton
key={cnt}
size={size}
variant="solid"
shape="horizontal"
rounding="lg"
>
Danger {size}
</DangerButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
variants.map((variant, cnt) => (
<DangerButton
key={cnt}
size="md"
variant={variant}
shape="horizontal"
rounding="lg"
>
Danger {variant}
</DangerButton>
))
}
</div>
<div
className="flex flex-row items-center justify-center gap-4"
>
{
shapes.map((shape, cnt) => (
<DangerButton
key={cnt}
size="md"
variant="solid"
shape={shape}
rounding="lg"
>
Danger {shape}
</DangerButton>
))
}
</div>
</main>
);
}