MicProtocol / packages /ui /src /components /AuthInput.tsx
Benaah's picture
Upload folder using huggingface_hub
43842af verified
Raw History Blame Contribute Delete
4.1 kB
import React from "react";
import { cn } from "../lib/cn";
export interface AuthInputProps
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "size"> {
/** Visual size of the input */
size?: "sm" | "md" | "lg";
/** Error message */
error?: string;
/** Helper text */
helperText?: string;
/** Icon to show at the start of the input */
startIcon?: React.ReactNode;
/** Whether the field is required */
required?: boolean;
/** Full width (default: true) */
fullWidth?: boolean;
}
const sizeMap: Record<string, { height: string; padding: string; fontSize: string }> = {
sm: { height: "32px", padding: "0 var(--space-3)", fontSize: "var(--text-xs)" },
md: { height: "44px", padding: "0 var(--space-4)", fontSize: "var(--text-sm)" },
lg: { height: "52px", padding: "0 var(--space-5)", fontSize: "var(--text-base)" },
};
export const AuthInput = React.forwardRef<HTMLInputElement, AuthInputProps>(
(
{
size = "md",
error,
helperText,
startIcon,
required = false,
fullWidth = true,
className,
style,
id: customId,
autoComplete,
...props
},
ref,
) => {
const s = sizeMap[size] || sizeMap.md;
const hasError = !!error;
const inputClass = cn(
"w-full rounded-[var(--radius-md)] border border-[var(--border-color)] bg-[var(--bg-tertiary)] text-[var(--text-primary)] font-sans outline-none transition-all duration-150 ease-[cubic-bezier(0.16,1,0.3,1)] disabled:opacity-60 disabled:cursor-not-allowed",
hasError ? "border-[var(--color-error)] focus-visible:ring-[var(--color-error)]" : "focus-visible:ring-[var(--color-primary)]",
className,
);
return (
<div
className="flex flex-col gap-[calc(var(--space-2)*0.5)]"
style={{ display: "flex", flexDirection: "column", gap: "4px" }}
>
<div className="relative" style={{ position: "relative", width: "100%" }}>
{startIcon && (
<div
className="absolute inset-y-0 left-[var(--space-3)] flex items-center justify-center pointer-events-none z-10"
style={{
position: "absolute",
top: 0,
bottom: 0,
left: "var(--space-3)",
display: "flex",
alignItems: "center",
justifyContent: "center",
color: "var(--text-tertiary)",
pointerEvents: "none",
zIndex: 10,
}}
>
{startIcon}
</div>
)}
<input
ref={ref}
id={customId}
className={inputClass}
style={{
width: "100%",
height: s.height,
padding: s.padding,
paddingLeft: startIcon ? "calc(var(--space-8) + 2px)" : undefined,
fontSize: s.fontSize,
fontFamily: "var(--font-sans)",
color: "var(--text-primary)",
background: "var(--bg-tertiary)",
borderRadius: "var(--radius-md)",
outline: "none",
opacity: props.disabled ? 0.5 : 1,
cursor: props.disabled ? "not-allowed" : "text",
...style,
}}
autoComplete={autoComplete}
disabled={props.disabled}
aria-invalid={hasError ? "true" : "false"}
aria-required={required}
{...props}
/>
</div>
{error && (
<span
className="text-[0.75rem] text-[var(--color-error)]"
style={{ fontSize: "0.75rem", color: "var(--color-error)", paddingLeft: "2px" }}
>
{error}
</span>
)}
{!error && helperText && (
<span
className="text-[0.75rem] text-[var(--text-tertiary)]"
style={{ fontSize: "0.75rem", color: "var(--text-tertiary)", paddingLeft: "2px" }}
>
{helperText}
</span>
)}
</div>
);
},
);
AuthInput.displayName = "AuthInput";