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