142 lines
4.2 KiB
TypeScript
142 lines
4.2 KiB
TypeScript
export type ButtonVariant =
|
|
| "primary"
|
|
| "secondary"
|
|
| "outline"
|
|
| "ghost"
|
|
| "danger";
|
|
|
|
export type ButtonSize = "sm" | "md" | "icon";
|
|
|
|
export type BadgeVariant =
|
|
| "neutral"
|
|
| "accent"
|
|
| "success"
|
|
| "warning"
|
|
| "danger"
|
|
| "info";
|
|
|
|
export type InputState = "default" | "error";
|
|
|
|
export type StatusNoteVariant = "info" | "success" | "warning" | "danger";
|
|
|
|
export function cn(...classes: Array<string | false | null | undefined>): string {
|
|
return classes.filter(Boolean).join(" ");
|
|
}
|
|
|
|
const buttonBase =
|
|
"inline-flex items-center justify-center gap-1.5 rounded border text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cinnabar/35 disabled:cursor-not-allowed disabled:opacity-45";
|
|
|
|
const buttonVariants: Record<ButtonVariant, string> = {
|
|
primary:
|
|
"border-cinnabar bg-cinnabar text-panel shadow-paper hover:bg-cinnabar/95",
|
|
secondary:
|
|
"border-line bg-panel text-ink hover:border-cinnabar hover:text-cinnabar",
|
|
outline:
|
|
"border-cinnabar bg-transparent text-cinnabar hover:bg-[var(--color-cinnabar-wash)]",
|
|
ghost:
|
|
"border-transparent bg-transparent text-ink-soft hover:bg-[var(--color-cinnabar-wash)] hover:text-cinnabar",
|
|
danger:
|
|
"border-conflict bg-transparent text-conflict hover:bg-conflict/10",
|
|
};
|
|
|
|
const buttonSizes: Record<ButtonSize, string> = {
|
|
sm: "px-3 py-1.5 text-xs",
|
|
md: "px-4 py-2",
|
|
icon: "h-9 w-9 p-0",
|
|
};
|
|
|
|
export function buttonClass({
|
|
variant = "secondary",
|
|
size = "md",
|
|
className,
|
|
}: {
|
|
variant?: ButtonVariant;
|
|
size?: ButtonSize;
|
|
className?: string;
|
|
} = {}): string {
|
|
return cn(buttonBase, buttonVariants[variant], buttonSizes[size], className);
|
|
}
|
|
|
|
const badgeBase =
|
|
"inline-flex items-center gap-1 rounded border px-2 py-0.5 text-xs leading-5";
|
|
|
|
const badgeVariants: Record<BadgeVariant, string> = {
|
|
neutral: "border-line bg-bg text-ink-soft",
|
|
accent:
|
|
"border-cinnabar/20 bg-[var(--color-cinnabar-wash)] text-cinnabar",
|
|
success: "border-pass/25 bg-pass/10 text-pass",
|
|
warning: "border-overdue/35 bg-overdue/10 text-ink",
|
|
danger: "border-conflict/25 bg-conflict/10 text-conflict",
|
|
info: "border-info/25 bg-info/10 text-info",
|
|
};
|
|
|
|
export function badgeClass({
|
|
variant = "neutral",
|
|
className,
|
|
}: {
|
|
variant?: BadgeVariant;
|
|
className?: string;
|
|
} = {}): string {
|
|
return cn(badgeBase, badgeVariants[variant], className);
|
|
}
|
|
|
|
export function cardClass(className?: string): string {
|
|
return cn("rounded border border-line bg-panel shadow-paper", className);
|
|
}
|
|
|
|
const fieldTextBase = "block text-sm font-medium text-ink";
|
|
|
|
export function fieldLabelClass(className?: string): string {
|
|
return cn(fieldTextBase, className);
|
|
}
|
|
|
|
export function fieldHelpClass(className?: string): string {
|
|
return cn("mt-1 text-xs leading-5 text-ink-soft", className);
|
|
}
|
|
|
|
export function fieldErrorClass(className?: string): string {
|
|
return cn("mt-1 text-xs leading-5 text-conflict", className);
|
|
}
|
|
|
|
const inputBase =
|
|
"w-full rounded border bg-bg text-ink transition-colors placeholder:text-ink-soft/65 focus:outline-none focus-visible:ring-2 focus-visible:ring-cinnabar/30 disabled:cursor-not-allowed disabled:bg-line/20 disabled:text-ink-soft";
|
|
|
|
const inputStates: Record<InputState, string> = {
|
|
default: "border-line focus:border-cinnabar",
|
|
error: "border-conflict focus:border-conflict focus-visible:ring-conflict/25",
|
|
};
|
|
|
|
export function inputClass({
|
|
state = "default",
|
|
className,
|
|
}: {
|
|
state?: InputState;
|
|
className?: string;
|
|
} = {}): string {
|
|
return cn(inputBase, inputStates[state], className);
|
|
}
|
|
|
|
const statusNoteBase =
|
|
"rounded border px-3 py-2 text-sm leading-6";
|
|
|
|
const statusNoteVariants: Record<StatusNoteVariant, string> = {
|
|
info: "border-info/25 bg-info/10 text-info",
|
|
success: "border-pass/25 bg-pass/10 text-pass",
|
|
warning: "border-overdue/35 bg-overdue/10 text-ink",
|
|
danger: "border-conflict/25 bg-conflict/10 text-conflict",
|
|
};
|
|
|
|
export function statusNoteClass({
|
|
variant = "info",
|
|
className,
|
|
}: {
|
|
variant?: StatusNoteVariant;
|
|
className?: string;
|
|
} = {}): string {
|
|
return cn(statusNoteBase, statusNoteVariants[variant], className);
|
|
}
|
|
|
|
export function segmentedClass(className?: string): string {
|
|
return cn("inline-flex rounded border border-line bg-bg p-1", className);
|
|
}
|