@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.es.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import Ids from 'ids';
2
- import { isString, get, some, isNumber, set, findIndex, isArray, isObject, isNil, isDefined, values, uniqueBy, isFunction, bind, assign, groupBy, flatten, isUndefined } from 'min-dash';
2
+ import { isString, get, some, isNil, isObject, isNumber, set, findIndex, isArray, isDefined, values, uniqueBy, isFunction, bind, assign, groupBy, flatten, isUndefined } from 'min-dash';
3
3
  import Big from 'big.js';
4
4
  import classNames from 'classnames';
5
5
  import { jsx, jsxs, Fragment } from 'preact/jsx-runtime';
6
6
  import { useContext, useMemo, useEffect, useRef, useState, useCallback, useLayoutEffect } from 'preact/hooks';
7
7
  import { createContext, createElement, Fragment as Fragment$1, render } from 'preact';
8
+ import isEqual from 'lodash/isEqual';
8
9
  import flatpickr from 'flatpickr';
9
10
  import * as React from 'preact/compat';
10
11
  import { createPortal } from 'preact/compat';
@@ -1084,39 +1085,58 @@ function getOptionsData(formField, formData) {
1084
1085
 
1085
1086
  // transforms the provided options into a normalized format, trimming invalid options
1086
1087
  function normalizeOptionsData(optionsData) {
1087
- return optionsData.filter(_isOptionSomething).map(v => _normalizeOptionsData(v)).filter(v => v);
1088
+ return optionsData.filter(_isAllowedValue).map(_normalizeOption).filter(o => !isNil(o));
1088
1089
  }
1089
- function _normalizeOptionsData(optionData) {
1090
- if (_isAllowedOption(optionData)) {
1091
- // if a primitive is provided, use it as label and value
1090
+
1091
+ /**
1092
+ * Converts the provided option to a normalized format.
1093
+ * If the option is not valid, null is returned.
1094
+ *
1095
+ * @param {object} option
1096
+ * @param {string} option.label
1097
+ * @param {*} option.value
1098
+ *
1099
+ * @returns
1100
+ */
1101
+ function _normalizeOption(option) {
1102
+ // (1) simple primitive case, use it as both label and value
1103
+ if (_isAllowedPrimitive(option)) {
1092
1104
  return {
1093
- value: optionData,
1094
- label: `${optionData}`
1105
+ value: option,
1106
+ label: `${option}`
1095
1107
  };
1096
1108
  }
1097
- if (typeof optionData === 'object') {
1098
- if (!optionData.label && _isAllowedOption(optionData.value)) {
1099
- // if no label is provided, use the value as label
1109
+ if (isObject(option)) {
1110
+ const isValidLabel = _isValidLabel(option.label);
1111
+
1112
+ // (2) no label provided, but value is a simple primitive, use it as label and value
1113
+ if (!isValidLabel && _isAllowedPrimitive(option.value)) {
1100
1114
  return {
1101
- value: optionData.value,
1102
- label: `${optionData.value}`
1115
+ value: option.value,
1116
+ label: `${option.value}`
1103
1117
  };
1104
1118
  }
1105
- if (_isOptionSomething(optionData.value) && _isAllowedOption(optionData.label)) {
1106
- // if both value and label are provided, use them as is, in this scenario, the value may also be an object
1107
- return optionData;
1119
+
1120
+ // (3) both label and value are provided, use them as is
1121
+ if (isValidLabel && _isAllowedValue(option.value)) {
1122
+ return option;
1108
1123
  }
1109
1124
  }
1110
1125
  return null;
1111
1126
  }
1112
- function _isAllowedOption(option) {
1113
- return _isReadableType(option) && _isOptionSomething(option);
1127
+ function _isAllowedPrimitive(value) {
1128
+ const isAllowedPrimitiveType = ['number', 'string', 'boolean'].includes(typeof value);
1129
+ const isValid = value || value === 0 || value === false;
1130
+ return isAllowedPrimitiveType && isValid;
1114
1131
  }
1115
- function _isReadableType(option) {
1116
- return ['number', 'string', 'boolean'].includes(typeof option);
1132
+ function _isValidLabel(label) {
1133
+ return label && isString(label);
1117
1134
  }
1118
- function _isOptionSomething(option) {
1119
- return option || option === 0 || option === false;
1135
+ function _isAllowedValue(value) {
1136
+ if (isObject(value)) {
1137
+ return Object.keys(value).length > 0;
1138
+ }
1139
+ return _isAllowedPrimitive(value);
1120
1140
  }
1121
1141
  function createEmptyOptions(options = {}) {
1122
1142
  const defaults = {};
@@ -1210,30 +1230,6 @@ const buildLoadedState = options => ({
1210
1230
  loadState: LOAD_STATES.LOADED
1211
1231
  });
1212
1232
 
1213
- function useCleanupMultiSelectValues (props) {
1214
- const {
1215
- field,
1216
- options,
1217
- loadState,
1218
- onChange,
1219
- values
1220
- } = props;
1221
-
1222
- // Ensures that the values are always a subset of the possible options
1223
- useEffect(() => {
1224
- if (loadState !== LOAD_STATES.LOADED) {
1225
- return;
1226
- }
1227
- const hasValuesNotInOptions = values.some(v => !options.map(o => o.value).includes(v));
1228
- if (hasValuesNotInOptions) {
1229
- onChange({
1230
- field,
1231
- value: values.filter(v => options.map(o => o.value).includes(v))
1232
- });
1233
- }
1234
- }, [field, options, onChange, JSON.stringify(values), loadState]);
1235
- }
1236
-
1237
1233
  const ENTER_KEYDOWN_EVENT = new KeyboardEvent('keydown', {
1238
1234
  code: 'Enter',
1239
1235
  key: 'Enter',
@@ -1409,6 +1405,12 @@ function sanitizeDateTimePickerValue(options) {
1409
1405
  if (subtype === DATETIME_SUBTYPES.DATETIME && (isInvalidDateString(value) || !isDateTimeInputInformationSufficient(value))) return null;
1410
1406
  return value;
1411
1407
  }
1408
+ function hasEqualValue(value, array) {
1409
+ if (!Array.isArray(array)) {
1410
+ return false;
1411
+ }
1412
+ return array.some(element => isEqual(value, element));
1413
+ }
1412
1414
  function sanitizeSingleSelectValue(options) {
1413
1415
  const {
1414
1416
  formField,
@@ -1417,7 +1419,7 @@ function sanitizeSingleSelectValue(options) {
1417
1419
  } = options;
1418
1420
  try {
1419
1421
  const validValues = normalizeOptionsData(getOptionsData(formField, data)).map(v => v.value);
1420
- return validValues.includes(value) ? value : null;
1422
+ return hasEqualValue(value, validValues) ? value : null;
1421
1423
  } catch (error) {
1422
1424
  // use default value in case of formatting error
1423
1425
  // TODO(@Skaiir): log a warning when this happens - https://github.com/bpmn-io/form-js/issues/289
@@ -1432,7 +1434,7 @@ function sanitizeMultiSelectValue(options) {
1432
1434
  } = options;
1433
1435
  try {
1434
1436
  const validValues = normalizeOptionsData(getOptionsData(formField, data)).map(v => v.value);
1435
- return value.filter(v => validValues.includes(v));
1437
+ return value.filter(v => hasEqualValue(v, validValues));
1436
1438
  } catch (error) {
1437
1439
  // use default value in case of formatting error
1438
1440
  // TODO(@Skaiir): log a warning when this happens - https://github.com/bpmn-io/form-js/issues/289
@@ -1440,6 +1442,31 @@ function sanitizeMultiSelectValue(options) {
1440
1442
  }
1441
1443
  }
1442
1444
 
1445
+ function useCleanupMultiSelectValues (props) {
1446
+ const {
1447
+ field,
1448
+ options,
1449
+ loadState,
1450
+ onChange,
1451
+ values
1452
+ } = props;
1453
+
1454
+ // Ensures that the values are always a subset of the possible options
1455
+ useEffect(() => {
1456
+ if (loadState !== LOAD_STATES.LOADED) {
1457
+ return;
1458
+ }
1459
+ const optionValues = options.map(o => o.value);
1460
+ const hasValuesNotInOptions = values.some(v => !hasEqualValue(v, optionValues));
1461
+ if (hasValuesNotInOptions) {
1462
+ onChange({
1463
+ field,
1464
+ value: values.filter(v => hasEqualValue(v, optionValues))
1465
+ });
1466
+ }
1467
+ }, [field, options, onChange, JSON.stringify(values), loadState]);
1468
+ }
1469
+
1443
1470
  const type$d = 'checklist';
1444
1471
  function Checklist(props) {
1445
1472
  const {
@@ -1462,16 +1489,11 @@ function Checklist(props) {
1462
1489
  const {
1463
1490
  required
1464
1491
  } = validate;
1465
- const toggleCheckbox = v => {
1466
- let newValue = [...values];
1467
- if (!newValue.includes(v)) {
1468
- newValue.push(v);
1469
- } else {
1470
- newValue = newValue.filter(x => x != v);
1471
- }
1492
+ const toggleCheckbox = toggledValue => {
1493
+ const newValues = hasEqualValue(toggledValue, values) ? values.filter(value => !isEqual(value, toggledValue)) : [...values, toggledValue];
1472
1494
  props.onChange({
1473
1495
  field,
1474
- value: newValue
1496
+ value: newValues
1475
1497
  });
1476
1498
  };
1477
1499
  const onCheckboxBlur = e => {
@@ -1509,15 +1531,16 @@ function Checklist(props) {
1509
1531
  required: required
1510
1532
  }), loadState == LOAD_STATES.LOADED && options.map((o, index) => {
1511
1533
  const itemDomId = `${domId}-${index}`;
1534
+ const isChecked = hasEqualValue(o.value, values);
1512
1535
  return jsx(Label, {
1513
1536
  id: itemDomId,
1514
1537
  label: o.label,
1515
1538
  class: classNames({
1516
- 'fjs-checked': values.includes(o.value)
1539
+ 'fjs-checked': isChecked
1517
1540
  }),
1518
1541
  required: false,
1519
1542
  children: jsx("input", {
1520
- checked: values.includes(o.value),
1543
+ checked: isChecked,
1521
1544
  class: "fjs-input",
1522
1545
  disabled: disabled,
1523
1546
  readOnly: readonly,
@@ -3035,6 +3058,40 @@ Image.config = {
3035
3058
  })
3036
3059
  };
3037
3060
 
3061
+ function useFlushDebounce(func, additionalDeps = []) {
3062
+ const timeoutRef = useRef(null);
3063
+ const lastArgsRef = useRef(null);
3064
+ const config = useService('config', false);
3065
+ const debounce = config && config.debounce;
3066
+ const shouldDebounce = debounce !== false && debounce !== 0;
3067
+ const delay = typeof debounce === 'number' ? debounce : 300;
3068
+ const debounceFunc = useCallback((...args) => {
3069
+ if (!shouldDebounce) {
3070
+ func(...args);
3071
+ return;
3072
+ }
3073
+ lastArgsRef.current = args;
3074
+ if (timeoutRef.current) {
3075
+ clearTimeout(timeoutRef.current);
3076
+ }
3077
+ timeoutRef.current = setTimeout(() => {
3078
+ func(...lastArgsRef.current);
3079
+ lastArgsRef.current = null;
3080
+ }, delay);
3081
+ }, [func, delay, shouldDebounce, ...additionalDeps]);
3082
+ const flushFunc = useCallback(() => {
3083
+ if (timeoutRef.current) {
3084
+ clearTimeout(timeoutRef.current);
3085
+ if (lastArgsRef.current !== null) {
3086
+ func(...lastArgsRef.current);
3087
+ lastArgsRef.current = null;
3088
+ }
3089
+ timeoutRef.current = null;
3090
+ }
3091
+ }, [func, ...additionalDeps]);
3092
+ return [debounceFunc, flushFunc];
3093
+ }
3094
+
3038
3095
  function TemplatedInputAdorner(props) {
3039
3096
  const {
3040
3097
  pre,
@@ -3125,8 +3182,7 @@ function Numberfield(props) {
3125
3182
  onFocus,
3126
3183
  field,
3127
3184
  value,
3128
- readonly,
3129
- onChange
3185
+ readonly
3130
3186
  } = props;
3131
3187
  const {
3132
3188
  description,
@@ -3146,6 +3202,16 @@ function Numberfield(props) {
3146
3202
  } = validate;
3147
3203
  const inputRef = useRef();
3148
3204
  const [stringValueCache, setStringValueCache] = useState('');
3205
+ const [onChangeDebounced, flushOnChange] = useFlushDebounce(params => {
3206
+ props.onChange(params);
3207
+ }, [props.onChange]);
3208
+ const onInputBlur = () => {
3209
+ flushOnChange && flushOnChange();
3210
+ onBlur && onBlur();
3211
+ };
3212
+ const onInputFocus = () => {
3213
+ onFocus && onFocus();
3214
+ };
3149
3215
 
3150
3216
  // checks whether the value currently in the form data is practically different from the one in the input field cache
3151
3217
  // this allows us to guarantee the field always displays valid form data, but without auto-simplifying values like 1.000 to 1
@@ -3169,7 +3235,7 @@ function Numberfield(props) {
3169
3235
  const setValue = useCallback(stringValue => {
3170
3236
  if (isNullEquivalentValue(stringValue)) {
3171
3237
  setStringValueCache('');
3172
- onChange({
3238
+ onChangeDebounced({
3173
3239
  field,
3174
3240
  value: null
3175
3241
  });
@@ -3184,18 +3250,18 @@ function Numberfield(props) {
3184
3250
  }
3185
3251
  if (isNaN(Number(stringValue))) {
3186
3252
  setStringValueCache('NaN');
3187
- onChange({
3253
+ onChangeDebounced({
3188
3254
  field,
3189
3255
  value: 'NaN'
3190
3256
  });
3191
3257
  return;
3192
3258
  }
3193
3259
  setStringValueCache(stringValue);
3194
- onChange({
3260
+ onChangeDebounced({
3195
3261
  field,
3196
3262
  value: serializeToString ? stringValue : Number(stringValue)
3197
3263
  });
3198
- }, [field, onChange, serializeToString]);
3264
+ }, [field, onChangeDebounced, serializeToString]);
3199
3265
  const increment = () => {
3200
3266
  if (readonly) {
3201
3267
  return;
@@ -3279,8 +3345,8 @@ function Numberfield(props) {
3279
3345
  id: domId,
3280
3346
  onKeyDown: onKeyDown,
3281
3347
  onKeyPress: onKeyPress,
3282
- onBlur: () => onBlur && onBlur(),
3283
- onFocus: () => onFocus && onFocus()
3348
+ onBlur: onInputBlur,
3349
+ onFocus: onInputFocus
3284
3350
 
3285
3351
  // @ts-ignore
3286
3352
  ,
@@ -3357,7 +3423,8 @@ function useCleanupSingleSelectValue (props) {
3357
3423
  if (loadState !== LOAD_STATES.LOADED) {
3358
3424
  return;
3359
3425
  }
3360
- const hasValueNotInOptions = value && !options.map(o => o.value).includes(value);
3426
+ const optionValues = options.map(o => o.value);
3427
+ const hasValueNotInOptions = value && !hasEqualValue(value, optionValues);
3361
3428
  if (hasValueNotInOptions) {
3362
3429
  onChange({
3363
3430
  field,
@@ -3430,15 +3497,16 @@ function Radio(props) {
3430
3497
  required: required
3431
3498
  }), loadState == LOAD_STATES.LOADED && options.map((option, index) => {
3432
3499
  const itemDomId = `${domId}-${index}`;
3500
+ const isChecked = isEqual(option.value, value);
3433
3501
  return jsx(Label, {
3434
3502
  id: itemDomId,
3435
3503
  label: option.label,
3436
3504
  class: classNames({
3437
- 'fjs-checked': option.value === value
3505
+ 'fjs-checked': isChecked
3438
3506
  }),
3439
3507
  required: false,
3440
3508
  children: jsx("input", {
3441
- checked: option.value === value,
3509
+ checked: isChecked,
3442
3510
  class: "fjs-input",
3443
3511
  disabled: disabled,
3444
3512
  readOnly: readonly,
@@ -3468,6 +3536,21 @@ Radio.config = {
3468
3536
  create: createEmptyOptions
3469
3537
  };
3470
3538
 
3539
+ /**
3540
+ * This hook allows us to retrieve the label from a value in linear time by caching it in a map
3541
+ * @param {Array} options
3542
+ */
3543
+ function useGetLabelCorrelation(options) {
3544
+ // This allows us to retrieve the label from a value in linear time
3545
+ const labelMap = useMemo(() => Object.assign({}, ...options.map(o => ({
3546
+ [_getValueHash(o.value)]: o.label
3547
+ }))), [options]);
3548
+ return useCallback(value => labelMap[_getValueHash(value)], [labelMap]);
3549
+ }
3550
+ const _getValueHash = value => {
3551
+ return isObject(value) ? JSON.stringify(value) : value;
3552
+ };
3553
+
3471
3554
  var _path$q;
3472
3555
  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); }
3473
3556
  var SvgXMark = function SvgXMark(props) {
@@ -3499,7 +3582,7 @@ function SearchableSelect(props) {
3499
3582
  } = props;
3500
3583
  const [filter, setFilter] = useState('');
3501
3584
  const [isDropdownExpanded, setIsDropdownExpanded] = useState(false);
3502
- const [shouldApplyFilter, setShouldApplyFilter] = useState(true);
3585
+ const [isFilterActive, setIsFilterActive] = useState(true);
3503
3586
  const [isEscapeClosed, setIsEscapeClose] = useState(false);
3504
3587
  const searchbarRef = useRef();
3505
3588
  const eventBus = useService('eventBus');
@@ -3514,23 +3597,22 @@ function SearchableSelect(props) {
3514
3597
  value,
3515
3598
  onChange: props.onChange
3516
3599
  });
3517
-
3518
- // 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
3519
- const valueToOptionMap = useMemo(() => Object.assign({}, ...options.map((o, x) => ({
3520
- [o.value]: options[x]
3521
- }))), [options]);
3522
- const valueLabel = useMemo(() => value && valueToOptionMap[value] && valueToOptionMap[value].label || '', [value, valueToOptionMap]);
3600
+ const getLabelCorrelation = useGetLabelCorrelation(options);
3601
+ const label = useMemo(() => value && getLabelCorrelation(value), [value, getLabelCorrelation]);
3523
3602
 
3524
3603
  // whenever we change the underlying value, set the label to it
3525
3604
  useEffect(() => {
3526
- setFilter(valueLabel);
3527
- }, [valueLabel]);
3605
+ setFilter(label);
3606
+ }, [label]);
3528
3607
  const filteredOptions = useMemo(() => {
3529
- if (loadState === LOAD_STATES.LOADED) {
3530
- return shouldApplyFilter ? options.filter(o => o.label && o.value && o.label.toLowerCase().includes(filter.toLowerCase())) : options;
3608
+ if (loadState !== LOAD_STATES.LOADED) {
3609
+ return [];
3531
3610
  }
3532
- return [];
3533
- }, [filter, loadState, options, shouldApplyFilter]);
3611
+ if (!filter || !isFilterActive) {
3612
+ return options;
3613
+ }
3614
+ return options.filter(o => o.label && o.value && o.label.toLowerCase().includes(filter.toLowerCase()));
3615
+ }, [filter, loadState, options, isFilterActive]);
3534
3616
  const setValue = useCallback(option => {
3535
3617
  setFilter(option && option.label || '');
3536
3618
  props.onChange({
@@ -3557,7 +3639,7 @@ function SearchableSelect(props) {
3557
3639
  }) => {
3558
3640
  setIsEscapeClose(false);
3559
3641
  setIsDropdownExpanded(true);
3560
- setShouldApplyFilter(true);
3642
+ setIsFilterActive(true);
3561
3643
  setFilter(target.value || '');
3562
3644
  eventBus.fire('formField.search', {
3563
3645
  formField: field,
@@ -3573,7 +3655,7 @@ function SearchableSelect(props) {
3573
3655
  {
3574
3656
  if (!isDropdownExpanded) {
3575
3657
  setIsDropdownExpanded(true);
3576
- setShouldApplyFilter(false);
3658
+ setIsFilterActive(false);
3577
3659
  }
3578
3660
  keyDownEvent.preventDefault();
3579
3661
  break;
@@ -3591,7 +3673,7 @@ function SearchableSelect(props) {
3591
3673
  const onInputMouseDown = useCallback(() => {
3592
3674
  setIsEscapeClose(false);
3593
3675
  setIsDropdownExpanded(true);
3594
- setShouldApplyFilter(false);
3676
+ setIsFilterActive(false);
3595
3677
  }, []);
3596
3678
  const onInputFocus = useCallback(() => {
3597
3679
  setIsEscapeClose(false);
@@ -3600,9 +3682,9 @@ function SearchableSelect(props) {
3600
3682
  }, [onFocus]);
3601
3683
  const onInputBlur = useCallback(() => {
3602
3684
  setIsDropdownExpanded(false);
3603
- setFilter(valueLabel);
3685
+ setFilter(label);
3604
3686
  onBlur && onBlur();
3605
- }, [onBlur, valueLabel]);
3687
+ }, [onBlur, label]);
3606
3688
  return jsxs(Fragment, {
3607
3689
  children: [jsxs("div", {
3608
3690
  class: classNames('fjs-input-group', {
@@ -3679,12 +3761,8 @@ function SimpleSelect(props) {
3679
3761
  value,
3680
3762
  onChange: props.onChange
3681
3763
  });
3682
-
3683
- // 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
3684
- const valueToOptionMap = useMemo(() => Object.assign({}, ...options.map((o, x) => ({
3685
- [o.value]: options[x]
3686
- }))), [options]);
3687
- const valueLabel = useMemo(() => value && valueToOptionMap[value] && valueToOptionMap[value].label || '', [value, valueToOptionMap]);
3764
+ const getLabelCorrelation = useGetLabelCorrelation(options);
3765
+ const valueLabel = useMemo(() => value && getLabelCorrelation(value), [value, getLabelCorrelation]);
3688
3766
  const setValue = useCallback(option => {
3689
3767
  props.onChange({
3690
3768
  value: option && option.value || null,
@@ -3999,11 +4077,7 @@ function Taglist(props) {
3999
4077
  values,
4000
4078
  onChange: props.onChange
4001
4079
  });
4002
-
4003
- // 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
4004
- const valueToOptionMap = useMemo(() => Object.assign({}, ...options.map((o, x) => ({
4005
- [o.value]: options[x]
4006
- }))), [options]);
4080
+ const getLabelCorrelation = useGetLabelCorrelation(options);
4007
4081
  const hasOptionsLeft = useMemo(() => options.length > values.length, [options.length, values.length]);
4008
4082
 
4009
4083
  // Usage of stringify is necessary here because we want this effect to only trigger when there is a value change to the array
@@ -4011,12 +4085,14 @@ function Taglist(props) {
4011
4085
  if (loadState !== LOAD_STATES.LOADED) {
4012
4086
  return [];
4013
4087
  }
4014
- return options.filter(o => o.label && o.value && o.label.toLowerCase().includes(filter.toLowerCase()) && !values.includes(o.value));
4088
+ const isValidFilteredOption = option => {
4089
+ const filterMatches = option.label.toLowerCase().includes(filter.toLowerCase());
4090
+ return filterMatches && !hasEqualValue(option.value, values);
4091
+ };
4092
+ return options.filter(isValidFilteredOption);
4015
4093
  }, [filter, options, JSON.stringify(values), loadState]);
4016
4094
  const selectValue = value => {
4017
- if (filter) {
4018
- setFilter('');
4019
- }
4095
+ setFilter('');
4020
4096
 
4021
4097
  // Ensure values cannot be double selected due to latency
4022
4098
  if (values.at(-1) === value) {
@@ -4028,8 +4104,9 @@ function Taglist(props) {
4028
4104
  });
4029
4105
  };
4030
4106
  const deselectValue = value => {
4107
+ const newValues = values.filter(v => !isEqual(v, value));
4031
4108
  props.onChange({
4032
- value: values.filter(v => v != value),
4109
+ value: newValues,
4033
4110
  field
4034
4111
  });
4035
4112
  };
@@ -4139,7 +4216,7 @@ function Taglist(props) {
4139
4216
  onMouseDown: e => e.preventDefault(),
4140
4217
  children: [jsx("span", {
4141
4218
  class: "fjs-taglist-tag-label",
4142
- children: valueToOptionMap[v] ? valueToOptionMap[v].label : undefined
4219
+ children: getLabelCorrelation(v)
4143
4220
  }), !disabled && !readonly && jsx("button", {
4144
4221
  type: "button",
4145
4222
  title: "Remove tag",
@@ -4305,13 +4382,20 @@ function Textfield(props) {
4305
4382
  const {
4306
4383
  required
4307
4384
  } = validate;
4308
- const onChange = ({
4385
+ const [onInputChange, flushOnChange] = useFlushDebounce(({
4309
4386
  target
4310
4387
  }) => {
4311
4388
  props.onChange({
4312
4389
  field,
4313
4390
  value: target.value
4314
4391
  });
4392
+ }, [props.onChange]);
4393
+ const onInputBlur = () => {
4394
+ flushOnChange && flushOnChange();
4395
+ onBlur && onBlur();
4396
+ };
4397
+ const onInputFocus = () => {
4398
+ onFocus && onFocus();
4315
4399
  };
4316
4400
  return jsxs("div", {
4317
4401
  class: formFieldClasses(type$2, {
@@ -4333,9 +4417,9 @@ function Textfield(props) {
4333
4417
  disabled: disabled,
4334
4418
  readOnly: readonly,
4335
4419
  id: domId,
4336
- onInput: onChange,
4337
- onBlur: () => onBlur && onBlur(),
4338
- onFocus: () => onFocus && onFocus(),
4420
+ onInput: onInputChange,
4421
+ onBlur: onInputBlur,
4422
+ onFocus: onInputFocus,
4339
4423
  type: "text",
4340
4424
  value: value,
4341
4425
  "aria-describedby": errorMessageId
@@ -4394,13 +4478,20 @@ function Textarea(props) {
4394
4478
  required
4395
4479
  } = validate;
4396
4480
  const textareaRef = useRef();
4397
- const onInput = ({
4481
+ const [onInputChange, flushOnChange] = useFlushDebounce(({
4398
4482
  target
4399
4483
  }) => {
4400
4484
  props.onChange({
4401
4485
  field,
4402
4486
  value: target.value
4403
4487
  });
4488
+ }, [props.onChange]);
4489
+ const onInputBlur = () => {
4490
+ flushOnChange && flushOnChange();
4491
+ onBlur && onBlur();
4492
+ };
4493
+ const onInputFocus = () => {
4494
+ onFocus && onFocus();
4404
4495
  };
4405
4496
  useLayoutEffect(() => {
4406
4497
  autoSizeTextarea(textareaRef.current);
@@ -4423,9 +4514,9 @@ function Textarea(props) {
4423
4514
  disabled: disabled,
4424
4515
  readonly: readonly,
4425
4516
  id: domId,
4426
- onInput: onInput,
4427
- onBlur: () => onBlur && onBlur(),
4428
- onFocus: () => onFocus && onFocus(),
4517
+ onInput: onInputChange,
4518
+ onBlur: onInputBlur,
4519
+ onFocus: onInputFocus,
4429
4520
  value: value,
4430
4521
  ref: textareaRef,
4431
4522
  "aria-describedby": errorMessageId
@@ -7966,16 +8057,18 @@ class Form {
7966
8057
  */
7967
8058
  _createInjector(options, container) {
7968
8059
  const {
8060
+ modules = this._getModules(),
7969
8061
  additionalModules = [],
7970
- modules = this._getModules()
8062
+ ...config
7971
8063
  } = options;
7972
- const config = {
8064
+ const enrichedConfig = {
8065
+ ...config,
7973
8066
  renderer: {
7974
8067
  container
7975
8068
  }
7976
8069
  };
7977
8070
  return createInjector([{
7978
- config: ['value', config]
8071
+ config: ['value', enrichedConfig]
7979
8072
  }, {
7980
8073
  form: ['value', this]
7981
8074
  }, core, ...modules, ...additionalModules]);
@@ -8222,9 +8315,9 @@ function createForm(options) {
8222
8315
  const {
8223
8316
  data,
8224
8317
  schema,
8225
- ...rest
8318
+ ...formOptions
8226
8319
  } = options;
8227
- const form = new Form(rest);
8320
+ const form = new Form(formOptions);
8228
8321
  return form.importSchema(schema, data).then(function () {
8229
8322
  return form;
8230
8323
  });