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

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,
@@ -3055,6 +3078,40 @@ Image.config = {
3055
3078
  })
3056
3079
  };
3057
3080
 
3081
+ function useFlushDebounce(func, additionalDeps = []) {
3082
+ const timeoutRef = hooks.useRef(null);
3083
+ const lastArgsRef = hooks.useRef(null);
3084
+ const config = useService('config', false);
3085
+ const debounce = config && config.debounce;
3086
+ const shouldDebounce = debounce !== false && debounce !== 0;
3087
+ const delay = typeof debounce === 'number' ? debounce : 300;
3088
+ const debounceFunc = hooks.useCallback((...args) => {
3089
+ if (!shouldDebounce) {
3090
+ func(...args);
3091
+ return;
3092
+ }
3093
+ lastArgsRef.current = args;
3094
+ if (timeoutRef.current) {
3095
+ clearTimeout(timeoutRef.current);
3096
+ }
3097
+ timeoutRef.current = setTimeout(() => {
3098
+ func(...lastArgsRef.current);
3099
+ lastArgsRef.current = null;
3100
+ }, delay);
3101
+ }, [func, delay, shouldDebounce, ...additionalDeps]);
3102
+ const flushFunc = hooks.useCallback(() => {
3103
+ if (timeoutRef.current) {
3104
+ clearTimeout(timeoutRef.current);
3105
+ if (lastArgsRef.current !== null) {
3106
+ func(...lastArgsRef.current);
3107
+ lastArgsRef.current = null;
3108
+ }
3109
+ timeoutRef.current = null;
3110
+ }
3111
+ }, [func, ...additionalDeps]);
3112
+ return [debounceFunc, flushFunc];
3113
+ }
3114
+
3058
3115
  function TemplatedInputAdorner(props) {
3059
3116
  const {
3060
3117
  pre,
@@ -3145,8 +3202,7 @@ function Numberfield(props) {
3145
3202
  onFocus,
3146
3203
  field,
3147
3204
  value,
3148
- readonly,
3149
- onChange
3205
+ readonly
3150
3206
  } = props;
3151
3207
  const {
3152
3208
  description,
@@ -3166,6 +3222,16 @@ function Numberfield(props) {
3166
3222
  } = validate;
3167
3223
  const inputRef = hooks.useRef();
3168
3224
  const [stringValueCache, setStringValueCache] = hooks.useState('');
3225
+ const [onChangeDebounced, flushOnChange] = useFlushDebounce(params => {
3226
+ props.onChange(params);
3227
+ }, [props.onChange]);
3228
+ const onInputBlur = () => {
3229
+ flushOnChange && flushOnChange();
3230
+ onBlur && onBlur();
3231
+ };
3232
+ const onInputFocus = () => {
3233
+ onFocus && onFocus();
3234
+ };
3169
3235
 
3170
3236
  // checks whether the value currently in the form data is practically different from the one in the input field cache
3171
3237
  // this allows us to guarantee the field always displays valid form data, but without auto-simplifying values like 1.000 to 1
@@ -3189,7 +3255,7 @@ function Numberfield(props) {
3189
3255
  const setValue = hooks.useCallback(stringValue => {
3190
3256
  if (isNullEquivalentValue(stringValue)) {
3191
3257
  setStringValueCache('');
3192
- onChange({
3258
+ onChangeDebounced({
3193
3259
  field,
3194
3260
  value: null
3195
3261
  });
@@ -3204,18 +3270,18 @@ function Numberfield(props) {
3204
3270
  }
3205
3271
  if (isNaN(Number(stringValue))) {
3206
3272
  setStringValueCache('NaN');
3207
- onChange({
3273
+ onChangeDebounced({
3208
3274
  field,
3209
3275
  value: 'NaN'
3210
3276
  });
3211
3277
  return;
3212
3278
  }
3213
3279
  setStringValueCache(stringValue);
3214
- onChange({
3280
+ onChangeDebounced({
3215
3281
  field,
3216
3282
  value: serializeToString ? stringValue : Number(stringValue)
3217
3283
  });
3218
- }, [field, onChange, serializeToString]);
3284
+ }, [field, onChangeDebounced, serializeToString]);
3219
3285
  const increment = () => {
3220
3286
  if (readonly) {
3221
3287
  return;
@@ -3299,8 +3365,8 @@ function Numberfield(props) {
3299
3365
  id: domId,
3300
3366
  onKeyDown: onKeyDown,
3301
3367
  onKeyPress: onKeyPress,
3302
- onBlur: () => onBlur && onBlur(),
3303
- onFocus: () => onFocus && onFocus()
3368
+ onBlur: onInputBlur,
3369
+ onFocus: onInputFocus
3304
3370
 
3305
3371
  // @ts-ignore
3306
3372
  ,
@@ -3377,7 +3443,8 @@ function useCleanupSingleSelectValue (props) {
3377
3443
  if (loadState !== LOAD_STATES.LOADED) {
3378
3444
  return;
3379
3445
  }
3380
- const hasValueNotInOptions = value && !options.map(o => o.value).includes(value);
3446
+ const optionValues = options.map(o => o.value);
3447
+ const hasValueNotInOptions = value && !hasEqualValue(value, optionValues);
3381
3448
  if (hasValueNotInOptions) {
3382
3449
  onChange({
3383
3450
  field,
@@ -3450,15 +3517,16 @@ function Radio(props) {
3450
3517
  required: required
3451
3518
  }), loadState == LOAD_STATES.LOADED && options.map((option, index) => {
3452
3519
  const itemDomId = `${domId}-${index}`;
3520
+ const isChecked = isEqual(option.value, value);
3453
3521
  return jsxRuntime.jsx(Label, {
3454
3522
  id: itemDomId,
3455
3523
  label: option.label,
3456
3524
  class: classNames({
3457
- 'fjs-checked': option.value === value
3525
+ 'fjs-checked': isChecked
3458
3526
  }),
3459
3527
  required: false,
3460
3528
  children: jsxRuntime.jsx("input", {
3461
- checked: option.value === value,
3529
+ checked: isChecked,
3462
3530
  class: "fjs-input",
3463
3531
  disabled: disabled,
3464
3532
  readOnly: readonly,
@@ -3488,6 +3556,21 @@ Radio.config = {
3488
3556
  create: createEmptyOptions
3489
3557
  };
3490
3558
 
3559
+ /**
3560
+ * This hook allows us to retrieve the label from a value in linear time by caching it in a map
3561
+ * @param {Array} options
3562
+ */
3563
+ function useGetLabelCorrelation(options) {
3564
+ // This allows us to retrieve the label from a value in linear time
3565
+ const labelMap = hooks.useMemo(() => Object.assign({}, ...options.map(o => ({
3566
+ [_getValueHash(o.value)]: o.label
3567
+ }))), [options]);
3568
+ return hooks.useCallback(value => labelMap[_getValueHash(value)], [labelMap]);
3569
+ }
3570
+ const _getValueHash = value => {
3571
+ return minDash.isObject(value) ? JSON.stringify(value) : value;
3572
+ };
3573
+
3491
3574
  var _path$q;
3492
3575
  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
3576
  var SvgXMark = function SvgXMark(props) {
@@ -3519,7 +3602,7 @@ function SearchableSelect(props) {
3519
3602
  } = props;
3520
3603
  const [filter, setFilter] = hooks.useState('');
3521
3604
  const [isDropdownExpanded, setIsDropdownExpanded] = hooks.useState(false);
3522
- const [shouldApplyFilter, setShouldApplyFilter] = hooks.useState(true);
3605
+ const [isFilterActive, setIsFilterActive] = hooks.useState(true);
3523
3606
  const [isEscapeClosed, setIsEscapeClose] = hooks.useState(false);
3524
3607
  const searchbarRef = hooks.useRef();
3525
3608
  const eventBus = useService('eventBus');
@@ -3534,23 +3617,22 @@ function SearchableSelect(props) {
3534
3617
  value,
3535
3618
  onChange: props.onChange
3536
3619
  });
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]);
3620
+ const getLabelCorrelation = useGetLabelCorrelation(options);
3621
+ const label = hooks.useMemo(() => value && getLabelCorrelation(value), [value, getLabelCorrelation]);
3543
3622
 
3544
3623
  // whenever we change the underlying value, set the label to it
3545
3624
  hooks.useEffect(() => {
3546
- setFilter(valueLabel);
3547
- }, [valueLabel]);
3625
+ setFilter(label);
3626
+ }, [label]);
3548
3627
  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;
3628
+ if (loadState !== LOAD_STATES.LOADED) {
3629
+ return [];
3551
3630
  }
3552
- return [];
3553
- }, [filter, loadState, options, shouldApplyFilter]);
3631
+ if (!filter || !isFilterActive) {
3632
+ return options;
3633
+ }
3634
+ return options.filter(o => o.label && o.value && o.label.toLowerCase().includes(filter.toLowerCase()));
3635
+ }, [filter, loadState, options, isFilterActive]);
3554
3636
  const setValue = hooks.useCallback(option => {
3555
3637
  setFilter(option && option.label || '');
3556
3638
  props.onChange({
@@ -3577,7 +3659,7 @@ function SearchableSelect(props) {
3577
3659
  }) => {
3578
3660
  setIsEscapeClose(false);
3579
3661
  setIsDropdownExpanded(true);
3580
- setShouldApplyFilter(true);
3662
+ setIsFilterActive(true);
3581
3663
  setFilter(target.value || '');
3582
3664
  eventBus.fire('formField.search', {
3583
3665
  formField: field,
@@ -3593,7 +3675,7 @@ function SearchableSelect(props) {
3593
3675
  {
3594
3676
  if (!isDropdownExpanded) {
3595
3677
  setIsDropdownExpanded(true);
3596
- setShouldApplyFilter(false);
3678
+ setIsFilterActive(false);
3597
3679
  }
3598
3680
  keyDownEvent.preventDefault();
3599
3681
  break;
@@ -3611,7 +3693,7 @@ function SearchableSelect(props) {
3611
3693
  const onInputMouseDown = hooks.useCallback(() => {
3612
3694
  setIsEscapeClose(false);
3613
3695
  setIsDropdownExpanded(true);
3614
- setShouldApplyFilter(false);
3696
+ setIsFilterActive(false);
3615
3697
  }, []);
3616
3698
  const onInputFocus = hooks.useCallback(() => {
3617
3699
  setIsEscapeClose(false);
@@ -3620,9 +3702,9 @@ function SearchableSelect(props) {
3620
3702
  }, [onFocus]);
3621
3703
  const onInputBlur = hooks.useCallback(() => {
3622
3704
  setIsDropdownExpanded(false);
3623
- setFilter(valueLabel);
3705
+ setFilter(label);
3624
3706
  onBlur && onBlur();
3625
- }, [onBlur, valueLabel]);
3707
+ }, [onBlur, label]);
3626
3708
  return jsxRuntime.jsxs(jsxRuntime.Fragment, {
3627
3709
  children: [jsxRuntime.jsxs("div", {
3628
3710
  class: classNames('fjs-input-group', {
@@ -3699,12 +3781,8 @@ function SimpleSelect(props) {
3699
3781
  value,
3700
3782
  onChange: props.onChange
3701
3783
  });
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]);
3784
+ const getLabelCorrelation = useGetLabelCorrelation(options);
3785
+ const valueLabel = hooks.useMemo(() => value && getLabelCorrelation(value), [value, getLabelCorrelation]);
3708
3786
  const setValue = hooks.useCallback(option => {
3709
3787
  props.onChange({
3710
3788
  value: option && option.value || null,
@@ -4019,11 +4097,7 @@ function Taglist(props) {
4019
4097
  values,
4020
4098
  onChange: props.onChange
4021
4099
  });
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]);
4100
+ const getLabelCorrelation = useGetLabelCorrelation(options);
4027
4101
  const hasOptionsLeft = hooks.useMemo(() => options.length > values.length, [options.length, values.length]);
4028
4102
 
4029
4103
  // 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 +4105,14 @@ function Taglist(props) {
4031
4105
  if (loadState !== LOAD_STATES.LOADED) {
4032
4106
  return [];
4033
4107
  }
4034
- return options.filter(o => o.label && o.value && o.label.toLowerCase().includes(filter.toLowerCase()) && !values.includes(o.value));
4108
+ const isValidFilteredOption = option => {
4109
+ const filterMatches = option.label.toLowerCase().includes(filter.toLowerCase());
4110
+ return filterMatches && !hasEqualValue(option.value, values);
4111
+ };
4112
+ return options.filter(isValidFilteredOption);
4035
4113
  }, [filter, options, JSON.stringify(values), loadState]);
4036
4114
  const selectValue = value => {
4037
- if (filter) {
4038
- setFilter('');
4039
- }
4115
+ setFilter('');
4040
4116
 
4041
4117
  // Ensure values cannot be double selected due to latency
4042
4118
  if (values.at(-1) === value) {
@@ -4048,8 +4124,9 @@ function Taglist(props) {
4048
4124
  });
4049
4125
  };
4050
4126
  const deselectValue = value => {
4127
+ const newValues = values.filter(v => !isEqual(v, value));
4051
4128
  props.onChange({
4052
- value: values.filter(v => v != value),
4129
+ value: newValues,
4053
4130
  field
4054
4131
  });
4055
4132
  };
@@ -4159,7 +4236,7 @@ function Taglist(props) {
4159
4236
  onMouseDown: e => e.preventDefault(),
4160
4237
  children: [jsxRuntime.jsx("span", {
4161
4238
  class: "fjs-taglist-tag-label",
4162
- children: valueToOptionMap[v] ? valueToOptionMap[v].label : undefined
4239
+ children: getLabelCorrelation(v)
4163
4240
  }), !disabled && !readonly && jsxRuntime.jsx("button", {
4164
4241
  type: "button",
4165
4242
  title: "Remove tag",
@@ -4325,13 +4402,20 @@ function Textfield(props) {
4325
4402
  const {
4326
4403
  required
4327
4404
  } = validate;
4328
- const onChange = ({
4405
+ const [onInputChange, flushOnChange] = useFlushDebounce(({
4329
4406
  target
4330
4407
  }) => {
4331
4408
  props.onChange({
4332
4409
  field,
4333
4410
  value: target.value
4334
4411
  });
4412
+ }, [props.onChange]);
4413
+ const onInputBlur = () => {
4414
+ flushOnChange && flushOnChange();
4415
+ onBlur && onBlur();
4416
+ };
4417
+ const onInputFocus = () => {
4418
+ onFocus && onFocus();
4335
4419
  };
4336
4420
  return jsxRuntime.jsxs("div", {
4337
4421
  class: formFieldClasses(type$2, {
@@ -4353,9 +4437,9 @@ function Textfield(props) {
4353
4437
  disabled: disabled,
4354
4438
  readOnly: readonly,
4355
4439
  id: domId,
4356
- onInput: onChange,
4357
- onBlur: () => onBlur && onBlur(),
4358
- onFocus: () => onFocus && onFocus(),
4440
+ onInput: onInputChange,
4441
+ onBlur: onInputBlur,
4442
+ onFocus: onInputFocus,
4359
4443
  type: "text",
4360
4444
  value: value,
4361
4445
  "aria-describedby": errorMessageId
@@ -4414,13 +4498,20 @@ function Textarea(props) {
4414
4498
  required
4415
4499
  } = validate;
4416
4500
  const textareaRef = hooks.useRef();
4417
- const onInput = ({
4501
+ const [onInputChange, flushOnChange] = useFlushDebounce(({
4418
4502
  target
4419
4503
  }) => {
4420
4504
  props.onChange({
4421
4505
  field,
4422
4506
  value: target.value
4423
4507
  });
4508
+ }, [props.onChange]);
4509
+ const onInputBlur = () => {
4510
+ flushOnChange && flushOnChange();
4511
+ onBlur && onBlur();
4512
+ };
4513
+ const onInputFocus = () => {
4514
+ onFocus && onFocus();
4424
4515
  };
4425
4516
  hooks.useLayoutEffect(() => {
4426
4517
  autoSizeTextarea(textareaRef.current);
@@ -4443,9 +4534,9 @@ function Textarea(props) {
4443
4534
  disabled: disabled,
4444
4535
  readonly: readonly,
4445
4536
  id: domId,
4446
- onInput: onInput,
4447
- onBlur: () => onBlur && onBlur(),
4448
- onFocus: () => onFocus && onFocus(),
4537
+ onInput: onInputChange,
4538
+ onBlur: onInputBlur,
4539
+ onFocus: onInputFocus,
4449
4540
  value: value,
4450
4541
  ref: textareaRef,
4451
4542
  "aria-describedby": errorMessageId
@@ -7986,16 +8077,18 @@ class Form {
7986
8077
  */
7987
8078
  _createInjector(options, container) {
7988
8079
  const {
8080
+ modules = this._getModules(),
7989
8081
  additionalModules = [],
7990
- modules = this._getModules()
8082
+ ...config
7991
8083
  } = options;
7992
- const config = {
8084
+ const enrichedConfig = {
8085
+ ...config,
7993
8086
  renderer: {
7994
8087
  container
7995
8088
  }
7996
8089
  };
7997
8090
  return createInjector([{
7998
- config: ['value', config]
8091
+ config: ['value', enrichedConfig]
7999
8092
  }, {
8000
8093
  form: ['value', this]
8001
8094
  }, core, ...modules, ...additionalModules]);
@@ -8242,9 +8335,9 @@ function createForm(options) {
8242
8335
  const {
8243
8336
  data,
8244
8337
  schema,
8245
- ...rest
8338
+ ...formOptions
8246
8339
  } = options;
8247
- const form = new Form(rest);
8340
+ const form = new Form(formOptions);
8248
8341
  return form.importSchema(schema, data).then(function () {
8249
8342
  return form;
8250
8343
  });