@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 CHANGED
@@ -741,6 +741,20 @@ function generateIdForType(type) {
741
741
  function clone(data, replacer) {
742
742
  return JSON.parse(JSON.stringify(data, replacer));
743
743
  }
744
+ function runRecursively(formField, fn) {
745
+ const components = formField.components || [];
746
+ components.forEach((component, _) => {
747
+ runRecursively(component, fn);
748
+ });
749
+ fn(formField);
750
+ }
751
+ function wrapObjectKeysWithUnderscores(obj) {
752
+ const newObj = {};
753
+ for (const [key, value] of Object.entries(obj)) {
754
+ newObj[`_${key}_`] = value;
755
+ }
756
+ return newObj;
757
+ }
744
758
 
745
759
  /**
746
760
  * Transform a LocalExpressionContext object into a usable FEEL context.
@@ -757,25 +771,24 @@ function buildExpressionContext(context) {
757
771
  return {
758
772
  ...specialContextKeys,
759
773
  ...data,
760
- ..._wrapObjectKeysWithUnderscores(specialContextKeys)
774
+ ...wrapObjectKeysWithUnderscores(specialContextKeys)
761
775
  };
762
776
  }
763
- function runRecursively(formField, fn) {
764
- const components = formField.components || [];
765
- components.forEach((component, index) => {
766
- runRecursively(component, fn);
767
- });
768
- fn(formField);
769
- }
770
-
771
- // helpers //////////////////////
772
777
 
773
- function _wrapObjectKeysWithUnderscores(obj) {
774
- const newObj = {};
775
- for (const [key, value] of Object.entries(obj)) {
776
- newObj[`_${key}_`] = value;
778
+ /**
779
+ * Evaluate a string based on the expressionLanguage and context information.
780
+ * If the string is not an expression, it is returned as is.
781
+ *
782
+ * @param {any} expressionLanguage - The expression language to use.
783
+ * @param {string} value - The string to evaluate.
784
+ * @param {Object} expressionContextInfo - The context information to use.
785
+ * @returns {any} - Evaluated value or the original value if not an expression.
786
+ */
787
+ function runExpressionEvaluation(expressionLanguage, value, expressionContextInfo) {
788
+ if (expressionLanguage && expressionLanguage.isExpression(value)) {
789
+ return expressionLanguage.evaluate(value, buildExpressionContext(expressionContextInfo));
777
790
  }
778
- return newObj;
791
+ return value;
779
792
  }
780
793
 
781
794
  /**
@@ -908,12 +921,7 @@ function _isAllowedValue(value) {
908
921
  function useExpressionEvaluation(value) {
909
922
  const expressionLanguage = useService('expressionLanguage');
910
923
  const expressionContextInfo = hooks.useContext(LocalExpressionContext);
911
- return hooks.useMemo(() => {
912
- if (expressionLanguage && expressionLanguage.isExpression(value)) {
913
- return expressionLanguage.evaluate(value, buildExpressionContext(expressionContextInfo));
914
- }
915
- return value;
916
- }, [expressionLanguage, expressionContextInfo, value]);
924
+ return hooks.useMemo(() => runExpressionEvaluation(expressionLanguage, value, expressionContextInfo), [expressionLanguage, expressionContextInfo, value]);
917
925
  }
918
926
 
919
927
  /**
@@ -1790,7 +1798,6 @@ function Checkbox(props) {
1790
1798
  target
1791
1799
  }) => {
1792
1800
  props.onChange({
1793
- field,
1794
1801
  value: target.checked
1795
1802
  });
1796
1803
  };
@@ -1869,7 +1876,6 @@ function Checklist(props) {
1869
1876
  const toggleCheckbox = toggledValue => {
1870
1877
  const newValues = hasEqualValue(toggledValue, values) ? values.filter(value => !isEqual(value, toggledValue)) : [...values, toggledValue];
1871
1878
  props.onChange({
1872
- field,
1873
1879
  value: newValues
1874
1880
  });
1875
1881
  };
@@ -1957,7 +1963,7 @@ function FormField(props) {
1957
1963
  const {
1958
1964
  field,
1959
1965
  indexes,
1960
- onChange
1966
+ onChange: _onChange
1961
1967
  } = props;
1962
1968
  const formFields = useService('formFields'),
1963
1969
  viewerCommands = useService('viewerCommands', false),
@@ -1999,21 +2005,22 @@ function FormField(props) {
1999
2005
  // add precedence: global readonly > form field disabled
2000
2006
  const disabled = !properties.readOnly && (properties.disabled || field.disabled || false);
2001
2007
  const hidden = useCondition(field.conditional && field.conditional.hide || null);
2008
+ const fieldInstance = hooks.useMemo(() => ({
2009
+ id: field.id,
2010
+ expressionContextInfo: localExpressionContext,
2011
+ valuePath,
2012
+ indexes
2013
+ }), [field.id, valuePath, localExpressionContext, indexes]);
2002
2014
 
2003
2015
  // register form field instance
2004
2016
  hooks.useEffect(() => {
2005
2017
  if (formFieldInstanceRegistry && !hidden) {
2006
- const instanceId = formFieldInstanceRegistry.add({
2007
- id: field.id,
2008
- expressionContextInfo: localExpressionContext,
2009
- valuePath,
2010
- indexes
2011
- });
2018
+ const instanceId = formFieldInstanceRegistry.add(fieldInstance);
2012
2019
  return () => {
2013
2020
  formFieldInstanceRegistry.remove(instanceId);
2014
2021
  };
2015
2022
  }
2016
- }, [formFieldInstanceRegistry, field.id, localExpressionContext, valuePath, indexes, hidden]);
2023
+ }, [fieldInstance, formFieldInstanceRegistry, hidden]);
2017
2024
 
2018
2025
  // ensures the initial validation behavior can be re-triggered upon form reset
2019
2026
  hooks.useEffect(() => {
@@ -2034,34 +2041,33 @@ function FormField(props) {
2034
2041
  const hasInitialValue = initialValue && !isEqual(initialValue, []);
2035
2042
  if (initialValidationTrigger && hasInitialValue) {
2036
2043
  setInitialValidationTrigger(false);
2037
- viewerCommands.updateFieldValidation(field, initialValue, indexes);
2044
+ viewerCommands.updateFieldInstanceValidation(fieldInstance, initialValue);
2038
2045
  }
2039
- }, [viewerCommands, field, initialValue, initialValidationTrigger, indexes]);
2046
+ }, [fieldInstance, initialValidationTrigger, initialValue, viewerCommands]);
2040
2047
  const onBlur = hooks.useCallback(() => {
2041
2048
  const value = minDash.get(data, valuePath);
2042
2049
  if (initialValidationTrigger) {
2043
2050
  setInitialValidationTrigger(false);
2044
- viewerCommands.updateFieldValidation(field, value, indexes);
2051
+ viewerCommands.updateFieldInstanceValidation(fieldInstance, value);
2045
2052
  }
2046
2053
  eventBus.fire('formField.blur', {
2047
2054
  formField: field
2048
2055
  });
2049
- }, [eventBus, field, indexes, viewerCommands, initialValidationTrigger, data, valuePath]);
2056
+ }, [data, eventBus, field, fieldInstance, initialValidationTrigger, valuePath, viewerCommands]);
2050
2057
  const onFocus = hooks.useCallback(() => {
2051
2058
  eventBus.fire('formField.focus', {
2052
2059
  formField: field
2053
2060
  });
2054
2061
  }, [eventBus, field]);
2055
- const onChangeIndexed = hooks.useCallback(update => {
2056
- // any data change will trigger validation
2062
+ const onChange = hooks.useCallback(update => {
2057
2063
  setInitialValidationTrigger(false);
2058
-
2059
- // add indexes of the keyed field to the update, if any
2060
- onChange(fieldConfig.keyed ? {
2061
- ...update,
2062
- indexes
2063
- } : update);
2064
- }, [onChange, fieldConfig.keyed, indexes]);
2064
+ _onChange({
2065
+ field,
2066
+ indexes,
2067
+ fieldInstance,
2068
+ ...update
2069
+ });
2070
+ }, [_onChange, field, fieldInstance, indexes]);
2065
2071
  if (hidden) {
2066
2072
  return jsxRuntime.jsx(Hidden, {
2067
2073
  field: field
@@ -2074,11 +2080,12 @@ function FormField(props) {
2074
2080
  disabled: disabled,
2075
2081
  errors: fieldErrors,
2076
2082
  domId: domId,
2077
- onChange: disabled || readonly ? noop$1 : onChangeIndexed,
2083
+ onChange: disabled || readonly ? noop$1 : onChange,
2078
2084
  onBlur: disabled || readonly ? noop$1 : onBlur,
2079
2085
  onFocus: disabled || readonly ? noop$1 : onFocus,
2080
2086
  readonly: readonly,
2081
- value: value
2087
+ value: value,
2088
+ fieldInstance: fieldInstance
2082
2089
  });
2083
2090
  if (fieldConfig.escapeGridRender) {
2084
2091
  return formFieldElement;
@@ -3914,7 +3921,6 @@ function Radio(props) {
3914
3921
  } = validate;
3915
3922
  const onChange = v => {
3916
3923
  props.onChange({
3917
- field,
3918
3924
  value: v
3919
3925
  });
3920
3926
  };
@@ -4062,10 +4068,9 @@ function SearchableSelect(props) {
4062
4068
  const setValue = hooks.useCallback(option => {
4063
4069
  setFilter(option && option.label || '');
4064
4070
  props.onChange({
4065
- value: option && option.value || null,
4066
- field
4071
+ value: option && option.value || null
4067
4072
  });
4068
- }, [field, props]);
4073
+ }, [props]);
4069
4074
  const displayState = hooks.useMemo(() => {
4070
4075
  const ds = {};
4071
4076
  ds.componentReady = !disabled && !readonly && loadState === LOAD_STATES.LOADED;
@@ -4211,10 +4216,9 @@ function SimpleSelect(props) {
4211
4216
  const valueLabel = hooks.useMemo(() => value && getLabelCorrelation(value), [value, getLabelCorrelation]);
4212
4217
  const setValue = hooks.useCallback(option => {
4213
4218
  props.onChange({
4214
- value: option && option.value || null,
4215
- field
4219
+ value: option && option.value || null
4216
4220
  });
4217
- }, [field, props]);
4221
+ }, [props]);
4218
4222
  const displayState = hooks.useMemo(() => {
4219
4223
  const ds = {};
4220
4224
  ds.componentReady = !disabled && !readonly && loadState === LOAD_STATES.LOADED;
@@ -4552,15 +4556,13 @@ function Taglist(props) {
4552
4556
  return;
4553
4557
  }
4554
4558
  props.onChange({
4555
- value: [...values, value],
4556
- field
4559
+ value: [...values, value]
4557
4560
  });
4558
4561
  };
4559
4562
  const deselectValue = value => {
4560
4563
  const newValues = values.filter(v => !isEqual(v, value));
4561
4564
  props.onChange({
4562
- value: newValues,
4563
- field
4565
+ value: newValues
4564
4566
  });
4565
4567
  };
4566
4568
  const onInputChange = ({
@@ -5049,7 +5051,6 @@ function Textfield(props) {
5049
5051
  target
5050
5052
  }) => {
5051
5053
  props.onChange({
5052
- field,
5053
5054
  value: target.value
5054
5055
  });
5055
5056
  });
@@ -5149,7 +5150,6 @@ function Textarea(props) {
5149
5150
  target
5150
5151
  }) => {
5151
5152
  props.onChange({
5152
- field,
5153
5153
  value: target.value
5154
5154
  });
5155
5155
  });
@@ -6618,6 +6618,9 @@ var commandModule = {
6618
6618
  commandStack: ['type', CommandStack]
6619
6619
  };
6620
6620
 
6621
+ /**
6622
+ * @deprecated
6623
+ */
6621
6624
  class UpdateFieldValidationHandler {
6622
6625
  constructor(form, validator) {
6623
6626
  this._form = form;
@@ -6647,6 +6650,38 @@ class UpdateFieldValidationHandler {
6647
6650
  }
6648
6651
  UpdateFieldValidationHandler.$inject = ['form', 'validator'];
6649
6652
 
6653
+ class UpdateFieldInstanceValidationHandler {
6654
+ constructor(form, validator) {
6655
+ this._form = form;
6656
+ this._validator = validator;
6657
+ }
6658
+ execute(context) {
6659
+ const {
6660
+ fieldInstance,
6661
+ value
6662
+ } = context;
6663
+ const {
6664
+ id,
6665
+ indexes
6666
+ } = fieldInstance;
6667
+ const {
6668
+ errors
6669
+ } = this._form._getState();
6670
+ context.oldErrors = clone(errors);
6671
+ const fieldErrors = this._validator.validateFieldInstance(fieldInstance, value);
6672
+ const updatedErrors = minDash.set(errors, [id, ...Object.values(indexes || {})], fieldErrors.length ? fieldErrors : undefined);
6673
+ this._form._setState({
6674
+ errors: updatedErrors
6675
+ });
6676
+ }
6677
+ revert(context) {
6678
+ this._form._setState({
6679
+ errors: context.oldErrors
6680
+ });
6681
+ }
6682
+ }
6683
+ UpdateFieldInstanceValidationHandler.$inject = ['form', 'validator'];
6684
+
6650
6685
  class ViewerCommands {
6651
6686
  constructor(commandStack, eventBus) {
6652
6687
  this._commandStack = commandStack;
@@ -6661,9 +6696,14 @@ class ViewerCommands {
6661
6696
  }
6662
6697
  getHandlers() {
6663
6698
  return {
6664
- 'formField.validation.update': UpdateFieldValidationHandler
6699
+ 'formField.validation.update': UpdateFieldValidationHandler,
6700
+ 'formFieldInstance.validation.update': UpdateFieldInstanceValidationHandler
6665
6701
  };
6666
6702
  }
6703
+
6704
+ /**
6705
+ * @deprecated
6706
+ */
6667
6707
  updateFieldValidation(field, value, indexes) {
6668
6708
  const context = {
6669
6709
  field,
@@ -6672,6 +6712,13 @@ class ViewerCommands {
6672
6712
  };
6673
6713
  this._commandStack.execute('formField.validation.update', context);
6674
6714
  }
6715
+ updateFieldInstanceValidation(fieldInstance, value) {
6716
+ const context = {
6717
+ fieldInstance,
6718
+ value
6719
+ };
6720
+ this._commandStack.execute('formFieldInstance.validation.update', context);
6721
+ }
6675
6722
  }
6676
6723
  ViewerCommands.$inject = ['commandStack', 'eventBus'];
6677
6724
 
@@ -6856,9 +6903,7 @@ class RepeatRenderManager {
6856
6903
  updatedValues.push(newItem);
6857
6904
  shouldScroll.current = true;
6858
6905
  props.onChange({
6859
- field: repeaterField,
6860
- value: updatedValues,
6861
- indexes
6906
+ value: updatedValues
6862
6907
  });
6863
6908
  setSharedRepeatState(state => ({
6864
6909
  ...state,
@@ -7517,11 +7562,18 @@ const EMAIL_PATTERN = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-
7517
7562
  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}$/;
7518
7563
  const VALIDATE_FEEL_PROPERTIES = ['min', 'max', 'minLength', 'maxLength'];
7519
7564
  class Validator {
7520
- constructor(expressionLanguage, conditionChecker, form) {
7565
+ constructor(expressionLanguage, conditionChecker, form, formFieldRegistry) {
7521
7566
  this._expressionLanguage = expressionLanguage;
7522
7567
  this._conditionChecker = conditionChecker;
7523
7568
  this._form = form;
7569
+ this._formFieldRegistry = formFieldRegistry;
7524
7570
  }
7571
+
7572
+ /**
7573
+ * Validate against a field definition, does not support proper expression evaluation.
7574
+ *
7575
+ * @deprecated use validateFieldInstance instead
7576
+ */
7525
7577
  validateField(field, value) {
7526
7578
  const {
7527
7579
  type,
@@ -7529,72 +7581,121 @@ class Validator {
7529
7581
  } = field;
7530
7582
  let errors = [];
7531
7583
  if (type === 'number') {
7532
- const {
7533
- decimalDigits,
7534
- increment
7535
- } = field;
7536
- if (value === 'NaN') {
7537
- errors = [...errors, 'Value is not a number.'];
7538
- } else if (value) {
7539
- if (decimalDigits >= 0 && countDecimals(value) > decimalDigits) {
7540
- errors = [...errors, 'Value is expected to ' + (decimalDigits === 0 ? 'be an integer' : `have at most ${decimalDigits} decimal digit${decimalDigits > 1 ? 's' : ''}`) + '.'];
7541
- }
7542
- if (increment) {
7543
- const bigValue = Big(value);
7544
- const bigIncrement = Big(increment);
7545
- const offset = bigValue.mod(bigIncrement);
7546
- if (offset.cmp(0) !== 0) {
7547
- const previousValue = bigValue.minus(offset);
7548
- const nextValue = previousValue.plus(bigIncrement);
7549
- errors = [...errors, `Please select a valid value, the two nearest valid values are ${previousValue} and ${nextValue}.`];
7550
- }
7551
- }
7552
- }
7584
+ errors = [...errors, ...runNumberValidation(field, value)];
7553
7585
  }
7554
7586
  if (!validate) {
7555
7587
  return errors;
7556
7588
  }
7557
- const evaluatedValidation = evaluateFEELValues(validate, this._expressionLanguage, this._conditionChecker, this._form);
7558
- if (evaluatedValidation.pattern && value && !new RegExp(evaluatedValidation.pattern).test(value)) {
7559
- errors = [...errors, `Field must match pattern ${evaluatedValidation.pattern}.`];
7560
- }
7561
- if (evaluatedValidation.required) {
7562
- const isUncheckedCheckbox = type === 'checkbox' && value === false;
7563
- const isUnsetValue = minDash.isNil(value) || value === '';
7564
- const isEmptyMultiselect = Array.isArray(value) && value.length === 0;
7565
- if (isUncheckedCheckbox || isUnsetValue || isEmptyMultiselect) {
7566
- errors = [...errors, 'Field is required.'];
7567
- }
7568
- }
7569
- if ('min' in evaluatedValidation && (value || value === 0) && value < evaluatedValidation.min) {
7570
- errors = [...errors, `Field must have minimum value of ${evaluatedValidation.min}.`];
7571
- }
7572
- if ('max' in evaluatedValidation && (value || value === 0) && value > evaluatedValidation.max) {
7573
- errors = [...errors, `Field must have maximum value of ${evaluatedValidation.max}.`];
7574
- }
7575
- if ('minLength' in evaluatedValidation && value && value.trim().length < evaluatedValidation.minLength) {
7576
- errors = [...errors, `Field must have minimum length of ${evaluatedValidation.minLength}.`];
7577
- }
7578
- if ('maxLength' in evaluatedValidation && value && value.trim().length > evaluatedValidation.maxLength) {
7579
- errors = [...errors, `Field must have maximum length of ${evaluatedValidation.maxLength}.`];
7580
- }
7581
- if ('validationType' in evaluatedValidation && value && evaluatedValidation.validationType === 'phone' && !PHONE_PATTERN.test(value)) {
7582
- errors = [...errors, 'Field must be a valid international phone number. (e.g. +4930664040900)'];
7589
+ const evaluatedValidation = oldEvaluateFEELValues(validate, this._expressionLanguage, this._conditionChecker, this._form);
7590
+ errors = [...errors, ...runPresetValidation(field, evaluatedValidation, value)];
7591
+ return errors;
7592
+ }
7593
+
7594
+ /**
7595
+ * Validate a field instance.
7596
+ *
7597
+ * @param {Object} fieldInstance
7598
+ * @param {string} value
7599
+ *
7600
+ * @returns {Array<string>}
7601
+ */
7602
+ validateFieldInstance(fieldInstance, value) {
7603
+ const {
7604
+ id,
7605
+ expressionContextInfo
7606
+ } = fieldInstance;
7607
+ const field = this._formFieldRegistry.get(id);
7608
+ const {
7609
+ type,
7610
+ validate
7611
+ } = field;
7612
+ let errors = [];
7613
+ if (type === 'number') {
7614
+ errors = [...errors, ...runNumberValidation(field, value)];
7583
7615
  }
7584
- if ('validationType' in evaluatedValidation && value && evaluatedValidation.validationType === 'email' && !EMAIL_PATTERN.test(value)) {
7585
- errors = [...errors, 'Field must be a valid email.'];
7616
+ if (!validate) {
7617
+ return errors;
7586
7618
  }
7619
+ const evaluatedValidation = evaluateFEELValues(validate, this._expressionLanguage, expressionContextInfo);
7620
+ errors = [...errors, ...runPresetValidation(field, evaluatedValidation, value)];
7587
7621
  return errors;
7588
7622
  }
7589
7623
  }
7590
- Validator.$inject = ['expressionLanguage', 'conditionChecker', 'form'];
7624
+ Validator.$inject = ['expressionLanguage', 'conditionChecker', 'form', 'formFieldRegistry'];
7591
7625
 
7592
7626
  // helpers //////////
7593
7627
 
7594
- /**
7595
- * Helper function to evaluate optional FEEL validation values.
7596
- */
7597
- function evaluateFEELValues(validate, expressionLanguage, conditionChecker, form) {
7628
+ function runNumberValidation(field, value) {
7629
+ const {
7630
+ decimalDigits,
7631
+ increment
7632
+ } = field;
7633
+ const errors = [];
7634
+ if (value === 'NaN') {
7635
+ errors.push('Value is not a number.');
7636
+ } else if (value) {
7637
+ if (decimalDigits >= 0 && countDecimals(value) > decimalDigits) {
7638
+ errors.push('Value is expected to ' + (decimalDigits === 0 ? 'be an integer' : `have at most ${decimalDigits} decimal digit${decimalDigits > 1 ? 's' : ''}`) + '.');
7639
+ }
7640
+ if (increment) {
7641
+ const bigValue = Big(value);
7642
+ const bigIncrement = Big(increment);
7643
+ const offset = bigValue.mod(bigIncrement);
7644
+ if (offset.cmp(0) !== 0) {
7645
+ const previousValue = bigValue.minus(offset);
7646
+ const nextValue = previousValue.plus(bigIncrement);
7647
+ errors.push(`Please select a valid value, the two nearest valid values are ${previousValue} and ${nextValue}.`);
7648
+ }
7649
+ }
7650
+ }
7651
+ return errors;
7652
+ }
7653
+ function runPresetValidation(field, validation, value) {
7654
+ const errors = [];
7655
+ if (validation.pattern && value && !new RegExp(validation.pattern).test(value)) {
7656
+ errors.push(`Field must match pattern ${validation.pattern}.`);
7657
+ }
7658
+ if (validation.required) {
7659
+ const isUncheckedCheckbox = field.type === 'checkbox' && value === false;
7660
+ const isUnsetValue = minDash.isNil(value) || value === '';
7661
+ const isEmptyMultiselect = Array.isArray(value) && value.length === 0;
7662
+ if (isUncheckedCheckbox || isUnsetValue || isEmptyMultiselect) {
7663
+ errors.push('Field is required.');
7664
+ }
7665
+ }
7666
+ if ('min' in validation && (value || value === 0) && value < validation.min) {
7667
+ errors.push(`Field must have minimum value of ${validation.min}.`);
7668
+ }
7669
+ if ('max' in validation && (value || value === 0) && value > validation.max) {
7670
+ errors.push(`Field must have maximum value of ${validation.max}.`);
7671
+ }
7672
+ if ('minLength' in validation && value && value.trim().length < validation.minLength) {
7673
+ errors.push(`Field must have minimum length of ${validation.minLength}.`);
7674
+ }
7675
+ if ('maxLength' in validation && value && value.trim().length > validation.maxLength) {
7676
+ errors.push(`Field must have maximum length of ${validation.maxLength}.`);
7677
+ }
7678
+ if ('validationType' in validation && value && validation.validationType === 'phone' && !PHONE_PATTERN.test(value)) {
7679
+ errors.push('Field must be a valid international phone number. (e.g. +4930664040900)');
7680
+ }
7681
+ if ('validationType' in validation && value && validation.validationType === 'email' && !EMAIL_PATTERN.test(value)) {
7682
+ errors.push('Field must be a valid email.');
7683
+ }
7684
+ return errors;
7685
+ }
7686
+ function evaluateFEELValues(validate, expressionLanguage, expressionContextInfo) {
7687
+ const evaluatedValidate = {
7688
+ ...validate
7689
+ };
7690
+ VALIDATE_FEEL_PROPERTIES.forEach(property => {
7691
+ const path = property.split('.');
7692
+ const value = minDash.get(evaluatedValidate, path);
7693
+ const evaluatedValue = runExpressionEvaluation(expressionLanguage, value, expressionContextInfo);
7694
+ minDash.set(evaluatedValidate, path, evaluatedValue === null ? undefined : evaluatedValue);
7695
+ });
7696
+ return evaluatedValidate;
7697
+ }
7698
+ function oldEvaluateFEELValues(validate, expressionLanguage, conditionChecker, form) {
7598
7699
  const evaluatedValidate = {
7599
7700
  ...validate
7600
7701
  };
@@ -8339,9 +8440,13 @@ class FormFieldInstanceRegistry {
8339
8440
  return this.getAll().filter(({
8340
8441
  id
8341
8442
  }) => {
8443
+ const formFieldDefinition = this._formFieldRegistry.get(id);
8444
+ if (!formFieldDefinition) {
8445
+ return false;
8446
+ }
8342
8447
  const {
8343
8448
  type
8344
- } = this._formFieldRegistry.get(id);
8449
+ } = formFieldDefinition;
8345
8450
  const {
8346
8451
  config
8347
8452
  } = this._formFields.get(type);
@@ -8601,11 +8706,12 @@ class Form {
8601
8706
  } = this._getState();
8602
8707
  const errors = {};
8603
8708
  const getErrorPath = (id, indexes) => [id, ...Object.values(indexes || {})];
8604
- formFieldInstanceRegistry.getAllKeyed().forEach(({
8605
- id,
8606
- valuePath,
8607
- indexes
8608
- }) => {
8709
+ formFieldInstanceRegistry.getAllKeyed().forEach(fieldInstance => {
8710
+ const {
8711
+ id,
8712
+ valuePath,
8713
+ indexes
8714
+ } = fieldInstance;
8609
8715
  const field = formFieldRegistry.get(id);
8610
8716
 
8611
8717
  // (1) Skip disabled fields
@@ -8613,9 +8719,9 @@ class Form {
8613
8719
  return;
8614
8720
  }
8615
8721
 
8616
- // (2) Validate the field
8722
+ // (2) Validate the field instance
8617
8723
  const value = minDash.get(data, valuePath);
8618
- const fieldErrors = validator.validateField(field, value);
8724
+ const fieldErrors = validator.validateFieldInstance(fieldInstance, value);
8619
8725
  if (fieldErrors.length) {
8620
8726
  minDash.set(errors, getErrorPath(field.id, indexes), fieldErrors);
8621
8727
  }
@@ -8720,26 +8826,26 @@ class Form {
8720
8826
  /**
8721
8827
  * @internal
8722
8828
  *
8723
- * @param { { field: any, indexes: object, value: any } } update
8829
+ * @param { { fieldInstance: any, value: any } } update
8724
8830
  */
8725
8831
  _update(update) {
8726
8832
  const {
8727
- field,
8728
- indexes,
8833
+ fieldInstance,
8729
8834
  value
8730
8835
  } = update;
8836
+ const {
8837
+ id,
8838
+ valuePath,
8839
+ indexes
8840
+ } = fieldInstance;
8731
8841
  const {
8732
8842
  data,
8733
8843
  errors
8734
8844
  } = this._getState();
8735
- const validator = this.get('validator'),
8736
- pathRegistry = this.get('pathRegistry');
8737
- const fieldErrors = validator.validateField(field, value);
8738
- const valuePath = pathRegistry.getValuePath(field, {
8739
- indexes
8740
- });
8845
+ const validator = this.get('validator');
8846
+ const fieldErrors = validator.validateFieldInstance(fieldInstance, value);
8741
8847
  minDash.set(data, valuePath, value);
8742
- minDash.set(errors, [field.id, ...Object.values(indexes || {})], fieldErrors.length ? fieldErrors : undefined);
8848
+ minDash.set(errors, [id, ...Object.values(indexes || {})], fieldErrors.length ? fieldErrors : undefined);
8743
8849
  this._emit('field.updated', update);
8744
8850
  this._setState({
8745
8851
  data: clone(data),
@@ -9006,6 +9112,7 @@ exports.iconsByType = iconsByType;
9006
9112
  exports.isRequired = isRequired;
9007
9113
  exports.pathParse = pathParse;
9008
9114
  exports.pathsEqual = pathsEqual;
9115
+ exports.runExpressionEvaluation = runExpressionEvaluation;
9009
9116
  exports.runRecursively = runRecursively;
9010
9117
  exports.sanitizeDateTimePickerValue = sanitizeDateTimePickerValue;
9011
9118
  exports.sanitizeHTML = sanitizeHTML;
@@ -9018,4 +9125,5 @@ exports.useExpressionEvaluation = useExpressionEvaluation;
9018
9125
  exports.useSingleLineTemplateEvaluation = useSingleLineTemplateEvaluation;
9019
9126
  exports.useTemplateEvaluation = useTemplateEvaluation;
9020
9127
  exports.wrapCSSStyles = wrapCSSStyles;
9128
+ exports.wrapObjectKeysWithUnderscores = wrapObjectKeysWithUnderscores;
9021
9129
  //# sourceMappingURL=index.cjs.map