@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 +193 -109
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +194 -110
- package/dist/index.es.js.map +1 -1
- package/dist/types/render/components/util/sanitizerUtil.d.ts +1 -0
- package/dist/types/render/hooks/useFlushDebounce.d.ts +2 -0
- package/dist/types/render/hooks/useGetLabelCorrelation.d.ts +5 -0
- package/package.json +3 -2
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,
|
|
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(
|
|
1088
|
+
return optionsData.filter(_isAllowedValue).map(_normalizeOption).filter(o => !isNil(o));
|
|
1088
1089
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
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:
|
|
1094
|
-
label: `${
|
|
1105
|
+
value: option,
|
|
1106
|
+
label: `${option}`
|
|
1095
1107
|
};
|
|
1096
1108
|
}
|
|
1097
|
-
if (
|
|
1098
|
-
|
|
1099
|
-
|
|
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:
|
|
1102
|
-
label: `${
|
|
1115
|
+
value: option.value,
|
|
1116
|
+
label: `${option.value}`
|
|
1103
1117
|
};
|
|
1104
1118
|
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
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
|
|
1113
|
-
|
|
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
|
|
1116
|
-
return
|
|
1132
|
+
function _isValidLabel(label) {
|
|
1133
|
+
return label && isString(label);
|
|
1117
1134
|
}
|
|
1118
|
-
function
|
|
1119
|
-
|
|
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
|
|
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 =>
|
|
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 =
|
|
1466
|
-
|
|
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:
|
|
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':
|
|
1539
|
+
'fjs-checked': isChecked
|
|
1517
1540
|
}),
|
|
1518
1541
|
required: false,
|
|
1519
1542
|
children: jsx("input", {
|
|
1520
|
-
checked:
|
|
1543
|
+
checked: isChecked,
|
|
1521
1544
|
class: "fjs-input",
|
|
1522
1545
|
disabled: disabled,
|
|
1523
1546
|
readOnly: readonly,
|
|
@@ -3279,8 +3302,8 @@ function Numberfield(props) {
|
|
|
3279
3302
|
id: domId,
|
|
3280
3303
|
onKeyDown: onKeyDown,
|
|
3281
3304
|
onKeyPress: onKeyPress,
|
|
3282
|
-
onBlur:
|
|
3283
|
-
onFocus:
|
|
3305
|
+
onBlur: onBlur,
|
|
3306
|
+
onFocus: onFocus
|
|
3284
3307
|
|
|
3285
3308
|
// @ts-ignore
|
|
3286
3309
|
,
|
|
@@ -3357,7 +3380,8 @@ function useCleanupSingleSelectValue (props) {
|
|
|
3357
3380
|
if (loadState !== LOAD_STATES.LOADED) {
|
|
3358
3381
|
return;
|
|
3359
3382
|
}
|
|
3360
|
-
const
|
|
3383
|
+
const optionValues = options.map(o => o.value);
|
|
3384
|
+
const hasValueNotInOptions = value && !hasEqualValue(value, optionValues);
|
|
3361
3385
|
if (hasValueNotInOptions) {
|
|
3362
3386
|
onChange({
|
|
3363
3387
|
field,
|
|
@@ -3430,15 +3454,16 @@ function Radio(props) {
|
|
|
3430
3454
|
required: required
|
|
3431
3455
|
}), loadState == LOAD_STATES.LOADED && options.map((option, index) => {
|
|
3432
3456
|
const itemDomId = `${domId}-${index}`;
|
|
3457
|
+
const isChecked = isEqual(option.value, value);
|
|
3433
3458
|
return jsx(Label, {
|
|
3434
3459
|
id: itemDomId,
|
|
3435
3460
|
label: option.label,
|
|
3436
3461
|
class: classNames({
|
|
3437
|
-
'fjs-checked':
|
|
3462
|
+
'fjs-checked': isChecked
|
|
3438
3463
|
}),
|
|
3439
3464
|
required: false,
|
|
3440
3465
|
children: jsx("input", {
|
|
3441
|
-
checked:
|
|
3466
|
+
checked: isChecked,
|
|
3442
3467
|
class: "fjs-input",
|
|
3443
3468
|
disabled: disabled,
|
|
3444
3469
|
readOnly: readonly,
|
|
@@ -3468,6 +3493,21 @@ Radio.config = {
|
|
|
3468
3493
|
create: createEmptyOptions
|
|
3469
3494
|
};
|
|
3470
3495
|
|
|
3496
|
+
/**
|
|
3497
|
+
* This hook allows us to retrieve the label from a value in linear time by caching it in a map
|
|
3498
|
+
* @param {Array} options
|
|
3499
|
+
*/
|
|
3500
|
+
function useGetLabelCorrelation(options) {
|
|
3501
|
+
// This allows us to retrieve the label from a value in linear time
|
|
3502
|
+
const labelMap = useMemo(() => Object.assign({}, ...options.map(o => ({
|
|
3503
|
+
[_getValueHash(o.value)]: o.label
|
|
3504
|
+
}))), [options]);
|
|
3505
|
+
return useCallback(value => labelMap[_getValueHash(value)], [labelMap]);
|
|
3506
|
+
}
|
|
3507
|
+
const _getValueHash = value => {
|
|
3508
|
+
return isObject(value) ? JSON.stringify(value) : value;
|
|
3509
|
+
};
|
|
3510
|
+
|
|
3471
3511
|
var _path$q;
|
|
3472
3512
|
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
3513
|
var SvgXMark = function SvgXMark(props) {
|
|
@@ -3499,7 +3539,7 @@ function SearchableSelect(props) {
|
|
|
3499
3539
|
} = props;
|
|
3500
3540
|
const [filter, setFilter] = useState('');
|
|
3501
3541
|
const [isDropdownExpanded, setIsDropdownExpanded] = useState(false);
|
|
3502
|
-
const [
|
|
3542
|
+
const [isFilterActive, setIsFilterActive] = useState(true);
|
|
3503
3543
|
const [isEscapeClosed, setIsEscapeClose] = useState(false);
|
|
3504
3544
|
const searchbarRef = useRef();
|
|
3505
3545
|
const eventBus = useService('eventBus');
|
|
@@ -3514,23 +3554,22 @@ function SearchableSelect(props) {
|
|
|
3514
3554
|
value,
|
|
3515
3555
|
onChange: props.onChange
|
|
3516
3556
|
});
|
|
3517
|
-
|
|
3518
|
-
|
|
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]);
|
|
3557
|
+
const getLabelCorrelation = useGetLabelCorrelation(options);
|
|
3558
|
+
const label = useMemo(() => value && getLabelCorrelation(value), [value, getLabelCorrelation]);
|
|
3523
3559
|
|
|
3524
3560
|
// whenever we change the underlying value, set the label to it
|
|
3525
3561
|
useEffect(() => {
|
|
3526
|
-
setFilter(
|
|
3527
|
-
}, [
|
|
3562
|
+
setFilter(label);
|
|
3563
|
+
}, [label]);
|
|
3528
3564
|
const filteredOptions = useMemo(() => {
|
|
3529
|
-
if (loadState
|
|
3530
|
-
return
|
|
3565
|
+
if (loadState !== LOAD_STATES.LOADED) {
|
|
3566
|
+
return [];
|
|
3531
3567
|
}
|
|
3532
|
-
|
|
3533
|
-
|
|
3568
|
+
if (!filter || !isFilterActive) {
|
|
3569
|
+
return options;
|
|
3570
|
+
}
|
|
3571
|
+
return options.filter(o => o.label && o.value && o.label.toLowerCase().includes(filter.toLowerCase()));
|
|
3572
|
+
}, [filter, loadState, options, isFilterActive]);
|
|
3534
3573
|
const setValue = useCallback(option => {
|
|
3535
3574
|
setFilter(option && option.label || '');
|
|
3536
3575
|
props.onChange({
|
|
@@ -3557,7 +3596,7 @@ function SearchableSelect(props) {
|
|
|
3557
3596
|
}) => {
|
|
3558
3597
|
setIsEscapeClose(false);
|
|
3559
3598
|
setIsDropdownExpanded(true);
|
|
3560
|
-
|
|
3599
|
+
setIsFilterActive(true);
|
|
3561
3600
|
setFilter(target.value || '');
|
|
3562
3601
|
eventBus.fire('formField.search', {
|
|
3563
3602
|
formField: field,
|
|
@@ -3573,7 +3612,7 @@ function SearchableSelect(props) {
|
|
|
3573
3612
|
{
|
|
3574
3613
|
if (!isDropdownExpanded) {
|
|
3575
3614
|
setIsDropdownExpanded(true);
|
|
3576
|
-
|
|
3615
|
+
setIsFilterActive(false);
|
|
3577
3616
|
}
|
|
3578
3617
|
keyDownEvent.preventDefault();
|
|
3579
3618
|
break;
|
|
@@ -3591,7 +3630,7 @@ function SearchableSelect(props) {
|
|
|
3591
3630
|
const onInputMouseDown = useCallback(() => {
|
|
3592
3631
|
setIsEscapeClose(false);
|
|
3593
3632
|
setIsDropdownExpanded(true);
|
|
3594
|
-
|
|
3633
|
+
setIsFilterActive(false);
|
|
3595
3634
|
}, []);
|
|
3596
3635
|
const onInputFocus = useCallback(() => {
|
|
3597
3636
|
setIsEscapeClose(false);
|
|
@@ -3600,9 +3639,9 @@ function SearchableSelect(props) {
|
|
|
3600
3639
|
}, [onFocus]);
|
|
3601
3640
|
const onInputBlur = useCallback(() => {
|
|
3602
3641
|
setIsDropdownExpanded(false);
|
|
3603
|
-
setFilter(
|
|
3642
|
+
setFilter(label);
|
|
3604
3643
|
onBlur && onBlur();
|
|
3605
|
-
}, [onBlur,
|
|
3644
|
+
}, [onBlur, label]);
|
|
3606
3645
|
return jsxs(Fragment, {
|
|
3607
3646
|
children: [jsxs("div", {
|
|
3608
3647
|
class: classNames('fjs-input-group', {
|
|
@@ -3679,12 +3718,8 @@ function SimpleSelect(props) {
|
|
|
3679
3718
|
value,
|
|
3680
3719
|
onChange: props.onChange
|
|
3681
3720
|
});
|
|
3682
|
-
|
|
3683
|
-
|
|
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]);
|
|
3721
|
+
const getLabelCorrelation = useGetLabelCorrelation(options);
|
|
3722
|
+
const valueLabel = useMemo(() => value && getLabelCorrelation(value), [value, getLabelCorrelation]);
|
|
3688
3723
|
const setValue = useCallback(option => {
|
|
3689
3724
|
props.onChange({
|
|
3690
3725
|
value: option && option.value || null,
|
|
@@ -3999,11 +4034,7 @@ function Taglist(props) {
|
|
|
3999
4034
|
values,
|
|
4000
4035
|
onChange: props.onChange
|
|
4001
4036
|
});
|
|
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]);
|
|
4037
|
+
const getLabelCorrelation = useGetLabelCorrelation(options);
|
|
4007
4038
|
const hasOptionsLeft = useMemo(() => options.length > values.length, [options.length, values.length]);
|
|
4008
4039
|
|
|
4009
4040
|
// 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 +4042,14 @@ function Taglist(props) {
|
|
|
4011
4042
|
if (loadState !== LOAD_STATES.LOADED) {
|
|
4012
4043
|
return [];
|
|
4013
4044
|
}
|
|
4014
|
-
|
|
4045
|
+
const isValidFilteredOption = option => {
|
|
4046
|
+
const filterMatches = option.label.toLowerCase().includes(filter.toLowerCase());
|
|
4047
|
+
return filterMatches && !hasEqualValue(option.value, values);
|
|
4048
|
+
};
|
|
4049
|
+
return options.filter(isValidFilteredOption);
|
|
4015
4050
|
}, [filter, options, JSON.stringify(values), loadState]);
|
|
4016
4051
|
const selectValue = value => {
|
|
4017
|
-
|
|
4018
|
-
setFilter('');
|
|
4019
|
-
}
|
|
4052
|
+
setFilter('');
|
|
4020
4053
|
|
|
4021
4054
|
// Ensure values cannot be double selected due to latency
|
|
4022
4055
|
if (values.at(-1) === value) {
|
|
@@ -4028,8 +4061,9 @@ function Taglist(props) {
|
|
|
4028
4061
|
});
|
|
4029
4062
|
};
|
|
4030
4063
|
const deselectValue = value => {
|
|
4064
|
+
const newValues = values.filter(v => !isEqual(v, value));
|
|
4031
4065
|
props.onChange({
|
|
4032
|
-
value:
|
|
4066
|
+
value: newValues,
|
|
4033
4067
|
field
|
|
4034
4068
|
});
|
|
4035
4069
|
};
|
|
@@ -4139,7 +4173,7 @@ function Taglist(props) {
|
|
|
4139
4173
|
onMouseDown: e => e.preventDefault(),
|
|
4140
4174
|
children: [jsx("span", {
|
|
4141
4175
|
class: "fjs-taglist-tag-label",
|
|
4142
|
-
children:
|
|
4176
|
+
children: getLabelCorrelation(v)
|
|
4143
4177
|
}), !disabled && !readonly && jsx("button", {
|
|
4144
4178
|
type: "button",
|
|
4145
4179
|
title: "Remove tag",
|
|
@@ -4279,6 +4313,40 @@ function DisabledLink({
|
|
|
4279
4313
|
});
|
|
4280
4314
|
}
|
|
4281
4315
|
|
|
4316
|
+
function useFlushDebounce(func, additionalDeps = []) {
|
|
4317
|
+
const timeoutRef = useRef(null);
|
|
4318
|
+
const lastArgsRef = useRef(null);
|
|
4319
|
+
const config = useService('config', false);
|
|
4320
|
+
const debounce = config && config.debounce;
|
|
4321
|
+
const shouldDebounce = debounce !== false && debounce !== 0;
|
|
4322
|
+
const delay = typeof debounce === 'number' ? debounce : 300;
|
|
4323
|
+
const debounceFunc = useCallback((...args) => {
|
|
4324
|
+
if (!shouldDebounce) {
|
|
4325
|
+
func(...args);
|
|
4326
|
+
return;
|
|
4327
|
+
}
|
|
4328
|
+
lastArgsRef.current = args;
|
|
4329
|
+
if (timeoutRef.current) {
|
|
4330
|
+
clearTimeout(timeoutRef.current);
|
|
4331
|
+
}
|
|
4332
|
+
timeoutRef.current = setTimeout(() => {
|
|
4333
|
+
func(...lastArgsRef.current);
|
|
4334
|
+
lastArgsRef.current = null;
|
|
4335
|
+
}, delay);
|
|
4336
|
+
}, [func, delay, shouldDebounce, ...additionalDeps]);
|
|
4337
|
+
const flushFunc = useCallback(() => {
|
|
4338
|
+
if (timeoutRef.current) {
|
|
4339
|
+
clearTimeout(timeoutRef.current);
|
|
4340
|
+
if (lastArgsRef.current !== null) {
|
|
4341
|
+
func(...lastArgsRef.current);
|
|
4342
|
+
lastArgsRef.current = null;
|
|
4343
|
+
}
|
|
4344
|
+
timeoutRef.current = null;
|
|
4345
|
+
}
|
|
4346
|
+
}, [func, ...additionalDeps]);
|
|
4347
|
+
return [debounceFunc, flushFunc];
|
|
4348
|
+
}
|
|
4349
|
+
|
|
4282
4350
|
const type$2 = 'textfield';
|
|
4283
4351
|
function Textfield(props) {
|
|
4284
4352
|
const {
|
|
@@ -4305,13 +4373,20 @@ function Textfield(props) {
|
|
|
4305
4373
|
const {
|
|
4306
4374
|
required
|
|
4307
4375
|
} = validate;
|
|
4308
|
-
const
|
|
4376
|
+
const [onInputChange, flushOnChange] = useFlushDebounce(({
|
|
4309
4377
|
target
|
|
4310
4378
|
}) => {
|
|
4311
4379
|
props.onChange({
|
|
4312
4380
|
field,
|
|
4313
4381
|
value: target.value
|
|
4314
4382
|
});
|
|
4383
|
+
}, [props.onChange]);
|
|
4384
|
+
const onInputBlur = () => {
|
|
4385
|
+
flushOnChange && flushOnChange();
|
|
4386
|
+
onBlur && onBlur();
|
|
4387
|
+
};
|
|
4388
|
+
const onInputFocus = () => {
|
|
4389
|
+
onFocus && onFocus();
|
|
4315
4390
|
};
|
|
4316
4391
|
return jsxs("div", {
|
|
4317
4392
|
class: formFieldClasses(type$2, {
|
|
@@ -4333,9 +4408,9 @@ function Textfield(props) {
|
|
|
4333
4408
|
disabled: disabled,
|
|
4334
4409
|
readOnly: readonly,
|
|
4335
4410
|
id: domId,
|
|
4336
|
-
onInput:
|
|
4337
|
-
onBlur:
|
|
4338
|
-
onFocus:
|
|
4411
|
+
onInput: onInputChange,
|
|
4412
|
+
onBlur: onInputBlur,
|
|
4413
|
+
onFocus: onInputFocus,
|
|
4339
4414
|
type: "text",
|
|
4340
4415
|
value: value,
|
|
4341
4416
|
"aria-describedby": errorMessageId
|
|
@@ -4394,13 +4469,20 @@ function Textarea(props) {
|
|
|
4394
4469
|
required
|
|
4395
4470
|
} = validate;
|
|
4396
4471
|
const textareaRef = useRef();
|
|
4397
|
-
const
|
|
4472
|
+
const [onInputChange, flushOnChange] = useFlushDebounce(({
|
|
4398
4473
|
target
|
|
4399
4474
|
}) => {
|
|
4400
4475
|
props.onChange({
|
|
4401
4476
|
field,
|
|
4402
4477
|
value: target.value
|
|
4403
4478
|
});
|
|
4479
|
+
}, [props.onChange]);
|
|
4480
|
+
const onInputBlur = () => {
|
|
4481
|
+
flushOnChange && flushOnChange();
|
|
4482
|
+
onBlur && onBlur();
|
|
4483
|
+
};
|
|
4484
|
+
const onInputFocus = () => {
|
|
4485
|
+
onFocus && onFocus();
|
|
4404
4486
|
};
|
|
4405
4487
|
useLayoutEffect(() => {
|
|
4406
4488
|
autoSizeTextarea(textareaRef.current);
|
|
@@ -4423,9 +4505,9 @@ function Textarea(props) {
|
|
|
4423
4505
|
disabled: disabled,
|
|
4424
4506
|
readonly: readonly,
|
|
4425
4507
|
id: domId,
|
|
4426
|
-
onInput:
|
|
4427
|
-
onBlur:
|
|
4428
|
-
onFocus:
|
|
4508
|
+
onInput: onInputChange,
|
|
4509
|
+
onBlur: onInputBlur,
|
|
4510
|
+
onFocus: onInputFocus,
|
|
4429
4511
|
value: value,
|
|
4430
4512
|
ref: textareaRef,
|
|
4431
4513
|
"aria-describedby": errorMessageId
|
|
@@ -7966,16 +8048,18 @@ class Form {
|
|
|
7966
8048
|
*/
|
|
7967
8049
|
_createInjector(options, container) {
|
|
7968
8050
|
const {
|
|
8051
|
+
modules = this._getModules(),
|
|
7969
8052
|
additionalModules = [],
|
|
7970
|
-
|
|
8053
|
+
...config
|
|
7971
8054
|
} = options;
|
|
7972
|
-
const
|
|
8055
|
+
const enrichedConfig = {
|
|
8056
|
+
...config,
|
|
7973
8057
|
renderer: {
|
|
7974
8058
|
container
|
|
7975
8059
|
}
|
|
7976
8060
|
};
|
|
7977
8061
|
return createInjector([{
|
|
7978
|
-
config: ['value',
|
|
8062
|
+
config: ['value', enrichedConfig]
|
|
7979
8063
|
}, {
|
|
7980
8064
|
form: ['value', this]
|
|
7981
8065
|
}, core, ...modules, ...additionalModules]);
|
|
@@ -8222,9 +8306,9 @@ function createForm(options) {
|
|
|
8222
8306
|
const {
|
|
8223
8307
|
data,
|
|
8224
8308
|
schema,
|
|
8225
|
-
...
|
|
8309
|
+
...formOptions
|
|
8226
8310
|
} = options;
|
|
8227
|
-
const form = new Form(
|
|
8311
|
+
const form = new Form(formOptions);
|
|
8228
8312
|
return form.importSchema(schema, data).then(function () {
|
|
8229
8313
|
return form;
|
|
8230
8314
|
});
|