src/components/ui/input.tsx
Linked repository file from this build.
"use client";
import { useRef, type ComponentProps } from "react";
import { Calendar } from "lucide-react";
import { cn } from "@/lib/utils";
const pickerTypes = new Set(["date", "month", "datetime-local", "time"]);
/** Compact form field. Used by Policy and the rest of the app. */
export const fieldClass =
"h-8 w-full min-w-0 rounded-md border bg-background px-2 text-sm tabular-nums outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/50 read-only:bg-muted/40 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40";
export const fieldMultilineClass =
"w-full min-w-0 rounded-md border bg-background px-2 py-1.5 text-sm outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/50 read-only:bg-muted/40 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40";
/** Same Lucide glyph for native date inputs and the custom month picker. */
export function FieldCalendarIcon({ className }: { className?: string }) {
return <Calendar className={cn("size-4 shrink-0 text-foreground", className)} aria-hidden />;
}
function Input({ className, type, ref, ...props }: ComponentProps<"input">) {
const picker = typeof type === "string" && pickerTypes.has(type);
const inputRef = useRef<HTMLInputElement>(null);
function assignRef(node: HTMLInputElement | null) {
inputRef.current = node;
if (typeof ref === "function") ref(node);
else if (ref) ref.current = node;
}
const input = (
<input
ref={assignRef}
type={type}
data-slot="input"
className={cn(
fieldClass,
"file:text-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none selection:bg-primary selection:text-primary-foreground",
picker && "pe-8",
className,
)}
{...props}
/>
);
if (!picker) return input;
return (
<span className={cn("relative block min-w-0 w-full", className)}>
{input}
<button
type="button"
tabIndex={-1}
aria-hidden
disabled={Boolean(props.disabled || props.readOnly)}
className="absolute top-1/2 right-1.5 -translate-y-1/2 rounded-sm disabled:pointer-events-none"
onClick={() => inputRef.current?.showPicker?.()}
>
<FieldCalendarIcon />
</button>
</span>
);
}
export { Input };