@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.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(
|
|
1108
|
+
return optionsData.filter(_isAllowedValue).map(_normalizeOption).filter(o => !minDash.isNil(o));
|
|
1108
1109
|
}
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
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:
|
|
1114
|
-
label: `${
|
|
1125
|
+
value: option,
|
|
1126
|
+
label: `${option}`
|
|
1115
1127
|
};
|
|
1116
1128
|
}
|
|
1117
|
-
if (
|
|
1118
|
-
|
|
1119
|
-
|
|
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:
|
|
1122
|
-
label: `${
|
|
1135
|
+
value: option.value,
|
|
1136
|
+
label: `${option.value}`
|
|
1123
1137
|
};
|
|
1124
1138
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
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
|
|
1133
|
-
|
|
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
|
|
1136
|
-
return
|
|
1152
|
+
function _isValidLabel(label) {
|
|
1153
|
+
return label && minDash.isString(label);
|
|
1137
1154
|
}
|
|
1138
|
-
function
|
|
1139
|
-
|
|
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
|
|
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 =>
|
|
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 =
|
|
1486
|
-
|
|
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:
|
|
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':
|
|
1559
|
+
'fjs-checked': isChecked
|
|
1537
1560
|
}),
|
|
1538
1561
|
required: false,
|
|
1539
1562
|
children: jsxRuntime.jsx("input", {
|
|
1540
|
-
checked:
|
|
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:
|
|
3303
|
-
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
|
|
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':
|
|
3482
|
+
'fjs-checked': isChecked
|
|
3458
3483
|
}),
|
|
3459
3484
|
required: false,
|
|
3460
3485
|
children: jsxRuntime.jsx("input", {
|
|
3461
|
-
checked:
|
|
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 [
|
|
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
|
-
|
|
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(
|
|
3547
|
-
}, [
|
|
3582
|
+
setFilter(label);
|
|
3583
|
+
}, [label]);
|
|
3548
3584
|
const filteredOptions = hooks.useMemo(() => {
|
|
3549
|
-
if (loadState
|
|
3550
|
-
return
|
|
3585
|
+
if (loadState !== LOAD_STATES.LOADED) {
|
|
3586
|
+
return [];
|
|
3551
3587
|
}
|
|
3552
|
-
|
|
3553
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
3662
|
+
setFilter(label);
|
|
3624
3663
|
onBlur && onBlur();
|
|
3625
|
-
}, [onBlur,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
4357
|
-
onBlur:
|
|
4358
|
-
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
|
|
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:
|
|
4447
|
-
onBlur:
|
|
4448
|
-
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
|
-
|
|
8073
|
+
...config
|
|
7991
8074
|
} = options;
|
|
7992
|
-
const
|
|
8075
|
+
const enrichedConfig = {
|
|
8076
|
+
...config,
|
|
7993
8077
|
renderer: {
|
|
7994
8078
|
container
|
|
7995
8079
|
}
|
|
7996
8080
|
};
|
|
7997
8081
|
return createInjector([{
|
|
7998
|
-
config: ['value',
|
|
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
|
-
...
|
|
8329
|
+
...formOptions
|
|
8246
8330
|
} = options;
|
|
8247
|
-
const form = new Form(
|
|
8331
|
+
const form = new Form(formOptions);
|
|
8248
8332
|
return form.importSchema(schema, data).then(function () {
|
|
8249
8333
|
return form;
|
|
8250
8334
|
});
|