because I got bored of customising my CV for every job
1import { cva, type VariantProps } from "class-variance-authority";
2import { cn } from "../lib/cn";
3
4const inputVariants = cva(
5 "flex h-10 w-full rounded-md border bg-transparent px-3 py-2 text-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-ctp-subtext0 focus-visible:outline-none focus-visible:ring-2 disabled:cursor-not-allowed disabled:opacity-50",
6 {
7 variants: {
8 state: {
9 default: "border-ctp-surface1 focus-visible:ring-ctp-blue",
10 success: "border-ctp-green focus-visible:ring-ctp-green",
11 error: "border-ctp-red focus-visible:ring-ctp-red",
12 },
13 },
14 defaultVariants: {
15 state: "default",
16 },
17 },
18);
19
20interface TextInputProps
21 extends Omit<VariantProps<typeof inputVariants>, "state"> {
22 label?: string;
23 placeholder?: string;
24 value?: string;
25 onChange?: (value: string) => void;
26 disabled?: boolean;
27 error?: string;
28 state?: "default" | "success" | "error";
29 type?:
30 | "text"
31 | "email"
32 | "password"
33 | "number"
34 | "tel"
35 | "url"
36 | "date"
37 | "datetime-local";
38 className?: string;
39 id?: string;
40 required?: boolean;
41 autoComplete?: string;
42 minLength?: number;
43 pattern?: string;
44}
45
46export const TextInput = ({
47 label,
48 placeholder,
49 value,
50 onChange,
51 disabled = false,
52 error,
53 state = "default",
54 type = "text",
55 className = "",
56 id,
57 required = false,
58 autoComplete,
59 minLength,
60 pattern,
61}: TextInputProps) => {
62 const inputId =
63 id || (label ? label.toLowerCase().replace(/\s+/g, "-") : undefined);
64 const inputState = error ? "error" : state;
65
66 return (
67 <div className="space-y-1">
68 {label && (
69 <label htmlFor={inputId} className="text-sm font-medium text-ctp-text">
70 {label}
71 </label>
72 )}
73 <input
74 id={inputId}
75 type={type}
76 placeholder={placeholder}
77 value={value}
78 onChange={(e) => onChange?.(e.target.value)}
79 disabled={disabled}
80 required={required}
81 autoComplete={autoComplete}
82 minLength={minLength}
83 pattern={pattern}
84 className={cn(inputVariants({ state: inputState }), className)}
85 />
86 {error && <p className="text-sm text-ctp-red">{error}</p>}
87 </div>
88 );
89};