@rjsf/utils
Advanced tools
Comparing version
@@ -1,2 +0,2 @@ | ||
import { RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** Checks the schema to see if it is allowing additional items, by verifying that `schema.additionalItems` is an | ||
@@ -3,0 +3,0 @@ * object. The user is warned in the console if `schema.additionalItems` has the value `true`. |
@@ -1,2 +0,2 @@ | ||
import isObject from './isObject'; | ||
import isObject from './isObject.js'; | ||
/** Checks the schema to see if it is allowing additional items, by verifying that `schema.additionalItems` is an | ||
@@ -3,0 +3,0 @@ * object. The user is warned in the console if `schema.additionalItems` has the value `true`. |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, UiSchema } from './types'; | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, UiSchema } from './types.js'; | ||
/** Checks whether the field described by `schema`, having the `uiSchema` and `formData` supports expanding. The UI for | ||
@@ -3,0 +3,0 @@ * the field can expand if it has additional properties, is not forced as non-expandable by the `uiSchema` and the |
@@ -1,2 +0,2 @@ | ||
import getUiOptions from './getUiOptions'; | ||
import getUiOptions from './getUiOptions.js'; | ||
/** Checks whether the field described by `schema`, having the `uiSchema` and `formData` supports expanding. The UI for | ||
@@ -12,3 +12,3 @@ * the field can expand if it has additional properties, is not forced as non-expandable by the `uiSchema` and the | ||
export default function canExpand(schema, uiSchema = {}, formData) { | ||
if (!schema.additionalProperties) { | ||
if (!(schema.additionalProperties || schema.patternProperties)) { | ||
return false; | ||
@@ -15,0 +15,0 @@ } |
@@ -22,10 +22,18 @@ /** Below are the list of all the keys into various elements of a RJSFSchema or UiSchema that are used by the various | ||
export declare const ONE_OF_KEY = "oneOf"; | ||
export declare const PATTERN_PROPERTIES_KEY = "patternProperties"; | ||
export declare const PROPERTIES_KEY = "properties"; | ||
export declare const READONLY_KEY = "readonly"; | ||
export declare const REQUIRED_KEY = "required"; | ||
export declare const SUBMIT_BTN_OPTIONS_KEY = "submitButtonOptions"; | ||
export declare const REF_KEY = "$ref"; | ||
/** | ||
* @deprecated Replace with correctly spelled constant `RJSF_ADDITIONAL_PROPERTIES_FLAG` | ||
/** The path of the discriminator value returned by the schema endpoint. | ||
* The discriminator is the value in a `oneOf` that determines which option is selected. | ||
*/ | ||
export declare const RJSF_ADDITONAL_PROPERTIES_FLAG = "__rjsf_additionalProperties"; | ||
export declare const DISCRIMINATOR_PATH: string[]; | ||
/** The name of the `formContext` attribute in the React JSON Schema Form Registry | ||
*/ | ||
export declare const FORM_CONTEXT_NAME = "formContext"; | ||
/** The name of the `layoutGridLookupMap` attribute in the form context | ||
*/ | ||
export declare const LOOKUP_MAP_NAME = "layoutGridLookupMap"; | ||
export declare const RJSF_ADDITIONAL_PROPERTIES_FLAG = "__rjsf_additionalProperties"; | ||
@@ -32,0 +40,0 @@ export declare const ROOT_SCHEMA_PREFIX = "__rjsf_rootSchema"; |
@@ -22,10 +22,18 @@ /** Below are the list of all the keys into various elements of a RJSFSchema or UiSchema that are used by the various | ||
export const ONE_OF_KEY = 'oneOf'; | ||
export const PATTERN_PROPERTIES_KEY = 'patternProperties'; | ||
export const PROPERTIES_KEY = 'properties'; | ||
export const READONLY_KEY = 'readonly'; | ||
export const REQUIRED_KEY = 'required'; | ||
export const SUBMIT_BTN_OPTIONS_KEY = 'submitButtonOptions'; | ||
export const REF_KEY = '$ref'; | ||
/** | ||
* @deprecated Replace with correctly spelled constant `RJSF_ADDITIONAL_PROPERTIES_FLAG` | ||
/** The path of the discriminator value returned by the schema endpoint. | ||
* The discriminator is the value in a `oneOf` that determines which option is selected. | ||
*/ | ||
export const RJSF_ADDITONAL_PROPERTIES_FLAG = '__rjsf_additionalProperties'; | ||
export const DISCRIMINATOR_PATH = ['discriminator', 'propertyName']; | ||
/** The name of the `formContext` attribute in the React JSON Schema Form Registry | ||
*/ | ||
export const FORM_CONTEXT_NAME = 'formContext'; | ||
/** The name of the `layoutGridLookupMap` attribute in the form context | ||
*/ | ||
export const LOOKUP_MAP_NAME = 'layoutGridLookupMap'; | ||
export const RJSF_ADDITIONAL_PROPERTIES_FLAG = '__rjsf_additionalProperties'; | ||
@@ -32,0 +40,0 @@ export const ROOT_SCHEMA_PREFIX = '__rjsf_rootSchema'; |
@@ -1,2 +0,2 @@ | ||
import { FormValidation } from './types'; | ||
import { FormValidation } from './types.js'; | ||
/** Given a `formData` object, recursively creates a `FormValidation` error handling structure around it | ||
@@ -3,0 +3,0 @@ * |
@@ -1,3 +0,3 @@ | ||
import isPlainObject from 'lodash/isPlainObject'; | ||
import { ERRORS_KEY } from './constants'; | ||
import isPlainObject from 'lodash-es/isPlainObject.js'; | ||
import { ERRORS_KEY } from './constants.js'; | ||
/** Given a `formData` object, recursively creates a `FormValidation` error handling structure around it | ||
@@ -4,0 +4,0 @@ * |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, RJSFSchema, SchemaUtilsType, StrictRJSFSchema, ValidatorType } from './types'; | ||
import { Experimental_CustomMergeAllOf, FormContextType, RJSFSchema, SchemaUtilsType, StrictRJSFSchema, ValidatorType } from './types.js'; | ||
/** Creates a `SchemaUtilsType` interface that is based around the given `validator` and `rootSchema` parameters. The | ||
@@ -8,4 +8,5 @@ * resulting interface implementation will forward the `validator` and `rootSchema` to all the wrapped APIs. | ||
* @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 - An implementation of a `SchemaUtilsType` interface | ||
*/ | ||
export default function createSchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rootSchema: S, experimental_defaultFormStateBehavior?: {}): SchemaUtilsType<T, S, F>; | ||
export default function createSchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rootSchema: S, experimental_defaultFormStateBehavior?: {}, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): SchemaUtilsType<T, S, F>; |
@@ -1,7 +0,8 @@ | ||
import deepEquals from './deepEquals'; | ||
import { getDefaultFormState, getDisplayLabel, getClosestMatchingOption, getFirstMatchingOption, getMatchingOption, isFilesArray, isMultiSelect, isSelect, mergeValidationData, retrieveSchema, sanitizeDataForNewSchema, toIdSchema, toPathSchema, } from './schema'; | ||
import deepEquals from './deepEquals.js'; | ||
import { findFieldInSchema, findSelectedOptionInXxxOf, getDefaultFormState, getDisplayLabel, getClosestMatchingOption, getFirstMatchingOption, getFromSchema, isFilesArray, isMultiSelect, isSelect, retrieveSchema, sanitizeDataForNewSchema, toIdSchema, toPathSchema, } from './schema/index.js'; | ||
/** The `SchemaUtils` class provides a wrapper around the publicly exported APIs in the `utils/schema` directory such | ||
* that one does not have to explicitly pass the `validator`, `rootSchema`, or `experimental_defaultFormStateBehavior` to each method. | ||
* Since these generally do not change across a `Form`, this allows for providing a simplified set of APIs to the | ||
* `@rjsf/core` components and the various themes as well. This class implements the `SchemaUtilsType` interface. | ||
* that one does not have to explicitly pass the `validator`, `rootSchema`, `experimental_defaultFormStateBehavior` or | ||
* `experimental_customMergeAllOf` to each method. Since these generally do not change across a `Form`, this allows for | ||
* providing a simplified set of APIs to the `@rjsf/core` components and the various themes as well. This class | ||
* implements the `SchemaUtilsType` interface. | ||
*/ | ||
@@ -14,7 +15,9 @@ class SchemaUtils { | ||
* @param experimental_defaultFormStateBehavior - Configuration flags to allow users to override default form state behavior | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
*/ | ||
constructor(validator, rootSchema, experimental_defaultFormStateBehavior) { | ||
constructor(validator, rootSchema, experimental_defaultFormStateBehavior, experimental_customMergeAllOf) { | ||
this.rootSchema = rootSchema; | ||
this.validator = validator; | ||
this.experimental_defaultFormStateBehavior = experimental_defaultFormStateBehavior; | ||
this.experimental_customMergeAllOf = experimental_customMergeAllOf; | ||
} | ||
@@ -35,5 +38,6 @@ /** Returns the `ValidatorType` in the `SchemaUtilsType` | ||
* @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 - True if the `SchemaUtilsType` differs from the given `validator` or `rootSchema` | ||
*/ | ||
doesSchemaUtilsDiffer(validator, rootSchema, experimental_defaultFormStateBehavior = {}) { | ||
doesSchemaUtilsDiffer(validator, rootSchema, experimental_defaultFormStateBehavior = {}, experimental_customMergeAllOf) { | ||
if (!validator || !rootSchema) { | ||
@@ -44,4 +48,31 @@ return false; | ||
!deepEquals(this.rootSchema, rootSchema) || | ||
!deepEquals(this.experimental_defaultFormStateBehavior, experimental_defaultFormStateBehavior)); | ||
!deepEquals(this.experimental_defaultFormStateBehavior, experimental_defaultFormStateBehavior) || | ||
this.experimental_customMergeAllOf !== experimental_customMergeAllOf); | ||
} | ||
/** Finds the field specified by the `path` within the root or recursed `schema`. If there is no field for the specified | ||
* `path`, then the default `{ field: undefined, isRequired: undefined }` is returned. It determines whether a leaf | ||
* field is in the `required` list for its parent and if so, it is marked as required on return. | ||
* | ||
* @param schema - The current node within the JSON schema | ||
* @param path - The remaining keys in the path to the desired field | ||
* @param [formData] - The form data that is used to determine which oneOf option | ||
* @returns - An object that contains the field and its required state. If no field can be found then | ||
* `{ field: undefined, isRequired: undefined }` is returned. | ||
*/ | ||
findFieldInSchema(schema, path, formData) { | ||
return findFieldInSchema(this.validator, this.rootSchema, schema, path, formData, this.experimental_customMergeAllOf); | ||
} | ||
/** Finds the oneOf option inside the `schema['any/oneOf']` list which has the `properties[selectorField].default` that | ||
* matches the `formData[selectorField]` value. For the purposes of this function, `selectorField` is either | ||
* `schema.discriminator.propertyName` or `fallbackField`. | ||
* | ||
* @param schema - The schema element in which to search for the selected oneOf option | ||
* @param fallbackField - The field to use as a backup selector field if the schema does not have a required field | ||
* @param xxx - Either `oneOf` or `anyOf`, defines which value is being sought | ||
* @param [formData={}] - The form data that is used to determine which oneOf option | ||
* @returns - The anyOf/oneOf option that matches the selector field in the schema or undefined if nothing is selected | ||
*/ | ||
findSelectedOptionInXxxOf(schema, fallbackField, xxx, formData) { | ||
return findSelectedOptionInXxxOf(this.validator, this.rootSchema, schema, fallbackField, xxx, formData, this.experimental_customMergeAllOf); | ||
} | ||
/** Returns the superset of `formData` that includes the given set updated to include any missing fields that have | ||
@@ -58,3 +89,3 @@ * computed to have defaults provided in the `schema`. | ||
getDefaultFormState(schema, formData, includeUndefinedValues = false) { | ||
return getDefaultFormState(this.validator, schema, formData, this.rootSchema, includeUndefinedValues, this.experimental_defaultFormStateBehavior); | ||
return getDefaultFormState(this.validator, schema, formData, this.rootSchema, includeUndefinedValues, this.experimental_defaultFormStateBehavior, this.experimental_customMergeAllOf); | ||
} | ||
@@ -70,3 +101,3 @@ /** Determines whether the combination of `schema` and `uiSchema` properties indicates that the label for the `schema` | ||
getDisplayLabel(schema, uiSchema, globalOptions) { | ||
return getDisplayLabel(this.validator, schema, uiSchema, this.rootSchema, globalOptions); | ||
return getDisplayLabel(this.validator, schema, uiSchema, this.rootSchema, globalOptions, this.experimental_customMergeAllOf); | ||
} | ||
@@ -87,3 +118,3 @@ /** Determines which of the given `options` provided most closely matches the `formData`. | ||
getClosestMatchingOption(formData, options, selectedOption, discriminatorField) { | ||
return getClosestMatchingOption(this.validator, this.rootSchema, formData, options, selectedOption, discriminatorField); | ||
return getClosestMatchingOption(this.validator, this.rootSchema, formData, options, selectedOption, discriminatorField, this.experimental_customMergeAllOf); | ||
} | ||
@@ -102,14 +133,6 @@ /** Given the `formData` and list of `options`, attempts to find the index of the first option that matches the data. | ||
} | ||
/** Given the `formData` and list of `options`, attempts to find the index of the option that best matches the data. | ||
* Deprecated, use `getFirstMatchingOption()` instead. | ||
* | ||
* @param formData - The current formData, if any, onto which to provide any missing defaults | ||
* @param options - The list of options to find a matching options from | ||
* @param [discriminatorField] - The optional name of the field within the options object whose value is used to | ||
* determine which option is selected | ||
* @returns - The index of the matched option or 0 if none is available | ||
* @deprecated | ||
*/ | ||
getMatchingOption(formData, options, discriminatorField) { | ||
return getMatchingOption(this.validator, formData, options, this.rootSchema, discriminatorField); | ||
getFromSchema(schema, path, defaultValue) { | ||
return getFromSchema(this.validator, this.rootSchema, schema, path, | ||
// @ts-expect-error TS2769: No overload matches this call | ||
defaultValue, this.experimental_customMergeAllOf); | ||
} | ||
@@ -123,3 +146,3 @@ /** Checks to see if the `schema` and `uiSchema` combination represents an array of files | ||
isFilesArray(schema, uiSchema) { | ||
return isFilesArray(this.validator, schema, uiSchema, this.rootSchema); | ||
return isFilesArray(this.validator, schema, uiSchema, this.rootSchema, this.experimental_customMergeAllOf); | ||
} | ||
@@ -132,3 +155,3 @@ /** Checks to see if the `schema` combination represents a multi-select | ||
isMultiSelect(schema) { | ||
return isMultiSelect(this.validator, schema, this.rootSchema); | ||
return isMultiSelect(this.validator, schema, this.rootSchema, this.experimental_customMergeAllOf); | ||
} | ||
@@ -141,18 +164,4 @@ /** Checks to see if the `schema` combination represents a select | ||
isSelect(schema) { | ||
return isSelect(this.validator, schema, this.rootSchema); | ||
return isSelect(this.validator, schema, this.rootSchema, this.experimental_customMergeAllOf); | ||
} | ||
/** Merges the errors in `additionalErrorSchema` into the existing `validationData` by combining the hierarchies in | ||
* the two `ErrorSchema`s and then appending the error list from the `additionalErrorSchema` obtained by calling | ||
* `getValidator().toErrorList()` onto the `errors` in the `validationData`. If no `additionalErrorSchema` is passed, | ||
* then `validationData` is returned. | ||
* | ||
* @param validationData - The current `ValidationData` into which to merge the additional errors | ||
* @param [additionalErrorSchema] - The additional set of errors | ||
* @returns - The `validationData` with the additional errors from `additionalErrorSchema` merged into it, if provided. | ||
* @deprecated - Use the `validationDataMerge()` function exported from `@rjsf/utils` instead. This function will be | ||
* removed in the next major release. | ||
*/ | ||
mergeValidationData(validationData, additionalErrorSchema) { | ||
return mergeValidationData(this.validator, validationData, additionalErrorSchema); | ||
} | ||
/** Retrieves an expanded schema that has had all of its conditions, additional properties, references and | ||
@@ -167,3 +176,3 @@ * dependencies resolved and merged into the `schema` given a `rawFormData` that is used to do the potentially | ||
retrieveSchema(schema, rawFormData) { | ||
return retrieveSchema(this.validator, schema, this.rootSchema, rawFormData); | ||
return retrieveSchema(this.validator, schema, this.rootSchema, rawFormData, this.experimental_customMergeAllOf); | ||
} | ||
@@ -182,3 +191,3 @@ /** Sanitize the `data` associated with the `oldSchema` so it is considered appropriate for the `newSchema`. If the | ||
sanitizeDataForNewSchema(newSchema, oldSchema, data) { | ||
return sanitizeDataForNewSchema(this.validator, this.rootSchema, newSchema, oldSchema, data); | ||
return sanitizeDataForNewSchema(this.validator, this.rootSchema, newSchema, oldSchema, data, this.experimental_customMergeAllOf); | ||
} | ||
@@ -195,3 +204,3 @@ /** Generates an `IdSchema` object for the `schema`, recursively | ||
toIdSchema(schema, id, formData, idPrefix = 'root', idSeparator = '_') { | ||
return toIdSchema(this.validator, schema, id, this.rootSchema, formData, idPrefix, idSeparator); | ||
return toIdSchema(this.validator, schema, id, this.rootSchema, formData, idPrefix, idSeparator, this.experimental_customMergeAllOf); | ||
} | ||
@@ -206,3 +215,3 @@ /** Generates an `PathSchema` object for the `schema`, recursively | ||
toPathSchema(schema, name, formData) { | ||
return toPathSchema(this.validator, schema, name, this.rootSchema, formData); | ||
return toPathSchema(this.validator, schema, name, this.rootSchema, formData, this.experimental_customMergeAllOf); | ||
} | ||
@@ -216,7 +225,8 @@ } | ||
* @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 - An implementation of a `SchemaUtilsType` interface | ||
*/ | ||
export default function createSchemaUtils(validator, rootSchema, experimental_defaultFormStateBehavior = {}) { | ||
return new SchemaUtils(validator, rootSchema, experimental_defaultFormStateBehavior); | ||
export default function createSchemaUtils(validator, rootSchema, experimental_defaultFormStateBehavior = {}, experimental_customMergeAllOf) { | ||
return new SchemaUtils(validator, rootSchema, experimental_defaultFormStateBehavior, experimental_customMergeAllOf); | ||
} | ||
//# sourceMappingURL=createSchemaUtils.js.map |
@@ -1,2 +0,2 @@ | ||
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** Returns a list of options for a date range between `start` and `stop`. If the start date is greater than the end | ||
@@ -3,0 +3,0 @@ * date, then the date range is reversed. If `start` and `stop` are negative numbers (or zero), then they will be |
@@ -1,2 +0,2 @@ | ||
import pad from './pad'; | ||
import pad from './pad.js'; | ||
/** Returns a list of options for a date range between `start` and `stop`. If the start date is greater than the end | ||
@@ -3,0 +3,0 @@ * date, then the date range is reversed. If `start` and `stop` are negative numbers (or zero), then they will be |
@@ -1,2 +0,2 @@ | ||
import isEqualWith from 'lodash/isEqualWith'; | ||
import isEqualWith from 'lodash-es/isEqualWith.js'; | ||
/** Implements a deep equals using the `lodash.isEqualWith` function, that provides a customized comparator that | ||
@@ -3,0 +3,0 @@ * assumes all functions are equivalent. |
@@ -1,2 +0,2 @@ | ||
import { TranslatableString } from './enums'; | ||
import { TranslatableString } from './enums.js'; | ||
/** Translates a `TranslatableString` value `stringToTranslate` into english. When a `params` array is provided, each | ||
@@ -3,0 +3,0 @@ * value in the array is used to replace any of the replaceable parameters in the `stringToTranslate` using the `%1`, |
@@ -1,2 +0,2 @@ | ||
import replaceStringParameters from './replaceStringParameters'; | ||
import replaceStringParameters from './replaceStringParameters.js'; | ||
/** Translates a `TranslatableString` value `stringToTranslate` into english. When a `params` array is provided, each | ||
@@ -3,0 +3,0 @@ * value in the array is used to replace any of the replaceable parameters in the `stringToTranslate` using the `%1`, |
@@ -1,2 +0,2 @@ | ||
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** Removes the enum option value at the `valueIndex` from the currently `selected` (list of) value(s). If `selected` is | ||
@@ -3,0 +3,0 @@ * a list, then that list is updated to remove the enum option value with the `valueIndex` in `allEnumOptions`. If it is |
@@ -1,3 +0,3 @@ | ||
import isEqual from 'lodash/isEqual'; | ||
import enumOptionsValueForIndex from './enumOptionsValueForIndex'; | ||
import enumOptionsValueForIndex from './enumOptionsValueForIndex.js'; | ||
import deepEquals from './deepEquals.js'; | ||
/** Removes the enum option value at the `valueIndex` from the currently `selected` (list of) value(s). If `selected` is | ||
@@ -18,6 +18,6 @@ * a list, then that list is updated to remove the enum option value with the `valueIndex` in `allEnumOptions`. If it is | ||
if (Array.isArray(selected)) { | ||
return selected.filter((v) => !isEqual(v, value)); | ||
return selected.filter((v) => !deepEquals(v, value)); | ||
} | ||
return isEqual(value, selected) ? undefined : selected; | ||
return deepEquals(value, selected) ? undefined : selected; | ||
} | ||
//# sourceMappingURL=enumOptionsDeselectValue.js.map |
@@ -1,2 +0,2 @@ | ||
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** Returns the index(es) of the options in `allEnumOptions` whose value(s) match the ones in `value`. All the | ||
@@ -3,0 +3,0 @@ * `enumOptions` are filtered based on whether they are a "selected" `value` and the index of each selected one is then |
@@ -1,2 +0,2 @@ | ||
import enumOptionsIsSelected from './enumOptionsIsSelected'; | ||
import enumOptionsIsSelected from './enumOptionsIsSelected.js'; | ||
/** Returns the index(es) of the options in `allEnumOptions` whose value(s) match the ones in `value`. All the | ||
@@ -3,0 +3,0 @@ * `enumOptions` are filtered based on whether they are a "selected" `value` and the index of each selected one is then |
@@ -1,2 +0,2 @@ | ||
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** Determines whether the given `value` is (one of) the `selected` value(s). | ||
@@ -3,0 +3,0 @@ * |
@@ -1,2 +0,2 @@ | ||
import isEqual from 'lodash/isEqual'; | ||
import deepEquals from './deepEquals.js'; | ||
/** Determines whether the given `value` is (one of) the `selected` value(s). | ||
@@ -10,6 +10,6 @@ * | ||
if (Array.isArray(selected)) { | ||
return selected.some((sel) => isEqual(sel, value)); | ||
return selected.some((sel) => deepEquals(sel, value)); | ||
} | ||
return isEqual(selected, value); | ||
return deepEquals(selected, value); | ||
} | ||
//# sourceMappingURL=enumOptionsIsSelected.js.map |
@@ -1,2 +0,2 @@ | ||
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** Add the enum option value at the `valueIndex` to the list of `selected` values in the proper order as defined by | ||
@@ -3,0 +3,0 @@ * `allEnumOptions` |
@@ -1,3 +0,3 @@ | ||
import enumOptionsValueForIndex from './enumOptionsValueForIndex'; | ||
import isNil from 'lodash/isNil'; | ||
import enumOptionsValueForIndex from './enumOptionsValueForIndex.js'; | ||
import isNil from 'lodash-es/isNil.js'; | ||
/** Add the enum option value at the `valueIndex` to the list of `selected` values in the proper order as defined by | ||
@@ -4,0 +4,0 @@ * `allEnumOptions` |
@@ -1,2 +0,2 @@ | ||
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** Returns the value(s) from `allEnumOptions` at the index(es) provided by `valueIndex`. If `valueIndex` is not an | ||
@@ -3,0 +3,0 @@ * array AND the index is not valid for `allEnumOptions`, `emptyValue` is returned. If `valueIndex` is an array, AND it |
@@ -12,2 +12,4 @@ /** An enumeration of all the translatable strings used by `@rjsf/core` and its themes. The value of each of the | ||
MissingItems = "Missing items definition", | ||
/** Empty array message, used by ArrayField */ | ||
EmptyArray = "No items yet. Use the button below to add some.", | ||
/** Yes label, used by BooleanField */ | ||
@@ -14,0 +16,0 @@ YesLabel = "Yes", |
@@ -13,2 +13,4 @@ /** An enumeration of all the translatable strings used by `@rjsf/core` and its themes. The value of each of the | ||
TranslatableString["MissingItems"] = "Missing items definition"; | ||
/** Empty array message, used by ArrayField */ | ||
TranslatableString["EmptyArray"] = "No items yet. Use the button below to add some."; | ||
/** Yes label, used by BooleanField */ | ||
@@ -15,0 +17,0 @@ TranslatableString["YesLabel"] = "Yes"; |
@@ -1,2 +0,6 @@ | ||
import { ErrorSchema } from './types'; | ||
import { ErrorSchema } from './types.js'; | ||
/** Represents the type of the path which can be a string of dotted path values or a list of string or numbers where | ||
* numbers represent array indexes/ | ||
*/ | ||
export type PathType = string | (string | number)[]; | ||
/** The `ErrorSchemaBuilder<T>` is used to build an `ErrorSchema<T>` since the definition of the `ErrorSchema` type is | ||
@@ -42,3 +46,3 @@ * designed for reading information rather than writing it. Use this class to add, replace or clear errors in an error | ||
*/ | ||
addErrors(errorOrList: string | string[], pathOfError?: string | string[]): this; | ||
addErrors(errorOrList: string | string[], pathOfError?: PathType): this; | ||
/** Sets/replaces the `errorOrList` as the error(s) in the `ErrorSchema` at either the root level or the location | ||
@@ -52,3 +56,3 @@ * within the schema described by the `pathOfError`. For more information about how to specify the path see the | ||
*/ | ||
setErrors(errorOrList: string | string[], pathOfError?: string | string[]): this; | ||
setErrors(errorOrList: string | string[], pathOfError?: PathType): this; | ||
/** Clears the error(s) in the `ErrorSchema` at either the root level or the location within the schema described by | ||
@@ -61,3 +65,3 @@ * the `pathOfError`. For more information about how to specify the path see the | ||
*/ | ||
clearErrors(pathOfError?: string | string[]): this; | ||
clearErrors(pathOfError?: PathType): this; | ||
} |
@@ -1,5 +0,6 @@ | ||
import cloneDeep from 'lodash/cloneDeep'; | ||
import get from 'lodash/get'; | ||
import set from 'lodash/set'; | ||
import { ERRORS_KEY } from './constants'; | ||
import cloneDeep from 'lodash-es/cloneDeep.js'; | ||
import get from 'lodash-es/get.js'; | ||
import set from 'lodash-es/set.js'; | ||
import setWith from 'lodash-es/setWith.js'; | ||
import { ERRORS_KEY } from './constants.js'; | ||
/** The `ErrorSchemaBuilder<T>` is used to build an `ErrorSchema<T>` since the definition of the `ErrorSchema` type is | ||
@@ -36,6 +37,7 @@ * designed for reading information rather than writing it. Use this class to add, replace or clear errors in an error | ||
const hasPath = (Array.isArray(pathOfError) && pathOfError.length > 0) || typeof pathOfError === 'string'; | ||
// @ts-expect-error TS2590 to avoid "Expression produces a union type that is too complex to represent" error | ||
let errorBlock = hasPath ? get(this.errorSchema, pathOfError) : this.errorSchema; | ||
if (!errorBlock && pathOfError) { | ||
errorBlock = {}; | ||
set(this.errorSchema, pathOfError, errorBlock); | ||
setWith(this.errorSchema, pathOfError, errorBlock, Object); | ||
} | ||
@@ -69,6 +71,6 @@ return errorBlock; | ||
if (Array.isArray(errorOrList)) { | ||
errorsList.push(...errorOrList); | ||
set(errorBlock, ERRORS_KEY, [...new Set([...errorsList, ...errorOrList])]); | ||
} | ||
else { | ||
errorsList.push(errorOrList); | ||
set(errorBlock, ERRORS_KEY, [...new Set([...errorsList, errorOrList])]); | ||
} | ||
@@ -88,3 +90,3 @@ return this; | ||
// Effectively clone the array being given to prevent accidental outside manipulation of the given list | ||
const listToAdd = Array.isArray(errorOrList) ? [...errorOrList] : [errorOrList]; | ||
const listToAdd = Array.isArray(errorOrList) ? [...new Set([...errorOrList])] : [errorOrList]; | ||
set(errorBlock, ERRORS_KEY, listToAdd); | ||
@@ -91,0 +93,0 @@ return this; |
@@ -1,2 +0,2 @@ | ||
import { GenericObjectType, RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { GenericObjectType, RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** Splits out the value at the `key` in `object` from the `object`, returning an array that contains in the first | ||
@@ -3,0 +3,0 @@ * location, the `object` minus the `key: value` and in the second location the `value`. |
import jsonpointer from 'jsonpointer'; | ||
import omit from 'lodash/omit'; | ||
import { REF_KEY } from './constants'; | ||
import omit from 'lodash-es/omit.js'; | ||
import { REF_KEY } from './constants.js'; | ||
/** Splits out the value at the `key` in `object` from the `object`, returning an array that contains in the first | ||
@@ -5,0 +5,0 @@ * location, the `object` minus the `key: value` and in the second location the `value`. |
@@ -1,2 +0,2 @@ | ||
import { type DateObject } from './types'; | ||
import { type DateObject } from './types.js'; | ||
/** Available options for re-ordering date input element */ | ||
@@ -3,0 +3,0 @@ export type DateElementFormat = 'DMY' | 'MDY' | 'YMD'; |
@@ -1,2 +0,2 @@ | ||
import { RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** Returns the `discriminator.propertyName` when defined in the `schema` if it is a string. A warning is generated when | ||
@@ -3,0 +3,0 @@ * it is not a string. Returns `undefined` when a valid discriminator is not present. |
@@ -1,3 +0,4 @@ | ||
import get from 'lodash/get'; | ||
import isString from 'lodash/isString'; | ||
import get from 'lodash-es/get.js'; | ||
import isString from 'lodash-es/isString.js'; | ||
import { DISCRIMINATOR_PATH } from './constants.js'; | ||
/** Returns the `discriminator.propertyName` when defined in the `schema` if it is a string. A warning is generated when | ||
@@ -11,3 +12,3 @@ * it is not a string. Returns `undefined` when a valid discriminator is not present. | ||
let discriminator; | ||
const maybeString = get(schema, 'discriminator.propertyName', undefined); | ||
const maybeString = get(schema, DISCRIMINATOR_PATH); | ||
if (isString(maybeString)) { | ||
@@ -14,0 +15,0 @@ discriminator = maybeString; |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, InputPropsType, RJSFSchema, StrictRJSFSchema, UIOptionsType } from './types'; | ||
import { FormContextType, InputPropsType, RJSFSchema, StrictRJSFSchema, UIOptionsType } from './types.js'; | ||
/** Using the `schema`, `defaultType` and `options`, extract out the props for the <input> element that make sense. | ||
@@ -3,0 +3,0 @@ * |
@@ -1,2 +0,2 @@ | ||
import rangeSpec from './rangeSpec'; | ||
import rangeSpec from './rangeSpec.js'; | ||
/** Using the `schema`, `defaultType` and `options`, extract out the props for the <input> element that make sense. | ||
@@ -42,4 +42,7 @@ * | ||
} | ||
if (options.accept) { | ||
inputProps.accept = options.accept; | ||
} | ||
return inputProps; | ||
} | ||
//# sourceMappingURL=getInputProps.js.map |
@@ -1,2 +0,2 @@ | ||
import { RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** Compares the value of `discriminatorField` within `formData` against the value of `discriminatorField` within schema for each `option`. | ||
@@ -3,0 +3,0 @@ * Returns index of first `option` whose discriminator matches formData. Returns `undefined` if there is no match. |
@@ -1,3 +0,3 @@ | ||
import get from 'lodash/get'; | ||
import { PROPERTIES_KEY } from './constants'; | ||
import get from 'lodash-es/get.js'; | ||
import { PROPERTIES_KEY } from './constants.js'; | ||
/** Compares the value of `discriminatorField` within `formData` against the value of `discriminatorField` within schema for each `option`. | ||
@@ -4,0 +4,0 @@ * Returns index of first `option` whose discriminator matches formData. Returns `undefined` if there is no match. |
@@ -1,2 +0,2 @@ | ||
import { RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** Gets the type of a given `schema`. If the type is not explicitly defined, then an attempt is made to infer it from | ||
@@ -8,2 +8,3 @@ * other elements of the schema as follows: | ||
* - schema.additionalProperties: Returns `object` | ||
* - schema.patternProperties: Returns `object` | ||
* - type is an array with a length of 2 and one type is 'null': Returns the other type | ||
@@ -10,0 +11,0 @@ * |
@@ -1,2 +0,2 @@ | ||
import guessType from './guessType'; | ||
import guessType from './guessType.js'; | ||
/** Gets the type of a given `schema`. If the type is not explicitly defined, then an attempt is made to infer it from | ||
@@ -8,2 +8,3 @@ * other elements of the schema as follows: | ||
* - schema.additionalProperties: Returns `object` | ||
* - schema.patternProperties: Returns `object` | ||
* - type is an array with a length of 2 and one type is 'null': Returns the other type | ||
@@ -22,3 +23,3 @@ * | ||
} | ||
if (!type && (schema.properties || schema.additionalProperties)) { | ||
if (!type && (schema.properties || schema.additionalProperties || schema.patternProperties)) { | ||
return 'object'; | ||
@@ -25,0 +26,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, UiSchema, UISchemaSubmitButtonOptions } from './types'; | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, UiSchema, UISchemaSubmitButtonOptions } from './types.js'; | ||
/** The default submit button options, exported for testing purposes | ||
@@ -3,0 +3,0 @@ */ |
@@ -1,3 +0,3 @@ | ||
import { SUBMIT_BTN_OPTIONS_KEY } from './constants'; | ||
import getUiOptions from './getUiOptions'; | ||
import { SUBMIT_BTN_OPTIONS_KEY } from './constants.js'; | ||
import getUiOptions from './getUiOptions.js'; | ||
/** The default submit button options, exported for testing purposes | ||
@@ -4,0 +4,0 @@ */ |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, TemplatesType, Registry, UIOptionsType, StrictRJSFSchema, RJSFSchema } from './types'; | ||
import { FormContextType, TemplatesType, Registry, UIOptionsType, StrictRJSFSchema, RJSFSchema } from './types.js'; | ||
/** Returns the template with the given `name` from either the `uiSchema` if it is defined or from the `registry` | ||
@@ -3,0 +3,0 @@ * otherwise. NOTE, since `ButtonTemplates` are not overridden in `uiSchema` only those in the `registry` are returned. |
@@ -14,2 +14,11 @@ /** Returns the template with the given `name` from either the `uiSchema` if it is defined or from the `registry` | ||
} | ||
// Allow templates to be customized per-field by using string keys from the registry | ||
if (Object.hasOwn(uiOptions, name) && | ||
typeof uiOptions[name] === 'string' && | ||
Object.hasOwn(templates, uiOptions[name])) { | ||
const key = uiOptions[name]; | ||
// Evaluating templates[key] results in TS2590: Expression produces a union type that is too complex to represent | ||
// To avoid that, we cast templates to `any` before accessing the key field | ||
return templates[key]; | ||
} | ||
return ( | ||
@@ -16,0 +25,0 @@ // Evaluating uiOptions[name] results in TS2590: Expression produces a union type that is too complex to represent |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, GlobalUISchemaOptions, RJSFSchema, StrictRJSFSchema, UIOptionsType, UiSchema } from './types'; | ||
import { FormContextType, GlobalUISchemaOptions, RJSFSchema, StrictRJSFSchema, UIOptionsType, UiSchema } from './types.js'; | ||
/** Get all passed options from ui:options, and ui:<optionName>, returning them in an object with the `ui:` | ||
@@ -3,0 +3,0 @@ * stripped off. Any `globalOptions` will always be returned, unless they are overridden by options in the `uiSchema`. |
@@ -1,3 +0,3 @@ | ||
import { UI_OPTIONS_KEY, UI_WIDGET_KEY } from './constants'; | ||
import isObject from './isObject'; | ||
import { UI_OPTIONS_KEY, UI_WIDGET_KEY } from './constants.js'; | ||
import isObject from './isObject.js'; | ||
/** Get all passed options from ui:options, and ui:<optionName>, returning them in an object with the `ui:` | ||
@@ -4,0 +4,0 @@ * stripped off. Any `globalOptions` will always be returned, unless they are overridden by options in the `uiSchema`. |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, RJSFSchema, Widget, RegistryWidgetsType, StrictRJSFSchema } from './types'; | ||
import { FormContextType, RJSFSchema, Widget, RegistryWidgetsType, StrictRJSFSchema } from './types.js'; | ||
/** Given a schema representing a field to render and either the name or actual `Widget` implementation, returns the | ||
@@ -3,0 +3,0 @@ * React component that is used to render the widget. If the `widget` is already a React component, then it is wrapped |
import { jsx as _jsx } from "react/jsx-runtime"; | ||
import { createElement } from 'react'; | ||
import ReactIs from 'react-is'; | ||
import get from 'lodash/get'; | ||
import set from 'lodash/set'; | ||
import getSchemaType from './getSchemaType'; | ||
import get from 'lodash-es/get.js'; | ||
import set from 'lodash-es/set.js'; | ||
import getSchemaType from './getSchemaType.js'; | ||
/** The map of schema types to widget type to widget name | ||
@@ -8,0 +8,0 @@ */ |
@@ -7,2 +7,2 @@ /** Given a specific `value` attempts to guess the type of a schema element. In the case where we have to implicitly | ||
*/ | ||
export default function guessType(value: any): "number" | "string" | "boolean" | "object" | "array" | "null"; | ||
export default function guessType(value: any): "string" | "object" | "number" | "boolean" | "array" | "null"; |
@@ -1,2 +0,24 @@ | ||
import { RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** Hashes a string using the algorithm based on Java's hashing function. | ||
* JS has no built-in hashing function, so rolling our own | ||
* based on Java's hashing fn: | ||
* http://www.java2s.com/example/nodejs-utility-method/string-hash/hashcode-4dc2b.html | ||
* | ||
* @param string - The string for which to get the hash | ||
* @returns - The resulting hash of the string in hex format | ||
*/ | ||
export declare function hashString(string: string): string; | ||
/** Stringifies an `object`, sorts object fields in consistent order before stringifying it. | ||
* | ||
* @param object - The object for which the sorted stringify is desired | ||
* @returns - The stringified object with keys sorted in a consistent order | ||
*/ | ||
export declare function sortedJSONStringify(object: unknown): string; | ||
/** Stringifies an `object` and returns the hash of the resulting string. Sorts object fields | ||
* in consistent order before stringify to prevent different hash ids for the same object. | ||
* | ||
* @param object - The object for which the hash is desired | ||
* @returns - The string obtained from the hash of the stringified object | ||
*/ | ||
export declare function hashObject(object: unknown): string; | ||
/** Stringifies the schema and returns the hash of the resulting string. Sorts schema fields | ||
@@ -3,0 +25,0 @@ * in consistent order before stringify to prevent different hash ids for the same schema. |
@@ -1,2 +0,3 @@ | ||
/** JS has no built-in hashing function, so rolling our own | ||
/** Hashes a string using the algorithm based on Java's hashing function. | ||
* JS has no built-in hashing function, so rolling our own | ||
* based on Java's hashing fn: | ||
@@ -8,3 +9,3 @@ * http://www.java2s.com/example/nodejs-utility-method/string-hash/hashcode-4dc2b.html | ||
*/ | ||
function hashString(string) { | ||
export function hashString(string) { | ||
let hash = 0; | ||
@@ -18,2 +19,22 @@ for (let i = 0; i < string.length; i += 1) { | ||
} | ||
/** Stringifies an `object`, sorts object fields in consistent order before stringifying it. | ||
* | ||
* @param object - The object for which the sorted stringify is desired | ||
* @returns - The stringified object with keys sorted in a consistent order | ||
*/ | ||
export function sortedJSONStringify(object) { | ||
const allKeys = new Set(); | ||
// solution source: https://stackoverflow.com/questions/16167581/sort-object-properties-and-json-stringify/53593328#53593328 | ||
JSON.stringify(object, (key, value) => (allKeys.add(key), value)); | ||
return JSON.stringify(object, Array.from(allKeys).sort()); | ||
} | ||
/** Stringifies an `object` and returns the hash of the resulting string. Sorts object fields | ||
* in consistent order before stringify to prevent different hash ids for the same object. | ||
* | ||
* @param object - The object for which the hash is desired | ||
* @returns - The string obtained from the hash of the stringified object | ||
*/ | ||
export function hashObject(object) { | ||
return hashString(sortedJSONStringify(object)); | ||
} | ||
/** Stringifies the schema and returns the hash of the resulting string. Sorts schema fields | ||
@@ -26,7 +47,4 @@ * in consistent order before stringify to prevent different hash ids for the same schema. | ||
export default function hashForSchema(schema) { | ||
const allKeys = new Set(); | ||
// solution source: https://stackoverflow.com/questions/16167581/sort-object-properties-and-json-stringify/53593328#53593328 | ||
JSON.stringify(schema, (key, value) => (allKeys.add(key), value)); | ||
return hashString(JSON.stringify(schema, Array.from(allKeys).sort())); | ||
return hashObject(schema); | ||
} | ||
//# sourceMappingURL=hashForSchema.js.map |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, RegistryWidgetsType, RJSFSchema, StrictRJSFSchema, Widget } from './types'; | ||
import { FormContextType, RegistryWidgetsType, RJSFSchema, StrictRJSFSchema, Widget } from './types.js'; | ||
/** Detects whether the `widget` exists for the `schema` with the associated `registryWidgets` and returns true if it | ||
@@ -3,0 +3,0 @@ * does, or false if it doesn't. |
@@ -1,2 +0,2 @@ | ||
import getWidget from './getWidget'; | ||
import getWidget from './getWidget.js'; | ||
/** Detects whether the `widget` exists for the `schema` with the associated `registryWidgets` and returns true if it | ||
@@ -3,0 +3,0 @@ * does, or false if it doesn't. |
@@ -1,2 +0,2 @@ | ||
import { IdSchema } from './types'; | ||
import { IdSchema } from './types.js'; | ||
/** Return a consistent `id` for the field description element | ||
@@ -48,1 +48,8 @@ * | ||
export declare function optionId(id: string, optionIndex: number): string; | ||
/** Return a consistent `id` for the `btn` button element | ||
* | ||
* @param id - Either simple string id or an IdSchema from which to extract it | ||
* @param btn - The button type for which to generate the id | ||
* @returns - The consistent id for the button from the given `id` and `btn` type | ||
*/ | ||
export declare function buttonId<T = any>(id: IdSchema<T> | string, btn: 'add' | 'copy' | 'moveDown' | 'moveUp' | 'remove'): string; |
@@ -1,3 +0,3 @@ | ||
import isString from 'lodash/isString'; | ||
import { ID_KEY } from './constants'; | ||
import isString from 'lodash-es/isString.js'; | ||
import { ID_KEY } from './constants.js'; | ||
/** Generates a consistent `id` pattern for a given `id` and a `suffix` | ||
@@ -73,2 +73,11 @@ * | ||
} | ||
/** Return a consistent `id` for the `btn` button element | ||
* | ||
* @param id - Either simple string id or an IdSchema from which to extract it | ||
* @param btn - The button type for which to generate the id | ||
* @returns - The consistent id for the button from the given `id` and `btn` type | ||
*/ | ||
export function buttonId(id, btn) { | ||
return idGenerator(id, btn); | ||
} | ||
//# sourceMappingURL=idGenerators.js.map |
@@ -1,60 +0,63 @@ | ||
import allowAdditionalItems from './allowAdditionalItems'; | ||
import asNumber from './asNumber'; | ||
import canExpand from './canExpand'; | ||
import createErrorHandler from './createErrorHandler'; | ||
import createSchemaUtils from './createSchemaUtils'; | ||
import dataURItoBlob from './dataURItoBlob'; | ||
import dateRangeOptions from './dateRangeOptions'; | ||
import deepEquals from './deepEquals'; | ||
import englishStringTranslator from './englishStringTranslator'; | ||
import enumOptionsDeselectValue from './enumOptionsDeselectValue'; | ||
import enumOptionsIndexForValue from './enumOptionsIndexForValue'; | ||
import enumOptionsIsSelected from './enumOptionsIsSelected'; | ||
import enumOptionsSelectValue from './enumOptionsSelectValue'; | ||
import enumOptionsValueForIndex from './enumOptionsValueForIndex'; | ||
import ErrorSchemaBuilder from './ErrorSchemaBuilder'; | ||
import findSchemaDefinition from './findSchemaDefinition'; | ||
import getDateElementProps, { type DateElementFormat } from './getDateElementProps'; | ||
import getDiscriminatorFieldFromSchema from './getDiscriminatorFieldFromSchema'; | ||
import getInputProps from './getInputProps'; | ||
import getSchemaType from './getSchemaType'; | ||
import getSubmitButtonOptions from './getSubmitButtonOptions'; | ||
import getTemplate from './getTemplate'; | ||
import getUiOptions from './getUiOptions'; | ||
import getWidget from './getWidget'; | ||
import guessType from './guessType'; | ||
import hashForSchema from './hashForSchema'; | ||
import hasWidget from './hasWidget'; | ||
import { ariaDescribedByIds, descriptionId, errorId, examplesId, helpId, optionId, titleId } from './idGenerators'; | ||
import isConstant from './isConstant'; | ||
import isCustomWidget from './isCustomWidget'; | ||
import isFixedItems from './isFixedItems'; | ||
import isObject from './isObject'; | ||
import labelValue from './labelValue'; | ||
import localToUTC from './localToUTC'; | ||
import mergeDefaultsWithFormData from './mergeDefaultsWithFormData'; | ||
import mergeObjects from './mergeObjects'; | ||
import mergeSchemas from './mergeSchemas'; | ||
import optionsList from './optionsList'; | ||
import orderProperties from './orderProperties'; | ||
import pad from './pad'; | ||
import parseDateString from './parseDateString'; | ||
import rangeSpec from './rangeSpec'; | ||
import replaceStringParameters from './replaceStringParameters'; | ||
import schemaRequiresTrueValue from './schemaRequiresTrueValue'; | ||
import shouldRender from './shouldRender'; | ||
import toConstant from './toConstant'; | ||
import toDateString from './toDateString'; | ||
import toErrorList from './toErrorList'; | ||
import toErrorSchema from './toErrorSchema'; | ||
import unwrapErrorHandler from './unwrapErrorHandler'; | ||
import utcToLocal from './utcToLocal'; | ||
import validationDataMerge from './validationDataMerge'; | ||
import withIdRefPrefix from './withIdRefPrefix'; | ||
import getOptionMatchingSimpleDiscriminator from './getOptionMatchingSimpleDiscriminator'; | ||
export * from './types'; | ||
export * from './enums'; | ||
export * from './constants'; | ||
export * from './parser'; | ||
export * from './schema'; | ||
export { allowAdditionalItems, ariaDescribedByIds, asNumber, canExpand, createErrorHandler, createSchemaUtils, DateElementFormat, dataURItoBlob, dateRangeOptions, deepEquals, descriptionId, englishStringTranslator, enumOptionsDeselectValue, enumOptionsIndexForValue, enumOptionsIsSelected, enumOptionsSelectValue, enumOptionsValueForIndex, errorId, examplesId, ErrorSchemaBuilder, findSchemaDefinition, getDateElementProps, getDiscriminatorFieldFromSchema, getInputProps, getOptionMatchingSimpleDiscriminator, getSchemaType, getSubmitButtonOptions, getTemplate, getUiOptions, getWidget, guessType, hasWidget, hashForSchema, helpId, isConstant, isCustomWidget, isFixedItems, isObject, labelValue, localToUTC, mergeDefaultsWithFormData, mergeObjects, mergeSchemas, optionId, optionsList, orderProperties, pad, parseDateString, rangeSpec, replaceStringParameters, schemaRequiresTrueValue, shouldRender, titleId, toConstant, toDateString, toErrorList, toErrorSchema, unwrapErrorHandler, utcToLocal, validationDataMerge, withIdRefPrefix, }; | ||
import allowAdditionalItems from './allowAdditionalItems.js'; | ||
import asNumber from './asNumber.js'; | ||
import canExpand from './canExpand.js'; | ||
import createErrorHandler from './createErrorHandler.js'; | ||
import createSchemaUtils from './createSchemaUtils.js'; | ||
import dataURItoBlob from './dataURItoBlob.js'; | ||
import dateRangeOptions from './dateRangeOptions.js'; | ||
import deepEquals from './deepEquals.js'; | ||
import englishStringTranslator from './englishStringTranslator.js'; | ||
import enumOptionsDeselectValue from './enumOptionsDeselectValue.js'; | ||
import enumOptionsIndexForValue from './enumOptionsIndexForValue.js'; | ||
import enumOptionsIsSelected from './enumOptionsIsSelected.js'; | ||
import enumOptionsSelectValue from './enumOptionsSelectValue.js'; | ||
import enumOptionsValueForIndex from './enumOptionsValueForIndex.js'; | ||
import ErrorSchemaBuilder from './ErrorSchemaBuilder.js'; | ||
import findSchemaDefinition from './findSchemaDefinition.js'; | ||
import getDateElementProps, { type DateElementFormat } from './getDateElementProps.js'; | ||
import getDiscriminatorFieldFromSchema from './getDiscriminatorFieldFromSchema.js'; | ||
import getInputProps from './getInputProps.js'; | ||
import getSchemaType from './getSchemaType.js'; | ||
import getSubmitButtonOptions from './getSubmitButtonOptions.js'; | ||
import getTemplate from './getTemplate.js'; | ||
import getTestIds from './getTestIds.js'; | ||
import getUiOptions from './getUiOptions.js'; | ||
import getWidget from './getWidget.js'; | ||
import guessType from './guessType.js'; | ||
import hashForSchema, { hashObject, hashString, sortedJSONStringify } from './hashForSchema.js'; | ||
import hasWidget from './hasWidget.js'; | ||
import { ariaDescribedByIds, buttonId, descriptionId, errorId, examplesId, helpId, optionId, titleId } from './idGenerators.js'; | ||
import isConstant from './isConstant.js'; | ||
import isCustomWidget from './isCustomWidget.js'; | ||
import isFixedItems from './isFixedItems.js'; | ||
import isObject from './isObject.js'; | ||
import labelValue from './labelValue.js'; | ||
import localToUTC from './localToUTC.js'; | ||
import lookupFromFormContext from './lookupFromFormContext.js'; | ||
import mergeDefaultsWithFormData from './mergeDefaultsWithFormData.js'; | ||
import mergeObjects from './mergeObjects.js'; | ||
import mergeSchemas from './mergeSchemas.js'; | ||
import optionsList from './optionsList.js'; | ||
import orderProperties from './orderProperties.js'; | ||
import pad from './pad.js'; | ||
import parseDateString from './parseDateString.js'; | ||
import rangeSpec from './rangeSpec.js'; | ||
import replaceStringParameters from './replaceStringParameters.js'; | ||
import schemaRequiresTrueValue from './schemaRequiresTrueValue.js'; | ||
import shouldRender from './shouldRender.js'; | ||
import toConstant from './toConstant.js'; | ||
import toDateString from './toDateString.js'; | ||
import toErrorList from './toErrorList.js'; | ||
import toErrorSchema from './toErrorSchema.js'; | ||
import unwrapErrorHandler from './unwrapErrorHandler.js'; | ||
import utcToLocal from './utcToLocal.js'; | ||
import validationDataMerge from './validationDataMerge.js'; | ||
import withIdRefPrefix from './withIdRefPrefix.js'; | ||
import getOptionMatchingSimpleDiscriminator from './getOptionMatchingSimpleDiscriminator.js'; | ||
import getChangedFields from './getChangedFields.js'; | ||
export * from './types.js'; | ||
export * from './enums.js'; | ||
export * from './constants.js'; | ||
export * from './parser/index.js'; | ||
export * from './schema/index.js'; | ||
export { allowAdditionalItems, ariaDescribedByIds, asNumber, buttonId, canExpand, createErrorHandler, createSchemaUtils, DateElementFormat, dataURItoBlob, dateRangeOptions, deepEquals, descriptionId, englishStringTranslator, enumOptionsDeselectValue, enumOptionsIndexForValue, enumOptionsIsSelected, enumOptionsSelectValue, enumOptionsValueForIndex, errorId, examplesId, ErrorSchemaBuilder, findSchemaDefinition, getChangedFields, getDateElementProps, getDiscriminatorFieldFromSchema, getInputProps, getOptionMatchingSimpleDiscriminator, getSchemaType, getSubmitButtonOptions, getTemplate, getTestIds, getUiOptions, getWidget, guessType, hasWidget, hashForSchema, hashObject, hashString, helpId, isConstant, isCustomWidget, isFixedItems, isObject, labelValue, localToUTC, lookupFromFormContext, mergeDefaultsWithFormData, mergeObjects, mergeSchemas, optionId, optionsList, orderProperties, pad, parseDateString, rangeSpec, replaceStringParameters, schemaRequiresTrueValue, shouldRender, sortedJSONStringify, titleId, toConstant, toDateString, toErrorList, toErrorSchema, unwrapErrorHandler, utcToLocal, validationDataMerge, withIdRefPrefix, }; |
123
lib/index.js
@@ -1,61 +0,64 @@ | ||
import allowAdditionalItems from './allowAdditionalItems'; | ||
import asNumber from './asNumber'; | ||
import canExpand from './canExpand'; | ||
import createErrorHandler from './createErrorHandler'; | ||
import createSchemaUtils from './createSchemaUtils'; | ||
import dataURItoBlob from './dataURItoBlob'; | ||
import dateRangeOptions from './dateRangeOptions'; | ||
import deepEquals from './deepEquals'; | ||
import englishStringTranslator from './englishStringTranslator'; | ||
import enumOptionsDeselectValue from './enumOptionsDeselectValue'; | ||
import enumOptionsIndexForValue from './enumOptionsIndexForValue'; | ||
import enumOptionsIsSelected from './enumOptionsIsSelected'; | ||
import enumOptionsSelectValue from './enumOptionsSelectValue'; | ||
import enumOptionsValueForIndex from './enumOptionsValueForIndex'; | ||
import ErrorSchemaBuilder from './ErrorSchemaBuilder'; | ||
import findSchemaDefinition from './findSchemaDefinition'; | ||
import getDateElementProps from './getDateElementProps'; | ||
import getDiscriminatorFieldFromSchema from './getDiscriminatorFieldFromSchema'; | ||
import getInputProps from './getInputProps'; | ||
import getSchemaType from './getSchemaType'; | ||
import getSubmitButtonOptions from './getSubmitButtonOptions'; | ||
import getTemplate from './getTemplate'; | ||
import getUiOptions from './getUiOptions'; | ||
import getWidget from './getWidget'; | ||
import guessType from './guessType'; | ||
import hashForSchema from './hashForSchema'; | ||
import hasWidget from './hasWidget'; | ||
import { ariaDescribedByIds, descriptionId, errorId, examplesId, helpId, optionId, titleId } from './idGenerators'; | ||
import isConstant from './isConstant'; | ||
import isCustomWidget from './isCustomWidget'; | ||
import isFixedItems from './isFixedItems'; | ||
import isObject from './isObject'; | ||
import labelValue from './labelValue'; | ||
import localToUTC from './localToUTC'; | ||
import mergeDefaultsWithFormData from './mergeDefaultsWithFormData'; | ||
import mergeObjects from './mergeObjects'; | ||
import mergeSchemas from './mergeSchemas'; | ||
import optionsList from './optionsList'; | ||
import orderProperties from './orderProperties'; | ||
import pad from './pad'; | ||
import parseDateString from './parseDateString'; | ||
import rangeSpec from './rangeSpec'; | ||
import replaceStringParameters from './replaceStringParameters'; | ||
import schemaRequiresTrueValue from './schemaRequiresTrueValue'; | ||
import shouldRender from './shouldRender'; | ||
import toConstant from './toConstant'; | ||
import toDateString from './toDateString'; | ||
import toErrorList from './toErrorList'; | ||
import toErrorSchema from './toErrorSchema'; | ||
import unwrapErrorHandler from './unwrapErrorHandler'; | ||
import utcToLocal from './utcToLocal'; | ||
import validationDataMerge from './validationDataMerge'; | ||
import withIdRefPrefix from './withIdRefPrefix'; | ||
import getOptionMatchingSimpleDiscriminator from './getOptionMatchingSimpleDiscriminator'; | ||
export * from './types'; | ||
export * from './enums'; | ||
export * from './constants'; | ||
export * from './parser'; | ||
export * from './schema'; | ||
export { allowAdditionalItems, ariaDescribedByIds, asNumber, canExpand, createErrorHandler, createSchemaUtils, dataURItoBlob, dateRangeOptions, deepEquals, descriptionId, englishStringTranslator, enumOptionsDeselectValue, enumOptionsIndexForValue, enumOptionsIsSelected, enumOptionsSelectValue, enumOptionsValueForIndex, errorId, examplesId, ErrorSchemaBuilder, findSchemaDefinition, getDateElementProps, getDiscriminatorFieldFromSchema, getInputProps, getOptionMatchingSimpleDiscriminator, getSchemaType, getSubmitButtonOptions, getTemplate, getUiOptions, getWidget, guessType, hasWidget, hashForSchema, helpId, isConstant, isCustomWidget, isFixedItems, isObject, labelValue, localToUTC, mergeDefaultsWithFormData, mergeObjects, mergeSchemas, optionId, optionsList, orderProperties, pad, parseDateString, rangeSpec, replaceStringParameters, schemaRequiresTrueValue, shouldRender, titleId, toConstant, toDateString, toErrorList, toErrorSchema, unwrapErrorHandler, utcToLocal, validationDataMerge, withIdRefPrefix, }; | ||
import allowAdditionalItems from './allowAdditionalItems.js'; | ||
import asNumber from './asNumber.js'; | ||
import canExpand from './canExpand.js'; | ||
import createErrorHandler from './createErrorHandler.js'; | ||
import createSchemaUtils from './createSchemaUtils.js'; | ||
import dataURItoBlob from './dataURItoBlob.js'; | ||
import dateRangeOptions from './dateRangeOptions.js'; | ||
import deepEquals from './deepEquals.js'; | ||
import englishStringTranslator from './englishStringTranslator.js'; | ||
import enumOptionsDeselectValue from './enumOptionsDeselectValue.js'; | ||
import enumOptionsIndexForValue from './enumOptionsIndexForValue.js'; | ||
import enumOptionsIsSelected from './enumOptionsIsSelected.js'; | ||
import enumOptionsSelectValue from './enumOptionsSelectValue.js'; | ||
import enumOptionsValueForIndex from './enumOptionsValueForIndex.js'; | ||
import ErrorSchemaBuilder from './ErrorSchemaBuilder.js'; | ||
import findSchemaDefinition from './findSchemaDefinition.js'; | ||
import getDateElementProps from './getDateElementProps.js'; | ||
import getDiscriminatorFieldFromSchema from './getDiscriminatorFieldFromSchema.js'; | ||
import getInputProps from './getInputProps.js'; | ||
import getSchemaType from './getSchemaType.js'; | ||
import getSubmitButtonOptions from './getSubmitButtonOptions.js'; | ||
import getTemplate from './getTemplate.js'; | ||
import getTestIds from './getTestIds.js'; | ||
import getUiOptions from './getUiOptions.js'; | ||
import getWidget from './getWidget.js'; | ||
import guessType from './guessType.js'; | ||
import hashForSchema, { hashObject, hashString, sortedJSONStringify } from './hashForSchema.js'; | ||
import hasWidget from './hasWidget.js'; | ||
import { ariaDescribedByIds, buttonId, descriptionId, errorId, examplesId, helpId, optionId, titleId, } from './idGenerators.js'; | ||
import isConstant from './isConstant.js'; | ||
import isCustomWidget from './isCustomWidget.js'; | ||
import isFixedItems from './isFixedItems.js'; | ||
import isObject from './isObject.js'; | ||
import labelValue from './labelValue.js'; | ||
import localToUTC from './localToUTC.js'; | ||
import lookupFromFormContext from './lookupFromFormContext.js'; | ||
import mergeDefaultsWithFormData from './mergeDefaultsWithFormData.js'; | ||
import mergeObjects from './mergeObjects.js'; | ||
import mergeSchemas from './mergeSchemas.js'; | ||
import optionsList from './optionsList.js'; | ||
import orderProperties from './orderProperties.js'; | ||
import pad from './pad.js'; | ||
import parseDateString from './parseDateString.js'; | ||
import rangeSpec from './rangeSpec.js'; | ||
import replaceStringParameters from './replaceStringParameters.js'; | ||
import schemaRequiresTrueValue from './schemaRequiresTrueValue.js'; | ||
import shouldRender from './shouldRender.js'; | ||
import toConstant from './toConstant.js'; | ||
import toDateString from './toDateString.js'; | ||
import toErrorList from './toErrorList.js'; | ||
import toErrorSchema from './toErrorSchema.js'; | ||
import unwrapErrorHandler from './unwrapErrorHandler.js'; | ||
import utcToLocal from './utcToLocal.js'; | ||
import validationDataMerge from './validationDataMerge.js'; | ||
import withIdRefPrefix from './withIdRefPrefix.js'; | ||
import getOptionMatchingSimpleDiscriminator from './getOptionMatchingSimpleDiscriminator.js'; | ||
import getChangedFields from './getChangedFields.js'; | ||
export * from './types.js'; | ||
export * from './enums.js'; | ||
export * from './constants.js'; | ||
export * from './parser/index.js'; | ||
export * from './schema/index.js'; | ||
export { allowAdditionalItems, ariaDescribedByIds, asNumber, buttonId, canExpand, createErrorHandler, createSchemaUtils, dataURItoBlob, dateRangeOptions, deepEquals, descriptionId, englishStringTranslator, enumOptionsDeselectValue, enumOptionsIndexForValue, enumOptionsIsSelected, enumOptionsSelectValue, enumOptionsValueForIndex, errorId, examplesId, ErrorSchemaBuilder, findSchemaDefinition, getChangedFields, getDateElementProps, getDiscriminatorFieldFromSchema, getInputProps, getOptionMatchingSimpleDiscriminator, getSchemaType, getSubmitButtonOptions, getTemplate, getTestIds, getUiOptions, getWidget, guessType, hasWidget, hashForSchema, hashObject, hashString, helpId, isConstant, isCustomWidget, isFixedItems, isObject, labelValue, localToUTC, lookupFromFormContext, mergeDefaultsWithFormData, mergeObjects, mergeSchemas, optionId, optionsList, orderProperties, pad, parseDateString, rangeSpec, replaceStringParameters, schemaRequiresTrueValue, shouldRender, sortedJSONStringify, titleId, toConstant, toDateString, toErrorList, toErrorSchema, unwrapErrorHandler, utcToLocal, validationDataMerge, withIdRefPrefix, }; | ||
//# sourceMappingURL=index.js.map |
@@ -1,2 +0,2 @@ | ||
import { RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** This function checks if the given `schema` matches a single constant value. This happens when either the schema has | ||
@@ -3,0 +3,0 @@ * an `enum` array with a single value or there is a `const` defined. |
@@ -1,2 +0,2 @@ | ||
import { CONST_KEY } from './constants'; | ||
import { CONST_KEY } from './constants.js'; | ||
/** This function checks if the given `schema` matches a single constant value. This happens when either the schema has | ||
@@ -3,0 +3,0 @@ * an `enum` array with a single value or there is a `const` defined. |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, UiSchema } from './types'; | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, UiSchema } from './types.js'; | ||
/** Checks to see if the `uiSchema` contains the `widget` field and that the widget is not `hidden` | ||
@@ -3,0 +3,0 @@ * |
@@ -1,2 +0,2 @@ | ||
import getUiOptions from './getUiOptions'; | ||
import getUiOptions from './getUiOptions.js'; | ||
/** Checks to see if the `uiSchema` contains the `widget` field and that the widget is not `hidden` | ||
@@ -3,0 +3,0 @@ * |
@@ -1,2 +0,2 @@ | ||
import { RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** Detects whether the given `schema` contains fixed items. This is the case when `schema.items` is a non-empty array | ||
@@ -3,0 +3,0 @@ * that only contains objects. |
@@ -1,2 +0,2 @@ | ||
import isObject from './isObject'; | ||
import isObject from './isObject.js'; | ||
/** Detects whether the given `schema` contains fixed items. This is the case when `schema.items` is a non-empty array | ||
@@ -3,0 +3,0 @@ * that only contains objects. |
@@ -1,2 +0,2 @@ | ||
/** Determines whether a `thing` is an object for the purposes of RSJF. In this case, `thing` is an object if it has | ||
/** Determines whether a `thing` is an object for the purposes of RJSF. In this case, `thing` is an object if it has | ||
* the type `object` but is NOT null, an array or a File. | ||
@@ -7,2 +7,2 @@ * | ||
*/ | ||
export default function isObject(thing: any): boolean; | ||
export default function isObject(thing: any): thing is object; |
@@ -1,2 +0,2 @@ | ||
/** Determines whether a `thing` is an object for the purposes of RSJF. In this case, `thing` is an object if it has | ||
/** Determines whether a `thing` is an object for the purposes of RJSF. In this case, `thing` is an object if it has | ||
* the type `object` but is NOT null, an array or a File. | ||
@@ -8,10 +8,17 @@ * | ||
export default function isObject(thing) { | ||
if (typeof File !== 'undefined' && thing instanceof File) { | ||
if (typeof thing !== 'object' || thing === null) { | ||
return false; | ||
} | ||
if (typeof Date !== 'undefined' && thing instanceof Date) { | ||
// lastModified is guaranteed to be a number on a File instance | ||
// as per https://w3c.github.io/FileAPI/#dfn-lastModified | ||
if (typeof thing.lastModified === 'number' && typeof File !== 'undefined' && thing instanceof File) { | ||
return false; | ||
} | ||
return typeof thing === 'object' && thing !== null && !Array.isArray(thing); | ||
// getMonth is guaranteed to be a method on a Date instance | ||
// as per https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-date.prototype.getmonth | ||
if (typeof thing.getMonth === 'function' && typeof Date !== 'undefined' && thing instanceof Date) { | ||
return false; | ||
} | ||
return !Array.isArray(thing); | ||
} | ||
//# sourceMappingURL=isObject.js.map |
@@ -10,3 +10,4 @@ /** Merges the `defaults` object of type `T` into the `formData` of type `T` | ||
* - when the array is not set in form data, the default is copied over | ||
* - scalars are overwritten/set by form data | ||
* - scalars are overwritten/set by form data unless undefined and there is a default AND `defaultSupercedesUndefined` | ||
* is true | ||
* | ||
@@ -16,4 +17,9 @@ * @param [defaults] - The defaults to merge | ||
* @param [mergeExtraArrayDefaults=false] - If true, any additional default array entries are appended onto the formData | ||
* @param [defaultSupercedesUndefined=false] - If true, an explicit undefined value will be overwritten by the default value | ||
* @param [overrideFormDataWithDefaults=false] - If true, the default value will overwrite the form data value. If the value | ||
* doesn't exist in the default, we take it from formData and in the case where the value is set to undefined in formData. | ||
* This is useful when we have already merged formData with defaults and want to add an additional field from formData | ||
* that does not exist in defaults. | ||
* @returns - The resulting merged form data with defaults | ||
*/ | ||
export default function mergeDefaultsWithFormData<T = any>(defaults?: T, formData?: T, mergeExtraArrayDefaults?: boolean): T | undefined; | ||
export default function mergeDefaultsWithFormData<T = any>(defaults?: T, formData?: T, mergeExtraArrayDefaults?: boolean, defaultSupercedesUndefined?: boolean, overrideFormDataWithDefaults?: boolean): T | undefined; |
@@ -1,3 +0,4 @@ | ||
import get from 'lodash/get'; | ||
import isObject from './isObject'; | ||
import get from 'lodash-es/get.js'; | ||
import isObject from './isObject.js'; | ||
import isNil from 'lodash-es/isNil.js'; | ||
/** Merges the `defaults` object of type `T` into the `formData` of type `T` | ||
@@ -12,3 +13,4 @@ * | ||
* - when the array is not set in form data, the default is copied over | ||
* - scalars are overwritten/set by form data | ||
* - scalars are overwritten/set by form data unless undefined and there is a default AND `defaultSupercedesUndefined` | ||
* is true | ||
* | ||
@@ -18,10 +20,19 @@ * @param [defaults] - The defaults to merge | ||
* @param [mergeExtraArrayDefaults=false] - If true, any additional default array entries are appended onto the formData | ||
* @param [defaultSupercedesUndefined=false] - If true, an explicit undefined value will be overwritten by the default value | ||
* @param [overrideFormDataWithDefaults=false] - If true, the default value will overwrite the form data value. If the value | ||
* doesn't exist in the default, we take it from formData and in the case where the value is set to undefined in formData. | ||
* This is useful when we have already merged formData with defaults and want to add an additional field from formData | ||
* that does not exist in defaults. | ||
* @returns - The resulting merged form data with defaults | ||
*/ | ||
export default function mergeDefaultsWithFormData(defaults, formData, mergeExtraArrayDefaults = false) { | ||
export default function mergeDefaultsWithFormData(defaults, formData, mergeExtraArrayDefaults = false, defaultSupercedesUndefined = false, overrideFormDataWithDefaults = false) { | ||
if (Array.isArray(formData)) { | ||
const defaultsArray = Array.isArray(defaults) ? defaults : []; | ||
const mapped = formData.map((value, idx) => { | ||
if (defaultsArray[idx]) { | ||
return mergeDefaultsWithFormData(defaultsArray[idx], value, mergeExtraArrayDefaults); | ||
// If overrideFormDataWithDefaults is true, we want to override the formData with the defaults | ||
const overrideArray = overrideFormDataWithDefaults ? defaultsArray : formData; | ||
const overrideOppositeArray = overrideFormDataWithDefaults ? formData : defaultsArray; | ||
const mapped = overrideArray.map((value, idx) => { | ||
// We want to explicitly make sure that the value is NOT undefined since null, 0 and empty space are valid values | ||
if (overrideOppositeArray[idx] !== undefined) { | ||
return mergeDefaultsWithFormData(defaultsArray[idx], formData[idx], mergeExtraArrayDefaults, defaultSupercedesUndefined, overrideFormDataWithDefaults); | ||
} | ||
@@ -31,4 +42,5 @@ return value; | ||
// Merge any extra defaults when mergeExtraArrayDefaults is true | ||
if (mergeExtraArrayDefaults && mapped.length < defaultsArray.length) { | ||
mapped.push(...defaultsArray.slice(mapped.length)); | ||
// Or when overrideFormDataWithDefaults is true and the default array is shorter than the formData array | ||
if ((mergeExtraArrayDefaults || overrideFormDataWithDefaults) && mapped.length < overrideOppositeArray.length) { | ||
mapped.push(...overrideOppositeArray.slice(mapped.length)); | ||
} | ||
@@ -40,8 +52,25 @@ return mapped; | ||
return Object.keys(formData).reduce((acc, key) => { | ||
acc[key] = mergeDefaultsWithFormData(defaults ? get(defaults, key) : {}, get(formData, key), mergeExtraArrayDefaults); | ||
const keyValue = get(formData, key); | ||
const keyExistsInDefaults = isObject(defaults) && key in defaults; | ||
const keyExistsInFormData = key in formData; | ||
acc[key] = mergeDefaultsWithFormData(defaults ? get(defaults, key) : {}, keyValue, mergeExtraArrayDefaults, defaultSupercedesUndefined, | ||
// overrideFormDataWithDefaults can be true only when the key value exists in defaults | ||
// Or if the key value doesn't exist in formData | ||
overrideFormDataWithDefaults && (keyExistsInDefaults || !keyExistsInFormData)); | ||
return acc; | ||
}, acc); | ||
} | ||
/** | ||
* If the defaultSupercedesUndefined flag is true | ||
* And formData is set to undefined or null and defaults are defined | ||
* Or if formData is a number and is NaN return defaults | ||
* Or if overrideFormDataWithDefaults flag is true and formData is set to not undefined/null return defaults | ||
*/ | ||
if ((defaultSupercedesUndefined && | ||
((!isNil(defaults) && isNil(formData)) || (typeof formData === 'number' && isNaN(formData)))) || | ||
(overrideFormDataWithDefaults && !isNil(formData))) { | ||
return defaults; | ||
} | ||
return formData; | ||
} | ||
//# sourceMappingURL=mergeDefaultsWithFormData.js.map |
@@ -1,2 +0,2 @@ | ||
import { GenericObjectType } from './types'; | ||
import { GenericObjectType } from './types.js'; | ||
/** Recursively merge deeply nested objects. | ||
@@ -3,0 +3,0 @@ * |
@@ -1,2 +0,2 @@ | ||
import isObject from './isObject'; | ||
import isObject from './isObject.js'; | ||
/** Recursively merge deeply nested objects. | ||
@@ -3,0 +3,0 @@ * |
@@ -1,2 +0,2 @@ | ||
import { GenericObjectType } from './types'; | ||
import { GenericObjectType } from './types.js'; | ||
/** Recursively merge deeply nested schemas. The difference between `mergeSchemas` and `mergeObjects` is that | ||
@@ -3,0 +3,0 @@ * `mergeSchemas` only concats arrays for values under the 'required' keyword, and when it does, it doesn't include |
@@ -1,5 +0,5 @@ | ||
import union from 'lodash/union'; | ||
import { REQUIRED_KEY } from './constants'; | ||
import getSchemaType from './getSchemaType'; | ||
import isObject from './isObject'; | ||
import union from 'lodash-es/union.js'; | ||
import { REQUIRED_KEY } from './constants.js'; | ||
import getSchemaType from './getSchemaType.js'; | ||
import isObject from './isObject.js'; | ||
/** Recursively merge deeply nested schemas. The difference between `mergeSchemas` and `mergeObjects` is that | ||
@@ -6,0 +6,0 @@ * `mergeSchemas` only concats arrays for values under the 'required' keyword, and when it does, it doesn't include |
@@ -1,9 +0,11 @@ | ||
import { RJSFSchema, EnumOptionsType, StrictRJSFSchema, FormContextType, UiSchema } from './types'; | ||
import { RJSFSchema, EnumOptionsType, StrictRJSFSchema, FormContextType, UiSchema } from './types.js'; | ||
/** Gets the list of options from the `schema`. If the schema has an enum list, then those enum values are returned. The | ||
* labels for the options will be extracted from the non-standard, RJSF-deprecated `enumNames` if it exists, otherwise | ||
* the label will be the same as the `value`. If the schema has a `oneOf` or `anyOf`, then the value is the list of | ||
* `const` values from the schema and the label is either the `schema.title` or the value. If a `uiSchema` is provided | ||
* and it has the `ui:enumNames` matched with `enum` or it has an associated `oneOf` or `anyOf` with a list of objects | ||
* containing `ui:title` then the UI schema values will replace the values from the schema. | ||
* label will be the same as the `value`. | ||
* | ||
* If the schema has a `oneOf` or `anyOf`, then the value is the list of either: | ||
* - The `const` values from the schema if present | ||
* - If the schema has a discriminator and the label using either the `schema.title` or the value. If a `uiSchema` is | ||
* provided, and it has the `ui:enumNames` matched with `enum` or it has an associated `oneOf` or `anyOf` with a list of | ||
* objects containing `ui:title` then the UI schema values will replace the values from the schema. | ||
* | ||
* @param schema - The schema from which to extract the options list | ||
@@ -13,2 +15,2 @@ * @param [uiSchema] - The optional uiSchema from which to get alternate labels for the options | ||
*/ | ||
export default function optionsList<S extends StrictRJSFSchema = RJSFSchema, T = any, F extends FormContextType = any>(schema: S, uiSchema?: UiSchema<T, S, F>): EnumOptionsType<S>[] | undefined; | ||
export default function optionsList<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(schema: S, uiSchema?: UiSchema<T, S, F>): EnumOptionsType<S>[] | undefined; |
@@ -1,10 +0,15 @@ | ||
import toConstant from './toConstant'; | ||
import getUiOptions from './getUiOptions'; | ||
import get from 'lodash-es/get.js'; | ||
import { CONST_KEY, DEFAULT_KEY, PROPERTIES_KEY } from './constants.js'; | ||
import getDiscriminatorFieldFromSchema from './getDiscriminatorFieldFromSchema.js'; | ||
import getUiOptions from './getUiOptions.js'; | ||
import toConstant from './toConstant.js'; | ||
/** Gets the list of options from the `schema`. If the schema has an enum list, then those enum values are returned. The | ||
* labels for the options will be extracted from the non-standard, RJSF-deprecated `enumNames` if it exists, otherwise | ||
* the label will be the same as the `value`. If the schema has a `oneOf` or `anyOf`, then the value is the list of | ||
* `const` values from the schema and the label is either the `schema.title` or the value. If a `uiSchema` is provided | ||
* and it has the `ui:enumNames` matched with `enum` or it has an associated `oneOf` or `anyOf` with a list of objects | ||
* containing `ui:title` then the UI schema values will replace the values from the schema. | ||
* label will be the same as the `value`. | ||
* | ||
* If the schema has a `oneOf` or `anyOf`, then the value is the list of either: | ||
* - The `const` values from the schema if present | ||
* - If the schema has a discriminator and the label using either the `schema.title` or the value. If a `uiSchema` is | ||
* provided, and it has the `ui:enumNames` matched with `enum` or it has an associated `oneOf` or `anyOf` with a list of | ||
* objects containing `ui:title` then the UI schema values will replace the values from the schema. | ||
* | ||
* @param schema - The schema from which to extract the options list | ||
@@ -15,4 +20,2 @@ * @param [uiSchema] - The optional uiSchema from which to get alternate labels for the options | ||
export default function optionsList(schema, uiSchema) { | ||
// TODO flip generics to move T first in v6 | ||
const schemaWithEnumNames = schema; | ||
if (schema.enum) { | ||
@@ -24,10 +27,2 @@ let enumNames; | ||
} | ||
if (!enumNames && schemaWithEnumNames.enumNames) { | ||
// enumNames was deprecated in v5 and is intentionally omitted from the RJSFSchema type. | ||
// Cast the type to include enumNames so the feature still works. | ||
if (process.env.NODE_ENV !== 'production') { | ||
console.warn('The "enumNames" property in the schema is deprecated and will be removed in a future major release. Use the "ui:enumNames" property in the uiSchema instead.'); | ||
} | ||
enumNames = schemaWithEnumNames.enumNames; | ||
} | ||
return schema.enum.map((value, i) => { | ||
@@ -48,2 +43,9 @@ const label = (enumNames === null || enumNames === void 0 ? void 0 : enumNames[i]) || String(value); | ||
} | ||
// See if there is a discriminator path specified in the schema, and if so, use it as the selectorField, otherwise | ||
// pull one from the uiSchema | ||
let selectorField = getDiscriminatorFieldFromSchema(schema); | ||
if (uiSchema) { | ||
const { optionsSchemaSelector = selectorField } = getUiOptions(uiSchema); | ||
selectorField = optionsSchemaSelector; | ||
} | ||
return (altSchemas && | ||
@@ -53,4 +55,13 @@ altSchemas.map((aSchemaDef, index) => { | ||
const aSchema = aSchemaDef; | ||
const value = toConstant(aSchema); | ||
const label = title || aSchema.title || String(value); | ||
let value; | ||
let label = title; | ||
if (selectorField) { | ||
const innerSchema = get(aSchema, [PROPERTIES_KEY, selectorField], {}); | ||
value = get(innerSchema, DEFAULT_KEY, get(innerSchema, CONST_KEY)); | ||
label = label || (innerSchema === null || innerSchema === void 0 ? void 0 : innerSchema.title) || aSchema.title || String(value); | ||
} | ||
else { | ||
value = toConstant(aSchema); | ||
label = label || aSchema.title || String(value); | ||
} | ||
return { | ||
@@ -57,0 +68,0 @@ schema: aSchema, |
@@ -1,2 +0,2 @@ | ||
import { DateObject } from './types'; | ||
import { DateObject } from './types.js'; | ||
/** Parses the `dateString` into a `DateObject`, including the time information when `includeTime` is true | ||
@@ -3,0 +3,0 @@ * |
@@ -25,3 +25,3 @@ /** Parses the `dateString` into a `DateObject`, including the time information when `includeTime` is true | ||
year: date.getUTCFullYear(), | ||
month: date.getUTCMonth() + 1, | ||
month: date.getUTCMonth() + 1, // oh you, javascript. | ||
day: date.getUTCDate(), | ||
@@ -28,0 +28,0 @@ hour: includeTime ? date.getUTCHours() : 0, |
@@ -1,4 +0,4 @@ | ||
import schemaParser from './schemaParser'; | ||
import { SchemaMap } from './ParserValidator'; | ||
import schemaParser from './schemaParser.js'; | ||
import { SchemaMap } from './ParserValidator.js'; | ||
export type { SchemaMap }; | ||
export { schemaParser }; |
@@ -1,3 +0,3 @@ | ||
import schemaParser from './schemaParser'; | ||
import schemaParser from './schemaParser.js'; | ||
export { schemaParser }; | ||
//# sourceMappingURL=index.js.map |
@@ -1,2 +0,2 @@ | ||
import { CustomValidator, ErrorSchema, ErrorTransformer, FormContextType, RJSFSchema, RJSFValidationError, StrictRJSFSchema, UiSchema, ValidationData, ValidatorType } from '../types'; | ||
import { CustomValidator, ErrorSchema, ErrorTransformer, FormContextType, RJSFSchema, RJSFValidationError, StrictRJSFSchema, UiSchema, ValidationData, ValidatorType } from '../types.js'; | ||
/** The type of the map of schema hash to schema | ||
@@ -3,0 +3,0 @@ */ |
@@ -1,5 +0,5 @@ | ||
import get from 'lodash/get'; | ||
import isEqual from 'lodash/isEqual'; | ||
import { ID_KEY } from '../constants'; | ||
import hashForSchema from '../hashForSchema'; | ||
import get from 'lodash-es/get.js'; | ||
import { ID_KEY } from '../constants.js'; | ||
import hashForSchema from '../hashForSchema.js'; | ||
import deepEquals from '../deepEquals.js'; | ||
/** An implementation of the `ValidatorType` interface that is designed for use in capturing schemas used by the | ||
@@ -43,3 +43,3 @@ * `isValid()` function. The rest of the implementation of the interface throws errors when it is attempted to be used. | ||
} | ||
else if (!isEqual(existing, identifiedSchema)) { | ||
else if (!deepEquals(existing, identifiedSchema)) { | ||
console.error('existing schema:', JSON.stringify(existing, null, 2)); | ||
@@ -64,3 +64,3 @@ console.error('new schema:', JSON.stringify(identifiedSchema, null, 2)); | ||
isValid(schema, _formData, rootSchema) { | ||
if (!isEqual(rootSchema, this.rootSchema)) { | ||
if (!deepEquals(rootSchema, this.rootSchema)) { | ||
throw new Error('Unexpectedly calling isValid() with a rootSchema that differs from the construction rootSchema'); | ||
@@ -67,0 +67,0 @@ } |
@@ -1,3 +0,3 @@ | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema } from '../types'; | ||
import { SchemaMap } from './ParserValidator'; | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema } from '../types.js'; | ||
import { SchemaMap } from './ParserValidator.js'; | ||
/** Parses the given `rootSchema` to extract out all the sub-schemas that maybe contained within it. Returns a map of | ||
@@ -4,0 +4,0 @@ * the hash of the schema to schema/sub-schema. |
@@ -1,6 +0,6 @@ | ||
import forEach from 'lodash/forEach'; | ||
import isEqual from 'lodash/isEqual'; | ||
import { PROPERTIES_KEY, ITEMS_KEY } from '../constants'; | ||
import ParserValidator from './ParserValidator'; | ||
import { retrieveSchemaInternal, resolveAnyOrOneOfSchemas } from '../schema/retrieveSchema'; | ||
import forEach from 'lodash-es/forEach.js'; | ||
import { ITEMS_KEY, PROPERTIES_KEY } from '../constants.js'; | ||
import ParserValidator from './ParserValidator.js'; | ||
import { resolveAnyOrOneOfSchemas, retrieveSchemaInternal } from '../schema/retrieveSchema.js'; | ||
import deepEquals from '../deepEquals.js'; | ||
/** Recursive function used to parse the given `schema` belonging to the `rootSchema`. The `validator` is used to | ||
@@ -19,3 +19,3 @@ * capture the sub-schemas that the `isValid()` function is called with. For each schema returned by the | ||
schemas.forEach((schema) => { | ||
const sameSchemaIndex = recurseList.findIndex((item) => isEqual(item, schema)); | ||
const sameSchemaIndex = recurseList.findIndex((item) => deepEquals(item, schema)); | ||
if (sameSchemaIndex === -1) { | ||
@@ -22,0 +22,0 @@ recurseList.push(schema); |
@@ -1,3 +0,3 @@ | ||
import { RangeSpecType, StrictRJSFSchema } from './types'; | ||
import { RJSFSchema } from './types'; | ||
import { RangeSpecType, StrictRJSFSchema } from './types.js'; | ||
import { RJSFSchema } from './types.js'; | ||
/** Extracts the range spec information `{ step?: number, min?: number, max?: number }` that can be spread onto an HTML | ||
@@ -4,0 +4,0 @@ * input from the range analog in the schema `{ multipleOf?: number, minimum?: number, maximum?: number }`. |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import { Experimental_CustomMergeAllOf, FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types.js'; | ||
/** A junk option used to determine when the getFirstMatchingOption call really matches an option rather than returning | ||
@@ -24,5 +24,6 @@ * the first item | ||
* @param formData - The form data associated with the schema, used to calculate the score | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The score a schema against the formData | ||
*/ | ||
export declare function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rootSchema: S, schema?: S, formData?: any): number; | ||
export declare function calculateIndexScore<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rootSchema: S, schema?: S, formData?: any, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): number; | ||
/** Determines which of the given `options` provided most closely matches the `formData`. Using | ||
@@ -48,4 +49,5 @@ * `getFirstMatchingOption()` to match two schemas that differ only by the readOnly, default or const value of a field | ||
* determine which option is selected | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The index of the option that is the closest match to the `formData` or the `selectedOption` if no match | ||
*/ | ||
export default function getClosestMatchingOption<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rootSchema: S, formData: T | undefined, options: S[], selectedOption?: number, discriminatorField?: string): number; | ||
export default function getClosestMatchingOption<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rootSchema: S, formData: T | undefined, options: S[], selectedOption?: number, discriminatorField?: string, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): number; |
@@ -1,14 +0,14 @@ | ||
import get from 'lodash/get'; | ||
import has from 'lodash/has'; | ||
import isNumber from 'lodash/isNumber'; | ||
import isObject from 'lodash/isObject'; | ||
import isString from 'lodash/isString'; | ||
import reduce from 'lodash/reduce'; | ||
import times from 'lodash/times'; | ||
import getFirstMatchingOption from './getFirstMatchingOption'; | ||
import retrieveSchema, { resolveAllReferences } from './retrieveSchema'; | ||
import { ONE_OF_KEY, REF_KEY, JUNK_OPTION_ID, ANY_OF_KEY } from '../constants'; | ||
import guessType from '../guessType'; | ||
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema'; | ||
import getOptionMatchingSimpleDiscriminator from '../getOptionMatchingSimpleDiscriminator'; | ||
import get from 'lodash-es/get.js'; | ||
import has from 'lodash-es/has.js'; | ||
import isNumber from 'lodash-es/isNumber.js'; | ||
import isObject from 'lodash-es/isObject.js'; | ||
import isString from 'lodash-es/isString.js'; | ||
import reduce from 'lodash-es/reduce.js'; | ||
import times from 'lodash-es/times.js'; | ||
import getFirstMatchingOption from './getFirstMatchingOption.js'; | ||
import retrieveSchema, { resolveAllReferences } from './retrieveSchema.js'; | ||
import { ONE_OF_KEY, REF_KEY, JUNK_OPTION_ID, ANY_OF_KEY } from '../constants.js'; | ||
import guessType from '../guessType.js'; | ||
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema.js'; | ||
import getOptionMatchingSimpleDiscriminator from '../getOptionMatchingSimpleDiscriminator.js'; | ||
/** A junk option used to determine when the getFirstMatchingOption call really matches an option rather than returning | ||
@@ -44,5 +44,6 @@ * the first item | ||
* @param formData - The form data associated with the schema, used to calculate the score | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The score a schema against the formData | ||
*/ | ||
export function calculateIndexScore(validator, rootSchema, schema, formData = {}) { | ||
export function calculateIndexScore(validator, rootSchema, schema, formData, experimental_customMergeAllOf) { | ||
let totalScore = 0; | ||
@@ -57,4 +58,5 @@ if (schema) { | ||
if (has(value, REF_KEY)) { | ||
const newSchema = retrieveSchema(validator, value, rootSchema, formValue); | ||
return score + calculateIndexScore(validator, rootSchema, newSchema, formValue || {}); | ||
const newSchema = retrieveSchema(validator, value, rootSchema, formValue, experimental_customMergeAllOf); | ||
return (score + | ||
calculateIndexScore(validator, rootSchema, newSchema, formValue || {}, experimental_customMergeAllOf)); | ||
} | ||
@@ -65,6 +67,11 @@ if ((has(value, ONE_OF_KEY) || has(value, ANY_OF_KEY)) && formValue) { | ||
return (score + | ||
getClosestMatchingOption(validator, rootSchema, formValue, get(value, key), -1, discriminator)); | ||
getClosestMatchingOption(validator, rootSchema, formValue, get(value, key), -1, discriminator, experimental_customMergeAllOf)); | ||
} | ||
if (value.type === 'object') { | ||
return score + calculateIndexScore(validator, rootSchema, value, formValue || {}); | ||
if (isObject(formValue)) { | ||
// If the structure is matching then give it a little boost in score | ||
score += 1; | ||
} | ||
return (score + | ||
calculateIndexScore(validator, rootSchema, value, formValue, experimental_customMergeAllOf)); | ||
} | ||
@@ -116,5 +123,6 @@ if (value.type === guessType(formValue)) { | ||
* determine which option is selected | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The index of the option that is the closest match to the `formData` or the `selectedOption` if no match | ||
*/ | ||
export default function getClosestMatchingOption(validator, rootSchema, formData, options, selectedOption = -1, discriminatorField) { | ||
export default function getClosestMatchingOption(validator, rootSchema, formData, options, selectedOption = -1, discriminatorField, experimental_customMergeAllOf) { | ||
// First resolve any refs in the options | ||
@@ -151,3 +159,3 @@ const resolvedOptions = options.map((option) => { | ||
const option = resolvedOptions[index]; | ||
const score = calculateIndexScore(validator, rootSchema, option, formData); | ||
const score = calculateIndexScore(validator, rootSchema, option, formData, experimental_customMergeAllOf); | ||
scoreCount.add(score); | ||
@@ -154,0 +162,0 @@ if (score > bestScore) { |
@@ -1,2 +0,2 @@ | ||
import { Experimental_DefaultFormStateBehavior, FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import { Experimental_CustomMergeAllOf, Experimental_DefaultFormStateBehavior, FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types.js'; | ||
/** Enum that indicates how `schema.additionalItems` should be handled by the `getInnerSchemaForArrayItem()` function. | ||
@@ -26,9 +26,25 @@ */ | ||
interface ComputeDefaultsProps<T = any, S extends StrictRJSFSchema = RJSFSchema> { | ||
/** Any defaults provided by the parent field in the schema */ | ||
parentDefaults?: T; | ||
/** The options root schema, used to primarily to look up `$ref`s */ | ||
rootSchema?: S; | ||
/** The current formData, if any, onto which to provide any missing defaults */ | ||
rawFormData?: T; | ||
/** 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. | ||
*/ | ||
includeUndefinedValues?: boolean | 'excludeObjectChildren'; | ||
/** The list of ref names currently being recursed, used to prevent infinite recursion */ | ||
_recurseList?: string[]; | ||
/** Optional configuration object, if provided, allows users to override default form state behavior */ | ||
experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior; | ||
/** Optional function that allows for custom merging of `allOf` schemas */ | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>; | ||
/** Optional flag, if true, indicates this schema was required in the parent schema. */ | ||
required?: boolean; | ||
/** Optional flag, if true, It will merge defaults into formData. | ||
* The formData should take precedence unless it's not valid. This is useful 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. | ||
*/ | ||
shouldMergeDefaultsIntoFormData?: boolean; | ||
} | ||
@@ -40,15 +56,45 @@ /** Computes the defaults for the current `schema` given the `rawFormData` and `parentDefaults` if any. This drills into | ||
* @param rawSchema - The schema for which the default state is desired | ||
* @param [props] - Optional props for this function | ||
* @param [props.parentDefaults] - Any defaults provided by the parent field in the schema | ||
* @param [props.rootSchema] - The options root schema, used to primarily to look up `$ref`s | ||
* @param [props.rawFormData] - The current formData, if any, onto which to provide any missing defaults | ||
* @param [props.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 [props._recurseList=[]] - The list of ref names currently being recursed, used to prevent infinite recursion | ||
* @param [props.experimental_defaultFormStateBehavior] Optional configuration object, if provided, allows users to override default form state behavior | ||
* @param [props.required] - Optional flag, if true, indicates this schema was required in the parent schema. | ||
* @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function | ||
* @returns - The resulting `formData` with all the defaults provided | ||
*/ | ||
export declare function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rawSchema: S, { parentDefaults, rawFormData, rootSchema, includeUndefinedValues, _recurseList, experimental_defaultFormStateBehavior, required, }?: ComputeDefaultsProps<T, S>): T | T[] | undefined; | ||
export declare function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rawSchema: S, computeDefaultsProps?: ComputeDefaultsProps<T, S>): T | T[] | undefined; | ||
/** | ||
* 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 declare function ensureFormDataMatchingSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S, formData: T | undefined, experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): T | T[] | undefined; | ||
/** 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 declare function getObjectDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rawSchema: S, { rawFormData, rootSchema, includeUndefinedValues, _recurseList, experimental_defaultFormStateBehavior, experimental_customMergeAllOf, required, shouldMergeDefaultsIntoFormData, }?: ComputeDefaultsProps<T, S>, defaults?: T | T[] | undefined): T; | ||
/** 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 declare function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rawSchema: S, { rawFormData, rootSchema, _recurseList, experimental_defaultFormStateBehavior, experimental_customMergeAllOf, required, shouldMergeDefaultsIntoFormData, }?: ComputeDefaultsProps<T, S>, defaults?: T | T[] | undefined): T | T[] | undefined; | ||
/** 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 declare function getDefaultBasedOnSchemaType<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rawSchema: S, computeDefaultsProps?: ComputeDefaultsProps<T, S>, defaults?: T | T[] | undefined): T | T[] | void; | ||
/** Returns the superset of `formData` that includes the given set updated to include any missing fields that have | ||
@@ -65,5 +111,6 @@ * computed to have defaults provided in the `schema`. | ||
* @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<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, theSchema: S, formData?: T, rootSchema?: S, includeUndefinedValues?: boolean | 'excludeObjectChildren', experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior): T | T[] | undefined; | ||
export default function getDefaultFormState<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, theSchema: S, formData?: T, rootSchema?: S, includeUndefinedValues?: boolean | 'excludeObjectChildren', experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): T | T[] | undefined; | ||
export {}; |
@@ -1,15 +0,21 @@ | ||
import get from 'lodash/get'; | ||
import isEmpty from 'lodash/isEmpty'; | ||
import { ANY_OF_KEY, DEFAULT_KEY, DEPENDENCIES_KEY, PROPERTIES_KEY, ONE_OF_KEY, REF_KEY, ALL_OF_KEY, } from '../constants'; | ||
import findSchemaDefinition from '../findSchemaDefinition'; | ||
import getClosestMatchingOption from './getClosestMatchingOption'; | ||
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema'; | ||
import getSchemaType from '../getSchemaType'; | ||
import isObject from '../isObject'; | ||
import isFixedItems from '../isFixedItems'; | ||
import mergeDefaultsWithFormData from '../mergeDefaultsWithFormData'; | ||
import mergeObjects from '../mergeObjects'; | ||
import mergeSchemas from '../mergeSchemas'; | ||
import isMultiSelect from './isMultiSelect'; | ||
import retrieveSchema, { resolveDependencies } from './retrieveSchema'; | ||
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. | ||
@@ -73,13 +79,16 @@ */ | ||
* 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 = {}) { | ||
function maybeAddDefaultToObject(obj, key, computedDefault, includeUndefinedValues, isParentRequired, requiredFields = [], experimental_defaultFormStateBehavior = {}, isConst = false) { | ||
const { emptyObjectFields = 'populateAllDefaults' } = experimental_defaultFormStateBehavior; | ||
if (includeUndefinedValues) { | ||
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 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 emptyObjectFields 'skipEmptyDefaults' store computedDefault if it's a non-empty object(e.g. not {}) | ||
@@ -90,4 +99,3 @@ if (emptyObjectFields === 'skipEmptyDefaults') { | ||
} | ||
} | ||
// Else store computedDefault if it's a non-empty object(e.g. not {}) and satisfies certain conditions | ||
} // 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 | ||
@@ -103,7 +111,8 @@ // Condition 2: If the parent object is required or emptyObjectFields is not 'populateRequiredDefaults' | ||
// Condition 1: computedDefault is not undefined | ||
// Condition 2: If emptyObjectFields is 'populateAllDefaults' or 'skipEmptyDefaults) or if the key is a required field | ||
// 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' || | ||
requiredFields.includes(key))) { | ||
(isSelfOrParentRequired && requiredFields.includes(key)))) { | ||
obj[key] = computedDefault; | ||
@@ -118,16 +127,7 @@ } | ||
* @param rawSchema - The schema for which the default state is desired | ||
* @param [props] - Optional props for this function | ||
* @param [props.parentDefaults] - Any defaults provided by the parent field in the schema | ||
* @param [props.rootSchema] - The options root schema, used to primarily to look up `$ref`s | ||
* @param [props.rawFormData] - The current formData, if any, onto which to provide any missing defaults | ||
* @param [props.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 [props._recurseList=[]] - The list of ref names currently being recursed, used to prevent infinite recursion | ||
* @param [props.experimental_defaultFormStateBehavior] Optional configuration object, if provided, allows users to override default form state behavior | ||
* @param [props.required] - Optional flag, if true, indicates this schema was required in the parent schema. | ||
* @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function | ||
* @returns - The resulting `formData` with all the defaults provided | ||
*/ | ||
export function computeDefaults(validator, rawSchema, { parentDefaults, rawFormData, rootSchema = {}, includeUndefinedValues = false, _recurseList = [], experimental_defaultFormStateBehavior = undefined, required, } = {}) { | ||
var _a, _b, _c, _d; | ||
export function computeDefaults(validator, rawSchema, computeDefaultsProps = {}) { | ||
const { parentDefaults, rawFormData, rootSchema = {}, includeUndefinedValues = false, _recurseList = [], experimental_defaultFormStateBehavior = undefined, experimental_customMergeAllOf = undefined, required, shouldMergeDefaultsIntoFormData = false, } = computeDefaultsProps; | ||
const formData = (isObject(rawFormData) ? rawFormData : {}); | ||
@@ -139,4 +139,10 @@ const schema = isObject(rawSchema) ? rawSchema : {}; | ||
let schemaToCompute = null; | ||
let experimental_dfsb_to_compute = experimental_defaultFormStateBehavior; | ||
let updatedRecurseList = _recurseList; | ||
if (isObject(defaults) && isObject(schema.default)) { | ||
if (schema[CONST_KEY] && | ||
(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 | ||
@@ -146,3 +152,5 @@ // schema.default. | ||
} | ||
else if (DEFAULT_KEY in schema) { | ||
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; | ||
@@ -159,3 +167,8 @@ } | ||
else if (DEPENDENCIES_KEY in schema) { | ||
const resolvedSchema = resolveDependencies(validator, schema, rootSchema, false, [], formData); | ||
// 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 | ||
@@ -169,5 +182,7 @@ } | ||
experimental_defaultFormStateBehavior, | ||
experimental_customMergeAllOf, | ||
parentDefaults: Array.isArray(parentDefaults) ? parentDefaults[idx] : undefined, | ||
rawFormData: formData, | ||
required, | ||
shouldMergeDefaultsIntoFormData, | ||
})); | ||
@@ -181,3 +196,13 @@ } | ||
const discriminator = getDiscriminatorFieldFromSchema(schema); | ||
schemaToCompute = oneOf[getClosestMatchingOption(validator, rootSchema, isEmpty(formData) ? undefined : formData, oneOf, 0, discriminator)]; | ||
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); | ||
@@ -191,3 +216,3 @@ } | ||
const discriminator = getDiscriminatorFieldFromSchema(schema); | ||
schemaToCompute = anyOf[getClosestMatchingOption(validator, rootSchema, isEmpty(formData) ? undefined : formData, anyOf, 0, discriminator)]; | ||
schemaToCompute = anyOf[getClosestMatchingOption(validator, rootSchema, rawFormData !== null && rawFormData !== void 0 ? rawFormData : schema.default, anyOf, 0, discriminator, experimental_customMergeAllOf)]; | ||
schemaToCompute = mergeSchemas(remaining, schemaToCompute); | ||
@@ -200,6 +225,8 @@ } | ||
_recurseList: updatedRecurseList, | ||
experimental_defaultFormStateBehavior, | ||
experimental_defaultFormStateBehavior: experimental_dfsb_to_compute, | ||
experimental_customMergeAllOf, | ||
parentDefaults: defaults, | ||
rawFormData: formData, | ||
required, | ||
shouldMergeDefaultsIntoFormData, | ||
}); | ||
@@ -211,18 +238,111 @@ } | ||
} | ||
switch (getSchemaType(schema)) { | ||
// We need to recurse for object schema inner default values. | ||
case 'object': { | ||
// 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) | ||
: schema; | ||
const objectDefaults = Object.keys(retrievedSchema.properties || {}).reduce((acc, key) => { | ||
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; | ||
// Compute the defaults for this node, with the parent defaults we might | ||
// have from a previous run: defaults[key]. | ||
const computedDefault = computeDefaults(validator, get(retrievedSchema, [PROPERTIES_KEY, key]), { | ||
const computedDefault = computeDefaults(validator, additionalPropertiesSchema, { | ||
rootSchema, | ||
_recurseList, | ||
experimental_defaultFormStateBehavior, | ||
experimental_customMergeAllOf, | ||
includeUndefinedValues: includeUndefinedValues === true, | ||
@@ -232,111 +352,125 @@ parentDefaults: get(defaults, [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); | ||
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, | ||
includeUndefinedValues: includeUndefinedValues === true, | ||
parentDefaults: get(defaults, [key]), | ||
rawFormData: get(formData, [key]), | ||
required: (_a = retrievedSchema.required) === null || _a === void 0 ? void 0 : _a.includes(key), | ||
}); | ||
// Since these are additional properties we don't need to add the `experimental_defaultFormStateBehavior` prop | ||
maybeAddDefaultToObject(objectDefaults, key, computedDefault, includeUndefinedValues, required, formDataRequired); | ||
}); | ||
} | ||
return objectDefaults; | ||
// Since these are additional properties we don't need to add the `experimental_defaultFormStateBehavior` prop | ||
maybeAddDefaultToObject(objectDefaults, key, computedDefault, includeUndefinedValues, required, formDataRequired); | ||
}); | ||
} | ||
case 'array': { | ||
const neverPopulate = ((_a = experimental_defaultFormStateBehavior === null || experimental_defaultFormStateBehavior === void 0 ? void 0 : experimental_defaultFormStateBehavior.arrayMinItems) === null || _a === void 0 ? void 0 : _a.populate) === 'never'; | ||
const ignoreMinItemsFlagSet = ((_b = experimental_defaultFormStateBehavior === null || experimental_defaultFormStateBehavior === void 0 ? void 0 : experimental_defaultFormStateBehavior.arrayMinItems) === null || _b === void 0 ? void 0 : _b.populate) === 'requiredOnly'; | ||
const isSkipEmptyDefaults = (experimental_defaultFormStateBehavior === null || experimental_defaultFormStateBehavior === void 0 ? void 0 : experimental_defaultFormStateBehavior.emptyObjectFields) === 'skipEmptyDefaults'; | ||
const computeSkipPopulate = (_d = (_c = experimental_defaultFormStateBehavior === null || experimental_defaultFormStateBehavior === void 0 ? void 0 : experimental_defaultFormStateBehavior.arrayMinItems) === null || _c === void 0 ? void 0 : _c.computeSkipPopulate) !== null && _d !== void 0 ? _d : (() => false); | ||
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, | ||
parentDefaults: item, | ||
required, | ||
}); | ||
}); | ||
} | ||
// Deeply inject defaults into already existing form data | ||
if (Array.isArray(rawFormData)) { | ||
const schemaItem = getInnerSchemaForArrayItem(schema); | ||
if (neverPopulate) { | ||
defaults = rawFormData; | ||
} | ||
else { | ||
defaults = rawFormData.map((item, idx) => { | ||
return computeDefaults(validator, schemaItem, { | ||
rootSchema, | ||
_recurseList, | ||
experimental_defaultFormStateBehavior, | ||
rawFormData: item, | ||
parentDefaults: get(defaults, [idx]), | ||
required, | ||
}); | ||
}); | ||
} | ||
} | ||
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) || | ||
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, | ||
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, | ||
})); | ||
// then fill up the rest with either the item default or empty, up to minItems | ||
return defaultEntries.concat(fillerEntries); | ||
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); | ||
} | ||
} | ||
return defaults; | ||
// 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 | ||
@@ -353,9 +487,13 @@ * computed to have defaults provided in the `schema`. | ||
* @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) { | ||
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); | ||
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, { | ||
@@ -365,17 +503,17 @@ rootSchema, | ||
experimental_defaultFormStateBehavior, | ||
experimental_customMergeAllOf, | ||
rawFormData: formData, | ||
shouldMergeDefaultsIntoFormData: true, | ||
}); | ||
if (formData === undefined || formData === null || (typeof formData === 'number' && isNaN(formData))) { | ||
// No form data? Use schema defaults. | ||
return defaults; | ||
// 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); | ||
return result; | ||
} | ||
const { mergeExtraDefaults } = (experimental_defaultFormStateBehavior === null || experimental_defaultFormStateBehavior === void 0 ? void 0 : experimental_defaultFormStateBehavior.arrayMinItems) || {}; | ||
if (isObject(formData)) { | ||
return mergeDefaultsWithFormData(defaults, formData, mergeExtraDefaults); | ||
} | ||
if (Array.isArray(formData)) { | ||
return mergeDefaultsWithFormData(defaults, formData, mergeExtraDefaults); | ||
} | ||
return formData; | ||
return defaults; | ||
} | ||
//# sourceMappingURL=getDefaultFormState.js.map |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, GlobalUISchemaOptions, RJSFSchema, StrictRJSFSchema, UiSchema, ValidatorType } from '../types'; | ||
import { FormContextType, GlobalUISchemaOptions, RJSFSchema, StrictRJSFSchema, UiSchema, ValidatorType, Experimental_CustomMergeAllOf } from '../types.js'; | ||
/** Determines whether the combination of `schema` and `uiSchema` properties indicates that the label for the `schema` | ||
@@ -10,4 +10,5 @@ * should be displayed in a UI. | ||
* @param [globalOptions={}] - The optional Global UI Schema from which to get any fallback `xxx` options | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - True if the label should be displayed or false if it should not | ||
*/ | ||
export default function getDisplayLabel<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, uiSchema?: UiSchema<T, S, F>, rootSchema?: S, globalOptions?: GlobalUISchemaOptions): boolean; | ||
export default function getDisplayLabel<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, uiSchema?: UiSchema<T, S, F>, rootSchema?: S, globalOptions?: GlobalUISchemaOptions, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): boolean; |
@@ -1,7 +0,7 @@ | ||
import { UI_FIELD_KEY, UI_WIDGET_KEY } from '../constants'; | ||
import getSchemaType from '../getSchemaType'; | ||
import getUiOptions from '../getUiOptions'; | ||
import isCustomWidget from '../isCustomWidget'; | ||
import isFilesArray from './isFilesArray'; | ||
import isMultiSelect from './isMultiSelect'; | ||
import { UI_FIELD_KEY, UI_WIDGET_KEY } from '../constants.js'; | ||
import getSchemaType from '../getSchemaType.js'; | ||
import getUiOptions from '../getUiOptions.js'; | ||
import isCustomWidget from '../isCustomWidget.js'; | ||
import isFilesArray from './isFilesArray.js'; | ||
import isMultiSelect from './isMultiSelect.js'; | ||
/** Determines whether the combination of `schema` and `uiSchema` properties indicates that the label for the `schema` | ||
@@ -15,5 +15,6 @@ * should be displayed in a UI. | ||
* @param [globalOptions={}] - The optional Global UI Schema from which to get any fallback `xxx` options | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - True if the label should be displayed or false if it should not | ||
*/ | ||
export default function getDisplayLabel(validator, schema, uiSchema = {}, rootSchema, globalOptions) { | ||
export default function getDisplayLabel(validator, schema, uiSchema = {}, rootSchema, globalOptions, experimental_customMergeAllOf) { | ||
const uiOptions = getUiOptions(uiSchema, globalOptions); | ||
@@ -25,4 +26,4 @@ const { label = true } = uiOptions; | ||
displayLabel = | ||
isMultiSelect(validator, schema, rootSchema) || | ||
isFilesArray(validator, schema, uiSchema, rootSchema) || | ||
isMultiSelect(validator, schema, rootSchema, experimental_customMergeAllOf) || | ||
isFilesArray(validator, schema, uiSchema, rootSchema, experimental_customMergeAllOf) || | ||
isCustomWidget(uiSchema); | ||
@@ -29,0 +30,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types.js'; | ||
/** Given the `formData` and list of `options`, attempts to find the index of the first option that matches the data. | ||
@@ -3,0 +3,0 @@ * Always returns the first option if there is nothing that matches. |
@@ -1,2 +0,6 @@ | ||
import getMatchingOption from './getMatchingOption'; | ||
import get from 'lodash-es/get.js'; | ||
import has from 'lodash-es/has.js'; | ||
import isNumber from 'lodash-es/isNumber.js'; | ||
import { PROPERTIES_KEY } from '../constants.js'; | ||
import getOptionMatchingSimpleDiscriminator from '../getOptionMatchingSimpleDiscriminator.js'; | ||
/** Given the `formData` and list of `options`, attempts to find the index of the first option that matches the data. | ||
@@ -14,4 +18,68 @@ * Always returns the first option if there is nothing that matches. | ||
export default function getFirstMatchingOption(validator, formData, options, rootSchema, discriminatorField) { | ||
return getMatchingOption(validator, formData, options, rootSchema, discriminatorField); | ||
// For performance, skip validating subschemas if formData is undefined. We just | ||
// want to get the first option in that case. | ||
if (formData === undefined) { | ||
return 0; | ||
} | ||
const simpleDiscriminatorMatch = getOptionMatchingSimpleDiscriminator(formData, options, discriminatorField); | ||
if (isNumber(simpleDiscriminatorMatch)) { | ||
return simpleDiscriminatorMatch; | ||
} | ||
for (let i = 0; i < options.length; i++) { | ||
const option = options[i]; | ||
// If we have a discriminator field, then we will use this to make the determination | ||
if (discriminatorField && has(option, [PROPERTIES_KEY, discriminatorField])) { | ||
const value = get(formData, discriminatorField); | ||
const discriminator = get(option, [PROPERTIES_KEY, discriminatorField], {}); | ||
if (validator.isValid(discriminator, value, rootSchema)) { | ||
return i; | ||
} | ||
} | ||
else if (option[PROPERTIES_KEY]) { | ||
// If the schema describes an object then we need to add slightly more | ||
// strict matching to the schema, because unless the schema uses the | ||
// "requires" keyword, an object will match the schema as long as it | ||
// doesn't have matching keys with a conflicting type. To do this we use an | ||
// "anyOf" with an array of requires. This augmentation expresses that the | ||
// schema should match if any of the keys in the schema are present on the | ||
// object and pass validation. | ||
// | ||
// Create an "anyOf" schema that requires at least one of the keys in the | ||
// "properties" object | ||
const requiresAnyOf = { | ||
anyOf: Object.keys(option[PROPERTIES_KEY]).map((key) => ({ | ||
required: [key], | ||
})), | ||
}; | ||
let augmentedSchema; | ||
// If the "anyOf" keyword already exists, wrap the augmentation in an "allOf" | ||
if (option.anyOf) { | ||
// Create a shallow clone of the option | ||
const { ...shallowClone } = option; | ||
if (!shallowClone.allOf) { | ||
shallowClone.allOf = []; | ||
} | ||
else { | ||
// If "allOf" already exists, shallow clone the array | ||
shallowClone.allOf = shallowClone.allOf.slice(); | ||
} | ||
shallowClone.allOf.push(requiresAnyOf); | ||
augmentedSchema = shallowClone; | ||
} | ||
else { | ||
augmentedSchema = Object.assign({}, option, requiresAnyOf); | ||
} | ||
// Remove the "required" field as it's likely that not all fields have | ||
// been filled in yet, which will mean that the schema is not valid | ||
delete augmentedSchema.required; | ||
if (validator.isValid(augmentedSchema, formData, rootSchema)) { | ||
return i; | ||
} | ||
} | ||
else if (validator.isValid(option, formData, rootSchema)) { | ||
return i; | ||
} | ||
} | ||
return 0; | ||
} | ||
//# sourceMappingURL=getFirstMatchingOption.js.map |
@@ -1,14 +0,15 @@ | ||
import getDefaultFormState from './getDefaultFormState'; | ||
import getDisplayLabel from './getDisplayLabel'; | ||
import getClosestMatchingOption from './getClosestMatchingOption'; | ||
import getFirstMatchingOption from './getFirstMatchingOption'; | ||
import getMatchingOption from './getMatchingOption'; | ||
import isFilesArray from './isFilesArray'; | ||
import isMultiSelect from './isMultiSelect'; | ||
import isSelect from './isSelect'; | ||
import mergeValidationData from './mergeValidationData'; | ||
import retrieveSchema from './retrieveSchema'; | ||
import sanitizeDataForNewSchema from './sanitizeDataForNewSchema'; | ||
import toIdSchema from './toIdSchema'; | ||
import toPathSchema from './toPathSchema'; | ||
export { getDefaultFormState, getDisplayLabel, getClosestMatchingOption, getFirstMatchingOption, getMatchingOption, isFilesArray, isMultiSelect, isSelect, mergeValidationData, retrieveSchema, sanitizeDataForNewSchema, toIdSchema, toPathSchema, }; | ||
import findFieldInSchema from './findFieldInSchema.js'; | ||
import findSelectedOptionInXxxOf from './findSelectedOptionInXxxOf.js'; | ||
import getDefaultFormState from './getDefaultFormState.js'; | ||
import getDisplayLabel from './getDisplayLabel.js'; | ||
import getClosestMatchingOption from './getClosestMatchingOption.js'; | ||
import getFirstMatchingOption from './getFirstMatchingOption.js'; | ||
import getFromSchema from './getFromSchema.js'; | ||
import isFilesArray from './isFilesArray.js'; | ||
import isMultiSelect from './isMultiSelect.js'; | ||
import isSelect from './isSelect.js'; | ||
import retrieveSchema from './retrieveSchema.js'; | ||
import sanitizeDataForNewSchema from './sanitizeDataForNewSchema.js'; | ||
import toIdSchema from './toIdSchema.js'; | ||
import toPathSchema from './toPathSchema.js'; | ||
export { findFieldInSchema, findSelectedOptionInXxxOf, getDefaultFormState, getDisplayLabel, getClosestMatchingOption, getFirstMatchingOption, getFromSchema, isFilesArray, isMultiSelect, isSelect, retrieveSchema, sanitizeDataForNewSchema, toIdSchema, toPathSchema, }; |
@@ -1,15 +0,16 @@ | ||
import getDefaultFormState from './getDefaultFormState'; | ||
import getDisplayLabel from './getDisplayLabel'; | ||
import getClosestMatchingOption from './getClosestMatchingOption'; | ||
import getFirstMatchingOption from './getFirstMatchingOption'; | ||
import getMatchingOption from './getMatchingOption'; | ||
import isFilesArray from './isFilesArray'; | ||
import isMultiSelect from './isMultiSelect'; | ||
import isSelect from './isSelect'; | ||
import mergeValidationData from './mergeValidationData'; | ||
import retrieveSchema from './retrieveSchema'; | ||
import sanitizeDataForNewSchema from './sanitizeDataForNewSchema'; | ||
import toIdSchema from './toIdSchema'; | ||
import toPathSchema from './toPathSchema'; | ||
export { getDefaultFormState, getDisplayLabel, getClosestMatchingOption, getFirstMatchingOption, getMatchingOption, isFilesArray, isMultiSelect, isSelect, mergeValidationData, retrieveSchema, sanitizeDataForNewSchema, toIdSchema, toPathSchema, }; | ||
import findFieldInSchema from './findFieldInSchema.js'; | ||
import findSelectedOptionInXxxOf from './findSelectedOptionInXxxOf.js'; | ||
import getDefaultFormState from './getDefaultFormState.js'; | ||
import getDisplayLabel from './getDisplayLabel.js'; | ||
import getClosestMatchingOption from './getClosestMatchingOption.js'; | ||
import getFirstMatchingOption from './getFirstMatchingOption.js'; | ||
import getFromSchema from './getFromSchema.js'; | ||
import isFilesArray from './isFilesArray.js'; | ||
import isMultiSelect from './isMultiSelect.js'; | ||
import isSelect from './isSelect.js'; | ||
import retrieveSchema from './retrieveSchema.js'; | ||
import sanitizeDataForNewSchema from './sanitizeDataForNewSchema.js'; | ||
import toIdSchema from './toIdSchema.js'; | ||
import toPathSchema from './toPathSchema.js'; | ||
export { findFieldInSchema, findSelectedOptionInXxxOf, getDefaultFormState, getDisplayLabel, getClosestMatchingOption, getFirstMatchingOption, getFromSchema, isFilesArray, isMultiSelect, isSelect, retrieveSchema, sanitizeDataForNewSchema, toIdSchema, toPathSchema, }; | ||
//# sourceMappingURL=index.js.map |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, UiSchema, ValidatorType } from '../types'; | ||
import { Experimental_CustomMergeAllOf, FormContextType, RJSFSchema, StrictRJSFSchema, UiSchema, ValidatorType } from '../types.js'; | ||
/** Checks to see if the `schema` and `uiSchema` combination represents an array of files | ||
@@ -8,4 +8,5 @@ * | ||
* @param [rootSchema] - The root schema, used to primarily to look up `$ref`s | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - True if schema/uiSchema contains an array of files, otherwise false | ||
*/ | ||
export default function isFilesArray<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, uiSchema?: UiSchema<T, S, F>, rootSchema?: S): boolean; | ||
export default function isFilesArray<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, uiSchema?: UiSchema<T, S, F>, rootSchema?: S, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): boolean; |
@@ -1,3 +0,3 @@ | ||
import { UI_WIDGET_KEY } from '../constants'; | ||
import retrieveSchema from './retrieveSchema'; | ||
import { UI_WIDGET_KEY } from '../constants.js'; | ||
import retrieveSchema from './retrieveSchema.js'; | ||
/** Checks to see if the `schema` and `uiSchema` combination represents an array of files | ||
@@ -9,5 +9,6 @@ * | ||
* @param [rootSchema] - The root schema, used to primarily to look up `$ref`s | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - True if schema/uiSchema contains an array of files, otherwise false | ||
*/ | ||
export default function isFilesArray(validator, schema, uiSchema = {}, rootSchema) { | ||
export default function isFilesArray(validator, schema, uiSchema = {}, rootSchema, experimental_customMergeAllOf) { | ||
if (uiSchema[UI_WIDGET_KEY] === 'files') { | ||
@@ -17,3 +18,3 @@ return true; | ||
if (schema.items) { | ||
const itemsSchema = retrieveSchema(validator, schema.items, rootSchema); | ||
const itemsSchema = retrieveSchema(validator, schema.items, rootSchema, undefined, experimental_customMergeAllOf); | ||
return itemsSchema.type === 'string' && itemsSchema.format === 'data-url'; | ||
@@ -20,0 +21,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType, Experimental_CustomMergeAllOf } from '../types.js'; | ||
/** Checks to see if the `schema` combination represents a multi-select | ||
@@ -7,4 +7,5 @@ * | ||
* @param [rootSchema] - The root schema, used to primarily to look up `$ref`s | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - True if schema contains a multi-select, otherwise false | ||
*/ | ||
export default function isMultiSelect<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema?: S): boolean; | ||
export default function isMultiSelect<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema?: S, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): boolean; |
@@ -1,2 +0,2 @@ | ||
import isSelect from './isSelect'; | ||
import isSelect from './isSelect.js'; | ||
/** Checks to see if the `schema` combination represents a multi-select | ||
@@ -7,10 +7,11 @@ * | ||
* @param [rootSchema] - The root schema, used to primarily to look up `$ref`s | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - True if schema contains a multi-select, otherwise false | ||
*/ | ||
export default function isMultiSelect(validator, schema, rootSchema) { | ||
export default function isMultiSelect(validator, schema, rootSchema, experimental_customMergeAllOf) { | ||
if (!schema.uniqueItems || !schema.items || typeof schema.items === 'boolean') { | ||
return false; | ||
} | ||
return isSelect(validator, schema.items, rootSchema); | ||
return isSelect(validator, schema.items, rootSchema, experimental_customMergeAllOf); | ||
} | ||
//# sourceMappingURL=isMultiSelect.js.map |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType, Experimental_CustomMergeAllOf } from '../types.js'; | ||
/** Checks to see if the `schema` combination represents a select | ||
@@ -7,4 +7,5 @@ * | ||
* @param [rootSchema] - The root schema, used to primarily to look up `$ref`s | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - True if schema contains a select, otherwise false | ||
*/ | ||
export default function isSelect<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, theSchema: S, rootSchema?: S): boolean; | ||
export default function isSelect<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, theSchema: S, rootSchema?: S, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): boolean; |
@@ -1,3 +0,3 @@ | ||
import isConstant from '../isConstant'; | ||
import retrieveSchema from './retrieveSchema'; | ||
import isConstant from '../isConstant.js'; | ||
import retrieveSchema from './retrieveSchema.js'; | ||
/** Checks to see if the `schema` combination represents a select | ||
@@ -8,6 +8,7 @@ * | ||
* @param [rootSchema] - The root schema, used to primarily to look up `$ref`s | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - True if schema contains a select, otherwise false | ||
*/ | ||
export default function isSelect(validator, theSchema, rootSchema = {}) { | ||
const schema = retrieveSchema(validator, theSchema, rootSchema, undefined); | ||
export default function isSelect(validator, theSchema, rootSchema = {}, experimental_customMergeAllOf) { | ||
const schema = retrieveSchema(validator, theSchema, rootSchema, undefined, experimental_customMergeAllOf); | ||
const altSchemas = schema.oneOf || schema.anyOf; | ||
@@ -14,0 +15,0 @@ if (Array.isArray(schema.enum)) { |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import { Experimental_CustomMergeAllOf, FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types.js'; | ||
/** Retrieves an expanded schema that has had all of its conditions, additional properties, references and dependencies | ||
@@ -10,5 +10,6 @@ * resolved and merged into the `schema` given a `validator`, `rootSchema` and `rawFormData` that is used to do the | ||
* @param [rawFormData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The schema having its conditions, additional properties, references and dependencies resolved | ||
*/ | ||
export default function retrieveSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema?: S, rawFormData?: T): S; | ||
export default function retrieveSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema?: S, rawFormData?: T, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): S; | ||
/** Resolves a conditional block (if/else/then) by removing the condition and merging the appropriate conditional branch | ||
@@ -25,5 +26,6 @@ * with the rest of the schema. If `expandAllBranches` is true, then the `retrieveSchemaInteral()` results for both | ||
* @param [formData] - The current formData to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - A list of schemas with the appropriate conditions resolved, possibly with all branches expanded | ||
*/ | ||
export declare function resolveCondition<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S, expandAllBranches: boolean, recurseList: string[], formData?: T): S[]; | ||
export declare function resolveCondition<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S, expandAllBranches: boolean, recurseList: string[], formData?: T, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): S[]; | ||
/** Given a list of lists of allOf, anyOf or oneOf values, create a list of lists of all permutations of the values. The | ||
@@ -44,2 +46,9 @@ * `listOfLists` is expected to be all resolved values of the 1st...nth schemas within an `allOf`, `anyOf` or `oneOf`. | ||
export declare function getAllPermutationsOfXxxOf<S extends StrictRJSFSchema = RJSFSchema>(listOfLists: S[][]): S[][]; | ||
/** Returns the subset of 'patternProperties' specifications that match the given 'key' | ||
* | ||
* @param schema - The schema whose 'patternProperties' are to be filtered | ||
* @param key - The key to match against the 'patternProperties' specifications | ||
* @returns - The subset of 'patternProperties' specifications that match the given 'key' | ||
*/ | ||
export declare function getMatchingPatternProperties<S extends StrictRJSFSchema = RJSFSchema>(schema: S, key: string): Required<S['patternProperties']>; | ||
/** Resolves references and dependencies within a schema and its 'allOf' children. Passes the `expandAllBranches` flag | ||
@@ -56,5 +65,6 @@ * down to the `retrieveSchemaInternal()`, `resolveReference()` and `resolveDependencies()` helper calls. If | ||
* @param [formData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The list of schemas having its references, dependencies and allOf schemas resolved | ||
*/ | ||
export declare function resolveSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S, expandAllBranches: boolean, recurseList: string[], formData?: T): S[]; | ||
export declare function resolveSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S, expandAllBranches: boolean, recurseList: string[], formData?: T, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): S[]; | ||
/** Resolves all references within a schema and then returns the `retrieveSchemaInternal()` if the resolved schema is | ||
@@ -71,5 +81,6 @@ * actually different than the original. Passes the `expandAllBranches` flag down to the `retrieveSchemaInternal()` | ||
* @param [formData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The list schemas retrieved after having all references resolved | ||
*/ | ||
export declare function resolveReference<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S, expandAllBranches: boolean, recurseList: string[], formData?: T): S[]; | ||
export declare function resolveReference<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S, expandAllBranches: boolean, recurseList: string[], formData?: T, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): S[]; | ||
/** Resolves all references within the schema itself as well as any of its properties and array items. | ||
@@ -89,5 +100,6 @@ * | ||
* @param [aFormData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The updated schema with additional properties stubbed | ||
*/ | ||
export declare function stubExistingAdditionalProperties<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, theSchema: S, rootSchema?: S, aFormData?: T): S; | ||
export declare function stubExistingAdditionalProperties<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, theSchema: S, rootSchema?: S, aFormData?: T, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): S; | ||
/** Internal handler that retrieves an expanded schema that has had all of its conditions, additional properties, | ||
@@ -105,6 +117,7 @@ * references and dependencies resolved and merged into the `schema` given a `validator`, `rootSchema` and `rawFormData` | ||
* @param [recurseList=[]] - The optional, list of recursive references already processed | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The schema(s) resulting from having its conditions, additional properties, references and dependencies | ||
* resolved. Multiple schemas may be returned if `expandAllBranches` is true. | ||
*/ | ||
export declare function retrieveSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S, rawFormData?: T, expandAllBranches?: boolean, recurseList?: string[]): S[]; | ||
export declare function retrieveSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S, rawFormData?: T, expandAllBranches?: boolean, recurseList?: string[], experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): S[]; | ||
/** Resolves an `anyOf` or `oneOf` within a schema (if present) to the list of schemas returned from | ||
@@ -133,5 +146,6 @@ * `retrieveSchemaInternal()` for the best matching option. If `expandAllBranches` is true, then a list of schemas for ALL | ||
* @param [formData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The list of schemas with their dependencies resolved | ||
*/ | ||
export declare function resolveDependencies<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S, expandAllBranches: boolean, recurseList: string[], formData?: T): S[]; | ||
export declare function resolveDependencies<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S, expandAllBranches: boolean, recurseList: string[], formData?: T, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): S[]; | ||
/** Processes all the `dependencies` recursively into the list of `resolvedSchema`s as needed. Passes the | ||
@@ -148,5 +162,6 @@ * `expandAllBranches` flag down to the `withDependentSchema()` and the recursive `processDependencies()` helper calls. | ||
* @param [formData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The schema with the `dependencies` resolved into it | ||
*/ | ||
export declare function processDependencies<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, dependencies: S['dependencies'], resolvedSchema: S, rootSchema: S, expandAllBranches: boolean, recurseList: string[], formData?: T): S[]; | ||
export declare function processDependencies<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, dependencies: S['dependencies'], resolvedSchema: S, rootSchema: S, expandAllBranches: boolean, recurseList: string[], formData?: T, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): S[]; | ||
/** Updates a schema with additionally required properties added | ||
@@ -171,5 +186,6 @@ * | ||
* @param [formData]- The current formData to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The list of schemas with the dependent schema resolved into them | ||
*/ | ||
export declare function withDependentSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S, dependencyKey: string, dependencyValue: S, expandAllBranches: boolean, recurseList: string[], formData?: T): S[]; | ||
export declare function withDependentSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S, dependencyKey: string, dependencyValue: S, expandAllBranches: boolean, recurseList: string[], formData?: T, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): S[]; | ||
/** Returns a list of `schema`s with the best choice from the `oneOf` options merged into it. If `expandAllBranches` is | ||
@@ -188,4 +204,5 @@ * true, then a list of schemas for ALL options are retrieved and returned. Passes the `expandAllBranches` flag down to | ||
* @param [formData] - The current formData to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - Either an array containing the best matching option or all options if `expandAllBranches` is true | ||
*/ | ||
export declare function withExactlyOneSubschema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S, dependencyKey: string, oneOf: S['oneOf'], expandAllBranches: boolean, recurseList: string[], formData?: T): S[]; | ||
export declare function withExactlyOneSubschema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S, dependencyKey: string, oneOf: S['oneOf'], expandAllBranches: boolean, recurseList: string[], formData?: T, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): S[]; |
@@ -1,17 +0,18 @@ | ||
import get from 'lodash/get'; | ||
import isEqual from 'lodash/isEqual'; | ||
import set from 'lodash/set'; | ||
import times from 'lodash/times'; | ||
import transform from 'lodash/transform'; | ||
import merge from 'lodash/merge'; | ||
import flattenDeep from 'lodash/flattenDeep'; | ||
import uniq from 'lodash/uniq'; | ||
import get from 'lodash-es/get.js'; | ||
import set from 'lodash-es/set.js'; | ||
import times from 'lodash-es/times.js'; | ||
import transform from 'lodash-es/transform.js'; | ||
import merge from 'lodash-es/merge.js'; | ||
import flattenDeep from 'lodash-es/flattenDeep.js'; | ||
import uniq from 'lodash-es/uniq.js'; | ||
import mergeAllOf from 'json-schema-merge-allof'; | ||
import { ADDITIONAL_PROPERTIES_KEY, ADDITIONAL_PROPERTY_FLAG, ALL_OF_KEY, ANY_OF_KEY, DEPENDENCIES_KEY, IF_KEY, ONE_OF_KEY, REF_KEY, PROPERTIES_KEY, ITEMS_KEY, } from '../constants'; | ||
import findSchemaDefinition, { splitKeyElementFromObject } from '../findSchemaDefinition'; | ||
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema'; | ||
import guessType from '../guessType'; | ||
import isObject from '../isObject'; | ||
import mergeSchemas from '../mergeSchemas'; | ||
import getFirstMatchingOption from './getFirstMatchingOption'; | ||
import { ADDITIONAL_PROPERTIES_KEY, ADDITIONAL_PROPERTY_FLAG, ALL_OF_KEY, ANY_OF_KEY, DEPENDENCIES_KEY, IF_KEY, ITEMS_KEY, ONE_OF_KEY, PATTERN_PROPERTIES_KEY, PROPERTIES_KEY, REF_KEY, } from '../constants.js'; | ||
import findSchemaDefinition, { splitKeyElementFromObject } from '../findSchemaDefinition.js'; | ||
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema.js'; | ||
import guessType from '../guessType.js'; | ||
import isObject from '../isObject.js'; | ||
import mergeSchemas from '../mergeSchemas.js'; | ||
import getFirstMatchingOption from './getFirstMatchingOption.js'; | ||
import deepEquals from '../deepEquals.js'; | ||
import isEmpty from 'lodash-es/isEmpty.js'; | ||
/** Retrieves an expanded schema that has had all of its conditions, additional properties, references and dependencies | ||
@@ -25,6 +26,7 @@ * resolved and merged into the `schema` given a `validator`, `rootSchema` and `rawFormData` that is used to do the | ||
* @param [rawFormData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The schema having its conditions, additional properties, references and dependencies resolved | ||
*/ | ||
export default function retrieveSchema(validator, schema, rootSchema = {}, rawFormData) { | ||
return retrieveSchemaInternal(validator, schema, rootSchema, rawFormData)[0]; | ||
export default function retrieveSchema(validator, schema, rootSchema = {}, rawFormData, experimental_customMergeAllOf) { | ||
return retrieveSchemaInternal(validator, schema, rootSchema, rawFormData, undefined, undefined, experimental_customMergeAllOf)[0]; | ||
} | ||
@@ -42,5 +44,6 @@ /** Resolves a conditional block (if/else/then) by removing the condition and merging the appropriate conditional branch | ||
* @param [formData] - The current formData to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - A list of schemas with the appropriate conditions resolved, possibly with all branches expanded | ||
*/ | ||
export function resolveCondition(validator, schema, rootSchema, expandAllBranches, recurseList, formData) { | ||
export function resolveCondition(validator, schema, rootSchema, expandAllBranches, recurseList, formData, experimental_customMergeAllOf) { | ||
const { if: expression, then, else: otherwise, ...resolvedSchemaLessConditional } = schema; | ||
@@ -52,6 +55,6 @@ const conditionValue = validator.isValid(expression, formData || {}, rootSchema); | ||
if (then && typeof then !== 'boolean') { | ||
schemas = schemas.concat(retrieveSchemaInternal(validator, then, rootSchema, formData, expandAllBranches, recurseList)); | ||
schemas = schemas.concat(retrieveSchemaInternal(validator, then, rootSchema, formData, expandAllBranches, recurseList, experimental_customMergeAllOf)); | ||
} | ||
if (otherwise && typeof otherwise !== 'boolean') { | ||
schemas = schemas.concat(retrieveSchemaInternal(validator, otherwise, rootSchema, formData, expandAllBranches, recurseList)); | ||
schemas = schemas.concat(retrieveSchemaInternal(validator, otherwise, rootSchema, formData, expandAllBranches, recurseList, experimental_customMergeAllOf)); | ||
} | ||
@@ -62,3 +65,3 @@ } | ||
if (conditionalSchema && typeof conditionalSchema !== 'boolean') { | ||
schemas = schemas.concat(retrieveSchemaInternal(validator, conditionalSchema, rootSchema, formData, expandAllBranches, recurseList)); | ||
schemas = schemas.concat(retrieveSchemaInternal(validator, conditionalSchema, rootSchema, formData, expandAllBranches, recurseList, experimental_customMergeAllOf)); | ||
} | ||
@@ -69,3 +72,3 @@ } | ||
} | ||
return resolvedSchemas.flatMap((s) => retrieveSchemaInternal(validator, s, rootSchema, formData, expandAllBranches, recurseList)); | ||
return resolvedSchemas.flatMap((s) => retrieveSchemaInternal(validator, s, rootSchema, formData, expandAllBranches, recurseList, experimental_customMergeAllOf)); | ||
} | ||
@@ -95,6 +98,20 @@ /** Given a list of lists of allOf, anyOf or oneOf values, create a list of lists of all permutations of the values. The | ||
return permutations; | ||
}, [[]] // Start with an empty list | ||
); | ||
}, [[]]); | ||
return allPermutations; | ||
} | ||
/** Returns the subset of 'patternProperties' specifications that match the given 'key' | ||
* | ||
* @param schema - The schema whose 'patternProperties' are to be filtered | ||
* @param key - The key to match against the 'patternProperties' specifications | ||
* @returns - The subset of 'patternProperties' specifications that match the given 'key' | ||
*/ | ||
export function getMatchingPatternProperties(schema, key) { | ||
return Object.keys(schema.patternProperties) | ||
.filter((pattern) => RegExp(pattern).test(key)) | ||
.reduce((obj, pattern) => { | ||
// Pass the pattern using the `[]` index notation so that any `.` in the pattern are not used as a dotted path | ||
set(obj, [pattern], schema.patternProperties[pattern]); | ||
return obj; | ||
}, {}); | ||
} | ||
/** Resolves references and dependencies within a schema and its 'allOf' children. Passes the `expandAllBranches` flag | ||
@@ -111,5 +128,6 @@ * down to the `retrieveSchemaInternal()`, `resolveReference()` and `resolveDependencies()` helper calls. If | ||
* @param [formData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The list of schemas having its references, dependencies and allOf schemas resolved | ||
*/ | ||
export function resolveSchema(validator, schema, rootSchema, expandAllBranches, recurseList, formData) { | ||
export function resolveSchema(validator, schema, rootSchema, expandAllBranches, recurseList, formData, experimental_customMergeAllOf) { | ||
const updatedSchemas = resolveReference(validator, schema, rootSchema, expandAllBranches, recurseList, formData); | ||
@@ -124,9 +142,12 @@ if (updatedSchemas.length > 1 || updatedSchemas[0] !== schema) { | ||
return resolvedSchemas.flatMap((s) => { | ||
return retrieveSchemaInternal(validator, s, rootSchema, formData, expandAllBranches, recurseList); | ||
return retrieveSchemaInternal(validator, s, rootSchema, formData, expandAllBranches, recurseList, experimental_customMergeAllOf); | ||
}); | ||
} | ||
if (ALL_OF_KEY in schema && Array.isArray(schema.allOf)) { | ||
const allOfSchemaElements = schema.allOf.map((allOfSubschema) => retrieveSchemaInternal(validator, allOfSubschema, rootSchema, formData, expandAllBranches, recurseList)); | ||
const allOfSchemaElements = schema.allOf.map((allOfSubschema) => retrieveSchemaInternal(validator, allOfSubschema, rootSchema, formData, expandAllBranches, recurseList, experimental_customMergeAllOf)); | ||
const allPermutations = getAllPermutationsOfXxxOf(allOfSchemaElements); | ||
return allPermutations.map((permutation) => ({ ...schema, allOf: permutation })); | ||
return allPermutations.map((permutation) => ({ | ||
...schema, | ||
allOf: permutation, | ||
})); | ||
} | ||
@@ -147,9 +168,10 @@ // No $ref or dependencies or allOf attribute was found, returning the original schema. | ||
* @param [formData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The list schemas retrieved after having all references resolved | ||
*/ | ||
export function resolveReference(validator, schema, rootSchema, expandAllBranches, recurseList, formData) { | ||
export function resolveReference(validator, schema, rootSchema, expandAllBranches, recurseList, formData, experimental_customMergeAllOf) { | ||
const updatedSchema = resolveAllReferences(schema, rootSchema, recurseList); | ||
if (updatedSchema !== schema) { | ||
// Only call this if the schema was actually changed by the `resolveAllReferences()` function | ||
return retrieveSchemaInternal(validator, updatedSchema, rootSchema, formData, expandAllBranches, recurseList); | ||
return retrieveSchemaInternal(validator, updatedSchema, rootSchema, formData, expandAllBranches, recurseList, experimental_customMergeAllOf); | ||
} | ||
@@ -200,3 +222,3 @@ return [schema]; | ||
} | ||
return isEqual(schema, resolvedSchema) ? schema : resolvedSchema; | ||
return deepEquals(schema, resolvedSchema) ? schema : resolvedSchema; | ||
} | ||
@@ -209,5 +231,6 @@ /** Creates new 'properties' items for each key in the `formData` | ||
* @param [aFormData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The updated schema with additional properties stubbed | ||
*/ | ||
export function stubExistingAdditionalProperties(validator, theSchema, rootSchema, aFormData) { | ||
export function stubExistingAdditionalProperties(validator, theSchema, rootSchema, aFormData, experimental_customMergeAllOf) { | ||
// Clone the schema so that we don't ruin the consumer's original | ||
@@ -225,27 +248,43 @@ const schema = { | ||
} | ||
let additionalProperties = {}; | ||
if (typeof schema.additionalProperties !== 'boolean') { | ||
if (REF_KEY in schema.additionalProperties) { | ||
additionalProperties = retrieveSchema(validator, { $ref: get(schema.additionalProperties, [REF_KEY]) }, rootSchema, formData); | ||
if (PATTERN_PROPERTIES_KEY in schema) { | ||
const matchingProperties = getMatchingPatternProperties(schema, key); | ||
if (!isEmpty(matchingProperties)) { | ||
schema.properties[key] = retrieveSchema(validator, { allOf: Object.values(matchingProperties) }, rootSchema, formData, experimental_customMergeAllOf); | ||
set(schema.properties, [key, ADDITIONAL_PROPERTY_FLAG], true); | ||
return; | ||
} | ||
else if ('type' in schema.additionalProperties) { | ||
additionalProperties = { ...schema.additionalProperties }; | ||
} | ||
if (ADDITIONAL_PROPERTIES_KEY in schema && schema.additionalProperties !== false) { | ||
let additionalProperties = {}; | ||
if (typeof schema.additionalProperties !== 'boolean') { | ||
if (REF_KEY in schema.additionalProperties) { | ||
additionalProperties = retrieveSchema(validator, { $ref: get(schema.additionalProperties, [REF_KEY]) }, rootSchema, formData, experimental_customMergeAllOf); | ||
} | ||
else if ('type' in schema.additionalProperties) { | ||
additionalProperties = { ...schema.additionalProperties }; | ||
} | ||
else if (ANY_OF_KEY in schema.additionalProperties || ONE_OF_KEY in schema.additionalProperties) { | ||
additionalProperties = { | ||
type: 'object', | ||
...schema.additionalProperties, | ||
}; | ||
} | ||
else { | ||
additionalProperties = { type: guessType(get(formData, [key])) }; | ||
} | ||
} | ||
else if (ANY_OF_KEY in schema.additionalProperties || ONE_OF_KEY in schema.additionalProperties) { | ||
additionalProperties = { | ||
type: 'object', | ||
...schema.additionalProperties, | ||
}; | ||
} | ||
else { | ||
additionalProperties = { type: guessType(get(formData, [key])) }; | ||
} | ||
// The type of our new key should match the additionalProperties value; | ||
schema.properties[key] = additionalProperties; | ||
// Set our additional property flag so we know it was dynamically added | ||
set(schema.properties, [key, ADDITIONAL_PROPERTY_FLAG], true); | ||
} | ||
else { | ||
additionalProperties = { type: guessType(get(formData, [key])) }; | ||
// Invalid property | ||
schema.properties[key] = { type: 'null' }; | ||
// Set our additional property flag so we know it was dynamically added | ||
set(schema.properties, [key, ADDITIONAL_PROPERTY_FLAG], true); | ||
} | ||
// The type of our new key should match the additionalProperties value; | ||
schema.properties[key] = additionalProperties; | ||
// Set our additional property flag so we know it was dynamically added | ||
set(schema.properties, [key, ADDITIONAL_PROPERTY_FLAG], true); | ||
}); | ||
@@ -266,14 +305,16 @@ return schema; | ||
* @param [recurseList=[]] - The optional, list of recursive references already processed | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The schema(s) resulting from having its conditions, additional properties, references and dependencies | ||
* resolved. Multiple schemas may be returned if `expandAllBranches` is true. | ||
*/ | ||
export function retrieveSchemaInternal(validator, schema, rootSchema, rawFormData, expandAllBranches = false, recurseList = []) { | ||
export function retrieveSchemaInternal(validator, schema, rootSchema, rawFormData, expandAllBranches = false, recurseList = [], experimental_customMergeAllOf) { | ||
if (!isObject(schema)) { | ||
return [{}]; | ||
} | ||
const resolvedSchemas = resolveSchema(validator, schema, rootSchema, expandAllBranches, recurseList, rawFormData); | ||
const resolvedSchemas = resolveSchema(validator, schema, rootSchema, expandAllBranches, recurseList, rawFormData, experimental_customMergeAllOf); | ||
return resolvedSchemas.flatMap((s) => { | ||
var _a; | ||
let resolvedSchema = s; | ||
if (IF_KEY in resolvedSchema) { | ||
return resolveCondition(validator, resolvedSchema, rootSchema, expandAllBranches, recurseList, rawFormData); | ||
return resolveCondition(validator, resolvedSchema, rootSchema, expandAllBranches, recurseList, rawFormData, experimental_customMergeAllOf); | ||
} | ||
@@ -287,5 +328,23 @@ if (ALL_OF_KEY in resolvedSchema) { | ||
try { | ||
resolvedSchema = mergeAllOf(resolvedSchema, { | ||
deep: false, | ||
const withContainsSchemas = []; | ||
const withoutContainsSchemas = []; | ||
(_a = resolvedSchema.allOf) === null || _a === void 0 ? void 0 : _a.forEach((s) => { | ||
if (typeof s === 'object' && s.contains) { | ||
withContainsSchemas.push(s); | ||
} | ||
else { | ||
withoutContainsSchemas.push(s); | ||
} | ||
}); | ||
if (withContainsSchemas.length) { | ||
resolvedSchema = { ...resolvedSchema, allOf: withoutContainsSchemas }; | ||
} | ||
resolvedSchema = experimental_customMergeAllOf | ||
? experimental_customMergeAllOf(resolvedSchema) | ||
: mergeAllOf(resolvedSchema, { | ||
deep: false, | ||
}); | ||
if (withContainsSchemas.length) { | ||
resolvedSchema.allOf = withContainsSchemas; | ||
} | ||
} | ||
@@ -298,5 +357,18 @@ catch (e) { | ||
} | ||
const hasAdditionalProperties = ADDITIONAL_PROPERTIES_KEY in resolvedSchema && resolvedSchema.additionalProperties !== false; | ||
if (PROPERTIES_KEY in resolvedSchema && PATTERN_PROPERTIES_KEY in resolvedSchema) { | ||
resolvedSchema = Object.keys(resolvedSchema.properties).reduce((schema, key) => { | ||
const matchingProperties = getMatchingPatternProperties(schema, key); | ||
if (!isEmpty(matchingProperties)) { | ||
schema.properties[key] = retrieveSchema(validator, { allOf: [schema.properties[key], ...Object.values(matchingProperties)] }, rootSchema, rawFormData, experimental_customMergeAllOf); | ||
} | ||
return schema; | ||
}, { | ||
...resolvedSchema, | ||
properties: { ...resolvedSchema.properties }, | ||
}); | ||
} | ||
const hasAdditionalProperties = PATTERN_PROPERTIES_KEY in resolvedSchema || | ||
(ADDITIONAL_PROPERTIES_KEY in resolvedSchema && resolvedSchema.additionalProperties !== false); | ||
if (hasAdditionalProperties) { | ||
return stubExistingAdditionalProperties(validator, resolvedSchema, rootSchema, rawFormData); | ||
return stubExistingAdditionalProperties(validator, resolvedSchema, rootSchema, rawFormData, experimental_customMergeAllOf); | ||
} | ||
@@ -355,9 +427,10 @@ return resolvedSchema; | ||
* @param [formData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The list of schemas with their dependencies resolved | ||
*/ | ||
export function resolveDependencies(validator, schema, rootSchema, expandAllBranches, recurseList, formData) { | ||
export function resolveDependencies(validator, schema, rootSchema, expandAllBranches, recurseList, formData, experimental_customMergeAllOf) { | ||
// Drop the dependencies from the source schema. | ||
const { dependencies, ...remainingSchema } = schema; | ||
const resolvedSchemas = resolveAnyOrOneOfSchemas(validator, remainingSchema, rootSchema, expandAllBranches, formData); | ||
return resolvedSchemas.flatMap((resolvedSchema) => processDependencies(validator, dependencies, resolvedSchema, rootSchema, expandAllBranches, recurseList, formData)); | ||
return resolvedSchemas.flatMap((resolvedSchema) => processDependencies(validator, dependencies, resolvedSchema, rootSchema, expandAllBranches, recurseList, formData, experimental_customMergeAllOf)); | ||
} | ||
@@ -375,5 +448,6 @@ /** Processes all the `dependencies` recursively into the list of `resolvedSchema`s as needed. Passes the | ||
* @param [formData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The schema with the `dependencies` resolved into it | ||
*/ | ||
export function processDependencies(validator, dependencies, resolvedSchema, rootSchema, expandAllBranches, recurseList, formData) { | ||
export function processDependencies(validator, dependencies, resolvedSchema, rootSchema, expandAllBranches, recurseList, formData, experimental_customMergeAllOf) { | ||
let schemas = [resolvedSchema]; | ||
@@ -395,5 +469,5 @@ // Process dependencies updating the local schema properties as appropriate. | ||
else if (isObject(dependencyValue)) { | ||
schemas = withDependentSchema(validator, resolvedSchema, rootSchema, dependencyKey, dependencyValue, expandAllBranches, recurseList, formData); | ||
schemas = withDependentSchema(validator, resolvedSchema, rootSchema, dependencyKey, dependencyValue, expandAllBranches, recurseList, formData, experimental_customMergeAllOf); | ||
} | ||
return schemas.flatMap((schema) => processDependencies(validator, remainingDependencies, schema, rootSchema, expandAllBranches, recurseList, formData)); | ||
return schemas.flatMap((schema) => processDependencies(validator, remainingDependencies, schema, rootSchema, expandAllBranches, recurseList, formData, experimental_customMergeAllOf)); | ||
} | ||
@@ -429,6 +503,7 @@ return schemas; | ||
* @param [formData]- The current formData to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The list of schemas with the dependent schema resolved into them | ||
*/ | ||
export function withDependentSchema(validator, schema, rootSchema, dependencyKey, dependencyValue, expandAllBranches, recurseList, formData) { | ||
const dependentSchemas = retrieveSchemaInternal(validator, dependencyValue, rootSchema, formData, expandAllBranches, recurseList); | ||
export function withDependentSchema(validator, schema, rootSchema, dependencyKey, dependencyValue, expandAllBranches, recurseList, formData, experimental_customMergeAllOf) { | ||
const dependentSchemas = retrieveSchemaInternal(validator, dependencyValue, rootSchema, formData, expandAllBranches, recurseList, experimental_customMergeAllOf); | ||
return dependentSchemas.flatMap((dependent) => { | ||
@@ -449,3 +524,3 @@ const { oneOf, ...dependentSchema } = dependent; | ||
const allPermutations = getAllPermutationsOfXxxOf(resolvedOneOfs); | ||
return allPermutations.flatMap((resolvedOneOf) => withExactlyOneSubschema(validator, schema, rootSchema, dependencyKey, resolvedOneOf, expandAllBranches, recurseList, formData)); | ||
return allPermutations.flatMap((resolvedOneOf) => withExactlyOneSubschema(validator, schema, rootSchema, dependencyKey, resolvedOneOf, expandAllBranches, recurseList, formData, experimental_customMergeAllOf)); | ||
}); | ||
@@ -466,5 +541,6 @@ } | ||
* @param [formData] - The current formData to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - Either an array containing the best matching option or all options if `expandAllBranches` is true | ||
*/ | ||
export function withExactlyOneSubschema(validator, schema, rootSchema, dependencyKey, oneOf, expandAllBranches, recurseList, formData) { | ||
export function withExactlyOneSubschema(validator, schema, rootSchema, dependencyKey, oneOf, expandAllBranches, recurseList, formData, experimental_customMergeAllOf) { | ||
const validSubschemas = oneOf.filter((subschema) => { | ||
@@ -494,3 +570,3 @@ if (typeof subschema === 'boolean' || !subschema || !subschema.properties) { | ||
const dependentSchema = { ...subschema, properties: dependentSubschema }; | ||
const schemas = retrieveSchemaInternal(validator, dependentSchema, rootSchema, formData, expandAllBranches, recurseList); | ||
const schemas = retrieveSchemaInternal(validator, dependentSchema, rootSchema, formData, expandAllBranches, recurseList, experimental_customMergeAllOf); | ||
return schemas.map((s) => mergeSchemas(schema, s)); | ||
@@ -497,0 +573,0 @@ }); |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import { Experimental_CustomMergeAllOf, FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types.js'; | ||
/** Sanitize the `data` associated with the `oldSchema` so it is considered appropriate for the `newSchema`. If the new | ||
@@ -46,5 +46,6 @@ * schema does not contain any properties, then `undefined` is returned to clear all the form data. Due to the nature | ||
* @param [data={}] - The form data associated with the schema, defaulting to an empty object when undefined | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The new form data, with all the fields uniquely associated with the old schema set | ||
* to `undefined`. Will return `undefined` if the new schema is not an object containing properties. | ||
*/ | ||
export default function sanitizeDataForNewSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rootSchema: S, newSchema?: S, oldSchema?: S, data?: any): T; | ||
export default function sanitizeDataForNewSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, rootSchema: S, newSchema?: S, oldSchema?: S, data?: any, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): T; |
@@ -1,5 +0,5 @@ | ||
import get from 'lodash/get'; | ||
import has from 'lodash/has'; | ||
import { PROPERTIES_KEY, REF_KEY } from '../constants'; | ||
import retrieveSchema from './retrieveSchema'; | ||
import get from 'lodash-es/get.js'; | ||
import has from 'lodash-es/has.js'; | ||
import { PROPERTIES_KEY, REF_KEY } from '../constants.js'; | ||
import retrieveSchema from './retrieveSchema.js'; | ||
const NO_VALUE = Symbol('no Value'); | ||
@@ -50,6 +50,7 @@ /** Sanitize the `data` associated with the `oldSchema` so it is considered appropriate for the `newSchema`. If the new | ||
* @param [data={}] - The form data associated with the schema, defaulting to an empty object when undefined | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The new form data, with all the fields uniquely associated with the old schema set | ||
* to `undefined`. Will return `undefined` if the new schema is not an object containing properties. | ||
*/ | ||
export default function sanitizeDataForNewSchema(validator, rootSchema, newSchema, oldSchema, data = {}) { | ||
export default function sanitizeDataForNewSchema(validator, rootSchema, newSchema, oldSchema, data = {}, experimental_customMergeAllOf) { | ||
// By default, we will clear the form data | ||
@@ -78,6 +79,6 @@ let newFormData; | ||
if (has(oldKeyedSchema, REF_KEY)) { | ||
oldKeyedSchema = retrieveSchema(validator, oldKeyedSchema, rootSchema, formValue); | ||
oldKeyedSchema = retrieveSchema(validator, oldKeyedSchema, rootSchema, formValue, experimental_customMergeAllOf); | ||
} | ||
if (has(newKeyedSchema, REF_KEY)) { | ||
newKeyedSchema = retrieveSchema(validator, newKeyedSchema, rootSchema, formValue); | ||
newKeyedSchema = retrieveSchema(validator, newKeyedSchema, rootSchema, formValue, experimental_customMergeAllOf); | ||
} | ||
@@ -96,3 +97,3 @@ // Now get types and see if they are the same | ||
// SIDE-EFFECT: process the new schema type of object recursively to save iterations | ||
const itemData = sanitizeDataForNewSchema(validator, rootSchema, newKeyedSchema, oldKeyedSchema, formValue); | ||
const itemData = sanitizeDataForNewSchema(validator, rootSchema, newKeyedSchema, oldKeyedSchema, formValue, experimental_customMergeAllOf); | ||
if (itemData !== undefined || newSchemaTypeForKey === 'array') { | ||
@@ -145,6 +146,6 @@ // only put undefined values for the array type and not the object type | ||
if (has(oldSchemaItems, REF_KEY)) { | ||
oldSchemaItems = retrieveSchema(validator, oldSchemaItems, rootSchema, data); | ||
oldSchemaItems = retrieveSchema(validator, oldSchemaItems, rootSchema, data, experimental_customMergeAllOf); | ||
} | ||
if (has(newSchemaItems, REF_KEY)) { | ||
newSchemaItems = retrieveSchema(validator, newSchemaItems, rootSchema, data); | ||
newSchemaItems = retrieveSchema(validator, newSchemaItems, rootSchema, data, experimental_customMergeAllOf); | ||
} | ||
@@ -159,3 +160,3 @@ // Now get types and see if they are the same | ||
newFormData = data.reduce((newValue, aValue) => { | ||
const itemValue = sanitizeDataForNewSchema(validator, rootSchema, newSchemaItems, oldSchemaItems, aValue); | ||
const itemValue = sanitizeDataForNewSchema(validator, rootSchema, newSchemaItems, oldSchemaItems, aValue, experimental_customMergeAllOf); | ||
if (itemValue !== undefined && (maxItems < 0 || newValue.length < maxItems)) { | ||
@@ -162,0 +163,0 @@ newValue.push(itemValue); |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, IdSchema, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import { Experimental_CustomMergeAllOf, FormContextType, IdSchema, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types.js'; | ||
/** Generates an `IdSchema` object for the `schema`, recursively | ||
@@ -11,4 +11,5 @@ * | ||
* @param [idSeparator='_'] - The separator to use for the path segments in the id | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The `IdSchema` object for the `schema` | ||
*/ | ||
export default function toIdSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, id?: string | null, rootSchema?: S, formData?: T, idPrefix?: string, idSeparator?: string): IdSchema<T>; | ||
export default function toIdSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, id?: string | null, rootSchema?: S, formData?: T, idPrefix?: string, idSeparator?: string, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): IdSchema<T>; |
@@ -1,7 +0,6 @@ | ||
import get from 'lodash/get'; | ||
import isEqual from 'lodash/isEqual'; | ||
import { ALL_OF_KEY, DEPENDENCIES_KEY, ID_KEY, ITEMS_KEY, PROPERTIES_KEY, REF_KEY } from '../constants'; | ||
import isObject from '../isObject'; | ||
import retrieveSchema from './retrieveSchema'; | ||
import getSchemaType from '../getSchemaType'; | ||
import get from 'lodash-es/get.js'; | ||
import { ALL_OF_KEY, DEPENDENCIES_KEY, ID_KEY, ITEMS_KEY, PROPERTIES_KEY, REF_KEY } from '../constants.js'; | ||
import retrieveSchema from './retrieveSchema.js'; | ||
import getSchemaType from '../getSchemaType.js'; | ||
import deepEquals from '../deepEquals.js'; | ||
/** An internal helper that generates an `IdSchema` object for the `schema`, recursively with protection against | ||
@@ -18,26 +17,29 @@ * infinite recursion | ||
* @param [_recurseList=[]] - The list of retrieved schemas currently being recursed, used to prevent infinite recursion | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The `IdSchema` object for the `schema` | ||
*/ | ||
function toIdSchemaInternal(validator, schema, idPrefix, idSeparator, id, rootSchema, formData, _recurseList = []) { | ||
if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) { | ||
const _schema = retrieveSchema(validator, schema, rootSchema, formData); | ||
const sameSchemaIndex = _recurseList.findIndex((item) => isEqual(item, _schema)); | ||
if (sameSchemaIndex === -1) { | ||
return toIdSchemaInternal(validator, _schema, idPrefix, idSeparator, id, rootSchema, formData, _recurseList.concat(_schema)); | ||
} | ||
} | ||
if (ITEMS_KEY in schema && !get(schema, [ITEMS_KEY, REF_KEY])) { | ||
return toIdSchemaInternal(validator, get(schema, ITEMS_KEY), idPrefix, idSeparator, id, rootSchema, formData, _recurseList); | ||
} | ||
function toIdSchemaInternal(validator, schema, idPrefix, idSeparator, id, rootSchema, formData, _recurseList = [], experimental_customMergeAllOf) { | ||
const $id = id || idPrefix; | ||
const idSchema = { $id }; | ||
if (getSchemaType(schema) === 'object' && PROPERTIES_KEY in schema) { | ||
for (const name in schema.properties) { | ||
const field = get(schema, [PROPERTIES_KEY, name]); | ||
const fieldId = idSchema[ID_KEY] + idSeparator + name; | ||
idSchema[name] = toIdSchemaInternal(validator, isObject(field) ? field : {}, idPrefix, idSeparator, fieldId, rootSchema, | ||
// It's possible that formData is not an object -- this can happen if an | ||
// array item has just been added, but not populated with data yet | ||
get(formData, [name]), _recurseList); | ||
if (typeof schema === 'object') { | ||
if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) { | ||
const _schema = retrieveSchema(validator, schema, rootSchema, formData, experimental_customMergeAllOf); | ||
const sameSchemaIndex = _recurseList.findIndex((item) => deepEquals(item, _schema)); | ||
if (sameSchemaIndex === -1) { | ||
return toIdSchemaInternal(validator, _schema, idPrefix, idSeparator, id, rootSchema, formData, _recurseList.concat(_schema), experimental_customMergeAllOf); | ||
} | ||
} | ||
if (ITEMS_KEY in schema && !get(schema, [ITEMS_KEY, REF_KEY])) { | ||
return toIdSchemaInternal(validator, get(schema, ITEMS_KEY), idPrefix, idSeparator, id, rootSchema, formData, _recurseList, experimental_customMergeAllOf); | ||
} | ||
if (getSchemaType(schema) === 'object' && PROPERTIES_KEY in schema) { | ||
for (const name in schema.properties) { | ||
const field = schema[PROPERTIES_KEY][name]; | ||
const fieldId = idSchema[ID_KEY] + idSeparator + name; | ||
idSchema[name] = toIdSchemaInternal(validator, field, idPrefix, idSeparator, fieldId, rootSchema, | ||
// It's possible that formData is not an object -- this can happen if an | ||
// array item has just been added, but not populated with data yet | ||
get(formData, [name]), _recurseList, experimental_customMergeAllOf); | ||
} | ||
} | ||
} | ||
@@ -55,7 +57,8 @@ return idSchema; | ||
* @param [idSeparator='_'] - The separator to use for the path segments in the id | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The `IdSchema` object for the `schema` | ||
*/ | ||
export default function toIdSchema(validator, schema, id, rootSchema, formData, idPrefix = 'root', idSeparator = '_') { | ||
return toIdSchemaInternal(validator, schema, idPrefix, idSeparator, id, rootSchema, formData); | ||
export default function toIdSchema(validator, schema, id, rootSchema, formData, idPrefix = 'root', idSeparator = '_', experimental_customMergeAllOf) { | ||
return toIdSchemaInternal(validator, schema, idPrefix, idSeparator, id, rootSchema, formData, undefined, experimental_customMergeAllOf); | ||
} | ||
//# sourceMappingURL=toIdSchema.js.map |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, PathSchema, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import { Experimental_CustomMergeAllOf, FormContextType, PathSchema, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types.js'; | ||
/** Generates an `PathSchema` object for the `schema`, recursively | ||
@@ -9,4 +9,5 @@ * | ||
* @param [formData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The `PathSchema` object for the `schema` | ||
*/ | ||
export default function toPathSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, name?: string, rootSchema?: S, formData?: T): PathSchema<T>; | ||
export default function toPathSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(validator: ValidatorType<T, S, F>, schema: S, name?: string, rootSchema?: S, formData?: T, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): PathSchema<T>; |
@@ -1,8 +0,8 @@ | ||
import get from 'lodash/get'; | ||
import isEqual from 'lodash/isEqual'; | ||
import set from 'lodash/set'; | ||
import { ALL_OF_KEY, ANY_OF_KEY, ADDITIONAL_PROPERTIES_KEY, DEPENDENCIES_KEY, ITEMS_KEY, NAME_KEY, ONE_OF_KEY, PROPERTIES_KEY, REF_KEY, RJSF_ADDITIONAL_PROPERTIES_FLAG, } from '../constants'; | ||
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema'; | ||
import getClosestMatchingOption from './getClosestMatchingOption'; | ||
import retrieveSchema from './retrieveSchema'; | ||
import get from 'lodash-es/get.js'; | ||
import set from 'lodash-es/set.js'; | ||
import { ADDITIONAL_PROPERTIES_KEY, ALL_OF_KEY, ANY_OF_KEY, DEPENDENCIES_KEY, ITEMS_KEY, NAME_KEY, ONE_OF_KEY, PROPERTIES_KEY, REF_KEY, RJSF_ADDITIONAL_PROPERTIES_FLAG, } from '../constants.js'; | ||
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema.js'; | ||
import getClosestMatchingOption from './getClosestMatchingOption.js'; | ||
import retrieveSchema from './retrieveSchema.js'; | ||
import deepEquals from '../deepEquals.js'; | ||
/** An internal helper that generates an `PathSchema` object for the `schema`, recursively with protection against | ||
@@ -17,10 +17,11 @@ * infinite recursion | ||
* @param [_recurseList=[]] - The list of retrieved schemas currently being recursed, used to prevent infinite recursion | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The `PathSchema` object for the `schema` | ||
*/ | ||
function toPathSchemaInternal(validator, schema, name, rootSchema, formData, _recurseList = []) { | ||
function toPathSchemaInternal(validator, schema, name, rootSchema, formData, _recurseList = [], experimental_customMergeAllOf) { | ||
if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) { | ||
const _schema = retrieveSchema(validator, schema, rootSchema, formData); | ||
const sameSchemaIndex = _recurseList.findIndex((item) => isEqual(item, _schema)); | ||
const _schema = retrieveSchema(validator, schema, rootSchema, formData, experimental_customMergeAllOf); | ||
const sameSchemaIndex = _recurseList.findIndex((item) => deepEquals(item, _schema)); | ||
if (sameSchemaIndex === -1) { | ||
return toPathSchemaInternal(validator, _schema, name, rootSchema, formData, _recurseList.concat(_schema)); | ||
return toPathSchemaInternal(validator, _schema, name, rootSchema, formData, _recurseList.concat(_schema), experimental_customMergeAllOf); | ||
} | ||
@@ -34,7 +35,7 @@ } | ||
const discriminator = getDiscriminatorFieldFromSchema(schema); | ||
const index = getClosestMatchingOption(validator, rootSchema, formData, xxxOf, 0, discriminator); | ||
const index = getClosestMatchingOption(validator, rootSchema, formData, xxxOf, 0, discriminator, experimental_customMergeAllOf); | ||
const _schema = xxxOf[index]; | ||
pathSchema = { | ||
...pathSchema, | ||
...toPathSchemaInternal(validator, _schema, name, rootSchema, formData, _recurseList), | ||
...toPathSchemaInternal(validator, _schema, name, rootSchema, formData, _recurseList, experimental_customMergeAllOf), | ||
}; | ||
@@ -50,6 +51,6 @@ } | ||
if (schemaItems[i]) { | ||
pathSchema[i] = toPathSchemaInternal(validator, schemaItems[i], `${name}.${i}`, rootSchema, element, _recurseList); | ||
pathSchema[i] = toPathSchemaInternal(validator, schemaItems[i], `${name}.${i}`, rootSchema, element, _recurseList, experimental_customMergeAllOf); | ||
} | ||
else if (schemaAdditionalItems) { | ||
pathSchema[i] = toPathSchemaInternal(validator, schemaAdditionalItems, `${name}.${i}`, rootSchema, element, _recurseList); | ||
pathSchema[i] = toPathSchemaInternal(validator, schemaAdditionalItems, `${name}.${i}`, rootSchema, element, _recurseList, experimental_customMergeAllOf); | ||
} | ||
@@ -63,3 +64,3 @@ else { | ||
formData.forEach((element, i) => { | ||
pathSchema[i] = toPathSchemaInternal(validator, schemaItems, `${name}.${i}`, rootSchema, element, _recurseList); | ||
pathSchema[i] = toPathSchemaInternal(validator, schemaItems, `${name}.${i}`, rootSchema, element, _recurseList, experimental_customMergeAllOf); | ||
}); | ||
@@ -70,7 +71,7 @@ } | ||
for (const property in schema.properties) { | ||
const field = get(schema, [PROPERTIES_KEY, property]); | ||
const field = get(schema, [PROPERTIES_KEY, property], {}); | ||
pathSchema[property] = toPathSchemaInternal(validator, field, `${name}.${property}`, rootSchema, | ||
// It's possible that formData is not an object -- this can happen if an | ||
// array item has just been added, but not populated with data yet | ||
get(formData, [property]), _recurseList); | ||
get(formData, [property]), _recurseList, experimental_customMergeAllOf); | ||
} | ||
@@ -87,7 +88,8 @@ } | ||
* @param [formData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The `PathSchema` object for the `schema` | ||
*/ | ||
export default function toPathSchema(validator, schema, name = '', rootSchema, formData) { | ||
return toPathSchemaInternal(validator, schema, name, rootSchema, formData); | ||
export default function toPathSchema(validator, schema, name = '', rootSchema, formData, experimental_customMergeAllOf) { | ||
return toPathSchemaInternal(validator, schema, name, rootSchema, formData, undefined, experimental_customMergeAllOf); | ||
} | ||
//# sourceMappingURL=toPathSchema.js.map |
@@ -1,2 +0,2 @@ | ||
import { RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** Check to see if a `schema` specifies that a value must be true. This happens when: | ||
@@ -3,0 +3,0 @@ * - `schema.const` is truthy |
@@ -1,2 +0,2 @@ | ||
import deepEquals from './deepEquals'; | ||
import deepEquals from './deepEquals.js'; | ||
/** Determines whether the given `component` should be rerendered by comparing its current set of props and state | ||
@@ -3,0 +3,0 @@ * against the next set. If either of those two sets are not the same, then the component should be rerendered. |
@@ -1,2 +0,2 @@ | ||
import { RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** Returns the constant value from the schema when it is either a single value enum or has a const key. Otherwise | ||
@@ -3,0 +3,0 @@ * throws an error. |
@@ -1,2 +0,2 @@ | ||
import { CONST_KEY, ENUM_KEY } from './constants'; | ||
import { CONST_KEY, ENUM_KEY } from './constants.js'; | ||
/** Returns the constant value from the schema when it is either a single value enum or has a const key. Otherwise | ||
@@ -3,0 +3,0 @@ * throws an error. |
@@ -1,2 +0,2 @@ | ||
import { DateObject } from './types'; | ||
import { DateObject } from './types.js'; | ||
/** Returns a UTC date string for the given `dateObject`. If `time` is false, then the time portion of the string is | ||
@@ -3,0 +3,0 @@ * removed. |
@@ -1,2 +0,2 @@ | ||
import { ErrorSchema, RJSFValidationError } from './types'; | ||
import { ErrorSchema, RJSFValidationError } from './types.js'; | ||
/** Converts an `errorSchema` into a list of `RJSFValidationErrors` | ||
@@ -3,0 +3,0 @@ * |
@@ -1,3 +0,3 @@ | ||
import isPlainObject from 'lodash/isPlainObject'; | ||
import { ERRORS_KEY } from './constants'; | ||
import isPlainObject from 'lodash-es/isPlainObject.js'; | ||
import { ERRORS_KEY } from './constants.js'; | ||
/** Converts an `errorSchema` into a list of `RJSFValidationErrors` | ||
@@ -4,0 +4,0 @@ * |
@@ -1,2 +0,2 @@ | ||
import { ErrorSchema, RJSFValidationError } from './types'; | ||
import { ErrorSchema, RJSFValidationError } from './types.js'; | ||
/** Transforms a rjsf validation errors list: | ||
@@ -3,0 +3,0 @@ * [ |
@@ -1,3 +0,3 @@ | ||
import toPath from 'lodash/toPath'; | ||
import ErrorSchemaBuilder from './ErrorSchemaBuilder'; | ||
import toPath from 'lodash-es/toPath.js'; | ||
import ErrorSchemaBuilder from './ErrorSchemaBuilder.js'; | ||
/** Transforms a rjsf validation errors list: | ||
@@ -4,0 +4,0 @@ * [ |
import type { ButtonHTMLAttributes, ChangeEvent, ComponentType, HTMLAttributes, ReactElement, ReactNode, StyleHTMLAttributes } from 'react'; | ||
import { JSONSchema7 } from 'json-schema'; | ||
import { TranslatableString } from './enums'; | ||
import { TranslatableString } from './enums.js'; | ||
/** The representation of any generic object type, usually used as an intersection on other types to make them more | ||
@@ -20,2 +20,5 @@ * flexible in the properties they support (i.e. anything else) | ||
export type FormContextType = GenericObjectType; | ||
/** The interface for the test ID proxy objects that are returned by the `getTestId` utility function. | ||
*/ | ||
export type TestIdShape = Record<string, string>; | ||
/** Experimental feature that specifies the Array `minItems` default form state behavior | ||
@@ -51,3 +54,4 @@ */ | ||
* handling of optional array fields where `minItems` is set and handling of setting defaults based on the | ||
* value of `emptyObjectFields`. | ||
* value of `emptyObjectFields`. It also affects how `allOf` fields are handled and how to handle merging defaults into | ||
* the formData in relation to explicit `undefined` values via `mergeDefaultsIntoFormData`. | ||
*/ | ||
@@ -72,3 +76,29 @@ export type Experimental_DefaultFormStateBehavior = { | ||
allOf?: 'populateDefaults' | 'skipDefaults'; | ||
/** Optional enumerated flag controlling how the defaults are merged into the form data when dealing with undefined | ||
* values, defaulting to `useFormDataIfPresent`. | ||
* NOTE: If there is a default for a field and the `formData` is unspecified, the default ALWAYS merges. | ||
* - `useFormDataIfPresent`: Legacy behavior - Do not merge defaults if there is a value for a field in `formData`, | ||
* even if that value is explicitly set to `undefined` | ||
* - `useDefaultIfFormDataUndefined`: - If the value of a field within the `formData` is `undefined`, then use the | ||
* default value instead | ||
*/ | ||
mergeDefaultsIntoFormData?: 'useFormDataIfPresent' | 'useDefaultIfFormDataUndefined'; | ||
/** Optional enumerated flag controlling how const values are merged into the form data as defaults when dealing with | ||
* undefined values, defaulting to `always`. The defaulting behavior for this flag will always be controlled by the | ||
* `emptyObjectField` flag value. For instance, if `populateRequiredDefaults` is set and the const value is not | ||
* required, it will not be set. | ||
* - `always`: A const value will always be merged into the form as a default. If there is are const values in a | ||
* `oneOf` (for instance to create an enumeration with title different from the values), the first const value | ||
* will be defaulted | ||
* - `skipOneOf`: If const is in a `oneOf` it will NOT pick the first value as a default | ||
* - `never`: A const value will never be used as a default | ||
* | ||
*/ | ||
constAsDefaults?: 'always' | 'skipOneOf' | 'never'; | ||
}; | ||
/** Optional function that allows for custom merging of `allOf` schemas | ||
* @param schema - Schema with `allOf` that needs to be merged | ||
* @returns The merged schema | ||
*/ | ||
export type Experimental_CustomMergeAllOf<S extends StrictRJSFSchema = RJSFSchema> = (schema: S) => S; | ||
/** The interface representing a Date object that contains an optional time */ | ||
@@ -106,2 +136,4 @@ export interface DateObject { | ||
autoComplete?: HTMLInputElement['autocomplete']; | ||
/** Specifies a filter for what file types the user can upload. */ | ||
accept?: HTMLInputElement['accept']; | ||
}; | ||
@@ -170,4 +202,13 @@ /** Type describing an id used for a field in the `IdSchema` */ | ||
}; | ||
/** The base properties passed to various RJSF components. */ | ||
export type RJSFBaseProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
/** The schema object for the field being described */ | ||
schema: S; | ||
/** The uiSchema object for this description field */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** The properties that are passed to an `ErrorListTemplate` implementation */ | ||
export type ErrorListProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
export type ErrorListProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = RJSFBaseProps<T, S, F> & { | ||
/** The errorSchema constructed by `Form` */ | ||
@@ -179,11 +220,5 @@ errorSchema: ErrorSchema<T>; | ||
formContext?: F; | ||
/** The schema that was passed to `Form` */ | ||
schema: S; | ||
/** The uiSchema that was passed to `Form` */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** The properties that are passed to an `FieldErrorTemplate` implementation */ | ||
export type FieldErrorProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
export type FieldErrorProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = RJSFBaseProps<T, S, F> & { | ||
/** The errorSchema constructed by `Form` */ | ||
@@ -195,11 +230,5 @@ errorSchema?: ErrorSchema<T>; | ||
idSchema: IdSchema<T>; | ||
/** The schema that was passed to field */ | ||
schema: S; | ||
/** The uiSchema that was passed to field */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** The properties that are passed to an `FieldHelpTemplate` implementation */ | ||
export type FieldHelpProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
export type FieldHelpProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = RJSFBaseProps<T, S, F> & { | ||
/** The help information to be rendered */ | ||
@@ -209,11 +238,14 @@ help?: string | ReactElement; | ||
idSchema: IdSchema<T>; | ||
/** The schema that was passed to field */ | ||
schema: S; | ||
/** The uiSchema that was passed to field */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** Flag indicating whether there are errors associated with this field */ | ||
hasErrors?: boolean; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** The properties that are passed to a `GridTemplate` */ | ||
export interface GridTemplateProps extends GenericObjectType { | ||
/** The contents of the grid template */ | ||
children?: ReactNode; | ||
/** Optional flag indicating whether the grid element represents a column, necessary for themes which have components | ||
* for Rows vs Columns. NOTE: This is falsy by default when not specified | ||
*/ | ||
column?: boolean; | ||
} | ||
/** The set of `Fields` stored in the `Registry` */ | ||
@@ -230,3 +262,3 @@ export type RegistryFieldsType<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
/** The set of RJSF templates that can be overridden by themes or users */ | ||
export interface TemplatesType<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> { | ||
export type TemplatesType<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
/** The template to use while rendering normal or fixed array fields */ | ||
@@ -236,4 +268,6 @@ ArrayFieldTemplate: ComponentType<ArrayFieldTemplateProps<T, S, F>>; | ||
ArrayFieldDescriptionTemplate: ComponentType<ArrayFieldDescriptionProps<T, S, F>>; | ||
/** The template to use while rendering the buttons for an item in an array field */ | ||
ArrayFieldItemButtonsTemplate: ComponentType<ArrayFieldItemButtonsTemplateType<T, S, F>>; | ||
/** The template to use while rendering an item in an array field */ | ||
ArrayFieldItemTemplate: ComponentType<ArrayFieldTemplateItemType<T, S, F>>; | ||
ArrayFieldItemTemplate: ComponentType<ArrayFieldItemTemplateType<T, S, F>>; | ||
/** The template to use while rendering the title for an array field */ | ||
@@ -253,2 +287,4 @@ ArrayFieldTitleTemplate: ComponentType<ArrayFieldTitleProps<T, S, F>>; | ||
FieldTemplate: ComponentType<FieldTemplateProps<T, S, F>>; | ||
/** The template to use to render a Grid element */ | ||
GridTemplate: ComponentType<GridTemplateProps>; | ||
/** The template to use while rendering an object */ | ||
@@ -277,3 +313,8 @@ ObjectFieldTemplate: ComponentType<ObjectFieldTemplateProps<T, S, F>>; | ||
}; | ||
} | ||
} & { | ||
/** Allow this to support any named `ComponentType` or an object of named `ComponentType`s */ | ||
[key: string]: ComponentType<any> | { | ||
[key: string]: ComponentType<any>; | ||
} | undefined; | ||
}; | ||
/** The set of UiSchema options that can be set globally and used as fallbacks at an individual template, field or | ||
@@ -375,3 +416,3 @@ * widget level when no field-level value of the option is provided. | ||
/** The properties that are passed to a FieldTemplate implementation */ | ||
export type FieldTemplateProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
export type FieldTemplateProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = RJSFBaseProps<T, S, F> & { | ||
/** The id of the field in the hierarchy. You can use it to render a label targeting the wrapped widget */ | ||
@@ -417,6 +458,2 @@ id: string; | ||
displayLabel?: boolean; | ||
/** The schema object for this field */ | ||
schema: S; | ||
/** The uiSchema object for this field */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** The `formContext` object that was passed to `Form` */ | ||
@@ -432,9 +469,5 @@ formContext?: F; | ||
onDropPropertyClick: (value: string) => () => void; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** The properties that are passed to the `UnsupportedFieldTemplate` implementation */ | ||
export type UnsupportedFieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
/** The schema object for this field */ | ||
schema: S; | ||
export type UnsupportedFieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = RJSFBaseProps<T, S, F> & { | ||
/** The tree of unique ids for every child field */ | ||
@@ -444,7 +477,5 @@ idSchema?: IdSchema<T>; | ||
reason: string; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** The properties that are passed to a `TitleFieldTemplate` implementation */ | ||
export type TitleFieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
export type TitleFieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = RJSFBaseProps<T, S, F> & { | ||
/** The id of the field title in the hierarchy */ | ||
@@ -454,23 +485,11 @@ id: string; | ||
title: string; | ||
/** The schema object for the field being titled */ | ||
schema: S; | ||
/** The uiSchema object for this title field */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** A boolean value stating if the field is required */ | ||
required?: boolean; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** The properties that are passed to a `DescriptionFieldTemplate` implementation */ | ||
export type DescriptionFieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
export type DescriptionFieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = RJSFBaseProps<T, S, F> & { | ||
/** The id of the field description in the hierarchy */ | ||
id: string; | ||
/** The schema object for the field being described */ | ||
schema: S; | ||
/** The uiSchema object for this description field */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** The description of the field being rendered */ | ||
description: string | ReactElement; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
@@ -491,8 +510,10 @@ /** The properties that are passed to a `ArrayFieldTitleTemplate` implementation */ | ||
}; | ||
/** The properties of each element in the ArrayFieldTemplateProps.items array */ | ||
export type ArrayFieldTemplateItemType<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
/** The html for the item's content */ | ||
children: ReactElement; | ||
/** The properties of the buttons to render for each element in the ArrayFieldTemplateProps.items array */ | ||
export type ArrayFieldItemButtonsTemplateType<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = RJSFBaseProps<T, S, F> & { | ||
/** The idSchema of the item for which buttons are being rendered */ | ||
idSchema: IdSchema<T>; | ||
/** The className string */ | ||
className: string; | ||
className?: string; | ||
/** Any optional style attributes */ | ||
style?: ButtonHTMLAttributes<HTMLButtonElement>['style']; | ||
/** A boolean value stating if the array item is disabled */ | ||
@@ -510,4 +531,2 @@ disabled?: boolean; | ||
hasRemove: boolean; | ||
/** A boolean value stating whether the array item has a toolbar */ | ||
hasToolbar: boolean; | ||
/** A number stating the index the array item occurs in `items` */ | ||
@@ -527,13 +546,30 @@ index: number; | ||
readonly?: boolean; | ||
}; | ||
/** The properties of each element in the ArrayFieldTemplateProps.items array */ | ||
export type ArrayFieldItemTemplateType<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = RJSFBaseProps<T, S, F> & { | ||
/** The html for the item's content */ | ||
children: ReactNode; | ||
/** The props to pass to the `ArrayFieldItemButtonTemplate` */ | ||
buttonsProps: ArrayFieldItemButtonsTemplateType<T, S, F>; | ||
/** The className string */ | ||
className: string; | ||
/** A boolean value stating if the array item is disabled */ | ||
disabled?: boolean; | ||
/** A boolean value stating whether the array item has a toolbar */ | ||
hasToolbar: boolean; | ||
/** A number stating the index the array item occurs in `items` */ | ||
index: number; | ||
/** A number stating the total number `items` in the array */ | ||
totalItems: number; | ||
/** A boolean value stating if the array item is read-only */ | ||
readonly?: boolean; | ||
/** A stable, unique key for the array item */ | ||
key: string; | ||
/** The schema object for this array item */ | ||
schema: S; | ||
/** The uiSchema object for this array item */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** | ||
* @deprecated - Use `ArrayFieldItemTemplateType` instead | ||
*/ | ||
export type ArrayFieldTemplateItemType<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = ArrayFieldItemTemplateType<T, S, F>; | ||
/** The properties that are passed to an ArrayFieldTemplate implementation */ | ||
export type ArrayFieldTemplateProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
export type ArrayFieldTemplateProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = RJSFBaseProps<T, S, F> & { | ||
/** A boolean value stating whether new elements can be added to the array */ | ||
@@ -548,3 +584,3 @@ canAdd?: boolean; | ||
/** An array of objects representing the items in the array */ | ||
items: ArrayFieldTemplateItemType<T, S, F>[]; | ||
items: ArrayFieldItemTemplateType<T, S, F>[]; | ||
/** A function that adds a new item to the array */ | ||
@@ -558,6 +594,2 @@ onAddClick: (event?: any) => void; | ||
hideError?: boolean; | ||
/** The schema object for this array */ | ||
schema: S; | ||
/** The uiSchema object for this array field */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** A string value containing the title for the array */ | ||
@@ -573,4 +605,2 @@ title: string; | ||
rawErrors?: string[]; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
@@ -591,3 +621,3 @@ /** The properties of each element in the ObjectFieldTemplateProps.properties array */ | ||
/** The properties that are passed to an ObjectFieldTemplate implementation */ | ||
export type ObjectFieldTemplateProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
export type ObjectFieldTemplateProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = RJSFBaseProps<T, S, F> & { | ||
/** A string value containing the title for the object */ | ||
@@ -609,6 +639,2 @@ title: string; | ||
hideError?: boolean; | ||
/** The schema object for this object */ | ||
schema: S; | ||
/** The uiSchema object for this object field */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** An object containing the id for this object & ids for its properties */ | ||
@@ -622,12 +648,10 @@ idSchema: IdSchema<T>; | ||
formContext?: F; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** The properties that are passed to a WrapIfAdditionalTemplate implementation */ | ||
export type WrapIfAdditionalTemplateProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
export type WrapIfAdditionalTemplateProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = RJSFBaseProps<T, S, F> & { | ||
/** The field or widget component instance for this field row */ | ||
children: ReactNode; | ||
} & Pick<FieldTemplateProps<T, S, F>, 'id' | 'classNames' | 'style' | 'label' | 'required' | 'readonly' | 'disabled' | 'schema' | 'uiSchema' | 'onKeyChange' | 'onDropPropertyClick' | 'registry'>; | ||
} & Pick<FieldTemplateProps<T, S, F>, 'id' | 'classNames' | 'hideError' | 'rawErrors' | 'style' | 'label' | 'required' | 'readonly' | 'disabled' | 'schema' | 'uiSchema' | 'onKeyChange' | 'onDropPropertyClick' | 'registry'>; | ||
/** The properties that are passed to a Widget implementation */ | ||
export interface WidgetProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> extends GenericObjectType, Pick<HTMLAttributes<HTMLElement>, Exclude<keyof HTMLAttributes<HTMLElement>, 'onBlur' | 'onFocus'>> { | ||
export interface WidgetProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> extends GenericObjectType, RJSFBaseProps<T, S, F>, Pick<HTMLAttributes<HTMLElement>, Exclude<keyof HTMLAttributes<HTMLElement>, 'onBlur' | 'onFocus'>> { | ||
/** The generated id for this widget, used to provide unique `name`s and `id`s for the HTML field elements rendered by | ||
@@ -641,6 +665,2 @@ * widgets | ||
name: string; | ||
/** The JSONSchema subschema object for this widget */ | ||
schema: S; | ||
/** The uiSchema for this widget */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** The current value for this widget */ | ||
@@ -685,4 +705,2 @@ value: any; | ||
rawErrors?: string[]; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
} | ||
@@ -707,3 +725,3 @@ /** The definition of a React-based Widget component */ | ||
/** The type that defines the props for an Icon button, extending from a basic HTML button attributes */ | ||
export type IconButtonProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = ButtonHTMLAttributes<HTMLButtonElement> & { | ||
export type IconButtonProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = ButtonHTMLAttributes<HTMLButtonElement> & Omit<RJSFBaseProps<T, S, F>, 'schema'> & { | ||
/** An alternative specification for the type of the icon button */ | ||
@@ -713,6 +731,2 @@ iconType?: string; | ||
icon?: string | ReactElement; | ||
/** The uiSchema for this widget */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
@@ -749,3 +763,7 @@ /** The type that defines how to change the behavior of the submit button for the form */ | ||
*/ | ||
type UIOptionsBaseType<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = Partial<Omit<TemplatesType<T, S, F>, 'ButtonTemplates'>> & GlobalUISchemaOptions & { | ||
type UIOptionsBaseType<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = Partial<Pick<TemplatesType<T, S, F>, 'ArrayFieldDescriptionTemplate' | 'ArrayFieldItemTemplate' | 'ArrayFieldTemplate' | 'ArrayFieldTitleTemplate' | 'BaseInputTemplate' | 'DescriptionFieldTemplate' | 'ErrorListTemplate' | 'FieldErrorTemplate' | 'FieldHelpTemplate' | 'FieldTemplate' | 'ObjectFieldTemplate' | 'TitleFieldTemplate' | 'UnsupportedFieldTemplate' | 'WrapIfAdditionalTemplate'>> & GlobalUISchemaOptions & { | ||
/** Allows RJSF to override the default field implementation by specifying either the name of a field that is used | ||
* to look up an implementation from the `fields` list or an actual one-off `Field` component implementation itself | ||
*/ | ||
field?: Field<T, S, F> | string; | ||
/** Any classnames that the user wants to be applied to a field in the ui */ | ||
@@ -773,2 +791,8 @@ classNames?: string; | ||
enumDisabled?: Array<string | number | boolean>; | ||
/** Allows a user to provide a list of labels for enum values in the schema */ | ||
enumNames?: string[]; | ||
/** Provides an optional field within a schema to be used as the oneOf/anyOf selector when there isn't a | ||
* discriminator | ||
*/ | ||
optionsSchemaSelector?: string; | ||
/** Flag, if set to `true`, will hide the default error display for the given field AND all of its child fields in the | ||
@@ -796,4 +820,2 @@ * hierarchy | ||
widget?: Widget<T, S, F> | string; | ||
/** Allows a user to provide a list of labels for enum values in the schema */ | ||
enumNames?: string[]; | ||
}; | ||
@@ -815,6 +837,2 @@ /** The type that represents the Options potentially provided by `ui:options` */ | ||
'ui:rootFieldId'?: string; | ||
/** Allows RJSF to override the default field implementation by specifying either the name of a field that is used | ||
* to look up an implementation from the `fields` list or an actual one-off `Field` component implementation itself | ||
*/ | ||
'ui:field'?: Field<T, S, F> | string; | ||
/** By default, any field that is rendered for an `anyOf`/`oneOf` schema will be wrapped inside the `AnyOfField` or | ||
@@ -862,10 +880,2 @@ * `OneOfField` component. This default behavior may be undesirable if your custom field already handles behavior | ||
validateFormData(formData: T | undefined, schema: S, customValidate?: CustomValidator<T, S, F>, transformErrors?: ErrorTransformer<T, S, F>, uiSchema?: UiSchema<T, S, F>): ValidationData<T>; | ||
/** Converts an `errorSchema` into a list of `RJSFValidationErrors` | ||
* | ||
* @param errorSchema - The `ErrorSchema` instance to convert | ||
* @param [fieldPath=[]] - The current field path, defaults to [] if not specified | ||
* @deprecated - Use the `toErrorList()` function provided by `@rjsf/utils` instead. This function will be removed in | ||
* the next major release. | ||
*/ | ||
toErrorList(errorSchema?: ErrorSchema<T>, fieldPath?: string[]): RJSFValidationError[]; | ||
/** Validates data against a schema, returning true if the data is valid, or | ||
@@ -895,2 +905,10 @@ * false otherwise. If the schema is invalid, then this function will return | ||
} | ||
/** The interface for the return value of the `findFieldInSchema` function | ||
*/ | ||
export interface FoundFieldType<S extends StrictRJSFSchema = RJSFSchema> { | ||
/** The field that was found, or undefined if it wasn't */ | ||
field?: S; | ||
/** The requiredness of the field found or undefined if it wasn't */ | ||
isRequired?: boolean; | ||
} | ||
/** The `SchemaUtilsType` interface provides a wrapper around the publicly exported APIs in the `@rjsf/utils/schema` | ||
@@ -914,5 +932,28 @@ * directory such that one does not have to explicitly pass the `validator` or `rootSchema` to each method. Since both | ||
* @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 - True if the `SchemaUtilsType` differs from the given `validator` or `rootSchema` | ||
*/ | ||
doesSchemaUtilsDiffer(validator: ValidatorType<T, S, F>, rootSchema: S, experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior): boolean; | ||
doesSchemaUtilsDiffer(validator: ValidatorType<T, S, F>, rootSchema: S, experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior, experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>): boolean; | ||
/** Finds the field specified by the `path` within the root or recursed `schema`. If there is no field for the specified | ||
* `path`, then the default `{ field: undefined, isRequired: undefined }` is returned. It determines whether a leaf | ||
* field is in the `required` list for its parent and if so, it is marked as required on return. | ||
* | ||
* @param schema - The current node within the JSON schema | ||
* @param path - The remaining keys in the path to the desired field | ||
* @param [formData] - The form data that is used to determine which oneOf option | ||
* @returns - An object that contains the field and its required state. If no field can be found then | ||
* `{ field: undefined, isRequired: undefined }` is returned. | ||
*/ | ||
findFieldInSchema(schema: S, path: string | string[], formData?: T): FoundFieldType<S>; | ||
/** Finds the oneOf option inside the `schema['any/oneOf']` list which has the `properties[selectorField].default` that | ||
* matches the `formData[selectorField]` value. For the purposes of this function, `selectorField` is either | ||
* `schema.discriminator.propertyName` or `fallbackField`. | ||
* | ||
* @param schema - The schema element in which to search for the selected oneOf option | ||
* @param fallbackField - The field to use as a backup selector field if the schema does not have a required field | ||
* @param xxx - Either `oneOf` or `anyOf`, defines which value is being sought | ||
* @param [formData] - The form data that is used to determine which oneOf option | ||
* @returns - The anyOf/oneOf option that matches the selector field in the schema or undefined if nothing is selected | ||
*/ | ||
findSelectedOptionInXxxOf(schema: S, fallbackField: string, xxx: 'anyOf' | `oneOf`, formData?: T): S | undefined; | ||
/** Returns the superset of `formData` that includes the given set updated to include any missing fields that have | ||
@@ -962,13 +1003,13 @@ * computed to have defaults provided in the `schema`. | ||
getFirstMatchingOption(formData: T | undefined, options: S[], discriminatorField?: string): number; | ||
/** Given the `formData` and list of `options`, attempts to find the index of the option that best matches the data. | ||
* Deprecated, use `getFirstMatchingOption()` instead. | ||
/** Helper that acts like lodash's `get` but additionally retrieves `$ref`s as needed to get the path for schemas | ||
* containing potentially nested `$ref`s. | ||
* | ||
* @param formData - The current formData, if any, onto which to provide any missing defaults | ||
* @param options - The list of options to find a matching options from | ||
* @param [discriminatorField] - The optional name of the field within the options object whose value is used to | ||
* determine which option is selected | ||
* @returns - The index of the matched option or 0 if none is available | ||
* @deprecated | ||
* @param schema - The current node within the JSON schema recursion | ||
* @param path - The remaining keys in the path to the desired property | ||
* @param defaultValue - The value to return if a value is not found for the `pathList` path | ||
* @returns - The internal schema from the `schema` for the given `path` or the `defaultValue` if not found | ||
*/ | ||
getMatchingOption(formData: T | undefined, options: S[], discriminatorField?: string): number; | ||
getFromSchema(schema: S, path: string | string[], defaultValue: T): T; | ||
getFromSchema(schema: S, path: string | string[], defaultValue: S): S; | ||
getFromSchema(schema: S, path: string | string[], defaultValue: T | S): S | T; | ||
/** Checks to see if the `schema` and `uiSchema` combination represents an array of files | ||
@@ -993,14 +1034,2 @@ * | ||
isSelect(schema: S): boolean; | ||
/** Merges the errors in `additionalErrorSchema` into the existing `validationData` by combining the hierarchies in | ||
* the two `ErrorSchema`s and then appending the error list from the `additionalErrorSchema` obtained by calling | ||
* `validator.toErrorList()` onto the `errors` in the `validationData`. If no `additionalErrorSchema` is passed, then | ||
* `validationData` is returned. | ||
* | ||
* @param validationData - The current `ValidationData` into which to merge the additional errors | ||
* @param [additionalErrorSchema] - The additional set of errors | ||
* @returns - The `validationData` with the additional errors from `additionalErrorSchema` merged into it, if provided | ||
* @deprecated - Use the `validationDataMerge()` function exported from `@rjsf/utils` instead. This function will be | ||
* removed in the next major release. | ||
*/ | ||
mergeValidationData(validationData: ValidationData<T>, additionalErrorSchema?: ErrorSchema<T>): ValidationData<T>; | ||
/** Retrieves an expanded schema that has had all of its conditions, additional properties, references and | ||
@@ -1007,0 +1036,0 @@ * dependencies resolved and merged into the `schema` given a `rawFormData` that is used to do the potentially |
@@ -1,2 +0,2 @@ | ||
import { ErrorSchema, FormValidation } from './types'; | ||
import { ErrorSchema, FormValidation } from './types.js'; | ||
/** Unwraps the `errorHandler` structure into the associated `ErrorSchema`, stripping the `addError()` functions from it | ||
@@ -3,0 +3,0 @@ * |
@@ -1,2 +0,2 @@ | ||
import isPlainObject from 'lodash/isPlainObject'; | ||
import isPlainObject from 'lodash-es/isPlainObject.js'; | ||
/** Unwraps the `errorHandler` structure into the associated `ErrorSchema`, stripping the `addError()` functions from it | ||
@@ -3,0 +3,0 @@ * |
@@ -1,2 +0,2 @@ | ||
import pad from './pad'; | ||
import pad from './pad.js'; | ||
/** Converts a UTC date string into a local Date format | ||
@@ -3,0 +3,0 @@ * |
@@ -1,2 +0,2 @@ | ||
import { ErrorSchema, ValidationData } from './types'; | ||
import { ErrorSchema, ValidationData } from './types.js'; | ||
/** Merges the errors in `additionalErrorSchema` into the existing `validationData` by combining the hierarchies in the | ||
@@ -3,0 +3,0 @@ * two `ErrorSchema`s and then appending the error list from the `additionalErrorSchema` obtained by calling |
@@ -1,4 +0,4 @@ | ||
import isEmpty from 'lodash/isEmpty'; | ||
import mergeObjects from './mergeObjects'; | ||
import toErrorList from './toErrorList'; | ||
import isEmpty from 'lodash-es/isEmpty.js'; | ||
import mergeObjects from './mergeObjects.js'; | ||
import toErrorList from './toErrorList.js'; | ||
/** Merges the errors in `additionalErrorSchema` into the existing `validationData` by combining the hierarchies in the | ||
@@ -5,0 +5,0 @@ * two `ErrorSchema`s and then appending the error list from the `additionalErrorSchema` obtained by calling |
@@ -1,2 +0,2 @@ | ||
import { RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { RJSFSchema, StrictRJSFSchema } from './types.js'; | ||
/** Recursively prefixes all `$ref`s in a schema with the value of the `ROOT_SCHEMA_PREFIX` constant. | ||
@@ -3,0 +3,0 @@ * This is used in isValid to make references to the rootSchema |
@@ -1,3 +0,3 @@ | ||
import { REF_KEY, ROOT_SCHEMA_PREFIX } from './constants'; | ||
import isObject from 'lodash/isObject'; | ||
import { REF_KEY, ROOT_SCHEMA_PREFIX } from './constants.js'; | ||
import isObject from 'lodash-es/isObject.js'; | ||
/** Takes a `node` object and transforms any contained `$ref` node variables with a prefix, recursively calling | ||
@@ -4,0 +4,0 @@ * `withIdRefPrefix` for any other elements. |
{ | ||
"name": "@rjsf/utils", | ||
"version": "6.0.0-alpha.0", | ||
"version": "6.0.0-beta.1", | ||
"main": "dist/index.js", | ||
"module": "lib/index.js", | ||
"typings": "lib/index.d.ts", | ||
"type": "module", | ||
"description": "Utility functions for @rjsf/core", | ||
"exports": { | ||
".": { | ||
"types": "./lib/index.d.ts", | ||
"require": "./dist/index.js", | ||
"import": "./lib/index.js" | ||
}, | ||
"./lib": { | ||
"types": "./lib/index.d.ts", | ||
"require": "./dist/index.js", | ||
"import": "./lib/index.js" | ||
}, | ||
"./lib/*.js": { | ||
"types": "./lib/*.d.ts", | ||
"require": "./dist/*.js", | ||
"import": "./lib/*.js" | ||
}, | ||
"./dist": { | ||
"types": "./lib/index.d.ts", | ||
"require": "./dist/index.js", | ||
"import": "./lib/index.js" | ||
}, | ||
"./dist/*.js": { | ||
"types": "./lib/*.d.ts", | ||
"require": "./dist/*.js", | ||
"import": "./lib/*.js" | ||
} | ||
}, | ||
"files": [ | ||
@@ -15,6 +43,7 @@ "dist", | ||
"engines": { | ||
"node": ">=14" | ||
"node": ">=20" | ||
}, | ||
"scripts": { | ||
"build:ts": "tsc -b", | ||
"compileReplacer": "tsc -p tsconfig.replacer.json && move-file lodashReplacer.js lodashReplacer.cjs", | ||
"build:ts": "npm run compileReplacer && rimraf ./lib && tsc -b tsconfig.build.json && tsc-alias -p tsconfig.build.json", | ||
"build:cjs": "esbuild ./src/index.ts --bundle --outfile=dist/index.js --sourcemap --packages=external --format=cjs", | ||
@@ -37,3 +66,3 @@ "build:esm": "esbuild ./src/index.ts --bundle --outfile=dist/utils.esm.js --sourcemap --packages=external --format=esm", | ||
"peerDependencies": { | ||
"react": "^16.14.0 || >=17" | ||
"react": ">=18" | ||
}, | ||
@@ -45,29 +74,11 @@ "dependencies": { | ||
"lodash-es": "^4.17.21", | ||
"nanoid": "^5.1.5", | ||
"react-is": "^18.2.0" | ||
}, | ||
"devDependencies": { | ||
"@babel/core": "^7.23.9", | ||
"@babel/plugin-proposal-class-properties": "^7.18.6", | ||
"@babel/plugin-proposal-optional-chaining": "^7.21.0", | ||
"@babel/preset-env": "^7.23.9", | ||
"@babel/preset-react": "^7.23.3", | ||
"@babel/preset-typescript": "^7.23.3", | ||
"@types/jest": "^29.5.12", | ||
"@types/json-schema": "^7.0.15", | ||
"@types/json-schema-merge-allof": "^0.6.5", | ||
"@types/lodash": "^4.14.202", | ||
"@types/react": "^18.2.58", | ||
"@types/react-is": "^18.2.4", | ||
"@types/react-test-renderer": "^18.0.7", | ||
"babel-jest": "^29.7.0", | ||
"deep-freeze-es6": "^1.4.1", | ||
"esbuild": "^0.18.20", | ||
"eslint": "^8.56.0", | ||
"jest": "^29.7.0", | ||
"jest-environment-jsdom": "^29.7.0", | ||
"react": "^18.2.0", | ||
"react-test-renderer": "^18.2.0", | ||
"rimraf": "^5.0.5", | ||
"rollup": "^3.29.4", | ||
"typescript": "^4.9.5" | ||
"eslint": "^8.56.0" | ||
}, | ||
@@ -91,4 +102,3 @@ "publishConfig": { | ||
}, | ||
"license": "Apache-2.0", | ||
"gitHead": "f4229bf6e067d31b24de3ef9d3ca754ee52529ac" | ||
"license": "Apache-2.0" | ||
} |
@@ -16,5 +16,5 @@ import { FormContextType, RJSFSchema, StrictRJSFSchema, UiSchema } from './types'; | ||
uiSchema: UiSchema<T, S, F> = {}, | ||
formData?: T | ||
formData?: T, | ||
) { | ||
if (!schema.additionalProperties) { | ||
if (!(schema.additionalProperties || schema.patternProperties)) { | ||
return false; | ||
@@ -21,0 +21,0 @@ } |
@@ -22,10 +22,19 @@ /** Below are the list of all the keys into various elements of a RJSFSchema or UiSchema that are used by the various | ||
export const ONE_OF_KEY = 'oneOf'; | ||
export const PATTERN_PROPERTIES_KEY = 'patternProperties'; | ||
export const PROPERTIES_KEY = 'properties'; | ||
export const READONLY_KEY = 'readonly'; | ||
export const REQUIRED_KEY = 'required'; | ||
export const SUBMIT_BTN_OPTIONS_KEY = 'submitButtonOptions'; | ||
export const REF_KEY = '$ref'; | ||
/** | ||
* @deprecated Replace with correctly spelled constant `RJSF_ADDITIONAL_PROPERTIES_FLAG` | ||
/** The path of the discriminator value returned by the schema endpoint. | ||
* The discriminator is the value in a `oneOf` that determines which option is selected. | ||
*/ | ||
export const RJSF_ADDITONAL_PROPERTIES_FLAG = '__rjsf_additionalProperties'; | ||
export const DISCRIMINATOR_PATH = ['discriminator', 'propertyName']; | ||
/** The name of the `formContext` attribute in the React JSON Schema Form Registry | ||
*/ | ||
export const FORM_CONTEXT_NAME = 'formContext'; | ||
/** The name of the `layoutGridLookupMap` attribute in the form context | ||
*/ | ||
export const LOOKUP_MAP_NAME = 'layoutGridLookupMap'; | ||
export const RJSF_ADDITIONAL_PROPERTIES_FLAG = '__rjsf_additionalProperties'; | ||
@@ -32,0 +41,0 @@ export const ROOT_SCHEMA_PREFIX = '__rjsf_rootSchema'; |
import deepEquals from './deepEquals'; | ||
import { | ||
ErrorSchema, | ||
Experimental_CustomMergeAllOf, | ||
Experimental_DefaultFormStateBehavior, | ||
FormContextType, | ||
FoundFieldType, | ||
GlobalUISchemaOptions, | ||
@@ -13,6 +14,7 @@ IdSchema, | ||
UiSchema, | ||
ValidationData, | ||
ValidatorType, | ||
} from './types'; | ||
import { | ||
findFieldInSchema, | ||
findSelectedOptionInXxxOf, | ||
getDefaultFormState, | ||
@@ -22,7 +24,6 @@ getDisplayLabel, | ||
getFirstMatchingOption, | ||
getMatchingOption, | ||
getFromSchema, | ||
isFilesArray, | ||
isMultiSelect, | ||
isSelect, | ||
mergeValidationData, | ||
retrieveSchema, | ||
@@ -35,5 +36,6 @@ sanitizeDataForNewSchema, | ||
/** The `SchemaUtils` class provides a wrapper around the publicly exported APIs in the `utils/schema` directory such | ||
* that one does not have to explicitly pass the `validator`, `rootSchema`, or `experimental_defaultFormStateBehavior` to each method. | ||
* Since these generally do not change across a `Form`, this allows for providing a simplified set of APIs to the | ||
* `@rjsf/core` components and the various themes as well. This class implements the `SchemaUtilsType` interface. | ||
* that one does not have to explicitly pass the `validator`, `rootSchema`, `experimental_defaultFormStateBehavior` or | ||
* `experimental_customMergeAllOf` to each method. Since these generally do not change across a `Form`, this allows for | ||
* providing a simplified set of APIs to the `@rjsf/core` components and the various themes as well. This class | ||
* implements the `SchemaUtilsType` interface. | ||
*/ | ||
@@ -46,2 +48,3 @@ class SchemaUtils<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> | ||
experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior; | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>; | ||
@@ -53,2 +56,3 @@ /** Constructs the `SchemaUtils` instance with the given `validator` and `rootSchema` stored as instance variables | ||
* @param experimental_defaultFormStateBehavior - Configuration flags to allow users to override default form state behavior | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
*/ | ||
@@ -58,3 +62,4 @@ constructor( | ||
rootSchema: S, | ||
experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior | ||
experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
) { | ||
@@ -64,2 +69,3 @@ this.rootSchema = rootSchema; | ||
this.experimental_defaultFormStateBehavior = experimental_defaultFormStateBehavior; | ||
this.experimental_customMergeAllOf = experimental_customMergeAllOf; | ||
} | ||
@@ -82,2 +88,3 @@ | ||
* @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 - True if the `SchemaUtilsType` differs from the given `validator` or `rootSchema` | ||
@@ -88,3 +95,4 @@ */ | ||
rootSchema: S, | ||
experimental_defaultFormStateBehavior = {} | ||
experimental_defaultFormStateBehavior = {}, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): boolean { | ||
@@ -97,6 +105,50 @@ if (!validator || !rootSchema) { | ||
!deepEquals(this.rootSchema, rootSchema) || | ||
!deepEquals(this.experimental_defaultFormStateBehavior, experimental_defaultFormStateBehavior) | ||
!deepEquals(this.experimental_defaultFormStateBehavior, experimental_defaultFormStateBehavior) || | ||
this.experimental_customMergeAllOf !== experimental_customMergeAllOf | ||
); | ||
} | ||
/** Finds the field specified by the `path` within the root or recursed `schema`. If there is no field for the specified | ||
* `path`, then the default `{ field: undefined, isRequired: undefined }` is returned. It determines whether a leaf | ||
* field is in the `required` list for its parent and if so, it is marked as required on return. | ||
* | ||
* @param schema - The current node within the JSON schema | ||
* @param path - The remaining keys in the path to the desired field | ||
* @param [formData] - The form data that is used to determine which oneOf option | ||
* @returns - An object that contains the field and its required state. If no field can be found then | ||
* `{ field: undefined, isRequired: undefined }` is returned. | ||
*/ | ||
findFieldInSchema(schema: S, path: string | string[], formData?: T): FoundFieldType<S> { | ||
return findFieldInSchema( | ||
this.validator, | ||
this.rootSchema, | ||
schema, | ||
path, | ||
formData, | ||
this.experimental_customMergeAllOf, | ||
); | ||
} | ||
/** Finds the oneOf option inside the `schema['any/oneOf']` list which has the `properties[selectorField].default` that | ||
* matches the `formData[selectorField]` value. For the purposes of this function, `selectorField` is either | ||
* `schema.discriminator.propertyName` or `fallbackField`. | ||
* | ||
* @param schema - The schema element in which to search for the selected oneOf option | ||
* @param fallbackField - The field to use as a backup selector field if the schema does not have a required field | ||
* @param xxx - Either `oneOf` or `anyOf`, defines which value is being sought | ||
* @param [formData={}] - The form data that is used to determine which oneOf option | ||
* @returns - The anyOf/oneOf option that matches the selector field in the schema or undefined if nothing is selected | ||
*/ | ||
findSelectedOptionInXxxOf(schema: S, fallbackField: string, xxx: 'anyOf' | `oneOf`, formData: T): S | undefined { | ||
return findSelectedOptionInXxxOf( | ||
this.validator, | ||
this.rootSchema, | ||
schema, | ||
fallbackField, | ||
xxx, | ||
formData, | ||
this.experimental_customMergeAllOf, | ||
); | ||
} | ||
/** Returns the superset of `formData` that includes the given set updated to include any missing fields that have | ||
@@ -115,3 +167,3 @@ * computed to have defaults provided in the `schema`. | ||
formData?: T, | ||
includeUndefinedValues: boolean | 'excludeObjectChildren' = false | ||
includeUndefinedValues: boolean | 'excludeObjectChildren' = false, | ||
): T | T[] | undefined { | ||
@@ -124,3 +176,4 @@ return getDefaultFormState<T, S, F>( | ||
includeUndefinedValues, | ||
this.experimental_defaultFormStateBehavior | ||
this.experimental_defaultFormStateBehavior, | ||
this.experimental_customMergeAllOf, | ||
); | ||
@@ -138,3 +191,10 @@ } | ||
getDisplayLabel(schema: S, uiSchema?: UiSchema<T, S, F>, globalOptions?: GlobalUISchemaOptions) { | ||
return getDisplayLabel<T, S, F>(this.validator, schema, uiSchema, this.rootSchema, globalOptions); | ||
return getDisplayLabel<T, S, F>( | ||
this.validator, | ||
schema, | ||
uiSchema, | ||
this.rootSchema, | ||
globalOptions, | ||
this.experimental_customMergeAllOf, | ||
); | ||
} | ||
@@ -159,3 +219,3 @@ | ||
selectedOption?: number, | ||
discriminatorField?: string | ||
discriminatorField?: string, | ||
): number { | ||
@@ -168,3 +228,4 @@ return getClosestMatchingOption<T, S, F>( | ||
selectedOption, | ||
discriminatorField | ||
discriminatorField, | ||
this.experimental_customMergeAllOf, | ||
); | ||
@@ -186,14 +247,22 @@ } | ||
/** Given the `formData` and list of `options`, attempts to find the index of the option that best matches the data. | ||
* Deprecated, use `getFirstMatchingOption()` instead. | ||
/** Helper that acts like lodash's `get` but additionally retrieves `$ref`s as needed to get the path for schemas | ||
* containing potentially nested `$ref`s. | ||
* | ||
* @param formData - The current formData, if any, onto which to provide any missing defaults | ||
* @param options - The list of options to find a matching options from | ||
* @param [discriminatorField] - The optional name of the field within the options object whose value is used to | ||
* determine which option is selected | ||
* @returns - The index of the matched option or 0 if none is available | ||
* @deprecated | ||
* @param schema - The current node within the JSON schema recursion | ||
* @param path - The remaining keys in the path to the desired property | ||
* @param defaultValue - The value to return if a value is not found for the `pathList` path | ||
* @returns - The internal schema from the `schema` for the given `path` or the `defaultValue` if not found | ||
*/ | ||
getMatchingOption(formData: T | undefined, options: S[], discriminatorField?: string) { | ||
return getMatchingOption<T, S, F>(this.validator, formData, options, this.rootSchema, discriminatorField); | ||
getFromSchema(schema: S, path: string | string[], defaultValue: T): T; | ||
getFromSchema(schema: S, path: string | string[], defaultValue: S): S; | ||
getFromSchema(schema: S, path: string | string[], defaultValue: T | S): T | S { | ||
return getFromSchema<T, S, F>( | ||
this.validator, | ||
this.rootSchema, | ||
schema, | ||
path, | ||
// @ts-expect-error TS2769: No overload matches this call | ||
defaultValue, | ||
this.experimental_customMergeAllOf, | ||
); | ||
} | ||
@@ -208,3 +277,3 @@ | ||
isFilesArray(schema: S, uiSchema?: UiSchema<T, S, F>) { | ||
return isFilesArray<T, S, F>(this.validator, schema, uiSchema, this.rootSchema); | ||
return isFilesArray<T, S, F>(this.validator, schema, uiSchema, this.rootSchema, this.experimental_customMergeAllOf); | ||
} | ||
@@ -218,3 +287,3 @@ | ||
isMultiSelect(schema: S) { | ||
return isMultiSelect<T, S, F>(this.validator, schema, this.rootSchema); | ||
return isMultiSelect<T, S, F>(this.validator, schema, this.rootSchema, this.experimental_customMergeAllOf); | ||
} | ||
@@ -228,20 +297,5 @@ | ||
isSelect(schema: S) { | ||
return isSelect<T, S, F>(this.validator, schema, this.rootSchema); | ||
return isSelect<T, S, F>(this.validator, schema, this.rootSchema, this.experimental_customMergeAllOf); | ||
} | ||
/** Merges the errors in `additionalErrorSchema` into the existing `validationData` by combining the hierarchies in | ||
* the two `ErrorSchema`s and then appending the error list from the `additionalErrorSchema` obtained by calling | ||
* `getValidator().toErrorList()` onto the `errors` in the `validationData`. If no `additionalErrorSchema` is passed, | ||
* then `validationData` is returned. | ||
* | ||
* @param validationData - The current `ValidationData` into which to merge the additional errors | ||
* @param [additionalErrorSchema] - The additional set of errors | ||
* @returns - The `validationData` with the additional errors from `additionalErrorSchema` merged into it, if provided. | ||
* @deprecated - Use the `validationDataMerge()` function exported from `@rjsf/utils` instead. This function will be | ||
* removed in the next major release. | ||
*/ | ||
mergeValidationData(validationData: ValidationData<T>, additionalErrorSchema?: ErrorSchema<T>): ValidationData<T> { | ||
return mergeValidationData<T, S, F>(this.validator, validationData, additionalErrorSchema); | ||
} | ||
/** Retrieves an expanded schema that has had all of its conditions, additional properties, references and | ||
@@ -256,3 +310,9 @@ * dependencies resolved and merged into the `schema` given a `rawFormData` that is used to do the potentially | ||
retrieveSchema(schema: S, rawFormData?: T) { | ||
return retrieveSchema<T, S, F>(this.validator, schema, this.rootSchema, rawFormData); | ||
return retrieveSchema<T, S, F>( | ||
this.validator, | ||
schema, | ||
this.rootSchema, | ||
rawFormData, | ||
this.experimental_customMergeAllOf, | ||
); | ||
} | ||
@@ -272,3 +332,10 @@ | ||
sanitizeDataForNewSchema(newSchema?: S, oldSchema?: S, data?: any): T { | ||
return sanitizeDataForNewSchema(this.validator, this.rootSchema, newSchema, oldSchema, data); | ||
return sanitizeDataForNewSchema( | ||
this.validator, | ||
this.rootSchema, | ||
newSchema, | ||
oldSchema, | ||
data, | ||
this.experimental_customMergeAllOf, | ||
); | ||
} | ||
@@ -286,3 +353,12 @@ | ||
toIdSchema(schema: S, id?: string | null, formData?: T, idPrefix = 'root', idSeparator = '_'): IdSchema<T> { | ||
return toIdSchema<T, S, F>(this.validator, schema, id, this.rootSchema, formData, idPrefix, idSeparator); | ||
return toIdSchema<T, S, F>( | ||
this.validator, | ||
schema, | ||
id, | ||
this.rootSchema, | ||
formData, | ||
idPrefix, | ||
idSeparator, | ||
this.experimental_customMergeAllOf, | ||
); | ||
} | ||
@@ -298,3 +374,10 @@ | ||
toPathSchema(schema: S, name?: string, formData?: T): PathSchema<T> { | ||
return toPathSchema<T, S, F>(this.validator, schema, name, this.rootSchema, formData); | ||
return toPathSchema<T, S, F>( | ||
this.validator, | ||
schema, | ||
name, | ||
this.rootSchema, | ||
formData, | ||
this.experimental_customMergeAllOf, | ||
); | ||
} | ||
@@ -309,2 +392,3 @@ } | ||
* @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 - An implementation of a `SchemaUtilsType` interface | ||
@@ -315,9 +399,15 @@ */ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
>( | ||
validator: ValidatorType<T, S, F>, | ||
rootSchema: S, | ||
experimental_defaultFormStateBehavior = {} | ||
experimental_defaultFormStateBehavior = {}, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): SchemaUtilsType<T, S, F> { | ||
return new SchemaUtils<T, S, F>(validator, rootSchema, experimental_defaultFormStateBehavior); | ||
return new SchemaUtils<T, S, F>( | ||
validator, | ||
rootSchema, | ||
experimental_defaultFormStateBehavior, | ||
experimental_customMergeAllOf, | ||
); | ||
} |
@@ -28,3 +28,3 @@ /** Given the `FileReader.readAsDataURL()` based `dataURI` extracts that data into an actual Blob along with the name | ||
// if no key is found, then the name is unknown | ||
mediaparams.map((param) => param.split('=')).find(([key]) => key === 'name')?.[1] || 'unknown' | ||
mediaparams.map((param) => param.split('=')).find(([key]) => key === 'name')?.[1] || 'unknown', | ||
); | ||
@@ -31,0 +31,0 @@ |
@@ -15,3 +15,3 @@ import pad from './pad'; | ||
start: number, | ||
stop: number | ||
stop: number, | ||
): EnumOptionsType<S>[] { | ||
@@ -18,0 +18,0 @@ if (start <= 0 && stop <= 0) { |
@@ -1,5 +0,4 @@ | ||
import isEqual from 'lodash/isEqual'; | ||
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types'; | ||
import enumOptionsValueForIndex from './enumOptionsValueForIndex'; | ||
import deepEquals from './deepEquals'; | ||
@@ -21,9 +20,9 @@ /** Removes the enum option value at the `valueIndex` from the currently `selected` (list of) value(s). If `selected` is | ||
selected?: EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][], | ||
allEnumOptions: EnumOptionsType<S>[] = [] | ||
allEnumOptions: EnumOptionsType<S>[] = [], | ||
): EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][] | undefined { | ||
const value = enumOptionsValueForIndex<S>(valueIndex, allEnumOptions); | ||
if (Array.isArray(selected)) { | ||
return selected.filter((v) => !isEqual(v, value)); | ||
return selected.filter((v) => !deepEquals(v, value)); | ||
} | ||
return isEqual(value, selected) ? undefined : selected; | ||
return deepEquals(value, selected) ? undefined : selected; | ||
} |
@@ -18,3 +18,3 @@ import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types'; | ||
allEnumOptions: EnumOptionsType<S>[] = [], | ||
multiple = false | ||
multiple = false, | ||
): string | string[] | undefined { | ||
@@ -21,0 +21,0 @@ const selectedIndexes: string[] = allEnumOptions |
@@ -1,4 +0,3 @@ | ||
import isEqual from 'lodash/isEqual'; | ||
import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types'; | ||
import deepEquals from './deepEquals'; | ||
@@ -13,8 +12,8 @@ /** Determines whether the given `value` is (one of) the `selected` value(s). | ||
value: EnumOptionsType<S>['value'], | ||
selected: EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][] | ||
selected: EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][], | ||
) { | ||
if (Array.isArray(selected)) { | ||
return selected.some((sel) => isEqual(sel, value)); | ||
return selected.some((sel) => deepEquals(sel, value)); | ||
} | ||
return isEqual(selected, value); | ||
return deepEquals(selected, value); | ||
} |
@@ -16,3 +16,3 @@ import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types'; | ||
selected: EnumOptionsType<S>['value'][], | ||
allEnumOptions: EnumOptionsType<S>[] = [] | ||
allEnumOptions: EnumOptionsType<S>[] = [], | ||
) { | ||
@@ -19,0 +19,0 @@ const value = enumOptionsValueForIndex<S>(valueIndex, allEnumOptions); |
@@ -17,3 +17,3 @@ import { EnumOptionsType, RJSFSchema, StrictRJSFSchema } from './types'; | ||
allEnumOptions: EnumOptionsType<S>[] = [], | ||
emptyValue?: EnumOptionsType<S>['value'] | ||
emptyValue?: EnumOptionsType<S>['value'], | ||
): EnumOptionsType<S>['value'] | EnumOptionsType<S>['value'][] | undefined { | ||
@@ -20,0 +20,0 @@ if (Array.isArray(valueIndex)) { |
@@ -12,2 +12,4 @@ /** An enumeration of all the translatable strings used by `@rjsf/core` and its themes. The value of each of the | ||
MissingItems = 'Missing items definition', | ||
/** Empty array message, used by ArrayField */ | ||
EmptyArray = 'No items yet. Use the button below to add some.', | ||
/** Yes label, used by BooleanField */ | ||
@@ -14,0 +16,0 @@ YesLabel = 'Yes', |
import cloneDeep from 'lodash/cloneDeep'; | ||
import get from 'lodash/get'; | ||
import set from 'lodash/set'; | ||
import setWith from 'lodash/setWith'; | ||
@@ -8,2 +9,7 @@ import { ErrorSchema } from './types'; | ||
/** Represents the type of the path which can be a string of dotted path values or a list of string or numbers where | ||
* numbers represent array indexes/ | ||
*/ | ||
export type PathType = string | (string | number)[]; | ||
/** The `ErrorSchemaBuilder<T>` is used to build an `ErrorSchema<T>` since the definition of the `ErrorSchema` type is | ||
@@ -41,8 +47,9 @@ * designed for reading information rather than writing it. Use this class to add, replace or clear errors in an error | ||
*/ | ||
private getOrCreateErrorBlock(pathOfError?: string | string[]) { | ||
private getOrCreateErrorBlock(pathOfError?: PathType) { | ||
const hasPath = (Array.isArray(pathOfError) && pathOfError.length > 0) || typeof pathOfError === 'string'; | ||
// @ts-expect-error TS2590 to avoid "Expression produces a union type that is too complex to represent" error | ||
let errorBlock: ErrorSchema = hasPath ? get(this.errorSchema, pathOfError) : this.errorSchema; | ||
if (!errorBlock && pathOfError) { | ||
errorBlock = {}; | ||
set(this.errorSchema, pathOfError, errorBlock); | ||
setWith(this.errorSchema, pathOfError, errorBlock, Object); | ||
} | ||
@@ -70,3 +77,3 @@ return errorBlock; | ||
*/ | ||
addErrors(errorOrList: string | string[], pathOfError?: string | string[]) { | ||
addErrors(errorOrList: string | string[], pathOfError?: PathType) { | ||
const errorBlock: ErrorSchema = this.getOrCreateErrorBlock(pathOfError); | ||
@@ -80,5 +87,5 @@ let errorsList = get(errorBlock, ERRORS_KEY); | ||
if (Array.isArray(errorOrList)) { | ||
errorsList.push(...errorOrList); | ||
set(errorBlock, ERRORS_KEY, [...new Set([...errorsList, ...errorOrList])]); | ||
} else { | ||
errorsList.push(errorOrList); | ||
set(errorBlock, ERRORS_KEY, [...new Set([...errorsList, errorOrList])]); | ||
} | ||
@@ -96,6 +103,6 @@ return this; | ||
*/ | ||
setErrors(errorOrList: string | string[], pathOfError?: string | string[]) { | ||
setErrors(errorOrList: string | string[], pathOfError?: PathType) { | ||
const errorBlock: ErrorSchema = this.getOrCreateErrorBlock(pathOfError); | ||
// Effectively clone the array being given to prevent accidental outside manipulation of the given list | ||
const listToAdd = Array.isArray(errorOrList) ? [...errorOrList] : [errorOrList]; | ||
const listToAdd = Array.isArray(errorOrList) ? [...new Set([...errorOrList])] : [errorOrList]; | ||
set(errorBlock, ERRORS_KEY, listToAdd); | ||
@@ -112,3 +119,3 @@ return this; | ||
*/ | ||
clearErrors(pathOfError?: string | string[]) { | ||
clearErrors(pathOfError?: PathType) { | ||
const errorBlock: ErrorSchema = this.getOrCreateErrorBlock(pathOfError); | ||
@@ -115,0 +122,0 @@ set(errorBlock, ERRORS_KEY, []); |
@@ -35,3 +35,3 @@ import jsonpointer from 'jsonpointer'; | ||
rootSchema: S = {} as S, | ||
recurseList: string[] = [] | ||
recurseList: string[] = [], | ||
): S { | ||
@@ -83,3 +83,3 @@ const ref = $ref || ''; | ||
$ref?: string, | ||
rootSchema: S = {} as S | ||
rootSchema: S = {} as S, | ||
): S { | ||
@@ -86,0 +86,0 @@ const recurseList: string[] = []; |
@@ -26,3 +26,3 @@ import { type DateObject } from './types'; | ||
yearRange: [number, number] = [1900, new Date().getFullYear() + 2], | ||
format: DateElementFormat = 'YMD' | ||
format: DateElementFormat = 'YMD', | ||
) { | ||
@@ -52,3 +52,3 @@ const { day, month, year, hour, minute, second } = date; | ||
{ type: 'minute', range: [0, 59], value: minute }, | ||
{ type: 'second', range: [0, 59], value: second } | ||
{ type: 'second', range: [0, 59], value: second }, | ||
); | ||
@@ -55,0 +55,0 @@ } |
@@ -5,2 +5,3 @@ import get from 'lodash/get'; | ||
import { RJSFSchema, StrictRJSFSchema } from './types'; | ||
import { DISCRIMINATOR_PATH } from './constants'; | ||
@@ -15,3 +16,3 @@ /** Returns the `discriminator.propertyName` when defined in the `schema` if it is a string. A warning is generated when | ||
let discriminator: string | undefined; | ||
const maybeString = get(schema, 'discriminator.propertyName', undefined); | ||
const maybeString = get(schema, DISCRIMINATOR_PATH); | ||
if (isString(maybeString)) { | ||
@@ -18,0 +19,0 @@ discriminator = maybeString; |
@@ -15,3 +15,3 @@ import rangeSpec from './rangeSpec'; | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
>( | ||
@@ -21,3 +21,3 @@ schema: RJSFSchema, | ||
options: UIOptionsType<T, S, F> = {}, | ||
autoDefaultStepAny = true | ||
autoDefaultStepAny = true, | ||
): InputPropsType { | ||
@@ -56,3 +56,7 @@ const inputProps: InputPropsType = { | ||
if (options.accept) { | ||
inputProps.accept = options.accept as string; | ||
} | ||
return inputProps; | ||
} |
@@ -18,3 +18,3 @@ import get from 'lodash/get'; | ||
options: S[], | ||
discriminatorField?: string | ||
discriminatorField?: string, | ||
): number | undefined { | ||
@@ -30,3 +30,3 @@ if (formData && discriminatorField) { | ||
const option = options[i]; | ||
const discriminator = get(option, [PROPERTIES_KEY, discriminatorField], {}); | ||
const discriminator: S = get(option, [PROPERTIES_KEY, discriminatorField], {}) as S; | ||
@@ -33,0 +33,0 @@ if (discriminator.type === 'object' || discriminator.type === 'array') { |
@@ -10,2 +10,3 @@ import guessType from './guessType'; | ||
* - schema.additionalProperties: Returns `object` | ||
* - schema.patternProperties: Returns `object` | ||
* - type is an array with a length of 2 and one type is 'null': Returns the other type | ||
@@ -17,3 +18,3 @@ * | ||
export default function getSchemaType<S extends StrictRJSFSchema = RJSFSchema>( | ||
schema: S | ||
schema: S, | ||
): string | string[] | undefined { | ||
@@ -30,3 +31,3 @@ let { type } = schema; | ||
if (!type && (schema.properties || schema.additionalProperties)) { | ||
if (!type && (schema.properties || schema.additionalProperties || schema.patternProperties)) { | ||
return 'object'; | ||
@@ -33,0 +34,0 @@ } |
@@ -23,3 +23,3 @@ import { SUBMIT_BTN_OPTIONS_KEY } from './constants'; | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
>(uiSchema: UiSchema<T, S, F> = {}): UISchemaSubmitButtonOptions { | ||
@@ -26,0 +26,0 @@ const uiOptions = getUiOptions<T, S, F>(uiSchema); |
@@ -15,3 +15,3 @@ import { FormContextType, TemplatesType, Registry, UIOptionsType, StrictRJSFSchema, RJSFSchema } from './types'; | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
>(name: Name, registry: Registry<T, S, F>, uiOptions: UIOptionsType<T, S, F> = {}): TemplatesType<T, S, F>[Name] { | ||
@@ -22,2 +22,13 @@ const { templates } = registry; | ||
} | ||
// Allow templates to be customized per-field by using string keys from the registry | ||
if ( | ||
Object.hasOwn(uiOptions, name) && | ||
typeof uiOptions[name] === 'string' && | ||
Object.hasOwn(templates, uiOptions[name] as string) | ||
) { | ||
const key = uiOptions[name]; | ||
// Evaluating templates[key] results in TS2590: Expression produces a union type that is too complex to represent | ||
// To avoid that, we cast templates to `any` before accessing the key field | ||
return (templates as any)[key]; | ||
} | ||
return ( | ||
@@ -24,0 +35,0 @@ // Evaluating uiOptions[name] results in TS2590: Expression produces a union type that is too complex to represent |
@@ -14,3 +14,3 @@ import { UI_OPTIONS_KEY, UI_WIDGET_KEY } from './constants'; | ||
uiSchema: UiSchema<T, S, F> = {}, | ||
globalOptions: GlobalUISchemaOptions = {} | ||
globalOptions: GlobalUISchemaOptions = {}, | ||
): UIOptionsType<T, S, F> { | ||
@@ -31,4 +31,4 @@ return Object.keys(uiSchema) | ||
}, | ||
{ ...globalOptions } | ||
{ ...globalOptions }, | ||
); | ||
} |
import { RJSFSchema, StrictRJSFSchema } from './types'; | ||
/** JS has no built-in hashing function, so rolling our own | ||
/** Hashes a string using the algorithm based on Java's hashing function. | ||
* JS has no built-in hashing function, so rolling our own | ||
* based on Java's hashing fn: | ||
@@ -10,3 +11,3 @@ * http://www.java2s.com/example/nodejs-utility-method/string-hash/hashcode-4dc2b.html | ||
*/ | ||
function hashString(string: string): string { | ||
export function hashString(string: string): string { | ||
let hash = 0; | ||
@@ -21,2 +22,24 @@ for (let i = 0; i < string.length; i += 1) { | ||
/** Stringifies an `object`, sorts object fields in consistent order before stringifying it. | ||
* | ||
* @param object - The object for which the sorted stringify is desired | ||
* @returns - The stringified object with keys sorted in a consistent order | ||
*/ | ||
export function sortedJSONStringify(object: unknown): string { | ||
const allKeys = new Set<string>(); | ||
// solution source: https://stackoverflow.com/questions/16167581/sort-object-properties-and-json-stringify/53593328#53593328 | ||
JSON.stringify(object, (key, value) => (allKeys.add(key), value)); | ||
return JSON.stringify(object, Array.from(allKeys).sort()); | ||
} | ||
/** Stringifies an `object` and returns the hash of the resulting string. Sorts object fields | ||
* in consistent order before stringify to prevent different hash ids for the same object. | ||
* | ||
* @param object - The object for which the hash is desired | ||
* @returns - The string obtained from the hash of the stringified object | ||
*/ | ||
export function hashObject(object: unknown): string { | ||
return hashString(sortedJSONStringify(object)); | ||
} | ||
/** Stringifies the schema and returns the hash of the resulting string. Sorts schema fields | ||
@@ -29,6 +52,3 @@ * in consistent order before stringify to prevent different hash ids for the same schema. | ||
export default function hashForSchema<S extends StrictRJSFSchema = RJSFSchema>(schema: S) { | ||
const allKeys = new Set<string>(); | ||
// solution source: https://stackoverflow.com/questions/16167581/sort-object-properties-and-json-stringify/53593328#53593328 | ||
JSON.stringify(schema, (key, value) => (allKeys.add(key), value)); | ||
return hashString(JSON.stringify(schema, Array.from(allKeys).sort())); | ||
return hashObject(schema); | ||
} |
@@ -15,3 +15,3 @@ import getWidget from './getWidget'; | ||
widget: Widget<T, S, F> | string, | ||
registeredWidgets: RegistryWidgetsType<T, S, F> = {} | ||
registeredWidgets: RegistryWidgetsType<T, S, F> = {}, | ||
) { | ||
@@ -18,0 +18,0 @@ try { |
@@ -82,1 +82,11 @@ import isString from 'lodash/isString'; | ||
} | ||
/** Return a consistent `id` for the `btn` button element | ||
* | ||
* @param id - Either simple string id or an IdSchema from which to extract it | ||
* @param btn - The button type for which to generate the id | ||
* @returns - The consistent id for the button from the given `id` and `btn` type | ||
*/ | ||
export function buttonId<T = any>(id: IdSchema<T> | string, btn: 'add' | 'copy' | 'moveDown' | 'moveUp' | 'remove') { | ||
return idGenerator<T>(id, btn); | ||
} |
@@ -23,8 +23,18 @@ import allowAdditionalItems from './allowAdditionalItems'; | ||
import getTemplate from './getTemplate'; | ||
import getTestIds from './getTestIds'; | ||
import getUiOptions from './getUiOptions'; | ||
import getWidget from './getWidget'; | ||
import guessType from './guessType'; | ||
import hashForSchema from './hashForSchema'; | ||
import hashForSchema, { hashObject, hashString, sortedJSONStringify } from './hashForSchema'; | ||
import hasWidget from './hasWidget'; | ||
import { ariaDescribedByIds, descriptionId, errorId, examplesId, helpId, optionId, titleId } from './idGenerators'; | ||
import { | ||
ariaDescribedByIds, | ||
buttonId, | ||
descriptionId, | ||
errorId, | ||
examplesId, | ||
helpId, | ||
optionId, | ||
titleId, | ||
} from './idGenerators'; | ||
import isConstant from './isConstant'; | ||
@@ -36,2 +46,3 @@ import isCustomWidget from './isCustomWidget'; | ||
import localToUTC from './localToUTC'; | ||
import lookupFromFormContext from './lookupFromFormContext'; | ||
import mergeDefaultsWithFormData from './mergeDefaultsWithFormData'; | ||
@@ -57,2 +68,3 @@ import mergeObjects from './mergeObjects'; | ||
import getOptionMatchingSimpleDiscriminator from './getOptionMatchingSimpleDiscriminator'; | ||
import getChangedFields from './getChangedFields'; | ||
@@ -70,2 +82,3 @@ export * from './types'; | ||
asNumber, | ||
buttonId, | ||
canExpand, | ||
@@ -89,2 +102,3 @@ createErrorHandler, | ||
findSchemaDefinition, | ||
getChangedFields, | ||
getDateElementProps, | ||
@@ -97,2 +111,3 @@ getDiscriminatorFieldFromSchema, | ||
getTemplate, | ||
getTestIds, | ||
getUiOptions, | ||
@@ -103,2 +118,4 @@ getWidget, | ||
hashForSchema, | ||
hashObject, | ||
hashString, | ||
helpId, | ||
@@ -111,2 +128,3 @@ isConstant, | ||
localToUTC, | ||
lookupFromFormContext, | ||
mergeDefaultsWithFormData, | ||
@@ -124,2 +142,3 @@ mergeObjects, | ||
shouldRender, | ||
sortedJSONStringify, | ||
titleId, | ||
@@ -126,0 +145,0 @@ toConstant, |
@@ -12,3 +12,3 @@ import getUiOptions from './getUiOptions'; | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
>(uiSchema: UiSchema<T, S, F> = {}) { | ||
@@ -15,0 +15,0 @@ return ( |
@@ -1,2 +0,2 @@ | ||
/** Determines whether a `thing` is an object for the purposes of RSJF. In this case, `thing` is an object if it has | ||
/** Determines whether a `thing` is an object for the purposes of RJSF. In this case, `thing` is an object if it has | ||
* the type `object` but is NOT null, an array or a File. | ||
@@ -7,10 +7,17 @@ * | ||
*/ | ||
export default function isObject(thing: any) { | ||
if (typeof File !== 'undefined' && thing instanceof File) { | ||
export default function isObject(thing: any): thing is object { | ||
if (typeof thing !== 'object' || thing === null) { | ||
return false; | ||
} | ||
if (typeof Date !== 'undefined' && thing instanceof Date) { | ||
// lastModified is guaranteed to be a number on a File instance | ||
// as per https://w3c.github.io/FileAPI/#dfn-lastModified | ||
if (typeof thing.lastModified === 'number' && typeof File !== 'undefined' && thing instanceof File) { | ||
return false; | ||
} | ||
return typeof thing === 'object' && thing !== null && !Array.isArray(thing); | ||
// getMonth is guaranteed to be a method on a Date instance | ||
// as per https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-date.prototype.getmonth | ||
if (typeof thing.getMonth === 'function' && typeof Date !== 'undefined' && thing instanceof Date) { | ||
return false; | ||
} | ||
return !Array.isArray(thing); | ||
} |
@@ -20,3 +20,3 @@ import { ReactElement } from 'react'; | ||
hideLabel?: boolean, | ||
fallback?: false | ||
fallback?: false, | ||
): undefined | false | ReactElement; | ||
@@ -26,5 +26,5 @@ export default function labelValue( | ||
hideLabel?: boolean, | ||
fallback?: false | '' | ||
fallback?: false | '', | ||
): undefined | false | string | ReactElement { | ||
return hideLabel ? fallback : label; | ||
} |
@@ -5,2 +5,3 @@ import get from 'lodash/get'; | ||
import { GenericObjectType } from '../src'; | ||
import isNil from 'lodash/isNil'; | ||
@@ -16,3 +17,4 @@ /** Merges the `defaults` object of type `T` into the `formData` of type `T` | ||
* - when the array is not set in form data, the default is copied over | ||
* - scalars are overwritten/set by form data | ||
* - scalars are overwritten/set by form data unless undefined and there is a default AND `defaultSupercedesUndefined` | ||
* is true | ||
* | ||
@@ -22,2 +24,7 @@ * @param [defaults] - The defaults to merge | ||
* @param [mergeExtraArrayDefaults=false] - If true, any additional default array entries are appended onto the formData | ||
* @param [defaultSupercedesUndefined=false] - If true, an explicit undefined value will be overwritten by the default value | ||
* @param [overrideFormDataWithDefaults=false] - If true, the default value will overwrite the form data value. If the value | ||
* doesn't exist in the default, we take it from formData and in the case where the value is set to undefined in formData. | ||
* This is useful when we have already merged formData with defaults and want to add an additional field from formData | ||
* that does not exist in defaults. | ||
* @returns - The resulting merged form data with defaults | ||
@@ -28,15 +35,31 @@ */ | ||
formData?: T, | ||
mergeExtraArrayDefaults = false | ||
mergeExtraArrayDefaults = false, | ||
defaultSupercedesUndefined = false, | ||
overrideFormDataWithDefaults = false, | ||
): T | undefined { | ||
if (Array.isArray(formData)) { | ||
const defaultsArray = Array.isArray(defaults) ? defaults : []; | ||
const mapped = formData.map((value, idx) => { | ||
if (defaultsArray[idx]) { | ||
return mergeDefaultsWithFormData<any>(defaultsArray[idx], value, mergeExtraArrayDefaults); | ||
// If overrideFormDataWithDefaults is true, we want to override the formData with the defaults | ||
const overrideArray = overrideFormDataWithDefaults ? defaultsArray : formData; | ||
const overrideOppositeArray = overrideFormDataWithDefaults ? formData : defaultsArray; | ||
const mapped = overrideArray.map((value, idx) => { | ||
// We want to explicitly make sure that the value is NOT undefined since null, 0 and empty space are valid values | ||
if (overrideOppositeArray[idx] !== undefined) { | ||
return mergeDefaultsWithFormData<any>( | ||
defaultsArray[idx], | ||
formData[idx], | ||
mergeExtraArrayDefaults, | ||
defaultSupercedesUndefined, | ||
overrideFormDataWithDefaults, | ||
); | ||
} | ||
return value; | ||
}); | ||
// Merge any extra defaults when mergeExtraArrayDefaults is true | ||
if (mergeExtraArrayDefaults && mapped.length < defaultsArray.length) { | ||
mapped.push(...defaultsArray.slice(mapped.length)); | ||
// Or when overrideFormDataWithDefaults is true and the default array is shorter than the formData array | ||
if ((mergeExtraArrayDefaults || overrideFormDataWithDefaults) && mapped.length < overrideOppositeArray.length) { | ||
mapped.push(...overrideOppositeArray.slice(mapped.length)); | ||
} | ||
@@ -48,6 +71,13 @@ return mapped as unknown as T; | ||
return Object.keys(formData as GenericObjectType).reduce((acc, key) => { | ||
const keyValue = get(formData, key); | ||
const keyExistsInDefaults = isObject(defaults) && key in (defaults as GenericObjectType); | ||
const keyExistsInFormData = key in (formData as GenericObjectType); | ||
acc[key as keyof T] = mergeDefaultsWithFormData<T>( | ||
defaults ? get(defaults, key) : {}, | ||
get(formData, key), | ||
mergeExtraArrayDefaults | ||
keyValue, | ||
mergeExtraArrayDefaults, | ||
defaultSupercedesUndefined, | ||
// overrideFormDataWithDefaults can be true only when the key value exists in defaults | ||
// Or if the key value doesn't exist in formData | ||
overrideFormDataWithDefaults && (keyExistsInDefaults || !keyExistsInFormData), | ||
); | ||
@@ -57,3 +87,18 @@ return acc; | ||
} | ||
/** | ||
* If the defaultSupercedesUndefined flag is true | ||
* And formData is set to undefined or null and defaults are defined | ||
* Or if formData is a number and is NaN return defaults | ||
* Or if overrideFormDataWithDefaults flag is true and formData is set to not undefined/null return defaults | ||
*/ | ||
if ( | ||
(defaultSupercedesUndefined && | ||
((!isNil(defaults) && isNil(formData)) || (typeof formData === 'number' && isNaN(formData)))) || | ||
(overrideFormDataWithDefaults && !isNil(formData)) | ||
) { | ||
return defaults; | ||
} | ||
return formData; | ||
} |
@@ -16,25 +16,28 @@ import isObject from './isObject'; | ||
obj2: GenericObjectType, | ||
concatArrays: boolean | 'preventDuplicates' = false | ||
concatArrays: boolean | 'preventDuplicates' = false, | ||
) { | ||
return Object.keys(obj2).reduce((acc, key) => { | ||
const left = obj1 ? obj1[key] : {}, | ||
right = obj2[key]; | ||
if (obj1 && key in obj1 && isObject(right)) { | ||
acc[key] = mergeObjects(left, right, concatArrays); | ||
} else if (concatArrays && Array.isArray(left) && Array.isArray(right)) { | ||
let toMerge = right; | ||
if (concatArrays === 'preventDuplicates') { | ||
toMerge = right.reduce((result, value) => { | ||
if (!left.includes(value)) { | ||
result.push(value); | ||
} | ||
return result; | ||
}, []); | ||
return Object.keys(obj2).reduce( | ||
(acc, key) => { | ||
const left = obj1 ? obj1[key] : {}, | ||
right = obj2[key]; | ||
if (obj1 && key in obj1 && isObject(right)) { | ||
acc[key] = mergeObjects(left, right, concatArrays); | ||
} else if (concatArrays && Array.isArray(left) && Array.isArray(right)) { | ||
let toMerge = right; | ||
if (concatArrays === 'preventDuplicates') { | ||
toMerge = right.reduce((result, value) => { | ||
if (!left.includes(value)) { | ||
result.push(value); | ||
} | ||
return result; | ||
}, []); | ||
} | ||
acc[key] = left.concat(toMerge); | ||
} else { | ||
acc[key] = right; | ||
} | ||
acc[key] = left.concat(toMerge); | ||
} else { | ||
acc[key] = right; | ||
} | ||
return acc; | ||
}, Object.assign({}, obj1)); // Prevent mutation of source object. | ||
return acc; | ||
}, | ||
Object.assign({}, obj1), | ||
); // Prevent mutation of source object. | ||
} |
@@ -0,12 +1,18 @@ | ||
import get from 'lodash/get'; | ||
import { CONST_KEY, DEFAULT_KEY, PROPERTIES_KEY } from './constants'; | ||
import getDiscriminatorFieldFromSchema from './getDiscriminatorFieldFromSchema'; | ||
import getUiOptions from './getUiOptions'; | ||
import toConstant from './toConstant'; | ||
import { RJSFSchema, EnumOptionsType, StrictRJSFSchema, FormContextType, UiSchema } from './types'; | ||
import getUiOptions from './getUiOptions'; | ||
/** Gets the list of options from the `schema`. If the schema has an enum list, then those enum values are returned. The | ||
* labels for the options will be extracted from the non-standard, RJSF-deprecated `enumNames` if it exists, otherwise | ||
* the label will be the same as the `value`. If the schema has a `oneOf` or `anyOf`, then the value is the list of | ||
* `const` values from the schema and the label is either the `schema.title` or the value. If a `uiSchema` is provided | ||
* and it has the `ui:enumNames` matched with `enum` or it has an associated `oneOf` or `anyOf` with a list of objects | ||
* containing `ui:title` then the UI schema values will replace the values from the schema. | ||
* label will be the same as the `value`. | ||
* | ||
* If the schema has a `oneOf` or `anyOf`, then the value is the list of either: | ||
* - The `const` values from the schema if present | ||
* - If the schema has a discriminator and the label using either the `schema.title` or the value. If a `uiSchema` is | ||
* provided, and it has the `ui:enumNames` matched with `enum` or it has an associated `oneOf` or `anyOf` with a list of | ||
* objects containing `ui:title` then the UI schema values will replace the values from the schema. | ||
* | ||
* @param schema - The schema from which to extract the options list | ||
@@ -16,8 +22,6 @@ * @param [uiSchema] - The optional uiSchema from which to get alternate labels for the options | ||
*/ | ||
export default function optionsList<S extends StrictRJSFSchema = RJSFSchema, T = any, F extends FormContextType = any>( | ||
export default function optionsList<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>( | ||
schema: S, | ||
uiSchema?: UiSchema<T, S, F> | ||
uiSchema?: UiSchema<T, S, F>, | ||
): EnumOptionsType<S>[] | undefined { | ||
// TODO flip generics to move T first in v6 | ||
const schemaWithEnumNames = schema as S & { enumNames?: string[] }; | ||
if (schema.enum) { | ||
@@ -29,12 +33,2 @@ let enumNames: string[] | undefined; | ||
} | ||
if (!enumNames && schemaWithEnumNames.enumNames) { | ||
// enumNames was deprecated in v5 and is intentionally omitted from the RJSFSchema type. | ||
// Cast the type to include enumNames so the feature still works. | ||
if (process.env.NODE_ENV !== 'production') { | ||
console.warn( | ||
'The "enumNames" property in the schema is deprecated and will be removed in a future major release. Use the "ui:enumNames" property in the uiSchema instead.' | ||
); | ||
} | ||
enumNames = schemaWithEnumNames.enumNames; | ||
} | ||
return schema.enum.map((value, i) => { | ||
@@ -54,2 +48,9 @@ const label = enumNames?.[i] || String(value); | ||
} | ||
// See if there is a discriminator path specified in the schema, and if so, use it as the selectorField, otherwise | ||
// pull one from the uiSchema | ||
let selectorField = getDiscriminatorFieldFromSchema<S>(schema); | ||
if (uiSchema) { | ||
const { optionsSchemaSelector = selectorField } = getUiOptions<T, S, F>(uiSchema); | ||
selectorField = optionsSchemaSelector; | ||
} | ||
return ( | ||
@@ -60,4 +61,12 @@ altSchemas && | ||
const aSchema = aSchemaDef as S; | ||
const value = toConstant(aSchema); | ||
const label = title || aSchema.title || String(value); | ||
let value: EnumOptionsType<S>['value']; | ||
let label = title; | ||
if (selectorField) { | ||
const innerSchema: S = get(aSchema, [PROPERTIES_KEY, selectorField], {}) as S; | ||
value = get(innerSchema, DEFAULT_KEY, get(innerSchema, CONST_KEY)); | ||
label = label || innerSchema?.title || aSchema.title || String(value); | ||
} else { | ||
value = toConstant(aSchema); | ||
label = label || aSchema.title || String(value); | ||
} | ||
return { | ||
@@ -64,0 +73,0 @@ schema: aSchema, |
import get from 'lodash/get'; | ||
import isEqual from 'lodash/isEqual'; | ||
@@ -18,2 +17,3 @@ import { ID_KEY } from '../constants'; | ||
} from '../types'; | ||
import deepEquals from '../deepEquals'; | ||
@@ -71,7 +71,7 @@ /** The type of the map of schema hash to schema | ||
this.schemaMap[key] = identifiedSchema; | ||
} else if (!isEqual(existing, identifiedSchema)) { | ||
} else if (!deepEquals(existing, identifiedSchema)) { | ||
console.error('existing schema:', JSON.stringify(existing, null, 2)); | ||
console.error('new schema:', JSON.stringify(identifiedSchema, null, 2)); | ||
throw new Error( | ||
`Two different schemas exist with the same key ${key}! What a bad coincidence. If possible, try adding an $id to one of the schemas` | ||
`Two different schemas exist with the same key ${key}! What a bad coincidence. If possible, try adding an $id to one of the schemas`, | ||
); | ||
@@ -96,3 +96,3 @@ } | ||
isValid(schema: S, _formData: T, rootSchema: S): boolean { | ||
if (!isEqual(rootSchema, this.rootSchema)) { | ||
if (!deepEquals(rootSchema, this.rootSchema)) { | ||
throw new Error('Unexpectedly calling isValid() with a rootSchema that differs from the construction rootSchema'); | ||
@@ -137,3 +137,3 @@ } | ||
_transformErrors?: ErrorTransformer<T, S, F>, | ||
_uiSchema?: UiSchema<T, S, F> | ||
_uiSchema?: UiSchema<T, S, F>, | ||
): ValidationData<T> { | ||
@@ -140,0 +140,0 @@ throw new Error('Unexpectedly calling the `validateFormData()` method during schema parsing'); |
import forEach from 'lodash/forEach'; | ||
import isEqual from 'lodash/isEqual'; | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema } from '../types'; | ||
import { PROPERTIES_KEY, ITEMS_KEY } from '../constants'; | ||
import { ITEMS_KEY, PROPERTIES_KEY } from '../constants'; | ||
import ParserValidator, { SchemaMap } from './ParserValidator'; | ||
import { retrieveSchemaInternal, resolveAnyOrOneOfSchemas } from '../schema/retrieveSchema'; | ||
import { resolveAnyOrOneOfSchemas, retrieveSchemaInternal } from '../schema/retrieveSchema'; | ||
import deepEquals from '../deepEquals'; | ||
@@ -23,7 +23,7 @@ /** Recursive function used to parse the given `schema` belonging to the `rootSchema`. The `validator` is used to | ||
rootSchema: S, | ||
schema: S | ||
schema: S, | ||
) { | ||
const schemas = retrieveSchemaInternal<T, S, F>(validator, schema, rootSchema, undefined, true); | ||
schemas.forEach((schema) => { | ||
const sameSchemaIndex = recurseList.findIndex((item) => isEqual(item, schema)); | ||
const sameSchemaIndex = recurseList.findIndex((item) => deepEquals(item, schema)); | ||
if (sameSchemaIndex === -1) { | ||
@@ -53,3 +53,3 @@ recurseList.push(schema); | ||
export default function schemaParser<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>( | ||
rootSchema: S | ||
rootSchema: S, | ||
): SchemaMap<S> { | ||
@@ -56,0 +56,0 @@ const validator = new ParserValidator<T, S, F>(rootSchema); |
@@ -13,3 +13,3 @@ import get from 'lodash/get'; | ||
import guessType from '../guessType'; | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import { Experimental_CustomMergeAllOf, FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema'; | ||
@@ -49,2 +49,3 @@ import getOptionMatchingSimpleDiscriminator from '../getOptionMatchingSimpleDiscriminator'; | ||
* @param formData - The form data associated with the schema, used to calculate the score | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The score a schema against the formData | ||
@@ -56,3 +57,4 @@ */ | ||
schema?: S, | ||
formData: any = {} | ||
formData?: any, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): number { | ||
@@ -70,4 +72,19 @@ let totalScore = 0; | ||
if (has(value, REF_KEY)) { | ||
const newSchema = retrieveSchema<T, S, F>(validator, value as S, rootSchema, formValue); | ||
return score + calculateIndexScore<T, S, F>(validator, rootSchema, newSchema, formValue || {}); | ||
const newSchema = retrieveSchema<T, S, F>( | ||
validator, | ||
value as S, | ||
rootSchema, | ||
formValue, | ||
experimental_customMergeAllOf, | ||
); | ||
return ( | ||
score + | ||
calculateIndexScore<T, S, F>( | ||
validator, | ||
rootSchema, | ||
newSchema, | ||
formValue || {}, | ||
experimental_customMergeAllOf, | ||
) | ||
); | ||
} | ||
@@ -85,3 +102,4 @@ if ((has(value, ONE_OF_KEY) || has(value, ANY_OF_KEY)) && formValue) { | ||
-1, | ||
discriminator | ||
discriminator, | ||
experimental_customMergeAllOf, | ||
) | ||
@@ -91,3 +109,10 @@ ); | ||
if (value.type === 'object') { | ||
return score + calculateIndexScore<T, S, F>(validator, rootSchema, value as S, formValue || {}); | ||
if (isObject(formValue)) { | ||
// If the structure is matching then give it a little boost in score | ||
score += 1; | ||
} | ||
return ( | ||
score + | ||
calculateIndexScore<T, S, F>(validator, rootSchema, value as S, formValue, experimental_customMergeAllOf) | ||
); | ||
} | ||
@@ -111,3 +136,3 @@ if (value.type === guessType(formValue)) { | ||
}, | ||
0 | ||
0, | ||
); | ||
@@ -141,2 +166,3 @@ } else if (isString(schema.type) && schema.type === guessType(formData)) { | ||
* determine which option is selected | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The index of the option that is the closest match to the `formData` or the `selectedOption` if no match | ||
@@ -147,3 +173,3 @@ */ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
>( | ||
@@ -155,3 +181,4 @@ validator: ValidatorType<T, S, F>, | ||
selectedOption = -1, | ||
discriminatorField?: string | ||
discriminatorField?: string, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): number { | ||
@@ -194,3 +221,3 @@ // First resolve any refs in the options | ||
const option = resolvedOptions[index]; | ||
const score = calculateIndexScore(validator, rootSchema, option, formData); | ||
const score = calculateIndexScore(validator, rootSchema, option, formData, experimental_customMergeAllOf); | ||
scoreCount.add(score); | ||
@@ -202,3 +229,3 @@ if (score > bestScore) { | ||
}, | ||
{ bestIndex: selectedOption, bestScore: 0 } | ||
{ bestIndex: selectedOption, bestScore: 0 }, | ||
); | ||
@@ -205,0 +232,0 @@ // if all scores are the same go with selectedOption |
import get from 'lodash/get'; | ||
import isEmpty from 'lodash/isEmpty'; | ||
import { JSONSchema7Object } from 'json-schema'; | ||
import { | ||
ALL_OF_KEY, | ||
ANY_OF_KEY, | ||
CONST_KEY, | ||
DEFAULT_KEY, | ||
DEPENDENCIES_KEY, | ||
ONE_OF_KEY, | ||
PROPERTIES_KEY, | ||
ONE_OF_KEY, | ||
REF_KEY, | ||
ALL_OF_KEY, | ||
} from '../constants'; | ||
@@ -23,2 +25,3 @@ import findSchemaDefinition from '../findSchemaDefinition'; | ||
import { | ||
Experimental_CustomMergeAllOf, | ||
Experimental_DefaultFormStateBehavior, | ||
@@ -32,4 +35,11 @@ FormContextType, | ||
import isMultiSelect from './isMultiSelect'; | ||
import isSelect from './isSelect'; | ||
import retrieveSchema, { resolveDependencies } from './retrieveSchema'; | ||
import isConstant from '../isConstant'; | ||
import constIsAjvDataReference from '../constIsAjvDataReference'; | ||
import optionsList from '../optionsList'; | ||
import deepEquals from '../deepEquals'; | ||
const PRIMITIVE_TYPES = ['string', 'number', 'integer', 'boolean', 'null']; | ||
/** Enum that indicates how `schema.additionalItems` should be handled by the `getInnerSchemaForArrayItem()` function. | ||
@@ -61,3 +71,3 @@ */ | ||
additionalItems: AdditionalItemsHandling = AdditionalItemsHandling.Ignore, | ||
idx = -1 | ||
idx = -1, | ||
): S { | ||
@@ -98,2 +108,3 @@ if (idx >= 0) { | ||
* 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. | ||
*/ | ||
@@ -107,13 +118,16 @@ function maybeAddDefaultToObject<T = any>( | ||
requiredFields: string[] = [], | ||
experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior = {} | ||
experimental_defaultFormStateBehavior: Experimental_DefaultFormStateBehavior = {}, | ||
isConst = false, | ||
) { | ||
const { emptyObjectFields = 'populateAllDefaults' } = experimental_defaultFormStateBehavior; | ||
if (includeUndefinedValues) { | ||
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 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 emptyObjectFields 'skipEmptyDefaults' store computedDefault if it's a non-empty object(e.g. not {}) | ||
@@ -124,4 +138,3 @@ if (emptyObjectFields === 'skipEmptyDefaults') { | ||
} | ||
} | ||
// Else store computedDefault if it's a non-empty object(e.g. not {}) and satisfies certain conditions | ||
} // 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 | ||
@@ -138,7 +151,8 @@ // Condition 2: If the parent object is required or emptyObjectFields is not 'populateRequiredDefaults' | ||
// Condition 1: computedDefault is not undefined | ||
// Condition 2: If emptyObjectFields is 'populateAllDefaults' or 'skipEmptyDefaults) or if the key is a required field | ||
// 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' || | ||
requiredFields.includes(key)) | ||
(isSelfOrParentRequired && requiredFields.includes(key))) | ||
) { | ||
@@ -151,9 +165,25 @@ obj[key] = computedDefault; | ||
interface ComputeDefaultsProps<T = any, S extends StrictRJSFSchema = RJSFSchema> { | ||
/** Any defaults provided by the parent field in the schema */ | ||
parentDefaults?: T; | ||
/** The options root schema, used to primarily to look up `$ref`s */ | ||
rootSchema?: S; | ||
/** The current formData, if any, onto which to provide any missing defaults */ | ||
rawFormData?: T; | ||
/** 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. | ||
*/ | ||
includeUndefinedValues?: boolean | 'excludeObjectChildren'; | ||
/** The list of ref names currently being recursed, used to prevent infinite recursion */ | ||
_recurseList?: string[]; | ||
/** Optional configuration object, if provided, allows users to override default form state behavior */ | ||
experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior; | ||
/** Optional function that allows for custom merging of `allOf` schemas */ | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>; | ||
/** Optional flag, if true, indicates this schema was required in the parent schema. */ | ||
required?: boolean; | ||
/** Optional flag, if true, It will merge defaults into formData. | ||
* The formData should take precedence unless it's not valid. This is useful 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. | ||
*/ | ||
shouldMergeDefaultsIntoFormData?: boolean; | ||
} | ||
@@ -166,12 +196,3 @@ | ||
* @param rawSchema - The schema for which the default state is desired | ||
* @param [props] - Optional props for this function | ||
* @param [props.parentDefaults] - Any defaults provided by the parent field in the schema | ||
* @param [props.rootSchema] - The options root schema, used to primarily to look up `$ref`s | ||
* @param [props.rawFormData] - The current formData, if any, onto which to provide any missing defaults | ||
* @param [props.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 [props._recurseList=[]] - The list of ref names currently being recursed, used to prevent infinite recursion | ||
* @param [props.experimental_defaultFormStateBehavior] Optional configuration object, if provided, allows users to override default form state behavior | ||
* @param [props.required] - Optional flag, if true, indicates this schema was required in the parent schema. | ||
* @param {ComputeDefaultsProps} computeDefaultsProps - Optional props for this function | ||
* @returns - The resulting `formData` with all the defaults provided | ||
@@ -182,3 +203,5 @@ */ | ||
rawSchema: S, | ||
{ | ||
computeDefaultsProps: ComputeDefaultsProps<T, S> = {}, | ||
): T | T[] | undefined { | ||
const { | ||
parentDefaults, | ||
@@ -190,5 +213,6 @@ rawFormData, | ||
experimental_defaultFormStateBehavior = undefined, | ||
experimental_customMergeAllOf = undefined, | ||
required, | ||
}: ComputeDefaultsProps<T, S> = {} | ||
): T | T[] | undefined { | ||
shouldMergeDefaultsIntoFormData = false, | ||
} = computeDefaultsProps; | ||
const formData: T = (isObject(rawFormData) ? rawFormData : {}) as T; | ||
@@ -200,9 +224,18 @@ const schema: S = isObject(rawSchema) ? rawSchema : ({} as S); | ||
let schemaToCompute: S | null = null; | ||
let experimental_dfsb_to_compute = experimental_defaultFormStateBehavior; | ||
let updatedRecurseList = _recurseList; | ||
if (isObject(defaults) && isObject(schema.default)) { | ||
if ( | ||
schema[CONST_KEY] && | ||
experimental_defaultFormStateBehavior?.constAsDefaults !== 'never' && | ||
!constIsAjvDataReference(schema) | ||
) { | ||
defaults = schema[CONST_KEY] as unknown as T; | ||
} 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 as GenericObjectType) as T; | ||
} else if (DEFAULT_KEY in schema) { | ||
} 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 as unknown as T; | ||
@@ -217,3 +250,16 @@ } else if (REF_KEY in schema) { | ||
} else if (DEPENDENCIES_KEY in schema) { | ||
const resolvedSchema = resolveDependencies<T, S, F>(validator, schema, rootSchema, false, [], formData); | ||
// Get the default if set from properties to ensure the dependencies conditions are resolved based on it | ||
const defaultFormData: T = { | ||
...getDefaultBasedOnSchemaType(validator, schema, computeDefaultsProps, defaults), | ||
...formData, | ||
}; | ||
const resolvedSchema = resolveDependencies<T, S, F>( | ||
validator, | ||
schema, | ||
rootSchema, | ||
false, | ||
[], | ||
defaultFormData, | ||
experimental_customMergeAllOf, | ||
); | ||
schemaToCompute = resolvedSchema[0]; // pick the first element from resolve dependencies | ||
@@ -227,6 +273,8 @@ } else if (isFixedItems(schema)) { | ||
experimental_defaultFormStateBehavior, | ||
experimental_customMergeAllOf, | ||
parentDefaults: Array.isArray(parentDefaults) ? parentDefaults[idx] : undefined, | ||
rawFormData: formData as T, | ||
required, | ||
}) | ||
shouldMergeDefaultsIntoFormData, | ||
}), | ||
) as T[]; | ||
@@ -239,2 +287,14 @@ } else if (ONE_OF_KEY in schema) { | ||
const discriminator = getDiscriminatorFieldFromSchema<S>(schema); | ||
const { type = 'null' } = remaining; | ||
if ( | ||
!Array.isArray(type) && | ||
PRIMITIVE_TYPES.includes(type) && | ||
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![ | ||
@@ -244,6 +304,7 @@ getClosestMatchingOption<T, S, F>( | ||
rootSchema, | ||
isEmpty(formData) ? undefined : formData, | ||
rawFormData ?? (schema.default as T), | ||
oneOf as S[], | ||
0, | ||
discriminator | ||
discriminator, | ||
experimental_customMergeAllOf, | ||
) | ||
@@ -262,6 +323,7 @@ ] as S; | ||
rootSchema, | ||
isEmpty(formData) ? undefined : formData, | ||
rawFormData ?? (schema.default as T), | ||
anyOf as S[], | ||
0, | ||
discriminator | ||
discriminator, | ||
experimental_customMergeAllOf, | ||
) | ||
@@ -277,6 +339,8 @@ ] as S; | ||
_recurseList: updatedRecurseList, | ||
experimental_defaultFormStateBehavior, | ||
experimental_defaultFormStateBehavior: experimental_dfsb_to_compute, | ||
experimental_customMergeAllOf, | ||
parentDefaults: defaults as T | undefined, | ||
rawFormData: formData as T, | ||
required, | ||
shouldMergeDefaultsIntoFormData, | ||
}); | ||
@@ -290,162 +354,341 @@ } | ||
switch (getSchemaType<S>(schema)) { | ||
// We need to recurse for object schema inner default values. | ||
case 'object': { | ||
// This is a custom addition that fixes this issue: | ||
// https://github.com/rjsf-team/react-jsonschema-form/issues/3832 | ||
const retrievedSchema = | ||
experimental_defaultFormStateBehavior?.allOf === 'populateDefaults' && ALL_OF_KEY in schema | ||
? retrieveSchema<T, S, F>(validator, schema, rootSchema, formData) | ||
: schema; | ||
const objectDefaults = Object.keys(retrievedSchema.properties || {}).reduce( | ||
(acc: GenericObjectType, key: string) => { | ||
// Compute the defaults for this node, with the parent defaults we might | ||
// have from a previous run: defaults[key]. | ||
const computedDefault = computeDefaults<T, S, F>(validator, get(retrievedSchema, [PROPERTIES_KEY, key]), { | ||
rootSchema, | ||
_recurseList, | ||
experimental_defaultFormStateBehavior, | ||
includeUndefinedValues: includeUndefinedValues === true, | ||
parentDefaults: get(defaults, [key]), | ||
rawFormData: get(formData, [key]), | ||
required: retrievedSchema.required?.includes(key), | ||
}); | ||
maybeAddDefaultToObject<T>( | ||
acc, | ||
key, | ||
computedDefault, | ||
includeUndefinedValues, | ||
required, | ||
retrievedSchema.required, | ||
experimental_defaultFormStateBehavior | ||
); | ||
return acc; | ||
}, | ||
{} | ||
const defaultBasedOnSchemaType = getDefaultBasedOnSchemaType(validator, schema, computeDefaultsProps, defaults); | ||
let defaultsWithFormData = 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<T>( | ||
defaultsWithFormData as T, | ||
matchingFormData as T, | ||
mergeExtraDefaults, | ||
true, | ||
) as T; | ||
if (retrievedSchema.additionalProperties) { | ||
// as per spec additionalProperties may be either schema or boolean | ||
const additionalPropertiesSchema = isObject(retrievedSchema.additionalProperties) | ||
? retrievedSchema.additionalProperties | ||
: {}; | ||
} | ||
} | ||
const keys = new Set<string>(); | ||
if (isObject(defaults)) { | ||
Object.keys(defaults as GenericObjectType) | ||
.filter((key) => !retrievedSchema.properties || !retrievedSchema.properties[key]) | ||
.forEach((key) => keys.add(key)); | ||
} | ||
const formDataRequired: string[] = []; | ||
Object.keys(formData as GenericObjectType) | ||
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< | ||
T = any, | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any, | ||
>( | ||
validator: ValidatorType<T, S, F>, | ||
schema: S, | ||
rootSchema: S, | ||
formData: T | undefined, | ||
experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): T | T[] | undefined { | ||
const isSelectField = | ||
!isConstant<S>(schema) && isSelect<T, S, F>(validator, schema, rootSchema, experimental_customMergeAllOf); | ||
let validFormData: T | T[] | undefined = formData; | ||
if (isSelectField) { | ||
const getOptionsList = optionsList<T, S, F>(schema); | ||
const isValid = 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?.constAsDefaults === 'always'; | ||
if (constTakesPrecedence) { | ||
validFormData = schema.const as T; | ||
} | ||
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<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>( | ||
validator: ValidatorType<T, S, F>, | ||
rawSchema: S, | ||
{ | ||
rawFormData, | ||
rootSchema = {} as S, | ||
includeUndefinedValues = false, | ||
_recurseList = [], | ||
experimental_defaultFormStateBehavior = undefined, | ||
experimental_customMergeAllOf = undefined, | ||
required, | ||
shouldMergeDefaultsIntoFormData, | ||
}: ComputeDefaultsProps<T, S> = {}, | ||
defaults?: T | T[] | undefined, | ||
): T { | ||
{ | ||
const formData: T = (isObject(rawFormData) ? rawFormData : {}) as T; | ||
const schema: S = rawSchema; | ||
// This is a custom addition that fixes this issue: | ||
// https://github.com/rjsf-team/react-jsonschema-form/issues/3832 | ||
const retrievedSchema = | ||
experimental_defaultFormStateBehavior?.allOf === 'populateDefaults' && ALL_OF_KEY in schema | ||
? retrieveSchema<T, S, F>(validator, schema, rootSchema, formData, experimental_customMergeAllOf) | ||
: schema; | ||
const parentConst = retrievedSchema[CONST_KEY]; | ||
const objectDefaults = Object.keys(retrievedSchema.properties || {}).reduce( | ||
(acc: GenericObjectType, key: string) => { | ||
const propertySchema: S = get(retrievedSchema, [PROPERTIES_KEY, key], {}) as S; | ||
// 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 as JSONSchema7Object)[key] !== undefined; | ||
const hasConst = | ||
((isObject(propertySchema) && CONST_KEY in propertySchema) || hasParentConst) && | ||
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<T, S, F>(validator, propertySchema, { | ||
rootSchema, | ||
_recurseList, | ||
experimental_defaultFormStateBehavior, | ||
experimental_customMergeAllOf, | ||
includeUndefinedValues: includeUndefinedValues === true, | ||
parentDefaults: get(defaults, [key]), | ||
rawFormData: get(formData, [key]), | ||
required: retrievedSchema.required?.includes(key), | ||
shouldMergeDefaultsIntoFormData, | ||
}); | ||
maybeAddDefaultToObject<T>( | ||
acc, | ||
key, | ||
computedDefault, | ||
includeUndefinedValues, | ||
required, | ||
retrievedSchema.required, | ||
experimental_defaultFormStateBehavior, | ||
hasConst, | ||
); | ||
return acc; | ||
}, | ||
{}, | ||
) as T; | ||
if (retrievedSchema.additionalProperties) { | ||
// as per spec additionalProperties may be either schema or boolean | ||
const additionalPropertiesSchema = isObject(retrievedSchema.additionalProperties) | ||
? retrievedSchema.additionalProperties | ||
: {}; | ||
const keys = new Set<string>(); | ||
if (isObject(defaults)) { | ||
Object.keys(defaults as GenericObjectType) | ||
.filter((key) => !retrievedSchema.properties || !retrievedSchema.properties[key]) | ||
.forEach((key) => { | ||
keys.add(key); | ||
formDataRequired.push(key); | ||
}); | ||
keys.forEach((key) => { | ||
const computedDefault = computeDefaults(validator, additionalPropertiesSchema as S, { | ||
rootSchema, | ||
_recurseList, | ||
experimental_defaultFormStateBehavior, | ||
includeUndefinedValues: includeUndefinedValues === true, | ||
parentDefaults: get(defaults, [key]), | ||
rawFormData: get(formData, [key]), | ||
required: retrievedSchema.required?.includes(key), | ||
}); | ||
// Since these are additional properties we don't need to add the `experimental_defaultFormStateBehavior` prop | ||
maybeAddDefaultToObject<T>( | ||
objectDefaults as GenericObjectType, | ||
key, | ||
computedDefault, | ||
includeUndefinedValues, | ||
required, | ||
formDataRequired | ||
); | ||
.forEach((key) => keys.add(key)); | ||
} | ||
const formDataRequired: string[] = []; | ||
Object.keys(formData as GenericObjectType) | ||
.filter((key) => !retrievedSchema.properties || !retrievedSchema.properties[key]) | ||
.forEach((key) => { | ||
keys.add(key); | ||
formDataRequired.push(key); | ||
}); | ||
} | ||
return objectDefaults; | ||
keys.forEach((key) => { | ||
const computedDefault = computeDefaults(validator, additionalPropertiesSchema as S, { | ||
rootSchema, | ||
_recurseList, | ||
experimental_defaultFormStateBehavior, | ||
experimental_customMergeAllOf, | ||
includeUndefinedValues: includeUndefinedValues === true, | ||
parentDefaults: get(defaults, [key]), | ||
rawFormData: get(formData, [key]), | ||
required: retrievedSchema.required?.includes(key), | ||
shouldMergeDefaultsIntoFormData, | ||
}); | ||
// Since these are additional properties we don't need to add the `experimental_defaultFormStateBehavior` prop | ||
maybeAddDefaultToObject<T>( | ||
objectDefaults as GenericObjectType, | ||
key, | ||
computedDefault, | ||
includeUndefinedValues, | ||
required, | ||
formDataRequired, | ||
); | ||
}); | ||
} | ||
case 'array': { | ||
const neverPopulate = experimental_defaultFormStateBehavior?.arrayMinItems?.populate === 'never'; | ||
const ignoreMinItemsFlagSet = experimental_defaultFormStateBehavior?.arrayMinItems?.populate === 'requiredOnly'; | ||
const isSkipEmptyDefaults = experimental_defaultFormStateBehavior?.emptyObjectFields === 'skipEmptyDefaults'; | ||
const computeSkipPopulate = | ||
experimental_defaultFormStateBehavior?.arrayMinItems?.computeSkipPopulate ?? (() => false); | ||
return objectDefaults; | ||
} | ||
} | ||
const emptyDefault = isSkipEmptyDefaults ? undefined : []; | ||
/** 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<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>( | ||
validator: ValidatorType<T, S, F>, | ||
rawSchema: S, | ||
{ | ||
rawFormData, | ||
rootSchema = {} as S, | ||
_recurseList = [], | ||
experimental_defaultFormStateBehavior = undefined, | ||
experimental_customMergeAllOf = undefined, | ||
required, | ||
shouldMergeDefaultsIntoFormData, | ||
}: ComputeDefaultsProps<T, S> = {}, | ||
defaults?: T | T[] | undefined, | ||
): T | T[] | undefined { | ||
const schema: S = rawSchema; | ||
// Inject defaults into existing array defaults | ||
if (Array.isArray(defaults)) { | ||
defaults = defaults.map((item, idx) => { | ||
const schemaItem: S = getInnerSchemaForArrayItem<S>(schema, AdditionalItemsHandling.Fallback, idx); | ||
return computeDefaults<T, S, F>(validator, schemaItem, { | ||
rootSchema, | ||
_recurseList, | ||
experimental_defaultFormStateBehavior, | ||
parentDefaults: item, | ||
required, | ||
}); | ||
}) as T[]; | ||
} | ||
const arrayMinItemsStateBehavior = experimental_defaultFormStateBehavior?.arrayMinItems ?? {}; | ||
const { populate: arrayMinItemsPopulate, mergeExtraDefaults: arrayMergeExtraDefaults } = arrayMinItemsStateBehavior; | ||
// Deeply inject defaults into already existing form data | ||
if (Array.isArray(rawFormData)) { | ||
const schemaItem: S = getInnerSchemaForArrayItem<S>(schema); | ||
if (neverPopulate) { | ||
defaults = rawFormData; | ||
} else { | ||
defaults = rawFormData.map((item: T, idx: number) => { | ||
return computeDefaults<T, S, F>(validator, schemaItem, { | ||
rootSchema, | ||
_recurseList, | ||
experimental_defaultFormStateBehavior, | ||
rawFormData: item, | ||
parentDefaults: get(defaults, [idx]), | ||
required, | ||
}); | ||
}) as T[]; | ||
} | ||
} | ||
const neverPopulate = arrayMinItemsPopulate === 'never'; | ||
const ignoreMinItemsFlagSet = arrayMinItemsPopulate === 'requiredOnly'; | ||
const isPopulateAll = arrayMinItemsPopulate === 'all' || (!neverPopulate && !ignoreMinItemsFlagSet); | ||
const computeSkipPopulate = arrayMinItemsStateBehavior?.computeSkipPopulate ?? (() => false); | ||
const isSkipEmptyDefaults = experimental_defaultFormStateBehavior?.emptyObjectFields === 'skipEmptyDefaults'; | ||
if (neverPopulate) { | ||
return 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 emptyDefault = isSkipEmptyDefaults ? undefined : []; | ||
const defaultsLength = Array.isArray(defaults) ? defaults.length : 0; | ||
if ( | ||
!schema.minItems || | ||
isMultiSelect<T, S, F>(validator, schema, rootSchema) || | ||
computeSkipPopulate<T, S, F>(validator, schema, rootSchema) || | ||
schema.minItems <= defaultsLength | ||
) { | ||
return defaults ? defaults : emptyDefault; | ||
} | ||
// Inject defaults into existing array defaults | ||
if (Array.isArray(defaults)) { | ||
defaults = defaults.map((item, idx) => { | ||
const schemaItem: S = getInnerSchemaForArrayItem<S>(schema, AdditionalItemsHandling.Fallback, idx); | ||
return computeDefaults<T, S, F>(validator, schemaItem, { | ||
rootSchema, | ||
_recurseList, | ||
experimental_defaultFormStateBehavior, | ||
experimental_customMergeAllOf, | ||
parentDefaults: item, | ||
required, | ||
shouldMergeDefaultsIntoFormData, | ||
}); | ||
}) as T[]; | ||
} | ||
const defaultEntries: T[] = (defaults || []) as T[]; | ||
const fillerSchema: S = getInnerSchemaForArrayItem<S>(schema, AdditionalItemsHandling.Invert); | ||
const fillerDefault = fillerSchema.default; | ||
// Calculate filler entries for remaining items (minItems - existing raw data/defaults) | ||
const fillerEntries: T[] = new Array(schema.minItems - defaultsLength).fill( | ||
computeDefaults<any, S, F>(validator, fillerSchema, { | ||
parentDefaults: fillerDefault, | ||
// Deeply inject defaults into already existing form data | ||
if (Array.isArray(rawFormData)) { | ||
const schemaItem: S = getInnerSchemaForArrayItem<S>(schema); | ||
if (neverPopulate) { | ||
defaults = rawFormData; | ||
} else { | ||
const itemDefaults = rawFormData.map((item: T, idx: number) => { | ||
return computeDefaults<T, S, F>(validator, schemaItem, { | ||
rootSchema, | ||
_recurseList, | ||
experimental_defaultFormStateBehavior, | ||
experimental_customMergeAllOf, | ||
rawFormData: item, | ||
parentDefaults: get(defaults, [idx]), | ||
required, | ||
}) | ||
) as T[]; | ||
// then fill up the rest with either the item default or empty, up to minItems | ||
return defaultEntries.concat(fillerEntries); | ||
shouldMergeDefaultsIntoFormData, | ||
}); | ||
}) as T[]; | ||
// 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); | ||
} | ||
} | ||
return defaults; | ||
// 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?.constAsDefaults !== 'never'; | ||
if (hasConst === false) { | ||
if (neverPopulate) { | ||
return 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<T, S, F>(validator, schema, rootSchema, experimental_customMergeAllOf) || | ||
computeSkipPopulate<T, S, F>(validator, schema, rootSchema) || | ||
schema.minItems <= defaultsLength | ||
) { | ||
return defaults ? defaults : emptyDefault; | ||
} | ||
const defaultEntries: T[] = (defaults || []) as T[]; | ||
const fillerSchema: S = getInnerSchemaForArrayItem<S>(schema, AdditionalItemsHandling.Invert); | ||
const fillerDefault = fillerSchema.default; | ||
// Calculate filler entries for remaining items (minItems - existing raw data/defaults) | ||
const fillerEntries: T[] = new Array(schema.minItems - defaultsLength).fill( | ||
computeDefaults<any, S, F>(validator, fillerSchema, { | ||
parentDefaults: fillerDefault, | ||
rootSchema, | ||
_recurseList, | ||
experimental_defaultFormStateBehavior, | ||
experimental_customMergeAllOf, | ||
required, | ||
shouldMergeDefaultsIntoFormData, | ||
}), | ||
) as T[]; | ||
// 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< | ||
T = any, | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any, | ||
>( | ||
validator: ValidatorType<T, S, F>, | ||
rawSchema: S, | ||
computeDefaultsProps: ComputeDefaultsProps<T, S> = {}, | ||
defaults?: T | T[] | undefined, | ||
): T | T[] | void { | ||
switch (getSchemaType<S>(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 | ||
@@ -462,2 +705,3 @@ * computed to have defaults provided in the `schema`. | ||
* @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 | ||
@@ -468,3 +712,3 @@ */ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
>( | ||
@@ -476,3 +720,4 @@ validator: ValidatorType<T, S, F>, | ||
includeUndefinedValues: boolean | 'excludeObjectChildren' = false, | ||
experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior | ||
experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
) { | ||
@@ -482,3 +727,7 @@ if (!isObject(theSchema)) { | ||
} | ||
const schema = retrieveSchema<T, S, F>(validator, theSchema, rootSchema, formData); | ||
const schema = retrieveSchema<T, S, F>(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<T, S, F>(validator, schema, { | ||
@@ -488,16 +737,23 @@ rootSchema, | ||
experimental_defaultFormStateBehavior, | ||
experimental_customMergeAllOf, | ||
rawFormData: formData, | ||
shouldMergeDefaultsIntoFormData: true, | ||
}); | ||
if (formData === undefined || formData === null || (typeof formData === 'number' && isNaN(formData))) { | ||
// No form data? Use schema defaults. | ||
return defaults; | ||
// 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<T | T[]>( | ||
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; | ||
} | ||
const { mergeExtraDefaults } = experimental_defaultFormStateBehavior?.arrayMinItems || {}; | ||
if (isObject(formData)) { | ||
return mergeDefaultsWithFormData<T>(defaults as T, formData, mergeExtraDefaults); | ||
} | ||
if (Array.isArray(formData)) { | ||
return mergeDefaultsWithFormData<T[]>(defaults as T[], formData, mergeExtraDefaults); | ||
} | ||
return formData; | ||
return defaults; | ||
} |
@@ -12,2 +12,3 @@ import { UI_FIELD_KEY, UI_WIDGET_KEY } from '../constants'; | ||
ValidatorType, | ||
Experimental_CustomMergeAllOf, | ||
} from '../types'; | ||
@@ -25,2 +26,3 @@ import isFilesArray from './isFilesArray'; | ||
* @param [globalOptions={}] - The optional Global UI Schema from which to get any fallback `xxx` options | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - True if the label should be displayed or false if it should not | ||
@@ -31,3 +33,3 @@ */ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
>( | ||
@@ -38,3 +40,4 @@ validator: ValidatorType<T, S, F>, | ||
rootSchema?: S, | ||
globalOptions?: GlobalUISchemaOptions | ||
globalOptions?: GlobalUISchemaOptions, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): boolean { | ||
@@ -48,4 +51,4 @@ const uiOptions = getUiOptions<T, S, F>(uiSchema, globalOptions); | ||
displayLabel = | ||
isMultiSelect<T, S, F>(validator, schema, rootSchema) || | ||
isFilesArray<T, S, F>(validator, schema, uiSchema, rootSchema) || | ||
isMultiSelect<T, S, F>(validator, schema, rootSchema, experimental_customMergeAllOf) || | ||
isFilesArray<T, S, F>(validator, schema, uiSchema, rootSchema, experimental_customMergeAllOf) || | ||
isCustomWidget(uiSchema); | ||
@@ -52,0 +55,0 @@ } |
@@ -1,2 +0,7 @@ | ||
import getMatchingOption from './getMatchingOption'; | ||
import get from 'lodash/get'; | ||
import has from 'lodash/has'; | ||
import isNumber from 'lodash/isNumber'; | ||
import { PROPERTIES_KEY } from '../constants'; | ||
import getOptionMatchingSimpleDiscriminator from '../getOptionMatchingSimpleDiscriminator'; | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
@@ -18,3 +23,3 @@ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
>( | ||
@@ -25,5 +30,75 @@ validator: ValidatorType<T, S, F>, | ||
rootSchema: S, | ||
discriminatorField?: string | ||
discriminatorField?: string, | ||
): number { | ||
return getMatchingOption<T, S, F>(validator, formData, options, rootSchema, discriminatorField); | ||
// For performance, skip validating subschemas if formData is undefined. We just | ||
// want to get the first option in that case. | ||
if (formData === undefined) { | ||
return 0; | ||
} | ||
const simpleDiscriminatorMatch = getOptionMatchingSimpleDiscriminator(formData, options, discriminatorField); | ||
if (isNumber(simpleDiscriminatorMatch)) { | ||
return simpleDiscriminatorMatch; | ||
} | ||
for (let i = 0; i < options.length; i++) { | ||
const option = options[i]; | ||
// If we have a discriminator field, then we will use this to make the determination | ||
if (discriminatorField && has(option, [PROPERTIES_KEY, discriminatorField])) { | ||
const value = get(formData, discriminatorField); | ||
const discriminator: S = get(option, [PROPERTIES_KEY, discriminatorField], {}) as S; | ||
if (validator.isValid(discriminator, value, rootSchema)) { | ||
return i; | ||
} | ||
} else if (option[PROPERTIES_KEY]) { | ||
// If the schema describes an object then we need to add slightly more | ||
// strict matching to the schema, because unless the schema uses the | ||
// "requires" keyword, an object will match the schema as long as it | ||
// doesn't have matching keys with a conflicting type. To do this we use an | ||
// "anyOf" with an array of requires. This augmentation expresses that the | ||
// schema should match if any of the keys in the schema are present on the | ||
// object and pass validation. | ||
// | ||
// Create an "anyOf" schema that requires at least one of the keys in the | ||
// "properties" object | ||
const requiresAnyOf = { | ||
anyOf: Object.keys(option[PROPERTIES_KEY]).map((key) => ({ | ||
required: [key], | ||
})), | ||
}; | ||
let augmentedSchema; | ||
// If the "anyOf" keyword already exists, wrap the augmentation in an "allOf" | ||
if (option.anyOf) { | ||
// Create a shallow clone of the option | ||
const { ...shallowClone } = option; | ||
if (!shallowClone.allOf) { | ||
shallowClone.allOf = []; | ||
} else { | ||
// If "allOf" already exists, shallow clone the array | ||
shallowClone.allOf = shallowClone.allOf.slice(); | ||
} | ||
shallowClone.allOf.push(requiresAnyOf); | ||
augmentedSchema = shallowClone; | ||
} else { | ||
augmentedSchema = Object.assign({}, option, requiresAnyOf); | ||
} | ||
// Remove the "required" field as it's likely that not all fields have | ||
// been filled in yet, which will mean that the schema is not valid | ||
delete augmentedSchema.required; | ||
if (validator.isValid(augmentedSchema, formData, rootSchema)) { | ||
return i; | ||
} | ||
} else if (validator.isValid(option, formData, rootSchema)) { | ||
return i; | ||
} | ||
} | ||
return 0; | ||
} |
@@ -0,1 +1,3 @@ | ||
import findFieldInSchema from './findFieldInSchema'; | ||
import findSelectedOptionInXxxOf from './findSelectedOptionInXxxOf'; | ||
import getDefaultFormState from './getDefaultFormState'; | ||
@@ -5,7 +7,6 @@ import getDisplayLabel from './getDisplayLabel'; | ||
import getFirstMatchingOption from './getFirstMatchingOption'; | ||
import getMatchingOption from './getMatchingOption'; | ||
import getFromSchema from './getFromSchema'; | ||
import isFilesArray from './isFilesArray'; | ||
import isMultiSelect from './isMultiSelect'; | ||
import isSelect from './isSelect'; | ||
import mergeValidationData from './mergeValidationData'; | ||
import retrieveSchema from './retrieveSchema'; | ||
@@ -17,2 +18,4 @@ import sanitizeDataForNewSchema from './sanitizeDataForNewSchema'; | ||
export { | ||
findFieldInSchema, | ||
findSelectedOptionInXxxOf, | ||
getDefaultFormState, | ||
@@ -22,7 +25,6 @@ getDisplayLabel, | ||
getFirstMatchingOption, | ||
getMatchingOption, | ||
getFromSchema, | ||
isFilesArray, | ||
isMultiSelect, | ||
isSelect, | ||
mergeValidationData, | ||
retrieveSchema, | ||
@@ -29,0 +31,0 @@ sanitizeDataForNewSchema, |
import { UI_WIDGET_KEY } from '../constants'; | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, UiSchema, ValidatorType } from '../types'; | ||
import { | ||
Experimental_CustomMergeAllOf, | ||
FormContextType, | ||
RJSFSchema, | ||
StrictRJSFSchema, | ||
UiSchema, | ||
ValidatorType, | ||
} from '../types'; | ||
import retrieveSchema from './retrieveSchema'; | ||
@@ -11,2 +18,3 @@ | ||
* @param [rootSchema] - The root schema, used to primarily to look up `$ref`s | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - True if schema/uiSchema contains an array of files, otherwise false | ||
@@ -18,3 +26,4 @@ */ | ||
uiSchema: UiSchema<T, S, F> = {}, | ||
rootSchema?: S | ||
rootSchema?: S, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
) { | ||
@@ -25,3 +34,9 @@ if (uiSchema[UI_WIDGET_KEY] === 'files') { | ||
if (schema.items) { | ||
const itemsSchema = retrieveSchema<T, S, F>(validator, schema.items as S, rootSchema); | ||
const itemsSchema = retrieveSchema<T, S, F>( | ||
validator, | ||
schema.items as S, | ||
rootSchema, | ||
undefined, | ||
experimental_customMergeAllOf, | ||
); | ||
return itemsSchema.type === 'string' && itemsSchema.format === 'data-url'; | ||
@@ -28,0 +43,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType, Experimental_CustomMergeAllOf } from '../types'; | ||
@@ -10,2 +10,3 @@ import isSelect from './isSelect'; | ||
* @param [rootSchema] - The root schema, used to primarily to look up `$ref`s | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - True if schema contains a multi-select, otherwise false | ||
@@ -16,8 +17,13 @@ */ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
>(validator: ValidatorType<T, S, F>, schema: S, rootSchema?: S) { | ||
F extends FormContextType = any, | ||
>( | ||
validator: ValidatorType<T, S, F>, | ||
schema: S, | ||
rootSchema?: S, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
) { | ||
if (!schema.uniqueItems || !schema.items || typeof schema.items === 'boolean') { | ||
return false; | ||
} | ||
return isSelect<T, S, F>(validator, schema.items as S, rootSchema); | ||
return isSelect<T, S, F>(validator, schema.items as S, rootSchema, experimental_customMergeAllOf); | ||
} |
import isConstant from '../isConstant'; | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType, Experimental_CustomMergeAllOf } from '../types'; | ||
import retrieveSchema from './retrieveSchema'; | ||
@@ -10,2 +10,3 @@ | ||
* @param [rootSchema] - The root schema, used to primarily to look up `$ref`s | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - True if schema contains a select, otherwise false | ||
@@ -16,5 +17,6 @@ */ | ||
theSchema: S, | ||
rootSchema: S = {} as S | ||
rootSchema: S = {} as S, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
) { | ||
const schema = retrieveSchema<T, S, F>(validator, theSchema, rootSchema, undefined); | ||
const schema = retrieveSchema<T, S, F>(validator, theSchema, rootSchema, undefined, experimental_customMergeAllOf); | ||
const altSchemas = schema.oneOf || schema.anyOf; | ||
@@ -21,0 +23,0 @@ if (Array.isArray(schema.enum)) { |
import get from 'lodash/get'; | ||
import isEqual from 'lodash/isEqual'; | ||
import set from 'lodash/set'; | ||
@@ -18,6 +17,7 @@ import times from 'lodash/times'; | ||
IF_KEY, | ||
ITEMS_KEY, | ||
ONE_OF_KEY, | ||
PATTERN_PROPERTIES_KEY, | ||
PROPERTIES_KEY, | ||
REF_KEY, | ||
PROPERTIES_KEY, | ||
ITEMS_KEY, | ||
} from '../constants'; | ||
@@ -29,4 +29,13 @@ import findSchemaDefinition, { splitKeyElementFromObject } from '../findSchemaDefinition'; | ||
import mergeSchemas from '../mergeSchemas'; | ||
import { FormContextType, GenericObjectType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import { | ||
Experimental_CustomMergeAllOf, | ||
FormContextType, | ||
GenericObjectType, | ||
RJSFSchema, | ||
StrictRJSFSchema, | ||
ValidatorType, | ||
} from '../types'; | ||
import getFirstMatchingOption from './getFirstMatchingOption'; | ||
import deepEquals from '../deepEquals'; | ||
import isEmpty from 'lodash/isEmpty'; | ||
@@ -41,2 +50,3 @@ /** Retrieves an expanded schema that has had all of its conditions, additional properties, references and dependencies | ||
* @param [rawFormData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The schema having its conditions, additional properties, references and dependencies resolved | ||
@@ -47,5 +57,19 @@ */ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S = {} as S, rawFormData?: T): S { | ||
return retrieveSchemaInternal<T, S, F>(validator, schema, rootSchema, rawFormData)[0]; | ||
F extends FormContextType = any, | ||
>( | ||
validator: ValidatorType<T, S, F>, | ||
schema: S, | ||
rootSchema: S = {} as S, | ||
rawFormData?: T, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): S { | ||
return retrieveSchemaInternal<T, S, F>( | ||
validator, | ||
schema, | ||
rootSchema, | ||
rawFormData, | ||
undefined, | ||
undefined, | ||
experimental_customMergeAllOf, | ||
)[0]; | ||
} | ||
@@ -64,2 +88,3 @@ | ||
* @param [formData] - The current formData to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - A list of schemas with the appropriate conditions resolved, possibly with all branches expanded | ||
@@ -73,3 +98,4 @@ */ | ||
recurseList: string[], | ||
formData?: T | ||
formData?: T, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): S[] { | ||
@@ -84,3 +110,11 @@ const { if: expression, then, else: otherwise, ...resolvedSchemaLessConditional } = schema; | ||
schemas = schemas.concat( | ||
retrieveSchemaInternal<T, S, F>(validator, then as S, rootSchema, formData, expandAllBranches, recurseList) | ||
retrieveSchemaInternal<T, S, F>( | ||
validator, | ||
then as S, | ||
rootSchema, | ||
formData, | ||
expandAllBranches, | ||
recurseList, | ||
experimental_customMergeAllOf, | ||
), | ||
); | ||
@@ -90,3 +124,11 @@ } | ||
schemas = schemas.concat( | ||
retrieveSchemaInternal<T, S, F>(validator, otherwise as S, rootSchema, formData, expandAllBranches, recurseList) | ||
retrieveSchemaInternal<T, S, F>( | ||
validator, | ||
otherwise as S, | ||
rootSchema, | ||
formData, | ||
expandAllBranches, | ||
recurseList, | ||
experimental_customMergeAllOf, | ||
), | ||
); | ||
@@ -104,4 +146,5 @@ } | ||
expandAllBranches, | ||
recurseList | ||
) | ||
recurseList, | ||
experimental_customMergeAllOf, | ||
), | ||
); | ||
@@ -114,3 +157,11 @@ } | ||
return resolvedSchemas.flatMap((s) => | ||
retrieveSchemaInternal<T, S, F>(validator, s, rootSchema, formData, expandAllBranches, recurseList) | ||
retrieveSchemaInternal<T, S, F>( | ||
validator, | ||
s, | ||
rootSchema, | ||
formData, | ||
expandAllBranches, | ||
recurseList, | ||
experimental_customMergeAllOf, | ||
), | ||
); | ||
@@ -144,3 +195,3 @@ } | ||
}, | ||
[[]] as S[][] // Start with an empty list | ||
[[]] as S[][], // Start with an empty list | ||
); | ||
@@ -151,2 +202,24 @@ | ||
/** Returns the subset of 'patternProperties' specifications that match the given 'key' | ||
* | ||
* @param schema - The schema whose 'patternProperties' are to be filtered | ||
* @param key - The key to match against the 'patternProperties' specifications | ||
* @returns - The subset of 'patternProperties' specifications that match the given 'key' | ||
*/ | ||
export function getMatchingPatternProperties<S extends StrictRJSFSchema = RJSFSchema>( | ||
schema: S, | ||
key: string, | ||
): Required<S['patternProperties']> { | ||
return Object.keys(schema.patternProperties!) | ||
.filter((pattern: string) => RegExp(pattern).test(key)) | ||
.reduce( | ||
(obj, pattern) => { | ||
// Pass the pattern using the `[]` index notation so that any `.` in the pattern are not used as a dotted path | ||
set(obj, [pattern], schema.patternProperties![pattern]); | ||
return obj; | ||
}, | ||
{} as Required<S['patternProperties']>, | ||
); | ||
} | ||
/** Resolves references and dependencies within a schema and its 'allOf' children. Passes the `expandAllBranches` flag | ||
@@ -163,2 +236,3 @@ * down to the `retrieveSchemaInternal()`, `resolveReference()` and `resolveDependencies()` helper calls. If | ||
* @param [formData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The list of schemas having its references, dependencies and allOf schemas resolved | ||
@@ -172,3 +246,4 @@ */ | ||
recurseList: string[], | ||
formData?: T | ||
formData?: T, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): S[] { | ||
@@ -181,3 +256,3 @@ const updatedSchemas = resolveReference<T, S, F>( | ||
recurseList, | ||
formData | ||
formData, | ||
); | ||
@@ -196,6 +271,14 @@ if (updatedSchemas.length > 1 || updatedSchemas[0] !== schema) { | ||
recurseList, | ||
formData | ||
formData, | ||
); | ||
return resolvedSchemas.flatMap((s) => { | ||
return retrieveSchemaInternal<T, S, F>(validator, s, rootSchema, formData, expandAllBranches, recurseList); | ||
return retrieveSchemaInternal<T, S, F>( | ||
validator, | ||
s, | ||
rootSchema, | ||
formData, | ||
expandAllBranches, | ||
recurseList, | ||
experimental_customMergeAllOf, | ||
); | ||
}); | ||
@@ -211,7 +294,11 @@ } | ||
expandAllBranches, | ||
recurseList | ||
) | ||
recurseList, | ||
experimental_customMergeAllOf, | ||
), | ||
); | ||
const allPermutations = getAllPermutationsOfXxxOf<S>(allOfSchemaElements); | ||
return allPermutations.map((permutation) => ({ ...schema, allOf: permutation })); | ||
return allPermutations.map((permutation) => ({ | ||
...schema, | ||
allOf: permutation, | ||
})); | ||
} | ||
@@ -233,2 +320,3 @@ // No $ref or dependencies or allOf attribute was found, returning the original schema. | ||
* @param [formData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The list schemas retrieved after having all references resolved | ||
@@ -242,3 +330,4 @@ */ | ||
recurseList: string[], | ||
formData?: T | ||
formData?: T, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): S[] { | ||
@@ -254,3 +343,4 @@ const updatedSchema = resolveAllReferences<S>(schema, rootSchema, recurseList); | ||
expandAllBranches, | ||
recurseList | ||
recurseList, | ||
experimental_customMergeAllOf, | ||
); | ||
@@ -271,3 +361,3 @@ } | ||
rootSchema: S, | ||
recurseList: string[] | ||
recurseList: string[], | ||
): S { | ||
@@ -300,3 +390,3 @@ if (!isObject(schema)) { | ||
}, | ||
{} as RJSFSchema | ||
{} as RJSFSchema, | ||
); | ||
@@ -318,3 +408,3 @@ merge(recurseList, uniq(flattenDeep(childrenLists))); | ||
return isEqual(schema, resolvedSchema) ? schema : resolvedSchema; | ||
return deepEquals(schema, resolvedSchema) ? schema : resolvedSchema; | ||
} | ||
@@ -328,2 +418,3 @@ | ||
* @param [aFormData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The updated schema with additional properties stubbed | ||
@@ -334,4 +425,10 @@ */ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
>(validator: ValidatorType<T, S, F>, theSchema: S, rootSchema?: S, aFormData?: T): S { | ||
F extends FormContextType = any, | ||
>( | ||
validator: ValidatorType<T, S, F>, | ||
theSchema: S, | ||
rootSchema?: S, | ||
aFormData?: T, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): S { | ||
// Clone the schema so that we don't ruin the consumer's original | ||
@@ -350,30 +447,51 @@ const schema = { | ||
} | ||
let additionalProperties: S['additionalProperties'] = {}; | ||
if (typeof schema.additionalProperties !== 'boolean') { | ||
if (REF_KEY in schema.additionalProperties!) { | ||
additionalProperties = retrieveSchema<T, S, F>( | ||
if (PATTERN_PROPERTIES_KEY in schema) { | ||
const matchingProperties = getMatchingPatternProperties(schema, key); | ||
if (!isEmpty(matchingProperties)) { | ||
schema.properties[key] = retrieveSchema<T, S, F>( | ||
validator, | ||
{ $ref: get(schema.additionalProperties, [REF_KEY]) } as S, | ||
{ allOf: Object.values(matchingProperties) } as S, | ||
rootSchema, | ||
formData as T | ||
formData as T, | ||
experimental_customMergeAllOf, | ||
); | ||
} else if ('type' in schema.additionalProperties!) { | ||
additionalProperties = { ...schema.additionalProperties }; | ||
} else if (ANY_OF_KEY in schema.additionalProperties! || ONE_OF_KEY in schema.additionalProperties!) { | ||
additionalProperties = { | ||
type: 'object', | ||
...schema.additionalProperties, | ||
}; | ||
set(schema.properties, [key, ADDITIONAL_PROPERTY_FLAG], true); | ||
return; | ||
} | ||
} | ||
if (ADDITIONAL_PROPERTIES_KEY in schema && schema.additionalProperties !== false) { | ||
let additionalProperties: S['additionalProperties'] = {}; | ||
if (typeof schema.additionalProperties !== 'boolean') { | ||
if (REF_KEY in schema.additionalProperties!) { | ||
additionalProperties = retrieveSchema<T, S, F>( | ||
validator, | ||
{ $ref: get(schema.additionalProperties, [REF_KEY]) } as S, | ||
rootSchema, | ||
formData as T, | ||
experimental_customMergeAllOf, | ||
); | ||
} else if ('type' in schema.additionalProperties!) { | ||
additionalProperties = { ...schema.additionalProperties }; | ||
} else if (ANY_OF_KEY in schema.additionalProperties! || ONE_OF_KEY in schema.additionalProperties!) { | ||
additionalProperties = { | ||
type: 'object', | ||
...schema.additionalProperties, | ||
}; | ||
} else { | ||
additionalProperties = { type: guessType(get(formData, [key])) }; | ||
} | ||
} else { | ||
additionalProperties = { type: guessType(get(formData, [key])) }; | ||
} | ||
// The type of our new key should match the additionalProperties value; | ||
schema.properties[key] = additionalProperties; | ||
// Set our additional property flag so we know it was dynamically added | ||
set(schema.properties, [key, ADDITIONAL_PROPERTY_FLAG], true); | ||
} else { | ||
additionalProperties = { type: guessType(get(formData, [key])) }; | ||
// Invalid property | ||
schema.properties[key] = { type: 'null' }; | ||
// Set our additional property flag so we know it was dynamically added | ||
set(schema.properties, [key, ADDITIONAL_PROPERTY_FLAG], true); | ||
} | ||
// The type of our new key should match the additionalProperties value; | ||
schema.properties[key] = additionalProperties; | ||
// Set our additional property flag so we know it was dynamically added | ||
set(schema.properties, [key, ADDITIONAL_PROPERTY_FLAG], true); | ||
}); | ||
@@ -396,2 +514,3 @@ | ||
* @param [recurseList=[]] - The optional, list of recursive references already processed | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The schema(s) resulting from having its conditions, additional properties, references and dependencies | ||
@@ -403,3 +522,3 @@ * resolved. Multiple schemas may be returned if `expandAllBranches` is true. | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
>( | ||
@@ -411,3 +530,4 @@ validator: ValidatorType<T, S, F>, | ||
expandAllBranches = false, | ||
recurseList: string[] = [] | ||
recurseList: string[] = [], | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): S[] { | ||
@@ -423,3 +543,4 @@ if (!isObject(schema)) { | ||
recurseList, | ||
rawFormData | ||
rawFormData, | ||
experimental_customMergeAllOf, | ||
); | ||
@@ -435,3 +556,4 @@ return resolvedSchemas.flatMap((s: S) => { | ||
recurseList, | ||
rawFormData as T | ||
rawFormData as T, | ||
experimental_customMergeAllOf, | ||
); | ||
@@ -446,5 +568,22 @@ } | ||
try { | ||
resolvedSchema = mergeAllOf(resolvedSchema, { | ||
deep: false, | ||
} as Options) as S; | ||
const withContainsSchemas = [] as S[]; | ||
const withoutContainsSchemas = [] as S[]; | ||
resolvedSchema.allOf?.forEach((s) => { | ||
if (typeof s === 'object' && s.contains) { | ||
withContainsSchemas.push(s as S); | ||
} else { | ||
withoutContainsSchemas.push(s as S); | ||
} | ||
}); | ||
if (withContainsSchemas.length) { | ||
resolvedSchema = { ...resolvedSchema, allOf: withoutContainsSchemas }; | ||
} | ||
resolvedSchema = experimental_customMergeAllOf | ||
? experimental_customMergeAllOf(resolvedSchema) | ||
: (mergeAllOf(resolvedSchema, { | ||
deep: false, | ||
} as Options) as S); | ||
if (withContainsSchemas.length) { | ||
resolvedSchema.allOf = withContainsSchemas; | ||
} | ||
} catch (e) { | ||
@@ -456,6 +595,34 @@ console.warn('could not merge subschemas in allOf:\n', e); | ||
} | ||
if (PROPERTIES_KEY in resolvedSchema && PATTERN_PROPERTIES_KEY in resolvedSchema) { | ||
resolvedSchema = Object.keys(resolvedSchema.properties!).reduce( | ||
(schema, key) => { | ||
const matchingProperties = getMatchingPatternProperties(schema, key); | ||
if (!isEmpty(matchingProperties)) { | ||
schema.properties[key] = retrieveSchema<T, S, F>( | ||
validator, | ||
{ allOf: [schema.properties[key], ...Object.values(matchingProperties)] } as S, | ||
rootSchema, | ||
rawFormData as T, | ||
experimental_customMergeAllOf, | ||
); | ||
} | ||
return schema; | ||
}, | ||
{ | ||
...resolvedSchema, | ||
properties: { ...resolvedSchema.properties }, | ||
}, | ||
); | ||
} | ||
const hasAdditionalProperties = | ||
ADDITIONAL_PROPERTIES_KEY in resolvedSchema && resolvedSchema.additionalProperties !== false; | ||
PATTERN_PROPERTIES_KEY in resolvedSchema || | ||
(ADDITIONAL_PROPERTIES_KEY in resolvedSchema && resolvedSchema.additionalProperties !== false); | ||
if (hasAdditionalProperties) { | ||
return stubExistingAdditionalProperties<T, S, F>(validator, resolvedSchema, rootSchema, rawFormData as T); | ||
return stubExistingAdditionalProperties<T, S, F>( | ||
validator, | ||
resolvedSchema, | ||
rootSchema, | ||
rawFormData as T, | ||
experimental_customMergeAllOf, | ||
); | ||
} | ||
@@ -482,3 +649,3 @@ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
>(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S, expandAllBranches: boolean, rawFormData?: T) { | ||
@@ -521,2 +688,3 @@ let anyOrOneOf: S[] | undefined; | ||
* @param [formData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The list of schemas with their dependencies resolved | ||
@@ -530,3 +698,4 @@ */ | ||
recurseList: string[], | ||
formData?: T | ||
formData?: T, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): S[] { | ||
@@ -540,3 +709,3 @@ // Drop the dependencies from the source schema. | ||
expandAllBranches, | ||
formData | ||
formData, | ||
); | ||
@@ -551,4 +720,5 @@ return resolvedSchemas.flatMap((resolvedSchema) => | ||
recurseList, | ||
formData | ||
) | ||
formData, | ||
experimental_customMergeAllOf, | ||
), | ||
); | ||
@@ -568,2 +738,3 @@ } | ||
* @param [formData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The schema with the `dependencies` resolved into it | ||
@@ -578,3 +749,4 @@ */ | ||
recurseList: string[], | ||
formData?: T | ||
formData?: T, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): S[] { | ||
@@ -594,3 +766,3 @@ let schemas = [resolvedSchema]; | ||
dependencyKey, | ||
dependencies as GenericObjectType | ||
dependencies as GenericObjectType, | ||
); | ||
@@ -608,3 +780,4 @@ if (Array.isArray(dependencyValue)) { | ||
recurseList, | ||
formData | ||
formData, | ||
experimental_customMergeAllOf, | ||
); | ||
@@ -620,4 +793,5 @@ } | ||
recurseList, | ||
formData | ||
) | ||
formData, | ||
experimental_customMergeAllOf, | ||
), | ||
); | ||
@@ -636,3 +810,3 @@ } | ||
schema: S, | ||
additionallyRequired?: string[] | ||
additionallyRequired?: string[], | ||
) { | ||
@@ -660,2 +834,3 @@ if (!additionallyRequired) { | ||
* @param [formData]- The current formData to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The list of schemas with the dependent schema resolved into them | ||
@@ -671,3 +846,4 @@ */ | ||
recurseList: string[], | ||
formData?: T | ||
formData?: T, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): S[] { | ||
@@ -680,3 +856,4 @@ const dependentSchemas = retrieveSchemaInternal<T, S, F>( | ||
expandAllBranches, | ||
recurseList | ||
recurseList, | ||
experimental_customMergeAllOf, | ||
); | ||
@@ -707,4 +884,5 @@ return dependentSchemas.flatMap((dependent) => { | ||
recurseList, | ||
formData | ||
) | ||
formData, | ||
experimental_customMergeAllOf, | ||
), | ||
); | ||
@@ -727,2 +905,3 @@ }); | ||
* @param [formData] - The current formData to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - Either an array containing the best matching option or all options if `expandAllBranches` is true | ||
@@ -733,3 +912,3 @@ */ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
>( | ||
@@ -743,3 +922,4 @@ validator: ValidatorType<T, S, F>, | ||
recurseList: string[], | ||
formData?: T | ||
formData?: T, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): S[] { | ||
@@ -777,3 +957,4 @@ const validSubschemas = oneOf!.filter((subschema) => { | ||
expandAllBranches, | ||
recurseList | ||
recurseList, | ||
experimental_customMergeAllOf, | ||
); | ||
@@ -780,0 +961,0 @@ return schemas.map((s) => mergeSchemas(schema, s) as S); |
import get from 'lodash/get'; | ||
import has from 'lodash/has'; | ||
import { FormContextType, GenericObjectType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import { | ||
Experimental_CustomMergeAllOf, | ||
FormContextType, | ||
GenericObjectType, | ||
RJSFSchema, | ||
StrictRJSFSchema, | ||
ValidatorType, | ||
} from '../types'; | ||
import { PROPERTIES_KEY, REF_KEY } from '../constants'; | ||
@@ -54,2 +61,3 @@ import retrieveSchema from './retrieveSchema'; | ||
* @param [data={}] - The form data associated with the schema, defaulting to an empty object when undefined | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The new form data, with all the fields uniquely associated with the old schema set | ||
@@ -61,4 +69,11 @@ * to `undefined`. Will return `undefined` if the new schema is not an object containing properties. | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
>(validator: ValidatorType<T, S, F>, rootSchema: S, newSchema?: S, oldSchema?: S, data: any = {}): T { | ||
F extends FormContextType = any, | ||
>( | ||
validator: ValidatorType<T, S, F>, | ||
rootSchema: S, | ||
newSchema?: S, | ||
oldSchema?: S, | ||
data: any = {}, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): T { | ||
// By default, we will clear the form data | ||
@@ -83,10 +98,22 @@ let newFormData; | ||
const formValue = get(data, key); | ||
let oldKeyedSchema: S = get(oldSchema, [PROPERTIES_KEY, key], {}); | ||
let newKeyedSchema: S = get(newSchema, [PROPERTIES_KEY, key], {}); | ||
let oldKeyedSchema: S = get(oldSchema, [PROPERTIES_KEY, key], {}) as S; | ||
let newKeyedSchema: S = get(newSchema, [PROPERTIES_KEY, key], {}) as S; | ||
// Resolve the refs if they exist | ||
if (has(oldKeyedSchema, REF_KEY)) { | ||
oldKeyedSchema = retrieveSchema<T, S, F>(validator, oldKeyedSchema, rootSchema, formValue); | ||
oldKeyedSchema = retrieveSchema<T, S, F>( | ||
validator, | ||
oldKeyedSchema, | ||
rootSchema, | ||
formValue, | ||
experimental_customMergeAllOf, | ||
); | ||
} | ||
if (has(newKeyedSchema, REF_KEY)) { | ||
newKeyedSchema = retrieveSchema<T, S, F>(validator, newKeyedSchema, rootSchema, formValue); | ||
newKeyedSchema = retrieveSchema<T, S, F>( | ||
validator, | ||
newKeyedSchema, | ||
rootSchema, | ||
formValue, | ||
experimental_customMergeAllOf, | ||
); | ||
} | ||
@@ -110,3 +137,4 @@ // Now get types and see if they are the same | ||
oldKeyedSchema, | ||
formValue | ||
formValue, | ||
experimental_customMergeAllOf, | ||
); | ||
@@ -161,6 +189,18 @@ if (itemData !== undefined || newSchemaTypeForKey === 'array') { | ||
if (has(oldSchemaItems, REF_KEY)) { | ||
oldSchemaItems = retrieveSchema<T, S, F>(validator, oldSchemaItems as S, rootSchema, data as T); | ||
oldSchemaItems = retrieveSchema<T, S, F>( | ||
validator, | ||
oldSchemaItems as S, | ||
rootSchema, | ||
data as T, | ||
experimental_customMergeAllOf, | ||
); | ||
} | ||
if (has(newSchemaItems, REF_KEY)) { | ||
newSchemaItems = retrieveSchema<T, S, F>(validator, newSchemaItems as S, rootSchema, data as T); | ||
newSchemaItems = retrieveSchema<T, S, F>( | ||
validator, | ||
newSchemaItems as S, | ||
rootSchema, | ||
data as T, | ||
experimental_customMergeAllOf, | ||
); | ||
} | ||
@@ -180,3 +220,4 @@ // Now get types and see if they are the same | ||
oldSchemaItems as S, | ||
aValue | ||
aValue, | ||
experimental_customMergeAllOf, | ||
); | ||
@@ -183,0 +224,0 @@ if (itemValue !== undefined && (maxItems < 0 || newValue.length < maxItems)) { |
import get from 'lodash/get'; | ||
import isEqual from 'lodash/isEqual'; | ||
import { ALL_OF_KEY, DEPENDENCIES_KEY, ID_KEY, ITEMS_KEY, PROPERTIES_KEY, REF_KEY } from '../constants'; | ||
import isObject from '../isObject'; | ||
import { FormContextType, GenericObjectType, IdSchema, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import { | ||
Experimental_CustomMergeAllOf, | ||
FormContextType, | ||
GenericObjectType, | ||
IdSchema, | ||
RJSFSchema, | ||
StrictRJSFSchema, | ||
ValidatorType, | ||
} from '../types'; | ||
import retrieveSchema from './retrieveSchema'; | ||
import getSchemaType from '../getSchemaType'; | ||
import deepEquals from '../deepEquals'; | ||
@@ -21,2 +28,3 @@ /** An internal helper that generates an `IdSchema` object for the `schema`, recursively with protection against | ||
* @param [_recurseList=[]] - The list of retrieved schemas currently being recursed, used to prevent infinite recursion | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The `IdSchema` object for the `schema` | ||
@@ -32,11 +40,29 @@ */ | ||
formData?: T, | ||
_recurseList: S[] = [] | ||
_recurseList: S[] = [], | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): IdSchema<T> { | ||
if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) { | ||
const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData); | ||
const sameSchemaIndex = _recurseList.findIndex((item) => isEqual(item, _schema)); | ||
if (sameSchemaIndex === -1) { | ||
const $id = id || idPrefix; | ||
const idSchema: IdSchema<T> = { $id } as IdSchema<T>; | ||
if (typeof schema === 'object') { | ||
if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) { | ||
const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData, experimental_customMergeAllOf); | ||
const sameSchemaIndex = _recurseList.findIndex((item) => deepEquals(item, _schema)); | ||
if (sameSchemaIndex === -1) { | ||
return toIdSchemaInternal<T, S, F>( | ||
validator, | ||
_schema, | ||
idPrefix, | ||
idSeparator, | ||
id, | ||
rootSchema, | ||
formData, | ||
_recurseList.concat(_schema), | ||
experimental_customMergeAllOf, | ||
); | ||
} | ||
} | ||
if (ITEMS_KEY in schema && !get(schema, [ITEMS_KEY, REF_KEY])) { | ||
return toIdSchemaInternal<T, S, F>( | ||
validator, | ||
_schema, | ||
get(schema, ITEMS_KEY) as S, | ||
idPrefix, | ||
@@ -47,36 +73,24 @@ idSeparator, | ||
formData, | ||
_recurseList.concat(_schema) | ||
_recurseList, | ||
experimental_customMergeAllOf, | ||
); | ||
} | ||
} | ||
if (ITEMS_KEY in schema && !get(schema, [ITEMS_KEY, REF_KEY])) { | ||
return toIdSchemaInternal<T, S, F>( | ||
validator, | ||
get(schema, ITEMS_KEY) as S, | ||
idPrefix, | ||
idSeparator, | ||
id, | ||
rootSchema, | ||
formData, | ||
_recurseList | ||
); | ||
} | ||
const $id = id || idPrefix; | ||
const idSchema: IdSchema<T> = { $id } as IdSchema<T>; | ||
if (getSchemaType<S>(schema) === 'object' && PROPERTIES_KEY in schema) { | ||
for (const name in schema.properties) { | ||
const field = get(schema, [PROPERTIES_KEY, name]); | ||
const fieldId = idSchema[ID_KEY] + idSeparator + name; | ||
(idSchema as IdSchema<GenericObjectType>)[name] = toIdSchemaInternal<T, S, F>( | ||
validator, | ||
isObject(field) ? field : {}, | ||
idPrefix, | ||
idSeparator, | ||
fieldId, | ||
rootSchema, | ||
// It's possible that formData is not an object -- this can happen if an | ||
// array item has just been added, but not populated with data yet | ||
get(formData, [name]), | ||
_recurseList | ||
); | ||
if (getSchemaType<S>(schema) === 'object' && PROPERTIES_KEY in schema) { | ||
for (const name in schema.properties) { | ||
const field: S = schema[PROPERTIES_KEY][name] as S; | ||
const fieldId = idSchema[ID_KEY] + idSeparator + name; | ||
(idSchema as IdSchema<GenericObjectType>)[name] = toIdSchemaInternal<T, S, F>( | ||
validator, | ||
field, | ||
idPrefix, | ||
idSeparator, | ||
fieldId, | ||
rootSchema, | ||
// It's possible that formData is not an object -- this can happen if an | ||
// array item has just been added, but not populated with data yet | ||
get(formData, [name]), | ||
_recurseList, | ||
experimental_customMergeAllOf, | ||
); | ||
} | ||
} | ||
@@ -96,2 +110,3 @@ } | ||
* @param [idSeparator='_'] - The separator to use for the path segments in the id | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The `IdSchema` object for the `schema` | ||
@@ -106,5 +121,16 @@ */ | ||
idPrefix = 'root', | ||
idSeparator = '_' | ||
idSeparator = '_', | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): IdSchema<T> { | ||
return toIdSchemaInternal<T, S, F>(validator, schema, idPrefix, idSeparator, id, rootSchema, formData); | ||
return toIdSchemaInternal<T, S, F>( | ||
validator, | ||
schema, | ||
idPrefix, | ||
idSeparator, | ||
id, | ||
rootSchema, | ||
formData, | ||
undefined, | ||
experimental_customMergeAllOf, | ||
); | ||
} |
import get from 'lodash/get'; | ||
import isEqual from 'lodash/isEqual'; | ||
import set from 'lodash/set'; | ||
import { | ||
ADDITIONAL_PROPERTIES_KEY, | ||
ALL_OF_KEY, | ||
ANY_OF_KEY, | ||
ADDITIONAL_PROPERTIES_KEY, | ||
DEPENDENCIES_KEY, | ||
@@ -18,5 +17,14 @@ ITEMS_KEY, | ||
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema'; | ||
import { FormContextType, GenericObjectType, PathSchema, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types'; | ||
import { | ||
Experimental_CustomMergeAllOf, | ||
FormContextType, | ||
GenericObjectType, | ||
PathSchema, | ||
RJSFSchema, | ||
StrictRJSFSchema, | ||
ValidatorType, | ||
} from '../types'; | ||
import getClosestMatchingOption from './getClosestMatchingOption'; | ||
import retrieveSchema from './retrieveSchema'; | ||
import deepEquals from '../deepEquals'; | ||
@@ -32,2 +40,3 @@ /** An internal helper that generates an `PathSchema` object for the `schema`, recursively with protection against | ||
* @param [_recurseList=[]] - The list of retrieved schemas currently being recursed, used to prevent infinite recursion | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The `PathSchema` object for the `schema` | ||
@@ -41,7 +50,8 @@ */ | ||
formData?: T, | ||
_recurseList: S[] = [] | ||
_recurseList: S[] = [], | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): PathSchema<T> { | ||
if (REF_KEY in schema || DEPENDENCIES_KEY in schema || ALL_OF_KEY in schema) { | ||
const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData); | ||
const sameSchemaIndex = _recurseList.findIndex((item) => isEqual(item, _schema)); | ||
const _schema = retrieveSchema<T, S, F>(validator, schema, rootSchema, formData, experimental_customMergeAllOf); | ||
const sameSchemaIndex = _recurseList.findIndex((item) => deepEquals(item, _schema)); | ||
if (sameSchemaIndex === -1) { | ||
@@ -54,3 +64,4 @@ return toPathSchemaInternal<T, S, F>( | ||
formData, | ||
_recurseList.concat(_schema) | ||
_recurseList.concat(_schema), | ||
experimental_customMergeAllOf, | ||
); | ||
@@ -67,7 +78,23 @@ } | ||
const discriminator = getDiscriminatorFieldFromSchema<S>(schema); | ||
const index = getClosestMatchingOption<T, S, F>(validator, rootSchema!, formData, xxxOf, 0, discriminator); | ||
const index = getClosestMatchingOption<T, S, F>( | ||
validator, | ||
rootSchema!, | ||
formData, | ||
xxxOf, | ||
0, | ||
discriminator, | ||
experimental_customMergeAllOf, | ||
); | ||
const _schema: S = xxxOf![index] as S; | ||
pathSchema = { | ||
...pathSchema, | ||
...toPathSchemaInternal<T, S, F>(validator, _schema, name, rootSchema, formData, _recurseList), | ||
...toPathSchemaInternal<T, S, F>( | ||
validator, | ||
_schema, | ||
name, | ||
rootSchema, | ||
formData, | ||
_recurseList, | ||
experimental_customMergeAllOf, | ||
), | ||
}; | ||
@@ -92,3 +119,4 @@ } | ||
element, | ||
_recurseList | ||
_recurseList, | ||
experimental_customMergeAllOf, | ||
); | ||
@@ -102,3 +130,4 @@ } else if (schemaAdditionalItems) { | ||
element, | ||
_recurseList | ||
_recurseList, | ||
experimental_customMergeAllOf, | ||
); | ||
@@ -117,3 +146,4 @@ } else { | ||
element, | ||
_recurseList | ||
_recurseList, | ||
experimental_customMergeAllOf, | ||
); | ||
@@ -124,3 +154,3 @@ }); | ||
for (const property in schema.properties) { | ||
const field = get(schema, [PROPERTIES_KEY, property]); | ||
const field: S = get(schema, [PROPERTIES_KEY, property], {}) as S; | ||
(pathSchema as PathSchema<GenericObjectType>)[property] = toPathSchemaInternal<T, S, F>( | ||
@@ -134,3 +164,4 @@ validator, | ||
get(formData, [property]), | ||
_recurseList | ||
_recurseList, | ||
experimental_customMergeAllOf, | ||
); | ||
@@ -149,2 +180,3 @@ } | ||
* @param [formData] - The current formData, if any, to assist retrieving a schema | ||
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas | ||
* @returns - The `PathSchema` object for the `schema` | ||
@@ -157,5 +189,6 @@ */ | ||
rootSchema?: S, | ||
formData?: T | ||
formData?: T, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): PathSchema<T> { | ||
return toPathSchemaInternal(validator, schema, name, rootSchema, formData); | ||
return toPathSchemaInternal(validator, schema, name, rootSchema, formData, undefined, experimental_customMergeAllOf); | ||
} |
@@ -14,3 +14,3 @@ import isPlainObject from 'lodash/isPlainObject'; | ||
errorSchema?: ErrorSchema<T>, | ||
fieldPath: string[] = [] | ||
fieldPath: string[] = [], | ||
): RJSFValidationError[] { | ||
@@ -30,3 +30,3 @@ if (!errorSchema) { | ||
}; | ||
}) | ||
}), | ||
); | ||
@@ -33,0 +33,0 @@ } |
440
src/types.ts
@@ -34,2 +34,6 @@ import type { | ||
/** The interface for the test ID proxy objects that are returned by the `getTestId` utility function. | ||
*/ | ||
export type TestIdShape = Record<string, string>; | ||
/** Experimental feature that specifies the Array `minItems` default form state behavior | ||
@@ -57,3 +61,3 @@ */ | ||
schema: S, | ||
rootSchema?: S | ||
rootSchema?: S, | ||
) => boolean; | ||
@@ -71,3 +75,4 @@ /** When `formData` is provided and does not contain `minItems` worth of data, this flag (`false` by default) controls | ||
* handling of optional array fields where `minItems` is set and handling of setting defaults based on the | ||
* value of `emptyObjectFields`. | ||
* value of `emptyObjectFields`. It also affects how `allOf` fields are handled and how to handle merging defaults into | ||
* the formData in relation to explicit `undefined` values via `mergeDefaultsIntoFormData`. | ||
*/ | ||
@@ -92,4 +97,31 @@ export type Experimental_DefaultFormStateBehavior = { | ||
allOf?: 'populateDefaults' | 'skipDefaults'; | ||
/** Optional enumerated flag controlling how the defaults are merged into the form data when dealing with undefined | ||
* values, defaulting to `useFormDataIfPresent`. | ||
* NOTE: If there is a default for a field and the `formData` is unspecified, the default ALWAYS merges. | ||
* - `useFormDataIfPresent`: Legacy behavior - Do not merge defaults if there is a value for a field in `formData`, | ||
* even if that value is explicitly set to `undefined` | ||
* - `useDefaultIfFormDataUndefined`: - If the value of a field within the `formData` is `undefined`, then use the | ||
* default value instead | ||
*/ | ||
mergeDefaultsIntoFormData?: 'useFormDataIfPresent' | 'useDefaultIfFormDataUndefined'; | ||
/** Optional enumerated flag controlling how const values are merged into the form data as defaults when dealing with | ||
* undefined values, defaulting to `always`. The defaulting behavior for this flag will always be controlled by the | ||
* `emptyObjectField` flag value. For instance, if `populateRequiredDefaults` is set and the const value is not | ||
* required, it will not be set. | ||
* - `always`: A const value will always be merged into the form as a default. If there is are const values in a | ||
* `oneOf` (for instance to create an enumeration with title different from the values), the first const value | ||
* will be defaulted | ||
* - `skipOneOf`: If const is in a `oneOf` it will NOT pick the first value as a default | ||
* - `never`: A const value will never be used as a default | ||
* | ||
*/ | ||
constAsDefaults?: 'always' | 'skipOneOf' | 'never'; | ||
}; | ||
/** Optional function that allows for custom merging of `allOf` schemas | ||
* @param schema - Schema with `allOf` that needs to be merged | ||
* @returns The merged schema | ||
*/ | ||
export type Experimental_CustomMergeAllOf<S extends StrictRJSFSchema = RJSFSchema> = (schema: S) => S; | ||
/** The interface representing a Date object that contains an optional time */ | ||
@@ -129,2 +161,4 @@ export interface DateObject { | ||
autoComplete?: HTMLInputElement['autocomplete']; | ||
/** Specifies a filter for what file types the user can upload. */ | ||
accept?: HTMLInputElement['accept']; | ||
}; | ||
@@ -153,12 +187,13 @@ | ||
/** Type describing a recursive structure of `FieldPath`s for an object with a non-empty set of keys */ | ||
export type PathSchema<T = any> = T extends Array<infer U> | ||
? FieldPath & { | ||
[i: number]: PathSchema<U>; | ||
} | ||
: T extends GenericObjectType | ||
? FieldPath & { | ||
/** The set of names for fields in the recursive object structure */ | ||
[key in keyof T]?: PathSchema<T[key]>; | ||
} | ||
: FieldPath; | ||
export type PathSchema<T = any> = | ||
T extends Array<infer U> | ||
? FieldPath & { | ||
[i: number]: PathSchema<U>; | ||
} | ||
: T extends GenericObjectType | ||
? FieldPath & { | ||
/** The set of names for fields in the recursive object structure */ | ||
[key in keyof T]?: PathSchema<T[key]>; | ||
} | ||
: FieldPath; | ||
@@ -215,4 +250,18 @@ /** The type for error produced by RJSF schema validation */ | ||
/** The base properties passed to various RJSF components. */ | ||
export type RJSFBaseProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
/** The schema object for the field being described */ | ||
schema: S; | ||
/** The uiSchema object for this description field */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** The properties that are passed to an `ErrorListTemplate` implementation */ | ||
export type ErrorListProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
export type ErrorListProps< | ||
T = any, | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any, | ||
> = RJSFBaseProps<T, S, F> & { | ||
/** The errorSchema constructed by `Form` */ | ||
@@ -224,12 +273,10 @@ errorSchema: ErrorSchema<T>; | ||
formContext?: F; | ||
/** The schema that was passed to `Form` */ | ||
schema: S; | ||
/** The uiSchema that was passed to `Form` */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** The properties that are passed to an `FieldErrorTemplate` implementation */ | ||
export type FieldErrorProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
export type FieldErrorProps< | ||
T = any, | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any, | ||
> = RJSFBaseProps<T, S, F> & { | ||
/** The errorSchema constructed by `Form` */ | ||
@@ -241,12 +288,10 @@ errorSchema?: ErrorSchema<T>; | ||
idSchema: IdSchema<T>; | ||
/** The schema that was passed to field */ | ||
schema: S; | ||
/** The uiSchema that was passed to field */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** The properties that are passed to an `FieldHelpTemplate` implementation */ | ||
export type FieldHelpProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
export type FieldHelpProps< | ||
T = any, | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any, | ||
> = RJSFBaseProps<T, S, F> & { | ||
/** The help information to be rendered */ | ||
@@ -256,12 +301,16 @@ help?: string | ReactElement; | ||
idSchema: IdSchema<T>; | ||
/** The schema that was passed to field */ | ||
schema: S; | ||
/** The uiSchema that was passed to field */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** Flag indicating whether there are errors associated with this field */ | ||
hasErrors?: boolean; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** The properties that are passed to a `GridTemplate` */ | ||
export interface GridTemplateProps extends GenericObjectType { | ||
/** The contents of the grid template */ | ||
children?: ReactNode; | ||
/** Optional flag indicating whether the grid element represents a column, necessary for themes which have components | ||
* for Rows vs Columns. NOTE: This is falsy by default when not specified | ||
*/ | ||
column?: boolean; | ||
} | ||
/** The set of `Fields` stored in the `Registry` */ | ||
@@ -280,3 +329,3 @@ export type RegistryFieldsType<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
/** The set of RJSF templates that can be overridden by themes or users */ | ||
export interface TemplatesType<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> { | ||
export type TemplatesType<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
/** The template to use while rendering normal or fixed array fields */ | ||
@@ -286,4 +335,6 @@ ArrayFieldTemplate: ComponentType<ArrayFieldTemplateProps<T, S, F>>; | ||
ArrayFieldDescriptionTemplate: ComponentType<ArrayFieldDescriptionProps<T, S, F>>; | ||
/** The template to use while rendering the buttons for an item in an array field */ | ||
ArrayFieldItemButtonsTemplate: ComponentType<ArrayFieldItemButtonsTemplateType<T, S, F>>; | ||
/** The template to use while rendering an item in an array field */ | ||
ArrayFieldItemTemplate: ComponentType<ArrayFieldTemplateItemType<T, S, F>>; | ||
ArrayFieldItemTemplate: ComponentType<ArrayFieldItemTemplateType<T, S, F>>; | ||
/** The template to use while rendering the title for an array field */ | ||
@@ -303,2 +354,4 @@ ArrayFieldTitleTemplate: ComponentType<ArrayFieldTitleProps<T, S, F>>; | ||
FieldTemplate: ComponentType<FieldTemplateProps<T, S, F>>; | ||
/** The template to use to render a Grid element */ | ||
GridTemplate: ComponentType<GridTemplateProps>; | ||
/** The template to use while rendering an object */ | ||
@@ -327,3 +380,6 @@ ObjectFieldTemplate: ComponentType<ObjectFieldTemplateProps<T, S, F>>; | ||
}; | ||
} | ||
} & { | ||
/** Allow this to support any named `ComponentType` or an object of named `ComponentType`s */ | ||
[key: string]: ComponentType<any> | { [key: string]: ComponentType<any> } | undefined; | ||
}; | ||
@@ -434,3 +490,7 @@ /** The set of UiSchema options that can be set globally and used as fallbacks at an individual template, field or | ||
/** The properties that are passed to a FieldTemplate implementation */ | ||
export type FieldTemplateProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
export type FieldTemplateProps< | ||
T = any, | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any, | ||
> = RJSFBaseProps<T, S, F> & { | ||
/** The id of the field in the hierarchy. You can use it to render a label targeting the wrapped widget */ | ||
@@ -476,6 +536,2 @@ id: string; | ||
displayLabel?: boolean; | ||
/** The schema object for this field */ | ||
schema: S; | ||
/** The uiSchema object for this field */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** The `formContext` object that was passed to `Form` */ | ||
@@ -491,10 +547,10 @@ formContext?: F; | ||
onDropPropertyClick: (value: string) => () => void; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** The properties that are passed to the `UnsupportedFieldTemplate` implementation */ | ||
export type UnsupportedFieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
/** The schema object for this field */ | ||
schema: S; | ||
export type UnsupportedFieldProps< | ||
T = any, | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any, | ||
> = RJSFBaseProps<T, S, F> & { | ||
/** The tree of unique ids for every child field */ | ||
@@ -504,8 +560,10 @@ idSchema?: IdSchema<T>; | ||
reason: string; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** The properties that are passed to a `TitleFieldTemplate` implementation */ | ||
export type TitleFieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
export type TitleFieldProps< | ||
T = any, | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any, | ||
> = RJSFBaseProps<T, S, F> & { | ||
/** The id of the field title in the hierarchy */ | ||
@@ -515,24 +573,16 @@ id: string; | ||
title: string; | ||
/** The schema object for the field being titled */ | ||
schema: S; | ||
/** The uiSchema object for this title field */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** A boolean value stating if the field is required */ | ||
required?: boolean; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** The properties that are passed to a `DescriptionFieldTemplate` implementation */ | ||
export type DescriptionFieldProps<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = { | ||
export type DescriptionFieldProps< | ||
T = any, | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any, | ||
> = RJSFBaseProps<T, S, F> & { | ||
/** The id of the field description in the hierarchy */ | ||
id: string; | ||
/** The schema object for the field being described */ | ||
schema: S; | ||
/** The uiSchema object for this description field */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** The description of the field being rendered */ | ||
description: string | ReactElement; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
@@ -544,3 +594,3 @@ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
> = Omit<TitleFieldProps<T, S, F>, 'id' | 'title'> & { | ||
@@ -557,3 +607,3 @@ /** The title for the field being rendered */ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
> = Omit<DescriptionFieldProps<T, S, F>, 'id' | 'description'> & { | ||
@@ -566,12 +616,14 @@ /** The description of the field being rendered */ | ||
/** The properties of each element in the ArrayFieldTemplateProps.items array */ | ||
export type ArrayFieldTemplateItemType< | ||
/** The properties of the buttons to render for each element in the ArrayFieldTemplateProps.items array */ | ||
export type ArrayFieldItemButtonsTemplateType< | ||
T = any, | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
> = { | ||
/** The html for the item's content */ | ||
children: ReactElement; | ||
F extends FormContextType = any, | ||
> = RJSFBaseProps<T, S, F> & { | ||
/** The idSchema of the item for which buttons are being rendered */ | ||
idSchema: IdSchema<T>; | ||
/** The className string */ | ||
className: string; | ||
className?: string; | ||
/** Any optional style attributes */ | ||
style?: ButtonHTMLAttributes<HTMLButtonElement>['style']; | ||
/** A boolean value stating if the array item is disabled */ | ||
@@ -589,4 +641,2 @@ disabled?: boolean; | ||
hasRemove: boolean; | ||
/** A boolean value stating whether the array item has a toolbar */ | ||
hasToolbar: boolean; | ||
/** A number stating the index the array item occurs in `items` */ | ||
@@ -606,12 +656,39 @@ index: number; | ||
readonly?: boolean; | ||
}; | ||
/** The properties of each element in the ArrayFieldTemplateProps.items array */ | ||
export type ArrayFieldItemTemplateType< | ||
T = any, | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any, | ||
> = RJSFBaseProps<T, S, F> & { | ||
/** The html for the item's content */ | ||
children: ReactNode; | ||
/** The props to pass to the `ArrayFieldItemButtonTemplate` */ | ||
buttonsProps: ArrayFieldItemButtonsTemplateType<T, S, F>; | ||
/** The className string */ | ||
className: string; | ||
/** A boolean value stating if the array item is disabled */ | ||
disabled?: boolean; | ||
/** A boolean value stating whether the array item has a toolbar */ | ||
hasToolbar: boolean; | ||
/** A number stating the index the array item occurs in `items` */ | ||
index: number; | ||
/** A number stating the total number `items` in the array */ | ||
totalItems: number; | ||
/** A boolean value stating if the array item is read-only */ | ||
readonly?: boolean; | ||
/** A stable, unique key for the array item */ | ||
key: string; | ||
/** The schema object for this array item */ | ||
schema: S; | ||
/** The uiSchema object for this array item */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
/** | ||
* @deprecated - Use `ArrayFieldItemTemplateType` instead | ||
*/ | ||
export type ArrayFieldTemplateItemType< | ||
T = any, | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any, | ||
> = ArrayFieldItemTemplateType<T, S, F>; | ||
/** The properties that are passed to an ArrayFieldTemplate implementation */ | ||
@@ -621,4 +698,4 @@ export type ArrayFieldTemplateProps< | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
> = { | ||
F extends FormContextType = any, | ||
> = RJSFBaseProps<T, S, F> & { | ||
/** A boolean value stating whether new elements can be added to the array */ | ||
@@ -633,3 +710,3 @@ canAdd?: boolean; | ||
/** An array of objects representing the items in the array */ | ||
items: ArrayFieldTemplateItemType<T, S, F>[]; | ||
items: ArrayFieldItemTemplateType<T, S, F>[]; | ||
/** A function that adds a new item to the array */ | ||
@@ -643,6 +720,2 @@ onAddClick: (event?: any) => void; | ||
hideError?: boolean; | ||
/** The schema object for this array */ | ||
schema: S; | ||
/** The uiSchema object for this array field */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** A string value containing the title for the array */ | ||
@@ -658,4 +731,2 @@ title: string; | ||
rawErrors?: string[]; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
@@ -681,4 +752,4 @@ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
> = { | ||
F extends FormContextType = any, | ||
> = RJSFBaseProps<T, S, F> & { | ||
/** A string value containing the title for the object */ | ||
@@ -700,6 +771,2 @@ title: string; | ||
hideError?: boolean; | ||
/** The schema object for this object */ | ||
schema: S; | ||
/** The uiSchema object for this object field */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** An object containing the id for this object & ids for its properties */ | ||
@@ -713,4 +780,2 @@ idSchema: IdSchema<T>; | ||
formContext?: F; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
@@ -722,21 +787,23 @@ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
> = { | ||
F extends FormContextType = any, | ||
> = RJSFBaseProps<T, S, F> & { | ||
/** The field or widget component instance for this field row */ | ||
children: ReactNode; | ||
} & Pick< | ||
FieldTemplateProps<T, S, F>, | ||
| 'id' | ||
| 'classNames' | ||
| 'style' | ||
| 'label' | ||
| 'required' | ||
| 'readonly' | ||
| 'disabled' | ||
| 'schema' | ||
| 'uiSchema' | ||
| 'onKeyChange' | ||
| 'onDropPropertyClick' | ||
| 'registry' | ||
>; | ||
FieldTemplateProps<T, S, F>, | ||
| 'id' | ||
| 'classNames' | ||
| 'hideError' | ||
| 'rawErrors' | ||
| 'style' | ||
| 'label' | ||
| 'required' | ||
| 'readonly' | ||
| 'disabled' | ||
| 'schema' | ||
| 'uiSchema' | ||
| 'onKeyChange' | ||
| 'onDropPropertyClick' | ||
| 'registry' | ||
>; | ||
@@ -746,2 +813,3 @@ /** The properties that are passed to a Widget implementation */ | ||
extends GenericObjectType, | ||
RJSFBaseProps<T, S, F>, | ||
Pick<HTMLAttributes<HTMLElement>, Exclude<keyof HTMLAttributes<HTMLElement>, 'onBlur' | 'onFocus'>> { | ||
@@ -756,6 +824,2 @@ /** The generated id for this widget, used to provide unique `name`s and `id`s for the HTML field elements rendered by | ||
name: string; | ||
/** The JSONSchema subschema object for this widget */ | ||
schema: S; | ||
/** The uiSchema for this widget */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** The current value for this widget */ | ||
@@ -800,4 +864,2 @@ value: any; | ||
rawErrors?: string[]; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
} | ||
@@ -814,3 +876,3 @@ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
> extends WidgetProps<T, S, F> { | ||
@@ -836,13 +898,10 @@ /** A `BaseInputTemplate` implements a default `onChange` handler that it passes to the HTML input component to handle | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
> = ButtonHTMLAttributes<HTMLButtonElement> & { | ||
/** An alternative specification for the type of the icon button */ | ||
iconType?: string; | ||
/** The name representation or actual react element implementation for the icon */ | ||
icon?: string | ReactElement; | ||
/** The uiSchema for this widget */ | ||
uiSchema?: UiSchema<T, S, F>; | ||
/** The `registry` object */ | ||
registry: Registry<T, S, F>; | ||
}; | ||
F extends FormContextType = any, | ||
> = ButtonHTMLAttributes<HTMLButtonElement> & | ||
Omit<RJSFBaseProps<T, S, F>, 'schema'> & { | ||
/** An alternative specification for the type of the icon button */ | ||
iconType?: string; | ||
/** The name representation or actual react element implementation for the icon */ | ||
icon?: string | ReactElement; | ||
}; | ||
@@ -883,5 +942,25 @@ /** The type that defines how to change the behavior of the submit button for the form */ | ||
type UIOptionsBaseType<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any> = Partial< | ||
Omit<TemplatesType<T, S, F>, 'ButtonTemplates'> | ||
Pick< | ||
TemplatesType<T, S, F>, | ||
| 'ArrayFieldDescriptionTemplate' | ||
| 'ArrayFieldItemTemplate' | ||
| 'ArrayFieldTemplate' | ||
| 'ArrayFieldTitleTemplate' | ||
| 'BaseInputTemplate' | ||
| 'DescriptionFieldTemplate' | ||
| 'ErrorListTemplate' | ||
| 'FieldErrorTemplate' | ||
| 'FieldHelpTemplate' | ||
| 'FieldTemplate' | ||
| 'ObjectFieldTemplate' | ||
| 'TitleFieldTemplate' | ||
| 'UnsupportedFieldTemplate' | ||
| 'WrapIfAdditionalTemplate' | ||
> | ||
> & | ||
GlobalUISchemaOptions & { | ||
/** Allows RJSF to override the default field implementation by specifying either the name of a field that is used | ||
* to look up an implementation from the `fields` list or an actual one-off `Field` component implementation itself | ||
*/ | ||
field?: Field<T, S, F> | string; | ||
/** Any classnames that the user wants to be applied to a field in the ui */ | ||
@@ -909,2 +988,8 @@ classNames?: string; | ||
enumDisabled?: Array<string | number | boolean>; | ||
/** Allows a user to provide a list of labels for enum values in the schema */ | ||
enumNames?: string[]; | ||
/** Provides an optional field within a schema to be used as the oneOf/anyOf selector when there isn't a | ||
* discriminator | ||
*/ | ||
optionsSchemaSelector?: string; | ||
/** Flag, if set to `true`, will hide the default error display for the given field AND all of its child fields in the | ||
@@ -932,4 +1017,2 @@ * hierarchy | ||
widget?: Widget<T, S, F> | string; | ||
/** Allows a user to provide a list of labels for enum values in the schema */ | ||
enumNames?: string[]; | ||
}; | ||
@@ -941,3 +1024,3 @@ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
> = UIOptionsBaseType<T, S, F> & { | ||
@@ -954,3 +1037,3 @@ /** Anything else will be one of these types */ | ||
S extends StrictRJSFSchema = RJSFSchema, | ||
F extends FormContextType = any | ||
F extends FormContextType = any, | ||
> = GenericObjectType & | ||
@@ -964,6 +1047,2 @@ MakeUIType<UIOptionsBaseType<T, S, F>> & { | ||
'ui:rootFieldId'?: string; | ||
/** Allows RJSF to override the default field implementation by specifying either the name of a field that is used | ||
* to look up an implementation from the `fields` list or an actual one-off `Field` component implementation itself | ||
*/ | ||
'ui:field'?: Field<T, S, F> | string; | ||
/** By default, any field that is rendered for an `anyOf`/`oneOf` schema will be wrapped inside the `AnyOfField` or | ||
@@ -987,3 +1066,3 @@ * `OneOfField` component. This default behavior may be undesirable if your custom field already handles behavior | ||
errors: FormValidation<T>, | ||
uiSchema?: UiSchema<T, S, F> | ||
uiSchema?: UiSchema<T, S, F>, | ||
) => FormValidation<T>; | ||
@@ -996,3 +1075,3 @@ | ||
errors: RJSFValidationError[], | ||
uiSchema?: UiSchema<T, S, F> | ||
uiSchema?: UiSchema<T, S, F>, | ||
) => RJSFValidationError[]; | ||
@@ -1028,12 +1107,4 @@ | ||
transformErrors?: ErrorTransformer<T, S, F>, | ||
uiSchema?: UiSchema<T, S, F> | ||
uiSchema?: UiSchema<T, S, F>, | ||
): ValidationData<T>; | ||
/** Converts an `errorSchema` into a list of `RJSFValidationErrors` | ||
* | ||
* @param errorSchema - The `ErrorSchema` instance to convert | ||
* @param [fieldPath=[]] - The current field path, defaults to [] if not specified | ||
* @deprecated - Use the `toErrorList()` function provided by `@rjsf/utils` instead. This function will be removed in | ||
* the next major release. | ||
*/ | ||
toErrorList(errorSchema?: ErrorSchema<T>, fieldPath?: string[]): RJSFValidationError[]; | ||
/** Validates data against a schema, returning true if the data is valid, or | ||
@@ -1061,2 +1132,11 @@ * false otherwise. If the schema is invalid, then this function will return | ||
/** The interface for the return value of the `findFieldInSchema` function | ||
*/ | ||
export interface FoundFieldType<S extends StrictRJSFSchema = RJSFSchema> { | ||
/** The field that was found, or undefined if it wasn't */ | ||
field?: S; | ||
/** The requiredness of the field found or undefined if it wasn't */ | ||
isRequired?: boolean; | ||
} | ||
/** The `SchemaUtilsType` interface provides a wrapper around the publicly exported APIs in the `@rjsf/utils/schema` | ||
@@ -1080,2 +1160,3 @@ * directory such that one does not have to explicitly pass the `validator` or `rootSchema` to each method. Since both | ||
* @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 - True if the `SchemaUtilsType` differs from the given `validator` or `rootSchema` | ||
@@ -1086,4 +1167,27 @@ */ | ||
rootSchema: S, | ||
experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior | ||
experimental_defaultFormStateBehavior?: Experimental_DefaultFormStateBehavior, | ||
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>, | ||
): boolean; | ||
/** Finds the field specified by the `path` within the root or recursed `schema`. If there is no field for the specified | ||
* `path`, then the default `{ field: undefined, isRequired: undefined }` is returned. It determines whether a leaf | ||
* field is in the `required` list for its parent and if so, it is marked as required on return. | ||
* | ||
* @param schema - The current node within the JSON schema | ||
* @param path - The remaining keys in the path to the desired field | ||
* @param [formData] - The form data that is used to determine which oneOf option | ||
* @returns - An object that contains the field and its required state. If no field can be found then | ||
* `{ field: undefined, isRequired: undefined }` is returned. | ||
*/ | ||
findFieldInSchema(schema: S, path: string | string[], formData?: T): FoundFieldType<S>; | ||
/** Finds the oneOf option inside the `schema['any/oneOf']` list which has the `properties[selectorField].default` that | ||
* matches the `formData[selectorField]` value. For the purposes of this function, `selectorField` is either | ||
* `schema.discriminator.propertyName` or `fallbackField`. | ||
* | ||
* @param schema - The schema element in which to search for the selected oneOf option | ||
* @param fallbackField - The field to use as a backup selector field if the schema does not have a required field | ||
* @param xxx - Either `oneOf` or `anyOf`, defines which value is being sought | ||
* @param [formData] - The form data that is used to determine which oneOf option | ||
* @returns - The anyOf/oneOf option that matches the selector field in the schema or undefined if nothing is selected | ||
*/ | ||
findSelectedOptionInXxxOf(schema: S, fallbackField: string, xxx: 'anyOf' | `oneOf`, formData?: T): S | undefined; | ||
/** Returns the superset of `formData` that includes the given set updated to include any missing fields that have | ||
@@ -1102,3 +1206,3 @@ * computed to have defaults provided in the `schema`. | ||
formData?: T, | ||
includeUndefinedValues?: boolean | 'excludeObjectChildren' | ||
includeUndefinedValues?: boolean | 'excludeObjectChildren', | ||
): T | T[] | undefined; | ||
@@ -1131,3 +1235,3 @@ /** Determines whether the combination of `schema` and `uiSchema` properties indicates that the label for the `schema` | ||
selectedOption?: number, | ||
discriminatorField?: string | ||
discriminatorField?: string, | ||
): number; | ||
@@ -1144,13 +1248,13 @@ /** Given the `formData` and list of `options`, attempts to find the index of the first option that matches the data. | ||
getFirstMatchingOption(formData: T | undefined, options: S[], discriminatorField?: string): number; | ||
/** Given the `formData` and list of `options`, attempts to find the index of the option that best matches the data. | ||
* Deprecated, use `getFirstMatchingOption()` instead. | ||
/** Helper that acts like lodash's `get` but additionally retrieves `$ref`s as needed to get the path for schemas | ||
* containing potentially nested `$ref`s. | ||
* | ||
* @param formData - The current formData, if any, onto which to provide any missing defaults | ||
* @param options - The list of options to find a matching options from | ||
* @param [discriminatorField] - The optional name of the field within the options object whose value is used to | ||
* determine which option is selected | ||
* @returns - The index of the matched option or 0 if none is available | ||
* @deprecated | ||
* @param schema - The current node within the JSON schema recursion | ||
* @param path - The remaining keys in the path to the desired property | ||
* @param defaultValue - The value to return if a value is not found for the `pathList` path | ||
* @returns - The internal schema from the `schema` for the given `path` or the `defaultValue` if not found | ||
*/ | ||
getMatchingOption(formData: T | undefined, options: S[], discriminatorField?: string): number; | ||
getFromSchema(schema: S, path: string | string[], defaultValue: T): T; | ||
getFromSchema(schema: S, path: string | string[], defaultValue: S): S; | ||
getFromSchema(schema: S, path: string | string[], defaultValue: T | S): S | T; | ||
/** Checks to see if the `schema` and `uiSchema` combination represents an array of files | ||
@@ -1175,14 +1279,2 @@ * | ||
isSelect(schema: S): boolean; | ||
/** Merges the errors in `additionalErrorSchema` into the existing `validationData` by combining the hierarchies in | ||
* the two `ErrorSchema`s and then appending the error list from the `additionalErrorSchema` obtained by calling | ||
* `validator.toErrorList()` onto the `errors` in the `validationData`. If no `additionalErrorSchema` is passed, then | ||
* `validationData` is returned. | ||
* | ||
* @param validationData - The current `ValidationData` into which to merge the additional errors | ||
* @param [additionalErrorSchema] - The additional set of errors | ||
* @returns - The `validationData` with the additional errors from `additionalErrorSchema` merged into it, if provided | ||
* @deprecated - Use the `validationDataMerge()` function exported from `@rjsf/utils` instead. This function will be | ||
* removed in the next major release. | ||
*/ | ||
mergeValidationData(validationData: ValidationData<T>, additionalErrorSchema?: ErrorSchema<T>): ValidationData<T>; | ||
/** Retrieves an expanded schema that has had all of its conditions, additional properties, references and | ||
@@ -1189,0 +1281,0 @@ * dependencies resolved and merged into the `schema` given a `rawFormData` that is used to do the potentially |
@@ -18,3 +18,3 @@ import isEmpty from 'lodash/isEmpty'; | ||
validationData: ValidationData<T>, | ||
additionalErrorSchema?: ErrorSchema<T> | ||
additionalErrorSchema?: ErrorSchema<T>, | ||
): ValidationData<T> { | ||
@@ -21,0 +21,0 @@ if (!additionalErrorSchema) { |
@@ -42,3 +42,3 @@ import { REF_KEY, ROOT_SCHEMA_PREFIX } from './constants'; | ||
export default function withIdRefPrefix<S extends StrictRJSFSchema = RJSFSchema>( | ||
schemaNode: S | S[] | S[keyof S] | ||
schemaNode: S | S[] | S[keyof S], | ||
): S | S[] | S[keyof S] { | ||
@@ -45,0 +45,0 @@ if (Array.isArray(schemaNode)) { |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
1966219
19.32%5
-79.17%329
6.13%23742
19.96%0
-100%Yes
NaN7
16.67%+ Added
+ Added