UNPKG

@nestledjs/forms

Version:

A flexible React form library supporting both declarative and imperative usage patterns with TypeScript support

433 lines (432 loc) 13.5 kB
import { DocumentNode, TypedDocumentNode } from '@apollo/client'; import { JSX, ReactNode } from 'react'; import { UseFormReturn } from 'react-hook-form'; export declare enum FormFieldType { Text = "Text", TextArea = "TextArea", Email = "Email", Password = "Password", Url = "Url", Phone = "Phone", Number = "Number", Currency = "Currency", Checkbox = "Checkbox", Switch = "Switch", Button = "Button", DatePicker = "DatePicker", DateTimePicker = "DateTimePicker", TimePicker = "TimePicker", Select = "Select", EnumSelect = "EnumSelect", MultiSelect = "MultiSelect", Radio = "Radio", CheckboxGroup = "CheckboxGroup", SearchSelect = "SearchSelect", SearchSelectApollo = "SearchSelectApollo", SearchSelectMulti = "SearchSelectMulti", SearchSelectMultiApollo = "SearchSelectMultiApollo", Content = "Content", Custom = "Custom", CustomCheckbox = "CustomCheckbox", MarkdownEditor = "MarkdownEditor" } export interface BaseFieldOptions { label?: string; required?: boolean; hidden?: boolean; disabled?: boolean; customWrapper?: (children: ReactNode) => JSX.Element; layout?: 'horizontal' | 'vertical'; defaultValue?: any; /** * If true, this specific field will be in read-only mode, overriding the form-level prop. */ readOnly?: boolean; /** * Determines how the field should appear when in read-only mode. * 'value': Renders the data as plain text. (Default) * 'disabled': Renders the UI component in a disabled state. */ readOnlyStyle?: 'value' | 'disabled'; helpText?: string; /** * Additional CSS classes to apply to the field wrapper. * Useful for layout customization (e.g., grid positioning, flexbox, spacing). */ wrapperClassName?: string; /** * Optional transformation function to convert field value from display format to submission format. * This is useful for fields that store rich objects internally but need to submit simple values. * For example, multi-select fields that display option objects but submit ID arrays. */ submitTransform?: (displayValue: unknown) => unknown; /** * Function that determines if this field should be visible based on current form values. * If this returns false, the field will not be rendered at all. * * @param formValues - Current values of all form fields * @returns true if field should be shown, false if it should be hidden * * @example * ```tsx * FormFieldClass.text('email', { * label: 'Email Address', * showWhen: (values) => values.contactMethod === 'email' * }) * ``` */ showWhen?: (formValues: any) => boolean; /** * Function that determines if this field should be required based on current form values. * This dynamically overrides the static 'required' property. * * @param formValues - Current values of all form fields * @returns true if field should be required, false otherwise * * @example * ```tsx * FormFieldClass.text('companyName', { * label: 'Company Name', * requiredWhen: (values) => values.accountType === 'business' * }) * ``` */ requiredWhen?: (formValues: any) => boolean; /** * Function that determines if this field should be disabled based on current form values. * This dynamically overrides the static 'disabled' property. * * @param formValues - Current values of all form fields * @returns true if field should be disabled, false otherwise * * @example * ```tsx * FormFieldClass.text('personalEmail', { * label: 'Personal Email', * disabledWhen: (values) => values.useCompanyEmail === true * }) * ``` */ disabledWhen?: (formValues: any) => boolean; } export interface InputFieldOptions extends BaseFieldOptions { placeholder?: string; validate?: (value: any) => string | boolean | Promise<string | boolean>; } export type UrlFieldOptions = InputFieldOptions; export type EmailFieldOptions = InputFieldOptions; export type PasswordFieldOptions = InputFieldOptions; export type PhoneFieldOptions = InputFieldOptions; export interface NumberFieldOptions extends InputFieldOptions { min?: number; max?: number; step?: number; } export type CurrencyCode = 'USD' | 'EUR' | 'GBP' | 'JPY' | 'CNY' | 'CAD' | 'AUD' | 'CHF' | 'SEK' | 'NOK' | 'DKK' | 'PLN' | 'CZK' | 'HUF' | 'RON' | 'BGN' | 'HRK' | 'RUB' | 'TRY' | 'BRL' | 'MXN' | 'INR' | 'KRW' | 'SGD' | 'HKD' | 'NZD' | 'ZAR' | 'THB' | 'MYR' | 'IDR' | 'PHP' | 'VND'; export interface CurrencyConfig { code: CurrencyCode; symbol: string; name: string; symbolPosition: 'before' | 'after'; decimalPlaces: number; thousandsSeparator: string; decimalSeparator: string; } export interface CurrencyFieldOptions extends InputFieldOptions { currency?: CurrencyCode | 'custom'; customCurrency?: Partial<CurrencyConfig>; showCurrencyCode?: boolean; hideSymbolWhenEmpty?: boolean; } export interface TextAreaOptions extends BaseFieldOptions { placeholder?: string; rows?: number; } export interface CheckboxOptions extends BaseFieldOptions { defaultValue?: boolean; labelTextSize?: string; fullWidthLabel?: boolean; wrapperClassNames?: string; helpText?: string; errorText?: string; indeterminate?: boolean; } export interface SwitchOptions extends BaseFieldOptions { defaultValue?: boolean; } export interface ButtonOptions extends BaseFieldOptions { text?: string; variant?: 'primary' | 'secondary' | 'danger'; loading?: boolean; onClick?: () => void | Promise<void>; type?: 'button' | 'submit' | 'reset'; fullWidth?: boolean; className?: string; } export interface DatePickerOptions extends BaseFieldOptions { defaultValue?: string; useController?: boolean; min?: string; max?: string; placeholder?: string; step?: number; } export interface SelectOption { label: string; value: string | number; } export interface SelectOptions extends BaseFieldOptions { options: SelectOption[]; placeholder?: string; } export interface EnumSelectOptions extends BaseFieldOptions { enum: { [s: string]: unknown; } | ArrayLike<unknown>; } export interface SearchSelectOption { label: string; value: string; } export interface SearchSelectOptions extends BaseFieldOptions { options: SearchSelectOption[]; placeholder?: string; onSearchChange?: (search: string) => void; loading?: boolean; searchDebounceMs?: number; } export interface SearchSelectApolloOptions<TDataItem = any> extends BaseFieldOptions { document: DocumentNode | TypedDocumentNode; dataType: string; searchFields?: string[]; filter?: (items: TDataItem[]) => TDataItem[]; selectOptionsFunction?: (items: TDataItem[]) => SearchSelectOption[]; } export interface SearchSelectMultiOptions extends BaseFieldOptions { options: SearchSelectOption[]; placeholder?: string; onSearchChange?: (search: string) => void; loading?: boolean; searchDebounceMs?: number; } export interface ContentOptions extends BaseFieldOptions { content: ReactNode; } export interface CustomFieldRenderProps<T = unknown> { value: T; onChange: (value: T) => void; field: CustomFieldType<T>; } export interface CustomFieldOptions<T = unknown> extends BaseFieldOptions { customField: (props: CustomFieldRenderProps<T>) => ReactNode; } interface CustomFieldType<T = unknown> { key: string; type: FormFieldType.Custom; options: CustomFieldOptions<T>; } interface InputField { key: string; type: FormFieldType.Text; options: InputFieldOptions; } interface TextAreaField { key: string; type: FormFieldType.TextArea; options: TextAreaOptions; } interface EmailField { key: string; type: FormFieldType.Email; options: EmailFieldOptions; } interface PasswordField { key: string; type: FormFieldType.Password; options: PasswordFieldOptions; } interface UrlField { key: string; type: FormFieldType.Url; options: UrlFieldOptions; } interface PhoneField { key: string; type: FormFieldType.Phone; options: PhoneFieldOptions; } interface NumberField { key: string; type: FormFieldType.Number; options: NumberFieldOptions; } interface CurrencyField { key: string; type: FormFieldType.Currency; options: CurrencyFieldOptions; } interface CheckboxField { key: string; type: FormFieldType.Checkbox; options: CheckboxOptions; } interface SwitchField { key: string; type: FormFieldType.Switch; options: SwitchOptions; } interface ButtonField { key: string; type: FormFieldType.Button; options: ButtonOptions; } interface DatePickerField { key: string; type: FormFieldType.DatePicker; options: DatePickerOptions; } interface SelectField { key: string; type: FormFieldType.Select; options: SelectOptions; } interface EnumSelectField { key: string; type: FormFieldType.EnumSelect; options: EnumSelectOptions; } interface SearchSelectField { key: string; type: FormFieldType.SearchSelect; options: SearchSelectOptions; } interface SearchSelectApolloField<TDataItem> { key: string; type: FormFieldType.SearchSelectApollo; options: SearchSelectApolloOptions<TDataItem>; } interface SearchSelectMultiApolloField<TDataItem> { key: string; type: FormFieldType.SearchSelectMultiApollo; options: SearchSelectApolloOptions<TDataItem>; } interface SearchSelectMultiField { key: string; type: FormFieldType.SearchSelectMulti; options: SearchSelectMultiOptions; } interface ContentField { key: string; type: FormFieldType.Content; options: ContentOptions; } interface MultiSelectField { key: string; type: FormFieldType.MultiSelect; options: SelectOptions; } interface DateTimePickerField { key: string; type: FormFieldType.DateTimePicker; options: DatePickerOptions; } interface TimePickerField { key: string; type: FormFieldType.TimePicker; options: BaseFieldOptions; } interface RadioField { key: string; type: FormFieldType.Radio; options: RadioFormFieldOptions; } interface CheckboxGroupField { key: string; type: FormFieldType.CheckboxGroup; options: CheckboxGroupOptions; } export interface RadioOption { key: string; value: string | number | boolean; label: string; checkedSubOption?: { key: string; label: string; }; hidden?: boolean; } export interface RadioFormFieldOptions extends BaseFieldOptions { radioOptions: RadioOption[]; defaultValue?: string | number | boolean; defaultSubValue?: string; fullWidthLabel?: boolean; radioDirection?: 'row' | 'column'; customWrapper?: (children: React.ReactNode) => JSX.Element; fancyStyle?: boolean; hidden?: boolean; disabled?: boolean; } export interface CheckboxGroupOption { key: string; value: string | number; label: string; hidden?: boolean; } export interface CheckboxGroupOptions extends BaseFieldOptions { checkboxOptions: CheckboxGroupOption[]; defaultValue?: string; fullWidthLabel?: boolean; checkboxDirection?: 'row' | 'column'; customWrapper?: (children: React.ReactNode) => JSX.Element; fancyStyle?: boolean; valueSeparator?: string; } export type FormField = InputField | TextAreaField | EmailField | PasswordField | UrlField | PhoneField | NumberField | CurrencyField | CheckboxField | SwitchField | ButtonField | DatePickerField | DateTimePickerField | TimePickerField | SelectField | EnumSelectField | MultiSelectField | RadioField | CheckboxGroupField | SearchSelectField | SearchSelectApolloField<any> | SearchSelectMultiField | SearchSelectMultiApolloField<any> | ContentField | CustomFieldType<any> | CustomCheckboxField | MarkdownEditorField; export interface FormFieldProps<T extends FormField> { field: T; form: UseFormReturn; hasError: boolean; } export interface CustomCheckboxOptions extends BaseFieldOptions { defaultValue?: boolean; fullWidthLabel?: boolean; wrapperClassNames?: string; helpText?: string; errorText?: string; checkedIcon?: ReactNode; uncheckedIcon?: ReactNode; readonlyCheckedIcon?: ReactNode; readonlyUncheckedIcon?: ReactNode; } interface CustomCheckboxField { key: string; type: FormFieldType.CustomCheckbox; options: CustomCheckboxOptions; } export interface MarkdownEditorOptions extends BaseFieldOptions { placeholder?: string; height?: number; maxLength?: number; readOnly?: boolean; readOnlyStyle?: 'value' | 'disabled'; disabled?: boolean; helpText?: string; defaultValue?: string; required?: boolean; enableImageUpload?: boolean; imageUploadHandler?: (file: File) => Promise<string>; maxImageSize?: number; allowedImageTypes?: string[]; imageUploadMode?: 'immediate' | 'base64' | 'custom'; imageUploadPlaceholder?: string; outputFormat?: 'markdown' | 'html' | 'both'; onHtmlChange?: (html: string) => void; overlayContainer?: HTMLElement | null; popupZIndex?: number; } interface MarkdownEditorField { key: string; type: FormFieldType.MarkdownEditor; options: MarkdownEditorOptions; } export {};