@rjsf/core 6.0.0-beta.9 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (160) hide show
  1. package/README.md +2 -0
  2. package/dist/core.umd.js +2042 -1987
  3. package/dist/index.cjs +4909 -0
  4. package/dist/index.cjs.map +7 -0
  5. package/dist/index.esm.js +2509 -2389
  6. package/dist/index.esm.js.map +4 -4
  7. package/lib/components/Form.d.ts +137 -34
  8. package/lib/components/Form.d.ts.map +1 -1
  9. package/lib/components/Form.js +318 -173
  10. package/lib/components/fields/ArrayField.d.ts +2 -187
  11. package/lib/components/fields/ArrayField.d.ts.map +1 -1
  12. package/lib/components/fields/ArrayField.js +526 -492
  13. package/lib/components/fields/BooleanField.d.ts.map +1 -1
  14. package/lib/components/fields/BooleanField.js +8 -3
  15. package/lib/components/fields/FallbackField.d.ts +7 -0
  16. package/lib/components/fields/FallbackField.d.ts.map +1 -0
  17. package/lib/components/fields/FallbackField.js +72 -0
  18. package/lib/components/fields/LayoutGridField.d.ts +109 -186
  19. package/lib/components/fields/LayoutGridField.d.ts.map +1 -1
  20. package/lib/components/fields/LayoutGridField.js +426 -426
  21. package/lib/components/fields/LayoutHeaderField.d.ts +1 -1
  22. package/lib/components/fields/LayoutHeaderField.js +3 -3
  23. package/lib/components/fields/LayoutMultiSchemaField.d.ts.map +1 -1
  24. package/lib/components/fields/LayoutMultiSchemaField.js +6 -6
  25. package/lib/components/fields/MultiSchemaField.d.ts.map +1 -1
  26. package/lib/components/fields/MultiSchemaField.js +16 -10
  27. package/lib/components/fields/NullField.js +3 -3
  28. package/lib/components/fields/NumberField.d.ts.map +1 -1
  29. package/lib/components/fields/NumberField.js +3 -3
  30. package/lib/components/fields/ObjectField.d.ts +2 -68
  31. package/lib/components/fields/ObjectField.d.ts.map +1 -1
  32. package/lib/components/fields/ObjectField.js +163 -163
  33. package/lib/components/fields/OptionalDataControlsField.d.ts +8 -0
  34. package/lib/components/fields/OptionalDataControlsField.d.ts.map +1 -0
  35. package/lib/components/fields/OptionalDataControlsField.js +43 -0
  36. package/lib/components/fields/SchemaField.d.ts.map +1 -1
  37. package/lib/components/fields/SchemaField.js +52 -30
  38. package/lib/components/fields/StringField.d.ts.map +1 -1
  39. package/lib/components/fields/StringField.js +8 -3
  40. package/lib/components/fields/index.d.ts.map +1 -1
  41. package/lib/components/fields/index.js +4 -0
  42. package/lib/components/templates/ArrayFieldDescriptionTemplate.d.ts +1 -1
  43. package/lib/components/templates/ArrayFieldDescriptionTemplate.js +3 -3
  44. package/lib/components/templates/ArrayFieldItemButtonsTemplate.d.ts +3 -3
  45. package/lib/components/templates/ArrayFieldItemButtonsTemplate.d.ts.map +1 -1
  46. package/lib/components/templates/ArrayFieldItemButtonsTemplate.js +3 -8
  47. package/lib/components/templates/ArrayFieldItemTemplate.d.ts +3 -3
  48. package/lib/components/templates/ArrayFieldItemTemplate.d.ts.map +1 -1
  49. package/lib/components/templates/ArrayFieldItemTemplate.js +1 -1
  50. package/lib/components/templates/ArrayFieldTemplate.d.ts +1 -1
  51. package/lib/components/templates/ArrayFieldTemplate.d.ts.map +1 -1
  52. package/lib/components/templates/ArrayFieldTemplate.js +4 -5
  53. package/lib/components/templates/ArrayFieldTitleTemplate.d.ts +1 -1
  54. package/lib/components/templates/ArrayFieldTitleTemplate.d.ts.map +1 -1
  55. package/lib/components/templates/ArrayFieldTitleTemplate.js +3 -3
  56. package/lib/components/templates/BaseInputTemplate.js +2 -2
  57. package/lib/components/templates/ButtonTemplates/AddButton.d.ts +1 -1
  58. package/lib/components/templates/ButtonTemplates/AddButton.d.ts.map +1 -1
  59. package/lib/components/templates/ButtonTemplates/AddButton.js +2 -2
  60. package/lib/components/templates/FallbackFieldTemplate.d.ts +7 -0
  61. package/lib/components/templates/FallbackFieldTemplate.d.ts.map +1 -0
  62. package/lib/components/templates/FallbackFieldTemplate.js +12 -0
  63. package/lib/components/templates/FieldErrorTemplate.js +2 -2
  64. package/lib/components/templates/FieldHelpTemplate.js +2 -2
  65. package/lib/components/templates/MultiSchemaFieldTemplate.d.ts +8 -0
  66. package/lib/components/templates/MultiSchemaFieldTemplate.d.ts.map +1 -0
  67. package/lib/components/templates/MultiSchemaFieldTemplate.js +10 -0
  68. package/lib/components/templates/ObjectFieldTemplate.d.ts.map +1 -1
  69. package/lib/components/templates/ObjectFieldTemplate.js +3 -2
  70. package/lib/components/templates/OptionalDataControlsTemplate.d.ts +11 -0
  71. package/lib/components/templates/OptionalDataControlsTemplate.d.ts.map +1 -0
  72. package/lib/components/templates/OptionalDataControlsTemplate.js +20 -0
  73. package/lib/components/templates/TitleField.d.ts.map +1 -1
  74. package/lib/components/templates/TitleField.js +2 -2
  75. package/lib/components/templates/UnsupportedField.js +3 -3
  76. package/lib/components/templates/WrapIfAdditionalTemplate.js +2 -2
  77. package/lib/components/templates/index.d.ts.map +1 -1
  78. package/lib/components/templates/index.js +6 -0
  79. package/lib/components/widgets/AltDateWidget.d.ts +1 -1
  80. package/lib/components/widgets/AltDateWidget.d.ts.map +1 -1
  81. package/lib/components/widgets/AltDateWidget.js +5 -46
  82. package/lib/components/widgets/CheckboxWidget.d.ts +1 -1
  83. package/lib/components/widgets/CheckboxWidget.d.ts.map +1 -1
  84. package/lib/components/widgets/CheckboxWidget.js +2 -2
  85. package/lib/components/widgets/CheckboxesWidget.d.ts +1 -1
  86. package/lib/components/widgets/CheckboxesWidget.d.ts.map +1 -1
  87. package/lib/components/widgets/CheckboxesWidget.js +4 -4
  88. package/lib/components/widgets/FileWidget.d.ts.map +1 -1
  89. package/lib/components/widgets/FileWidget.js +7 -87
  90. package/lib/components/widgets/HiddenWidget.d.ts +1 -1
  91. package/lib/components/widgets/HiddenWidget.d.ts.map +1 -1
  92. package/lib/components/widgets/HiddenWidget.js +2 -2
  93. package/lib/components/widgets/RadioWidget.d.ts +1 -1
  94. package/lib/components/widgets/RadioWidget.d.ts.map +1 -1
  95. package/lib/components/widgets/RadioWidget.js +2 -2
  96. package/lib/components/widgets/RatingWidget.d.ts +1 -1
  97. package/lib/components/widgets/RatingWidget.d.ts.map +1 -1
  98. package/lib/components/widgets/RatingWidget.js +2 -2
  99. package/lib/components/widgets/SelectWidget.d.ts +1 -1
  100. package/lib/components/widgets/SelectWidget.d.ts.map +1 -1
  101. package/lib/components/widgets/SelectWidget.js +2 -2
  102. package/lib/components/widgets/TextareaWidget.d.ts +1 -1
  103. package/lib/components/widgets/TextareaWidget.d.ts.map +1 -1
  104. package/lib/components/widgets/TextareaWidget.js +2 -2
  105. package/lib/getDefaultRegistry.d.ts.map +1 -1
  106. package/lib/getDefaultRegistry.js +6 -1
  107. package/lib/getTestRegistry.d.ts +5 -0
  108. package/lib/getTestRegistry.d.ts.map +1 -0
  109. package/lib/getTestRegistry.js +23 -0
  110. package/lib/index.d.ts +2 -1
  111. package/lib/index.d.ts.map +1 -1
  112. package/lib/index.js +2 -1
  113. package/lib/tsconfig.tsbuildinfo +1 -1
  114. package/package.json +35 -20
  115. package/src/components/Form.tsx +468 -206
  116. package/src/components/fields/ArrayField.tsx +871 -723
  117. package/src/components/fields/BooleanField.tsx +14 -5
  118. package/src/components/fields/FallbackField.tsx +157 -0
  119. package/src/components/fields/LayoutGridField.tsx +626 -603
  120. package/src/components/fields/LayoutHeaderField.tsx +3 -3
  121. package/src/components/fields/LayoutMultiSchemaField.tsx +9 -10
  122. package/src/components/fields/MultiSchemaField.tsx +57 -36
  123. package/src/components/fields/NullField.tsx +3 -3
  124. package/src/components/fields/NumberField.tsx +11 -3
  125. package/src/components/fields/ObjectField.tsx +308 -239
  126. package/src/components/fields/OptionalDataControlsField.tsx +84 -0
  127. package/src/components/fields/SchemaField.tsx +75 -94
  128. package/src/components/fields/StringField.tsx +14 -5
  129. package/src/components/fields/index.ts +4 -0
  130. package/src/components/templates/ArrayFieldDescriptionTemplate.tsx +3 -3
  131. package/src/components/templates/ArrayFieldItemButtonsTemplate.tsx +16 -21
  132. package/src/components/templates/ArrayFieldItemTemplate.tsx +3 -3
  133. package/src/components/templates/ArrayFieldTemplate.tsx +11 -18
  134. package/src/components/templates/ArrayFieldTitleTemplate.tsx +4 -3
  135. package/src/components/templates/BaseInputTemplate.tsx +5 -5
  136. package/src/components/templates/ButtonTemplates/AddButton.tsx +2 -0
  137. package/src/components/templates/FallbackFieldTemplate.tsx +28 -0
  138. package/src/components/templates/FieldErrorTemplate.tsx +2 -2
  139. package/src/components/templates/FieldHelpTemplate.tsx +2 -2
  140. package/src/components/templates/MultiSchemaFieldTemplate.tsx +20 -0
  141. package/src/components/templates/ObjectFieldTemplate.tsx +12 -7
  142. package/src/components/templates/OptionalDataControlsTemplate.tsx +43 -0
  143. package/src/components/templates/TitleField.tsx +6 -1
  144. package/src/components/templates/UnsupportedField.tsx +3 -3
  145. package/src/components/templates/WrapIfAdditionalTemplate.tsx +5 -5
  146. package/src/components/templates/index.ts +6 -0
  147. package/src/components/widgets/AltDateWidget.tsx +8 -126
  148. package/src/components/widgets/CheckboxWidget.tsx +4 -3
  149. package/src/components/widgets/CheckboxesWidget.tsx +5 -4
  150. package/src/components/widgets/FileWidget.tsx +11 -102
  151. package/src/components/widgets/HiddenWidget.tsx +2 -1
  152. package/src/components/widgets/RadioWidget.tsx +3 -2
  153. package/src/components/widgets/RatingWidget.tsx +2 -1
  154. package/src/components/widgets/SelectWidget.tsx +3 -2
  155. package/src/components/widgets/TextareaWidget.tsx +3 -2
  156. package/src/getDefaultRegistry.ts +14 -1
  157. package/src/getTestRegistry.tsx +38 -0
  158. package/src/index.ts +2 -1
  159. package/dist/index.js +0 -4834
  160. package/dist/index.js.map +0 -7
@@ -1,8 +1,6 @@
1
1
  import { createElement as _createElement } from "react";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
- import { PureComponent } from 'react';
4
- import { ANY_OF_KEY, getDiscriminatorFieldFromSchema, getTemplate, getTestIds, getUiOptions, hashObject, ID_KEY, lookupFromFormContext, mergeObjects, ONE_OF_KEY, PROPERTIES_KEY, READONLY_KEY, UI_OPTIONS_KEY, UI_GLOBAL_OPTIONS_KEY, } from '@rjsf/utils';
5
- import cloneDeep from 'lodash-es/cloneDeep.js';
3
+ import { ANY_OF_KEY, getDiscriminatorFieldFromSchema, getTemplate, getTestIds, getUiOptions, hashObject, ID_KEY, lookupFromFormContext, ONE_OF_KEY, PROPERTIES_KEY, READONLY_KEY, toFieldPathId, UI_OPTIONS_KEY, UI_GLOBAL_OPTIONS_KEY, ITEMS_KEY, useDeepCompareMemo, } from '@rjsf/utils';
6
4
  import each from 'lodash-es/each.js';
7
5
  import flatten from 'lodash-es/flatten.js';
8
6
  import get from 'lodash-es/get.js';
@@ -16,6 +14,7 @@ import isObject from 'lodash-es/isObject.js';
16
14
  import isPlainObject from 'lodash-es/isPlainObject.js';
17
15
  import isString from 'lodash-es/isString.js';
18
16
  import isUndefined from 'lodash-es/isUndefined.js';
17
+ import last from 'lodash-es/last.js';
19
18
  import set from 'lodash-es/set.js';
20
19
  /** The enumeration of the three different Layout GridTemplate type values
21
20
  */
@@ -52,6 +51,409 @@ export const LAYOUT_GRID_OPTION = `ui:${LAYOUT_GRID_UI_OPTION}`;
52
51
  function getNonNullishValue(value, fallback) {
53
52
  return value ?? fallback;
54
53
  }
54
+ /** Detects if a `str` is made up entirely of numeric characters
55
+ *
56
+ * @param str - The string to check to see if it is a numeric index
57
+ * @return - True if the string consists entirely of numeric characters
58
+ */
59
+ function isNumericIndex(str) {
60
+ return /^\d+?$/.test(str); // Matches positive integers
61
+ }
62
+ const LAYOUT_GRID_FIELD_TEST_IDS = getTestIds();
63
+ /** Computes the uiSchema for the field with `name` from the `uiProps` and `uiSchema` provided. The field UI Schema
64
+ * will always contain a copy of the global options from the `uiSchema` (so they can be passed down) as well as
65
+ * copying them into the local ui options. When the `forceReadonly` flag is true, then the field UI Schema is
66
+ * updated to make "readonly" be true. When the `schemaReadonly` flag is true AND the field UI Schema does NOT have
67
+ * the flag already provided, then we also make "readonly" true. We always make sure to return the final value of the
68
+ * field UI Schema's "readonly" flag as `uiReadonly` along with the `fieldUiSchema` in the return value.
69
+ *
70
+ * @param field - The name of the field to pull the existing UI Schema for
71
+ * @param uiProps - Any props that should be put into the field's uiSchema
72
+ * @param [uiSchema] - The optional UI Schema from which to get the UI schema for the field
73
+ * @param [schemaReadonly] - Optional flag indicating whether the schema indicates the field is readonly
74
+ * @param [forceReadonly] - Optional flag indicating whether the Form itself is in readonly mode
75
+ */
76
+ export function computeFieldUiSchema(field, uiProps, uiSchema, schemaReadonly, forceReadonly) {
77
+ const globalUiOptions = get(uiSchema, [UI_GLOBAL_OPTIONS_KEY], {});
78
+ const localUiSchema = get(uiSchema, field);
79
+ const localUiOptions = { ...get(localUiSchema, [UI_OPTIONS_KEY], {}), ...uiProps, ...globalUiOptions };
80
+ const fieldUiSchema = { ...localUiSchema };
81
+ if (!isEmpty(localUiOptions)) {
82
+ set(fieldUiSchema, [UI_OPTIONS_KEY], localUiOptions);
83
+ }
84
+ if (!isEmpty(globalUiOptions)) {
85
+ // pass the global uiOptions down to the field uiSchema so that they can be applied to all nested fields
86
+ set(fieldUiSchema, [UI_GLOBAL_OPTIONS_KEY], globalUiOptions);
87
+ }
88
+ let { readonly: uiReadonly } = getUiOptions(fieldUiSchema);
89
+ if (forceReadonly === true || (isUndefined(uiReadonly) && schemaReadonly === true)) {
90
+ // If we are forcing all widgets to be readonly, OR the schema indicates it is readonly AND the uiSchema does not
91
+ // have an overriding value, then update the uiSchema to set readonly to true. Doing this will
92
+ uiReadonly = true;
93
+ if (has(localUiOptions, READONLY_KEY)) {
94
+ // If the local options has the key value provided in it, then set that one to true
95
+ set(fieldUiSchema, [UI_OPTIONS_KEY, READONLY_KEY], true);
96
+ }
97
+ else {
98
+ // otherwise set the `ui:` version
99
+ set(fieldUiSchema, `ui:${READONLY_KEY}`, true);
100
+ }
101
+ }
102
+ return { fieldUiSchema, uiReadonly };
103
+ }
104
+ /** Given an `operator`, `datum` and `value` determines whether this condition is considered matching. Matching
105
+ * depends on the `operator`. The `datum` and `value` are converted into arrays if they aren't already and then the
106
+ * contents of the two arrays are compared using the `operator`. When `operator` is All, then the two arrays must be
107
+ * equal to match. When `operator` is SOME then the intersection of the two arrays must have at least one value in
108
+ * common to match. When `operator` is NONE then the intersection of the two arrays must not have any values in common
109
+ * to match.
110
+ *
111
+ * @param [operator] - The optional operator for the condition
112
+ * @param [datum] - The optional datum for the condition, this can be an item or a list of items of type unknown
113
+ * @param [value='$0m3tH1nG Un3xP3cT3d'] The optional value for the condition, defaulting to a highly unlikely value
114
+ * to avoid comparing two undefined elements when `value` was forgotten in the condition definition.
115
+ * This can be an item or a list of items of type unknown
116
+ * @returns - True if the condition matches, false otherwise
117
+ */
118
+ export function conditionMatches(operator, datum, value = '$0m3tH1nG Un3xP3cT3d') {
119
+ const data = flatten([datum]).sort();
120
+ const values = flatten([value]).sort();
121
+ switch (operator) {
122
+ case Operators.ALL:
123
+ return isEqual(data, values);
124
+ case Operators.SOME:
125
+ return intersection(data, values).length > 0;
126
+ case Operators.NONE:
127
+ return intersection(data, values).length === 0;
128
+ default:
129
+ return false;
130
+ }
131
+ }
132
+ /** From within the `layoutGridSchema` finds the `children` and any extra `gridProps` from the object keyed by
133
+ * `schemaKey`. If the `children` contains extra `gridProps` and those props contain a `className` string, try to
134
+ * lookup whether that `className` has a replacement value in the `registry` using the `FORM_CONTEXT_LOOKUP_BASE`.
135
+ * When the `className` value contains multiple classNames separated by a space, the lookup will look for a
136
+ * replacement value for each `className` and combine them into one.
137
+ *
138
+ * @param layoutGridSchema - The GridSchemaType instance from which to obtain the `schemaKey` children and extra props
139
+ * @param schemaKey - A `GridType` value, used to get the children and extra props from within the `layoutGridSchema`
140
+ * @param registry - The `@rjsf` Registry from which to look up `classNames` if they are present in the extra props
141
+ * @returns - An object containing the list of `LayoutGridSchemaType` `children` and any extra `gridProps`
142
+ * @throws - A `TypeError` when the `children` is not an array
143
+ */
144
+ export function findChildrenAndProps(layoutGridSchema, schemaKey, registry) {
145
+ let gridProps = {};
146
+ let children = layoutGridSchema[schemaKey];
147
+ if (isPlainObject(children)) {
148
+ const { children: elements, className: toMapClassNames, ...otherProps } = children;
149
+ children = elements;
150
+ if (toMapClassNames) {
151
+ const classes = toMapClassNames.split(' ');
152
+ const className = classes.map((ele) => lookupFromFormContext(registry, ele, ele)).join(' ');
153
+ gridProps = { ...otherProps, className };
154
+ }
155
+ else {
156
+ gridProps = otherProps;
157
+ }
158
+ }
159
+ if (!Array.isArray(children)) {
160
+ throw new TypeError(`Expected array for "${schemaKey}" in ${JSON.stringify(layoutGridSchema)}`);
161
+ }
162
+ return { children: children, gridProps };
163
+ }
164
+ /** Computes the `rawSchema` and `fieldPathId` for a `schema` and a `potentialIndex`. If the `schema` is of type array,
165
+ * has an `ITEMS_KEY` element and `potentialIndex` represents a numeric value, the element at `ITEMS_KEY` is checked
166
+ * to see if it is an array. If it is AND the `potentialIndex`th element is available, it is used as the `rawSchema`,
167
+ * otherwise the last value of the element is used. If it is not, then the element is used as the `rawSchema`. In
168
+ * either case, an `fieldPathId` is computed for the array index. If the `schema` does not represent an array or the
169
+ * `potentialIndex` is not a numeric value, then `rawSchema` is returned as undefined and given `fieldPathId` is returned
170
+ * as is.
171
+ *
172
+ * @param schema - The schema to generate the fieldPathId for
173
+ * @param fieldPathId - The FieldPathId for the schema
174
+ * @param potentialIndex - A string containing a potential index
175
+ * @returns - An object containing the `rawSchema` and `fieldPathId` of an array item, otherwise an undefined `rawSchema`
176
+ */
177
+ export function computeArraySchemasIfPresent(schema, fieldPathId, potentialIndex) {
178
+ let rawSchema;
179
+ if (isNumericIndex(potentialIndex) && schema && schema?.type === 'array' && has(schema, ITEMS_KEY)) {
180
+ const index = Number(potentialIndex);
181
+ const items = schema[ITEMS_KEY];
182
+ if (Array.isArray(items)) {
183
+ if (index > items.length) {
184
+ rawSchema = last(items);
185
+ }
186
+ else {
187
+ rawSchema = items[index];
188
+ }
189
+ }
190
+ else {
191
+ rawSchema = items;
192
+ }
193
+ fieldPathId = {
194
+ [ID_KEY]: fieldPathId[ID_KEY],
195
+ path: [...fieldPathId.path.slice(0, fieldPathId.path.length - 1), index],
196
+ };
197
+ }
198
+ return { rawSchema, fieldPathId };
199
+ }
200
+ /** Given a `dottedPath` to a field in the `initialSchema`, iterate through each individual path in the schema until
201
+ * the leaf path is found and returned (along with whether that leaf path `isRequired`) OR no schema exists for an
202
+ * element in the path. If the leaf schema element happens to be a oneOf/anyOf then also return the oneOf/anyOf as
203
+ * `options`.
204
+ *
205
+ * @param registry - The registry
206
+ * @param dottedPath - The dotted-path to the field for which to get the schema
207
+ * @param initialSchema - The initial schema to start the search from
208
+ * @param formData - The formData, useful for resolving a oneOf/anyOf selection in the path hierarchy
209
+ * @param initialFieldIdPath - The initial fieldPathId to start the search from
210
+ * @returns - An object containing the destination schema, isRequired and isReadonly flags for the field and options
211
+ * info if a oneOf/anyOf
212
+ */
213
+ export function getSchemaDetailsForField(registry, dottedPath, initialSchema, formData, initialFieldIdPath) {
214
+ const { schemaUtils, globalFormOptions } = registry;
215
+ let rawSchema = initialSchema;
216
+ let fieldPathId = initialFieldIdPath;
217
+ const parts = dottedPath.split('.');
218
+ const leafPath = parts.pop(); // pop off the last element in the list as the leaf
219
+ let schema = schemaUtils.retrieveSchema(rawSchema, formData); // always returns an object
220
+ let innerData = formData;
221
+ let isReadonly = schema.readOnly;
222
+ // For all the remaining path parts
223
+ parts.forEach((part) => {
224
+ // dive into the properties of the current schema (when it exists) and get the schema for the next part
225
+ fieldPathId = toFieldPathId(part, globalFormOptions, fieldPathId);
226
+ if (has(schema, PROPERTIES_KEY)) {
227
+ rawSchema = get(schema, [PROPERTIES_KEY, part], {});
228
+ }
229
+ else if (schema && (has(schema, ONE_OF_KEY) || has(schema, ANY_OF_KEY))) {
230
+ const xxx = has(schema, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
231
+ // When the schema represents a oneOf/anyOf, find the selected schema for it and grab the inner part
232
+ const selectedSchema = schemaUtils.findSelectedOptionInXxxOf(schema, part, xxx, innerData);
233
+ rawSchema = get(selectedSchema, [PROPERTIES_KEY, part], {});
234
+ }
235
+ else {
236
+ const result = computeArraySchemasIfPresent(schema, fieldPathId, part);
237
+ rawSchema = result.rawSchema ?? {};
238
+ fieldPathId = result.fieldPathId;
239
+ }
240
+ // Now drill into the innerData for the part, returning an empty object by default if it doesn't exist
241
+ innerData = get(innerData, part, {});
242
+ // Resolve any `$ref`s for the current rawSchema
243
+ schema = schemaUtils.retrieveSchema(rawSchema, innerData);
244
+ isReadonly = getNonNullishValue(schema.readOnly, isReadonly);
245
+ });
246
+ let optionsInfo;
247
+ let isRequired = false;
248
+ // retrieveSchema will return an empty schema in the worst case scenario, convert it to undefined
249
+ if (isEmpty(schema)) {
250
+ schema = undefined;
251
+ }
252
+ if (schema && leafPath) {
253
+ // When we have both a schema and a leafPath...
254
+ if (schema && (has(schema, ONE_OF_KEY) || has(schema, ANY_OF_KEY))) {
255
+ const xxx = has(schema, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
256
+ // Grab the selected schema for the oneOf/anyOf value for the leafPath using the innerData
257
+ schema = schemaUtils.findSelectedOptionInXxxOf(schema, leafPath, xxx, innerData);
258
+ }
259
+ fieldPathId = toFieldPathId(leafPath, globalFormOptions, fieldPathId);
260
+ isRequired = schema !== undefined && Array.isArray(schema.required) && includes(schema.required, leafPath);
261
+ const result = computeArraySchemasIfPresent(schema, fieldPathId, leafPath);
262
+ if (result.rawSchema) {
263
+ schema = result.rawSchema;
264
+ fieldPathId = result.fieldPathId;
265
+ }
266
+ else {
267
+ // Now grab the schema from the leafPath of the current schema properties
268
+ schema = get(schema, [PROPERTIES_KEY, leafPath]);
269
+ // Resolve any `$ref`s for the current schema
270
+ schema = schema ? schemaUtils.retrieveSchema(schema) : schema;
271
+ }
272
+ isReadonly = getNonNullishValue(schema?.readOnly, isReadonly);
273
+ if (schema && (has(schema, ONE_OF_KEY) || has(schema, ANY_OF_KEY))) {
274
+ const xxx = has(schema, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
275
+ // Set the options if we have a schema with a oneOf/anyOf
276
+ const discriminator = getDiscriminatorFieldFromSchema(schema);
277
+ optionsInfo = { options: schema[xxx], hasDiscriminator: !!discriminator };
278
+ }
279
+ }
280
+ return { schema, isRequired, isReadonly, optionsInfo, fieldPathId };
281
+ }
282
+ /** Gets the custom render component from the `render`, by either determining that it is either already a function or
283
+ * it is a non-function value that can be used to look up the function in the registry. If no function can be found,
284
+ * null is returned.
285
+ *
286
+ * @param render - The potential render function or lookup name to one
287
+ * @param registry - The `@rjsf` Registry from which to look up `classNames` if they are present in the extra props
288
+ * @returns - Either a render function if available, or null if not
289
+ */
290
+ export function getCustomRenderComponent(render, registry) {
291
+ let customRenderer = render;
292
+ if (isString(customRenderer)) {
293
+ customRenderer = lookupFromFormContext(registry, customRenderer);
294
+ }
295
+ if (isFunction(customRenderer)) {
296
+ return customRenderer;
297
+ }
298
+ return null;
299
+ }
300
+ /** Extract the `name`, and optional `render` and all other props from the `gridSchema`. We look up the `render` to
301
+ * see if can be resolved to a UIComponent. If `name` does not exist and there is an optional `render` UIComponent, we
302
+ * set the `rendered` component with only specified props for that component in the object.
303
+ *
304
+ * @param registry - The `@rjsf` Registry from which to look up `classNames` if they are present in the extra props
305
+ * @param gridSchema - The string or object that represents the configuration for the grid field
306
+ * @returns - The UIComponentPropsType computed from the gridSchema
307
+ */
308
+ export function computeUIComponentPropsFromGridSchema(registry, gridSchema) {
309
+ let name;
310
+ let UIComponent = null;
311
+ let uiProps = {};
312
+ let rendered;
313
+ if (isString(gridSchema) || isUndefined(gridSchema)) {
314
+ name = gridSchema ?? '';
315
+ }
316
+ else {
317
+ const { name: innerName = '', render, ...innerProps } = gridSchema;
318
+ name = innerName;
319
+ uiProps = innerProps;
320
+ if (!isEmpty(uiProps)) {
321
+ // Transform any `$lookup=` in the uiProps props with the appropriate value
322
+ each(uiProps, (prop, key) => {
323
+ if (isString(prop)) {
324
+ const match = LOOKUP_REGEX.exec(prop);
325
+ if (Array.isArray(match) && match.length > 1) {
326
+ const name = match[1];
327
+ uiProps[key] = lookupFromFormContext(registry, name, name);
328
+ }
329
+ }
330
+ });
331
+ }
332
+ UIComponent = getCustomRenderComponent(render, registry);
333
+ if (!innerName && UIComponent) {
334
+ rendered = _jsx(UIComponent, { ...innerProps, "data-testid": LAYOUT_GRID_FIELD_TEST_IDS.uiComponent });
335
+ }
336
+ }
337
+ return { name, UIComponent, uiProps, rendered };
338
+ }
339
+ /** Iterates through all the `childrenLayoutGridSchemaId`, rendering a nested `LayoutGridField` for each item in the
340
+ * list, passing all the props for the current `LayoutGridField` along, updating the `schema` by calling
341
+ * `retrieveSchema()` on it to resolve any `$ref`s. In addition to the updated `schema`, each item in
342
+ * `childrenLayoutGridSchemaId` is passed as `layoutGridSchema`.
343
+ *
344
+ * @returns - The nested `LayoutGridField`s
345
+ */
346
+ function LayoutGridFieldChildren(props) {
347
+ const { childrenLayoutGridSchemaId, ...layoutGridFieldProps } = props;
348
+ const { registry, schema: rawSchema, formData } = layoutGridFieldProps;
349
+ const { schemaUtils } = registry;
350
+ const schema = schemaUtils.retrieveSchema(rawSchema, formData);
351
+ return childrenLayoutGridSchemaId.map((layoutGridSchema) => (_createElement(LayoutGridField, { ...layoutGridFieldProps, key: `layoutGrid-${hashObject(layoutGridSchema)}`, schema: schema, layoutGridSchema: layoutGridSchema })));
352
+ }
353
+ /** Renders the `children` of the `GridType.CONDITION` if it passes. The `layoutGridSchema` for the
354
+ * `GridType.CONDITION` is separated into the `children` and other `gridProps`. The `gridProps` are used to extract
355
+ * the `operator`, `field` and `value` of the condition. If the condition matches, then all of the `children` are
356
+ * rendered, otherwise null is returned.
357
+ *
358
+ * @returns - The rendered the children for the `GridType.CONDITION` or null
359
+ */
360
+ function LayoutGridCondition(props) {
361
+ const { layoutGridSchema, ...layoutGridFieldProps } = props;
362
+ const { formData, registry } = layoutGridFieldProps;
363
+ const { children, gridProps } = findChildrenAndProps(layoutGridSchema, GridType.CONDITION, registry);
364
+ const { operator, field = '', value } = gridProps;
365
+ const fieldData = get(formData, field, null);
366
+ if (conditionMatches(operator, fieldData, value)) {
367
+ return _jsx(LayoutGridFieldChildren, { ...layoutGridFieldProps, childrenLayoutGridSchemaId: children });
368
+ }
369
+ return null;
370
+ }
371
+ /** Renders a `GridTemplate` as an item. The `layoutGridSchema` for the `GridType.COLUMN` is separated into the
372
+ * `children` and other `gridProps`. The `gridProps` will be spread onto the outer `GridTemplate`. Inside the
373
+ * `GridTemplate` all the `children` are rendered.
374
+ *
375
+ * @returns - The rendered `GridTemplate` containing the children for the `GridType.COLUMN`
376
+ */
377
+ function LayoutGridCol(props) {
378
+ const { layoutGridSchema, ...layoutGridFieldProps } = props;
379
+ const { registry, uiSchema } = layoutGridFieldProps;
380
+ const { children, gridProps } = findChildrenAndProps(layoutGridSchema, GridType.COLUMN, registry);
381
+ const uiOptions = getUiOptions(uiSchema);
382
+ const GridTemplate = getTemplate('GridTemplate', registry, uiOptions);
383
+ return (_jsx(GridTemplate, { column: true, "data-testid": LAYOUT_GRID_FIELD_TEST_IDS.col, ...gridProps, children: _jsx(LayoutGridFieldChildren, { ...layoutGridFieldProps, childrenLayoutGridSchemaId: children }) }));
384
+ }
385
+ /** Renders a `GridTemplate` as an item. The `layoutGridSchema` for the `GridType.COLUMNS` is separated into the
386
+ * `children` and other `gridProps`. The `children` is iterated on and `gridProps` will be spread onto the outer
387
+ * `GridTemplate`. Each child will have their own rendered `GridTemplate`.
388
+ *
389
+ * @returns - The rendered `GridTemplate` containing the children for the `GridType.COLUMNS`
390
+ */
391
+ function LayoutGridColumns(props) {
392
+ const { layoutGridSchema, ...layoutGridFieldProps } = props;
393
+ const { registry, uiSchema } = layoutGridFieldProps;
394
+ const { children, gridProps } = findChildrenAndProps(layoutGridSchema, GridType.COLUMNS, registry);
395
+ const uiOptions = getUiOptions(uiSchema);
396
+ const GridTemplate = getTemplate('GridTemplate', registry, uiOptions);
397
+ return children.map((child) => (_jsx(GridTemplate, { column: true, "data-testid": LAYOUT_GRID_FIELD_TEST_IDS.col, ...gridProps, children: _jsx(LayoutGridFieldChildren, { ...layoutGridFieldProps, childrenLayoutGridSchemaId: [child] }) }, `column-${hashObject(child)}`)));
398
+ }
399
+ /** Renders a `GridTemplate` as a container. The `layoutGridSchema` for the `GridType.ROW` is separated into the
400
+ * `children` and other `gridProps`. The `gridProps` will be spread onto the outer `GridTemplate`. Inside of the
401
+ * `GridTemplate` all of the `children` are rendered.
402
+ *
403
+ * @returns - The rendered `GridTemplate` containing the children for the `GridType.ROW`
404
+ */
405
+ function LayoutGridRow(props) {
406
+ const { layoutGridSchema, ...layoutGridFieldProps } = props;
407
+ const { registry, uiSchema } = layoutGridFieldProps;
408
+ const { children, gridProps } = findChildrenAndProps(layoutGridSchema, GridType.ROW, registry);
409
+ const uiOptions = getUiOptions(uiSchema);
410
+ const GridTemplate = getTemplate('GridTemplate', registry, uiOptions);
411
+ return (_jsx(GridTemplate, { ...gridProps, "data-testid": LAYOUT_GRID_FIELD_TEST_IDS.row, children: _jsx(LayoutGridFieldChildren, { ...layoutGridFieldProps, childrenLayoutGridSchemaId: children }) }));
412
+ }
413
+ /** Renders the field described by `gridSchema`. If `gridSchema` is not an object, then is will be assumed
414
+ * to be the dotted-path to the field in the schema. Otherwise, we extract the `name`, and optional `render` and all
415
+ * other props. If `name` does not exist and there is an optional `render`, we return the `render` component with only
416
+ * specified props for that component. If `name` exists, we take the name, the initial & root schemas and the formData
417
+ * and get the destination schema, is required state and optional oneOf/anyOf options for it. If the destination
418
+ * schema was located along with oneOf/anyOf options then a `LayoutMultiSchemaField` will be rendered with the
419
+ * `uiSchema`, `errorSchema`, `fieldPathId` and `formData` drilled down to the dotted-path field, spreading any other
420
+ * props from `gridSchema` into the `ui:options`. If the destination schema located without any oneOf/anyOf options,
421
+ * then a `SchemaField` will be rendered with the same props as mentioned in the previous sentence. If no destination
422
+ * schema was located, but a custom render component was found, then it will be rendered with many of the non-event
423
+ * handling props. If none of the previous render paths are valid, then a null is returned.
424
+ *
425
+ * @returns - One of `LayoutMultiSchemaField`, `SchemaField`, a custom render component or null, depending
426
+ */
427
+ function LayoutGridFieldComponent(props) {
428
+ const { gridSchema, schema: initialSchema, uiSchema, errorSchema, fieldPathId, onBlur, onFocus, formData, readonly, registry, layoutGridSchema, // Used to pull this out of otherProps since we don't want to pass it through
429
+ ...otherProps } = props;
430
+ const { onChange } = otherProps;
431
+ const { fields } = registry;
432
+ const { SchemaField, LayoutMultiSchemaField } = fields;
433
+ const uiComponentProps = computeUIComponentPropsFromGridSchema(registry, gridSchema);
434
+ const { name, UIComponent, uiProps } = uiComponentProps;
435
+ const { schema, isRequired, isReadonly, optionsInfo, fieldPathId: fieldIdSchema, } = getSchemaDetailsForField(registry, name, initialSchema, formData, fieldPathId);
436
+ const memoFieldPathId = useDeepCompareMemo(fieldIdSchema);
437
+ if (uiComponentProps.rendered) {
438
+ return uiComponentProps.rendered;
439
+ }
440
+ if (schema) {
441
+ const Field = optionsInfo?.hasDiscriminator ? LayoutMultiSchemaField : SchemaField;
442
+ // Call this function to get the appropriate UISchema, which will always have its `readonly` state matching the
443
+ // `uiReadonly` flag that it returns. This is done since the `SchemaField` will always defer to the `readonly`
444
+ // state in the uiSchema over anything in the props or schema. Because we are implementing the "readonly" state of
445
+ // the `Form` via the prop passed to `LayoutGridField` we need to make sure the uiSchema always has a true value
446
+ // when it is needed
447
+ const { fieldUiSchema, uiReadonly } = computeFieldUiSchema(name, uiProps, uiSchema, isReadonly, readonly);
448
+ return (_jsx(Field, { "data-testid": optionsInfo?.hasDiscriminator
449
+ ? LAYOUT_GRID_FIELD_TEST_IDS.layoutMultiSchemaField
450
+ : LAYOUT_GRID_FIELD_TEST_IDS.field, ...otherProps, name: name, required: isRequired, readonly: uiReadonly, schema: schema, uiSchema: fieldUiSchema, errorSchema: get(errorSchema, name), fieldPathId: memoFieldPathId, formData: get(formData, name), onChange: onChange, onBlur: onBlur, onFocus: onFocus, options: optionsInfo?.options, registry: registry }));
451
+ }
452
+ if (UIComponent) {
453
+ return (_jsx(UIComponent, { "data-testid": LAYOUT_GRID_FIELD_TEST_IDS.uiComponent, ...otherProps, name: name, required: isRequired, formData: formData, readOnly: !!isReadonly || readonly, errorSchema: errorSchema, uiSchema: uiSchema, schema: initialSchema, fieldPathId: fieldPathId, onBlur: onBlur, onFocus: onFocus, registry: registry, ...uiProps }));
454
+ }
455
+ return null;
456
+ }
55
457
  /** The `LayoutGridField` will render a schema, uiSchema and formData combination out into a GridTemplate in the shape
56
458
  * described in the uiSchema. To define the grid to use to render the elements within a field in the schema, provide in
57
459
  * the uiSchema for that field the object contained under a `ui:layoutGrid` element. E.g. (as a JSON object):
@@ -275,434 +677,32 @@ function getNonNullishValue(value, fallback) {
275
677
  * ]
276
678
  * ```
277
679
  */
278
- export default class LayoutGridField extends PureComponent {
279
- static defaultProps = {
280
- layoutGridSchema: undefined,
281
- };
282
- static TEST_IDS = getTestIds();
283
- /** Computes the uiSchema for the field with `name` from the `uiProps` and `uiSchema` provided. The field UI Schema
284
- * will always contain a copy of the global options from the `uiSchema` (so they can be passed down) as well as
285
- * copying them into the local ui options. When the `forceReadonly` flag is true, then the field UI Schema is
286
- * updated to make "readonly" be true. When the `schemaReadonly` flag is true AND the field UI Schema does NOT have
287
- * the flag already provided, then we also make "readonly" true. We always make sure to return the final value of the
288
- * field UI Schema's "readonly" flag as `uiReadonly` along with the `fieldUiSchema` in the return value.
289
- *
290
- * @param field - The name of the field to pull the existing UI Schema for
291
- * @param uiProps - Any props that should be put into the field's uiSchema
292
- * @param [uiSchema] - The optional UI Schema from which to get the UI schema for the field
293
- * @param [schemaReadonly] - Optional flag indicating whether the schema indicates the field is readonly
294
- * @param [forceReadonly] - Optional flag indicating whether the Form itself is in readonly mode
295
- */
296
- static computeFieldUiSchema(field, uiProps, uiSchema, schemaReadonly, forceReadonly) {
297
- const globalUiOptions = get(uiSchema, [UI_GLOBAL_OPTIONS_KEY], {});
298
- const localUiSchema = get(uiSchema, field);
299
- const localUiOptions = { ...get(localUiSchema, [UI_OPTIONS_KEY], {}), ...uiProps, ...globalUiOptions };
300
- const fieldUiSchema = { ...localUiSchema };
301
- if (!isEmpty(localUiOptions)) {
302
- set(fieldUiSchema, [UI_OPTIONS_KEY], localUiOptions);
303
- }
304
- if (!isEmpty(globalUiOptions)) {
305
- // pass the global uiOptions down to the field uiSchema so that they can be applied to all nested fields
306
- set(fieldUiSchema, [UI_GLOBAL_OPTIONS_KEY], globalUiOptions);
307
- }
308
- let { readonly: uiReadonly } = getUiOptions(fieldUiSchema);
309
- if (forceReadonly === true || (isUndefined(uiReadonly) && schemaReadonly === true)) {
310
- // If we are forcing all widgets to be readonly, OR the schema indicates it is readonly AND the uiSchema does not
311
- // have an overriding value, then update the uiSchema to set readonly to true. Doing this will
312
- uiReadonly = true;
313
- if (has(localUiOptions, READONLY_KEY)) {
314
- // If the local options has the key value provided in it, then set that one to true
315
- set(fieldUiSchema, [UI_OPTIONS_KEY, READONLY_KEY], true);
316
- }
317
- else {
318
- // otherwise set the `ui:` version
319
- set(fieldUiSchema, `ui:${READONLY_KEY}`, true);
320
- }
321
- }
322
- return { fieldUiSchema, uiReadonly };
323
- }
324
- /** Given an `operator`, `datum` and `value` determines whether this condition is considered matching. Matching
325
- * depends on the `operator`. The `datum` and `value` are converted into arrays if they aren't already and then the
326
- * contents of the two arrays are compared using the `operator`. When `operator` is All, then the two arrays must be
327
- * equal to match. When `operator` is SOME then the intersection of the two arrays must have at least one value in
328
- * common to match. When `operator` is NONE then the intersection of the two arrays must not have any values in common
329
- * to match.
330
- *
331
- * @param [operator] - The optional operator for the condition
332
- * @param [datum] - The optional datum for the condition, this can be an item or a list of items of type unknown
333
- * @param [value='$0m3tH1nG Un3xP3cT3d'] The optional value for the condition, defaulting to a highly unlikely value
334
- * to avoid comparing two undefined elements when `value` was forgotten in the condition definition.
335
- * This can be an item or a list of items of type unknown
336
- * @returns - True if the condition matches, false otherwise
337
- */
338
- static conditionMatches(operator, datum, value = '$0m3tH1nG Un3xP3cT3d') {
339
- const data = flatten([datum]).sort();
340
- const values = flatten([value]).sort();
341
- switch (operator) {
342
- case Operators.ALL:
343
- return isEqual(data, values);
344
- case Operators.SOME:
345
- return intersection(data, values).length > 0;
346
- case Operators.NONE:
347
- return intersection(data, values).length === 0;
348
- default:
349
- return false;
350
- }
351
- }
352
- /** From within the `layoutGridSchema` finds the `children` and any extra `gridProps` from the object keyed by
353
- * `schemaKey`. If the `children` contains extra `gridProps` and those props contain a `className` string, try to
354
- * lookup whether that `className` has a replacement value in the `registry` using the `FORM_CONTEXT_LOOKUP_BASE`.
355
- * When the `className` value contains multiple classNames separated by a space, the lookup will look for a
356
- * replacement value for each `className` and combine them into one.
357
- *
358
- * @param layoutGridSchema - The GridSchemaType instance from which to obtain the `schemaKey` children and extra props
359
- * @param schemaKey - A `GridType` value, used to get the children and extra props from within the `layoutGridSchema`
360
- * @param registry - The `@rjsf` Registry from which to look up `classNames` if they are present in the extra props
361
- * @returns - An object containing the list of `LayoutGridSchemaType` `children` and any extra `gridProps`
362
- * @throws - A `TypeError` when the `children` is not an array
363
- */
364
- static findChildrenAndProps(layoutGridSchema, schemaKey, registry) {
365
- let gridProps = {};
366
- let children = layoutGridSchema[schemaKey];
367
- if (isPlainObject(children)) {
368
- const { children: elements, className: toMapClassNames, ...otherProps } = children;
369
- children = elements;
370
- if (toMapClassNames) {
371
- const classes = toMapClassNames.split(' ');
372
- const className = classes.map((ele) => lookupFromFormContext(registry, ele, ele)).join(' ');
373
- gridProps = { ...otherProps, className };
374
- }
375
- else {
376
- gridProps = otherProps;
377
- }
378
- }
379
- if (!Array.isArray(children)) {
380
- throw new TypeError(`Expected array for "${schemaKey}" in ${JSON.stringify(layoutGridSchema)}`);
381
- }
382
- return { children: children, gridProps };
383
- }
384
- /** Generates an idSchema for the `schema` using `@rjsf`'s `toIdSchema` util, passing the `baseIdSchema`'s `$id` value
385
- * as the id prefix.
386
- *
387
- * @param schemaUtils - The `SchemaUtilsType` used to call `toIdSchema`
388
- * @param schema - The schema to generate the idSchema for
389
- * @param baseIdSchema - The IdSchema for the base
390
- * @param formData - The formData to pass the `toIdSchema`
391
- * @param [idSeparator] - The param to pass into the `toIdSchema` util which will use it to join the `idSchema` paths
392
- * @returns - The generated `idSchema` for the `schema`
393
- */
394
- static getIdSchema(schemaUtils, baseIdSchema, formData, schema = {}, idSeparator) {
395
- const baseId = get(baseIdSchema, ID_KEY);
396
- return schemaUtils.toIdSchema(schema, baseId, formData, baseId, idSeparator);
397
- }
398
- /** Given a `dottedPath` to a field in the `initialSchema`, iterate through each individual path in the schema until
399
- * the leaf path is found and returned (along with whether that leaf path `isRequired`) OR no schema exists for an
400
- * element in the path. If the leaf schema element happens to be a oneOf/anyOf then also return the oneOf/anyOf as
401
- * `options`.
402
- *
403
- * @param schemaUtils - The `SchemaUtilsType` used to call `retrieveSchema`
404
- * @param dottedPath - The dotted-path to the field for which to get the schema
405
- * @param initialSchema - The initial schema to start the search from
406
- * @param formData - The formData, useful for resolving a oneOf/anyOf selection in the path hierarchy
407
- * @param initialIdSchema - The initial idSchema to start the search from
408
- * @param [idSeparator] - The param to pass into the `toIdSchema` util which will use it to join the `idSchema` paths
409
- * @returns - An object containing the destination schema, isRequired and isReadonly flags for the field and options
410
- * info if a oneOf/anyOf
411
- */
412
- static getSchemaDetailsForField(schemaUtils, dottedPath, initialSchema, formData, initialIdSchema, idSeparator) {
413
- let rawSchema = initialSchema;
414
- let idSchema = initialIdSchema;
415
- const parts = dottedPath.split('.');
416
- const leafPath = parts.pop(); // pop off the last element in the list as the leaf
417
- let schema = schemaUtils.retrieveSchema(rawSchema, formData); // always returns an object
418
- let innerData = formData;
419
- let isReadonly = schema.readOnly;
420
- // For all the remaining path parts
421
- parts.forEach((part) => {
422
- // dive into the properties of the current schema (when it exists) and get the schema for the next part
423
- if (has(schema, PROPERTIES_KEY)) {
424
- rawSchema = get(schema, [PROPERTIES_KEY, part], {});
425
- idSchema = get(idSchema, part, {});
426
- }
427
- else if (schema && (has(schema, ONE_OF_KEY) || has(schema, ANY_OF_KEY))) {
428
- const xxx = has(schema, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
429
- // When the schema represents a oneOf/anyOf, find the selected schema for it and grab the inner part
430
- const selectedSchema = schemaUtils.findSelectedOptionInXxxOf(schema, part, xxx, innerData);
431
- const selectedIdSchema = LayoutGridField.getIdSchema(schemaUtils, idSchema, formData, selectedSchema, idSeparator);
432
- rawSchema = get(selectedSchema, [PROPERTIES_KEY, part], {});
433
- idSchema = get(selectedIdSchema, part, {});
434
- }
435
- else {
436
- rawSchema = {};
437
- }
438
- // Now drill into the innerData for the part, returning an empty object by default if it doesn't exist
439
- innerData = get(innerData, part, {});
440
- // Resolve any `$ref`s for the current rawSchema
441
- schema = schemaUtils.retrieveSchema(rawSchema, innerData);
442
- isReadonly = getNonNullishValue(schema.readOnly, isReadonly);
443
- });
444
- let optionsInfo;
445
- let isRequired = false;
446
- // retrieveSchema will return an empty schema in the worst case scenario, convert it to undefined
447
- if (isEmpty(schema)) {
448
- schema = undefined;
449
- }
450
- if (schema && leafPath) {
451
- // When we have both a schema and a leafPath...
452
- if (schema && (has(schema, ONE_OF_KEY) || has(schema, ANY_OF_KEY))) {
453
- const xxx = has(schema, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
454
- // Grab the selected schema for the oneOf/anyOf value for the leafPath using the innerData
455
- schema = schemaUtils.findSelectedOptionInXxxOf(schema, leafPath, xxx, innerData);
456
- // Generate the idSchema for the oneOf/anyOf value then merge with the existing `idSchema`
457
- const rawIdSchema = LayoutGridField.getIdSchema(schemaUtils, idSchema, formData, schema, idSeparator);
458
- idSchema = mergeObjects(rawIdSchema, idSchema);
459
- }
460
- isRequired = schema !== undefined && Array.isArray(schema.required) && includes(schema.required, leafPath);
461
- // Now grab the schema from the leafPath of the current schema properties
462
- schema = get(schema, [PROPERTIES_KEY, leafPath]);
463
- // Resolve any `$ref`s for the current schema
464
- schema = schema ? schemaUtils.retrieveSchema(schema) : schema;
465
- idSchema = get(idSchema, leafPath, {});
466
- isReadonly = getNonNullishValue(schema?.readOnly, isReadonly);
467
- if (schema && (has(schema, ONE_OF_KEY) || has(schema, ANY_OF_KEY))) {
468
- const xxx = has(schema, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
469
- // Set the options if we have a schema with a oneOf/anyOf
470
- const discriminator = getDiscriminatorFieldFromSchema(schema);
471
- optionsInfo = { options: schema[xxx], hasDiscriminator: !!discriminator };
472
- }
473
- }
474
- return { schema, isRequired, isReadonly, optionsInfo, idSchema };
475
- }
476
- /** Gets the custom render component from the `render`, by either determining that it is either already a function or
477
- * it is a non-function value that can be used to look up the function in the registry. If no function can be found,
478
- * null is returned.
479
- *
480
- * @param render - The potential render function or lookup name to one
481
- * @param registry - The `@rjsf` Registry from which to look up `classNames` if they are present in the extra props
482
- * @returns - Either a render function if available, or null if not
483
- */
484
- static getCustomRenderComponent(render, registry) {
485
- let customRenderer = render;
486
- if (isString(customRenderer)) {
487
- customRenderer = lookupFromFormContext(registry, customRenderer);
488
- }
489
- if (isFunction(customRenderer)) {
490
- return customRenderer;
491
- }
492
- return null;
493
- }
494
- /** Extract the `name`, and optional `render` and all other props from the `gridSchema`. We look up the `render` to
495
- * see if can be resolved to a UIComponent. If `name` does not exist and there is an optional `render` UIComponent, we
496
- * set the `rendered` component with only specified props for that component in the object.
497
- *
498
- * @param registry - The `@rjsf` Registry from which to look up `classNames` if they are present in the extra props
499
- * @param gridSchema - The string or object that represents the configuration for the grid field
500
- * @returns - The UIComponentPropsType computed from the gridSchema
501
- */
502
- static computeUIComponentPropsFromGridSchema(registry, gridSchema) {
503
- let name;
504
- let UIComponent = null;
505
- let uiProps = {};
506
- let rendered;
507
- if (isString(gridSchema) || isUndefined(gridSchema)) {
508
- name = gridSchema ?? '';
509
- }
510
- else {
511
- const { name: innerName = '', render, ...innerProps } = gridSchema;
512
- name = innerName;
513
- uiProps = innerProps;
514
- if (!isEmpty(uiProps)) {
515
- // Transform any `$lookup=` in the uiProps props with the appropriate value
516
- each(uiProps, (prop, key) => {
517
- if (isString(prop)) {
518
- const match = LOOKUP_REGEX.exec(prop);
519
- if (Array.isArray(match) && match.length > 1) {
520
- const name = match[1];
521
- uiProps[key] = lookupFromFormContext(registry, name, name);
522
- }
523
- }
524
- });
525
- }
526
- UIComponent = LayoutGridField.getCustomRenderComponent(render, registry);
527
- if (!innerName && UIComponent) {
528
- rendered = _jsx(UIComponent, { ...innerProps, "data-testid": LayoutGridField.TEST_IDS.uiComponent });
529
- }
530
- }
531
- return { name, UIComponent, uiProps, rendered };
532
- }
533
- /** Constructs an `LayoutGridField` with the given `props`
534
- *
535
- * @param props - The `LayoutGridField` for this template
536
- */
537
- constructor(props) {
538
- super(props);
539
- }
540
- /** Generates an `onChange` handler for the field associated with the `dottedPath`. This handler will clone and update
541
- * the `formData` with the new `value` and the `errorSchema` if an `errSchema` is provided. After updating those two
542
- * elements, they will then be passed on to the `onChange` handler of the `LayoutFieldGrid`.
543
- *
544
- * @param dottedPath - The dotted-path to the field for which to generate the onChange handler
545
- * @returns - The `onChange` handling function for the `dottedPath` field
546
- */
547
- onFieldChange = (dottedPath) => {
548
- return (value, errSchema, id) => {
549
- const { onChange, errorSchema, formData } = this.props;
550
- const newFormData = cloneDeep(formData || {});
551
- let newErrorSchema = errorSchema;
552
- if (errSchema && errorSchema) {
553
- newErrorSchema = cloneDeep(errorSchema);
554
- set(newErrorSchema, dottedPath, errSchema);
555
- }
556
- set(newFormData, dottedPath, value);
557
- onChange(newFormData, newErrorSchema, id);
558
- };
559
- };
560
- /** Renders the `children` of the `GridType.CONDITION` if it passes. The `layoutGridSchema` for the
561
- * `GridType.CONDITION` is separated into the `children` and other `gridProps`. The `gridProps` are used to extract
562
- * the `operator`, `field` and `value` of the condition. If the condition matches, then all of the `children` are
563
- * rendered, otherwise null is returned.
564
- *
565
- * @param layoutGridSchema - The string or object that represents the configuration for the grid field
566
- * @returns - The rendered the children for the `GridType.CONDITION` or null
567
- */
568
- renderCondition(layoutGridSchema) {
569
- const { formData, registry } = this.props;
570
- const { children, gridProps } = LayoutGridField.findChildrenAndProps(layoutGridSchema, GridType.CONDITION, registry);
571
- const { operator, field = '', value } = gridProps;
572
- const fieldData = get(formData, field, null);
573
- if (LayoutGridField.conditionMatches(operator, fieldData, value)) {
574
- return this.renderChildren(children);
575
- }
576
- return null;
577
- }
578
- /** Renders a material-ui `GridTemplate` as an item. The `layoutGridSchema` for the `GridType.COLUMN` is separated
579
- * into the `children` and other `gridProps`. The `gridProps` will be spread onto the outer `GridTemplate`. Inside
580
- * the `GridTemplate` all the `children` are rendered.
581
- *
582
- * @param layoutGridSchema - The string or object that represents the configuration for the grid field
583
- * @returns - The rendered `GridTemplate` containing the children for the `GridType.COLUMN`
584
- */
585
- renderCol(layoutGridSchema) {
586
- const { registry, uiSchema } = this.props;
587
- const { children, gridProps } = LayoutGridField.findChildrenAndProps(layoutGridSchema, GridType.COLUMN, registry);
588
- const uiOptions = getUiOptions(uiSchema);
589
- const GridTemplate = getTemplate('GridTemplate', registry, uiOptions);
590
- return (_jsx(GridTemplate, { column: true, "data-testid": LayoutGridField.TEST_IDS.col, ...gridProps, children: this.renderChildren(children) }));
591
- }
592
- /** Renders a material-ui `GridTemplate` as an item. The `layoutGridSchema` for the `GridType.COLUMNS` is separated
593
- * into the `children` and other `gridProps`. The `children` is iterated on and `gridProps` will be spread onto the
594
- * outer `GridTemplate`. Each child will have their own rendered `GridTemplate`.
595
- *
596
- * @param layoutGridSchema - The string or object that represents the configuration for the grid field
597
- * @returns - The rendered `GridTemplate` containing the children for the `GridType.COLUMNS`
598
- */
599
- renderColumns(layoutGridSchema) {
600
- const { registry, uiSchema } = this.props;
601
- const { children, gridProps } = LayoutGridField.findChildrenAndProps(layoutGridSchema, GridType.COLUMNS, registry);
602
- const uiOptions = getUiOptions(uiSchema);
603
- const GridTemplate = getTemplate('GridTemplate', registry, uiOptions);
604
- return children.map((child) => (_jsx(GridTemplate, { column: true, "data-testid": LayoutGridField.TEST_IDS.col, ...gridProps, children: this.renderChildren([child]) }, `column-${hashObject(child)}`)));
605
- }
606
- /** Renders a material-ui `GridTemplate` as a container. The
607
- * `layoutGridSchema` for the `GridType.ROW` is separated into the `children` and other `gridProps`. The `gridProps`
608
- * will be spread onto the outer `GridTemplate`. Inside of the `GridTemplate` all of the `children` are rendered.
609
- *
610
- * @param layoutGridSchema - The string or object that represents the configuration for the grid field
611
- * @returns - The rendered `GridTemplate` containing the children for the `GridType.ROW`
612
- */
613
- renderRow(layoutGridSchema) {
614
- const { registry, uiSchema } = this.props;
615
- const { children, gridProps } = LayoutGridField.findChildrenAndProps(layoutGridSchema, GridType.ROW, registry);
616
- const uiOptions = getUiOptions(uiSchema);
617
- const GridTemplate = getTemplate('GridTemplate', registry, uiOptions);
618
- return (_jsx(GridTemplate, { ...gridProps, "data-testid": LayoutGridField.TEST_IDS.row, children: this.renderChildren(children) }));
619
- }
620
- /** Iterates through all the `childrenLayoutGridSchema`, rendering a nested `LayoutGridField` for each item in the
621
- * list, passing all the props for the current `LayoutGridField` along, updating the `schema` by calling
622
- * `retrieveSchema()` on it to resolve any `$ref`s. In addition to the updated `schema`, each item in
623
- * `childrenLayoutGridSchema` is passed as `layoutGridSchema`.
624
- *
625
- * @param childrenLayoutGridSchema - The list of strings or objects that represents the configurations for the
626
- * children fields
627
- * @returns - The nested `LayoutGridField`s
628
- */
629
- renderChildren(childrenLayoutGridSchema) {
630
- const { registry, schema: rawSchema, formData } = this.props;
631
- const { schemaUtils } = registry;
632
- const schema = schemaUtils.retrieveSchema(rawSchema, formData);
633
- return childrenLayoutGridSchema.map((layoutGridSchema) => (_createElement(LayoutGridField, { ...this.props, key: `layoutGrid-${hashObject(layoutGridSchema)}`, schema: schema, layoutGridSchema: layoutGridSchema })));
634
- }
635
- /** Renders the field described by `gridSchema`. If `gridSchema` is not an object, then is will be assumed
636
- * to be the dotted-path to the field in the schema. Otherwise, we extract the `name`, and optional `render` and all
637
- * other props. If `name` does not exist and there is an optional `render`, we return the `render` component with only
638
- * specified props for that component. If `name` exists, we take the name, the initial & root schemas and the formData
639
- * and get the destination schema, is required state and optional oneOf/anyOf options for it. If the destination
640
- * schema was located along with oneOf/anyOf options then a `LayoutMultiSchemaField` will be rendered with the
641
- * `uiSchema`, `errorSchema`, `idSchema` and `formData` drilled down to the dotted-path field, spreading any other
642
- * props from `gridSchema` into the `ui:options`. If the destination schema located without any oneOf/anyOf options,
643
- * then a `SchemaField` will be rendered with the same props as mentioned in the previous sentence. If no destination
644
- * schema was located, but a custom render component was found, then it will be rendered with many of the non-event
645
- * handling props. If none of the previous render paths are valid, then a null is returned.
646
- *
647
- * @param gridSchema - The string or object that represents the configuration for the grid field
648
- * @returns - One of `LayoutMultiSchemaField`, `SchemaField`, a custom render component or null, depending
649
- */
650
- renderField(gridSchema) {
651
- const { schema: initialSchema, uiSchema, errorSchema, idSchema, onBlur, onFocus, formData, readonly, registry, idSeparator, layoutGridSchema, // Used to pull this out of otherProps since we don't want to pass it through
652
- ...otherProps } = this.props;
653
- const { fields, schemaUtils } = registry;
654
- const { SchemaField, LayoutMultiSchemaField } = fields;
655
- const uiComponentProps = LayoutGridField.computeUIComponentPropsFromGridSchema(registry, gridSchema);
656
- if (uiComponentProps.rendered) {
657
- return uiComponentProps.rendered;
658
- }
659
- const { name, UIComponent, uiProps } = uiComponentProps;
660
- const { schema, isRequired, isReadonly, optionsInfo, idSchema: fieldIdSchema, } = LayoutGridField.getSchemaDetailsForField(schemaUtils, name, initialSchema, formData, idSchema, idSeparator);
661
- if (schema) {
662
- const Field = optionsInfo?.hasDiscriminator ? LayoutMultiSchemaField : SchemaField;
663
- // Call this function to get the appropriate UISchema, which will always have its `readonly` state matching the
664
- // `uiReadonly` flag that it returns. This is done since the `SchemaField` will always defer to the `readonly`
665
- // state in the uiSchema over anything in the props or schema. Because we are implementing the "readonly" state of
666
- // the `Form` via the prop passed to `LayoutGridField` we need to make sure the uiSchema always has a true value
667
- // when it is needed
668
- const { fieldUiSchema, uiReadonly } = LayoutGridField.computeFieldUiSchema(name, uiProps, uiSchema, isReadonly, readonly);
669
- return (_jsx(Field, { "data-testid": optionsInfo?.hasDiscriminator
670
- ? LayoutGridField.TEST_IDS.layoutMultiSchemaField
671
- : LayoutGridField.TEST_IDS.field, ...otherProps, name: name, required: isRequired, readonly: uiReadonly, schema: schema, uiSchema: fieldUiSchema, errorSchema: get(errorSchema, name), idSchema: fieldIdSchema, idSeparator: idSeparator, formData: get(formData, name), onChange: this.onFieldChange(name), onBlur: onBlur, onFocus: onFocus, options: optionsInfo?.options, registry: registry }));
672
- }
673
- if (UIComponent) {
674
- return (_jsx(UIComponent, { "data-testid": LayoutGridField.TEST_IDS.uiComponent, ...otherProps, name: name, required: isRequired, formData: formData, readOnly: !!isReadonly || readonly, errorSchema: errorSchema, uiSchema: uiSchema, schema: initialSchema, idSchema: idSchema, idSeparator: idSeparator, onBlur: onBlur, onFocus: onFocus, registry: registry, ...uiProps }));
675
- }
676
- return null;
677
- }
678
- /** Renders the `LayoutGridField`. If there isn't a `layoutGridSchema` prop defined, then try pulling it out of the
680
+ export default function LayoutGridField(props) {
681
+ /** Render the `LayoutGridField`. If there isn't a `layoutGridSchema` prop defined, then try pulling it out of the
679
682
  * `uiSchema` via `ui:LayoutGridField`. If `layoutGridSchema` is an object, then check to see if any of the properties
680
683
  * match one of the `GridType`s. If so, call the appropriate render function for the type. Otherwise, just call the
681
684
  * generic `renderField()` function with the `layoutGridSchema`.
682
- *
683
- * @returns - the rendered `LayoutGridField`
684
685
  */
685
- render() {
686
- const { uiSchema } = this.props;
687
- let { layoutGridSchema } = this.props;
688
- const uiOptions = getUiOptions(uiSchema);
689
- if (!layoutGridSchema && LAYOUT_GRID_UI_OPTION in uiOptions && isObject(uiOptions[LAYOUT_GRID_UI_OPTION])) {
690
- layoutGridSchema = uiOptions[LAYOUT_GRID_UI_OPTION];
686
+ const { uiSchema } = props;
687
+ let { layoutGridSchema } = props;
688
+ const uiOptions = getUiOptions(uiSchema);
689
+ if (!layoutGridSchema && LAYOUT_GRID_UI_OPTION in uiOptions && isObject(uiOptions[LAYOUT_GRID_UI_OPTION])) {
690
+ layoutGridSchema = uiOptions[LAYOUT_GRID_UI_OPTION];
691
+ }
692
+ if (isObject(layoutGridSchema)) {
693
+ if (GridType.ROW in layoutGridSchema) {
694
+ return _jsx(LayoutGridRow, { ...props, layoutGridSchema: layoutGridSchema });
691
695
  }
692
- if (isObject(layoutGridSchema)) {
693
- if (GridType.ROW in layoutGridSchema) {
694
- return this.renderRow(layoutGridSchema);
695
- }
696
- if (GridType.COLUMN in layoutGridSchema) {
697
- return this.renderCol(layoutGridSchema);
698
- }
699
- if (GridType.COLUMNS in layoutGridSchema) {
700
- return this.renderColumns(layoutGridSchema);
701
- }
702
- if (GridType.CONDITION in layoutGridSchema) {
703
- return this.renderCondition(layoutGridSchema);
704
- }
696
+ if (GridType.COLUMN in layoutGridSchema) {
697
+ return _jsx(LayoutGridCol, { ...props, layoutGridSchema: layoutGridSchema });
698
+ }
699
+ if (GridType.COLUMNS in layoutGridSchema) {
700
+ return _jsx(LayoutGridColumns, { ...props, layoutGridSchema: layoutGridSchema });
701
+ }
702
+ if (GridType.CONDITION in layoutGridSchema) {
703
+ return _jsx(LayoutGridCondition, { ...props, layoutGridSchema: layoutGridSchema });
705
704
  }
706
- return this.renderField(layoutGridSchema);
707
705
  }
706
+ return _jsx(LayoutGridFieldComponent, { ...props, gridSchema: layoutGridSchema });
708
707
  }
708
+ LayoutGridField.TEST_IDS = LAYOUT_GRID_FIELD_TEST_IDS;