@react-formgen/json-schema
Version:
A headless, type-safe, customizable, and super simple React form generator.
313 lines (312 loc) • 11.9 kB
TypeScript
import { ErrorObject } from "ajv";
import { JSONSchema7 } from "json-schema";
/**
* Represents the props for the Form component.
*
* @property {JSONSchema7 | FormgenJSONSchema7} schema - The JSON Schema for the form
* @property {{ [key: string]: unknown }} [initialData] - Initial form data
* @property {(data: { [key: string]: unknown }) => void} [onSubmit] - Called on successful form submission
* @property {(errors: ErrorObject[], data?: { [key: string]: unknown }) => void} [onError] - Called when form has validation errors
* @property {Templates} templates - Custom templates used in rendering type-specific components
* @property {React.FC<FormRootProps>} formRoot - Custom form root component
* @property {React.FC<RenderTemplateProps>} [renderTemplate] - Custom template renderer override
* @property {boolean} [readonly] - Whether to render the form in readonly mode
* @property {boolean} [enableDevtools] - Whether to enable Zustand devtools for debugging
*/
export type FormProps = {
schema: JSONSchema7 | FormgenJSONSchema7;
initialData?: {
[key: string]: unknown;
};
onSubmit?: (data: {
[key: string]: unknown;
}) => void;
onError?: (errors: ErrorObject[], data?: {
[key: string]: unknown;
}) => void;
templates: Templates;
formRoot: React.FC<FormRootProps>;
renderTemplate?: React.FC<RenderTemplateProps>;
readonly?: boolean;
enableDevtools?: boolean;
};
/**
* Represents the props for the FormProvider component.
*
* @property {JSONSchema7 | FormgenJSONSchema7} schema - The JSON Schema for the form
* @property {{ [key: string]: unknown }} [initialData] - Initial form data
* @property {React.ReactNode} children - Child components that will have access to the form context
* @property {Templates} templates - Custom templates used in rendering type-specific components
* @property {boolean} [readonly] - Whether to render the form in readonly mode
* @property {React.FC<RenderTemplateProps>} [renderTemplate] - Optional custom render template function
* @property {boolean} [enableDevtools] - Whether to enable Zustand devtools for debugging
*/
export type FormProviderProps = {
schema: JSONSchema7 | FormgenJSONSchema7;
initialData?: {
[key: string]: unknown;
};
children: React.ReactNode;
templates: Templates;
readonly?: boolean;
renderTemplate?: React.FC<RenderTemplateProps>;
enableDevtools?: boolean;
};
/**
* Represents the props for the FormRoot component.
* This is the component responsible for rendering the form container, handling form submission,
* and displaying validation errors.
*
* @property {(data: { [key: string]: unknown }) => void} onSubmit - Called on successful form submission
* @property {(errors: ErrorObject[], data?: { [key: string]: unknown }) => void} onError - Called when form has validation errors
*/
export type FormRootProps = {
onSubmit: (data: {
[key: string]: unknown;
}) => void;
onError: (errors: ErrorObject[], data?: {
[key: string]: unknown;
}) => void;
};
/**
* Represents the UI schema for a property.
* This can be used to provide UI-specific configuration for a schema property.
*
* @property {string} component - The component to use for the property
* @property {Record<string, unknown>} [props] - The props to pass to the component
*/
export interface UISchema {
component: string;
props?: Record<string, unknown>;
}
/**
* Represents an extended JSONSchema7 with additional properties for customizing forms.
* This adds form-specific extensions to the standard JSON Schema.
*
* @extends {Omit<JSONSchema7, "properties" | "definitions" | "items">}
* @property {UISchema} [uiSchema] - UI-specific configuration
* @property {Record<string, FormgenJSONSchema7>} [properties] - Object properties
* @property {Record<string, FormgenJSONSchema7>} [definitions] - Schema definitions
* @property {FormgenJSONSchema7 | FormgenJSONSchema7[]} [items] - Array items schema with uiSchema support
*/
export interface FormgenJSONSchema7 extends Omit<JSONSchema7, "properties" | "definitions" | "items"> {
uiSchema?: UISchema;
properties?: Record<string, FormgenJSONSchema7>;
definitions?: Record<string, FormgenJSONSchema7>;
items?: FormgenJSONSchema7 | FormgenJSONSchema7[];
}
/**
* Custom OneOf type for strings.
* Used in the StringSchema to provide multiple-choice options with labels.
*
* @property {string} const - The actual string value
* @property {string} [title] - Display label for the option
* @property {string} [description] - Optional description for the option
*/
export type StringOneOf = {
const: string;
title?: string;
description?: string;
};
/**
* Represents a base string schema.
* This is used as a base for the StringSchema type, for instances where all constraints are not needed.
*
* @extends {Omit<JSONSchema7, "type">}
* @property {"string"} type - The schema type
*/
export interface BaseStringSchema extends Omit<JSONSchema7, "type"> {
type: "string";
}
/**
* Represents a fully-featured string schema with additional UI options.
* Use this type when implementing your StringTemplate component.
*
* @extends {Omit<FormgenJSONSchema7, "type" | "enum" | "oneOf">}
* @property {"string"} type - The schema type
* @property {string[]} [enum] - Dropdown options
* @property {StringOneOf[]} [oneOf] - Multiple-choice options with labels
* @property {UISchema} [uiSchema] - UI-specific configuration
*/
export interface StringSchema extends Omit<FormgenJSONSchema7, "type" | "enum" | "oneOf"> {
type: "string";
enum?: string[];
oneOf?: StringOneOf[];
uiSchema?: UISchema;
}
/**
* Custom OneOf type for numbers.
* Used in the NumberSchema to provide multiple-choice options with labels.
*
* @property {number} const - The actual numeric value
* @property {string} [title] - Display label for the option
* @property {string} [description] - Optional description for the option
*/
export type NumberOneOf = {
const: number;
title?: string;
description?: string;
};
/**
* Represents a base number schema.
* This is used as a base for the NumberSchema type, for instances where all constraints are not needed.
*
* @extends {Omit<JSONSchema7, "type">}
* @property {"number" | "integer"} type - The schema type
*/
export interface BaseNumberSchema extends Omit<JSONSchema7, "type"> {
type: "number" | "integer";
}
/**
* Represents a fully-featured number schema with additional UI options.
* Use this type when implementing your NumberTemplate component.
*
* @extends {Omit<FormgenJSONSchema7, "type" | "enum" | "oneOf">}
* @property {"number" | "integer"} type - The schema type
* @property {number[]} [enum] - Dropdown options
* @property {NumberOneOf[]} [oneOf] - Multiple-choice options with labels
* @property {UISchema} [uiSchema] - UI-specific configuration
*/
export interface NumberSchema extends Omit<FormgenJSONSchema7, "type" | "enum" | "oneOf"> {
type: "number" | "integer";
enum?: number[];
oneOf?: NumberOneOf[];
uiSchema?: UISchema;
}
/**
* Custom OneOf type for booleans.
* Used in the BooleanSchema to provide labeled boolean options (e.g., "Yes"/"No").
*
* @property {boolean} const - The actual boolean value
* @property {string} [title] - Display label for the option
*/
export type BooleanOneOf = {
const: boolean;
title?: string;
};
/**
* Represents a base boolean schema.
* This is used as a base for the BooleanSchema type, for instances where all constraints are not needed.
*
* @extends {Omit<JSONSchema7, "type">}
* @property {"boolean"} type - The schema type
*/
export interface BaseBooleanSchema extends Omit<JSONSchema7, "type"> {
type: "boolean";
}
/**
* Represents a fully-featured boolean schema with additional UI options.
* Use this type when implementing your BooleanTemplate component.
*
* @extends {Omit<FormgenJSONSchema7, "type" | "oneOf">}
* @property {"boolean"} type - The schema type
* @property {BooleanOneOf[]} [oneOf] - Options for rendering as radio buttons with labels
* @property {UISchema} [uiSchema] - UI-specific configuration
*/
export interface BooleanSchema extends Omit<FormgenJSONSchema7, "type" | "oneOf"> {
type: "boolean";
oneOf?: BooleanOneOf[];
uiSchema?: UISchema;
}
/**
* Represents a base object schema.
* This is used as a base for the ObjectSchema type, for instances where all constraints are not needed.
*
* @extends {Omit<JSONSchema7, "type">}
* @property {"object"} type - The schema type
*/
export interface BaseObjectSchema extends Omit<JSONSchema7, "type"> {
type: "object";
}
/**
* Represents a fully-featured object schema with additional UI options.
* Use this type when implementing your ObjectTemplate component.
*
* @extends {Omit<FormgenJSONSchema7, "type">}
* @property {"object"} type - The schema type
* @property {UISchema} [uiSchema] - UI-specific configuration
*/
export interface ObjectSchema extends Omit<FormgenJSONSchema7, "type"> {
type: "object";
uiSchema?: UISchema;
}
/**
* Represents a base array schema.
* This is used as a base for the ArraySchema type, for instances where all constraints are not needed.
*
* @extends {Omit<JSONSchema7, "type">}
* @property {"array"} type - The schema type
*/
export interface BaseArraySchema extends Omit<JSONSchema7, "type"> {
type: "array";
}
/**
* Represents a fully-featured array schema with additional UI options.
* Use this type when implementing your ArrayTemplate component.
*
* @extends {Omit<FormgenJSONSchema7, "type">}
* @property {"array"} type - The schema type
* @property {UISchema} [uiSchema] - UI-specific configuration
*/
export interface ArraySchema extends Omit<FormgenJSONSchema7, "type"> {
type: "array";
uiSchema?: UISchema;
}
/**
* Represents the custom templates for each schema type.
* You MUST provide implementations for all of these template components.
*
* @property {React.FC<{ schema: StringSchema; path: string[] }>} StringTemplate - Template for string-schema
* @property {React.FC<{ schema: NumberSchema; path: string[] }>} NumberTemplate - Template for number-schema
* @property {React.FC<{ schema: BooleanSchema; path: string[] }>} BooleanTemplate - Template for boolean-schema
* @property {React.FC<{ schema: ObjectSchema; path: string[] }>} ObjectTemplate - Template for object-schema
* @property {React.FC<{ schema: ArraySchema; path: string[] }>} ArrayTemplate - Template for array-schema
*/
export type Templates = {
StringTemplate: React.FC<{
schema: StringSchema;
path: string[];
}> | React.FC<{
schema: BaseStringSchema;
path: string[];
}>;
NumberTemplate: React.FC<{
schema: NumberSchema;
path: string[];
}> | React.FC<{
schema: BaseNumberSchema;
path: string[];
}>;
BooleanTemplate: React.FC<{
schema: BooleanSchema;
path: string[];
}> | React.FC<{
schema: BaseBooleanSchema;
path: string[];
}>;
ObjectTemplate: React.FC<{
schema: ObjectSchema;
path: string[];
}> | React.FC<{
schema: BaseObjectSchema;
path: string[];
}>;
ArrayTemplate: React.FC<{
schema: ArraySchema;
path: string[];
}> | React.FC<{
schema: BaseArraySchema;
path: string[];
}>;
};
/**
* Props for the RenderTemplate component.
* This component is responsible for rendering the correct template based on the schema type.
*
* @property {JSONSchema7 | FormgenJSONSchema7} schema - The schema to render
* @property {string[]} path - The path to the property corresponding to the schema
*/
export interface RenderTemplateProps {
schema: JSONSchema7 | FormgenJSONSchema7;
path: string[];
}