@rjsf/utils
Version:
Utility functions for @rjsf/core
516 lines • 32.3 kB
JavaScript
import get from 'lodash-es/get.js';
import isEmpty from 'lodash-es/isEmpty.js';
import { ALL_OF_KEY, ANY_OF_KEY, CONST_KEY, DEFAULT_KEY, DEPENDENCIES_KEY, ONE_OF_KEY, PROPERTIES_KEY, REF_KEY, } from '../constants.js';
import findSchemaDefinition from '../findSchemaDefinition.js';
import getClosestMatchingOption from './getClosestMatchingOption.js';
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema.js';
import getSchemaType from '../getSchemaType.js';
import isObject from '../isObject.js';
import isFixedItems from '../isFixedItems.js';
import mergeDefaultsWithFormData from '../mergeDefaultsWithFormData.js';
import mergeObjects from '../mergeObjects.js';
import mergeSchemas from '../mergeSchemas.js';
import isMultiSelect from './isMultiSelect.js';
import isSelect from './isSelect.js';
import retrieveSchema, { resolveDependencies } from './retrieveSchema.js';
import isConstant from '../isConstant.js';
import constIsAjvDataReference from '../constIsAjvDataReference.js';
import optionsList from '../optionsList.js';
import deepEquals from '../deepEquals.js';
const PRIMITIVE_TYPES = ['string', 'number', 'integer', 'boolean', 'null'];
/** Enum that indicates how `schema.additionalItems` should be handled by the `getInnerSchemaForArrayItem()` function.
*/
export var AdditionalItemsHandling;
(function (AdditionalItemsHandling) {
AdditionalItemsHandling[AdditionalItemsHandling["Ignore"] = 0] = "Ignore";
AdditionalItemsHandling[AdditionalItemsHandling["Invert"] = 1] = "Invert";
AdditionalItemsHandling[AdditionalItemsHandling["Fallback"] = 2] = "Fallback";
})(AdditionalItemsHandling || (AdditionalItemsHandling = {}));
/** Given a `schema` will return an inner schema that for an array item. This is computed differently based on the
* `additionalItems` enum and the value of `idx`. There are four possible returns:
* 1. If `idx` is >= 0, then if `schema.items` is an array the `idx`th element of the array is returned if it is a valid
* index and not a boolean, otherwise it falls through to 3.
* 2. If `schema.items` is not an array AND truthy and not a boolean, then `schema.items` is returned since it actually
* is a schema, otherwise it falls through to 3.
* 3. If `additionalItems` is not `AdditionalItemsHandling.Ignore` and `schema.additionalItems` is an object, then
* `schema.additionalItems` is returned since it actually is a schema, otherwise it falls through to 4.
* 4. {} is returned representing an empty schema
*
* @param schema - The schema from which to get the particular item
* @param [additionalItems=AdditionalItemsHandling.Ignore] - How do we want to handle additional items?
* @param [idx=-1] - Index, if non-negative, will be used to return the idx-th element in a `schema.items` array
* @returns - The best fit schema object from the `schema` given the `additionalItems` and `idx` modifiers
*/
export function getInnerSchemaForArrayItem(schema, additionalItems = AdditionalItemsHandling.Ignore, idx = -1) {
if (idx >= 0) {
if (Array.isArray(schema.items) && idx < schema.items.length) {
const item = schema.items[idx];
if (typeof item !== 'boolean') {
return item;
}
}
}
else if (schema.items && !Array.isArray(schema.items) && typeof schema.items !== 'boolean') {
return schema.items;
}
if (additionalItems !== AdditionalItemsHandling.Ignore && isObject(schema.additionalItems)) {
return schema.additionalItems;
}
return {};
}
/** Either add `computedDefault` at `key` into `obj` or not add it based on its value, the value of
* `includeUndefinedValues`, the value of `emptyObjectFields` and if its parent field is required. Generally undefined
* `computedDefault` values are added only when `includeUndefinedValues` is either true/"excludeObjectChildren". If `
* includeUndefinedValues` is false and `emptyObjectFields` is not "skipDefaults", then non-undefined and non-empty-object
* values will be added based on certain conditions.
*
* @param obj - The object into which the computed default may be added
* @param key - The key into the object at which the computed default may be added
* @param computedDefault - The computed default value that maybe should be added to the obj
* @param includeUndefinedValues - Optional flag, if true, cause undefined values to be added as defaults.
* If "excludeObjectChildren", cause undefined values for this object and pass `includeUndefinedValues` as
* false when computing defaults for any nested object properties. If "allowEmptyObject", prevents undefined
* values in this object while allow the object itself to be empty and passing `includeUndefinedValues` as
* false when computing defaults for any nested object properties.
* @param isParentRequired - The optional boolean that indicates whether the parent field is required
* @param requiredFields - The list of fields that are required
* @param experimental_defaultFormStateBehavior - Optional configuration object, if provided, allows users to override
* default form state behavior
* @param isConst - Optional flag, if true, indicates that the schema has a const property defined, thus we should always return the computedDefault since it's coming from the const.
*/
function maybeAddDefaultToObject(obj, key, computedDefault, includeUndefinedValues, isParentRequired, requiredFields = [], experimental_defaultFormStateBehavior = {}, isConst = false) {
const { emptyObjectFields = 'populateAllDefaults' } = experimental_defaultFormStateBehavior;
if (includeUndefinedValues || isConst) {
// If includeUndefinedValues
// Or if the schema has a const property defined, then we should always return the computedDefault since it's coming from the const.
obj[key] = computedDefault;
}
else if (emptyObjectFields !== 'skipDefaults') {
// If isParentRequired is undefined, then we are at the root level of the schema so defer to the requiredness of
// the field key itself in the `requiredField` list
const isSelfOrParentRequired = isParentRequired === undefined ? requiredFields.includes(key) : isParentRequired;
if (isObject(computedDefault)) {
// If emptyObjectFields 'skipEmptyDefaults' store computedDefault if it's a non-empty object(e.g. not {})
if (emptyObjectFields === 'skipEmptyDefaults') {
if (!isEmpty(computedDefault)) {
obj[key] = computedDefault;
}
} // Else store computedDefault if it's a non-empty object(e.g. not {}) and satisfies certain conditions
// Condition 1: If computedDefault is not empty or if the key is a required field
// Condition 2: If the parent object is required or emptyObjectFields is not 'populateRequiredDefaults'
else if ((!isEmpty(computedDefault) || requiredFields.includes(key)) &&
(isSelfOrParentRequired || emptyObjectFields !== 'populateRequiredDefaults')) {
obj[key] = computedDefault;
}
}
else if (
// Store computedDefault if it's a defined primitive (e.g., true) and satisfies certain conditions
// Condition 1: computedDefault is not undefined
// Condition 2: If emptyObjectFields is 'populateAllDefaults' or 'skipEmptyDefaults)
// Or if isSelfOrParentRequired is 'true' and the key is a required field
computedDefault !== undefined &&
(emptyObjectFields === 'populateAllDefaults' ||
emptyObjectFields === 'skipEmptyDefaults' ||
(isSelfOrParentRequired && requiredFields.includes(key)))) {
obj[key] = computedDefault;
}
}
}
/** Computes the defaults for the current `schema` given the `rawFormData` and `parentDefaults` if any. This drills into
* each level of the schema, recursively, to fill out every level of defaults provided by the schema.
*
* @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
* @param rawSchema - The schema for which the default state is desired
* @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function
* @returns - The resulting `formData` with all the defaults provided
*/
export function computeDefaults(validator, rawSchema, computeDefaultsProps = {}) {
const { parentDefaults, rawFormData, rootSchema = {}, includeUndefinedValues = false, _recurseList = [], experimental_defaultFormStateBehavior = undefined, experimental_customMergeAllOf = undefined, required, shouldMergeDefaultsIntoFormData = false, } = computeDefaultsProps;
let formData = (isObject(rawFormData) ? rawFormData : {});
const schema = isObject(rawSchema) ? rawSchema : {};
// Compute the defaults recursively: give highest priority to deepest nodes.
let defaults = parentDefaults;
// If we get a new schema, then we need to recompute defaults again for the new schema found.
let schemaToCompute = null;
let experimental_dfsb_to_compute = experimental_defaultFormStateBehavior;
let updatedRecurseList = _recurseList;
if (schema[CONST_KEY] !== undefined &&
(experimental_defaultFormStateBehavior === null || experimental_defaultFormStateBehavior === void 0 ? void 0 : experimental_defaultFormStateBehavior.constAsDefaults) !== 'never' &&
!constIsAjvDataReference(schema)) {
defaults = schema[CONST_KEY];
}
else if (isObject(defaults) && isObject(schema.default)) {
// For object defaults, only override parent defaults that are defined in
// schema.default.
defaults = mergeObjects(defaults, schema.default);
}
else if (DEFAULT_KEY in schema && !schema[ANY_OF_KEY] && !schema[ONE_OF_KEY]) {
// If the schema has a default value, then we should use it as the default.
// And if the schema does not have anyOf or oneOf, this is done because we need to merge the defaults with the formData.
defaults = schema.default;
}
else if (REF_KEY in schema) {
const refName = schema[REF_KEY];
// Use referenced schema defaults for this node.
if (!_recurseList.includes(refName)) {
updatedRecurseList = _recurseList.concat(refName);
schemaToCompute = findSchemaDefinition(refName, rootSchema);
}
// If the referenced schema exists and parentDefaults is not set
// Then set the defaults from the current schema for the referenced schema
if (schemaToCompute && !defaults) {
defaults = schema.default;
}
// If shouldMergeDefaultsIntoFormData is true
// And the schemaToCompute is set and the rawFormData is not an object
// Then set the formData to the rawFormData
if (shouldMergeDefaultsIntoFormData && schemaToCompute && !isObject(rawFormData)) {
formData = rawFormData;
}
}
else if (DEPENDENCIES_KEY in schema) {
// Get the default if set from properties to ensure the dependencies conditions are resolved based on it
const defaultFormData = {
...getDefaultBasedOnSchemaType(validator, schema, computeDefaultsProps, defaults),
...formData,
};
const resolvedSchema = resolveDependencies(validator, schema, rootSchema, false, [], defaultFormData, experimental_customMergeAllOf);
schemaToCompute = resolvedSchema[0]; // pick the first element from resolve dependencies
}
else if (isFixedItems(schema)) {
defaults = schema.items.map((itemSchema, idx) => computeDefaults(validator, itemSchema, {
rootSchema,
includeUndefinedValues,
_recurseList,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
parentDefaults: Array.isArray(parentDefaults) ? parentDefaults[idx] : undefined,
rawFormData: formData,
required,
shouldMergeDefaultsIntoFormData,
}));
}
else if (ONE_OF_KEY in schema) {
const { oneOf, ...remaining } = schema;
if (oneOf.length === 0) {
return undefined;
}
const discriminator = getDiscriminatorFieldFromSchema(schema);
const { type = 'null' } = remaining;
if (!Array.isArray(type) &&
PRIMITIVE_TYPES.includes(type) &&
(experimental_dfsb_to_compute === null || experimental_dfsb_to_compute === void 0 ? void 0 : experimental_dfsb_to_compute.constAsDefaults) === 'skipOneOf') {
// If we are in a oneOf of a primitive type, then we want to pass constAsDefaults as 'never' for the recursion
experimental_dfsb_to_compute = {
...experimental_dfsb_to_compute,
constAsDefaults: 'never',
};
}
schemaToCompute = oneOf[getClosestMatchingOption(validator, rootSchema, rawFormData !== null && rawFormData !== void 0 ? rawFormData : schema.default, oneOf, 0, discriminator, experimental_customMergeAllOf)];
schemaToCompute = mergeSchemas(remaining, schemaToCompute);
}
else if (ANY_OF_KEY in schema) {
const { anyOf, ...remaining } = schema;
if (anyOf.length === 0) {
return undefined;
}
const discriminator = getDiscriminatorFieldFromSchema(schema);
schemaToCompute = anyOf[getClosestMatchingOption(validator, rootSchema, rawFormData !== null && rawFormData !== void 0 ? rawFormData : schema.default, anyOf, 0, discriminator, experimental_customMergeAllOf)];
schemaToCompute = mergeSchemas(remaining, schemaToCompute);
}
if (schemaToCompute) {
return computeDefaults(validator, schemaToCompute, {
rootSchema,
includeUndefinedValues,
_recurseList: updatedRecurseList,
experimental_defaultFormStateBehavior: experimental_dfsb_to_compute,
experimental_customMergeAllOf,
parentDefaults: defaults,
rawFormData: (rawFormData !== null && rawFormData !== void 0 ? rawFormData : formData),
required,
shouldMergeDefaultsIntoFormData,
});
}
// No defaults defined for this node, fallback to generic typed ones.
if (defaults === undefined) {
defaults = schema.default;
}
const defaultBasedOnSchemaType = getDefaultBasedOnSchemaType(validator, schema, computeDefaultsProps, defaults);
let defaultsWithFormData = defaultBasedOnSchemaType !== null && defaultBasedOnSchemaType !== void 0 ? defaultBasedOnSchemaType : defaults;
// if shouldMergeDefaultsIntoFormData is true, then merge the defaults into the formData.
if (shouldMergeDefaultsIntoFormData) {
const { arrayMinItems = {} } = experimental_defaultFormStateBehavior || {};
const { mergeExtraDefaults } = arrayMinItems;
const matchingFormData = ensureFormDataMatchingSchema(validator, schema, rootSchema, rawFormData, experimental_defaultFormStateBehavior, experimental_customMergeAllOf);
if (!isObject(rawFormData) || ALL_OF_KEY in schema) {
// If the formData is not an object which means it's a primitive field, then we need to merge the defaults into the formData.
// Or if the schema has allOf, we need to merge the defaults into the formData because we don't compute the defaults for allOf.
defaultsWithFormData = mergeDefaultsWithFormData(defaultsWithFormData, matchingFormData, mergeExtraDefaults, true);
}
}
return defaultsWithFormData;
}
/**
* Ensure that the formData matches the given schema. If it's not matching in the case of a selectField, we change it to match the schema.
*
* @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
* @param schema - The schema for which the formData state is desired
* @param rootSchema - The root schema, used to primarily to look up `$ref`s
* @param formData - The current formData
* @param [experimental_defaultFormStateBehavior] - Optional configuration object, if provided, allows users to override default form state behavior
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
* @returns - valid formData that matches schema
*/
export function ensureFormDataMatchingSchema(validator, schema, rootSchema, formData, experimental_defaultFormStateBehavior, experimental_customMergeAllOf) {
const isSelectField = !isConstant(schema) && isSelect(validator, schema, rootSchema, experimental_customMergeAllOf);
let validFormData = formData;
if (isSelectField) {
const getOptionsList = optionsList(schema);
const isValid = getOptionsList === null || getOptionsList === void 0 ? void 0 : getOptionsList.some((option) => deepEquals(option.value, formData));
validFormData = isValid ? formData : undefined;
}
// Override the formData with the const if the constAsDefaults is set to always
const constTakesPrecedence = schema[CONST_KEY] && (experimental_defaultFormStateBehavior === null || experimental_defaultFormStateBehavior === void 0 ? void 0 : experimental_defaultFormStateBehavior.constAsDefaults) === 'always';
if (constTakesPrecedence) {
validFormData = schema.const;
}
return validFormData;
}
/** Computes the default value for objects.
*
* @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
* @param rawSchema - The schema for which the default state is desired
* @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function
* @param defaults - Optional props for this function
* @returns - The default value based on the schema type if they are defined for object or array schemas.
*/
export function getObjectDefaults(validator, rawSchema, { rawFormData, rootSchema = {}, includeUndefinedValues = false, _recurseList = [], experimental_defaultFormStateBehavior = undefined, experimental_customMergeAllOf = undefined, required, shouldMergeDefaultsIntoFormData, } = {}, defaults) {
{
const formData = (isObject(rawFormData) ? rawFormData : {});
const schema = rawSchema;
// This is a custom addition that fixes this issue:
// https://github.com/rjsf-team/react-jsonschema-form/issues/3832
const retrievedSchema = (experimental_defaultFormStateBehavior === null || experimental_defaultFormStateBehavior === void 0 ? void 0 : experimental_defaultFormStateBehavior.allOf) === 'populateDefaults' && ALL_OF_KEY in schema
? retrieveSchema(validator, schema, rootSchema, formData, experimental_customMergeAllOf)
: schema;
const parentConst = retrievedSchema[CONST_KEY];
const objectDefaults = Object.keys(retrievedSchema.properties || {}).reduce((acc, key) => {
var _a;
const propertySchema = get(retrievedSchema, [PROPERTIES_KEY, key]);
// Check if the parent schema has a const property defined AND we are supporting const as defaults, then we
// should always return the computedDefault since it's coming from the const.
const hasParentConst = isObject(parentConst) && parentConst[key] !== undefined;
const hasConst = ((isObject(propertySchema) && CONST_KEY in propertySchema) || hasParentConst) &&
(experimental_defaultFormStateBehavior === null || experimental_defaultFormStateBehavior === void 0 ? void 0 : experimental_defaultFormStateBehavior.constAsDefaults) !== 'never' &&
!constIsAjvDataReference(propertySchema);
// Compute the defaults for this node, with the parent defaults we might
// have from a previous run: defaults[key].
const computedDefault = computeDefaults(validator, propertySchema, {
rootSchema,
_recurseList,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
includeUndefinedValues: includeUndefinedValues === true,
parentDefaults: get(defaults, [key]),
rawFormData: get(formData, [key]),
required: (_a = retrievedSchema.required) === null || _a === void 0 ? void 0 : _a.includes(key),
shouldMergeDefaultsIntoFormData,
});
maybeAddDefaultToObject(acc, key, computedDefault, includeUndefinedValues, required, retrievedSchema.required, experimental_defaultFormStateBehavior, hasConst);
return acc;
}, {});
if (retrievedSchema.additionalProperties) {
// as per spec additionalProperties may be either schema or boolean
const additionalPropertiesSchema = isObject(retrievedSchema.additionalProperties)
? retrievedSchema.additionalProperties
: {};
const keys = new Set();
if (isObject(defaults)) {
Object.keys(defaults)
.filter((key) => !retrievedSchema.properties || !retrievedSchema.properties[key])
.forEach((key) => keys.add(key));
}
const formDataRequired = [];
Object.keys(formData)
.filter((key) => !retrievedSchema.properties || !retrievedSchema.properties[key])
.forEach((key) => {
keys.add(key);
formDataRequired.push(key);
});
keys.forEach((key) => {
var _a;
const computedDefault = computeDefaults(validator, additionalPropertiesSchema, {
rootSchema,
_recurseList,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
includeUndefinedValues: includeUndefinedValues === true,
parentDefaults: get(defaults, [key]),
rawFormData: get(formData, [key]),
required: (_a = retrievedSchema.required) === null || _a === void 0 ? void 0 : _a.includes(key),
shouldMergeDefaultsIntoFormData,
});
// Since these are additional properties we don't need to add the `experimental_defaultFormStateBehavior` prop
maybeAddDefaultToObject(objectDefaults, key, computedDefault, includeUndefinedValues, required, formDataRequired);
});
}
return objectDefaults;
}
}
/** Computes the default value for arrays.
*
* @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
* @param rawSchema - The schema for which the default state is desired
* @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function
* @param defaults - Optional props for this function
* @returns - The default value based on the schema type if they are defined for object or array schemas.
*/
export function getArrayDefaults(validator, rawSchema, { rawFormData, rootSchema = {}, _recurseList = [], experimental_defaultFormStateBehavior = undefined, experimental_customMergeAllOf = undefined, required, shouldMergeDefaultsIntoFormData, } = {}, defaults) {
var _a, _b;
const schema = rawSchema;
const arrayMinItemsStateBehavior = (_a = experimental_defaultFormStateBehavior === null || experimental_defaultFormStateBehavior === void 0 ? void 0 : experimental_defaultFormStateBehavior.arrayMinItems) !== null && _a !== void 0 ? _a : {};
const { populate: arrayMinItemsPopulate, mergeExtraDefaults: arrayMergeExtraDefaults } = arrayMinItemsStateBehavior;
const neverPopulate = arrayMinItemsPopulate === 'never';
const ignoreMinItemsFlagSet = arrayMinItemsPopulate === 'requiredOnly';
const isPopulateAll = arrayMinItemsPopulate === 'all' || (!neverPopulate && !ignoreMinItemsFlagSet);
const computeSkipPopulate = (_b = arrayMinItemsStateBehavior === null || arrayMinItemsStateBehavior === void 0 ? void 0 : arrayMinItemsStateBehavior.computeSkipPopulate) !== null && _b !== void 0 ? _b : (() => false);
const isSkipEmptyDefaults = (experimental_defaultFormStateBehavior === null || experimental_defaultFormStateBehavior === void 0 ? void 0 : experimental_defaultFormStateBehavior.emptyObjectFields) === 'skipEmptyDefaults';
const emptyDefault = isSkipEmptyDefaults ? undefined : [];
// Inject defaults into existing array defaults
if (Array.isArray(defaults)) {
defaults = defaults.map((item, idx) => {
const schemaItem = getInnerSchemaForArrayItem(schema, AdditionalItemsHandling.Fallback, idx);
return computeDefaults(validator, schemaItem, {
rootSchema,
_recurseList,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
parentDefaults: item,
required,
shouldMergeDefaultsIntoFormData,
});
});
}
// Deeply inject defaults into already existing form data
if (Array.isArray(rawFormData)) {
const schemaItem = getInnerSchemaForArrayItem(schema);
if (neverPopulate) {
defaults = rawFormData;
}
else {
const itemDefaults = rawFormData.map((item, idx) => {
return computeDefaults(validator, schemaItem, {
rootSchema,
_recurseList,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
rawFormData: item,
parentDefaults: get(defaults, [idx]),
required,
shouldMergeDefaultsIntoFormData,
});
});
// If the populate 'requiredOnly' flag is set then we only merge and include extra defaults if they are required.
// Or if populate 'all' is set we merge and include extra defaults.
const mergeExtraDefaults = ((ignoreMinItemsFlagSet && required) || isPopulateAll) && arrayMergeExtraDefaults;
defaults = mergeDefaultsWithFormData(defaults, itemDefaults, mergeExtraDefaults);
}
}
// Check if the schema has a const property defined AND we are supporting const as defaults, then we should always
// return the computedDefault since it's coming from the const.
const hasConst = isObject(schema) && CONST_KEY in schema && (experimental_defaultFormStateBehavior === null || experimental_defaultFormStateBehavior === void 0 ? void 0 : experimental_defaultFormStateBehavior.constAsDefaults) !== 'never';
if (hasConst === false) {
if (neverPopulate) {
return defaults !== null && defaults !== void 0 ? defaults : emptyDefault;
}
if (ignoreMinItemsFlagSet && !required) {
// If no form data exists or defaults are set leave the field empty/non-existent, otherwise
// return form data/defaults
return defaults ? defaults : undefined;
}
}
const defaultsLength = Array.isArray(defaults) ? defaults.length : 0;
if (!schema.minItems ||
isMultiSelect(validator, schema, rootSchema, experimental_customMergeAllOf) ||
computeSkipPopulate(validator, schema, rootSchema) ||
schema.minItems <= defaultsLength) {
return defaults ? defaults : emptyDefault;
}
const defaultEntries = (defaults || []);
const fillerSchema = getInnerSchemaForArrayItem(schema, AdditionalItemsHandling.Invert);
const fillerDefault = fillerSchema.default;
// Calculate filler entries for remaining items (minItems - existing raw data/defaults)
const fillerEntries = new Array(schema.minItems - defaultsLength).fill(computeDefaults(validator, fillerSchema, {
parentDefaults: fillerDefault,
rootSchema,
_recurseList,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
required,
shouldMergeDefaultsIntoFormData,
}));
// then fill up the rest with either the item default or empty, up to minItems
return defaultEntries.concat(fillerEntries);
}
/** Computes the default value based on the schema type.
*
* @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
* @param rawSchema - The schema for which the default state is desired
* @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function
* @param defaults - Optional props for this function
* @returns - The default value based on the schema type if they are defined for object or array schemas.
*/
export function getDefaultBasedOnSchemaType(validator, rawSchema, computeDefaultsProps = {}, defaults) {
switch (getSchemaType(rawSchema)) {
// We need to recurse for object schema inner default values.
case 'object': {
return getObjectDefaults(validator, rawSchema, computeDefaultsProps, defaults);
}
case 'array': {
return getArrayDefaults(validator, rawSchema, computeDefaultsProps, defaults);
}
}
}
/** Returns the superset of `formData` that includes the given set updated to include any missing fields that have
* computed to have defaults provided in the `schema`.
*
* @param validator - An implementation of the `ValidatorType` interface that will be used when necessary
* @param theSchema - The schema for which the default state is desired
* @param [formData] - The current formData, if any, onto which to provide any missing defaults
* @param [rootSchema] - The root schema, used to primarily to look up `$ref`s
* @param [includeUndefinedValues=false] - Optional flag, if true, cause undefined values to be added as defaults.
* If "excludeObjectChildren", cause undefined values for this object and pass `includeUndefinedValues` as
* false when computing defaults for any nested object properties.
* @param [experimental_defaultFormStateBehavior] Optional configuration object, if provided, allows users to override default form state behavior
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
* @returns - The resulting `formData` with all the defaults provided
*/
export default function getDefaultFormState(validator, theSchema, formData, rootSchema, includeUndefinedValues = false, experimental_defaultFormStateBehavior, experimental_customMergeAllOf) {
if (!isObject(theSchema)) {
throw new Error('Invalid schema: ' + theSchema);
}
const schema = retrieveSchema(validator, theSchema, rootSchema, formData, experimental_customMergeAllOf);
// Get the computed defaults with 'shouldMergeDefaultsIntoFormData' set to true to merge defaults into formData.
// This is done when for example the value from formData does not exist in the schema 'enum' property, in such
// cases we take the value from the defaults because the value from the formData is not valid.
const defaults = computeDefaults(validator, schema, {
rootSchema,
includeUndefinedValues,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
rawFormData: formData,
shouldMergeDefaultsIntoFormData: true,
});
// If the formData is an object or an array, add additional properties from formData and override formData with
// defaults since the defaults are already merged with formData.
if (isObject(formData) || Array.isArray(formData)) {
const { mergeDefaultsIntoFormData } = experimental_defaultFormStateBehavior || {};
const defaultSupercedesUndefined = mergeDefaultsIntoFormData === 'useDefaultIfFormDataUndefined';
const result = mergeDefaultsWithFormData(defaults, formData, true, // set to true to add any additional default array entries.
defaultSupercedesUndefined, true // set to true to override formData with defaults if they exist.
);
return result;
}
return defaults;
}
//# sourceMappingURL=getDefaultFormState.js.map