@bpmn-io/form-js-viewer 1.6.1 → 1.6.3

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/index.cjs CHANGED
@@ -7,6 +7,7 @@ var classNames = require('classnames');
7
7
  var jsxRuntime = require('preact/jsx-runtime');
8
8
  var hooks = require('preact/hooks');
9
9
  var preact = require('preact');
10
+ var isEqual = require('lodash/isEqual');
10
11
  var flatpickr = require('flatpickr');
11
12
  var React = require('preact/compat');
12
13
  var Markup = require('preact-markup');
@@ -1104,39 +1105,58 @@ function getOptionsData(formField, formData) {
1104
1105
 
1105
1106
  // transforms the provided options into a normalized format, trimming invalid options
1106
1107
  function normalizeOptionsData(optionsData) {
1107
- return optionsData.filter(_isOptionSomething).map(v => _normalizeOptionsData(v)).filter(v => v);
1108
+ return optionsData.filter(_isAllowedValue).map(_normalizeOption).filter(o => !minDash.isNil(o));
1108
1109
  }
1109
- function _normalizeOptionsData(optionData) {
1110
- if (_isAllowedOption(optionData)) {
1111
- // if a primitive is provided, use it as label and value
1110
+
1111
+ /**
1112
+ * Converts the provided option to a normalized format.
1113
+ * If the option is not valid, null is returned.
1114
+ *
1115
+ * @param {object} option
1116
+ * @param {string} option.label
1117
+ * @param {*} option.value
1118
+ *
1119
+ * @returns
1120
+ */
1121
+ function _normalizeOption(option) {
1122
+ // (1) simple primitive case, use it as both label and value
1123
+ if (_isAllowedPrimitive(option)) {
1112
1124
  return {
1113
- value: optionData,
1114
- label: `${optionData}`
1125
+ value: option,
1126
+ label: `${option}`
1115
1127
  };
1116
1128
  }
1117
- if (typeof optionData === 'object') {
1118
- if (!optionData.label && _isAllowedOption(optionData.value)) {
1119
- // if no label is provided, use the value as label
1129
+ if (minDash.isObject(option)) {
1130
+ const isValidLabel = _isValidLabel(option.label);
1131
+
1132
+ // (2) no label provided, but value is a simple primitive, use it as label and value
1133
+ if (!isValidLabel && _isAllowedPrimitive(option.value)) {
1120
1134
  return {
1121
- value: optionData.value,
1122
- label: `${optionData.value}`
1135
+ value: option.value,
1136
+ label: `${option.value}`
1123
1137
  };
1124
1138
  }
1125
- if (_isOptionSomething(optionData.value) && _isAllowedOption(optionData.label)) {
1126
- // if both value and label are provided, use them as is, in this scenario, the value may also be an object
1127
- return optionData;
1139
+
1140
+ // (3) both label and value are provided, use them as is
1141
+ if (isValidLabel && _isAllowedValue(option.value)) {
1142
+ return option;
1128
1143
  }
1129
1144
  }
1130
1145
  return null;
1131
1146
  }
1132
- function _isAllowedOption(option) {
1133
- return _isReadableType(option) && _isOptionSomething(option);
1147
+ function _isAllowedPrimitive(value) {
1148
+ const isAllowedPrimitiveType = ['number', 'string', 'boolean'].includes(typeof value);
1149
+ const isValid = value || value === 0 || value === false;
1150
+ return isAllowedPrimitiveType && isValid;
1134
1151
  }
1135
- function _isReadableType(option) {
1136
- return ['number', 'string', 'boolean'].includes(typeof option);
1152
+ function _isValidLabel(label) {
1153
+ return label && minDash.isString(label);
1137
1154
  }
1138
- function _isOptionSomething(option) {
1139
- return option || option === 0 || option === false;
1155
+ function _isAllowedValue(value) {
1156
+ if (minDash.isObject(value)) {
1157
+ return Object.keys(value).length > 0;
1158
+ }
1159
+ return _isAllowedPrimitive(value);
1140
1160
  }
1141
1161
  function createEmptyOptions(options = {}) {
1142
1162
  const defaults = {};
@@ -1230,30 +1250,6 @@ const buildLoadedState = options => ({
1230
1250
  loadState: LOAD_STATES.LOADED
1231
1251
  });
1232
1252
 
1233
- function useCleanupMultiSelectValues (props) {
1234
- const {
1235
- field,
1236
- options,
1237
- loadState,
1238
- onChange,
1239
- values
1240
- } = props;
1241
-
1242
- // Ensures that the values are always a subset of the possible options
1243
- hooks.useEffect(() => {
1244
- if (loadState !== LOAD_STATES.LOADED) {
1245
- return;
1246
- }
1247
- const hasValuesNotInOptions = values.some(v => !options.map(o => o.value).includes(v));
1248
- if (hasValuesNotInOptions) {
1249
- onChange({
1250
- field,
1251
- value: values.filter(v => options.map(o => o.value).includes(v))
1252
- });
1253
- }
1254
- }, [field, options, onChange, JSON.stringify(values), loadState]);
1255
- }
1256
-
1257
1253
  const ENTER_KEYDOWN_EVENT = new KeyboardEvent('keydown', {
1258
1254
  code: 'Enter',
1259
1255
  key: 'Enter',
@@ -1429,6 +1425,12 @@ function sanitizeDateTimePickerValue(options) {
1429
1425
  if (subtype === DATETIME_SUBTYPES.DATETIME && (isInvalidDateString(value) || !isDateTimeInputInformationSufficient(value))) return null;
1430
1426
  return value;
1431
1427
  }
1428
+ function hasEqualValue(value, array) {
1429
+ if (!Array.isArray(array)) {
1430
+ return false;
1431
+ }
1432
+ return array.some(element => isEqual(value, element));
1433
+ }
1432
1434
  function sanitizeSingleSelectValue(options) {
1433
1435
  const {
1434
1436
  formField,
@@ -1437,7 +1439,7 @@ function sanitizeSingleSelectValue(options) {
1437
1439
  } = options;
1438
1440
  try {
1439
1441
  const validValues = normalizeOptionsData(getOptionsData(formField, data)).map(v => v.value);
1440
- return validValues.includes(value) ? value : null;
1442
+ return hasEqualValue(value, validValues) ? value : null;
1441
1443
  } catch (error) {
1442
1444
  // use default value in case of formatting error
1443
1445
  // TODO(@Skaiir): log a warning when this happens - https://github.com/bpmn-io/form-js/issues/289
@@ -1452,7 +1454,7 @@ function sanitizeMultiSelectValue(options) {
1452
1454
  } = options;
1453
1455
  try {
1454
1456
  const validValues = normalizeOptionsData(getOptionsData(formField, data)).map(v => v.value);
1455
- return value.filter(v => validValues.includes(v));
1457
+ return value.filter(v => hasEqualValue(v, validValues));
1456
1458
  } catch (error) {
1457
1459
  // use default value in case of formatting error
1458
1460
  // TODO(@Skaiir): log a warning when this happens - https://github.com/bpmn-io/form-js/issues/289
@@ -1460,6 +1462,31 @@ function sanitizeMultiSelectValue(options) {
1460
1462
  }
1461
1463
  }
1462
1464
 
1465
+ function useCleanupMultiSelectValues (props) {
1466
+ const {
1467
+ field,
1468
+ options,
1469
+ loadState,
1470
+ onChange,
1471
+ values
1472
+ } = props;
1473
+
1474
+ // Ensures that the values are always a subset of the possible options
1475
+ hooks.useEffect(() => {
1476
+ if (loadState !== LOAD_STATES.LOADED) {
1477
+ return;
1478
+ }
1479
+ const optionValues = options.map(o => o.value);
1480
+ const hasValuesNotInOptions = values.some(v => !hasEqualValue(v, optionValues));
1481
+ if (hasValuesNotInOptions) {
1482
+ onChange({
1483
+ field,
1484
+ value: values.filter(v => hasEqualValue(v, optionValues))
1485
+ });
1486
+ }
1487
+ }, [field, options, onChange, JSON.stringify(values), loadState]);
1488
+ }
1489
+
1463
1490
  const type$d = 'checklist';
1464
1491
  function Checklist(props) {
1465
1492
  const {
@@ -1482,16 +1509,11 @@ function Checklist(props) {
1482
1509
  const {
1483
1510
  required
1484
1511
  } = validate;
1485
- const toggleCheckbox = v => {
1486
- let newValue = [...values];
1487
- if (!newValue.includes(v)) {
1488
- newValue.push(v);
1489
- } else {
1490
- newValue = newValue.filter(x => x != v);
1491
- }
1512
+ const toggleCheckbox = toggledValue => {
1513
+ const newValues = hasEqualValue(toggledValue, values) ? values.filter(value => !isEqual(value, toggledValue)) : [...values, toggledValue];
1492
1514
  props.onChange({
1493
1515
  field,
1494
- value: newValue
1516
+ value: newValues
1495
1517
  });
1496
1518
  };
1497
1519
  const onCheckboxBlur = e => {
@@ -1529,15 +1551,16 @@ function Checklist(props) {
1529
1551
  required: required
1530
1552
  }), loadState == LOAD_STATES.LOADED && options.map((o, index) => {
1531
1553
  const itemDomId = `${domId}-${index}`;
1554
+ const isChecked = hasEqualValue(o.value, values);
1532
1555
  return jsxRuntime.jsx(Label, {
1533
1556
  id: itemDomId,
1534
1557
  label: o.label,
1535
1558
  class: classNames({
1536
- 'fjs-checked': values.includes(o.value)
1559
+ 'fjs-checked': isChecked
1537
1560
  }),
1538
1561
  required: false,
1539
1562
  children: jsxRuntime.jsx("input", {
1540
- checked: values.includes(o.value),
1563
+ checked: isChecked,
1541
1564
  class: "fjs-input",
1542
1565
  disabled: disabled,
1543
1566
  readOnly: readonly,
@@ -3299,8 +3322,8 @@ function Numberfield(props) {
3299
3322
  id: domId,
3300
3323
  onKeyDown: onKeyDown,
3301
3324
  onKeyPress: onKeyPress,
3302
- onBlur: () => onBlur && onBlur(),
3303
- onFocus: () => onFocus && onFocus()
3325
+ onBlur: onBlur,
3326
+ onFocus: onFocus
3304
3327
 
3305
3328
  // @ts-ignore
3306
3329
  ,
@@ -3377,7 +3400,8 @@ function useCleanupSingleSelectValue (props) {
3377
3400
  if (loadState !== LOAD_STATES.LOADED) {
3378
3401
  return;
3379
3402
  }
3380
- const hasValueNotInOptions = value && !options.map(o => o.value).includes(value);
3403
+ const optionValues = options.map(o => o.value);
3404
+ const hasValueNotInOptions = value && !hasEqualValue(value, optionValues);
3381
3405
  if (hasValueNotInOptions) {
3382
3406
  onChange({
3383
3407
  field,
@@ -3450,15 +3474,16 @@ function Radio(props) {
3450
3474
  required: required
3451
3475
  }), loadState == LOAD_STATES.LOADED && options.map((option, index) => {
3452
3476
  const itemDomId = `${domId}-${index}`;
3477
+ const isChecked = isEqual(option.value, value);
3453
3478
  return jsxRuntime.jsx(Label, {
3454
3479
  id: itemDomId,
3455
3480
  label: option.label,
3456
3481
  class: classNames({
3457
- 'fjs-checked': option.value === value
3482
+ 'fjs-checked': isChecked
3458
3483
  }),
3459
3484
  required: false,
3460
3485
  children: jsxRuntime.jsx("input", {
3461
- checked: option.value === value,
3486
+ checked: isChecked,
3462
3487
  class: "fjs-input",
3463
3488
  disabled: disabled,
3464
3489
  readOnly: readonly,
@@ -3488,6 +3513,21 @@ Radio.config = {
3488
3513
  create: createEmptyOptions
3489
3514
  };
3490
3515
 
3516
+ /**
3517
+ * This hook allows us to retrieve the label from a value in linear time by caching it in a map
3518
+ * @param {Array} options
3519
+ */
3520
+ function useGetLabelCorrelation(options) {
3521
+ // This allows us to retrieve the label from a value in linear time
3522
+ const labelMap = hooks.useMemo(() => Object.assign({}, ...options.map(o => ({
3523
+ [_getValueHash(o.value)]: o.label
3524
+ }))), [options]);
3525
+ return hooks.useCallback(value => labelMap[_getValueHash(value)], [labelMap]);
3526
+ }
3527
+ const _getValueHash = value => {
3528
+ return minDash.isObject(value) ? JSON.stringify(value) : value;
3529
+ };
3530
+
3491
3531
  var _path$q;
3492
3532
  function _extends$r() { _extends$r = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$r.apply(this, arguments); }
3493
3533
  var SvgXMark = function SvgXMark(props) {
@@ -3519,7 +3559,7 @@ function SearchableSelect(props) {
3519
3559
  } = props;
3520
3560
  const [filter, setFilter] = hooks.useState('');
3521
3561
  const [isDropdownExpanded, setIsDropdownExpanded] = hooks.useState(false);
3522
- const [shouldApplyFilter, setShouldApplyFilter] = hooks.useState(true);
3562
+ const [isFilterActive, setIsFilterActive] = hooks.useState(true);
3523
3563
  const [isEscapeClosed, setIsEscapeClose] = hooks.useState(false);
3524
3564
  const searchbarRef = hooks.useRef();
3525
3565
  const eventBus = useService('eventBus');
@@ -3534,23 +3574,22 @@ function SearchableSelect(props) {
3534
3574
  value,
3535
3575
  onChange: props.onChange
3536
3576
  });
3537
-
3538
- // We cache a map of option values to their index so that we don't need to search the whole options array every time to correlate the label
3539
- const valueToOptionMap = hooks.useMemo(() => Object.assign({}, ...options.map((o, x) => ({
3540
- [o.value]: options[x]
3541
- }))), [options]);
3542
- const valueLabel = hooks.useMemo(() => value && valueToOptionMap[value] && valueToOptionMap[value].label || '', [value, valueToOptionMap]);
3577
+ const getLabelCorrelation = useGetLabelCorrelation(options);
3578
+ const label = hooks.useMemo(() => value && getLabelCorrelation(value), [value, getLabelCorrelation]);
3543
3579
 
3544
3580
  // whenever we change the underlying value, set the label to it
3545
3581
  hooks.useEffect(() => {
3546
- setFilter(valueLabel);
3547
- }, [valueLabel]);
3582
+ setFilter(label);
3583
+ }, [label]);
3548
3584
  const filteredOptions = hooks.useMemo(() => {
3549
- if (loadState === LOAD_STATES.LOADED) {
3550
- return shouldApplyFilter ? options.filter(o => o.label && o.value && o.label.toLowerCase().includes(filter.toLowerCase())) : options;
3585
+ if (loadState !== LOAD_STATES.LOADED) {
3586
+ return [];
3551
3587
  }
3552
- return [];
3553
- }, [filter, loadState, options, shouldApplyFilter]);
3588
+ if (!filter || !isFilterActive) {
3589
+ return options;
3590
+ }
3591
+ return options.filter(o => o.label && o.value && o.label.toLowerCase().includes(filter.toLowerCase()));
3592
+ }, [filter, loadState, options, isFilterActive]);
3554
3593
  const setValue = hooks.useCallback(option => {
3555
3594
  setFilter(option && option.label || '');
3556
3595
  props.onChange({
@@ -3577,7 +3616,7 @@ function SearchableSelect(props) {
3577
3616
  }) => {
3578
3617
  setIsEscapeClose(false);
3579
3618
  setIsDropdownExpanded(true);
3580
- setShouldApplyFilter(true);
3619
+ setIsFilterActive(true);
3581
3620
  setFilter(target.value || '');
3582
3621
  eventBus.fire('formField.search', {
3583
3622
  formField: field,
@@ -3593,7 +3632,7 @@ function SearchableSelect(props) {
3593
3632
  {
3594
3633
  if (!isDropdownExpanded) {
3595
3634
  setIsDropdownExpanded(true);
3596
- setShouldApplyFilter(false);
3635
+ setIsFilterActive(false);
3597
3636
  }
3598
3637
  keyDownEvent.preventDefault();
3599
3638
  break;
@@ -3611,7 +3650,7 @@ function SearchableSelect(props) {
3611
3650
  const onInputMouseDown = hooks.useCallback(() => {
3612
3651
  setIsEscapeClose(false);
3613
3652
  setIsDropdownExpanded(true);
3614
- setShouldApplyFilter(false);
3653
+ setIsFilterActive(false);
3615
3654
  }, []);
3616
3655
  const onInputFocus = hooks.useCallback(() => {
3617
3656
  setIsEscapeClose(false);
@@ -3620,9 +3659,9 @@ function SearchableSelect(props) {
3620
3659
  }, [onFocus]);
3621
3660
  const onInputBlur = hooks.useCallback(() => {
3622
3661
  setIsDropdownExpanded(false);
3623
- setFilter(valueLabel);
3662
+ setFilter(label);
3624
3663
  onBlur && onBlur();
3625
- }, [onBlur, valueLabel]);
3664
+ }, [onBlur, label]);
3626
3665
  return jsxRuntime.jsxs(jsxRuntime.Fragment, {
3627
3666
  children: [jsxRuntime.jsxs("div", {
3628
3667
  class: classNames('fjs-input-group', {
@@ -3699,12 +3738,8 @@ function SimpleSelect(props) {
3699
3738
  value,
3700
3739
  onChange: props.onChange
3701
3740
  });
3702
-
3703
- // We cache a map of option values to their index so that we don't need to search the whole options array every time to correlate the label
3704
- const valueToOptionMap = hooks.useMemo(() => Object.assign({}, ...options.map((o, x) => ({
3705
- [o.value]: options[x]
3706
- }))), [options]);
3707
- const valueLabel = hooks.useMemo(() => value && valueToOptionMap[value] && valueToOptionMap[value].label || '', [value, valueToOptionMap]);
3741
+ const getLabelCorrelation = useGetLabelCorrelation(options);
3742
+ const valueLabel = hooks.useMemo(() => value && getLabelCorrelation(value), [value, getLabelCorrelation]);
3708
3743
  const setValue = hooks.useCallback(option => {
3709
3744
  props.onChange({
3710
3745
  value: option && option.value || null,
@@ -4019,11 +4054,7 @@ function Taglist(props) {
4019
4054
  values,
4020
4055
  onChange: props.onChange
4021
4056
  });
4022
-
4023
- // We cache a map of option values to their index so that we don't need to search the whole options array every time to correlate the label
4024
- const valueToOptionMap = hooks.useMemo(() => Object.assign({}, ...options.map((o, x) => ({
4025
- [o.value]: options[x]
4026
- }))), [options]);
4057
+ const getLabelCorrelation = useGetLabelCorrelation(options);
4027
4058
  const hasOptionsLeft = hooks.useMemo(() => options.length > values.length, [options.length, values.length]);
4028
4059
 
4029
4060
  // Usage of stringify is necessary here because we want this effect to only trigger when there is a value change to the array
@@ -4031,12 +4062,14 @@ function Taglist(props) {
4031
4062
  if (loadState !== LOAD_STATES.LOADED) {
4032
4063
  return [];
4033
4064
  }
4034
- return options.filter(o => o.label && o.value && o.label.toLowerCase().includes(filter.toLowerCase()) && !values.includes(o.value));
4065
+ const isValidFilteredOption = option => {
4066
+ const filterMatches = option.label.toLowerCase().includes(filter.toLowerCase());
4067
+ return filterMatches && !hasEqualValue(option.value, values);
4068
+ };
4069
+ return options.filter(isValidFilteredOption);
4035
4070
  }, [filter, options, JSON.stringify(values), loadState]);
4036
4071
  const selectValue = value => {
4037
- if (filter) {
4038
- setFilter('');
4039
- }
4072
+ setFilter('');
4040
4073
 
4041
4074
  // Ensure values cannot be double selected due to latency
4042
4075
  if (values.at(-1) === value) {
@@ -4048,8 +4081,9 @@ function Taglist(props) {
4048
4081
  });
4049
4082
  };
4050
4083
  const deselectValue = value => {
4084
+ const newValues = values.filter(v => !isEqual(v, value));
4051
4085
  props.onChange({
4052
- value: values.filter(v => v != value),
4086
+ value: newValues,
4053
4087
  field
4054
4088
  });
4055
4089
  };
@@ -4159,7 +4193,7 @@ function Taglist(props) {
4159
4193
  onMouseDown: e => e.preventDefault(),
4160
4194
  children: [jsxRuntime.jsx("span", {
4161
4195
  class: "fjs-taglist-tag-label",
4162
- children: valueToOptionMap[v] ? valueToOptionMap[v].label : undefined
4196
+ children: getLabelCorrelation(v)
4163
4197
  }), !disabled && !readonly && jsxRuntime.jsx("button", {
4164
4198
  type: "button",
4165
4199
  title: "Remove tag",
@@ -4299,6 +4333,40 @@ function DisabledLink({
4299
4333
  });
4300
4334
  }
4301
4335
 
4336
+ function useFlushDebounce(func, additionalDeps = []) {
4337
+ const timeoutRef = hooks.useRef(null);
4338
+ const lastArgsRef = hooks.useRef(null);
4339
+ const config = useService('config', false);
4340
+ const debounce = config && config.debounce;
4341
+ const shouldDebounce = debounce !== false && debounce !== 0;
4342
+ const delay = typeof debounce === 'number' ? debounce : 300;
4343
+ const debounceFunc = hooks.useCallback((...args) => {
4344
+ if (!shouldDebounce) {
4345
+ func(...args);
4346
+ return;
4347
+ }
4348
+ lastArgsRef.current = args;
4349
+ if (timeoutRef.current) {
4350
+ clearTimeout(timeoutRef.current);
4351
+ }
4352
+ timeoutRef.current = setTimeout(() => {
4353
+ func(...lastArgsRef.current);
4354
+ lastArgsRef.current = null;
4355
+ }, delay);
4356
+ }, [func, delay, shouldDebounce, ...additionalDeps]);
4357
+ const flushFunc = hooks.useCallback(() => {
4358
+ if (timeoutRef.current) {
4359
+ clearTimeout(timeoutRef.current);
4360
+ if (lastArgsRef.current !== null) {
4361
+ func(...lastArgsRef.current);
4362
+ lastArgsRef.current = null;
4363
+ }
4364
+ timeoutRef.current = null;
4365
+ }
4366
+ }, [func, ...additionalDeps]);
4367
+ return [debounceFunc, flushFunc];
4368
+ }
4369
+
4302
4370
  const type$2 = 'textfield';
4303
4371
  function Textfield(props) {
4304
4372
  const {
@@ -4325,13 +4393,20 @@ function Textfield(props) {
4325
4393
  const {
4326
4394
  required
4327
4395
  } = validate;
4328
- const onChange = ({
4396
+ const [onInputChange, flushOnChange] = useFlushDebounce(({
4329
4397
  target
4330
4398
  }) => {
4331
4399
  props.onChange({
4332
4400
  field,
4333
4401
  value: target.value
4334
4402
  });
4403
+ }, [props.onChange]);
4404
+ const onInputBlur = () => {
4405
+ flushOnChange && flushOnChange();
4406
+ onBlur && onBlur();
4407
+ };
4408
+ const onInputFocus = () => {
4409
+ onFocus && onFocus();
4335
4410
  };
4336
4411
  return jsxRuntime.jsxs("div", {
4337
4412
  class: formFieldClasses(type$2, {
@@ -4353,9 +4428,9 @@ function Textfield(props) {
4353
4428
  disabled: disabled,
4354
4429
  readOnly: readonly,
4355
4430
  id: domId,
4356
- onInput: onChange,
4357
- onBlur: () => onBlur && onBlur(),
4358
- onFocus: () => onFocus && onFocus(),
4431
+ onInput: onInputChange,
4432
+ onBlur: onInputBlur,
4433
+ onFocus: onInputFocus,
4359
4434
  type: "text",
4360
4435
  value: value,
4361
4436
  "aria-describedby": errorMessageId
@@ -4414,13 +4489,20 @@ function Textarea(props) {
4414
4489
  required
4415
4490
  } = validate;
4416
4491
  const textareaRef = hooks.useRef();
4417
- const onInput = ({
4492
+ const [onInputChange, flushOnChange] = useFlushDebounce(({
4418
4493
  target
4419
4494
  }) => {
4420
4495
  props.onChange({
4421
4496
  field,
4422
4497
  value: target.value
4423
4498
  });
4499
+ }, [props.onChange]);
4500
+ const onInputBlur = () => {
4501
+ flushOnChange && flushOnChange();
4502
+ onBlur && onBlur();
4503
+ };
4504
+ const onInputFocus = () => {
4505
+ onFocus && onFocus();
4424
4506
  };
4425
4507
  hooks.useLayoutEffect(() => {
4426
4508
  autoSizeTextarea(textareaRef.current);
@@ -4443,9 +4525,9 @@ function Textarea(props) {
4443
4525
  disabled: disabled,
4444
4526
  readonly: readonly,
4445
4527
  id: domId,
4446
- onInput: onInput,
4447
- onBlur: () => onBlur && onBlur(),
4448
- onFocus: () => onFocus && onFocus(),
4528
+ onInput: onInputChange,
4529
+ onBlur: onInputBlur,
4530
+ onFocus: onInputFocus,
4449
4531
  value: value,
4450
4532
  ref: textareaRef,
4451
4533
  "aria-describedby": errorMessageId
@@ -7986,16 +8068,18 @@ class Form {
7986
8068
  */
7987
8069
  _createInjector(options, container) {
7988
8070
  const {
8071
+ modules = this._getModules(),
7989
8072
  additionalModules = [],
7990
- modules = this._getModules()
8073
+ ...config
7991
8074
  } = options;
7992
- const config = {
8075
+ const enrichedConfig = {
8076
+ ...config,
7993
8077
  renderer: {
7994
8078
  container
7995
8079
  }
7996
8080
  };
7997
8081
  return createInjector([{
7998
- config: ['value', config]
8082
+ config: ['value', enrichedConfig]
7999
8083
  }, {
8000
8084
  form: ['value', this]
8001
8085
  }, core, ...modules, ...additionalModules]);
@@ -8242,9 +8326,9 @@ function createForm(options) {
8242
8326
  const {
8243
8327
  data,
8244
8328
  schema,
8245
- ...rest
8329
+ ...formOptions
8246
8330
  } = options;
8247
- const form = new Form(rest);
8331
+ const form = new Form(formOptions);
8248
8332
  return form.importSchema(schema, data).then(function () {
8249
8333
  return form;
8250
8334
  });