EditableImageCropField
EditableImageCropField muestra la imagen actual como una miniatura de 100×100 píxeles en modo lectura (o un placeholder cuando no hay imagen). Al hacer clic en el ícono de edición se activa ImageCropField, que proporciona una dropzone para carga de imagen y un modal de recorte interactivo. La imagen recortada se sube al servidor y la ruta del archivo resultante se guarda. Al confirmar, la ruta se persiste mediante la API.
Props
| Prop | Tipo | Requerido | Por defecto | Descripción |
|------|------|-----------|-------------|-------------|
| name | string | Sí | — | Nombre del campo enviado como clave en el payload de actualización. |
| value | string | Sí | "" | Ruta del archivo de imagen actual (relativa a filesBaseUrl). |
| updatePath | string | Sí | — | Ruta del endpoint API para persistir la nueva ruta de imagen. |
| uploadPath | string | Sí | — | Ruta API donde se sube el archivo de imagen. Si no se provee, usa updatePath. |
| targetDir | string | Sí | — | Directorio del servidor donde se almacena el archivo subido. |
| label | string | No | — | Etiqueta mostrada sobre la vista previa de la imagen. |
| description | string | No | — | Texto de ayuda mostrado debajo de la etiqueta. |
| apiBaseUrl | string | No | — | URL base que se antepone a updatePath y uploadPath. |
| filesBaseUrl | string | No | urls.filesBase | URL base antepuesta a la ruta guardada para construir el src de la vista previa. |
| useAuthToken | boolean | No | false | Cuando es true, los requests incluyen el token de autorización. |
| maxFileSize | number | No | — | Tamaño máximo de archivo permitido (en bytes). |
| dropzoneLabel | string | No | — | Etiqueta mostrada dentro de la dropzone. |
| dropzoneIcon | string | No | — | Nombre del ícono mostrado dentro de la dropzone. |
| isPublic | boolean | No | true | Cuando es true, el archivo subido se almacena en el directorio público. |
| fileName | string | No | — | Nombre de archivo personalizado para el archivo subido (sin extensión). |
| aspectRatio | number | No | 1 | Relación de aspecto del recorte (ej. 16/9 para paisaje, 1 para cuadrado). |
| modalZIndex | number | No | 99999 | z-index del modal de recorte. |
| onChange | (newValue?: string) => void | No | — | Llamado localmente cuando una imagen se sube y su ruta está disponible. |
| onEditStart | () => void | No | — | Llamado cuando comienza el modo edición. |
| onEditSuccess | (updatedValue: string, newFormData: any) => void | No | — | Llamado tras un guardado exitoso. |
| onEditError | (error: any) => void | No | — | Llamado al fallar el request; el valor se revierte. |
| onEditCancel | () => void | No | — | Llamado al cancelar; el valor se revierte. |
| editIcon | string | No | "pencil" | Nombre del ícono para el botón de edición. |
| saveIcon | string | No | "check" | Nombre del ícono para el botón de guardar. |
| cancelIcon | string | No | "close" | Nombre del ícono para el botón de cancelar. |
| containerStyle | React.CSSProperties | No | — | Estilo del contenedor externo. |
| inputStyle | React.CSSProperties | No | — | Estilo del input interno. |
| labelStyle | React.CSSProperties | No | — | Estilo del elemento <label>. |
| descriptionStyle | React.CSSProperties | No | — | Estilo del <p> de descripción. |
Uso
Básico — avatar cuadrado
import React, { useState } from 'react';
import EditableImageCropField from '@/components/editable-fields/EditableImageCropField';
export default function Example() {
const [avatar, setAvatar] = useState('uploads/users/42/avatar.jpg');
return (
<EditableImageCropField
label="Avatar"
name="avatar"
value={avatar}
updatePath="/v1/users/42"
uploadPath="/v1/files/upload"
targetDir="users/42"
apiBaseUrl="https://api.example.com"
filesBaseUrl="https://cdn.example.com"
aspectRatio={1}
useAuthToken
onEditSuccess={(updated) => setAvatar(updated)}
/>
);
}
Banner horizontal
<EditableImageCropField
label="Banner"
name="banner"
value={banner}
updatePath="/v1/organizations/3"
uploadPath="/v1/files/upload"
targetDir="organizations/3/banners"
apiBaseUrl="https://api.example.com"
filesBaseUrl="https://cdn.example.com"
aspectRatio={16 / 9}
maxFileSize={5 * 1024 * 1024}
useAuthToken
onEditSuccess={(updated) => setBanner(updated)}
/>