/v1.0

FormModal

A modal that renders a DynamicForm inside a Paper card. It supports fetching initial data from an API (fetchPath), submitting via configurable HTTP methods (saveMethod), and mode-aware callbacks for create, edit, and field-level edits. Errors are surfaced via an inline Toast. The form is deferred-mounted until the modal's open animation finishes.

Props

| Prop | Type | Required | Default | Description | |------|------|----------|---------|-------------| | isOpen | boolean | Yes | — | Controls whether the modal is visible. | | onClose | (reason: 'cancel' \| 'success' \| 'error' \| 'custom') => void | Yes | — | Called when the modal closes. Receives a reason string. | | fields | Field[] | Yes | — | Form field definitions. Each field has type, name, and optional label, config, value. | | backdropStyle | React.CSSProperties | No | — | Inline styles for the modal backdrop. | | windowStyle | React.CSSProperties | No | — | Inline styles merged into the modal window. | | closeButtonStyle | React.CSSProperties | No | — | Styles for the close button. | | closeIcon | string | No | — | Icon name for the close button. | | closeIconPaths | any[] | No | — | Custom icon paths for the close button. | | closeIconSize | number | No | — | Icon size for the close button. | | zIndex | number | No | — | CSS z-index of the modal overlay. | | id | string | No | — | HTML id for the modal element. | | fullScreen | boolean | No | — | When true, the modal occupies the full viewport. | | formTitle | string | No | — | Title displayed on the form card. | | data | Record<string, any> | No | — | Initial field values. If data.formatted_id exists, it is shown above the card. | | fetchPath | string | No | — | API path to prefetch form data before rendering the form. | | savePath | string | No | — | API path for form submission. | | onSave | (formData: Record<string, any>) => void | No | — | Called instead of an API request when savePath is not provided. | | onCreateSuccess | (formData: Record<string, any>) => void | No | — | Called after a successful create submission. | | onCreateError | (error: any) => void | No | — | Called after a failed create submission. | | onEditSuccess | (formData: Record<string, any>) => void | No | — | Called after a successful edit submission. | | onEditError | (error: any) => void | No | — | Called after a failed edit submission. | | onFieldEditSuccess | (field: any, fieldValue: any, formData: Record<string, any>) => void | No | — | Called after a successful individual field edit. | | onFieldEditError | (field: any, error: any) => void | No | — | Called after a failed individual field edit. | | extraData | Record<string, any> | No | — | Extra fields merged into the submission payload. | | mode | "create" \| "edit" \| "globalEdit" \| "readOnly" \| "submit" \| "view" | No | — | Form mode passed to DynamicForm. | | apiBaseUrl | string | No | urls.apiBase | Base URL for the HTTP client. | | saveMethod | 'POST' \| 'PUT' \| 'PATCH' | No | 'POST' | HTTP method used for form submission. | | useAuthToken | boolean | No | — | When true, uses the authenticated HTTP client. | | formWidth | number \| string | No | 700 | Max width of the form card. | | formContainerStyle | React.CSSProperties | No | — | Styles for the form container. | | formHeaderStyle | React.CSSProperties | No | — | Styles for the form header section. | | formBodyStyle | React.CSSProperties | No | — | Styles for the form body section. | | formTitleStyle | React.CSSProperties | No | — | Styles for the form title. | | fieldContainerStyle | React.CSSProperties | No | — | Styles for each field's container. | | fieldLabelStyle | React.CSSProperties | No | — | Styles for field labels. | | fieldDescriptionStyle | React.CSSProperties | No | — | Styles for field descriptions. | | fieldHeaderStyle | React.CSSProperties | No | — | Styles for each field's header. | | fieldBodyStyle | React.CSSProperties | No | — | Styles for each field's body. | | className | string | No | — | CSS class applied to the form. | | sendButtonType | "clear" \| "outline" \| "solid" | No | — | Visual variant of the submit button. | | sendButtonSize | "xs" \| "sm" \| "md" \| "lg" \| "xl" | No | — | Size of the submit button. | | sendButtonColor | string | No | — | Color of the submit button. | | sendButtonTitle | string | No | — | Label for the submit button. | | sendButtonIcon | string | No | — | Icon name for the submit button. | | sendButtonIconPaths | any[] | No | — | Custom icon paths for the submit button. | | sendButtonIconSize | number | No | — | Icon size for the submit button. | | sendButtonStyle | React.CSSProperties | No | — | Inline styles for the submit button. | | sendButtonTitleStyle | React.CSSProperties | No | — | Inline styles for the submit button label. |

Usage

Basic

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)}
      />
    </>
  );
}