initial commit
This commit is contained in:
79
packages/ui/src/components/Button.tsx
Normal file
79
packages/ui/src/components/Button.tsx
Normal file
@@ -0,0 +1,79 @@
|
||||
import { forwardRef, type ButtonHTMLAttributes } from "react";
|
||||
import { cn } from "../lib/cn";
|
||||
|
||||
export type ButtonVariant = "primary" | "secondary" | "ghost" | "danger";
|
||||
export type ButtonSize = "sm" | "md" | "lg";
|
||||
|
||||
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
variant?: ButtonVariant;
|
||||
size?: ButtonSize;
|
||||
isLoading?: boolean;
|
||||
}
|
||||
|
||||
const variantStyles: Record<ButtonVariant, string> = {
|
||||
primary:
|
||||
"bg-sky-600 text-white hover:bg-sky-500 focus-visible:ring-sky-500 dark:bg-sky-500 dark:hover:bg-sky-400",
|
||||
secondary:
|
||||
"border border-zinc-300 bg-white text-zinc-900 hover:bg-zinc-50 focus-visible:ring-zinc-400 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-100 dark:hover:bg-zinc-800",
|
||||
ghost:
|
||||
"text-zinc-700 hover:bg-zinc-100 focus-visible:ring-zinc-400 dark:text-zinc-300 dark:hover:bg-zinc-800",
|
||||
danger:
|
||||
"bg-red-600 text-white hover:bg-red-500 focus-visible:ring-red-500 dark:bg-red-500 dark:hover:bg-red-400",
|
||||
};
|
||||
|
||||
const sizeStyles: Record<ButtonSize, string> = {
|
||||
sm: "h-8 px-3 text-sm",
|
||||
md: "h-10 px-4 text-sm",
|
||||
lg: "h-11 px-6 text-base",
|
||||
};
|
||||
|
||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
(
|
||||
{
|
||||
className,
|
||||
variant = "primary",
|
||||
size = "md",
|
||||
isLoading = false,
|
||||
disabled,
|
||||
type = "button",
|
||||
children,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const isDisabled = disabled || isLoading;
|
||||
|
||||
return (
|
||||
<button
|
||||
ref={ref}
|
||||
type={type}
|
||||
disabled={isDisabled}
|
||||
aria-busy={isLoading || undefined}
|
||||
aria-disabled={isDisabled || undefined}
|
||||
className={cn(
|
||||
"inline-flex items-center justify-center gap-2 rounded-md font-medium transition-colors",
|
||||
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
|
||||
"dark:focus-visible:ring-offset-zinc-950",
|
||||
"disabled:pointer-events-none disabled:opacity-50",
|
||||
variantStyles[variant],
|
||||
sizeStyles[size],
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<span
|
||||
className="size-4 animate-spin rounded-full border-2 border-current border-t-transparent"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<span className="sr-only">Loading</span>
|
||||
</>
|
||||
) : null}
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
Button.displayName = "Button";
|
||||
97
packages/ui/src/components/Card.tsx
Normal file
97
packages/ui/src/components/Card.tsx
Normal file
@@ -0,0 +1,97 @@
|
||||
import { forwardRef, type HTMLAttributes } from "react";
|
||||
import { cn } from "../lib/cn";
|
||||
|
||||
export interface CardProps extends HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
export const Card = forwardRef<HTMLDivElement, CardProps>(
|
||||
({ className, children, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"rounded-lg border border-zinc-200 bg-white shadow-sm",
|
||||
"dark:border-zinc-800 dark:bg-zinc-900",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
);
|
||||
|
||||
Card.displayName = "Card";
|
||||
|
||||
export interface CardHeaderProps extends HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
export const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(
|
||||
({ className, children, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("flex flex-col gap-1.5 border-b border-zinc-200 px-6 py-4 dark:border-zinc-800", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
);
|
||||
|
||||
CardHeader.displayName = "CardHeader";
|
||||
|
||||
export interface CardTitleProps extends HTMLAttributes<HTMLHeadingElement> {}
|
||||
|
||||
export const CardTitle = forwardRef<HTMLHeadingElement, CardTitleProps>(
|
||||
({ className, children, ...props }, ref) => (
|
||||
<h3
|
||||
ref={ref}
|
||||
className={cn("text-base font-semibold tracking-tight text-zinc-900 dark:text-zinc-50", className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</h3>
|
||||
),
|
||||
);
|
||||
|
||||
CardTitle.displayName = "CardTitle";
|
||||
|
||||
export interface CardDescriptionProps extends HTMLAttributes<HTMLParagraphElement> {}
|
||||
|
||||
export const CardDescription = forwardRef<HTMLParagraphElement, CardDescriptionProps>(
|
||||
({ className, children, ...props }, ref) => (
|
||||
<p ref={ref} className={cn("text-sm text-zinc-600 dark:text-zinc-400", className)} {...props}>
|
||||
{children}
|
||||
</p>
|
||||
),
|
||||
);
|
||||
|
||||
CardDescription.displayName = "CardDescription";
|
||||
|
||||
export interface CardContentProps extends HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
export const CardContent = forwardRef<HTMLDivElement, CardContentProps>(
|
||||
({ className, children, ...props }, ref) => (
|
||||
<div ref={ref} className={cn("px-6 py-4", className)} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
);
|
||||
|
||||
CardContent.displayName = "CardContent";
|
||||
|
||||
export interface CardFooterProps extends HTMLAttributes<HTMLDivElement> {}
|
||||
|
||||
export const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(
|
||||
({ className, children, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex items-center border-t border-zinc-200 px-6 py-4 dark:border-zinc-800",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
);
|
||||
|
||||
CardFooter.displayName = "CardFooter";
|
||||
16
packages/ui/src/index.ts
Normal file
16
packages/ui/src/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
export { Button, type ButtonProps, type ButtonSize, type ButtonVariant } from "./components/Button";
|
||||
export {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
type CardContentProps,
|
||||
type CardDescriptionProps,
|
||||
type CardFooterProps,
|
||||
type CardHeaderProps,
|
||||
type CardProps,
|
||||
type CardTitleProps,
|
||||
} from "./components/Card";
|
||||
export { cn } from "./lib/cn";
|
||||
3
packages/ui/src/lib/cn.ts
Normal file
3
packages/ui/src/lib/cn.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export function cn(...classes: Array<string | false | null | undefined>): string {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
Reference in New Issue
Block a user