diff options
Diffstat (limited to 'apps/web/src/components/ui/input.tsx')
| -rw-r--r-- | apps/web/src/components/ui/input.tsx | 286 |
1 files changed, 143 insertions, 143 deletions
diff --git a/apps/web/src/components/ui/input.tsx b/apps/web/src/components/ui/input.tsx index 1dce2f24..6be8e2bf 100644 --- a/apps/web/src/components/ui/input.tsx +++ b/apps/web/src/components/ui/input.tsx @@ -1,143 +1,143 @@ -"use client"; - -import { Eye, EyeOff, X } from "lucide-react"; -import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/utils/ui"; -import { Button } from "./button"; -import { forwardRef, type ComponentProps } from "react"; -import { useState } from "react"; - -const inputVariants = cva( - "file:text-foreground placeholder:text-muted-foreground border-border bg-input flex w-full min-w-0 rounded-md border shadow-xs outline-none file:inline-flex file:border-0 file:bg-transparent file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:ring-offset-0 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", - { - variants: { - variant: { - default: "selection:bg-primary selection:text-primary-foreground focus-visible:border-primary/50 focus-visible:ring-2 focus-visible:ring-primary/20", - outline: "bg-background", - destructive: - "selection:bg-destructive selection:text-destructive-foreground focus-visible:border-destructive focus-visible:ring-destructive/10", - }, - size: { - default: "h-9 px-3 py-1 text-base file:h-7 file:text-sm md:text-sm", - xs: "h-7 px-3 text-xs file:h-6 file:text-xs", - sm: "h-7 px-3 text-sm file:h-6 file:text-xs", - lg: "h-10 px-4 text-base file:h-8 file:text-sm md:text-sm", - }, - }, - defaultVariants: { - size: "default", - variant: "default", - }, - }, -); - -interface InputProps - extends Omit<ComponentProps<"input">, "size">, - VariantProps<typeof inputVariants> { - showPassword?: boolean; - onShowPasswordChange?: (show: boolean) => void; - showClearIcon?: boolean; - onClear?: () => void; - containerClassName?: string; -} - -const Input = forwardRef<HTMLInputElement, InputProps>( - ( - { - className, - type, - size, - variant, - containerClassName, - showPassword, - onShowPasswordChange, - showClearIcon, - onClear, - value, - onFocus, - onBlur, - ...props - }, - ref, - ) => { - const [isFocused, setIsFocused] = useState(false); - - const isPassword = type === "password"; - const showPasswordToggle = isPassword && onShowPasswordChange; - const showClear = - showClearIcon && - onClear && - value && - String(value).length > 0 && - isFocused; - const inputType = isPassword && showPassword ? "text" : type; - - const hasIcons = showPasswordToggle || showClear; - const iconCount = Number(showPasswordToggle) + Number(showClear); - const paddingRight = - iconCount === 2 ? "pr-20" : iconCount === 1 ? "pr-10" : ""; - - return ( - <div - className={cn(hasIcons ? "relative w-full" : "", containerClassName)} - > - <input - type={inputType} - className={cn( - inputVariants({ - size, - className: cn(paddingRight, className), - variant, - }), - )} - ref={ref} - value={value} - onFocus={(e) => { - setIsFocused(true); - onFocus?.(e); - }} - onBlur={(e) => { - setIsFocused(false); - onBlur?.(e); - }} - {...props} - /> - {showClear && ( - <Button - variant="text" - size="icon" - onMouseDown={(e) => { - e.preventDefault(); - onClear?.(); - }} - className="text-muted-foreground absolute top-0 right-0 h-full px-3 !opacity-100" - aria-label="Clear input" - > - <X className="!size-[0.85]" /> - </Button> - )} - {showPasswordToggle && ( - <Button - variant="text" - size="icon" - onClick={() => onShowPasswordChange?.(!showPassword)} - className={cn( - "text-muted-foreground hover:text-foreground absolute top-0 h-full px-3", - showClear ? "right-10" : "right-0", - )} - aria-label={showPassword ? "Hide password" : "Show password"} - > - {showPassword ? ( - <Eye className="size-4" /> - ) : ( - <EyeOff className="size-4" /> - )} - </Button> - )} - </div> - ); - }, -); -Input.displayName = "Input"; - -export { Input }; +"use client";
+
+import { Eye, EyeOff, X } from "lucide-react";
+import { cva, type VariantProps } from "class-variance-authority";
+import { cn } from "@/utils/ui";
+import { Button } from "./button";
+import { forwardRef, type ComponentProps } from "react";
+import { useState } from "react";
+
+const inputVariants = cva(
+ "file:text-foreground placeholder:text-muted-foreground border-border bg-input flex w-full min-w-0 rounded-md border shadow-xs outline-none file:inline-flex file:border-0 file:bg-transparent file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 focus-visible:ring-offset-0 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
+ {
+ variants: {
+ variant: {
+ default: "selection:bg-primary selection:text-primary-foreground focus-visible:border-primary/50 focus-visible:ring-2 focus-visible:ring-primary/20",
+ outline: "bg-background",
+ destructive:
+ "selection:bg-destructive selection:text-destructive-foreground focus-visible:border-destructive focus-visible:ring-destructive/10",
+ },
+ size: {
+ default: "h-9 px-3 py-1 text-base file:h-7 file:text-sm md:text-sm",
+ xs: "h-7 px-3 text-xs file:h-6 file:text-xs",
+ sm: "h-7 px-3 text-sm file:h-6 file:text-xs",
+ lg: "h-10 px-4 text-base file:h-8 file:text-sm md:text-sm",
+ },
+ },
+ defaultVariants: {
+ size: "default",
+ variant: "default",
+ },
+ },
+);
+
+interface InputProps
+ extends Omit<ComponentProps<"input">, "size">,
+ VariantProps<typeof inputVariants> {
+ showPassword?: boolean;
+ onShowPasswordChange?: (show: boolean) => void;
+ showClearIcon?: boolean;
+ onClear?: () => void;
+ containerClassName?: string;
+}
+
+const Input = forwardRef<HTMLInputElement, InputProps>(
+ (
+ {
+ className,
+ type,
+ size,
+ variant,
+ containerClassName,
+ showPassword,
+ onShowPasswordChange,
+ showClearIcon,
+ onClear,
+ value,
+ onFocus,
+ onBlur,
+ ...props
+ },
+ ref,
+ ) => {
+ const [isFocused, setIsFocused] = useState(false);
+
+ const isPassword = type === "password";
+ const showPasswordToggle = isPassword && onShowPasswordChange;
+ const showClear =
+ showClearIcon &&
+ onClear &&
+ value &&
+ String(value).length > 0 &&
+ isFocused;
+ const inputType = isPassword && showPassword ? "text" : type;
+
+ const hasIcons = showPasswordToggle || showClear;
+ const iconCount = Number(showPasswordToggle) + Number(showClear);
+ const paddingRight =
+ iconCount === 2 ? "pr-20" : iconCount === 1 ? "pr-10" : "";
+
+ return (
+ <div
+ className={cn(hasIcons ? "relative w-full" : "", containerClassName)}
+ >
+ <input
+ type={inputType}
+ className={cn(
+ inputVariants({
+ size,
+ className: cn(paddingRight, className),
+ variant,
+ }),
+ )}
+ ref={ref}
+ value={value}
+ onFocus={(e) => {
+ setIsFocused(true);
+ onFocus?.(e);
+ }}
+ onBlur={(e) => {
+ setIsFocused(false);
+ onBlur?.(e);
+ }}
+ {...props}
+ />
+ {showClear && (
+ <Button
+ variant="text"
+ size="icon"
+ onMouseDown={(e) => {
+ e.preventDefault();
+ onClear?.();
+ }}
+ className="text-muted-foreground absolute top-0 right-0 h-full px-3 !opacity-100"
+ aria-label="Clear input"
+ >
+ <X className="!size-[0.85]" />
+ </Button>
+ )}
+ {showPasswordToggle && (
+ <Button
+ variant="text"
+ size="icon"
+ onClick={() => onShowPasswordChange?.(!showPassword)}
+ className={cn(
+ "text-muted-foreground hover:text-foreground absolute top-0 h-full px-3",
+ showClear ? "right-10" : "right-0",
+ )}
+ aria-label={showPassword ? "Hide password" : "Show password"}
+ >
+ {showPassword ? (
+ <Eye className="size-4" />
+ ) : (
+ <EyeOff className="size-4" />
+ )}
+ </Button>
+ )}
+ </div>
+ );
+ },
+);
+Input.displayName = "Input";
+
+export { Input };
|
