@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 CHANGED
@@ -1740,23 +1740,37 @@
1740
1740
  const { registry, onChange, formData, value: initialValue } = props;
1741
1741
  const [lastValue, setLastValue] = react.useState(initialValue);
1742
1742
  const { StringField: StringField2 } = registry.fields;
1743
+ const separator = utils.getDecimalSeparator();
1744
+ const escapedSeparator = separator === "." ? "\\." : separator;
1743
1745
  let value = formData;
1744
1746
  const handleChange = react.useCallback(
1745
1747
  (newValue, path, errorSchema, id) => {
1746
1748
  setLastValue(newValue);
1747
- const normalizedValue = `${newValue}`.startsWith(".") ? `0${newValue}` : newValue;
1749
+ const standardValue = typeof newValue === "string" ? newValue.replace(separator, ".") : newValue;
1750
+ const normalizedValue = `${standardValue}`.startsWith(".") ? `0${standardValue}` : standardValue;
1748
1751
  const processed = typeof normalizedValue === "string" && trailingCharMatcherWithPrefix.exec(normalizedValue) ? utils.asNumber(normalizedValue.replace(trailingCharMatcher, "")) : utils.asNumber(normalizedValue);
1749
1752
  onChange(processed, path, errorSchema, id);
1750
1753
  },
1751
- [onChange]
1754
+ [onChange, separator]
1752
1755
  );
1753
1756
  if (typeof lastValue === "string" && typeof value === "number") {
1754
- const re = new RegExp(`^(${String(value).replace(".", "\\.")})?\\.?0*$`);
1757
+ const re = new RegExp(`^(${String(value).replace(".", escapedSeparator)})?${escapedSeparator}?0*$`);
1755
1758
  if (lastValue.match(re)) {
1756
1759
  value = lastValue;
1757
1760
  }
1758
1761
  }
1759
- return /* @__PURE__ */ jsxRuntime.jsx(StringField2, { ...props, formData: value, onChange: handleChange });
1762
+ let displayValue = value;
1763
+ if (typeof value === "number" && separator !== ".") {
1764
+ const { schema, uiSchema } = props;
1765
+ const { schemaUtils } = registry;
1766
+ const enumOptions = schemaUtils.isSelect(schema) ? utils.optionsList(schema, uiSchema) : void 0;
1767
+ const defaultWidget = enumOptions ? "select" : "text";
1768
+ const { widget = defaultWidget } = utils.getUiOptions(uiSchema);
1769
+ if (widget !== "radio" && widget !== "select" && widget !== "hidden") {
1770
+ displayValue = String(value).replace(".", separator);
1771
+ }
1772
+ }
1773
+ return /* @__PURE__ */ jsxRuntime.jsx(StringField2, { ...props, formData: displayValue, onChange: handleChange });
1760
1774
  }
1761
1775
  var NumberField_default = NumberField;
1762
1776
 
@@ -1783,6 +1797,12 @@
1783
1797
  return translateString(utils.TranslatableString.NewStringDefault);
1784
1798
  }
1785
1799
  }
1800
+ function isAdditionalPropertySchema(schema) {
1801
+ return Boolean(schema?.[utils.ADDITIONAL_PROPERTY_FLAG]);
1802
+ }
1803
+ function getAdditionalPropertyOrder(schemaProperties) {
1804
+ return Object.keys(schemaProperties).filter((property) => isAdditionalPropertySchema(schemaProperties[property]));
1805
+ }
1786
1806
  function ObjectFieldPropertyFn(props) {
1787
1807
  const {
1788
1808
  fieldPathId,
@@ -1892,9 +1912,16 @@
1892
1912
  [schemaUtils, rawSchema, formData]
1893
1913
  );
1894
1914
  const uiOptions = react.useMemo(() => utils.getUiOptions(uiSchema, globalUiOptions), [uiSchema, globalUiOptions]);
1895
- const { properties: schemaProperties = {} } = schema;
1915
+ const schemaProperties = react.useMemo(() => schema.properties ?? {}, [schema.properties]);
1896
1916
  const childFieldPathId = props.childFieldPathId ?? fieldPathId;
1897
1917
  const lastRenamedProperty = react.useRef({ previousKey: "", currentKey: void 0 });
1918
+ const [additionalPropertyOrder, setAdditionalPropertyOrder] = react.useState(
1919
+ () => getAdditionalPropertyOrder(schemaProperties)
1920
+ );
1921
+ const definedPropertyOrder = react.useMemo(() => {
1922
+ const additionalPropertySet = new Set(getAdditionalPropertyOrder(schemaProperties));
1923
+ return Object.keys(schemaProperties).filter((property) => !additionalPropertySet.has(property));
1924
+ }, [schemaProperties]);
1898
1925
  const templateTitle = uiOptions.title ?? schema.title ?? title ?? name;
1899
1926
  const description = uiOptions.description ?? schema.description;
1900
1927
  const renderOptionalField = utils.shouldRenderOptionalField(registry, schema, required, uiSchema);
@@ -1947,6 +1974,7 @@
1947
1974
  lastRenamedProperty.current.currentKey = newKey;
1948
1975
  lastRenamedProperty.current.previousKey = getAvailableKey(newKey, newFormData);
1949
1976
  }
1977
+ setAdditionalPropertyOrder((order) => [...order, newKey]);
1950
1978
  onChange(newFormData, childFieldPathId.path);
1951
1979
  }, [formData, onChange, translateString, schemaUtils, childFieldPathId, getAvailableKey, schema]);
1952
1980
  const handleKeyRename = react.useCallback(
@@ -1968,6 +1996,7 @@
1968
1996
  lastRenamedProperty.current.previousKey = oldKey;
1969
1997
  }
1970
1998
  lastRenamedProperty.current.currentKey = actualNewKey;
1999
+ setAdditionalPropertyOrder((order) => order.map((property) => property === oldKey ? actualNewKey : property));
1971
2000
  onChange(renamedObj, childFieldPathId.path);
1972
2001
  }
1973
2002
  },
@@ -1975,6 +2004,7 @@
1975
2004
  );
1976
2005
  const handleRemoveProperty = react.useCallback(
1977
2006
  (key) => {
2007
+ setAdditionalPropertyOrder((order) => order.filter((property) => property !== key));
1978
2008
  onChange(ADDITIONAL_PROPERTY_KEY_REMOVE, [...childFieldPathId.path, key]);
1979
2009
  },
1980
2010
  [onChange, childFieldPathId]
@@ -1987,8 +2017,11 @@
1987
2017
  }, []);
1988
2018
  if (!renderOptionalField || hasFormData) {
1989
2019
  try {
1990
- const properties = Object.keys(schemaProperties);
1991
- orderedProperties = utils.orderProperties(properties, uiOptions.order);
2020
+ const definedPropertySet = new Set(definedPropertyOrder);
2021
+ const currentAdditionalProperties = additionalPropertyOrder.filter(
2022
+ (property) => Object.hasOwn(schemaProperties, property) && !definedPropertySet.has(property)
2023
+ );
2024
+ orderedProperties = utils.orderProperties([...definedPropertyOrder, ...currentAdditionalProperties], uiOptions.order);
1992
2025
  } catch (err) {
1993
2026
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
1994
2027
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "rjsf-config-error", style: { color: "red" }, children: /* @__PURE__ */ jsxRuntime.jsx(react$1.Markdown, { options: { disableParsingRawHTML: true }, children: translateString(utils.TranslatableString.InvalidObjectField, [name || "root", err.message]) }) }),
@@ -2003,9 +2036,7 @@
2003
2036
  title: uiOptions.label === false ? "" : templateTitle,
2004
2037
  description: uiOptions.label === false ? void 0 : description,
2005
2038
  properties: orderedProperties.map((propertyName) => {
2006
- const addedByAdditionalProperties = Boolean(
2007
- schema.properties?.[propertyName]?.[utils.ADDITIONAL_PROPERTY_FLAG]
2008
- );
2039
+ const addedByAdditionalProperties = isAdditionalPropertySchema(schema.properties?.[propertyName]);
2009
2040
  const fieldUiSchema = addedByAdditionalProperties ? uiSchema.additionalProperties : uiSchema[propertyName];
2010
2041
  const hidden = utils.getUiOptions(fieldUiSchema).widget === "hidden";
2011
2042
  const content = /* @__PURE__ */ jsxRuntime.jsx(
@@ -3807,10 +3838,23 @@
3807
3838
  return /* @__PURE__ */ jsxRuntime.jsx(BaseInputTemplate2, { ...props });
3808
3839
  }
3809
3840
  function TimeWidget(props) {
3810
- const { onChange, options, registry } = props;
3841
+ const { onChange, options, registry, schema, value } = props;
3811
3842
  const BaseInputTemplate2 = utils.getTemplate("BaseInputTemplate", registry, options);
3812
- const handleChange = react.useCallback((value) => onChange(value ? `${value}:00` : void 0), [onChange]);
3813
- return /* @__PURE__ */ jsxRuntime.jsx(BaseInputTemplate2, { type: "time", ...props, onChange: handleChange });
3843
+ const hasSecondPrecision = typeof schema.multipleOf === "number" && Number.isFinite(schema.multipleOf) && schema.multipleOf < 60;
3844
+ const handleChange = react.useCallback(
3845
+ (newValue) => {
3846
+ if (!newValue) {
3847
+ onChange(void 0);
3848
+ } else if (hasSecondPrecision) {
3849
+ onChange(newValue);
3850
+ } else {
3851
+ onChange(`${newValue}:00`);
3852
+ }
3853
+ },
3854
+ [hasSecondPrecision, onChange]
3855
+ );
3856
+ const displayValue = typeof value === "string" && !hasSecondPrecision && /^\d{2}:\d{2}:00$/.test(value) ? value.slice(0, -3) : value;
3857
+ return /* @__PURE__ */ jsxRuntime.jsx(BaseInputTemplate2, { type: "time", ...props, value: displayValue, onChange: handleChange });
3814
3858
  }
3815
3859
  function UpDownWidget(props) {
3816
3860
  const { options, registry } = props;
package/dist/index.cjs CHANGED
@@ -1865,23 +1865,37 @@ function NumberField(props) {
1865
1865
  const { registry, onChange, formData, value: initialValue } = props;
1866
1866
  const [lastValue, setLastValue] = (0, import_react9.useState)(initialValue);
1867
1867
  const { StringField: StringField2 } = registry.fields;
1868
+ const separator = (0, import_utils9.getDecimalSeparator)();
1869
+ const escapedSeparator = separator === "." ? "\\." : separator;
1868
1870
  let value = formData;
1869
1871
  const handleChange = (0, import_react9.useCallback)(
1870
1872
  (newValue, path, errorSchema, id) => {
1871
1873
  setLastValue(newValue);
1872
- const normalizedValue = `${newValue}`.startsWith(".") ? `0${newValue}` : newValue;
1874
+ const standardValue = typeof newValue === "string" ? newValue.replace(separator, ".") : newValue;
1875
+ const normalizedValue = `${standardValue}`.startsWith(".") ? `0${standardValue}` : standardValue;
1873
1876
  const processed = typeof normalizedValue === "string" && trailingCharMatcherWithPrefix.exec(normalizedValue) ? (0, import_utils9.asNumber)(normalizedValue.replace(trailingCharMatcher, "")) : (0, import_utils9.asNumber)(normalizedValue);
1874
1877
  onChange(processed, path, errorSchema, id);
1875
1878
  },
1876
- [onChange]
1879
+ [onChange, separator]
1877
1880
  );
1878
1881
  if (typeof lastValue === "string" && typeof value === "number") {
1879
- const re = new RegExp(`^(${String(value).replace(".", "\\.")})?\\.?0*$`);
1882
+ const re = new RegExp(`^(${String(value).replace(".", escapedSeparator)})?${escapedSeparator}?0*$`);
1880
1883
  if (lastValue.match(re)) {
1881
1884
  value = lastValue;
1882
1885
  }
1883
1886
  }
1884
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StringField2, { ...props, formData: value, onChange: handleChange });
1887
+ let displayValue = value;
1888
+ if (typeof value === "number" && separator !== ".") {
1889
+ const { schema, uiSchema } = props;
1890
+ const { schemaUtils } = registry;
1891
+ const enumOptions = schemaUtils.isSelect(schema) ? (0, import_utils9.optionsList)(schema, uiSchema) : void 0;
1892
+ const defaultWidget = enumOptions ? "select" : "text";
1893
+ const { widget = defaultWidget } = (0, import_utils9.getUiOptions)(uiSchema);
1894
+ if (widget !== "radio" && widget !== "select" && widget !== "hidden") {
1895
+ displayValue = String(value).replace(".", separator);
1896
+ }
1897
+ }
1898
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(StringField2, { ...props, formData: displayValue, onChange: handleChange });
1885
1899
  }
1886
1900
  var NumberField_default = NumberField;
1887
1901
 
@@ -1920,6 +1934,12 @@ function getDefaultValue(translateString, type) {
1920
1934
  return translateString(import_utils10.TranslatableString.NewStringDefault);
1921
1935
  }
1922
1936
  }
1937
+ function isAdditionalPropertySchema(schema) {
1938
+ return Boolean(schema?.[import_utils10.ADDITIONAL_PROPERTY_FLAG]);
1939
+ }
1940
+ function getAdditionalPropertyOrder(schemaProperties) {
1941
+ return Object.keys(schemaProperties).filter((property) => isAdditionalPropertySchema(schemaProperties[property]));
1942
+ }
1923
1943
  function ObjectFieldPropertyFn(props) {
1924
1944
  const {
1925
1945
  fieldPathId,
@@ -2029,9 +2049,16 @@ function ObjectField(props) {
2029
2049
  [schemaUtils, rawSchema, formData]
2030
2050
  );
2031
2051
  const uiOptions = (0, import_react10.useMemo)(() => (0, import_utils10.getUiOptions)(uiSchema, globalUiOptions), [uiSchema, globalUiOptions]);
2032
- const { properties: schemaProperties = {} } = schema;
2052
+ const schemaProperties = (0, import_react10.useMemo)(() => schema.properties ?? {}, [schema.properties]);
2033
2053
  const childFieldPathId = props.childFieldPathId ?? fieldPathId;
2034
2054
  const lastRenamedProperty = (0, import_react10.useRef)({ previousKey: "", currentKey: void 0 });
2055
+ const [additionalPropertyOrder, setAdditionalPropertyOrder] = (0, import_react10.useState)(
2056
+ () => getAdditionalPropertyOrder(schemaProperties)
2057
+ );
2058
+ const definedPropertyOrder = (0, import_react10.useMemo)(() => {
2059
+ const additionalPropertySet = new Set(getAdditionalPropertyOrder(schemaProperties));
2060
+ return Object.keys(schemaProperties).filter((property) => !additionalPropertySet.has(property));
2061
+ }, [schemaProperties]);
2035
2062
  const templateTitle = uiOptions.title ?? schema.title ?? title ?? name;
2036
2063
  const description = uiOptions.description ?? schema.description;
2037
2064
  const renderOptionalField = (0, import_utils10.shouldRenderOptionalField)(registry, schema, required, uiSchema);
@@ -2084,6 +2111,7 @@ function ObjectField(props) {
2084
2111
  lastRenamedProperty.current.currentKey = newKey;
2085
2112
  lastRenamedProperty.current.previousKey = getAvailableKey(newKey, newFormData);
2086
2113
  }
2114
+ setAdditionalPropertyOrder((order) => [...order, newKey]);
2087
2115
  onChange(newFormData, childFieldPathId.path);
2088
2116
  }, [formData, onChange, translateString, schemaUtils, childFieldPathId, getAvailableKey, schema]);
2089
2117
  const handleKeyRename = (0, import_react10.useCallback)(
@@ -2105,6 +2133,7 @@ function ObjectField(props) {
2105
2133
  lastRenamedProperty.current.previousKey = oldKey;
2106
2134
  }
2107
2135
  lastRenamedProperty.current.currentKey = actualNewKey;
2136
+ setAdditionalPropertyOrder((order) => order.map((property) => property === oldKey ? actualNewKey : property));
2108
2137
  onChange(renamedObj, childFieldPathId.path);
2109
2138
  }
2110
2139
  },
@@ -2112,6 +2141,7 @@ function ObjectField(props) {
2112
2141
  );
2113
2142
  const handleRemoveProperty = (0, import_react10.useCallback)(
2114
2143
  (key) => {
2144
+ setAdditionalPropertyOrder((order) => order.filter((property) => property !== key));
2115
2145
  onChange(ADDITIONAL_PROPERTY_KEY_REMOVE, [...childFieldPathId.path, key]);
2116
2146
  },
2117
2147
  [onChange, childFieldPathId]
@@ -2124,8 +2154,11 @@ function ObjectField(props) {
2124
2154
  }, []);
2125
2155
  if (!renderOptionalField || hasFormData) {
2126
2156
  try {
2127
- const properties = Object.keys(schemaProperties);
2128
- orderedProperties = (0, import_utils10.orderProperties)(properties, uiOptions.order);
2157
+ const definedPropertySet = new Set(definedPropertyOrder);
2158
+ const currentAdditionalProperties = additionalPropertyOrder.filter(
2159
+ (property) => Object.hasOwn(schemaProperties, property) && !definedPropertySet.has(property)
2160
+ );
2161
+ orderedProperties = (0, import_utils10.orderProperties)([...definedPropertyOrder, ...currentAdditionalProperties], uiOptions.order);
2129
2162
  } catch (err) {
2130
2163
  return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
2131
2164
  /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "rjsf-config-error", style: { color: "red" }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react11.Markdown, { options: { disableParsingRawHTML: true }, children: translateString(import_utils10.TranslatableString.InvalidObjectField, [name || "root", err.message]) }) }),
@@ -2140,9 +2173,7 @@ function ObjectField(props) {
2140
2173
  title: uiOptions.label === false ? "" : templateTitle,
2141
2174
  description: uiOptions.label === false ? void 0 : description,
2142
2175
  properties: orderedProperties.map((propertyName) => {
2143
- const addedByAdditionalProperties = Boolean(
2144
- schema.properties?.[propertyName]?.[import_utils10.ADDITIONAL_PROPERTY_FLAG]
2145
- );
2176
+ const addedByAdditionalProperties = isAdditionalPropertySchema(schema.properties?.[propertyName]);
2146
2177
  const fieldUiSchema = addedByAdditionalProperties ? uiSchema.additionalProperties : uiSchema[propertyName];
2147
2178
  const hidden = (0, import_utils10.getUiOptions)(fieldUiSchema).widget === "hidden";
2148
2179
  const content = /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
@@ -4152,10 +4183,23 @@ var import_react27 = require("react");
4152
4183
  var import_utils48 = require("@rjsf/utils");
4153
4184
  var import_jsx_runtime58 = require("react/jsx-runtime");
4154
4185
  function TimeWidget(props) {
4155
- const { onChange, options, registry } = props;
4186
+ const { onChange, options, registry, schema, value } = props;
4156
4187
  const BaseInputTemplate2 = (0, import_utils48.getTemplate)("BaseInputTemplate", registry, options);
4157
- const handleChange = (0, import_react27.useCallback)((value) => onChange(value ? `${value}:00` : void 0), [onChange]);
4158
- return /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(BaseInputTemplate2, { type: "time", ...props, onChange: handleChange });
4188
+ const hasSecondPrecision = typeof schema.multipleOf === "number" && Number.isFinite(schema.multipleOf) && schema.multipleOf < 60;
4189
+ const handleChange = (0, import_react27.useCallback)(
4190
+ (newValue) => {
4191
+ if (!newValue) {
4192
+ onChange(void 0);
4193
+ } else if (hasSecondPrecision) {
4194
+ onChange(newValue);
4195
+ } else {
4196
+ onChange(`${newValue}:00`);
4197
+ }
4198
+ },
4199
+ [hasSecondPrecision, onChange]
4200
+ );
4201
+ const displayValue = typeof value === "string" && !hasSecondPrecision && /^\d{2}:\d{2}:00$/.test(value) ? value.slice(0, -3) : value;
4202
+ return /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(BaseInputTemplate2, { type: "time", ...props, value: displayValue, onChange: handleChange });
4159
4203
  }
4160
4204
 
4161
4205
  // src/components/widgets/UpDownWidget.tsx