@rjsf/core 6.7.0 → 6.8.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.
- package/dist/core.umd.js +57 -13
- package/dist/index.cjs +57 -13
- package/dist/index.cjs.map +2 -2
- package/dist/index.esm.js +90 -46
- package/dist/index.esm.js.map +3 -3
- package/lib/components/fields/NumberField.d.ts.map +1 -1
- package/lib/components/fields/NumberField.js +25 -14
- package/lib/components/fields/ObjectField.d.ts.map +1 -1
- package/lib/components/fields/ObjectField.js +19 -4
- package/lib/components/widgets/TimeWidget.d.ts.map +1 -1
- package/lib/components/widgets/TimeWidget.js +15 -3
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/src/components/fields/NumberField.tsx +29 -15
- package/src/components/fields/ObjectField.tsx +27 -6
- package/src/components/widgets/TimeWidget.tsx +18 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NumberField.d.ts","sourceRoot":"","sources":["../../../src/components/fields/NumberField.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,UAAU,EACV,eAAe,EACf,UAAU,EACV,gBAAgB,EACjB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"NumberField.d.ts","sourceRoot":"","sources":["../../../src/components/fields/NumberField.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,UAAU,EACV,eAAe,EACf,UAAU,EACV,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAOrB;;;;;;;;;;;;;;;;GAgBG;AACH,iBAAS,WAAW,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,SAAS,gBAAgB,GAAG,UAAU,EAAE,CAAC,SAAS,eAAe,GAAG,GAAG,EACpG,KAAK,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,+BAsE3B;AAED,eAAe,WAAW,CAAC"}
|
|
@@ -1,15 +1,8 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useCallback } from 'react';
|
|
3
|
-
import { asNumber } from '@rjsf/utils';
|
|
4
|
-
//
|
|
5
|
-
// digits followed by any number of 0 characters up until the end of the line.
|
|
6
|
-
// Ensuring that there is at least one prefixed character is important so that
|
|
7
|
-
// you don't incorrectly match against "0".
|
|
3
|
+
import { asNumber, getDecimalSeparator, getUiOptions, optionsList } from '@rjsf/utils';
|
|
4
|
+
// Static matchers for standard '.' separator used during normalization inside handleChange
|
|
8
5
|
const trailingCharMatcherWithPrefix = /\.([0-9]*0)*$/;
|
|
9
|
-
// This is used for trimming the trailing 0 and . characters without affecting
|
|
10
|
-
// the rest of the string. Its possible to use one RegEx with groups for this
|
|
11
|
-
// functionality, but it is fairly complex compared to simply defining two
|
|
12
|
-
// different matchers.
|
|
13
6
|
const trailingCharMatcher = /[0.]0*$/;
|
|
14
7
|
/**
|
|
15
8
|
* The NumberField class has some special handling for dealing with trailing
|
|
@@ -32,6 +25,8 @@ function NumberField(props) {
|
|
|
32
25
|
const { registry, onChange, formData, value: initialValue } = props;
|
|
33
26
|
const [lastValue, setLastValue] = useState(initialValue);
|
|
34
27
|
const { StringField } = registry.fields;
|
|
28
|
+
const separator = getDecimalSeparator();
|
|
29
|
+
const escapedSeparator = separator === '.' ? '\\.' : separator;
|
|
35
30
|
let value = formData;
|
|
36
31
|
/** Handle the change from the `StringField` to properly convert to a number
|
|
37
32
|
*
|
|
@@ -40,9 +35,11 @@ function NumberField(props) {
|
|
|
40
35
|
const handleChange = useCallback((newValue, path, errorSchema, id) => {
|
|
41
36
|
// Cache the original value in component state
|
|
42
37
|
setLastValue(newValue);
|
|
38
|
+
// Convert locale separator to standard '.' first
|
|
39
|
+
const standardValue = typeof newValue === 'string' ? newValue.replace(separator, '.') : newValue;
|
|
43
40
|
// Normalize decimals that don't start with a zero character in advance so
|
|
44
41
|
// that the rest of the normalization logic is simpler
|
|
45
|
-
const normalizedValue = `${
|
|
42
|
+
const normalizedValue = `${standardValue}`.startsWith('.') ? `0${standardValue}` : standardValue;
|
|
46
43
|
// Check that the value is a string (this can happen if the widget used is a
|
|
47
44
|
// <select>, due to an enum declaration etc) then, if the value ends in a
|
|
48
45
|
// trailing decimal point or multiple zeroes, strip the trailing values
|
|
@@ -50,18 +47,32 @@ function NumberField(props) {
|
|
|
50
47
|
? asNumber(normalizedValue.replace(trailingCharMatcher, ''))
|
|
51
48
|
: asNumber(normalizedValue);
|
|
52
49
|
onChange(processed, path, errorSchema, id);
|
|
53
|
-
}, [onChange]);
|
|
50
|
+
}, [onChange, separator]);
|
|
54
51
|
if (typeof lastValue === 'string' && typeof value === 'number') {
|
|
55
52
|
// Construct a regular expression that checks for a string that consists
|
|
56
|
-
// of the formData value suffixed with zero or one
|
|
53
|
+
// of the formData value suffixed with zero or one locale separator characters and zero
|
|
57
54
|
// or more '0' characters
|
|
58
|
-
const re = new RegExp(`^(${String(value).replace('.',
|
|
55
|
+
const re = new RegExp(`^(${String(value).replace('.', escapedSeparator)})?${escapedSeparator}?0*$`);
|
|
59
56
|
// If the cached "lastValue" is a match, use that instead of the formData
|
|
60
57
|
// value to prevent the input value from changing in the UI
|
|
61
58
|
if (lastValue.match(re)) {
|
|
62
59
|
value = lastValue;
|
|
63
60
|
}
|
|
64
61
|
}
|
|
65
|
-
|
|
62
|
+
// Format value to use the locale separator for rendering if it is a number
|
|
63
|
+
let displayValue = value;
|
|
64
|
+
if (typeof value === 'number' && separator !== '.') {
|
|
65
|
+
const { schema, uiSchema } = props;
|
|
66
|
+
const { schemaUtils } = registry;
|
|
67
|
+
const enumOptions = schemaUtils.isSelect(schema) ? optionsList(schema, uiSchema) : undefined;
|
|
68
|
+
const defaultWidget = enumOptions ? 'select' : 'text';
|
|
69
|
+
const { widget = defaultWidget } = getUiOptions(uiSchema);
|
|
70
|
+
// Do not convert the value to a locale-specific string for radio, select,
|
|
71
|
+
// or hidden widgets because option matching relies on the original numeric value.
|
|
72
|
+
if (widget !== 'radio' && widget !== 'select' && widget !== 'hidden') {
|
|
73
|
+
displayValue = String(value).replace('.', separator);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return _jsx(StringField, { ...props, formData: displayValue, onChange: handleChange });
|
|
66
77
|
}
|
|
67
78
|
export default NumberField;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ObjectField.d.ts","sourceRoot":"","sources":["../../../src/components/fields/ObjectField.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAIV,UAAU,EACV,eAAe,EAIf,UAAU,EACV,gBAAgB,EACjB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"ObjectField.d.ts","sourceRoot":"","sources":["../../../src/components/fields/ObjectField.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAIV,UAAU,EACV,eAAe,EAIf,UAAU,EACV,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAuMrB;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,SAAS,gBAAgB,GAAG,UAAU,EAAE,CAAC,SAAS,eAAe,GAAG,GAAG,EACnH,KAAK,EAAE,UAAU,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,+BAyP3B"}
|
|
@@ -39,6 +39,12 @@ function getDefaultValue(translateString, type) {
|
|
|
39
39
|
return translateString(TranslatableString.NewStringDefault);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
+
function isAdditionalPropertySchema(schema) {
|
|
43
|
+
return Boolean(schema?.[ADDITIONAL_PROPERTY_FLAG]);
|
|
44
|
+
}
|
|
45
|
+
function getAdditionalPropertyOrder(schemaProperties) {
|
|
46
|
+
return Object.keys(schemaProperties).filter((property) => isAdditionalPropertySchema(schemaProperties[property]));
|
|
47
|
+
}
|
|
42
48
|
/** The `ObjectFieldProperty` component is used to render the `SchemaField` for a child property of an object
|
|
43
49
|
*/
|
|
44
50
|
function ObjectFieldPropertyFn(props) {
|
|
@@ -103,10 +109,15 @@ export default function ObjectField(props) {
|
|
|
103
109
|
formDataRef.current = formData;
|
|
104
110
|
const schema = useMemo(() => schemaUtils.retrieveSchema(rawSchema, formData, true), [schemaUtils, rawSchema, formData]);
|
|
105
111
|
const uiOptions = useMemo(() => getUiOptions(uiSchema, globalUiOptions), [uiSchema, globalUiOptions]);
|
|
106
|
-
const
|
|
112
|
+
const schemaProperties = useMemo(() => schema.properties ?? {}, [schema.properties]);
|
|
107
113
|
// All the children will use childFieldPathId if present in the props, falling back to the fieldPathId
|
|
108
114
|
const childFieldPathId = props.childFieldPathId ?? fieldPathId;
|
|
109
115
|
const lastRenamedProperty = useRef({ previousKey: '', currentKey: undefined });
|
|
116
|
+
const [additionalPropertyOrder, setAdditionalPropertyOrder] = useState(() => getAdditionalPropertyOrder(schemaProperties));
|
|
117
|
+
const definedPropertyOrder = useMemo(() => {
|
|
118
|
+
const additionalPropertySet = new Set(getAdditionalPropertyOrder(schemaProperties));
|
|
119
|
+
return Object.keys(schemaProperties).filter((property) => !additionalPropertySet.has(property));
|
|
120
|
+
}, [schemaProperties]);
|
|
110
121
|
const templateTitle = uiOptions.title ?? schema.title ?? title ?? name;
|
|
111
122
|
const description = uiOptions.description ?? schema.description;
|
|
112
123
|
const renderOptionalField = shouldRenderOptionalField(registry, schema, required, uiSchema);
|
|
@@ -169,6 +180,7 @@ export default function ObjectField(props) {
|
|
|
169
180
|
lastRenamedProperty.current.currentKey = newKey;
|
|
170
181
|
lastRenamedProperty.current.previousKey = getAvailableKey(newKey, newFormData);
|
|
171
182
|
}
|
|
183
|
+
setAdditionalPropertyOrder((order) => [...order, newKey]);
|
|
172
184
|
onChange(newFormData, childFieldPathId.path);
|
|
173
185
|
}, [formData, onChange, translateString, schemaUtils, childFieldPathId, getAvailableKey, schema]);
|
|
174
186
|
/** Returns a callback function that deals with the rename of a key for an additional property for a schema. That
|
|
@@ -197,6 +209,7 @@ export default function ObjectField(props) {
|
|
|
197
209
|
lastRenamedProperty.current.previousKey = oldKey;
|
|
198
210
|
}
|
|
199
211
|
lastRenamedProperty.current.currentKey = actualNewKey;
|
|
212
|
+
setAdditionalPropertyOrder((order) => order.map((property) => (property === oldKey ? actualNewKey : property)));
|
|
200
213
|
onChange(renamedObj, childFieldPathId.path);
|
|
201
214
|
}
|
|
202
215
|
}, [onChange, childFieldPathId, getAvailableKey]);
|
|
@@ -204,6 +217,7 @@ export default function ObjectField(props) {
|
|
|
204
217
|
* value for the path plus the key to be removed
|
|
205
218
|
*/
|
|
206
219
|
const handleRemoveProperty = useCallback((key) => {
|
|
220
|
+
setAdditionalPropertyOrder((order) => order.filter((property) => property !== key));
|
|
207
221
|
onChange(ADDITIONAL_PROPERTY_KEY_REMOVE, [...childFieldPathId.path, key]);
|
|
208
222
|
}, [onChange, childFieldPathId]);
|
|
209
223
|
/** Returns the stable React key for a property. For the most recently renamed
|
|
@@ -219,8 +233,9 @@ export default function ObjectField(props) {
|
|
|
219
233
|
}, []);
|
|
220
234
|
if (!renderOptionalField || hasFormData) {
|
|
221
235
|
try {
|
|
222
|
-
const
|
|
223
|
-
|
|
236
|
+
const definedPropertySet = new Set(definedPropertyOrder);
|
|
237
|
+
const currentAdditionalProperties = additionalPropertyOrder.filter((property) => Object.hasOwn(schemaProperties, property) && !definedPropertySet.has(property));
|
|
238
|
+
orderedProperties = orderProperties([...definedPropertyOrder, ...currentAdditionalProperties], uiOptions.order);
|
|
224
239
|
}
|
|
225
240
|
catch (err) {
|
|
226
241
|
return (_jsxs("div", { children: [_jsx("p", { className: 'rjsf-config-error', style: { color: 'red' }, children: _jsx(Markdown, { options: { disableParsingRawHTML: true }, children: translateString(TranslatableString.InvalidObjectField, [name || 'root', err.message]) }) }), _jsx("pre", { children: JSON.stringify(schema) })] }));
|
|
@@ -233,7 +248,7 @@ export default function ObjectField(props) {
|
|
|
233
248
|
title: uiOptions.label === false ? '' : templateTitle,
|
|
234
249
|
description: uiOptions.label === false ? undefined : description,
|
|
235
250
|
properties: orderedProperties.map((propertyName) => {
|
|
236
|
-
const addedByAdditionalProperties =
|
|
251
|
+
const addedByAdditionalProperties = isAdditionalPropertySchema(schema.properties?.[propertyName]);
|
|
237
252
|
const fieldUiSchema = addedByAdditionalProperties ? uiSchema.additionalProperties : uiSchema[propertyName];
|
|
238
253
|
const hidden = getUiOptions(fieldUiSchema).widget === 'hidden';
|
|
239
254
|
const content = (_jsx(ObjectFieldProperty, { propertyName: propertyName, required: isRequired(schema, propertyName), schema: get(schema, [PROPERTIES_KEY, propertyName], {}), uiSchema: fieldUiSchema, errorSchema: get(errorSchema, [propertyName]), fieldPathId: childFieldPathId, formData: get(formData, [propertyName]), handleKeyRename: handleKeyRename, handleRemoveProperty: handleRemoveProperty, addedByAdditionalProperties: addedByAdditionalProperties, onChange: onChange, onBlur: onBlur, onFocus: onFocus, registry: registry, disabled: disabled, readonly: readonly, hideError: hideError }, getStableKey(propertyName)));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeWidget.d.ts","sourceRoot":"","sources":["../../../src/components/widgets/TimeWidget.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG9F;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,SAAS,gBAAgB,GAAG,UAAU,EAAE,CAAC,SAAS,eAAe,GAAG,GAAG,EAClH,KAAK,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,+
|
|
1
|
+
{"version":3,"file":"TimeWidget.d.ts","sourceRoot":"","sources":["../../../src/components/widgets/TimeWidget.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG9F;;;;GAIG;AACH,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,SAAS,gBAAgB,GAAG,UAAU,EAAE,CAAC,SAAS,eAAe,GAAG,GAAG,EAClH,KAAK,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,+BAsB5B"}
|
|
@@ -7,8 +7,20 @@ import { getTemplate } from '@rjsf/utils';
|
|
|
7
7
|
* @param props - The `WidgetProps` for this component
|
|
8
8
|
*/
|
|
9
9
|
export default function TimeWidget(props) {
|
|
10
|
-
const { onChange, options, registry } = props;
|
|
10
|
+
const { onChange, options, registry, schema, value } = props;
|
|
11
11
|
const BaseInputTemplate = getTemplate('BaseInputTemplate', registry, options);
|
|
12
|
-
const
|
|
13
|
-
|
|
12
|
+
const hasSecondPrecision = typeof schema.multipleOf === 'number' && Number.isFinite(schema.multipleOf) && schema.multipleOf < 60;
|
|
13
|
+
const handleChange = useCallback((newValue) => {
|
|
14
|
+
if (!newValue) {
|
|
15
|
+
onChange(undefined);
|
|
16
|
+
}
|
|
17
|
+
else if (hasSecondPrecision) {
|
|
18
|
+
onChange(newValue);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
onChange(`${newValue}:00`);
|
|
22
|
+
}
|
|
23
|
+
}, [hasSecondPrecision, onChange]);
|
|
24
|
+
const displayValue = typeof value === 'string' && !hasSecondPrecision && /^\d{2}:\d{2}:00$/.test(value) ? value.slice(0, -3) : value;
|
|
25
|
+
return _jsx(BaseInputTemplate, { type: 'time', ...props, value: displayValue, onChange: handleChange });
|
|
14
26
|
}
|