Initial commit

This commit is contained in:
2026-03-27 19:35:14 +01:00
commit 38581b88a4
68 changed files with 12137 additions and 0 deletions

46
components/ui/button.tsx Normal file
View File

@@ -0,0 +1,46 @@
import { ButtonHTMLAttributes, forwardRef } from "react";
import { cn } from "@/lib/utils";
type ButtonVariant = "primary" | "secondary" | "danger" | "ghost";
type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
variant?: ButtonVariant;
};
const variantClasses: Record<ButtonVariant, string> = {
primary: "border-[#86ad60] bg-[#5f7f42] text-[#f7fbef] hover:border-[#98c16f] hover:bg-[#6a8d4a]",
secondary: "border-[#7a7f87] bg-[#14171b] text-[#f0e9da] hover:border-[#97a86f] hover:bg-[#1a1f24]",
danger: "border-[#b14a4a] bg-[#5a1f1f] text-[#ffeaea] hover:bg-[#6b2525]",
ghost: "border-[#5f646d] bg-transparent text-[#ddd5c4] hover:border-[#95a86e] hover:bg-[#171b20] hover:text-[#f4ecdc]"
};
export function buttonStyles({
variant = "secondary",
className
}: {
variant?: ButtonVariant;
className?: string;
}) {
return cn(
"inline-flex min-h-10 appearance-none items-center justify-center gap-2 rounded-md border px-4 py-2.5 text-sm font-semibold no-underline transition-colors cursor-pointer",
"tracking-[0.02em]",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#a6c279] focus-visible:ring-offset-2 focus-visible:ring-offset-bg",
"disabled:cursor-not-allowed disabled:opacity-50",
variantClasses[variant],
className
);
}
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
{ className, variant = "secondary", type = "button", ...props },
ref
) {
return (
<button
ref={ref}
type={type}
className={buttonStyles({ variant, className })}
{...props}
/>
);
});

19
components/ui/input.tsx Normal file
View File

@@ -0,0 +1,19 @@
import { forwardRef, InputHTMLAttributes } from "react";
import { cn } from "@/lib/utils";
export const Input = forwardRef<HTMLInputElement, InputHTMLAttributes<HTMLInputElement>>(function Input(
{ className, ...props },
ref
) {
return (
<input
ref={ref}
className={cn(
"ui-control",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/70 focus-visible:ring-offset-2 focus-visible:ring-offset-bg",
className
)}
{...props}
/>
);
});

62
components/ui/modal.tsx Normal file
View File

@@ -0,0 +1,62 @@
"use client";
import { ReactNode, useEffect } from "react";
import { createPortal } from "react-dom";
import { Button } from "@/components/ui/button";
type ModalProps = {
open: boolean;
title: string;
onClose: () => void;
children: ReactNode;
};
export function Modal({ open, title, onClose, children }: ModalProps) {
useEffect(() => {
if (!open) {
return;
}
const handleEscape = (event: KeyboardEvent) => {
if (event.key === "Escape") {
onClose();
}
};
window.addEventListener("keydown", handleEscape);
return () => window.removeEventListener("keydown", handleEscape);
}, [open, onClose]);
if (!open) {
return null;
}
if (typeof document === "undefined") {
return null;
}
return createPortal(
<div
className="fixed inset-0 z-[120] grid place-items-center bg-black/80 p-4 md:p-6"
onClick={onClose}
role="presentation"
>
<section
aria-modal="true"
aria-label={title}
role="dialog"
className="w-full max-w-[34rem] rounded-lg border border-border bg-panel p-5 max-h-[90vh] overflow-y-auto"
onClick={(event) => event.stopPropagation()}
>
<header className="mb-4 flex items-center justify-between">
<h2 className="text-base font-semibold text-text">{title}</h2>
<Button variant="ghost" className="px-2 py-1" onClick={onClose}>
Close
</Button>
</header>
{children}
</section>
</div>,
document.body
);
}

21
components/ui/select.tsx Normal file
View File

@@ -0,0 +1,21 @@
import { forwardRef, SelectHTMLAttributes } from "react";
import { cn } from "@/lib/utils";
export const Select = forwardRef<HTMLSelectElement, SelectHTMLAttributes<HTMLSelectElement>>(function Select(
{ className, children, ...props },
ref
) {
return (
<select
ref={ref}
className={cn(
"ui-control",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/70 focus-visible:ring-offset-2 focus-visible:ring-offset-bg",
className
)}
{...props}
>
{children}
</select>
);
});

View File

@@ -0,0 +1,18 @@
import { forwardRef, TextareaHTMLAttributes } from "react";
import { cn } from "@/lib/utils";
export const Textarea = forwardRef<HTMLTextAreaElement, TextareaHTMLAttributes<HTMLTextAreaElement>>(
function Textarea({ className, ...props }, ref) {
return (
<textarea
ref={ref}
className={cn(
"ui-control",
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent/70 focus-visible:ring-offset-2 focus-visible:ring-offset-bg",
className
)}
{...props}
/>
);
}
);