import { ImagePlus, RotateCcw, XCircle } from "lucide-react"; import React, { useCallback } from "react"; import type { ControllerRenderProps, FieldError } from "react-hook-form"; import { Button } from "./button"; // Props for our custom field component interface IconUploadFieldProps { field: ControllerRenderProps; // field.value can be File | string (URL) | null | undefined error?: FieldError; accept?: string; previewContainerClassName?: string; defaultPreview?: string; // Visual fallback URL if field.value is initially undefined formDefaultValue?: string | null | undefined; // The actual RHF default value for this field } export function IconUploadField({ field, error, accept = "image/*", previewContainerClassName = "w-24 h-24 rounded-full", defaultPreview, formDefaultValue, // New prop }: IconUploadFieldProps) { const [previewUrl, setPreviewUrl] = React.useState(null); const fileInputRef = React.useRef(null); React.useEffect(() => { let objectUrlToRevoke: string | null = null; if (field.value instanceof File) { objectUrlToRevoke = URL.createObjectURL(field.value); setPreviewUrl(objectUrlToRevoke); } else if (typeof field.value === "string" && field.value) { setPreviewUrl(field.value); } else if (field.value === null) { setPreviewUrl(null); } else if (field.value === undefined && defaultPreview) { // Show visual default prop if field value is undefined setPreviewUrl(defaultPreview); } else { setPreviewUrl(null); } return () => { if (objectUrlToRevoke) { URL.revokeObjectURL(objectUrlToRevoke); } }; }, [field.value, defaultPreview]); const handleFileChange = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; field.onChange(file || undefined); if (event.target) { event.target.value = ""; } }; const handleRemoveImage = useCallback( (e: React.MouseEvent) => { e.preventDefault(); field.onChange(null); // Set RHF value to null if (fileInputRef.current) { fileInputRef.current.value = ""; // Clear the native file input } }, [field], ); const handleResetToDefault = useCallback( (e: React.MouseEvent) => { e.preventDefault(); // The button's visibility logic ensures formDefaultValue is relevant. // This will set RHF value to string (URL), null, or undefined, as per formDefaultValue. field.onChange(undefined); if (fileInputRef.current) { fileInputRef.current.value = ""; // Clear the native file input } }, [field, formDefaultValue], ); const triggerFileInput = useCallback(() => { fileInputRef.current?.click(); }, []); // Visibility for the "Remove" button: show if there's a File or a URL string in field.value const showRemoveButton = (!!field.value && (field.value instanceof File || typeof field.value === "string")) || (formDefaultValue && field.value === undefined); // Visibility for the "Reset to Default" button const canReset = typeof formDefaultValue !== "undefined"; // Was a formDefaultValue prop provided? // Is current value different from the formDefaultValue? // (A File object is always different from a URL string/null/undefined default) const isDifferentFromDefault = (field.value instanceof File || (field.value !== formDefaultValue && field.value !== undefined)) && formDefaultValue; const showResetButton = canReset && isDifferentFromDefault; return (
{ if (e.key === "Enter" || e.key === " ") { e.preventDefault(); triggerFileInput(); } }} role="button" tabIndex={0} aria-label={previewUrl ? "Change icon" : "Upload icon"} aria-invalid={!!error} aria-describedby={error ? `${field.name}-error` : undefined} > {previewUrl ? ( Icon preview ) : ( )}
{/* Optional: A separate button to trigger file input, if needed. */} {/* */} {showRemoveButton && ( )} {showResetButton && ( )}
); }