@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 +208 -115
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +209 -116
- 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,
|
|
@@ -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
|
-
|
|
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
|
-
|
|
3253
|
+
onChangeDebounced({
|
|
3188
3254
|
field,
|
|
3189
3255
|
value: 'NaN'
|
|
3190
3256
|
});
|
|
3191
3257
|
return;
|
|
3192
3258
|
}
|
|
3193
3259
|
setStringValueCache(stringValue);
|
|
3194
|
-
|
|
3260
|
+
onChangeDebounced({
|
|
3195
3261
|
field,
|
|
3196
3262
|
value: serializeToString ? stringValue : Number(stringValue)
|
|
3197
3263
|
});
|
|
3198
|
-
}, [field,
|
|
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:
|
|
3283
|
-
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
|
|
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':
|
|
3505
|
+
'fjs-checked': isChecked
|
|
3438
3506
|
}),
|
|
3439
3507
|
required: false,
|
|
3440
3508
|
children: jsx("input", {
|
|
3441
|
-
checked:
|
|
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 [
|
|
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
|
-
|
|
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(
|
|
3527
|
-
}, [
|
|
3605
|
+
setFilter(label);
|
|
3606
|
+
}, [label]);
|
|
3528
3607
|
const filteredOptions = useMemo(() => {
|
|
3529
|
-
if (loadState
|
|
3530
|
-
return
|
|
3608
|
+
if (loadState !== LOAD_STATES.LOADED) {
|
|
3609
|
+
return [];
|
|
3531
3610
|
}
|
|
3532
|
-
|
|
3533
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
3685
|
+
setFilter(label);
|
|
3604
3686
|
onBlur && onBlur();
|
|
3605
|
-
}, [onBlur,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
4337
|
-
onBlur:
|
|
4338
|
-
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
|
|
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:
|
|
4427
|
-
onBlur:
|
|
4428
|
-
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
|
-
|
|
8062
|
+
...config
|
|
7971
8063
|
} = options;
|
|
7972
|
-
const
|
|
8064
|
+
const enrichedConfig = {
|
|
8065
|
+
...config,
|
|
7973
8066
|
renderer: {
|
|
7974
8067
|
container
|
|
7975
8068
|
}
|
|
7976
8069
|
};
|
|
7977
8070
|
return createInjector([{
|
|
7978
|
-
config: ['value',
|
|
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
|
-
...
|
|
8318
|
+
...formOptions
|
|
8226
8319
|
} = options;
|
|
8227
|
-
const form = new Form(
|
|
8320
|
+
const form = new Form(formOptions);
|
|
8228
8321
|
return form.importSchema(schema, data).then(function () {
|
|
8229
8322
|
return form;
|
|
8230
8323
|
});
|