import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
import { useCallback, Component } from 'react';
import { ADDITIONAL_PROPERTY_FLAG, deepEquals, descriptionId, getSchemaType, getTemplate, getUiOptions, ID_KEY, mergeObjects, TranslatableString, UI_OPTIONS_KEY, } from '@rjsf/utils';
import isObject from 'lodash/isObject';
import omit from 'lodash/omit';
import Markdown from 'markdown-to-jsx';
/** The map of component type to FieldName */
const COMPONENT_TYPES = {
    array: 'ArrayField',
    boolean: 'BooleanField',
    integer: 'NumberField',
    number: 'NumberField',
    object: 'ObjectField',
    string: 'StringField',
    null: 'NullField',
};
/** Computes and returns which `Field` implementation to return in order to render the field represented by the
 * `schema`. The `uiOptions` are used to alter what potential `Field` implementation is actually returned. If no
 * appropriate `Field` implementation can be found then a wrapper around `UnsupportedFieldTemplate` is used.
 *
 * @param schema - The schema from which to obtain the type
 * @param uiOptions - The UI Options that may affect the component decision
 * @param idSchema - The id that is passed to the `UnsupportedFieldTemplate`
 * @param registry - The registry from which fields and templates are obtained
 * @returns - The `Field` component that is used to render the actual field data
 */
function getFieldComponent(schema, uiOptions, idSchema, registry) {
    const field = uiOptions.field;
    const { fields, translateString } = registry;
    if (typeof field === 'function') {
        return field;
    }
    if (typeof field === 'string' && field in fields) {
        return fields[field];
    }
    const schemaType = getSchemaType(schema);
    const type = Array.isArray(schemaType) ? schemaType[0] : schemaType || '';
    const schemaId = schema.$id;
    let componentName = COMPONENT_TYPES[type];
    if (schemaId && schemaId in fields) {
        componentName = schemaId;
    }
    // If the type is not defined and the schema uses 'anyOf' or 'oneOf', don't
    // render a field and let the MultiSchemaField component handle the form display
    if (!componentName && (schema.anyOf || schema.oneOf)) {
        return () => null;
    }
    return componentName in fields
        ? fields[componentName]
        : () => {
            const UnsupportedFieldTemplate = getTemplate('UnsupportedFieldTemplate', registry, uiOptions);
            return (_jsx(UnsupportedFieldTemplate, { schema: schema, idSchema: idSchema, reason: translateString(TranslatableString.UnknownFieldType, [String(schema.type)]), registry: registry }));
        };
}
/** The `SchemaFieldRender` component is the work-horse of react-jsonschema-form, determining what kind of real field to
 * render based on the `schema`, `uiSchema` and all the other props. It also deals with rendering the `anyOf` and
 * `oneOf` fields.
 *
 * @param props - The `FieldProps` for this component
 */
function SchemaFieldRender(props) {
    var _a, _b, _c;
    const { schema: _schema, idSchema: _idSchema, uiSchema, formData, errorSchema, idPrefix, idSeparator, name, onChange, onKeyChange, onDropPropertyClick, required, registry, wasPropertyKeyModified = false, } = props;
    const { formContext, schemaUtils, globalUiOptions } = registry;
    const uiOptions = getUiOptions(uiSchema, globalUiOptions);
    const FieldTemplate = getTemplate('FieldTemplate', registry, uiOptions);
    const DescriptionFieldTemplate = getTemplate('DescriptionFieldTemplate', registry, uiOptions);
    const FieldHelpTemplate = getTemplate('FieldHelpTemplate', registry, uiOptions);
    const FieldErrorTemplate = getTemplate('FieldErrorTemplate', registry, uiOptions);
    const schema = schemaUtils.retrieveSchema(_schema, formData);
    const fieldId = _idSchema[ID_KEY];
    const idSchema = mergeObjects(schemaUtils.toIdSchema(schema, fieldId, formData, idPrefix, idSeparator), _idSchema);
    /** Intermediary `onChange` handler for field components that will inject the `id` of the current field into the
     * `onChange` chain if it is not already being provided from a deeper level in the hierarchy
     */
    const handleFieldComponentChange = useCallback((formData, newErrorSchema, id) => {
        const theId = id || fieldId;
        return onChange(formData, newErrorSchema, theId);
    }, [fieldId, onChange]);
    const FieldComponent = getFieldComponent(schema, uiOptions, idSchema, registry);
    const disabled = Boolean((_a = uiOptions.disabled) !== null && _a !== void 0 ? _a : props.disabled);
    const readonly = Boolean((_b = uiOptions.readonly) !== null && _b !== void 0 ? _b : (props.readonly || props.schema.readOnly || schema.readOnly));
    const uiSchemaHideError = uiOptions.hideError;
    // Set hideError to the value provided in the uiSchema, otherwise stick with the prop to propagate to children
    const hideError = uiSchemaHideError === undefined ? props.hideError : Boolean(uiSchemaHideError);
    const autofocus = Boolean((_c = uiOptions.autofocus) !== null && _c !== void 0 ? _c : props.autofocus);
    if (Object.keys(schema).length === 0) {
        return null;
    }
    const displayLabel = schemaUtils.getDisplayLabel(schema, uiSchema, globalUiOptions);
    const { __errors, ...fieldErrorSchema } = errorSchema || {};
    // See #439: uiSchema: Don't pass consumed class names or style to child components
    const fieldUiSchema = omit(uiSchema, ['ui:classNames', 'classNames', 'ui:style']);
    if (UI_OPTIONS_KEY in fieldUiSchema) {
        fieldUiSchema[UI_OPTIONS_KEY] = omit(fieldUiSchema[UI_OPTIONS_KEY], ['classNames', 'style']);
    }
    const field = (_jsx(FieldComponent, { ...props, onChange: handleFieldComponentChange, idSchema: idSchema, schema: schema, uiSchema: fieldUiSchema, disabled: disabled, readonly: readonly, hideError: hideError, autofocus: autofocus, errorSchema: fieldErrorSchema, formContext: formContext, rawErrors: __errors }));
    const id = idSchema[ID_KEY];
    // If this schema has a title defined, but the user has set a new key/label, retain their input.
    let label;
    if (wasPropertyKeyModified) {
        label = name;
    }
    else {
        label =
            ADDITIONAL_PROPERTY_FLAG in schema
                ? name
                : uiOptions.title || props.schema.title || schema.title || props.title || name;
    }
    const description = uiOptions.description || props.schema.description || schema.description || '';
    const richDescription = uiOptions.enableMarkdownInDescription ? (_jsx(Markdown, { options: { disableParsingRawHTML: true }, children: description })) : (description);
    const help = uiOptions.help;
    const hidden = uiOptions.widget === 'hidden';
    const classNames = ['form-group', 'field', `field-${getSchemaType(schema)}`];
    if (!hideError && __errors && __errors.length > 0) {
        classNames.push('field-error has-error has-danger');
    }
    if (uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema.classNames) {
        if (process.env.NODE_ENV !== 'production') {
            console.warn("'uiSchema.classNames' is deprecated and may be removed in a major release; Use 'ui:classNames' instead.");
        }
        classNames.push(uiSchema.classNames);
    }
    if (uiOptions.classNames) {
        classNames.push(uiOptions.classNames);
    }
    const helpComponent = (_jsx(FieldHelpTemplate, { help: help, idSchema: idSchema, schema: schema, uiSchema: uiSchema, hasErrors: !hideError && __errors && __errors.length > 0, registry: registry }));
    /*
     * AnyOf/OneOf errors handled by child schema
     * unless it can be rendered as select control
     */
    const errorsComponent = hideError || ((schema.anyOf || schema.oneOf) && !schemaUtils.isSelect(schema)) ? undefined : (_jsx(FieldErrorTemplate, { errors: __errors, errorSchema: errorSchema, idSchema: idSchema, schema: schema, uiSchema: uiSchema, registry: registry }));
    const fieldProps = {
        description: (_jsx(DescriptionFieldTemplate, { id: descriptionId(id), description: richDescription, schema: schema, uiSchema: uiSchema, registry: registry })),
        rawDescription: description,
        help: helpComponent,
        rawHelp: typeof help === 'string' ? help : undefined,
        errors: errorsComponent,
        rawErrors: hideError ? undefined : __errors,
        id,
        label,
        hidden,
        onChange,
        onKeyChange,
        onDropPropertyClick,
        required,
        disabled,
        readonly,
        hideError,
        displayLabel,
        classNames: classNames.join(' ').trim(),
        style: uiOptions.style,
        formContext,
        formData,
        schema,
        uiSchema,
        registry,
    };
    const _AnyOfField = registry.fields.AnyOfField;
    const _OneOfField = registry.fields.OneOfField;
    const isReplacingAnyOrOneOf = (uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema['ui:field']) && (uiSchema === null || uiSchema === void 0 ? void 0 : uiSchema['ui:fieldReplacesAnyOrOneOf']) === true;
    return (_jsx(FieldTemplate, { ...fieldProps, children: _jsxs(_Fragment, { children: [field, schema.anyOf && !isReplacingAnyOrOneOf && !schemaUtils.isSelect(schema) && (_jsx(_AnyOfField, { name: name, disabled: disabled, readonly: readonly, hideError: hideError, errorSchema: errorSchema, formData: formData, formContext: formContext, idPrefix: idPrefix, idSchema: idSchema, idSeparator: idSeparator, onBlur: props.onBlur, onChange: props.onChange, onFocus: props.onFocus, options: schema.anyOf.map((_schema) => schemaUtils.retrieveSchema(isObject(_schema) ? _schema : {}, formData)), registry: registry, required: required, schema: schema, uiSchema: uiSchema })), schema.oneOf && !isReplacingAnyOrOneOf && !schemaUtils.isSelect(schema) && (_jsx(_OneOfField, { name: name, disabled: disabled, readonly: readonly, hideError: hideError, errorSchema: errorSchema, formData: formData, formContext: formContext, idPrefix: idPrefix, idSchema: idSchema, idSeparator: idSeparator, onBlur: props.onBlur, onChange: props.onChange, onFocus: props.onFocus, options: schema.oneOf.map((_schema) => schemaUtils.retrieveSchema(isObject(_schema) ? _schema : {}, formData)), registry: registry, required: required, schema: schema, uiSchema: uiSchema }))] }) }));
}
/** The `SchemaField` component determines whether it is necessary to rerender the component based on any props changes
 * and if so, calls the `SchemaFieldRender` component with the props.
 */
class SchemaField extends Component {
    shouldComponentUpdate(nextProps) {
        return !deepEquals(this.props, nextProps);
    }
    render() {
        return _jsx(SchemaFieldRender, { ...this.props });
    }
}
export default SchemaField;
//# sourceMappingURL=SchemaField.js.map