FormModal
Modal que renderiza un DynamicForm dentro de una tarjeta Paper. Admite obtener datos iniciales desde una API (fetchPath), enviar mediante métodos HTTP configurables (saveMethod), y callbacks según el modo para creación, edición y edición a nivel de campo. Los errores se muestran mediante un Toast en línea. El formulario se monta de forma diferida hasta que la animación de apertura del modal finaliza.
Props
| Prop | Tipo | Requerido | Por defecto | Descripción |
|------|------|-----------|-------------|-------------|
| isOpen | boolean | Sí | — | Controla si el modal está visible. |
| onClose | (reason: 'cancel' \| 'success' \| 'error' \| 'custom') => void | Sí | — | Se llama cuando el modal se cierra. Recibe una cadena de razón. |
| fields | Field[] | Sí | — | Definiciones de campos del formulario. Cada campo tiene type, name y opcionalmente label, config, value. |
| backdropStyle | React.CSSProperties | No | — | Estilos en línea para el backdrop del modal. |
| windowStyle | React.CSSProperties | No | — | Estilos en línea fusionados en la ventana del modal. |
| closeButtonStyle | React.CSSProperties | No | — | Estilos para el botón de cierre. |
| closeIcon | string | No | — | Nombre del ícono del botón de cierre. |
| closeIconPaths | any[] | No | — | Rutas de ícono personalizadas para el botón de cierre. |
| closeIconSize | number | No | — | Tamaño del ícono del botón de cierre. |
| zIndex | number | No | — | z-index CSS del overlay del modal. |
| id | string | No | — | id HTML del elemento modal. |
| fullScreen | boolean | No | — | Cuando es true, el modal ocupa toda la pantalla. |
| formTitle | string | No | — | Título mostrado en la tarjeta del formulario. |
| data | Record<string, any> | No | — | Valores iniciales de los campos. Si data.formatted_id existe, se muestra encima de la tarjeta. |
| fetchPath | string | No | — | Ruta de API para precargar datos del formulario antes de renderizarlo. |
| savePath | string | No | — | Ruta de API para enviar el formulario. |
| onSave | (formData: Record<string, any>) => void | No | — | Se llama en lugar de una petición API cuando savePath no está definido. |
| onCreateSuccess | (formData: Record<string, any>) => void | No | — | Se llama tras un envío de creación exitoso. |
| onCreateError | (error: any) => void | No | — | Se llama tras un envío de creación fallido. |
| onEditSuccess | (formData: Record<string, any>) => void | No | — | Se llama tras un envío de edición exitoso. |
| onEditError | (error: any) => void | No | — | Se llama tras un envío de edición fallido. |
| onFieldEditSuccess | (field: any, fieldValue: any, formData: Record<string, any>) => void | No | — | Se llama tras una edición de campo individual exitosa. |
| onFieldEditError | (field: any, error: any) => void | No | — | Se llama tras una edición de campo individual fallida. |
| extraData | Record<string, any> | No | — | Campos adicionales fusionados en el payload de envío. |
| mode | "create" \| "edit" \| "globalEdit" \| "readOnly" \| "submit" \| "view" | No | — | Modo del formulario pasado a DynamicForm. |
| apiBaseUrl | string | No | urls.apiBase | URL base del cliente HTTP. |
| saveMethod | 'POST' \| 'PUT' \| 'PATCH' | No | 'POST' | Método HTTP para el envío del formulario. |
| useAuthToken | boolean | No | — | Cuando es true, usa el cliente HTTP autenticado. |
| formWidth | number \| string | No | 700 | Ancho máximo de la tarjeta del formulario. |
| formContainerStyle | React.CSSProperties | No | — | Estilos para el contenedor del formulario. |
| formHeaderStyle | React.CSSProperties | No | — | Estilos para la sección de encabezado del formulario. |
| formBodyStyle | React.CSSProperties | No | — | Estilos para la sección del cuerpo del formulario. |
| formTitleStyle | React.CSSProperties | No | — | Estilos para el título del formulario. |
| fieldContainerStyle | React.CSSProperties | No | — | Estilos para el contenedor de cada campo. |
| fieldLabelStyle | React.CSSProperties | No | — | Estilos para las etiquetas de los campos. |
| fieldDescriptionStyle | React.CSSProperties | No | — | Estilos para las descripciones de los campos. |
| fieldHeaderStyle | React.CSSProperties | No | — | Estilos para el encabezado de cada campo. |
| fieldBodyStyle | React.CSSProperties | No | — | Estilos para el cuerpo de cada campo. |
| className | string | No | — | Clase CSS aplicada al formulario. |
| sendButtonType | "clear" \| "outline" \| "solid" | No | — | Variante visual del botón de envío. |
| sendButtonSize | "xs" \| "sm" \| "md" \| "lg" \| "xl" | No | — | Tamaño del botón de envío. |
| sendButtonColor | string | No | — | Color del botón de envío. |
| sendButtonTitle | string | No | — | Etiqueta del botón de envío. |
| sendButtonIcon | string | No | — | Nombre del ícono del botón de envío. |
| sendButtonIconPaths | any[] | No | — | Rutas de ícono personalizadas para el botón de envío. |
| sendButtonIconSize | number | No | — | Tamaño del ícono del botón de envío. |
| sendButtonStyle | React.CSSProperties | No | — | Estilos en línea para el botón de envío. |
| sendButtonTitleStyle | React.CSSProperties | No | — | Estilos en línea para la etiqueta del botón de envío. |
Uso
Básico
import React from 'react';
import FormModal from '@/components/modals/FormModal';
export default function Example() {
const [open, setOpen] = React.useState(false);
return (
<>
<button onClick={() => setOpen(true)}>Edit Product</button>
<FormModal
isOpen={open}
onClose={(reason) => {
console.log('Closed:', reason);
setOpen(false);
}}
formTitle="Edit Product"
fields={[
{ type: 'text', name: 'name', label: 'Name' },
{ type: 'number', name: 'price', label: 'Price' },
]}
data={{ name: 'Blue T-Shirt', price: 29.99 }}
savePath="/v1/products/123"
saveMethod="PATCH"
mode="edit"
useAuthToken
onEditSuccess={(data) => console.log('Updated:', data)}
/>
</>
);
}