A React component capable of building HTML forms out of a JSON schema.
import React, { useState } from 'react'; import { SchemaForm, initFormData, JSONSchema, UISchema, FormKit, } from '@/components'; const schema: JSONSchema = { title: 'General', properties: { name: { type: 'string', title: 'Name', }, age: { type: 'number', title: 'Age', }, sex: { type: 'string', title: 'sex', enum: [1, 2], enumNames: ['male', 'female'], }, }, }; const uiSchema: UISchema = { name: { 'ui:widget': 'input', }, age: { 'ui:widget': 'input', 'ui:options': { type: 'number', }, }, sex: { 'ui:widget': 'radio', }, }; const Form = () => { const [formData, setFormData] = useState(initFormData(schema)); const formRef = useRef<{ validator: () => Promise<boolean>; }>(null); const refreshConfig: FormKit['refreshConfig'] = async () => { // refreshFormConfig(); }; const handleChange = (data) => { setFormData(data); }; return ( <SchemaForm ref={formRef} schema={schema} uiSchema={uiSchema} formData={formData} onChange={handleChange} refreshConfig={refreshConfig} /> ); }; export default Form;
interface FormProps { // Describe the form structure with schema schema: JSONSchema | null; // Describe the properties of the field uiSchema?: UISchema; // Describe form data formData: Type.FormDataType | null; // Callback function when form data changes onChange?: (data: Type.FormDataType) => void; // Handler for when a form fires a `submit` event onSubmit?: (e: React.FormEvent) => void; /** * Callback method for updating form configuration * information (schema/uiSchema) in UIAction */ refreshConfig?: FormKit['refreshConfig']; }
export interface FormRef { validator: () => Promise<boolean>; }
When you need to validate a form and get the result outside the form, you can create a FormRef with useRef and pass it to the form using the ref property.
This allows you to validate the form and get the result outside the form using formRef.current.validator().
export interface JSONSchema { title: string; description?: string; required?: string[]; properties: { [key: string]: { type: 'string' | 'boolean' | 'number'; title: string; label?: string; description?: string; enum?: Array<string | boolean | number>; enumNames?: string[]; default?: string | boolean | number; }; }; }
export interface UISchema { [key: string]: { 'ui:widget'?: UIWidget; 'ui:options'?: UIOptions; }; }
export type UIWidget = | 'textarea' | 'input' | 'checkbox' | 'radio' | 'select' | 'upload' | 'timezone' | 'switch' | 'legend' | 'button';
export type UIOptions = | InputOptions | SelectOptions | UploadOptions | SwitchOptions | TimezoneOptions | CheckboxOptions | RadioOptions | TextareaOptions | ButtonOptions;
export interface BaseUIOptions { empty?: string; // Will be appended to the className of the form component itself className?: classnames.Argument; class_name?: classnames.Argument; // The className that will be attached to a form field container field_class_name?: classnames.Argument; // Make a form component render into simplified mode readOnly?: boolean; simplify?: boolean; validator?: ( value, formData?, ) => Promise<string | true | void> | true | string; }
export interface InputOptions extends BaseUIOptions { placeholder?: string; inputType?: | 'color' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'range' | 'search' | 'tel' | 'text' | 'time' | 'url' | 'week'; }
export interface SelectOptions extends UIOptions {}
export interface UploadOptions extends BaseUIOptions { acceptType?: string; imageType?: Type.UploadType; }
export interface SwitchOptions extends BaseUIOptions { label?: string; }
export interface TimezoneOptions extends UIOptions { placeholder?: string; }
export interface CheckboxOptions extends UIOptions {}
export interface RadioOptions extends UIOptions {}
export interface TextareaOptions extends UIOptions { placeholder?: string; rows?: number; }
export interface ButtonOptions extends BaseUIOptions { text: string; icon?: string; action?: UIAction; variant?: ButtonProps['variant']; size?: ButtonProps['size']; }
export interface UIAction { url: string; method?: 'get' | 'post' | 'put' | 'delete'; loading?: { text: string; state?: 'none' | 'pending' | 'completed'; }; on_complete?: { toast_return_message?: boolean; refresh_form_config?: boolean; }; }
export interface FormKit { refreshConfig(): void; }
export interface FormValue<T = any> { value: T; isInvalid: boolean; errorMsg: string; [prop: string]: any; } export interface FormDataType { [prop: string]: FormValue; }
For backend generating modal form you can return json like this.
{ "name": "string", "slug_name": "string", "description": "string", "version": "string", "config_fields": [ { "name": "string", "type": "textarea", "title": "string", "description": "string", "required": true, "value": "string", "ui_options": { "placeholder": "placeholder", "rows": 4 }, "options": [ { "value": "string", "label": "string" } ] } ] }