@bpmn-io/form-js-viewer 1.8.4 → 1.8.5
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 +244 -136
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +243 -137
- package/dist/index.es.js.map +1 -1
- package/dist/types/Form.d.ts +2 -3
- package/dist/types/core/Validator.d.ts +16 -1
- package/dist/types/features/viewerCommands/ViewerCommands.d.ts +6 -0
- package/dist/types/features/viewerCommands/cmd/UpdateFieldInstanceValidationHandler.d.ts +10 -0
- package/dist/types/features/viewerCommands/cmd/UpdateFieldValidationHandler.d.ts +3 -0
- package/dist/types/util/expressions.d.ts +17 -0
- package/dist/types/util/index.d.ts +1 -0
- package/dist/types/util/simple.d.ts +1 -7
- package/package.json +2 -2
package/dist/index.es.js
CHANGED
|
@@ -721,6 +721,20 @@ function generateIdForType(type) {
|
|
|
721
721
|
function clone(data, replacer) {
|
|
722
722
|
return JSON.parse(JSON.stringify(data, replacer));
|
|
723
723
|
}
|
|
724
|
+
function runRecursively(formField, fn) {
|
|
725
|
+
const components = formField.components || [];
|
|
726
|
+
components.forEach((component, _) => {
|
|
727
|
+
runRecursively(component, fn);
|
|
728
|
+
});
|
|
729
|
+
fn(formField);
|
|
730
|
+
}
|
|
731
|
+
function wrapObjectKeysWithUnderscores(obj) {
|
|
732
|
+
const newObj = {};
|
|
733
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
734
|
+
newObj[`_${key}_`] = value;
|
|
735
|
+
}
|
|
736
|
+
return newObj;
|
|
737
|
+
}
|
|
724
738
|
|
|
725
739
|
/**
|
|
726
740
|
* Transform a LocalExpressionContext object into a usable FEEL context.
|
|
@@ -737,25 +751,24 @@ function buildExpressionContext(context) {
|
|
|
737
751
|
return {
|
|
738
752
|
...specialContextKeys,
|
|
739
753
|
...data,
|
|
740
|
-
...
|
|
754
|
+
...wrapObjectKeysWithUnderscores(specialContextKeys)
|
|
741
755
|
};
|
|
742
756
|
}
|
|
743
|
-
function runRecursively(formField, fn) {
|
|
744
|
-
const components = formField.components || [];
|
|
745
|
-
components.forEach((component, index) => {
|
|
746
|
-
runRecursively(component, fn);
|
|
747
|
-
});
|
|
748
|
-
fn(formField);
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
// helpers //////////////////////
|
|
752
757
|
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
758
|
+
/**
|
|
759
|
+
* Evaluate a string based on the expressionLanguage and context information.
|
|
760
|
+
* If the string is not an expression, it is returned as is.
|
|
761
|
+
*
|
|
762
|
+
* @param {any} expressionLanguage - The expression language to use.
|
|
763
|
+
* @param {string} value - The string to evaluate.
|
|
764
|
+
* @param {Object} expressionContextInfo - The context information to use.
|
|
765
|
+
* @returns {any} - Evaluated value or the original value if not an expression.
|
|
766
|
+
*/
|
|
767
|
+
function runExpressionEvaluation(expressionLanguage, value, expressionContextInfo) {
|
|
768
|
+
if (expressionLanguage && expressionLanguage.isExpression(value)) {
|
|
769
|
+
return expressionLanguage.evaluate(value, buildExpressionContext(expressionContextInfo));
|
|
757
770
|
}
|
|
758
|
-
return
|
|
771
|
+
return value;
|
|
759
772
|
}
|
|
760
773
|
|
|
761
774
|
/**
|
|
@@ -888,12 +901,7 @@ function _isAllowedValue(value) {
|
|
|
888
901
|
function useExpressionEvaluation(value) {
|
|
889
902
|
const expressionLanguage = useService('expressionLanguage');
|
|
890
903
|
const expressionContextInfo = useContext(LocalExpressionContext);
|
|
891
|
-
return useMemo(() =>
|
|
892
|
-
if (expressionLanguage && expressionLanguage.isExpression(value)) {
|
|
893
|
-
return expressionLanguage.evaluate(value, buildExpressionContext(expressionContextInfo));
|
|
894
|
-
}
|
|
895
|
-
return value;
|
|
896
|
-
}, [expressionLanguage, expressionContextInfo, value]);
|
|
904
|
+
return useMemo(() => runExpressionEvaluation(expressionLanguage, value, expressionContextInfo), [expressionLanguage, expressionContextInfo, value]);
|
|
897
905
|
}
|
|
898
906
|
|
|
899
907
|
/**
|
|
@@ -1770,7 +1778,6 @@ function Checkbox(props) {
|
|
|
1770
1778
|
target
|
|
1771
1779
|
}) => {
|
|
1772
1780
|
props.onChange({
|
|
1773
|
-
field,
|
|
1774
1781
|
value: target.checked
|
|
1775
1782
|
});
|
|
1776
1783
|
};
|
|
@@ -1849,7 +1856,6 @@ function Checklist(props) {
|
|
|
1849
1856
|
const toggleCheckbox = toggledValue => {
|
|
1850
1857
|
const newValues = hasEqualValue(toggledValue, values) ? values.filter(value => !isEqual(value, toggledValue)) : [...values, toggledValue];
|
|
1851
1858
|
props.onChange({
|
|
1852
|
-
field,
|
|
1853
1859
|
value: newValues
|
|
1854
1860
|
});
|
|
1855
1861
|
};
|
|
@@ -1937,7 +1943,7 @@ function FormField(props) {
|
|
|
1937
1943
|
const {
|
|
1938
1944
|
field,
|
|
1939
1945
|
indexes,
|
|
1940
|
-
onChange
|
|
1946
|
+
onChange: _onChange
|
|
1941
1947
|
} = props;
|
|
1942
1948
|
const formFields = useService('formFields'),
|
|
1943
1949
|
viewerCommands = useService('viewerCommands', false),
|
|
@@ -1979,21 +1985,22 @@ function FormField(props) {
|
|
|
1979
1985
|
// add precedence: global readonly > form field disabled
|
|
1980
1986
|
const disabled = !properties.readOnly && (properties.disabled || field.disabled || false);
|
|
1981
1987
|
const hidden = useCondition(field.conditional && field.conditional.hide || null);
|
|
1988
|
+
const fieldInstance = useMemo(() => ({
|
|
1989
|
+
id: field.id,
|
|
1990
|
+
expressionContextInfo: localExpressionContext,
|
|
1991
|
+
valuePath,
|
|
1992
|
+
indexes
|
|
1993
|
+
}), [field.id, valuePath, localExpressionContext, indexes]);
|
|
1982
1994
|
|
|
1983
1995
|
// register form field instance
|
|
1984
1996
|
useEffect(() => {
|
|
1985
1997
|
if (formFieldInstanceRegistry && !hidden) {
|
|
1986
|
-
const instanceId = formFieldInstanceRegistry.add(
|
|
1987
|
-
id: field.id,
|
|
1988
|
-
expressionContextInfo: localExpressionContext,
|
|
1989
|
-
valuePath,
|
|
1990
|
-
indexes
|
|
1991
|
-
});
|
|
1998
|
+
const instanceId = formFieldInstanceRegistry.add(fieldInstance);
|
|
1992
1999
|
return () => {
|
|
1993
2000
|
formFieldInstanceRegistry.remove(instanceId);
|
|
1994
2001
|
};
|
|
1995
2002
|
}
|
|
1996
|
-
}, [
|
|
2003
|
+
}, [fieldInstance, formFieldInstanceRegistry, hidden]);
|
|
1997
2004
|
|
|
1998
2005
|
// ensures the initial validation behavior can be re-triggered upon form reset
|
|
1999
2006
|
useEffect(() => {
|
|
@@ -2014,34 +2021,33 @@ function FormField(props) {
|
|
|
2014
2021
|
const hasInitialValue = initialValue && !isEqual(initialValue, []);
|
|
2015
2022
|
if (initialValidationTrigger && hasInitialValue) {
|
|
2016
2023
|
setInitialValidationTrigger(false);
|
|
2017
|
-
viewerCommands.
|
|
2024
|
+
viewerCommands.updateFieldInstanceValidation(fieldInstance, initialValue);
|
|
2018
2025
|
}
|
|
2019
|
-
}, [
|
|
2026
|
+
}, [fieldInstance, initialValidationTrigger, initialValue, viewerCommands]);
|
|
2020
2027
|
const onBlur = useCallback(() => {
|
|
2021
2028
|
const value = get(data, valuePath);
|
|
2022
2029
|
if (initialValidationTrigger) {
|
|
2023
2030
|
setInitialValidationTrigger(false);
|
|
2024
|
-
viewerCommands.
|
|
2031
|
+
viewerCommands.updateFieldInstanceValidation(fieldInstance, value);
|
|
2025
2032
|
}
|
|
2026
2033
|
eventBus.fire('formField.blur', {
|
|
2027
2034
|
formField: field
|
|
2028
2035
|
});
|
|
2029
|
-
}, [eventBus, field,
|
|
2036
|
+
}, [data, eventBus, field, fieldInstance, initialValidationTrigger, valuePath, viewerCommands]);
|
|
2030
2037
|
const onFocus = useCallback(() => {
|
|
2031
2038
|
eventBus.fire('formField.focus', {
|
|
2032
2039
|
formField: field
|
|
2033
2040
|
});
|
|
2034
2041
|
}, [eventBus, field]);
|
|
2035
|
-
const
|
|
2036
|
-
// any data change will trigger validation
|
|
2042
|
+
const onChange = useCallback(update => {
|
|
2037
2043
|
setInitialValidationTrigger(false);
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
}
|
|
2044
|
-
}, [
|
|
2044
|
+
_onChange({
|
|
2045
|
+
field,
|
|
2046
|
+
indexes,
|
|
2047
|
+
fieldInstance,
|
|
2048
|
+
...update
|
|
2049
|
+
});
|
|
2050
|
+
}, [_onChange, field, fieldInstance, indexes]);
|
|
2045
2051
|
if (hidden) {
|
|
2046
2052
|
return jsx(Hidden, {
|
|
2047
2053
|
field: field
|
|
@@ -2054,11 +2060,12 @@ function FormField(props) {
|
|
|
2054
2060
|
disabled: disabled,
|
|
2055
2061
|
errors: fieldErrors,
|
|
2056
2062
|
domId: domId,
|
|
2057
|
-
onChange: disabled || readonly ? noop$1 :
|
|
2063
|
+
onChange: disabled || readonly ? noop$1 : onChange,
|
|
2058
2064
|
onBlur: disabled || readonly ? noop$1 : onBlur,
|
|
2059
2065
|
onFocus: disabled || readonly ? noop$1 : onFocus,
|
|
2060
2066
|
readonly: readonly,
|
|
2061
|
-
value: value
|
|
2067
|
+
value: value,
|
|
2068
|
+
fieldInstance: fieldInstance
|
|
2062
2069
|
});
|
|
2063
2070
|
if (fieldConfig.escapeGridRender) {
|
|
2064
2071
|
return formFieldElement;
|
|
@@ -3894,7 +3901,6 @@ function Radio(props) {
|
|
|
3894
3901
|
} = validate;
|
|
3895
3902
|
const onChange = v => {
|
|
3896
3903
|
props.onChange({
|
|
3897
|
-
field,
|
|
3898
3904
|
value: v
|
|
3899
3905
|
});
|
|
3900
3906
|
};
|
|
@@ -4042,10 +4048,9 @@ function SearchableSelect(props) {
|
|
|
4042
4048
|
const setValue = useCallback(option => {
|
|
4043
4049
|
setFilter(option && option.label || '');
|
|
4044
4050
|
props.onChange({
|
|
4045
|
-
value: option && option.value || null
|
|
4046
|
-
field
|
|
4051
|
+
value: option && option.value || null
|
|
4047
4052
|
});
|
|
4048
|
-
}, [
|
|
4053
|
+
}, [props]);
|
|
4049
4054
|
const displayState = useMemo(() => {
|
|
4050
4055
|
const ds = {};
|
|
4051
4056
|
ds.componentReady = !disabled && !readonly && loadState === LOAD_STATES.LOADED;
|
|
@@ -4191,10 +4196,9 @@ function SimpleSelect(props) {
|
|
|
4191
4196
|
const valueLabel = useMemo(() => value && getLabelCorrelation(value), [value, getLabelCorrelation]);
|
|
4192
4197
|
const setValue = useCallback(option => {
|
|
4193
4198
|
props.onChange({
|
|
4194
|
-
value: option && option.value || null
|
|
4195
|
-
field
|
|
4199
|
+
value: option && option.value || null
|
|
4196
4200
|
});
|
|
4197
|
-
}, [
|
|
4201
|
+
}, [props]);
|
|
4198
4202
|
const displayState = useMemo(() => {
|
|
4199
4203
|
const ds = {};
|
|
4200
4204
|
ds.componentReady = !disabled && !readonly && loadState === LOAD_STATES.LOADED;
|
|
@@ -4532,15 +4536,13 @@ function Taglist(props) {
|
|
|
4532
4536
|
return;
|
|
4533
4537
|
}
|
|
4534
4538
|
props.onChange({
|
|
4535
|
-
value: [...values, value]
|
|
4536
|
-
field
|
|
4539
|
+
value: [...values, value]
|
|
4537
4540
|
});
|
|
4538
4541
|
};
|
|
4539
4542
|
const deselectValue = value => {
|
|
4540
4543
|
const newValues = values.filter(v => !isEqual(v, value));
|
|
4541
4544
|
props.onChange({
|
|
4542
|
-
value: newValues
|
|
4543
|
-
field
|
|
4545
|
+
value: newValues
|
|
4544
4546
|
});
|
|
4545
4547
|
};
|
|
4546
4548
|
const onInputChange = ({
|
|
@@ -5029,7 +5031,6 @@ function Textfield(props) {
|
|
|
5029
5031
|
target
|
|
5030
5032
|
}) => {
|
|
5031
5033
|
props.onChange({
|
|
5032
|
-
field,
|
|
5033
5034
|
value: target.value
|
|
5034
5035
|
});
|
|
5035
5036
|
});
|
|
@@ -5129,7 +5130,6 @@ function Textarea(props) {
|
|
|
5129
5130
|
target
|
|
5130
5131
|
}) => {
|
|
5131
5132
|
props.onChange({
|
|
5132
|
-
field,
|
|
5133
5133
|
value: target.value
|
|
5134
5134
|
});
|
|
5135
5135
|
});
|
|
@@ -6598,6 +6598,9 @@ var commandModule = {
|
|
|
6598
6598
|
commandStack: ['type', CommandStack]
|
|
6599
6599
|
};
|
|
6600
6600
|
|
|
6601
|
+
/**
|
|
6602
|
+
* @deprecated
|
|
6603
|
+
*/
|
|
6601
6604
|
class UpdateFieldValidationHandler {
|
|
6602
6605
|
constructor(form, validator) {
|
|
6603
6606
|
this._form = form;
|
|
@@ -6627,6 +6630,38 @@ class UpdateFieldValidationHandler {
|
|
|
6627
6630
|
}
|
|
6628
6631
|
UpdateFieldValidationHandler.$inject = ['form', 'validator'];
|
|
6629
6632
|
|
|
6633
|
+
class UpdateFieldInstanceValidationHandler {
|
|
6634
|
+
constructor(form, validator) {
|
|
6635
|
+
this._form = form;
|
|
6636
|
+
this._validator = validator;
|
|
6637
|
+
}
|
|
6638
|
+
execute(context) {
|
|
6639
|
+
const {
|
|
6640
|
+
fieldInstance,
|
|
6641
|
+
value
|
|
6642
|
+
} = context;
|
|
6643
|
+
const {
|
|
6644
|
+
id,
|
|
6645
|
+
indexes
|
|
6646
|
+
} = fieldInstance;
|
|
6647
|
+
const {
|
|
6648
|
+
errors
|
|
6649
|
+
} = this._form._getState();
|
|
6650
|
+
context.oldErrors = clone(errors);
|
|
6651
|
+
const fieldErrors = this._validator.validateFieldInstance(fieldInstance, value);
|
|
6652
|
+
const updatedErrors = set(errors, [id, ...Object.values(indexes || {})], fieldErrors.length ? fieldErrors : undefined);
|
|
6653
|
+
this._form._setState({
|
|
6654
|
+
errors: updatedErrors
|
|
6655
|
+
});
|
|
6656
|
+
}
|
|
6657
|
+
revert(context) {
|
|
6658
|
+
this._form._setState({
|
|
6659
|
+
errors: context.oldErrors
|
|
6660
|
+
});
|
|
6661
|
+
}
|
|
6662
|
+
}
|
|
6663
|
+
UpdateFieldInstanceValidationHandler.$inject = ['form', 'validator'];
|
|
6664
|
+
|
|
6630
6665
|
class ViewerCommands {
|
|
6631
6666
|
constructor(commandStack, eventBus) {
|
|
6632
6667
|
this._commandStack = commandStack;
|
|
@@ -6641,9 +6676,14 @@ class ViewerCommands {
|
|
|
6641
6676
|
}
|
|
6642
6677
|
getHandlers() {
|
|
6643
6678
|
return {
|
|
6644
|
-
'formField.validation.update': UpdateFieldValidationHandler
|
|
6679
|
+
'formField.validation.update': UpdateFieldValidationHandler,
|
|
6680
|
+
'formFieldInstance.validation.update': UpdateFieldInstanceValidationHandler
|
|
6645
6681
|
};
|
|
6646
6682
|
}
|
|
6683
|
+
|
|
6684
|
+
/**
|
|
6685
|
+
* @deprecated
|
|
6686
|
+
*/
|
|
6647
6687
|
updateFieldValidation(field, value, indexes) {
|
|
6648
6688
|
const context = {
|
|
6649
6689
|
field,
|
|
@@ -6652,6 +6692,13 @@ class ViewerCommands {
|
|
|
6652
6692
|
};
|
|
6653
6693
|
this._commandStack.execute('formField.validation.update', context);
|
|
6654
6694
|
}
|
|
6695
|
+
updateFieldInstanceValidation(fieldInstance, value) {
|
|
6696
|
+
const context = {
|
|
6697
|
+
fieldInstance,
|
|
6698
|
+
value
|
|
6699
|
+
};
|
|
6700
|
+
this._commandStack.execute('formFieldInstance.validation.update', context);
|
|
6701
|
+
}
|
|
6655
6702
|
}
|
|
6656
6703
|
ViewerCommands.$inject = ['commandStack', 'eventBus'];
|
|
6657
6704
|
|
|
@@ -6836,9 +6883,7 @@ class RepeatRenderManager {
|
|
|
6836
6883
|
updatedValues.push(newItem);
|
|
6837
6884
|
shouldScroll.current = true;
|
|
6838
6885
|
props.onChange({
|
|
6839
|
-
|
|
6840
|
-
value: updatedValues,
|
|
6841
|
-
indexes
|
|
6886
|
+
value: updatedValues
|
|
6842
6887
|
});
|
|
6843
6888
|
setSharedRepeatState(state => ({
|
|
6844
6889
|
...state,
|
|
@@ -7497,11 +7542,18 @@ const EMAIL_PATTERN = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-
|
|
|
7497
7542
|
const PHONE_PATTERN = /(\+|00)(297|93|244|1264|358|355|376|971|54|374|1684|1268|61|43|994|257|32|229|226|880|359|973|1242|387|590|375|501|1441|591|55|1246|673|975|267|236|1|61|41|56|86|225|237|243|242|682|57|269|238|506|53|5999|61|1345|357|420|49|253|1767|45|1809|1829|1849|213|593|20|291|212|34|372|251|358|679|500|33|298|691|241|44|995|44|233|350|224|590|220|245|240|30|1473|299|502|594|1671|592|852|504|385|509|36|62|44|91|246|353|98|964|354|972|39|1876|44|962|81|76|77|254|996|855|686|1869|82|383|965|856|961|231|218|1758|423|94|266|370|352|371|853|590|212|377|373|261|960|52|692|389|223|356|95|382|976|1670|258|222|1664|596|230|265|60|262|264|687|227|672|234|505|683|31|47|977|674|64|968|92|507|64|51|63|680|675|48|1787|1939|850|351|595|970|689|974|262|40|7|250|966|249|221|65|500|4779|677|232|503|378|252|508|381|211|239|597|421|386|46|268|1721|248|963|1649|235|228|66|992|690|993|670|676|1868|216|90|688|886|255|256|380|598|1|998|3906698|379|1784|58|1284|1340|84|678|681|685|967|27|260|263)(9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)\d{4,20}$/;
|
|
7498
7543
|
const VALIDATE_FEEL_PROPERTIES = ['min', 'max', 'minLength', 'maxLength'];
|
|
7499
7544
|
class Validator {
|
|
7500
|
-
constructor(expressionLanguage, conditionChecker, form) {
|
|
7545
|
+
constructor(expressionLanguage, conditionChecker, form, formFieldRegistry) {
|
|
7501
7546
|
this._expressionLanguage = expressionLanguage;
|
|
7502
7547
|
this._conditionChecker = conditionChecker;
|
|
7503
7548
|
this._form = form;
|
|
7549
|
+
this._formFieldRegistry = formFieldRegistry;
|
|
7504
7550
|
}
|
|
7551
|
+
|
|
7552
|
+
/**
|
|
7553
|
+
* Validate against a field definition, does not support proper expression evaluation.
|
|
7554
|
+
*
|
|
7555
|
+
* @deprecated use validateFieldInstance instead
|
|
7556
|
+
*/
|
|
7505
7557
|
validateField(field, value) {
|
|
7506
7558
|
const {
|
|
7507
7559
|
type,
|
|
@@ -7509,72 +7561,121 @@ class Validator {
|
|
|
7509
7561
|
} = field;
|
|
7510
7562
|
let errors = [];
|
|
7511
7563
|
if (type === 'number') {
|
|
7512
|
-
|
|
7513
|
-
decimalDigits,
|
|
7514
|
-
increment
|
|
7515
|
-
} = field;
|
|
7516
|
-
if (value === 'NaN') {
|
|
7517
|
-
errors = [...errors, 'Value is not a number.'];
|
|
7518
|
-
} else if (value) {
|
|
7519
|
-
if (decimalDigits >= 0 && countDecimals(value) > decimalDigits) {
|
|
7520
|
-
errors = [...errors, 'Value is expected to ' + (decimalDigits === 0 ? 'be an integer' : `have at most ${decimalDigits} decimal digit${decimalDigits > 1 ? 's' : ''}`) + '.'];
|
|
7521
|
-
}
|
|
7522
|
-
if (increment) {
|
|
7523
|
-
const bigValue = Big(value);
|
|
7524
|
-
const bigIncrement = Big(increment);
|
|
7525
|
-
const offset = bigValue.mod(bigIncrement);
|
|
7526
|
-
if (offset.cmp(0) !== 0) {
|
|
7527
|
-
const previousValue = bigValue.minus(offset);
|
|
7528
|
-
const nextValue = previousValue.plus(bigIncrement);
|
|
7529
|
-
errors = [...errors, `Please select a valid value, the two nearest valid values are ${previousValue} and ${nextValue}.`];
|
|
7530
|
-
}
|
|
7531
|
-
}
|
|
7532
|
-
}
|
|
7564
|
+
errors = [...errors, ...runNumberValidation(field, value)];
|
|
7533
7565
|
}
|
|
7534
7566
|
if (!validate) {
|
|
7535
7567
|
return errors;
|
|
7536
7568
|
}
|
|
7537
|
-
const evaluatedValidation =
|
|
7538
|
-
|
|
7539
|
-
|
|
7540
|
-
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
}
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
if (
|
|
7562
|
-
errors = [...errors,
|
|
7569
|
+
const evaluatedValidation = oldEvaluateFEELValues(validate, this._expressionLanguage, this._conditionChecker, this._form);
|
|
7570
|
+
errors = [...errors, ...runPresetValidation(field, evaluatedValidation, value)];
|
|
7571
|
+
return errors;
|
|
7572
|
+
}
|
|
7573
|
+
|
|
7574
|
+
/**
|
|
7575
|
+
* Validate a field instance.
|
|
7576
|
+
*
|
|
7577
|
+
* @param {Object} fieldInstance
|
|
7578
|
+
* @param {string} value
|
|
7579
|
+
*
|
|
7580
|
+
* @returns {Array<string>}
|
|
7581
|
+
*/
|
|
7582
|
+
validateFieldInstance(fieldInstance, value) {
|
|
7583
|
+
const {
|
|
7584
|
+
id,
|
|
7585
|
+
expressionContextInfo
|
|
7586
|
+
} = fieldInstance;
|
|
7587
|
+
const field = this._formFieldRegistry.get(id);
|
|
7588
|
+
const {
|
|
7589
|
+
type,
|
|
7590
|
+
validate
|
|
7591
|
+
} = field;
|
|
7592
|
+
let errors = [];
|
|
7593
|
+
if (type === 'number') {
|
|
7594
|
+
errors = [...errors, ...runNumberValidation(field, value)];
|
|
7563
7595
|
}
|
|
7564
|
-
if (
|
|
7565
|
-
|
|
7596
|
+
if (!validate) {
|
|
7597
|
+
return errors;
|
|
7566
7598
|
}
|
|
7599
|
+
const evaluatedValidation = evaluateFEELValues(validate, this._expressionLanguage, expressionContextInfo);
|
|
7600
|
+
errors = [...errors, ...runPresetValidation(field, evaluatedValidation, value)];
|
|
7567
7601
|
return errors;
|
|
7568
7602
|
}
|
|
7569
7603
|
}
|
|
7570
|
-
Validator.$inject = ['expressionLanguage', 'conditionChecker', 'form'];
|
|
7604
|
+
Validator.$inject = ['expressionLanguage', 'conditionChecker', 'form', 'formFieldRegistry'];
|
|
7571
7605
|
|
|
7572
7606
|
// helpers //////////
|
|
7573
7607
|
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7608
|
+
function runNumberValidation(field, value) {
|
|
7609
|
+
const {
|
|
7610
|
+
decimalDigits,
|
|
7611
|
+
increment
|
|
7612
|
+
} = field;
|
|
7613
|
+
const errors = [];
|
|
7614
|
+
if (value === 'NaN') {
|
|
7615
|
+
errors.push('Value is not a number.');
|
|
7616
|
+
} else if (value) {
|
|
7617
|
+
if (decimalDigits >= 0 && countDecimals(value) > decimalDigits) {
|
|
7618
|
+
errors.push('Value is expected to ' + (decimalDigits === 0 ? 'be an integer' : `have at most ${decimalDigits} decimal digit${decimalDigits > 1 ? 's' : ''}`) + '.');
|
|
7619
|
+
}
|
|
7620
|
+
if (increment) {
|
|
7621
|
+
const bigValue = Big(value);
|
|
7622
|
+
const bigIncrement = Big(increment);
|
|
7623
|
+
const offset = bigValue.mod(bigIncrement);
|
|
7624
|
+
if (offset.cmp(0) !== 0) {
|
|
7625
|
+
const previousValue = bigValue.minus(offset);
|
|
7626
|
+
const nextValue = previousValue.plus(bigIncrement);
|
|
7627
|
+
errors.push(`Please select a valid value, the two nearest valid values are ${previousValue} and ${nextValue}.`);
|
|
7628
|
+
}
|
|
7629
|
+
}
|
|
7630
|
+
}
|
|
7631
|
+
return errors;
|
|
7632
|
+
}
|
|
7633
|
+
function runPresetValidation(field, validation, value) {
|
|
7634
|
+
const errors = [];
|
|
7635
|
+
if (validation.pattern && value && !new RegExp(validation.pattern).test(value)) {
|
|
7636
|
+
errors.push(`Field must match pattern ${validation.pattern}.`);
|
|
7637
|
+
}
|
|
7638
|
+
if (validation.required) {
|
|
7639
|
+
const isUncheckedCheckbox = field.type === 'checkbox' && value === false;
|
|
7640
|
+
const isUnsetValue = isNil(value) || value === '';
|
|
7641
|
+
const isEmptyMultiselect = Array.isArray(value) && value.length === 0;
|
|
7642
|
+
if (isUncheckedCheckbox || isUnsetValue || isEmptyMultiselect) {
|
|
7643
|
+
errors.push('Field is required.');
|
|
7644
|
+
}
|
|
7645
|
+
}
|
|
7646
|
+
if ('min' in validation && (value || value === 0) && value < validation.min) {
|
|
7647
|
+
errors.push(`Field must have minimum value of ${validation.min}.`);
|
|
7648
|
+
}
|
|
7649
|
+
if ('max' in validation && (value || value === 0) && value > validation.max) {
|
|
7650
|
+
errors.push(`Field must have maximum value of ${validation.max}.`);
|
|
7651
|
+
}
|
|
7652
|
+
if ('minLength' in validation && value && value.trim().length < validation.minLength) {
|
|
7653
|
+
errors.push(`Field must have minimum length of ${validation.minLength}.`);
|
|
7654
|
+
}
|
|
7655
|
+
if ('maxLength' in validation && value && value.trim().length > validation.maxLength) {
|
|
7656
|
+
errors.push(`Field must have maximum length of ${validation.maxLength}.`);
|
|
7657
|
+
}
|
|
7658
|
+
if ('validationType' in validation && value && validation.validationType === 'phone' && !PHONE_PATTERN.test(value)) {
|
|
7659
|
+
errors.push('Field must be a valid international phone number. (e.g. +4930664040900)');
|
|
7660
|
+
}
|
|
7661
|
+
if ('validationType' in validation && value && validation.validationType === 'email' && !EMAIL_PATTERN.test(value)) {
|
|
7662
|
+
errors.push('Field must be a valid email.');
|
|
7663
|
+
}
|
|
7664
|
+
return errors;
|
|
7665
|
+
}
|
|
7666
|
+
function evaluateFEELValues(validate, expressionLanguage, expressionContextInfo) {
|
|
7667
|
+
const evaluatedValidate = {
|
|
7668
|
+
...validate
|
|
7669
|
+
};
|
|
7670
|
+
VALIDATE_FEEL_PROPERTIES.forEach(property => {
|
|
7671
|
+
const path = property.split('.');
|
|
7672
|
+
const value = get(evaluatedValidate, path);
|
|
7673
|
+
const evaluatedValue = runExpressionEvaluation(expressionLanguage, value, expressionContextInfo);
|
|
7674
|
+
set(evaluatedValidate, path, evaluatedValue === null ? undefined : evaluatedValue);
|
|
7675
|
+
});
|
|
7676
|
+
return evaluatedValidate;
|
|
7677
|
+
}
|
|
7678
|
+
function oldEvaluateFEELValues(validate, expressionLanguage, conditionChecker, form) {
|
|
7578
7679
|
const evaluatedValidate = {
|
|
7579
7680
|
...validate
|
|
7580
7681
|
};
|
|
@@ -8319,9 +8420,13 @@ class FormFieldInstanceRegistry {
|
|
|
8319
8420
|
return this.getAll().filter(({
|
|
8320
8421
|
id
|
|
8321
8422
|
}) => {
|
|
8423
|
+
const formFieldDefinition = this._formFieldRegistry.get(id);
|
|
8424
|
+
if (!formFieldDefinition) {
|
|
8425
|
+
return false;
|
|
8426
|
+
}
|
|
8322
8427
|
const {
|
|
8323
8428
|
type
|
|
8324
|
-
} =
|
|
8429
|
+
} = formFieldDefinition;
|
|
8325
8430
|
const {
|
|
8326
8431
|
config
|
|
8327
8432
|
} = this._formFields.get(type);
|
|
@@ -8581,11 +8686,12 @@ class Form {
|
|
|
8581
8686
|
} = this._getState();
|
|
8582
8687
|
const errors = {};
|
|
8583
8688
|
const getErrorPath = (id, indexes) => [id, ...Object.values(indexes || {})];
|
|
8584
|
-
formFieldInstanceRegistry.getAllKeyed().forEach(
|
|
8585
|
-
|
|
8586
|
-
|
|
8587
|
-
|
|
8588
|
-
|
|
8689
|
+
formFieldInstanceRegistry.getAllKeyed().forEach(fieldInstance => {
|
|
8690
|
+
const {
|
|
8691
|
+
id,
|
|
8692
|
+
valuePath,
|
|
8693
|
+
indexes
|
|
8694
|
+
} = fieldInstance;
|
|
8589
8695
|
const field = formFieldRegistry.get(id);
|
|
8590
8696
|
|
|
8591
8697
|
// (1) Skip disabled fields
|
|
@@ -8593,9 +8699,9 @@ class Form {
|
|
|
8593
8699
|
return;
|
|
8594
8700
|
}
|
|
8595
8701
|
|
|
8596
|
-
// (2) Validate the field
|
|
8702
|
+
// (2) Validate the field instance
|
|
8597
8703
|
const value = get(data, valuePath);
|
|
8598
|
-
const fieldErrors = validator.
|
|
8704
|
+
const fieldErrors = validator.validateFieldInstance(fieldInstance, value);
|
|
8599
8705
|
if (fieldErrors.length) {
|
|
8600
8706
|
set(errors, getErrorPath(field.id, indexes), fieldErrors);
|
|
8601
8707
|
}
|
|
@@ -8700,26 +8806,26 @@ class Form {
|
|
|
8700
8806
|
/**
|
|
8701
8807
|
* @internal
|
|
8702
8808
|
*
|
|
8703
|
-
* @param { {
|
|
8809
|
+
* @param { { fieldInstance: any, value: any } } update
|
|
8704
8810
|
*/
|
|
8705
8811
|
_update(update) {
|
|
8706
8812
|
const {
|
|
8707
|
-
|
|
8708
|
-
indexes,
|
|
8813
|
+
fieldInstance,
|
|
8709
8814
|
value
|
|
8710
8815
|
} = update;
|
|
8816
|
+
const {
|
|
8817
|
+
id,
|
|
8818
|
+
valuePath,
|
|
8819
|
+
indexes
|
|
8820
|
+
} = fieldInstance;
|
|
8711
8821
|
const {
|
|
8712
8822
|
data,
|
|
8713
8823
|
errors
|
|
8714
8824
|
} = this._getState();
|
|
8715
|
-
const validator = this.get('validator')
|
|
8716
|
-
|
|
8717
|
-
const fieldErrors = validator.validateField(field, value);
|
|
8718
|
-
const valuePath = pathRegistry.getValuePath(field, {
|
|
8719
|
-
indexes
|
|
8720
|
-
});
|
|
8825
|
+
const validator = this.get('validator');
|
|
8826
|
+
const fieldErrors = validator.validateFieldInstance(fieldInstance, value);
|
|
8721
8827
|
set(data, valuePath, value);
|
|
8722
|
-
set(errors, [
|
|
8828
|
+
set(errors, [id, ...Object.values(indexes || {})], fieldErrors.length ? fieldErrors : undefined);
|
|
8723
8829
|
this._emit('field.updated', update);
|
|
8724
8830
|
this._setState({
|
|
8725
8831
|
data: clone(data),
|
|
@@ -8899,5 +9005,5 @@ function createForm(options) {
|
|
|
8899
9005
|
});
|
|
8900
9006
|
}
|
|
8901
9007
|
|
|
8902
|
-
export { ALLOW_ATTRIBUTE, Button, Checkbox, Checklist, ConditionChecker, DATETIME_SUBTYPES, DATETIME_SUBTYPES_LABELS, DATETIME_SUBTYPE_PATH, DATE_DISALLOW_PAST_PATH, DATE_LABEL_PATH, Datetime, Default, Description, DynamicList, Errors, ExpressionField, ExpressionFieldModule, ExpressionLanguageModule, ExpressionLoopPreventer, FeelExpressionLanguage, FeelersTemplating, FieldFactory, Form, FormComponent, FormContext, FormField, FormFieldRegistry, FormFields, FormLayouter, FormRenderContext, Group, Html, IFrame, Image, Importer, Label, LocalExpressionContext, MINUTES_IN_DAY, MarkdownRenderer, MarkdownRendererModule, Numberfield, OPTIONS_SOURCES, OPTIONS_SOURCES_DEFAULTS, OPTIONS_SOURCES_LABELS, OPTIONS_SOURCES_PATHS, OPTIONS_SOURCE_DEFAULT, PathRegistry, Radio, RenderModule, RepeatRenderManager, RepeatRenderModule, SANDBOX_ATTRIBUTE, SECURITY_ATTRIBUTES_DEFINITIONS, Select, Separator, Spacer, TIME_INTERVAL_PATH, TIME_LABEL_PATH, TIME_SERIALISINGFORMAT_LABELS, TIME_SERIALISING_FORMATS, TIME_SERIALISING_FORMAT_PATH, TIME_USE24H_PATH, Table, Taglist, Text, Textarea, Textfield, ViewerCommands, ViewerCommandsModule, buildExpressionContext, clone, createForm, createFormContainer, createInjector, escapeHTML, formFields, generateIdForType, generateIndexForType, getAncestryList, getOptionsSource, getSchemaVariables, getScrollContainer, hasEqualValue, iconsByType, isRequired, pathParse, pathsEqual, runRecursively, sanitizeDateTimePickerValue, sanitizeHTML, sanitizeIFrameSource, sanitizeImageSource, sanitizeMultiSelectValue, sanitizeSingleSelectValue, schemaVersion, useExpressionEvaluation, useSingleLineTemplateEvaluation, useTemplateEvaluation, wrapCSSStyles };
|
|
9008
|
+
export { ALLOW_ATTRIBUTE, Button, Checkbox, Checklist, ConditionChecker, DATETIME_SUBTYPES, DATETIME_SUBTYPES_LABELS, DATETIME_SUBTYPE_PATH, DATE_DISALLOW_PAST_PATH, DATE_LABEL_PATH, Datetime, Default, Description, DynamicList, Errors, ExpressionField, ExpressionFieldModule, ExpressionLanguageModule, ExpressionLoopPreventer, FeelExpressionLanguage, FeelersTemplating, FieldFactory, Form, FormComponent, FormContext, FormField, FormFieldRegistry, FormFields, FormLayouter, FormRenderContext, Group, Html, IFrame, Image, Importer, Label, LocalExpressionContext, MINUTES_IN_DAY, MarkdownRenderer, MarkdownRendererModule, Numberfield, OPTIONS_SOURCES, OPTIONS_SOURCES_DEFAULTS, OPTIONS_SOURCES_LABELS, OPTIONS_SOURCES_PATHS, OPTIONS_SOURCE_DEFAULT, PathRegistry, Radio, RenderModule, RepeatRenderManager, RepeatRenderModule, SANDBOX_ATTRIBUTE, SECURITY_ATTRIBUTES_DEFINITIONS, Select, Separator, Spacer, TIME_INTERVAL_PATH, TIME_LABEL_PATH, TIME_SERIALISINGFORMAT_LABELS, TIME_SERIALISING_FORMATS, TIME_SERIALISING_FORMAT_PATH, TIME_USE24H_PATH, Table, Taglist, Text, Textarea, Textfield, ViewerCommands, ViewerCommandsModule, buildExpressionContext, clone, createForm, createFormContainer, createInjector, escapeHTML, formFields, generateIdForType, generateIndexForType, getAncestryList, getOptionsSource, getSchemaVariables, getScrollContainer, hasEqualValue, iconsByType, isRequired, pathParse, pathsEqual, runExpressionEvaluation, runRecursively, sanitizeDateTimePickerValue, sanitizeHTML, sanitizeIFrameSource, sanitizeImageSource, sanitizeMultiSelectValue, sanitizeSingleSelectValue, schemaVersion, useExpressionEvaluation, useSingleLineTemplateEvaluation, useTemplateEvaluation, wrapCSSStyles, wrapObjectKeysWithUnderscores };
|
|
8903
9009
|
//# sourceMappingURL=index.es.js.map
|