@bpmn-io/form-js-viewer 1.7.1 → 1.7.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 +112 -83
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +113 -84
- package/dist/index.es.js.map +1 -1
- package/dist/types/render/components/index.d.ts +1 -1
- package/dist/types/render/components/util/optionsUtil.d.ts +24 -8
- package/dist/types/render/hooks/index.d.ts +1 -1
- package/dist/types/render/hooks/useDeepCompareMemoize.d.ts +8 -0
- package/package.json +2 -2
- package/dist/types/render/hooks/useDeepCompareState.d.ts +0 -8
package/dist/index.es.js
CHANGED
|
@@ -3,7 +3,7 @@ import { isString, get, isNil, isObject, some, isNumber, set, findIndex, isArray
|
|
|
3
3
|
import Big from 'big.js';
|
|
4
4
|
import classNames from 'classnames';
|
|
5
5
|
import { jsx, jsxs, Fragment } from 'preact/jsx-runtime';
|
|
6
|
-
import { useContext, useMemo, useRef, useEffect, useState,
|
|
6
|
+
import { useContext, useMemo, useRef, useCallback, useEffect, useState, useLayoutEffect } from 'preact/hooks';
|
|
7
7
|
import { createContext, createElement, Fragment as Fragment$1, render } from 'preact';
|
|
8
8
|
import isEqual from 'lodash/isEqual';
|
|
9
9
|
import flatpickr from 'flatpickr';
|
|
@@ -772,20 +772,59 @@ function useCondition(condition) {
|
|
|
772
772
|
}, [conditionChecker, condition, expressionContextInfo]);
|
|
773
773
|
}
|
|
774
774
|
|
|
775
|
-
|
|
776
|
-
|
|
775
|
+
/**
|
|
776
|
+
* Returns the options data for the provided if they can be simply determined, ignoring expression defined options.
|
|
777
|
+
*
|
|
778
|
+
* @param {object} formField
|
|
779
|
+
* @param {object} formData
|
|
780
|
+
*/
|
|
781
|
+
function getSimpleOptionsData(formField, formData) {
|
|
777
782
|
const {
|
|
783
|
+
valuesExpression: optionsExpression,
|
|
778
784
|
valuesKey: optionsKey,
|
|
779
785
|
values: staticOptions
|
|
780
786
|
} = formField;
|
|
787
|
+
if (optionsExpression) {
|
|
788
|
+
return null;
|
|
789
|
+
}
|
|
781
790
|
return optionsKey ? get(formData, [optionsKey]) : staticOptions;
|
|
782
791
|
}
|
|
783
792
|
|
|
784
|
-
|
|
793
|
+
/**
|
|
794
|
+
* Normalizes the provided options data to a format that can be used by the select components.
|
|
795
|
+
* If the options data is not valid, it is filtered out.
|
|
796
|
+
*
|
|
797
|
+
* @param {any[]} optionsData
|
|
798
|
+
*
|
|
799
|
+
* @returns {object[]}
|
|
800
|
+
*/
|
|
785
801
|
function normalizeOptionsData(optionsData) {
|
|
786
802
|
return optionsData.filter(_isAllowedValue).map(_normalizeOption).filter(o => !isNil(o));
|
|
787
803
|
}
|
|
788
804
|
|
|
805
|
+
/**
|
|
806
|
+
* Creates an options object with default values if no options are provided.
|
|
807
|
+
*
|
|
808
|
+
* @param {object} options
|
|
809
|
+
*
|
|
810
|
+
* @returns {object}
|
|
811
|
+
*/
|
|
812
|
+
function createEmptyOptions(options = {}) {
|
|
813
|
+
const defaults = {};
|
|
814
|
+
|
|
815
|
+
// provide default options if valuesKey and valuesExpression are not set
|
|
816
|
+
if (!options.valuesKey && !options.valuesExpression) {
|
|
817
|
+
defaults.values = [{
|
|
818
|
+
label: 'Value',
|
|
819
|
+
value: 'value'
|
|
820
|
+
}];
|
|
821
|
+
}
|
|
822
|
+
return {
|
|
823
|
+
...defaults,
|
|
824
|
+
...options
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
|
|
789
828
|
/**
|
|
790
829
|
* Converts the provided option to a normalized format.
|
|
791
830
|
* If the option is not valid, null is returned.
|
|
@@ -836,21 +875,6 @@ function _isAllowedValue(value) {
|
|
|
836
875
|
}
|
|
837
876
|
return _isAllowedPrimitive(value);
|
|
838
877
|
}
|
|
839
|
-
function createEmptyOptions(options = {}) {
|
|
840
|
-
const defaults = {};
|
|
841
|
-
|
|
842
|
-
// provide default options if valuesKey and valuesExpression are not set
|
|
843
|
-
if (!options.valuesKey && !options.valuesExpression) {
|
|
844
|
-
defaults.values = [{
|
|
845
|
-
label: 'Value',
|
|
846
|
-
value: 'value'
|
|
847
|
-
}];
|
|
848
|
-
}
|
|
849
|
-
return {
|
|
850
|
-
...defaults,
|
|
851
|
-
...options
|
|
852
|
-
};
|
|
853
|
-
}
|
|
854
878
|
|
|
855
879
|
/**
|
|
856
880
|
* Evaluate a string reactively based on the expressionLanguage and form data.
|
|
@@ -871,29 +895,19 @@ function useExpressionEvaluation(value) {
|
|
|
871
895
|
}, [expressionLanguage, expressionContextInfo, value]);
|
|
872
896
|
}
|
|
873
897
|
|
|
874
|
-
function usePrevious(value, defaultValue = null) {
|
|
875
|
-
const ref = useRef(defaultValue);
|
|
876
|
-
useEffect(() => ref.current = value, [value]);
|
|
877
|
-
return ref.current;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
898
|
/**
|
|
881
899
|
* A custom hook to manage state changes with deep comparison.
|
|
882
900
|
*
|
|
883
|
-
* @
|
|
884
|
-
* @param {
|
|
885
|
-
* @returns {
|
|
901
|
+
* @template T
|
|
902
|
+
* @param {T} value - The current value to manage.
|
|
903
|
+
* @returns {T} - Returns the current state.
|
|
886
904
|
*/
|
|
887
|
-
function
|
|
888
|
-
const
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
setState(value);
|
|
894
|
-
}
|
|
895
|
-
}, [changed, value]);
|
|
896
|
-
return state;
|
|
905
|
+
function useDeepCompareMemoize(value) {
|
|
906
|
+
const ref = useRef();
|
|
907
|
+
if (!isEqual(value, ref.current)) {
|
|
908
|
+
ref.current = value;
|
|
909
|
+
}
|
|
910
|
+
return ref.current;
|
|
897
911
|
}
|
|
898
912
|
|
|
899
913
|
/**
|
|
@@ -923,15 +937,10 @@ function useOptionsAsync(field) {
|
|
|
923
937
|
valuesKey: optionsKey,
|
|
924
938
|
values: staticOptions
|
|
925
939
|
} = field;
|
|
926
|
-
const [optionsGetter, setOptionsGetter] = useState({
|
|
927
|
-
options: [],
|
|
928
|
-
error: undefined,
|
|
929
|
-
loadState: LOAD_STATES.LOADING
|
|
930
|
-
});
|
|
931
940
|
const initialData = useService('form')._getState().initialData;
|
|
932
941
|
const expressionEvaluation = useExpressionEvaluation(optionsExpression);
|
|
933
|
-
const evaluatedOptions =
|
|
934
|
-
|
|
942
|
+
const evaluatedOptions = useDeepCompareMemoize(expressionEvaluation || []);
|
|
943
|
+
const optionsGetter = useMemo(() => {
|
|
935
944
|
let options = [];
|
|
936
945
|
|
|
937
946
|
// dynamic options
|
|
@@ -946,18 +955,16 @@ function useOptionsAsync(field) {
|
|
|
946
955
|
options = Array.isArray(staticOptions) ? staticOptions : [];
|
|
947
956
|
|
|
948
957
|
// expression
|
|
949
|
-
} else if (optionsExpression) {
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
958
|
+
} else if (optionsExpression && evaluatedOptions && Array.isArray(evaluatedOptions)) {
|
|
959
|
+
options = evaluatedOptions;
|
|
960
|
+
|
|
961
|
+
// error case
|
|
953
962
|
} else {
|
|
954
|
-
|
|
955
|
-
return;
|
|
963
|
+
return buildErrorState('No options source defined in the form definition');
|
|
956
964
|
}
|
|
957
965
|
|
|
958
966
|
// normalize data to support primitives and partially defined objects
|
|
959
|
-
|
|
960
|
-
setOptionsGetter(buildLoadedState(options));
|
|
967
|
+
return buildLoadedState(normalizeOptionsData(options));
|
|
961
968
|
}, [optionsKey, staticOptions, initialData, optionsExpression, evaluatedOptions]);
|
|
962
969
|
return optionsGetter;
|
|
963
970
|
}
|
|
@@ -1002,14 +1009,14 @@ const getDOMPurifyConfig = sanitizeStyleTags => {
|
|
|
1002
1009
|
};
|
|
1003
1010
|
};
|
|
1004
1011
|
|
|
1005
|
-
/**
|
|
1006
|
-
* A custom hook to build up security attributes from form configuration.
|
|
1007
|
-
*
|
|
1008
|
-
* @param {Object} security - The security configuration.
|
|
1009
|
-
* @returns {Array} - Returns a tuple with sandbox and allow attributes.
|
|
1012
|
+
/**
|
|
1013
|
+
* A custom hook to build up security attributes from form configuration.
|
|
1014
|
+
*
|
|
1015
|
+
* @param {Object} security - The security configuration.
|
|
1016
|
+
* @returns {Array} - Returns a tuple with sandbox and allow attributes.
|
|
1010
1017
|
*/
|
|
1011
1018
|
function useSecurityAttributesMap(security) {
|
|
1012
|
-
const securityMemoized =
|
|
1019
|
+
const securityMemoized = useDeepCompareMemoize(security);
|
|
1013
1020
|
const sandbox = useMemo(() => SECURITY_ATTRIBUTES_DEFINITIONS.filter(({
|
|
1014
1021
|
attribute
|
|
1015
1022
|
}) => attribute === SANDBOX_ATTRIBUTE).filter(({
|
|
@@ -1181,6 +1188,12 @@ function useReadonly(formField, properties = {}) {
|
|
|
1181
1188
|
return readonly || false;
|
|
1182
1189
|
}
|
|
1183
1190
|
|
|
1191
|
+
function usePrevious(value, defaultValue = null) {
|
|
1192
|
+
const ref = useRef(defaultValue);
|
|
1193
|
+
useEffect(() => ref.current = value, [value]);
|
|
1194
|
+
return ref.current;
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1184
1197
|
function useFlushDebounce(func) {
|
|
1185
1198
|
const timeoutRef = useRef(null);
|
|
1186
1199
|
const lastArgsRef = useRef(null);
|
|
@@ -1445,8 +1458,16 @@ function sanitizeSingleSelectValue(options) {
|
|
|
1445
1458
|
data,
|
|
1446
1459
|
value
|
|
1447
1460
|
} = options;
|
|
1461
|
+
const {
|
|
1462
|
+
valuesExpression: optionsExpression
|
|
1463
|
+
} = formField;
|
|
1448
1464
|
try {
|
|
1449
|
-
|
|
1465
|
+
// if options are expression evaluated, we don't need to sanitize the value against the options
|
|
1466
|
+
// and defer to the field's internal validation
|
|
1467
|
+
if (optionsExpression) {
|
|
1468
|
+
return value;
|
|
1469
|
+
}
|
|
1470
|
+
const validValues = normalizeOptionsData(getSimpleOptionsData(formField, data)).map(v => v.value);
|
|
1450
1471
|
return hasEqualValue(value, validValues) ? value : null;
|
|
1451
1472
|
} catch (error) {
|
|
1452
1473
|
// use default value in case of formatting error
|
|
@@ -1460,8 +1481,16 @@ function sanitizeMultiSelectValue(options) {
|
|
|
1460
1481
|
data,
|
|
1461
1482
|
value
|
|
1462
1483
|
} = options;
|
|
1484
|
+
const {
|
|
1485
|
+
valuesExpression: optionsExpression
|
|
1486
|
+
} = formField;
|
|
1463
1487
|
try {
|
|
1464
|
-
|
|
1488
|
+
// if options are expression evaluated, we don't need to sanitize the values against the options
|
|
1489
|
+
// and defer to the field's internal validation
|
|
1490
|
+
if (optionsExpression) {
|
|
1491
|
+
return value;
|
|
1492
|
+
}
|
|
1493
|
+
const validValues = normalizeOptionsData(getSimpleOptionsData(formField, data)).map(v => v.value);
|
|
1465
1494
|
return value.filter(v => hasEqualValue(v, validValues));
|
|
1466
1495
|
} catch (error) {
|
|
1467
1496
|
// use default value in case of formatting error
|
|
@@ -1549,7 +1578,7 @@ function useCleanupMultiSelectValue(props) {
|
|
|
1549
1578
|
onChange,
|
|
1550
1579
|
values
|
|
1551
1580
|
} = props;
|
|
1552
|
-
const memoizedValues =
|
|
1581
|
+
const memoizedValues = useDeepCompareMemoize(values || []);
|
|
1553
1582
|
|
|
1554
1583
|
// ensures that the values are always a subset of the possible options
|
|
1555
1584
|
useEffect(() => {
|
|
@@ -2237,7 +2266,7 @@ function Datepicker(props) {
|
|
|
2237
2266
|
const [forceFocusCalendar, setForceFocusCalendar] = useState(false);
|
|
2238
2267
|
|
|
2239
2268
|
// ensures we render based on date value instead of reference
|
|
2240
|
-
const date =
|
|
2269
|
+
const date = useDeepCompareMemoize(dateObject);
|
|
2241
2270
|
|
|
2242
2271
|
// shorts the date value back to the source
|
|
2243
2272
|
useEffect(() => {
|
|
@@ -3673,8 +3702,8 @@ function Numberfield(props) {
|
|
|
3673
3702
|
'fjs-readonly': readonly
|
|
3674
3703
|
}),
|
|
3675
3704
|
children: [jsx("button", {
|
|
3676
|
-
class: "fjs-number-arrow-up",
|
|
3677
3705
|
type: "button",
|
|
3706
|
+
class: "fjs-number-arrow-up",
|
|
3678
3707
|
"aria-label": "Increment",
|
|
3679
3708
|
onClick: () => increment(),
|
|
3680
3709
|
tabIndex: -1,
|
|
@@ -3682,8 +3711,8 @@ function Numberfield(props) {
|
|
|
3682
3711
|
}), jsx("div", {
|
|
3683
3712
|
class: "fjs-number-arrow-separator"
|
|
3684
3713
|
}), jsx("button", {
|
|
3685
|
-
class: "fjs-number-arrow-down",
|
|
3686
3714
|
type: "button",
|
|
3715
|
+
class: "fjs-number-arrow-down",
|
|
3687
3716
|
"aria-label": "Decrement",
|
|
3688
3717
|
onClick: () => decrement(),
|
|
3689
3718
|
tabIndex: -1,
|
|
@@ -4354,7 +4383,7 @@ function Taglist(props) {
|
|
|
4354
4383
|
} = useOptionsAsync(field);
|
|
4355
4384
|
|
|
4356
4385
|
// ensures we render based on array content instead of reference
|
|
4357
|
-
const values =
|
|
4386
|
+
const values = useDeepCompareMemoize(value || []);
|
|
4358
4387
|
useCleanupMultiSelectValue({
|
|
4359
4388
|
field,
|
|
4360
4389
|
loadState,
|
|
@@ -6595,9 +6624,9 @@ class RepeatRenderManager {
|
|
|
6595
6624
|
};
|
|
6596
6625
|
const parentExpressionContextInfo = useContext(LocalExpressionContext);
|
|
6597
6626
|
return jsx(Fragment, {
|
|
6598
|
-
children: displayValues.map((
|
|
6599
|
-
|
|
6600
|
-
|
|
6627
|
+
children: displayValues.map((itemValue, itemIndex) => jsx(RepetitionScaffold, {
|
|
6628
|
+
itemIndex: itemIndex,
|
|
6629
|
+
itemValue: itemValue,
|
|
6601
6630
|
parentExpressionContextInfo: parentExpressionContextInfo,
|
|
6602
6631
|
repeaterField: repeaterField,
|
|
6603
6632
|
RowsRenderer: RowsRenderer,
|
|
@@ -6605,7 +6634,7 @@ class RepeatRenderManager {
|
|
|
6605
6634
|
onDeleteItem: onDeleteItem,
|
|
6606
6635
|
showRemove: showRemove,
|
|
6607
6636
|
...restProps
|
|
6608
|
-
},
|
|
6637
|
+
}, itemIndex))
|
|
6609
6638
|
});
|
|
6610
6639
|
}
|
|
6611
6640
|
RepeatFooter(props) {
|
|
@@ -6668,18 +6697,18 @@ class RepeatRenderManager {
|
|
|
6668
6697
|
'fjs-remove-allowed': repeaterField.allowAddRemove
|
|
6669
6698
|
}),
|
|
6670
6699
|
children: [showAdd ? jsx("button", {
|
|
6700
|
+
type: "button",
|
|
6671
6701
|
readOnly: readonly,
|
|
6672
6702
|
disabled: disabled || readonly,
|
|
6673
6703
|
class: "fjs-repeat-render-add",
|
|
6674
|
-
type: "button",
|
|
6675
6704
|
ref: addButtonRef,
|
|
6676
6705
|
onClick: onAddItem,
|
|
6677
6706
|
children: jsxs(Fragment, {
|
|
6678
6707
|
children: [jsx(AddSvg, {}), " ", 'Add new']
|
|
6679
6708
|
})
|
|
6680
6709
|
}) : null, collapseEnabled ? jsx("button", {
|
|
6681
|
-
class: "fjs-repeat-render-collapse",
|
|
6682
6710
|
type: "button",
|
|
6711
|
+
class: "fjs-repeat-render-collapse",
|
|
6683
6712
|
onClick: toggle,
|
|
6684
6713
|
children: isCollapsed ? jsxs(Fragment, {
|
|
6685
6714
|
children: [jsx(ExpandSvg, {}), " ", `Expand all (${values.length})`]
|
|
@@ -6702,8 +6731,8 @@ class RepeatRenderManager {
|
|
|
6702
6731
|
* Individual repetition of a repeated field and context scaffolding.
|
|
6703
6732
|
*
|
|
6704
6733
|
* @param {Object} props
|
|
6705
|
-
* @param {number} props.
|
|
6706
|
-
* @param {Object} props.
|
|
6734
|
+
* @param {number} props.itemIndex
|
|
6735
|
+
* @param {Object} props.itemValue
|
|
6707
6736
|
* @param {Object} props.parentExpressionContextInfo
|
|
6708
6737
|
* @param {Object} props.repeaterField
|
|
6709
6738
|
* @param {Function} props.RowsRenderer
|
|
@@ -6714,8 +6743,8 @@ class RepeatRenderManager {
|
|
|
6714
6743
|
|
|
6715
6744
|
const RepetitionScaffold = props => {
|
|
6716
6745
|
const {
|
|
6717
|
-
|
|
6718
|
-
|
|
6746
|
+
itemIndex,
|
|
6747
|
+
itemValue,
|
|
6719
6748
|
parentExpressionContextInfo,
|
|
6720
6749
|
repeaterField,
|
|
6721
6750
|
RowsRenderer,
|
|
@@ -6728,15 +6757,15 @@ const RepetitionScaffold = props => {
|
|
|
6728
6757
|
...restProps,
|
|
6729
6758
|
indexes: {
|
|
6730
6759
|
...(indexes || {}),
|
|
6731
|
-
[repeaterField.id]:
|
|
6760
|
+
[repeaterField.id]: itemIndex
|
|
6732
6761
|
}
|
|
6733
|
-
}), [
|
|
6762
|
+
}), [itemIndex, indexes, repeaterField.id, restProps]);
|
|
6734
6763
|
const localExpressionContextInfo = useMemo(() => ({
|
|
6735
6764
|
data: parentExpressionContextInfo.data,
|
|
6736
|
-
this:
|
|
6765
|
+
this: itemValue,
|
|
6737
6766
|
parent: buildExpressionContext(parentExpressionContextInfo),
|
|
6738
|
-
i: [...parentExpressionContextInfo.i,
|
|
6739
|
-
}), [
|
|
6767
|
+
i: [...parentExpressionContextInfo.i, itemIndex + 1]
|
|
6768
|
+
}), [itemIndex, parentExpressionContextInfo, itemValue]);
|
|
6740
6769
|
return !showRemove ? jsx(LocalExpressionContext.Provider, {
|
|
6741
6770
|
value: localExpressionContextInfo,
|
|
6742
6771
|
children: jsx(RowsRenderer, {
|
|
@@ -6753,10 +6782,10 @@ const RepetitionScaffold = props => {
|
|
|
6753
6782
|
})
|
|
6754
6783
|
})
|
|
6755
6784
|
}), jsx("button", {
|
|
6756
|
-
class: "fjs-repeat-row-remove",
|
|
6757
6785
|
type: "button",
|
|
6758
|
-
"
|
|
6759
|
-
|
|
6786
|
+
class: "fjs-repeat-row-remove",
|
|
6787
|
+
"aria-label": `Remove list item ${itemIndex + 1}`,
|
|
6788
|
+
onClick: () => onDeleteItem(itemIndex),
|
|
6760
6789
|
children: jsx("div", {
|
|
6761
6790
|
class: "fjs-repeat-row-remove-icon-container",
|
|
6762
6791
|
children: jsx(DeleteSvg, {})
|