finform-react-builder
Version:
A powerful, flexible React form builder with dynamic field rendering, custom validation, multi-step forms, Material-UI integration, image component support, toggle/radio buttons, switches, autocomplete, and advanced button positioning
429 lines (428 loc) • 12.5 kB
TypeScript
export interface ValidationRule {
pattern?: string | RegExp;
message?: string;
required?: boolean | ((formValues?: any) => boolean);
minLength?: number;
maxLength?: number;
min?: number | string | {
field: string;
message?: string;
};
max?: number | string | {
field: string;
message?: string;
};
custom?: (value: any, formValues?: any) => boolean | string;
}
export interface ApiConfig {
endpoint: string;
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
valueField?: string;
labelField?: string;
headers?: Record<string, string>;
params?: Record<string, any>;
body?: any;
transform?: (data: any[]) => Array<{
label: string;
value: string | number;
}>;
dependsOn?: string;
conditional?: boolean;
passThroughFields?: string[];
}
export interface BaseField {
id?: string;
title?: string;
name: string;
label: string;
type: 'text' | 'email' | 'password' | 'number' | 'tel' | 'select' | 'checkbox' | 'checkboxGroup' | 'toggle' | 'radio' | 'switch' | 'autocomplete' | 'date' | 'textarea' | 'image' | 'title' | 'section' | 'component' | 'finuiHeader' | 'tabs' | 'grid' | 'listcards' | 'dragdroplist';
placeholder?: string;
labelPosition?: 'inner' | 'top' | 'none';
labelVariant?: 'caption' | 'body2' | 'subtitle2' | 'h6';
required?: boolean | ((formValues?: any) => boolean);
disabled?: boolean;
copyWhenDisabled?: boolean;
passThroughFields?: string[];
helperText?: string;
validation?: ValidationRule;
step?: number;
value?: any | ((form: any) => any);
valueDeps?: string[];
col?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
xs?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
sm?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
md?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
lg?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
xl?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
api_endpoint?: string;
api_method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
value_field?: string;
label_field?: string;
depends_on?: string;
conditional?: boolean;
apiConfig?: ApiConfig;
showWhen?: ((formData: any) => boolean) | {
field: string;
equals: any;
} | {
field: string;
in: any[];
};
}
export interface TextField extends BaseField {
type: 'text' | 'email' | 'password' | 'tel' | 'textarea';
minLength?: number;
maxLength?: number;
pattern?: string;
}
export interface NumberField extends BaseField {
type: 'number';
min?: number;
max?: number;
}
export interface SelectField extends BaseField {
type: 'select';
options?: Array<{
label: string;
value: string | number;
}>;
default?: string | number;
api_endpoint?: string;
api_method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
value_field?: string;
label_field?: string;
depends_on?: string;
conditional?: boolean;
apiConfig?: ApiConfig;
}
export interface CheckboxField extends BaseField {
type: 'checkbox';
}
export interface ToggleField extends BaseField {
type: 'toggle';
options: Array<{
label: string;
value: string | number;
}>;
}
export interface RadioField extends BaseField {
type: 'radio';
options: Array<{
label: string;
value: string | number;
description?: string;
}>;
row?: boolean;
groupPadding?: number;
itemGap?: number;
helperText?: string;
}
export interface CheckboxGroupField extends BaseField {
type: 'checkboxGroup';
options: Array<{
label: string;
value: string | number;
}>;
row?: boolean | number;
groupPadding?: number;
itemGap?: number;
helperText?: string;
}
export interface SwitchField extends BaseField {
type: 'switch';
}
export interface AutocompleteField extends BaseField {
type: 'autocomplete';
options?: Array<{
label: string;
value: string | number;
}>;
multiple?: boolean;
freeSolo?: boolean;
filterOptions?: boolean;
selectedAssignments?: Record<string, string>;
api_endpoint?: string;
api_method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
value_field?: string;
label_field?: string;
depends_on?: string;
conditional?: boolean;
apiConfig?: ApiConfig;
}
export interface DateField extends BaseField {
type: 'date';
minDate?: string;
maxDate?: string;
}
export interface ImageField extends BaseField {
type: 'image';
src?: string;
alt?: string;
width?: number | string;
height?: number | string;
style?: React.CSSProperties;
className?: string;
onClick?: () => void;
defaultValue?: string;
accept?: string | string[];
onFileSelected?: (file: File) => void;
onRemove?: () => void;
onPreview?: (url: string) => void;
onSave?: (payload: {
url: string;
file?: File;
}) => void;
imagePlaceholder?: {
icon?: 'upload' | 'image';
title?: string;
subtitle?: string;
buttonText?: string;
helperText?: string;
sx?: any;
iconSx?: any;
titleSx?: any;
subtitleSx?: any;
buttonSx?: any;
helperTextSx?: any;
};
imageUploaded?: {
previewText?: string;
removeText?: string;
saveText?: string;
showSave?: boolean;
previewTitle?: string;
replaceText?: string;
closeText?: string;
cardSx?: any;
thumbnailSx?: any;
fileNameSx?: any;
fileMetaSx?: any;
previewButtonSx?: any;
removeButtonSx?: any;
saveButtonSx?: any;
};
}
export interface ComponentField extends BaseField {
type: 'component';
content?: React.ReactNode;
render?: () => React.ReactNode;
}
export interface TitleField extends BaseField {
type: 'title';
variant?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
color?: string;
align?: 'left' | 'center' | 'right';
sx?: any;
}
export interface SectionField extends BaseField {
type: 'section';
variant?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
color?: string;
align?: 'left' | 'center' | 'right';
sx?: any;
cardWrap?: boolean;
cardSx?: any;
cardProps?: any;
}
export interface FinUiHeaderField extends BaseField {
type: 'finuiHeader';
title?: string;
subtitle?: string;
align?: 'left' | 'center' | 'right';
actions?: any;
actionName?: string;
actionComponent?: 'switch' | 'checkbox';
actionLabel?: string;
cardWrap?: boolean;
cardSx?: any;
cardProps?: any;
}
export interface TabsField extends BaseField {
type: 'tabs';
items: Array<{
label: React.ReactNode;
value?: string | number | React.ReactNode;
content?: React.ReactNode;
disabled?: boolean;
fields?: FieldConfig[];
}>;
defaultValue?: any;
tabSx?: any;
}
export interface GridField extends BaseField {
type: 'grid';
initialRow: Record<string, any>;
columns: Array<{
key: string;
header: string;
type?: 'text' | 'number' | 'select' | 'checkbox' | 'date' | 'index';
width?: number | string | 'auto';
options?: {
label: string;
value: any;
}[];
placeholder?: string;
disabled?: boolean | ((row: any, index: number) => boolean);
value?: any | ((row: any, index: number) => any);
editable?: boolean;
indexFormat?: (index: number) => string;
helperText?: string;
required?: boolean;
minLength?: number;
maxLength?: number;
pattern?: string;
min?: number;
max?: number;
step?: number;
endorsementText?: string | ((row: any, index: number) => string);
endorsementBg?: string;
endorsementColor?: string;
endorsementRadius?: number | string;
endorsementHeight?: number;
endorsementPaddingX?: number;
apiConfig?: {
endpoint: string;
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
valueField?: string;
labelField?: string;
headers?: Record<string, string>;
params?: Record<string, any>;
body?: any;
transform?: (data: any[]) => Array<{
label: string;
value: string | number;
}>;
dependsOn?: string;
conditional?: boolean;
passThroughFields?: string[];
};
sx?: any;
cellSx?: any;
}>;
addLabel?: string;
rowDeletable?: boolean;
addActions?: {
label: string;
template: Record<string, any>;
}[];
emptyTitle?: string;
emptySubtitle?: string;
emptyCardSx?: any;
}
export interface ListCardsField extends BaseField {
type: 'listcards';
items: Array<{
id: string | number;
header?: string;
chipText?: string;
leftAction?: {
name: string;
label?: string;
};
rightAction?: {
name: string;
label?: string;
};
}>;
sx?: any;
cardSx?: any;
}
export interface DragDropListField extends Omit<BaseField, 'helperText'> {
type: 'dragdroplist';
items: any[];
getId?: string | ((item: any) => string | number);
getText?: string | ((item: any) => string);
header?: React.ReactNode;
chipText?: string | ((item: any, index: number) => string | undefined);
showIndex?: boolean;
showLeftIcon?: boolean;
renderItem?: (item: any, isDragging: boolean) => React.ReactNode;
helperText?: React.ReactNode | string;
sx?: any;
}
export type FieldConfig = TextField | NumberField | SelectField | CheckboxField | CheckboxGroupField | ToggleField | RadioField | SwitchField | AutocompleteField | DateField | ImageField | TitleField | SectionField | ComponentField | FinUiHeaderField | TabsField | GridField | ListCardsField | DragDropListField;
export interface FormButton {
text: string;
color?: string;
size?: 'small' | 'medium' | 'large';
onClick?: () => void;
disabled?: boolean;
loading?: boolean;
position?: 'left' | 'center' | 'right' | 'space-between';
type?: 'submit' | 'button' | 'reset';
sx?: any;
}
export interface ButtonGroup {
buttons: FormButton[];
position?: 'left' | 'center' | 'right' | 'space-between';
layout?: 'horizontal' | 'vertical';
spacing?: number;
sx?: any;
}
export interface FormTitle {
text: string;
variant?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
color?: string;
align?: 'left' | 'center' | 'right';
sx?: any;
}
export interface FormTheme {
primaryColor?: string;
secondaryColor?: string;
backgroundColor?: string;
textColor?: string;
borderRadius?: number | string;
spacing?: number;
typography?: {
fontFamily?: string;
fontSize?: number | string;
};
}
export interface FinFormProps {
id?: string;
title?: FormTitle;
theme?: FormTheme;
fields: FieldConfig[];
onSubmit: (data: any) => void;
onChange?: (data: any) => void;
submitButtonText?: string;
onSubmitClick?: (data: any) => void;
defaultValues?: Record<string, any>;
showSubmitButton?: boolean;
buttons?: FormButton[];
buttonGroup?: ButtonGroup;
stickyButtons?: boolean;
stickyButtonsSx?: any;
fixedButtons?: boolean;
fixedButtonsSx?: any;
formCardWrap?: boolean;
formCardSx?: any;
formCardProps?: any;
baseUrl?: string;
apiHeaders?: Record<string, string>;
isMultiStep?: boolean;
currentStep?: number;
onStepChange?: (currentStep: number, totalSteps: number) => void;
showStepNavigation?: boolean;
stepNavigationProps?: {
showStepTitles?: boolean;
stepTitles?: string[];
};
}
export interface FieldRendererProps {
field: FieldConfig;
control: any;
errors: any;
baseUrl?: string;
apiHeaders?: Record<string, string>;
formData?: Record<string, any>;
setValue?: (name: string, value: any, options?: any) => void;
}
export interface StepNavigationProps {
currentStep: number;
totalSteps: number;
onStepChange: (step: number) => void;
stepTitles?: string[];
showStepTitles?: boolean;
completedSteps?: number[];
}