# Schema Form ## Introduction A React component capable of building HTML forms out of a [JSON schema](https://json-schema.org/understanding-json-schema/index.html). ## Usage ```tsx 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; }>(null); const refreshConfig: FormKit['refreshConfig'] = async () => { // refreshFormConfig(); }; const handleChange = (data) => { setFormData(data); }; return ( ); }; export default Form; ``` --- ## Form Props ```ts 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']; } ``` ## Form Ref ```ts export interface FormRef { validator: () => Promise; } ``` 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()`. --- ## Types Definition ### JSONSchema ```ts export interface JSONSchema { title: string; description?: string; required?: string[]; properties: { [key: string]: { type: 'string' | 'boolean' | 'number'; title: string; label?: string; description?: string; enum?: Array; enumNames?: string[]; default?: string | boolean | number; }; }; } ``` ### UISchema ```ts export interface UISchema { [key: string]: { 'ui:widget'?: UIWidget; 'ui:options'?: UIOptions; }; } ``` ### UIWidget ```ts export type UIWidget = | 'textarea' | 'input' | 'checkbox' | 'radio' | 'select' | 'upload' | 'timezone' | 'switch' | 'legend' | 'button'; ``` --- ### UIOptions ```ts export type UIOptions = | InputOptions | SelectOptions | UploadOptions | SwitchOptions | TimezoneOptions | CheckboxOptions | RadioOptions | TextareaOptions | ButtonOptions; ``` #### BaseUIOptions ```ts 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 | true | string; } ``` #### InputOptions ```ts export interface InputOptions extends BaseUIOptions { placeholder?: string; inputType?: | 'color' | 'date' | 'datetime-local' | 'email' | 'month' | 'number' | 'password' | 'range' | 'search' | 'tel' | 'text' | 'time' | 'url' | 'week'; } ``` #### SelectOptions ```ts export interface SelectOptions extends UIOptions {} ``` #### UploadOptions ```ts export interface UploadOptions extends BaseUIOptions { acceptType?: string; imageType?: Type.UploadType; } ``` #### SwitchOptions ```ts export interface SwitchOptions extends BaseUIOptions { label?: string; } ``` #### TimezoneOptions ```ts export interface TimezoneOptions extends UIOptions { placeholder?: string; } ``` #### CheckboxOptions ```ts export interface CheckboxOptions extends UIOptions {} ``` #### RadioOptions ```ts export interface RadioOptions extends UIOptions {} ``` #### TextareaOptions ```ts export interface TextareaOptions extends UIOptions { placeholder?: string; rows?: number; } ``` #### ButtonOptions ```ts export interface ButtonOptions extends BaseUIOptions { text: string; icon?: string; action?: UIAction; variant?: ButtonProps['variant']; size?: ButtonProps['size']; } ``` #### UIAction ```ts 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; }; } ``` #### FormKit ```ts export interface FormKit { refreshConfig(): void; } ``` --- ### FormData ```ts export interface FormValue { value: T; isInvalid: boolean; errorMsg: string; [prop: string]: any; } export interface FormDataType { [prop: string]: FormValue; } ``` --- ## Backend API For backend generating modal form you can return json like this. ### Response ```json { "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" } ] } ] } ``` ## reference - [json schema](https://json-schema.org/understanding-json-schema/index.html) - [react-jsonschema-form](https://github.com/rjsf-team/react-jsonschema-form) - [vue-json-schema-form](https://github.com/lljj-x/vue-json-schema-form/)