{ "version": 3, "sources": ["../src/index.ts", "../src/components/Form.tsx", "../src/getDefaultRegistry.ts", "../src/components/fields/ArrayField.tsx", "../src/components/fields/BooleanField.tsx", "../src/components/fields/MultiSchemaField.tsx", "../src/components/fields/NumberField.tsx", "../src/components/fields/ObjectField.tsx", "../src/components/fields/SchemaField.tsx", "../src/components/fields/StringField.tsx", "../src/components/fields/NullField.tsx", "../src/components/fields/index.ts", "../src/components/templates/ArrayFieldDescriptionTemplate.tsx", "../src/components/templates/ArrayFieldItemTemplate.tsx", "../src/components/templates/ArrayFieldTemplate.tsx", "../src/components/templates/ArrayFieldTitleTemplate.tsx", "../src/components/templates/BaseInputTemplate.tsx", "../src/components/templates/ButtonTemplates/SubmitButton.tsx", "../src/components/templates/ButtonTemplates/AddButton.tsx", "../src/components/templates/ButtonTemplates/IconButton.tsx", "../src/components/templates/ButtonTemplates/index.ts", "../src/components/templates/DescriptionField.tsx", "../src/components/templates/ErrorList.tsx", "../src/components/templates/FieldTemplate/FieldTemplate.tsx", "../src/components/templates/FieldTemplate/Label.tsx", "../src/components/templates/FieldTemplate/index.ts", "../src/components/templates/FieldErrorTemplate.tsx", "../src/components/templates/FieldHelpTemplate.tsx", "../src/components/templates/ObjectFieldTemplate.tsx", "../src/components/templates/TitleField.tsx", "../src/components/templates/UnsupportedField.tsx", "../src/components/templates/WrapIfAdditionalTemplate.tsx", "../src/components/templates/index.ts", "../src/components/widgets/AltDateWidget.tsx", "../src/components/widgets/AltDateTimeWidget.tsx", "../src/components/widgets/CheckboxWidget.tsx", "../src/components/widgets/CheckboxesWidget.tsx", "../src/components/widgets/ColorWidget.tsx", "../src/components/widgets/DateWidget.tsx", "../src/components/widgets/DateTimeWidget.tsx", "../src/components/widgets/EmailWidget.tsx", "../src/components/widgets/FileWidget.tsx", "../src/components/widgets/HiddenWidget.tsx", "../src/components/widgets/PasswordWidget.tsx", "../src/components/widgets/RadioWidget.tsx", "../src/components/widgets/RangeWidget.tsx", "../src/components/widgets/SelectWidget.tsx", "../src/components/widgets/TextareaWidget.tsx", "../src/components/widgets/TextWidget.tsx", "../src/components/widgets/TimeWidget.tsx", "../src/components/widgets/URLWidget.tsx", "../src/components/widgets/UpDownWidget.tsx", "../src/components/widgets/index.ts", "../src/withTheme.tsx"], "sourcesContent": ["import Form, { FormProps, FormState, IChangeEvent } from './components/Form';\nimport withTheme, { ThemeProps } from './withTheme';\nimport getDefaultRegistry from './getDefaultRegistry';\n\nexport type { FormProps, FormState, IChangeEvent, ThemeProps };\n\nexport { withTheme, getDefaultRegistry };\nexport default Form;\n", "import { Component, ElementType, FormEvent, ReactNode, Ref, RefObject, createRef } from 'react';\nimport {\n createSchemaUtils,\n CustomValidator,\n deepEquals,\n ErrorSchema,\n ErrorTransformer,\n FormContextType,\n GenericObjectType,\n getTemplate,\n getUiOptions,\n IdSchema,\n isObject,\n mergeObjects,\n NAME_KEY,\n PathSchema,\n StrictRJSFSchema,\n Registry,\n RegistryFieldsType,\n RegistryWidgetsType,\n RJSFSchema,\n RJSFValidationError,\n RJSF_ADDITIONAL_PROPERTIES_FLAG,\n SchemaUtilsType,\n shouldRender,\n SUBMIT_BTN_OPTIONS_KEY,\n TemplatesType,\n toErrorList,\n UiSchema,\n UI_GLOBAL_OPTIONS_KEY,\n UI_OPTIONS_KEY,\n ValidationData,\n validationDataMerge,\n ValidatorType,\n Experimental_DefaultFormStateBehavior,\n Experimental_CustomMergeAllOf,\n} from '@rjsf/utils';\nimport _forEach from 'lodash/forEach';\nimport _get from 'lodash/get';\nimport _isEmpty from 'lodash/isEmpty';\nimport _isNil from 'lodash/isNil';\nimport _pick from 'lodash/pick';\nimport _toPath from 'lodash/toPath';\n\nimport getDefaultRegistry from '../getDefaultRegistry';\n\n/** The properties that are passed to the `Form` */\nexport interface FormProps {\n /** The JSON schema object for the form */\n schema: S;\n /** An implementation of the `ValidatorType` interface that is needed for form validation to work */\n validator: ValidatorType;\n /** The optional children for the form, if provided, it will replace the default `SubmitButton` */\n children?: ReactNode;\n /** The uiSchema for the form */\n uiSchema?: UiSchema;\n /** The data for the form, used to prefill a form with existing data */\n formData?: T;\n // Form presentation and behavior modifiers\n /** You can provide a `formContext` object to the form, which is passed down to all fields and widgets. Useful for\n * implementing context aware fields and widgets.\n *\n * NOTE: Setting `{readonlyAsDisabled: false}` on the formContext will make the antd theme treat readOnly fields as\n * disabled.\n */\n formContext?: F;\n /** To avoid collisions with existing ids in the DOM, it is possible to change the prefix used for ids;\n * Default is `root`\n */\n idPrefix?: string;\n /** To avoid using a path separator that is present in field names, it is possible to change the separator used for\n * ids (Default is `_`)\n */\n idSeparator?: string;\n /** It's possible to disable the whole form by setting the `disabled` prop. The `disabled` prop is then forwarded down\n * to each field of the form. If you just want to disable some fields, see the `ui:disabled` parameter in `uiSchema`\n */\n disabled?: boolean;\n /** It's possible to make the whole form read-only by setting the `readonly` prop. The `readonly` prop is then\n * forwarded down to each field of the form. If you just want to make some fields read-only, see the `ui:readonly`\n * parameter in `uiSchema`\n */\n readonly?: boolean;\n // Form registry\n /** The dictionary of registered fields in the form */\n fields?: RegistryFieldsType;\n /** The dictionary of registered templates in the form; Partial allows a subset to be provided beyond the defaults */\n templates?: Partial, 'ButtonTemplates'>> & {\n ButtonTemplates?: Partial['ButtonTemplates']>;\n };\n /** The dictionary of registered widgets in the form */\n widgets?: RegistryWidgetsType;\n // Callbacks\n /** If you plan on being notified every time the form data are updated, you can pass an `onChange` handler, which will\n * receive the same args as `onSubmit` any time a value is updated in the form. Can also return the `id` of the field\n * that caused the change\n */\n onChange?: (data: IChangeEvent, id?: string) => void;\n /** To react when submitted form data are invalid, pass an `onError` handler. It will be passed the list of\n * encountered errors\n */\n onError?: (errors: RJSFValidationError[]) => void;\n /** You can pass a function as the `onSubmit` prop of your `Form` component to listen to when the form is submitted\n * and its data are valid. It will be passed a result object having a `formData` attribute, which is the valid form\n * data you're usually after. The original event will also be passed as a second parameter\n */\n onSubmit?: (data: IChangeEvent, event: FormEvent) => void;\n /** Sometimes you may want to trigger events or modify external state when a field has been touched, so you can pass\n * an `onBlur` handler, which will receive the id of the input that was blurred and the field value\n */\n onBlur?: (id: string, data: any) => void;\n /** Sometimes you may want to trigger events or modify external state when a field has been focused, so you can pass\n * an `onFocus` handler, which will receive the id of the input that is focused and the field value\n */\n onFocus?: (id: string, data: any) => void;\n //
HTML attributes\n /** The value of this prop will be passed to the `accept-charset` HTML attribute on the form\n * @deprecated replaced with `acceptCharset` which will supercede this value if both are specified\n */\n acceptcharset?: string;\n /** The value of this prop will be passed to the `accept-charset` HTML attribute on the form */\n acceptCharset?: string;\n /** The value of this prop will be passed to the `action` HTML attribute on the form\n *\n * NOTE: this just renders the `action` attribute in the HTML markup. There is no real network request being sent to\n * this `action` on submit. Instead, react-jsonschema-form catches the submit event with `event.preventDefault()`\n * and then calls the `onSubmit` function, where you could send a request programmatically with `fetch` or similar.\n */\n action?: string;\n /** The value of this prop will be passed to the `autocomplete` HTML attribute on the form */\n autoComplete?: string;\n /** The value of this prop will be passed to the `class` HTML attribute on the form */\n className?: string;\n /** The value of this prop will be passed to the `enctype` HTML attribute on the form */\n enctype?: string;\n /** The value of this prop will be passed to the `id` HTML attribute on the form */\n id?: string;\n /** The value of this prop will be passed to the `name` HTML attribute on the form */\n name?: string;\n /** The value of this prop will be passed to the `method` HTML attribute on the form */\n method?: string;\n /** It's possible to change the default `form` tag name to a different HTML tag, which can be helpful if you are\n * nesting forms. However, native browser form behaviour, such as submitting when the `Enter` key is pressed, may no\n * longer work\n */\n tagName?: ElementType;\n /** The value of this prop will be passed to the `target` HTML attribute on the form */\n target?: string;\n // Errors and validation\n /** Formerly the `validate` prop; Takes a function that specifies custom validation rules for the form */\n customValidate?: CustomValidator;\n /** This prop allows passing in custom errors that are augmented with the existing JSON Schema errors on the form; it\n * can be used to implement asynchronous validation. By default, these are non-blocking errors, meaning that you can\n * still submit the form when these are the only errors displayed to the user.\n */\n extraErrors?: ErrorSchema;\n /** If set to true, causes the `extraErrors` to become blocking when the form is submitted */\n extraErrorsBlockSubmit?: boolean;\n /** If set to true, turns off HTML5 validation on the form; Set to `false` by default */\n noHtml5Validate?: boolean;\n /** If set to true, turns off all validation. Set to `false` by default\n *\n * @deprecated - In a future release, this switch may be replaced by making `validator` prop optional\n */\n noValidate?: boolean;\n /** If set to true, the form will perform validation and show any validation errors whenever the form data is changed,\n * rather than just on submit\n */\n liveValidate?: boolean;\n /** If `omitExtraData` and `liveOmit` are both set to true, then extra form data values that are not in any form field\n * will be removed whenever `onChange` is called. Set to `false` by default\n */\n liveOmit?: boolean;\n /** If set to true, then extra form data values that are not in any form field will be removed whenever `onSubmit` is\n * called. Set to `false` by default.\n */\n omitExtraData?: boolean;\n /** When this prop is set to `top` or 'bottom', a list of errors (or the custom error list defined in the `ErrorList`) will also\n * show. When set to false, only inline input validation errors will be shown. Set to `top` by default\n */\n showErrorList?: false | 'top' | 'bottom';\n /** A function can be passed to this prop in order to make modifications to the default errors resulting from JSON\n * Schema validation\n */\n transformErrors?: ErrorTransformer;\n /** If set to true, then the first field with an error will receive the focus when the form is submitted with errors\n */\n focusOnFirstError?: boolean | ((error: RJSFValidationError) => void);\n /** Optional string translation function, if provided, allows users to change the translation of the RJSF internal\n * strings. Some strings contain replaceable parameter values as indicated by `%1`, `%2`, etc. The number after the\n * `%` indicates the order of the parameter. The ordering of parameters is important because some languages may choose\n * to put the second parameter before the first in its translation.\n */\n translateString?: Registry['translateString'];\n /** Optional configuration object with flags, if provided, allows users to override default form state behavior\n * Currently only affecting minItems on array fields and handling of setting defaults based on the value of\n * `emptyObjectFields`\n */\n experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior;\n /** Optional function that allows for custom merging of `allOf` schemas\n */\n experimental_customMergeAllOf?: Experimental_CustomMergeAllOf;\n // Private\n /**\n * _internalFormWrapper is currently used by the semantic-ui theme to provide a custom wrapper around ``\n * that supports the proper rendering of those themes. To use this prop, one must pass a component that takes two\n * props: `children` and `as`. That component, at minimum, should render the `children` inside of a tag\n * unless `as` is provided, in which case, use the `as` prop in place of ``.\n * i.e.:\n * ```\n * export default function InternalForm({ children, as }) {\n * const FormTag = as || 'form';\n * return {children};\n * }\n * ```\n *\n * Use at your own risk as this prop is private and may change at any time without notice.\n */\n _internalFormWrapper?: ElementType;\n /** Support receiving a React ref to the Form\n */\n ref?: Ref>;\n}\n\n/** The data that is contained within the state for the `Form` */\nexport interface FormState {\n /** The JSON schema object for the form */\n schema: S;\n /** The uiSchema for the form */\n uiSchema: UiSchema;\n /** The `IdSchema` for the form, computed from the `schema`, the `rootFieldId`, the `formData` and the `idPrefix` and\n * `idSeparator` props.\n */\n idSchema: IdSchema;\n /** The schemaUtils implementation used by the `Form`, created from the `validator` and the `schema` */\n schemaUtils: SchemaUtilsType;\n /** The current data for the form, computed from the `formData` prop and the changes made by the user */\n formData?: T;\n /** Flag indicating whether the form is in edit mode, true when `formData` is passed to the form, otherwise false */\n edit: boolean;\n /** The current list of errors for the form, includes `extraErrors` */\n errors: RJSFValidationError[];\n /** The current errors, in `ErrorSchema` format, for the form, includes `extraErrors` */\n errorSchema: ErrorSchema;\n /** The current list of errors for the form directly from schema validation, does NOT include `extraErrors` */\n schemaValidationErrors: RJSFValidationError[];\n /** The current errors, in `ErrorSchema` format, for the form directly from schema validation, does NOT include\n * `extraErrors`\n */\n schemaValidationErrorSchema: ErrorSchema;\n // Private\n /** @description result of schemaUtils.retrieveSchema(schema, formData). This a memoized value to avoid re calculate at internal functions (getStateFromProps, onChange) */\n retrievedSchema: S;\n}\n\n/** The event data passed when changes have been made to the form, includes everything from the `FormState` except\n * the schema validation errors. An additional `status` is added when returned from `onSubmit`\n */\nexport interface IChangeEvent\n extends Omit, 'schemaValidationErrors' | 'schemaValidationErrorSchema'> {\n /** The status of the form when submitted */\n status?: 'submitted';\n}\n\n/** The `Form` component renders the outer form and all the fields defined in the `schema` */\nexport default class Form<\n T = any,\n S extends StrictRJSFSchema = RJSFSchema,\n F extends FormContextType = any\n> extends Component, FormState> {\n /** The ref used to hold the `form` element, this needs to be `any` because `tagName` or `_internalFormWrapper` can\n * provide any possible type here\n */\n formElement: RefObject;\n\n /** Constructs the `Form` from the `props`. Will setup the initial state from the props. It will also call the\n * `onChange` handler if the initially provided `formData` is modified to add missing default values as part of the\n * state construction.\n *\n * @param props - The initial props for the `Form`\n */\n constructor(props: FormProps) {\n super(props);\n\n if (!props.validator) {\n throw new Error('A validator is required for Form functionality to work');\n }\n\n this.state = this.getStateFromProps(props, props.formData);\n if (this.props.onChange && !deepEquals(this.state.formData, this.props.formData)) {\n this.props.onChange(this.state);\n }\n this.formElement = createRef();\n }\n\n /**\n * `getSnapshotBeforeUpdate` is a React lifecycle method that is invoked right before the most recently rendered\n * output is committed to the DOM. It enables your component to capture current values (e.g., scroll position) before\n * they are potentially changed.\n *\n * In this case, it checks if the props have changed since the last render. If they have, it computes the next state\n * of the component using `getStateFromProps` method and returns it along with a `shouldUpdate` flag set to `true` IF\n * the `nextState` and `prevState` are different, otherwise `false`. This ensures that we have the most up-to-date\n * state ready to be applied in `componentDidUpdate`.\n *\n * If `formData` hasn't changed, it simply returns an object with `shouldUpdate` set to `false`, indicating that a\n * state update is not necessary.\n *\n * @param prevProps - The previous set of props before the update.\n * @param prevState - The previous state before the update.\n * @returns Either an object containing the next state and a flag indicating that an update should occur, or an object\n * with a flag indicating that an update is not necessary.\n */\n getSnapshotBeforeUpdate(\n prevProps: FormProps,\n prevState: FormState\n ): { nextState: FormState; shouldUpdate: true } | { shouldUpdate: false } {\n if (!deepEquals(this.props, prevProps)) {\n const isSchemaChanged = !deepEquals(prevProps.schema, this.props.schema);\n const isFormDataChanged = !deepEquals(prevProps.formData, this.props.formData);\n const nextState = this.getStateFromProps(\n this.props,\n this.props.formData,\n // If the `schema` has changed, we need to update the retrieved schema.\n // Or if the `formData` changes, for example in the case of a schema with dependencies that need to\n // match one of the subSchemas, the retrieved schema must be updated.\n isSchemaChanged || isFormDataChanged ? undefined : this.state.retrievedSchema,\n isSchemaChanged\n );\n const shouldUpdate = !deepEquals(nextState, prevState);\n return { nextState, shouldUpdate };\n }\n return { shouldUpdate: false };\n }\n\n /**\n * `componentDidUpdate` is a React lifecycle method that is invoked immediately after updating occurs. This method is\n * not called for the initial render.\n *\n * Here, it checks if an update is necessary based on the `shouldUpdate` flag received from `getSnapshotBeforeUpdate`.\n * If an update is required, it applies the next state and, if needed, triggers the `onChange` handler to inform about\n * changes.\n *\n * This method effectively replaces the deprecated `UNSAFE_componentWillReceiveProps`, providing a safer alternative\n * to handle prop changes and state updates.\n *\n * @param _ - The previous set of props.\n * @param prevState - The previous state of the component before the update.\n * @param snapshot - The value returned from `getSnapshotBeforeUpdate`.\n */\n componentDidUpdate(\n _: FormProps,\n prevState: FormState,\n snapshot: { nextState: FormState; shouldUpdate: true } | { shouldUpdate: false }\n ) {\n if (snapshot.shouldUpdate) {\n const { nextState } = snapshot;\n\n if (\n !deepEquals(nextState.formData, this.props.formData) &&\n !deepEquals(nextState.formData, prevState.formData) &&\n this.props.onChange\n ) {\n this.props.onChange(nextState);\n }\n this.setState(nextState);\n }\n }\n\n /** Extracts the updated state from the given `props` and `inputFormData`. As part of this process, the\n * `inputFormData` is first processed to add any missing required defaults. After that, the data is run through the\n * validation process IF required by the `props`.\n *\n * @param props - The props passed to the `Form`\n * @param inputFormData - The new or current data for the `Form`\n * @param retrievedSchema - An expanded schema, if not provided, it will be retrieved from the `schema` and `formData`.\n * @param isSchemaChanged - A flag indicating whether the schema has changed.\n * @returns - The new state for the `Form`\n */\n getStateFromProps(\n props: FormProps,\n inputFormData?: T,\n retrievedSchema?: S,\n isSchemaChanged = false\n ): FormState {\n const state: FormState = this.state || {};\n const schema = 'schema' in props ? props.schema : this.props.schema;\n const uiSchema: UiSchema = ('uiSchema' in props ? props.uiSchema! : this.props.uiSchema!) || {};\n const edit = typeof inputFormData !== 'undefined';\n const liveValidate = 'liveValidate' in props ? props.liveValidate : this.props.liveValidate;\n const mustValidate = edit && !props.noValidate && liveValidate;\n const rootSchema = schema;\n const experimental_defaultFormStateBehavior =\n 'experimental_defaultFormStateBehavior' in props\n ? props.experimental_defaultFormStateBehavior\n : this.props.experimental_defaultFormStateBehavior;\n const experimental_customMergeAllOf =\n 'experimental_customMergeAllOf' in props\n ? props.experimental_customMergeAllOf\n : this.props.experimental_customMergeAllOf;\n let schemaUtils: SchemaUtilsType = state.schemaUtils;\n if (\n !schemaUtils ||\n schemaUtils.doesSchemaUtilsDiffer(\n props.validator,\n rootSchema,\n experimental_defaultFormStateBehavior,\n experimental_customMergeAllOf\n )\n ) {\n schemaUtils = createSchemaUtils(\n props.validator,\n rootSchema,\n experimental_defaultFormStateBehavior,\n experimental_customMergeAllOf\n );\n }\n const formData: T = schemaUtils.getDefaultFormState(schema, inputFormData) as T;\n const _retrievedSchema = retrievedSchema ?? schemaUtils.retrieveSchema(schema, formData);\n\n const getCurrentErrors = (): ValidationData => {\n // If the `props.noValidate` option is set or the schema has changed, we reset the error state.\n if (props.noValidate || isSchemaChanged) {\n return { errors: [], errorSchema: {} };\n } else if (!props.liveValidate) {\n return {\n errors: state.schemaValidationErrors || [],\n errorSchema: state.schemaValidationErrorSchema || {},\n };\n }\n return {\n errors: state.errors || [],\n errorSchema: state.errorSchema || {},\n };\n };\n\n let errors: RJSFValidationError[];\n let errorSchema: ErrorSchema | undefined;\n let schemaValidationErrors: RJSFValidationError[] = state.schemaValidationErrors;\n let schemaValidationErrorSchema: ErrorSchema = state.schemaValidationErrorSchema;\n if (mustValidate) {\n const schemaValidation = this.validate(formData, schema, schemaUtils, _retrievedSchema);\n errors = schemaValidation.errors;\n // If retrievedSchema is undefined which means the schema or formData has changed, we do not merge state.\n // Else in the case where it hasn't changed, we merge 'state.errorSchema' with 'schemaValidation.errorSchema.' This done to display the raised field error.\n if (retrievedSchema === undefined) {\n errorSchema = schemaValidation.errorSchema;\n } else {\n errorSchema = mergeObjects(\n this.state?.errorSchema,\n schemaValidation.errorSchema,\n 'preventDuplicates'\n ) as ErrorSchema;\n }\n schemaValidationErrors = errors;\n schemaValidationErrorSchema = errorSchema;\n } else {\n const currentErrors = getCurrentErrors();\n errors = currentErrors.errors;\n errorSchema = currentErrors.errorSchema;\n }\n if (props.extraErrors) {\n const merged = validationDataMerge({ errorSchema, errors }, props.extraErrors);\n errorSchema = merged.errorSchema;\n errors = merged.errors;\n }\n const idSchema = schemaUtils.toIdSchema(\n _retrievedSchema,\n uiSchema['ui:rootFieldId'],\n formData,\n props.idPrefix,\n props.idSeparator\n );\n const nextState: FormState = {\n schemaUtils,\n schema,\n uiSchema,\n idSchema,\n formData,\n edit,\n errors,\n errorSchema,\n schemaValidationErrors,\n schemaValidationErrorSchema,\n retrievedSchema: _retrievedSchema,\n };\n return nextState;\n }\n\n /** React lifecycle method that is used to determine whether component should be updated.\n *\n * @param nextProps - The next version of the props\n * @param nextState - The next version of the state\n * @returns - True if the component should be updated, false otherwise\n */\n shouldComponentUpdate(nextProps: FormProps, nextState: FormState): boolean {\n return shouldRender(this, nextProps, nextState);\n }\n\n /** Validates the `formData` against the `schema` using the `altSchemaUtils` (if provided otherwise it uses the\n * `schemaUtils` in the state), returning the results.\n *\n * @param formData - The new form data to validate\n * @param schema - The schema used to validate against\n * @param altSchemaUtils - The alternate schemaUtils to use for validation\n */\n validate(\n formData: T | undefined,\n schema = this.props.schema,\n altSchemaUtils?: SchemaUtilsType,\n retrievedSchema?: S\n ): ValidationData {\n const schemaUtils = altSchemaUtils ? altSchemaUtils : this.state.schemaUtils;\n const { customValidate, transformErrors, uiSchema } = this.props;\n const resolvedSchema = retrievedSchema ?? schemaUtils.retrieveSchema(schema, formData);\n return schemaUtils\n .getValidator()\n .validateFormData(formData, resolvedSchema, customValidate, transformErrors, uiSchema);\n }\n\n /** Renders any errors contained in the `state` in using the `ErrorList`, if not disabled by `showErrorList`. */\n renderErrors(registry: Registry) {\n const { errors, errorSchema, schema, uiSchema } = this.state;\n const { formContext } = this.props;\n const options = getUiOptions(uiSchema);\n const ErrorListTemplate = getTemplate<'ErrorListTemplate', T, S, F>('ErrorListTemplate', registry, options);\n\n if (errors && errors.length) {\n return (\n \n );\n }\n return null;\n }\n\n /** Returns the `formData` with only the elements specified in the `fields` list\n *\n * @param formData - The data for the `Form`\n * @param fields - The fields to keep while filtering\n */\n getUsedFormData = (formData: T | undefined, fields: string[][]): T | undefined => {\n // For the case of a single input form\n if (fields.length === 0 && typeof formData !== 'object') {\n return formData;\n }\n\n // _pick has incorrect type definition, it works with string[][], because lodash/hasIn supports it\n const data: GenericObjectType = _pick(formData, fields as unknown as string[]);\n if (Array.isArray(formData)) {\n return Object.keys(data).map((key: string) => data[key]) as unknown as T;\n }\n\n return data as T;\n };\n\n /** Returns the list of field names from inspecting the `pathSchema` as well as using the `formData`\n *\n * @param pathSchema - The `PathSchema` object for the form\n * @param [formData] - The form data to use while checking for empty objects/arrays\n */\n getFieldNames = (pathSchema: PathSchema, formData?: T): string[][] => {\n const getAllPaths = (_obj: GenericObjectType, acc: string[][] = [], paths: string[][] = [[]]) => {\n Object.keys(_obj).forEach((key: string) => {\n if (typeof _obj[key] === 'object') {\n const newPaths = paths.map((path) => [...path, key]);\n // If an object is marked with additionalProperties, all its keys are valid\n if (_obj[key][RJSF_ADDITIONAL_PROPERTIES_FLAG] && _obj[key][NAME_KEY] !== '') {\n acc.push(_obj[key][NAME_KEY]);\n } else {\n getAllPaths(_obj[key], acc, newPaths);\n }\n } else if (key === NAME_KEY && _obj[key] !== '') {\n paths.forEach((path) => {\n const formValue = _get(formData, path);\n // adds path to fieldNames if it points to a value\n // or an empty object/array\n if (\n typeof formValue !== 'object' ||\n _isEmpty(formValue) ||\n (Array.isArray(formValue) && formValue.every((val) => typeof val !== 'object'))\n ) {\n acc.push(path);\n }\n });\n }\n });\n return acc;\n };\n\n return getAllPaths(pathSchema);\n };\n\n /** Returns the `formData` after filtering to remove any extra data not in a form field\n *\n * @param formData - The data for the `Form`\n * @returns The `formData` after omitting extra data\n */\n omitExtraData = (formData?: T): T | undefined => {\n const { schema, schemaUtils } = this.state;\n const retrievedSchema = schemaUtils.retrieveSchema(schema, formData);\n const pathSchema = schemaUtils.toPathSchema(retrievedSchema, '', formData);\n const fieldNames = this.getFieldNames(pathSchema, formData);\n const newFormData = this.getUsedFormData(formData, fieldNames);\n return newFormData;\n };\n\n // Filtering errors based on your retrieved schema to only show errors for properties in the selected branch.\n private filterErrorsBasedOnSchema(schemaErrors: ErrorSchema, resolvedSchema?: S, formData?: any): ErrorSchema {\n const { retrievedSchema, schemaUtils } = this.state;\n const _retrievedSchema = resolvedSchema ?? retrievedSchema;\n const pathSchema = schemaUtils.toPathSchema(_retrievedSchema, '', formData);\n const fieldNames = this.getFieldNames(pathSchema, formData);\n const filteredErrors: ErrorSchema = _pick(schemaErrors, fieldNames as unknown as string[]);\n // If the root schema is of a primitive type, do not filter out the __errors\n if (resolvedSchema?.type !== 'object' && resolvedSchema?.type !== 'array') {\n filteredErrors.__errors = schemaErrors.__errors;\n }\n // Removing undefined, null and empty errors.\n const filterNilOrEmptyErrors = (errors: any): ErrorSchema => {\n _forEach(errors, (errorAtKey, errorKey: keyof typeof errors) => {\n if (_isNil(errorAtKey)) {\n delete errors[errorKey];\n } else if (typeof errorAtKey === 'object' && !Array.isArray(errorAtKey.__errors)) {\n filterNilOrEmptyErrors(errorAtKey);\n }\n });\n return errors;\n };\n return filterNilOrEmptyErrors(filteredErrors);\n }\n\n /** Function to handle changes made to a field in the `Form`. This handler receives an entirely new copy of the\n * `formData` along with a new `ErrorSchema`. It will first update the `formData` with any missing default fields and\n * then, if `omitExtraData` and `liveOmit` are turned on, the `formData` will be filtered to remove any extra data not\n * in a form field. Then, the resulting formData will be validated if required. The state will be updated with the new\n * updated (potentially filtered) `formData`, any errors that resulted from validation. Finally the `onChange`\n * callback will be called if specified with the updated state.\n *\n * @param formData - The new form data from a change to a field\n * @param newErrorSchema - The new `ErrorSchema` based on the field change\n * @param id - The id of the field that caused the change\n */\n onChange = (formData: T | undefined, newErrorSchema?: ErrorSchema, id?: string) => {\n const { extraErrors, omitExtraData, liveOmit, noValidate, liveValidate, onChange } = this.props;\n const { schemaUtils, schema, retrievedSchema } = this.state;\n\n if (isObject(formData) || Array.isArray(formData)) {\n const newState = this.getStateFromProps(this.props, formData, retrievedSchema);\n formData = newState.formData;\n }\n\n const mustValidate = !noValidate && liveValidate;\n let state: Partial> = { formData, schema };\n let newFormData = formData;\n\n if (omitExtraData === true && liveOmit === true) {\n newFormData = this.omitExtraData(formData);\n state = {\n formData: newFormData,\n };\n }\n\n if (mustValidate) {\n const schemaValidation = this.validate(newFormData, schema, schemaUtils, retrievedSchema);\n let errors = schemaValidation.errors;\n let errorSchema = schemaValidation.errorSchema;\n const schemaValidationErrors = errors;\n const schemaValidationErrorSchema = errorSchema;\n if (extraErrors) {\n const merged = validationDataMerge(schemaValidation, extraErrors);\n errorSchema = merged.errorSchema;\n errors = merged.errors;\n }\n // Merging 'newErrorSchema' into 'errorSchema' to display the custom raised errors.\n if (newErrorSchema) {\n const filteredErrors = this.filterErrorsBasedOnSchema(newErrorSchema, retrievedSchema, newFormData);\n errorSchema = mergeObjects(errorSchema, filteredErrors, 'preventDuplicates') as ErrorSchema;\n }\n state = {\n formData: newFormData,\n errors,\n errorSchema,\n schemaValidationErrors,\n schemaValidationErrorSchema,\n };\n } else if (!noValidate && newErrorSchema) {\n const errorSchema = extraErrors\n ? (mergeObjects(newErrorSchema, extraErrors, 'preventDuplicates') as ErrorSchema)\n : newErrorSchema;\n state = {\n formData: newFormData,\n errorSchema: errorSchema,\n errors: toErrorList(errorSchema),\n };\n }\n this.setState(state as FormState, () => onChange && onChange({ ...this.state, ...state }, id));\n };\n\n /**\n * Callback function to handle reset form data.\n * - Reset all fields with default values.\n * - Reset validations and errors\n *\n */\n reset = () => {\n const { onChange } = this.props;\n const newState = this.getStateFromProps(this.props, undefined);\n const newFormData = newState.formData;\n const state = {\n formData: newFormData,\n errorSchema: {},\n errors: [] as unknown,\n schemaValidationErrors: [] as unknown,\n schemaValidationErrorSchema: {},\n } as FormState;\n\n this.setState(state, () => onChange && onChange({ ...this.state, ...state }));\n };\n\n /** Callback function to handle when a field on the form is blurred. Calls the `onBlur` callback for the `Form` if it\n * was provided.\n *\n * @param id - The unique `id` of the field that was blurred\n * @param data - The data associated with the field that was blurred\n */\n onBlur = (id: string, data: any) => {\n const { onBlur } = this.props;\n if (onBlur) {\n onBlur(id, data);\n }\n };\n\n /** Callback function to handle when a field on the form is focused. Calls the `onFocus` callback for the `Form` if it\n * was provided.\n *\n * @param id - The unique `id` of the field that was focused\n * @param data - The data associated with the field that was focused\n */\n onFocus = (id: string, data: any) => {\n const { onFocus } = this.props;\n if (onFocus) {\n onFocus(id, data);\n }\n };\n\n /** Callback function to handle when the form is submitted. First, it prevents the default event behavior. Nothing\n * happens if the target and currentTarget of the event are not the same. It will omit any extra data in the\n * `formData` in the state if `omitExtraData` is true. It will validate the resulting `formData`, reporting errors\n * via the `onError()` callback unless validation is disabled. Finally, it will add in any `extraErrors` and then call\n * back the `onSubmit` callback if it was provided.\n *\n * @param event - The submit HTML form event\n */\n onSubmit = (event: FormEvent) => {\n event.preventDefault();\n if (event.target !== event.currentTarget) {\n return;\n }\n\n event.persist();\n const { omitExtraData, extraErrors, noValidate, onSubmit } = this.props;\n let { formData: newFormData } = this.state;\n\n if (omitExtraData === true) {\n newFormData = this.omitExtraData(newFormData);\n }\n\n if (noValidate || this.validateFormWithFormData(newFormData)) {\n // There are no errors generated through schema validation.\n // Check for user provided errors and update state accordingly.\n const errorSchema = extraErrors || {};\n const errors = extraErrors ? toErrorList(extraErrors) : [];\n this.setState(\n {\n formData: newFormData,\n errors,\n errorSchema,\n schemaValidationErrors: [],\n schemaValidationErrorSchema: {},\n },\n () => {\n if (onSubmit) {\n onSubmit({ ...this.state, formData: newFormData, status: 'submitted' }, event);\n }\n }\n );\n }\n };\n\n /** Returns the registry for the form */\n getRegistry(): Registry {\n const { translateString: customTranslateString, uiSchema = {} } = this.props;\n const { schemaUtils } = this.state;\n const { fields, templates, widgets, formContext, translateString } = getDefaultRegistry();\n return {\n fields: { ...fields, ...this.props.fields },\n templates: {\n ...templates,\n ...this.props.templates,\n ButtonTemplates: {\n ...templates.ButtonTemplates,\n ...this.props.templates?.ButtonTemplates,\n },\n },\n widgets: { ...widgets, ...this.props.widgets },\n rootSchema: this.props.schema,\n formContext: this.props.formContext || formContext,\n schemaUtils,\n translateString: customTranslateString || translateString,\n globalUiOptions: uiSchema[UI_GLOBAL_OPTIONS_KEY],\n };\n }\n\n /** Provides a function that can be used to programmatically submit the `Form` */\n submit = () => {\n if (this.formElement.current) {\n const submitCustomEvent = new CustomEvent('submit', {\n cancelable: true,\n });\n submitCustomEvent.preventDefault();\n this.formElement.current.dispatchEvent(submitCustomEvent);\n this.formElement.current.requestSubmit();\n }\n };\n\n /** Attempts to focus on the field associated with the `error`. Uses the `property` field to compute path of the error\n * field, then, using the `idPrefix` and `idSeparator` converts that path into an id. Then the input element with that\n * id is attempted to be found using the `formElement` ref. If it is located, then it is focused.\n *\n * @param error - The error on which to focus\n */\n focusOnError(error: RJSFValidationError) {\n const { idPrefix = 'root', idSeparator = '_' } = this.props;\n const { property } = error;\n const path = _toPath(property);\n if (path[0] === '') {\n // Most of the time the `.foo` property results in the first element being empty, so replace it with the idPrefix\n path[0] = idPrefix;\n } else {\n // Otherwise insert the idPrefix into the first location using unshift\n path.unshift(idPrefix);\n }\n\n const elementId = path.join(idSeparator);\n let field = this.formElement.current.elements[elementId];\n if (!field) {\n // if not an exact match, try finding an input starting with the element id (like radio buttons or checkboxes)\n field = this.formElement.current.querySelector(`input[id^=\"${elementId}\"`);\n }\n if (field && field.length) {\n // If we got a list with length > 0\n field = field[0];\n }\n if (field) {\n field.focus();\n }\n }\n\n /** Validates the form using the given `formData`. For use on form submission or on programmatic validation.\n * If `onError` is provided, then it will be called with the list of errors.\n *\n * @param formData - The form data to validate\n * @returns - True if the form is valid, false otherwise.\n */\n validateFormWithFormData = (formData?: T): boolean => {\n const { extraErrors, extraErrorsBlockSubmit, focusOnFirstError, onError } = this.props;\n const { errors: prevErrors } = this.state;\n const schemaValidation = this.validate(formData);\n let errors = schemaValidation.errors;\n let errorSchema = schemaValidation.errorSchema;\n const schemaValidationErrors = errors;\n const schemaValidationErrorSchema = errorSchema;\n const hasError = errors.length > 0 || (extraErrors && extraErrorsBlockSubmit);\n if (hasError) {\n if (extraErrors) {\n const merged = validationDataMerge(schemaValidation, extraErrors);\n errorSchema = merged.errorSchema;\n errors = merged.errors;\n }\n if (focusOnFirstError) {\n if (typeof focusOnFirstError === 'function') {\n focusOnFirstError(errors[0]);\n } else {\n this.focusOnError(errors[0]);\n }\n }\n this.setState(\n {\n errors,\n errorSchema,\n schemaValidationErrors,\n schemaValidationErrorSchema,\n },\n () => {\n if (onError) {\n onError(errors);\n } else {\n console.error('Form validation failed', errors);\n }\n }\n );\n } else if (prevErrors.length > 0) {\n this.setState({\n errors: [],\n errorSchema: {},\n schemaValidationErrors: [],\n schemaValidationErrorSchema: {},\n });\n }\n return !hasError;\n };\n\n /** Programmatically validate the form. If `omitExtraData` is true, the `formData` will first be filtered to remove\n * any extra data not in a form field. If `onError` is provided, then it will be called with the list of errors the\n * same way as would happen on form submission.\n *\n * @returns - True if the form is valid, false otherwise.\n */\n validateForm() {\n const { omitExtraData } = this.props;\n let { formData: newFormData } = this.state;\n if (omitExtraData === true) {\n newFormData = this.omitExtraData(newFormData);\n }\n return this.validateFormWithFormData(newFormData);\n }\n\n /** Renders the `Form` fields inside the | `tagName` or `_internalFormWrapper`, rendering any errors if\n * needed along with the submit button or any children of the form.\n */\n render() {\n const {\n children,\n id,\n idPrefix,\n idSeparator,\n className = '',\n tagName,\n name,\n method,\n target,\n action,\n autoComplete,\n enctype,\n acceptcharset,\n acceptCharset,\n noHtml5Validate = false,\n disabled,\n readonly,\n formContext,\n showErrorList = 'top',\n _internalFormWrapper,\n } = this.props;\n\n const { schema, uiSchema, formData, errorSchema, idSchema } = this.state;\n const registry = this.getRegistry();\n const { SchemaField: _SchemaField } = registry.fields;\n const { SubmitButton } = registry.templates.ButtonTemplates;\n // The `semantic-ui` and `material-ui` themes have `_internalFormWrapper`s that take an `as` prop that is the\n // PropTypes.elementType to use for the inner tag, so we'll need to pass `tagName` along if it is provided.\n // NOTE, the `as` prop is native to `semantic-ui` and is emulated in the `material-ui` theme\n const as = _internalFormWrapper ? tagName : undefined;\n const FormTag = _internalFormWrapper || tagName || 'form';\n\n let { [SUBMIT_BTN_OPTIONS_KEY]: submitOptions = {} } = getUiOptions(uiSchema);\n if (disabled) {\n submitOptions = { ...submitOptions, props: { ...submitOptions.props, disabled: true } };\n }\n const submitUiSchema = { [UI_OPTIONS_KEY]: { [SUBMIT_BTN_OPTIONS_KEY]: submitOptions } };\n\n return (\n \n {showErrorList === 'top' && this.renderErrors(registry)}\n <_SchemaField\n name=''\n schema={schema}\n uiSchema={uiSchema}\n errorSchema={errorSchema}\n idSchema={idSchema}\n idPrefix={idPrefix}\n idSeparator={idSeparator}\n formContext={formContext}\n formData={formData}\n onChange={this.onChange}\n onBlur={this.onBlur}\n onFocus={this.onFocus}\n registry={registry}\n disabled={disabled}\n readonly={readonly}\n />\n\n {children ? children : }\n {showErrorList === 'bottom' && this.renderErrors(registry)}\n \n );\n }\n}\n", "import { englishStringTranslator, FormContextType, Registry, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';\n\nimport fields from './components/fields';\nimport templates from './components/templates';\nimport widgets from './components/widgets';\n\n/** The default registry consists of all the fields, templates and widgets provided in the core implementation,\n * plus an empty `rootSchema` and `formContext. We omit schemaUtils here because it cannot be defaulted without a\n * rootSchema and validator. It will be added into the computed registry later in the Form.\n */\nexport default function getDefaultRegistry<\n T = any,\n S extends StrictRJSFSchema = RJSFSchema,\n F extends FormContextType = any\n>(): Omit, 'schemaUtils'> {\n return {\n fields: fields(),\n templates: templates(),\n widgets: widgets(),\n rootSchema: {} as S,\n formContext: {} as F,\n translateString: englishStringTranslator,\n };\n}\n", "import { Component, MouseEvent } from 'react';\nimport {\n getTemplate,\n getWidget,\n getUiOptions,\n isFixedItems,\n allowAdditionalItems,\n isCustomWidget,\n optionsList,\n ArrayFieldTemplateProps,\n ErrorSchema,\n FieldProps,\n FormContextType,\n IdSchema,\n RJSFSchema,\n StrictRJSFSchema,\n TranslatableString,\n UiSchema,\n ITEMS_KEY,\n} from '@rjsf/utils';\nimport cloneDeep from 'lodash/cloneDeep';\nimport get from 'lodash/get';\nimport isObject from 'lodash/isObject';\nimport set from 'lodash/set';\nimport { nanoid } from 'nanoid';\n\n/** Type used to represent the keyed form data used in the state */\ntype KeyedFormDataType = { key: string; item: T };\n\n/** Type used for the state of the `ArrayField` component */\ntype ArrayFieldState = {\n /** The keyed form data elements */\n keyedFormData: KeyedFormDataType[];\n /** Flag indicating whether any of the keyed form data has been updated */\n updatedKeyedFormData: boolean;\n};\n\n/** Used to generate a unique ID for an element in a row */\nfunction generateRowId() {\n return nanoid();\n}\n\n/** Converts the `formData` into `KeyedFormDataType` data, using the `generateRowId()` function to create the key\n *\n * @param formData - The data for the form\n * @returns - The `formData` converted into a `KeyedFormDataType` element\n */\nfunction generateKeyedFormData(formData: T[]): KeyedFormDataType[] {\n return !Array.isArray(formData)\n ? []\n : formData.map((item) => {\n return {\n key: generateRowId(),\n item,\n };\n });\n}\n\n/** Converts `KeyedFormDataType` data into the inner `formData`\n *\n * @param keyedFormData - The `KeyedFormDataType` to be converted\n * @returns - The inner `formData` item(s) in the `keyedFormData`\n */\nfunction keyedToPlainFormData(keyedFormData: KeyedFormDataType | KeyedFormDataType[]): T[] {\n if (Array.isArray(keyedFormData)) {\n return keyedFormData.map((keyedItem) => keyedItem.item);\n }\n return [];\n}\n\n/** The `ArrayField` component is used to render a field in the schema that is of type `array`. It supports both normal\n * and fixed array, allowing user to add and remove elements from the array data.\n */\nclass ArrayField extends Component<\n FieldProps,\n ArrayFieldState\n> {\n /** Constructs an `ArrayField` from the `props`, generating the initial keyed data from the `formData`\n *\n * @param props - The `FieldProps` for this template\n */\n constructor(props: FieldProps) {\n super(props);\n const { formData = [] } = props;\n const keyedFormData = generateKeyedFormData(formData);\n this.state = {\n keyedFormData,\n updatedKeyedFormData: false,\n };\n }\n\n /** React lifecycle method that is called when the props are about to change allowing the state to be updated. It\n * regenerates the keyed form data and returns it\n *\n * @param nextProps - The next set of props data\n * @param prevState - The previous set of state data\n */\n static getDerivedStateFromProps(\n nextProps: Readonly>,\n prevState: Readonly>\n ) {\n // Don't call getDerivedStateFromProps if keyed formdata was just updated.\n if (prevState.updatedKeyedFormData) {\n return {\n updatedKeyedFormData: false,\n };\n }\n const nextFormData = Array.isArray(nextProps.formData) ? nextProps.formData : [];\n const previousKeyedFormData = prevState.keyedFormData || [];\n const newKeyedFormData =\n nextFormData.length === previousKeyedFormData.length\n ? previousKeyedFormData.map((previousKeyedFormDatum, index) => {\n return {\n key: previousKeyedFormDatum.key,\n item: nextFormData[index],\n };\n })\n : generateKeyedFormData(nextFormData);\n return {\n keyedFormData: newKeyedFormData,\n };\n }\n\n /** Returns the appropriate title for an item by getting first the title from the schema.items, then falling back to\n * the description from the schema.items, and finally the string \"Item\"\n */\n get itemTitle() {\n const { schema, registry } = this.props;\n const { translateString } = registry;\n return get(\n schema,\n [ITEMS_KEY, 'title'],\n get(schema, [ITEMS_KEY, 'description'], translateString(TranslatableString.ArrayItemTitle))\n );\n }\n\n /** Determines whether the item described in the schema is always required, which is determined by whether any item\n * may be null.\n *\n * @param itemSchema - The schema for the item\n * @return - True if the item schema type does not contain the \"null\" type\n */\n isItemRequired(itemSchema: S) {\n if (Array.isArray(itemSchema.type)) {\n // While we don't yet support composite/nullable jsonschema types, it's\n // future-proof to check for requirement against these.\n return !itemSchema.type.includes('null');\n }\n // All non-null array item types are inherently required by design\n return itemSchema.type !== 'null';\n }\n\n /** Determines whether more items can be added to the array. If the uiSchema indicates the array doesn't allow adding\n * then false is returned. Otherwise, if the schema indicates that there are a maximum number of items and the\n * `formData` matches that value, then false is returned, otherwise true is returned.\n *\n * @param formItems - The list of items in the form\n * @returns - True if the item is addable otherwise false\n */\n canAddItem(formItems: any[]) {\n const { schema, uiSchema, registry } = this.props;\n let { addable } = getUiOptions(uiSchema, registry.globalUiOptions);\n if (addable !== false) {\n // if ui:options.addable was not explicitly set to false, we can add\n // another item if we have not exceeded maxItems yet\n if (schema.maxItems !== undefined) {\n addable = formItems.length < schema.maxItems;\n } else {\n addable = true;\n }\n }\n return addable;\n }\n\n /** Returns the default form information for an item based on the schema for that item. Deals with the possibility\n * that the schema is fixed and allows additional items.\n */\n _getNewFormDataRow = (): T => {\n const { schema, registry } = this.props;\n const { schemaUtils } = registry;\n let itemSchema = schema.items as S;\n if (isFixedItems(schema) && allowAdditionalItems(schema)) {\n itemSchema = schema.additionalItems as S;\n }\n // Cast this as a T to work around schema utils being for T[] caused by the FieldProps call on the class\n return schemaUtils.getDefaultFormState(itemSchema) as unknown as T;\n };\n\n /** Callback handler for when the user clicks on the add or add at index buttons. Creates a new row of keyed form data\n * either at the end of the list (when index is not specified) or inserted at the `index` when it is, adding it into\n * the state, and then returning `onChange()` with the plain form data converted from the keyed data\n *\n * @param event - The event for the click\n * @param [index] - The optional index at which to add the new data\n */\n _handleAddClick(event: MouseEvent, index?: number) {\n if (event) {\n event.preventDefault();\n }\n\n const { onChange, errorSchema } = this.props;\n const { keyedFormData } = this.state;\n // refs #195: revalidate to ensure properly reindexing errors\n let newErrorSchema: ErrorSchema;\n if (errorSchema) {\n newErrorSchema = {};\n for (const idx in errorSchema) {\n const i = parseInt(idx);\n if (index === undefined || i < index) {\n set(newErrorSchema, [i], errorSchema[idx]);\n } else if (i >= index) {\n set(newErrorSchema, [i + 1], errorSchema[idx]);\n }\n }\n }\n\n const newKeyedFormDataRow: KeyedFormDataType = {\n key: generateRowId(),\n item: this._getNewFormDataRow(),\n };\n const newKeyedFormData = [...keyedFormData];\n if (index !== undefined) {\n newKeyedFormData.splice(index, 0, newKeyedFormDataRow);\n } else {\n newKeyedFormData.push(newKeyedFormDataRow);\n }\n this.setState(\n {\n keyedFormData: newKeyedFormData,\n updatedKeyedFormData: true,\n },\n () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema as ErrorSchema)\n );\n }\n\n /** Callback handler for when the user clicks on the add button. Creates a new row of keyed form data at the end of\n * the list, adding it into the state, and then returning `onChange()` with the plain form data converted from the\n * keyed data\n *\n * @param event - The event for the click\n */\n onAddClick = (event: MouseEvent) => {\n this._handleAddClick(event);\n };\n\n /** Callback handler for when the user clicks on the add button on an existing array element. Creates a new row of\n * keyed form data inserted at the `index`, adding it into the state, and then returning `onChange()` with the plain\n * form data converted from the keyed data\n *\n * @param index - The index at which the add button is clicked\n */\n onAddIndexClick = (index: number) => {\n return (event: MouseEvent) => {\n this._handleAddClick(event, index);\n };\n };\n\n /** Callback handler for when the user clicks on the copy button on an existing array element. Clones the row of\n * keyed form data at the `index` into the next position in the state, and then returning `onChange()` with the plain\n * form data converted from the keyed data\n *\n * @param index - The index at which the copy button is clicked\n */\n onCopyIndexClick = (index: number) => {\n return (event: MouseEvent) => {\n if (event) {\n event.preventDefault();\n }\n\n const { onChange, errorSchema } = this.props;\n const { keyedFormData } = this.state;\n // refs #195: revalidate to ensure properly reindexing errors\n let newErrorSchema: ErrorSchema;\n if (errorSchema) {\n newErrorSchema = {};\n for (const idx in errorSchema) {\n const i = parseInt(idx);\n if (i <= index) {\n set(newErrorSchema, [i], errorSchema[idx]);\n } else if (i > index) {\n set(newErrorSchema, [i + 1], errorSchema[idx]);\n }\n }\n }\n\n const newKeyedFormDataRow: KeyedFormDataType = {\n key: generateRowId(),\n item: cloneDeep(keyedFormData[index].item),\n };\n const newKeyedFormData = [...keyedFormData];\n if (index !== undefined) {\n newKeyedFormData.splice(index + 1, 0, newKeyedFormDataRow);\n } else {\n newKeyedFormData.push(newKeyedFormDataRow);\n }\n this.setState(\n {\n keyedFormData: newKeyedFormData,\n updatedKeyedFormData: true,\n },\n () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema as ErrorSchema)\n );\n };\n };\n\n /** Callback handler for when the user clicks on the remove button on an existing array element. Removes the row of\n * keyed form data at the `index` in the state, and then returning `onChange()` with the plain form data converted\n * from the keyed data\n *\n * @param index - The index at which the remove button is clicked\n */\n onDropIndexClick = (index: number) => {\n return (event: MouseEvent) => {\n if (event) {\n event.preventDefault();\n }\n const { onChange, errorSchema } = this.props;\n const { keyedFormData } = this.state;\n // refs #195: revalidate to ensure properly reindexing errors\n let newErrorSchema: ErrorSchema;\n if (errorSchema) {\n newErrorSchema = {};\n for (const idx in errorSchema) {\n const i = parseInt(idx);\n if (i < index) {\n set(newErrorSchema, [i], errorSchema[idx]);\n } else if (i > index) {\n set(newErrorSchema, [i - 1], errorSchema[idx]);\n }\n }\n }\n const newKeyedFormData = keyedFormData.filter((_, i) => i !== index);\n this.setState(\n {\n keyedFormData: newKeyedFormData,\n updatedKeyedFormData: true,\n },\n () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema as ErrorSchema)\n );\n };\n };\n\n /** Callback handler for when the user clicks on one of the move item buttons on an existing array element. Moves the\n * row of keyed form data at the `index` to the `newIndex` in the state, and then returning `onChange()` with the\n * plain form data converted from the keyed data\n *\n * @param index - The index of the item to move\n * @param newIndex - The index to where the item is to be moved\n */\n onReorderClick = (index: number, newIndex: number) => {\n return (event: MouseEvent) => {\n if (event) {\n event.preventDefault();\n event.currentTarget.blur();\n }\n const { onChange, errorSchema } = this.props;\n let newErrorSchema: ErrorSchema;\n if (errorSchema) {\n newErrorSchema = {};\n for (const idx in errorSchema) {\n const i = parseInt(idx);\n if (i == index) {\n set(newErrorSchema, [newIndex], errorSchema[index]);\n } else if (i == newIndex) {\n set(newErrorSchema, [index], errorSchema[newIndex]);\n } else {\n set(newErrorSchema, [idx], errorSchema[i]);\n }\n }\n }\n\n const { keyedFormData } = this.state;\n function reOrderArray() {\n // Copy item\n const _newKeyedFormData = keyedFormData.slice();\n\n // Moves item from index to newIndex\n _newKeyedFormData.splice(index, 1);\n _newKeyedFormData.splice(newIndex, 0, keyedFormData[index]);\n\n return _newKeyedFormData;\n }\n const newKeyedFormData = reOrderArray();\n this.setState(\n {\n keyedFormData: newKeyedFormData,\n },\n () => onChange(keyedToPlainFormData(newKeyedFormData), newErrorSchema as ErrorSchema)\n );\n };\n };\n\n /** Callback handler used to deal with changing the value of the data in the array at the `index`. Calls the\n * `onChange` callback with the updated form data\n *\n * @param index - The index of the item being changed\n */\n onChangeForIndex = (index: number) => {\n return (value: any, newErrorSchema?: ErrorSchema, id?: string) => {\n const { formData, onChange, errorSchema } = this.props;\n const arrayData = Array.isArray(formData) ? formData : [];\n const newFormData = arrayData.map((item: T, i: number) => {\n // We need to treat undefined items as nulls to have validation.\n // See https://github.com/tdegrunt/jsonschema/issues/206\n const jsonValue = typeof value === 'undefined' ? null : value;\n return index === i ? jsonValue : item;\n });\n onChange(\n newFormData,\n errorSchema &&\n errorSchema && {\n ...errorSchema,\n [index]: newErrorSchema,\n },\n id\n );\n };\n };\n\n /** Callback handler used to change the value for a checkbox */\n onSelectChange = (value: any) => {\n const { onChange, idSchema } = this.props;\n onChange(value, undefined, idSchema && idSchema.$id);\n };\n\n /** Renders the `ArrayField` depending on the specific needs of the schema and uischema elements\n */\n render() {\n const { schema, uiSchema, idSchema, registry } = this.props;\n const { schemaUtils, translateString } = registry;\n if (!(ITEMS_KEY in schema)) {\n const uiOptions = getUiOptions(uiSchema);\n const UnsupportedFieldTemplate = getTemplate<'UnsupportedFieldTemplate', T[], S, F>(\n 'UnsupportedFieldTemplate',\n registry,\n uiOptions\n );\n\n return (\n \n );\n }\n if (schemaUtils.isMultiSelect(schema)) {\n // If array has enum or uniqueItems set to true, call renderMultiSelect() to render the default multiselect widget or a custom widget, if specified.\n return this.renderMultiSelect();\n }\n if (isCustomWidget(uiSchema)) {\n return this.renderCustomWidget();\n }\n if (isFixedItems(schema)) {\n return this.renderFixedArray();\n }\n if (schemaUtils.isFilesArray(schema, uiSchema)) {\n return this.renderFiles();\n }\n return this.renderNormalArray();\n }\n\n /** Renders a normal array without any limitations of length\n */\n renderNormalArray() {\n const {\n schema,\n uiSchema = {},\n errorSchema,\n idSchema,\n name,\n title,\n disabled = false,\n readonly = false,\n autofocus = false,\n required = false,\n registry,\n onBlur,\n onFocus,\n idPrefix,\n idSeparator = '_',\n rawErrors,\n } = this.props;\n const { keyedFormData } = this.state;\n const fieldTitle = schema.title || title || name;\n const { schemaUtils, formContext } = registry;\n const uiOptions = getUiOptions(uiSchema);\n const _schemaItems: S = isObject(schema.items) ? (schema.items as S) : ({} as S);\n const itemsSchema: S = schemaUtils.retrieveSchema(_schemaItems);\n const formData = keyedToPlainFormData(this.state.keyedFormData);\n const canAdd = this.canAddItem(formData);\n const arrayProps: ArrayFieldTemplateProps = {\n canAdd,\n items: keyedFormData.map((keyedItem, index) => {\n const { key, item } = keyedItem;\n // While we are actually dealing with a single item of type T, the types require a T[], so cast\n const itemCast = item as unknown as T[];\n const itemSchema = schemaUtils.retrieveSchema(_schemaItems, itemCast);\n const itemErrorSchema = errorSchema ? (errorSchema[index] as ErrorSchema) : undefined;\n const itemIdPrefix = idSchema.$id + idSeparator + index;\n const itemIdSchema = schemaUtils.toIdSchema(itemSchema, itemIdPrefix, itemCast, idPrefix, idSeparator);\n return this.renderArrayFieldItem({\n key,\n index,\n name: name && `${name}-${index}`,\n title: fieldTitle ? `${fieldTitle}-${index + 1}` : undefined,\n canAdd,\n canMoveUp: index > 0,\n canMoveDown: index < formData.length - 1,\n itemSchema,\n itemIdSchema,\n itemErrorSchema,\n itemData: itemCast,\n itemUiSchema: uiSchema.items,\n autofocus: autofocus && index === 0,\n onBlur,\n onFocus,\n rawErrors,\n totalItems: keyedFormData.length,\n });\n }),\n className: `field field-array field-array-of-${itemsSchema.type}`,\n disabled,\n idSchema,\n uiSchema,\n onAddClick: this.onAddClick,\n readonly,\n required,\n schema,\n title: fieldTitle,\n formContext,\n formData,\n rawErrors,\n registry,\n };\n\n const Template = getTemplate<'ArrayFieldTemplate', T[], S, F>('ArrayFieldTemplate', registry, uiOptions);\n return