@bpmn-io/form-js-viewer 1.8.1 → 1.8.4

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
@@ -11,6 +11,7 @@ var isEqual = require('lodash/isEqual');
11
11
  var flatpickr = require('flatpickr');
12
12
  var React = require('preact/compat');
13
13
  var DOMPurify = require('dompurify');
14
+ var lodash = require('lodash');
14
15
  var didi = require('didi');
15
16
  var feelin = require('feelin');
16
17
  var feelers = require('feelers');
@@ -372,10 +373,10 @@ class FeelersTemplating {
372
373
  }
373
374
 
374
375
  /**
375
- * @typedef {Object} ExpressionWithDepth
376
- * @property {number} depth - The depth of the expression in the syntax tree.
377
- * @property {string} expression - The extracted expression
378
- */
376
+ * @typedef {Object} ExpressionWithDepth
377
+ * @property {number} depth - The depth of the expression in the syntax tree.
378
+ * @property {string} expression - The extracted expression
379
+ */
379
380
 
380
381
  /**
381
382
  * Extracts all feel expressions in the template along with their depth in the syntax tree.
@@ -1029,11 +1030,11 @@ const getDOMPurifyConfig = sanitizeStyleTags => {
1029
1030
  };
1030
1031
  };
1031
1032
 
1032
- /**
1033
- * A custom hook to build up security attributes from form configuration.
1034
- *
1035
- * @param {Object} security - The security configuration.
1036
- * @returns {Array} - Returns a tuple with sandbox and allow attributes.
1033
+ /**
1034
+ * A custom hook to build up security attributes from form configuration.
1035
+ *
1036
+ * @param {Object} security - The security configuration.
1037
+ * @returns {Array} - Returns a tuple with sandbox and allow attributes.
1037
1038
  */
1038
1039
  function useSecurityAttributesMap(security) {
1039
1040
  const securityMemoized = useDeepCompareMemoize(security);
@@ -1320,17 +1321,6 @@ function useFlushDebounce(func) {
1320
1321
  return [debounceFunc, flushFunc];
1321
1322
  }
1322
1323
 
1323
- function useEffectOnChange(value, callback, dependencies = []) {
1324
- const previousValue = usePrevious(value);
1325
- React.useEffect(() => {
1326
- if (value !== previousValue) {
1327
- callback();
1328
- }
1329
-
1330
- // eslint-disable-next-line react-hooks/exhaustive-deps
1331
- }, [value, ...dependencies]);
1332
- }
1333
-
1334
1324
  /**
1335
1325
  * Template a string reactively based on form data. If the string is not a template, it is returned as is.
1336
1326
  * Memoised to minimize re-renders
@@ -1521,6 +1511,18 @@ function formatTimezoneOffset(minutes) {
1521
1511
  function isInvalidDateString(value) {
1522
1512
  return isNaN(new Date(Date.parse(value)).getTime());
1523
1513
  }
1514
+ function getNullDateTime() {
1515
+ return {
1516
+ date: new Date(Date.parse(null)),
1517
+ time: null
1518
+ };
1519
+ }
1520
+ function isValidDate(date) {
1521
+ return date && !isNaN(date.getTime());
1522
+ }
1523
+ function isValidTime(time) {
1524
+ return !isNaN(parseInt(time));
1525
+ }
1524
1526
  function _getSignedPaddedHours(minutes) {
1525
1527
  if (minutes > 0) {
1526
1528
  return '-' + _getZeroPaddedString(Math.floor(minutes / 60));
@@ -1959,6 +1961,7 @@ function FormField(props) {
1959
1961
  } = props;
1960
1962
  const formFields = useService('formFields'),
1961
1963
  viewerCommands = useService('viewerCommands', false),
1964
+ formFieldInstanceRegistry = useService('formFieldInstanceRegistry', false),
1962
1965
  pathRegistry = useService('pathRegistry'),
1963
1966
  eventBus = useService('eventBus'),
1964
1967
  form = useService('form');
@@ -1985,6 +1988,7 @@ function FormField(props) {
1985
1988
  throw new Error(`cannot render field <${field.type}>`);
1986
1989
  }
1987
1990
  const fieldConfig = FormFieldComponent.config;
1991
+ const localExpressionContext = hooks.useContext(LocalExpressionContext);
1988
1992
  const valuePath = hooks.useMemo(() => pathRegistry.getValuePath(field, {
1989
1993
  indexes
1990
1994
  }), [field, indexes, pathRegistry]);
@@ -1994,6 +1998,22 @@ function FormField(props) {
1994
1998
 
1995
1999
  // add precedence: global readonly > form field disabled
1996
2000
  const disabled = !properties.readOnly && (properties.disabled || field.disabled || false);
2001
+ const hidden = useCondition(field.conditional && field.conditional.hide || null);
2002
+
2003
+ // register form field instance
2004
+ hooks.useEffect(() => {
2005
+ if (formFieldInstanceRegistry && !hidden) {
2006
+ const instanceId = formFieldInstanceRegistry.add({
2007
+ id: field.id,
2008
+ expressionContextInfo: localExpressionContext,
2009
+ valuePath,
2010
+ indexes
2011
+ });
2012
+ return () => {
2013
+ formFieldInstanceRegistry.remove(instanceId);
2014
+ };
2015
+ }
2016
+ }, [formFieldInstanceRegistry, field.id, localExpressionContext, valuePath, indexes, hidden]);
1997
2017
 
1998
2018
  // ensures the initial validation behavior can be re-triggered upon form reset
1999
2019
  hooks.useEffect(() => {
@@ -2011,7 +2031,8 @@ function FormField(props) {
2011
2031
  };
2012
2032
  }, [eventBus, viewerCommands]);
2013
2033
  hooks.useEffect(() => {
2014
- if (initialValidationTrigger && initialValue) {
2034
+ const hasInitialValue = initialValue && !isEqual(initialValue, []);
2035
+ if (initialValidationTrigger && hasInitialValue) {
2015
2036
  setInitialValidationTrigger(false);
2016
2037
  viewerCommands.updateFieldValidation(field, initialValue, indexes);
2017
2038
  }
@@ -2031,7 +2052,6 @@ function FormField(props) {
2031
2052
  formField: field
2032
2053
  });
2033
2054
  }, [eventBus, field]);
2034
- const hidden = useCondition(field.conditional && field.conditional.hide || null);
2035
2055
  const onChangeIndexed = hooks.useCallback(update => {
2036
2056
  // any data change will trigger validation
2037
2057
  setInitialValidationTrigger(false);
@@ -2176,7 +2196,7 @@ function RowsRenderer(props) {
2176
2196
  Row
2177
2197
  } = hooks.useContext(FormRenderContext);
2178
2198
  return jsxRuntime.jsxs(jsxRuntime.Fragment, {
2179
- children: [" ", rows.map(row => {
2199
+ children: [' ', rows.map(row => {
2180
2200
  const {
2181
2201
  components = []
2182
2202
  } = row;
@@ -2202,7 +2222,7 @@ function RowsRenderer(props) {
2202
2222
  });
2203
2223
  })
2204
2224
  });
2205
- }), " "]
2225
+ }), ' ']
2206
2226
  });
2207
2227
  }
2208
2228
 
@@ -2332,23 +2352,23 @@ function InputAdorner(props) {
2332
2352
  'fjs-disabled': disabled,
2333
2353
  'fjs-readonly': readonly
2334
2354
  }, {
2335
- 'hasErrors': hasErrors
2355
+ hasErrors: hasErrors
2336
2356
  }),
2337
2357
  ref: rootRef,
2338
2358
  children: [pre && jsxRuntime.jsxs("span", {
2339
2359
  class: "fjs-input-adornment border-right border-radius-left",
2340
2360
  onClick: onAdornmentClick,
2341
- children: [" ", minDash.isString(pre) ? jsxRuntime.jsx("span", {
2361
+ children: [' ', minDash.isString(pre) ? jsxRuntime.jsx("span", {
2342
2362
  class: "fjs-input-adornment-text",
2343
2363
  children: pre
2344
- }) : pre, " "]
2364
+ }) : pre, ' ']
2345
2365
  }), children, post && jsxRuntime.jsxs("span", {
2346
2366
  class: "fjs-input-adornment border-left border-radius-right",
2347
2367
  onClick: onAdornmentClick,
2348
- children: [" ", minDash.isString(post) ? jsxRuntime.jsx("span", {
2368
+ children: [' ', minDash.isString(post) ? jsxRuntime.jsx("span", {
2349
2369
  class: "fjs-input-adornment-text",
2350
2370
  children: post
2351
- }) : post, " "]
2371
+ }) : post, ' ']
2352
2372
  })]
2353
2373
  });
2354
2374
  }
@@ -2397,7 +2417,9 @@ function Datepicker(props) {
2397
2417
  clickOpens: false,
2398
2418
  // TODO: support dates prior to 1900 (https://github.com/bpmn-io/form-js/issues/533)
2399
2419
  minDate: disallowPassedDates ? 'today' : '01/01/1900',
2400
- errorHandler: () => {/* do nothing, we expect the values to sometimes be erronous and we don't want warnings polluting the console */}
2420
+ errorHandler: () => {
2421
+ /* do nothing, we expect the values to sometimes be erronous and we don't want warnings polluting the console */
2422
+ }
2401
2423
  };
2402
2424
  const instance = flatpickr(dateInputRef.current, config);
2403
2425
  setFlatpickrInstance(instance);
@@ -2502,7 +2524,7 @@ function Datepicker(props) {
2502
2524
  });
2503
2525
  }
2504
2526
 
2505
- var _path$v, _path2$5;
2527
+ var _path$v, _path2$4;
2506
2528
  function _extends$w() { _extends$w = 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$w.apply(this, arguments); }
2507
2529
  var SvgClock = function SvgClock(props) {
2508
2530
  return /*#__PURE__*/React__namespace.createElement("svg", _extends$w({
@@ -2514,7 +2536,7 @@ var SvgClock = function SvgClock(props) {
2514
2536
  }, props), _path$v || (_path$v = /*#__PURE__*/React__namespace.createElement("path", {
2515
2537
  fill: "currentColor",
2516
2538
  d: "M13 14.41 18.59 20 20 18.59l-5-5.01V5h-2v9.41Z"
2517
- })), _path2$5 || (_path2$5 = /*#__PURE__*/React__namespace.createElement("path", {
2539
+ })), _path2$4 || (_path2$4 = /*#__PURE__*/React__namespace.createElement("path", {
2518
2540
  fill: "currentColor",
2519
2541
  fillRule: "evenodd",
2520
2542
  d: "M6.222 25.64A14 14 0 1 0 21.778 2.36 14 14 0 0 0 6.222 25.64ZM7.333 4.023a12 12 0 1 1 13.334 19.955A12 12 0 0 1 7.333 4.022Z",
@@ -2605,7 +2627,7 @@ function DropdownList(props) {
2605
2627
  children: [values.length > 0 && values.map((v, i) => {
2606
2628
  return jsxRuntime.jsx("div", {
2607
2629
  class: classNames('fjs-dropdownlist-item', {
2608
- 'focused': focusedValueIndex === i
2630
+ focused: focusedValueIndex === i
2609
2631
  }),
2610
2632
  onMouseMove: mouseControl ? undefined : e => onMouseMovedInKeyboardMode(e, i),
2611
2633
  onMouseEnter: mouseControl ? () => setFocusedValueIndex(i) : undefined,
@@ -2757,11 +2779,9 @@ function Timepicker(props) {
2757
2779
  disabled: disabled,
2758
2780
  readOnly: readonly,
2759
2781
  placeholder: use24h ? 'hh:mm' : 'hh:mm ?m',
2760
- autoComplete: "off"
2761
-
2762
- // @ts-ignore
2763
- ,
2782
+ autoComplete: "off",
2764
2783
  onInput: e => {
2784
+ // @ts-expect-error
2765
2785
  setRawValue(e.target.value);
2766
2786
  useDropdown && setDropdownIsOpen(false);
2767
2787
  },
@@ -2815,14 +2835,8 @@ function Datetime(props) {
2815
2835
  formId
2816
2836
  } = hooks.useContext(FormContext);
2817
2837
  const dateTimeGroupRef = hooks.useRef();
2818
- const getNullDateTime = () => ({
2819
- date: new Date(Date.parse(null)),
2820
- time: null
2821
- });
2822
2838
  const [dateTime, setDateTime] = hooks.useState(getNullDateTime());
2823
2839
  const [dateTimeUpdateRequest, setDateTimeUpdateRequest] = hooks.useState(null);
2824
- const isValidDate = date => date && !isNaN(date.getTime());
2825
- const isValidTime = time => !isNaN(parseInt(time));
2826
2840
  const useDatePicker = hooks.useMemo(() => subtype === DATETIME_SUBTYPES.DATE || subtype === DATETIME_SUBTYPES.DATETIME, [subtype]);
2827
2841
  const useTimePicker = hooks.useMemo(() => subtype === DATETIME_SUBTYPES.TIME || subtype === DATETIME_SUBTYPES.DATETIME, [subtype]);
2828
2842
  const onDateTimeBlur = hooks.useCallback(e => {
@@ -2877,11 +2891,14 @@ function Datetime(props) {
2877
2891
  } else if (subtype === DATETIME_SUBTYPES.DATETIME && isValidDate(date) && isValidTime(time)) {
2878
2892
  newDateTimeValue = serializeDateTime(date, time, timeSerializingFormat);
2879
2893
  }
2894
+ if (value === newDateTimeValue) {
2895
+ return;
2896
+ }
2880
2897
  onChange({
2881
2898
  value: newDateTimeValue,
2882
2899
  field
2883
2900
  });
2884
- }, [field, onChange, subtype, timeSerializingFormat]);
2901
+ }, [value, field, onChange, subtype, timeSerializingFormat]);
2885
2902
  hooks.useEffect(() => {
2886
2903
  if (dateTimeUpdateRequest) {
2887
2904
  if (dateTimeUpdateRequest.refreshOnly) {
@@ -3163,7 +3180,7 @@ var SvgChecklist = function SvgChecklist(props) {
3163
3180
  };
3164
3181
  var ChecklistIcon = SvgChecklist;
3165
3182
 
3166
- var _path$r, _path2$4, _path3;
3183
+ var _path$r, _path2$3, _path3;
3167
3184
  function _extends$s() { _extends$s = 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$s.apply(this, arguments); }
3168
3185
  var SvgDatetime = function SvgDatetime(props) {
3169
3186
  return /*#__PURE__*/React__namespace.createElement("svg", _extends$s({
@@ -3174,7 +3191,7 @@ var SvgDatetime = function SvgDatetime(props) {
3174
3191
  }, props), _path$r || (_path$r = /*#__PURE__*/React__namespace.createElement("path", {
3175
3192
  fillRule: "evenodd",
3176
3193
  d: "M37.908 13.418h-5.004v-2.354h-1.766v2.354H21.13v-2.354h-1.766v2.354H14.36a2.07 2.07 0 0 0-2.06 2.06v23.549a2.07 2.07 0 0 0 2.06 2.06h6.77v-1.766h-6.358a.707.707 0 0 1-.706-.706V15.89c0-.39.316-.707.706-.707h4.592v2.355h1.766v-2.355h10.008v2.355h1.766v-2.355h4.592a.71.71 0 0 1 .707.707v6.358h1.765v-6.77c0-1.133-.927-2.06-2.06-2.06z"
3177
- })), _path2$4 || (_path2$4 = /*#__PURE__*/React__namespace.createElement("path", {
3194
+ })), _path2$3 || (_path2$3 = /*#__PURE__*/React__namespace.createElement("path", {
3178
3195
  d: "m35.13 37.603 1.237-1.237-3.468-3.475v-5.926h-1.754v6.654l3.984 3.984Z"
3179
3196
  })), _path3 || (_path3 = /*#__PURE__*/React__namespace.createElement("path", {
3180
3197
  fillRule: "evenodd",
@@ -3183,7 +3200,7 @@ var SvgDatetime = function SvgDatetime(props) {
3183
3200
  };
3184
3201
  var DatetimeIcon = SvgDatetime;
3185
3202
 
3186
- var _path$q, _path2$3;
3203
+ var _path$q, _path2$2;
3187
3204
  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); }
3188
3205
  var SvgTaglist = function SvgTaglist(props) {
3189
3206
  return /*#__PURE__*/React__namespace.createElement("svg", _extends$r({
@@ -3194,7 +3211,7 @@ var SvgTaglist = function SvgTaglist(props) {
3194
3211
  }, props), _path$q || (_path$q = /*#__PURE__*/React__namespace.createElement("path", {
3195
3212
  fillRule: "evenodd",
3196
3213
  d: "M45 16a3 3 0 0 1 3 3v16a3 3 0 0 1-3 3H9a3 3 0 0 1-3-3V19a3 3 0 0 1 3-3h36Zm0 2H9a1 1 0 0 0-1 1v16a1 1 0 0 0 1 1h36a1 1 0 0 0 1-1V19a1 1 0 0 0-1-1Z"
3197
- })), _path2$3 || (_path2$3 = /*#__PURE__*/React__namespace.createElement("path", {
3214
+ })), _path2$2 || (_path2$2 = /*#__PURE__*/React__namespace.createElement("path", {
3198
3215
  d: "M11 22a1 1 0 0 1 1-1h19a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H12a1 1 0 0 1-1-1V22Z"
3199
3216
  })));
3200
3217
  };
@@ -3236,10 +3253,12 @@ var SvgGroup = function SvgGroup(props) {
3236
3253
  xmlns: "http://www.w3.org/2000/svg",
3237
3254
  width: 54,
3238
3255
  height: 54,
3239
- fill: "currentcolor"
3256
+ fill: "none"
3240
3257
  }, props), _path$p || (_path$p = /*#__PURE__*/React__namespace.createElement("path", {
3258
+ fill: "#000",
3241
3259
  fillRule: "evenodd",
3242
- d: "M8 33v5a1 1 0 0 0 1 1h4v2H9a3 3 0 0 1-3-3v-5h2Zm18 6v2H15v-2h11Zm13 0v2H28v-2h11Zm9-6v5a3 3 0 0 1-3 3h-4v-2h4a1 1 0 0 0 .993-.883L46 38v-5h2ZM8 22v9H6v-9h2Zm40 0v9h-2v-9h2Zm-35-9v2H9a1 1 0 0 0-.993.883L8 16v4H6v-4a3 3 0 0 1 3-3h4Zm32 0a3 3 0 0 1 3 3v4h-2v-4a1 1 0 0 0-.883-.993L45 15h-4v-2h4Zm-6 0v2H28v-2h11Zm-13 0v2H15v-2h11Z"
3260
+ d: "M4.05 42.132v1.164c0 .693.604 1.254 1.35 1.254h1.35v-2.507h-2.7v.09Zm0-2.328h2.7v-2.328h-2.7v2.328Zm0-4.656h2.7V32.82h-2.7v2.328Zm0-4.656h2.7v-2.328h-2.7v2.328Zm0-4.656h2.7v-2.328h-2.7v2.328Zm0-4.656h2.7v-2.328h-2.7v2.328Zm0-4.656h2.7v-2.328h-2.7v2.328Zm0-4.656v.09h2.7V9.45H5.4c-.746 0-1.35.561-1.35 1.254v1.164Zm5.4-2.418v2.507h2.7V9.45h-2.7Zm5.4 0v2.507h2.7V9.45h-2.7Zm5.4 0v2.507h2.7V9.45h-2.7Zm5.4 0v2.507h2.7V9.45h-2.7Zm5.4 0v2.507h2.7V9.45h-2.7Zm5.4 0v2.507h2.7V9.45h-2.7Zm5.4 0v2.507h2.7V9.45h-2.7Zm5.4 0v2.507h2.7v-1.253c0-.693-.604-1.254-1.35-1.254h-1.35Zm2.7 4.746h-2.7v2.328h2.7v-2.328Zm0 4.656h-2.7v2.328h2.7v-2.328Zm0 4.656h-2.7v2.328h2.7v-2.328Zm0 4.656h-2.7v2.328h2.7v-2.328Zm0 4.656h-2.7v2.328h2.7V32.82Zm0 4.656h-2.7v2.328h2.7v-2.328Zm0 4.656v-.09h-2.7v2.508h1.35c.746 0 1.35-.561 1.35-1.254v-1.164Zm-5.4 2.418v-2.507h-2.7v2.507h2.7Zm-5.4 0v-2.507h-2.7v2.507h2.7Zm-5.4 0v-2.507h-2.7v2.507h2.7Zm-5.4 0v-2.507h-2.7v2.507h2.7Zm-5.4 0v-2.507h-2.7v2.507h2.7Zm-5.4 0v-2.507h-2.7v2.507h2.7Zm-5.4 0v-2.507h-2.7v2.507h2.7Z",
3261
+ clipRule: "evenodd"
3243
3262
  })));
3244
3263
  };
3245
3264
  var GroupIcon = SvgGroup;
@@ -3329,7 +3348,7 @@ var SvgDynamicList = function SvgDynamicList(props) {
3329
3348
  }, props), _path$j || (_path$j = /*#__PURE__*/React__namespace.createElement("path", {
3330
3349
  fill: "currentColor",
3331
3350
  fillRule: "evenodd",
3332
- d: "M2.7 43.296v1.254c0 .746.604 1.35 1.35 1.35h1.275v-1.795c.049.14.075.29.075.445v-1.254h-.075V43.2H4.05c.177 0 .347.034.502.096H2.7Zm2.7-2.507v-2.507H2.7v2.507h2.7Zm0-5.014v-2.507H2.7v2.507h2.7Zm0-5.014v-2.507H2.7v2.507h2.7Zm0-5.015V23.24H2.7v2.507h2.7Zm0-5.014v-2.507H2.7v2.507h2.7Zm0-5.014V13.21H2.7v2.507h2.7Zm-2.7-5.014h1.852a1.346 1.346 0 0 1-.502.096h1.275v-.096H5.4V9.45c0 .156-.026.306-.075.445V8.1H4.05A1.35 1.35 0 0 0 2.7 9.45v1.254Zm5.175.096h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1-2.7v1.795a1.348 1.348 0 0 1-.075-.445v1.254h.075v.096h1.275c-.177 0-.347-.034-.502-.096H51.3V9.45a1.35 1.35 0 0 0-1.35-1.35h-1.275Zm-.075 5.11v2.508h2.7V13.21h-2.7Zm0 5.015v2.507h2.7v-2.507h-2.7Zm0 5.014v2.507h2.7V23.24h-2.7Zm0 5.015v2.507h2.7v-2.507h-2.7Zm0 5.014v2.507h2.7v-2.507h-2.7Zm0 5.014v2.507h2.7v-2.507h-2.7Zm2.7 5.014h-1.852c.155-.062.325-.096.502-.096h-1.275v.096H48.6v1.254c0-.156.026-.305.075-.445V45.9h1.275a1.35 1.35 0 0 0 1.35-1.35v-1.254Zm-5.175-.096h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7ZM16.2 17.55a4.05 4.05 0 0 1 4.05 4.05v1.35A4.05 4.05 0 0 1 16.2 27h-1.35a4.05 4.05 0 0 1-4.05-4.05V21.6a4.05 4.05 0 0 1 4.05-4.05h1.35Zm0 2.7h-1.35a1.35 1.35 0 0 0-1.35 1.35v1.35c0 .746.604 1.35 1.35 1.35h1.35a1.35 1.35 0 0 0 1.35-1.35V21.6a1.35 1.35 0 0 0-1.35-1.35Zm27 1.35a4.05 4.05 0 0 0-4.05-4.05H29.7a4.05 4.05 0 0 0-4.05 4.05v1.35A4.05 4.05 0 0 0 29.7 27h9.45a4.05 4.05 0 0 0 4.05-4.05V21.6Zm-13.5-1.35h9.45c.746 0 1.35.604 1.35 1.35v1.35a1.35 1.35 0 0 1-1.35 1.35H29.7a1.35 1.35 0 0 1-1.35-1.35V21.6c0-.746.604-1.35 1.35-1.35ZM43.2 37.8a4.05 4.05 0 0 0-4.05-4.05H29.7a4.05 4.05 0 0 0-4.05 4.05v1.35h2.7V37.8c0-.746.604-1.35 1.35-1.35h9.45c.746 0 1.35.604 1.35 1.35v1.35h2.7V37.8Zm-27-4.05a4.05 4.05 0 0 1 4.05 4.05v1.35h-2.7V37.8a1.35 1.35 0 0 0-1.35-1.35h-1.35a1.35 1.35 0 0 0-1.35 1.35v1.35h-2.7V37.8a4.05 4.05 0 0 1 4.05-4.05h1.35Z",
3351
+ d: "M2.7 43.296v1.254c0 .746.604 1.35 1.35 1.35h1.275v-1.795c.049.14.075.29.075.445v-1.254h-.075V43.2H4.05c.177 0 .347.034.502.096H2.7Zm2.7-2.507v-2.507H2.7v2.507h2.7Zm0-5.014v-2.507H2.7v2.507h2.7Zm0-5.014v-2.507H2.7v2.507h2.7Zm0-5.015V23.24H2.7v2.507h2.7Zm0-5.014v-2.507H2.7v2.507h2.7Zm0-5.014V13.21H2.7v2.507h2.7Zm-2.7-5.014h1.852a1.346 1.346 0 0 1-.502.096h1.275v-.096H5.4V9.45c0 .156-.026.306-.075.445V8.1H4.05A1.35 1.35 0 0 0 2.7 9.45v1.254Zm5.175.096h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1 0h2.55V8.1h-2.55v2.7Zm5.1-2.7v1.795a1.348 1.348 0 0 1-.075-.445v1.254h.075v.096h1.275a1.35 1.35 0 0 1-.502-.096H51.3V9.45a1.35 1.35 0 0 0-1.35-1.35h-1.275Zm-.075 5.11v2.508h2.7V13.21h-2.7Zm0 5.015v2.507h2.7v-2.507h-2.7Zm0 5.014v2.507h2.7V23.24h-2.7Zm0 5.015v2.507h2.7v-2.507h-2.7Zm0 5.014v2.507h2.7v-2.507h-2.7Zm0 5.014v2.507h2.7v-2.507h-2.7Zm2.7 5.014h-1.852a1.35 1.35 0 0 1 .502-.096h-1.275v.096H48.6v1.254c0-.156.026-.305.075-.445V45.9h1.275a1.35 1.35 0 0 0 1.35-1.35v-1.254Zm-5.175-.096h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7Zm-5.1 0h-2.55v2.7h2.55v-2.7ZM16.2 17.55a4.05 4.05 0 0 1 4.05 4.05v1.35A4.05 4.05 0 0 1 16.2 27h-1.35a4.05 4.05 0 0 1-4.05-4.05V21.6a4.05 4.05 0 0 1 4.05-4.05h1.35Zm0 2.7h-1.35a1.35 1.35 0 0 0-1.35 1.35v1.35c0 .746.604 1.35 1.35 1.35h1.35a1.35 1.35 0 0 0 1.35-1.35V21.6a1.35 1.35 0 0 0-1.35-1.35Zm27 1.35a4.05 4.05 0 0 0-4.05-4.05H29.7a4.05 4.05 0 0 0-4.05 4.05v1.35A4.05 4.05 0 0 0 29.7 27h9.45a4.05 4.05 0 0 0 4.05-4.05V21.6Zm-13.5-1.35h9.45c.746 0 1.35.604 1.35 1.35v1.35a1.35 1.35 0 0 1-1.35 1.35H29.7a1.35 1.35 0 0 1-1.35-1.35V21.6c0-.746.604-1.35 1.35-1.35ZM43.2 37.8a4.05 4.05 0 0 0-4.05-4.05H29.7a4.05 4.05 0 0 0-4.05 4.05v1.35h2.7V37.8c0-.746.604-1.35 1.35-1.35h9.45c.746 0 1.35.604 1.35 1.35v1.35h2.7V37.8Zm-27-4.05a4.05 4.05 0 0 1 4.05 4.05v1.35h-2.7V37.8a1.35 1.35 0 0 0-1.35-1.35h-1.35a1.35 1.35 0 0 0-1.35 1.35v1.35h-2.7V37.8a4.05 4.05 0 0 1 4.05-4.05h1.35Z",
3333
3352
  clipRule: "evenodd"
3334
3353
  })));
3335
3354
  };
@@ -3413,7 +3432,7 @@ var SvgTextarea = function SvgTextarea(props) {
3413
3432
  };
3414
3433
  var TextareaIcon = SvgTextarea;
3415
3434
 
3416
- var _path$d, _path2$2;
3435
+ var _path$d;
3417
3436
  function _extends$d() { _extends$d = 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$d.apply(this, arguments); }
3418
3437
  var SvgIFrame = function SvgIFrame(props) {
3419
3438
  return /*#__PURE__*/React__namespace.createElement("svg", _extends$d({
@@ -3422,12 +3441,9 @@ var SvgIFrame = function SvgIFrame(props) {
3422
3441
  height: 54,
3423
3442
  fill: "none"
3424
3443
  }, props), _path$d || (_path$d = /*#__PURE__*/React__namespace.createElement("path", {
3425
- fill: "currentcolor",
3426
- d: "M34.467 37.3 41 31l-6.533-6.3-1.32 1.273L38.36 31l-5.213 5.027 1.32 1.273ZM19.533 24.7 13 31l6.533 6.3 1.32-1.273L15.64 31l5.214-5.027-1.32-1.273Zm4.127 14.832 1.805.468 4.875-17.532L28.535 22 23.66 39.532Z"
3427
- })), _path2$2 || (_path2$2 = /*#__PURE__*/React__namespace.createElement("path", {
3428
- fill: "currentcolor",
3444
+ fill: "currentColor",
3429
3445
  fillRule: "evenodd",
3430
- d: "M46 9a3 3 0 0 1 3 3v30a3 3 0 0 1-3 3H8a3 3 0 0 1-3-3V12a3 3 0 0 1 3-3h38Zm0 2H8a1 1 0 0 0-1 1v4h40v-4a1 1 0 0 0-1-1ZM7 42V18h40v24a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1Z",
3446
+ d: "M45.658 9.45c1.625 0 2.942 1.36 2.942 3.039V22.95h-1.961v-4.383H7.36V41.51c0 .56.44 1.013.98 1.013H27v2.026H8.342c-1.625 0-2.942-1.36-2.942-3.039V12.489c0-1.678 1.317-3.039 2.942-3.039h37.316Zm0 2.026H8.342c-.542 0-.98.454-.98 1.013v4.052h39.277v-4.052c0-.56-.44-1.013-.98-1.013ZM31.05 35.775A8.768 8.768 0 0 1 39.825 27a8.768 8.768 0 0 1 8.775 8.775 8.768 8.768 0 0 1-8.775 8.775 8.768 8.768 0 0 1-8.775-8.775Zm12.388-.516h3.097c-.206-2.581-1.858-4.646-4.026-5.678.62 1.548.93 3.613.93 5.678Zm-5.162 2.065c.207 3.303 1.136 4.955 1.549 5.161.413-.206 1.239-1.858 1.445-5.161h-2.994Zm1.446-8.26c-.31.207-1.342 2.272-1.446 6.195h2.994c-.103-3.923-1.135-5.988-1.548-6.194Zm-3.51 6.195c.103-2.065.31-4.13.929-5.678-2.168 1.032-3.82 3.097-4.026 5.678h3.097Zm0 2.065h-2.89c.515 2.064 1.96 3.82 3.819 4.645-.516-1.342-.826-2.994-.93-4.645Zm7.226 0c-.103 1.755-.413 3.303-.929 4.645 1.858-.826 3.304-2.58 3.923-4.645h-2.994Z",
3431
3447
  clipRule: "evenodd"
3432
3448
  })));
3433
3449
  };
@@ -3798,7 +3814,7 @@ function Numberfield(props) {
3798
3814
  'fjs-disabled': disabled,
3799
3815
  'fjs-readonly': readonly
3800
3816
  }, {
3801
- 'hasErrors': errors.length
3817
+ hasErrors: errors.length
3802
3818
  }),
3803
3819
  children: [jsxRuntime.jsx("input", {
3804
3820
  ref: inputRef,
@@ -3810,7 +3826,6 @@ function Numberfield(props) {
3810
3826
  onKeyPress: onKeyPress,
3811
3827
  onBlur: onInputBlur,
3812
3828
  onFocus: onInputFocus
3813
-
3814
3829
  // @ts-ignore
3815
3830
  ,
3816
3831
  onInput: e => setValue(e.target.value, true),
@@ -4119,10 +4134,10 @@ function SearchableSelect(props) {
4119
4134
  return jsxRuntime.jsxs(jsxRuntime.Fragment, {
4120
4135
  children: [jsxRuntime.jsxs("div", {
4121
4136
  class: classNames('fjs-input-group', {
4122
- 'disabled': disabled,
4123
- 'readonly': readonly
4137
+ disabled: disabled,
4138
+ readonly: readonly
4124
4139
  }, {
4125
- 'hasErrors': errors.length
4140
+ hasErrors: errors.length
4126
4141
  }),
4127
4142
  children: [jsxRuntime.jsx("input", {
4128
4143
  disabled: disabled,
@@ -4146,7 +4161,7 @@ function SearchableSelect(props) {
4146
4161
  setValue(null);
4147
4162
  e.preventDefault();
4148
4163
  },
4149
- children: [jsxRuntime.jsx(XMarkIcon, {}), " "]
4164
+ children: [jsxRuntime.jsx(XMarkIcon, {}), ' ']
4150
4165
  }), jsxRuntime.jsx("span", {
4151
4166
  class: "fjs-select-arrow",
4152
4167
  onMouseDown: e => onAngelMouseDown(e),
@@ -4240,7 +4255,7 @@ function SimpleSelect(props) {
4240
4255
  disabled,
4241
4256
  readonly
4242
4257
  }, {
4243
- 'hasErrors': errors.length
4258
+ hasErrors: errors.length
4244
4259
  }),
4245
4260
  onFocus: onInputFocus,
4246
4261
  onBlur: onInputBlur,
@@ -4948,7 +4963,7 @@ function Html(props) {
4948
4963
  Html.config = {
4949
4964
  type: type$4,
4950
4965
  keyed: false,
4951
- label: 'HTML',
4966
+ label: 'HTML view',
4952
4967
  group: 'presentation',
4953
4968
  create: (options = {}) => ({
4954
4969
  content: '',
@@ -4960,7 +4975,8 @@ const type$3 = 'expression';
4960
4975
  function ExpressionField(props) {
4961
4976
  const {
4962
4977
  field,
4963
- onChange
4978
+ onChange,
4979
+ value
4964
4980
  } = props;
4965
4981
  const {
4966
4982
  computeOn,
@@ -4969,18 +4985,20 @@ function ExpressionField(props) {
4969
4985
  const evaluation = useExpressionEvaluation(expression);
4970
4986
  const evaluationMemo = useDeepCompareMemoize(evaluation);
4971
4987
  const eventBus = useService('eventBus');
4988
+ const expressionLoopPreventer = useService('expressionLoopPreventer');
4972
4989
  const sendValue = hooks.useCallback(() => {
4973
4990
  onChange && onChange({
4974
4991
  field,
4975
- value: evaluationMemo
4992
+ value: evaluationMemo,
4993
+ shouldNotRecompute: true
4976
4994
  });
4977
4995
  }, [field, evaluationMemo, onChange]);
4978
- useEffectOnChange(evaluationMemo, () => {
4979
- if (computeOn !== 'change') {
4996
+ hooks.useEffect(() => {
4997
+ if (computeOn !== 'change' || lodash.isEqual(evaluationMemo, value) || !expressionLoopPreventer.registerExpressionExecution(this)) {
4980
4998
  return;
4981
4999
  }
4982
5000
  sendValue();
4983
- }, [computeOn, sendValue]);
5001
+ });
4984
5002
  hooks.useEffect(() => {
4985
5003
  if (computeOn === 'presubmit') {
4986
5004
  eventBus.on('presubmit', sendValue);
@@ -4994,6 +5012,7 @@ ExpressionField.config = {
4994
5012
  label: 'Expression',
4995
5013
  group: 'basic-input',
4996
5014
  keyed: true,
5015
+ emptyValue: null,
4997
5016
  escapeGridRender: true,
4998
5017
  create: (options = {}) => ({
4999
5018
  computeOn: 'change',
@@ -5644,7 +5663,7 @@ function Lightbox(props) {
5644
5663
  style: "margin: 15px 20px 15px 10px; align-self: center; color: var(--cds-icon-primary, #404040)",
5645
5664
  children: jsxRuntime.jsx(Logo, {})
5646
5665
  }), jsxRuntime.jsxs("span", {
5647
- children: ["Web-based tooling for BPMN, DMN, and forms powered by ", jsxRuntime.jsx("a", {
5666
+ children: ["Web-based tooling for BPMN, DMN, and forms powered by", ' ', jsxRuntime.jsx("a", {
5648
5667
  href: "https://bpmn.io",
5649
5668
  target: "_blank",
5650
5669
  rel: "noopener",
@@ -5734,7 +5753,12 @@ function FormComponent(props) {
5734
5753
  });
5735
5754
  }
5736
5755
 
5737
- const formFields = [Button, Checkbox, Checklist, Default, DynamicList, Numberfield, Datetime, Radio, Select, Taglist, Textfield, Textarea, ExpressionField, Text, Image, Table, Html, Spacer, Separator, Group, DynamicList, IFrame];
5756
+ const formFields = [/* Input */
5757
+ Textfield, Textarea, Numberfield, Datetime, ExpressionField, /* Selection */
5758
+ Checkbox, Checklist, Radio, Select, Taglist, /* Presentation */
5759
+ Text, Image, Table, Html, Spacer, Separator, /* Containers */
5760
+ Group, DynamicList, IFrame, /* Other */
5761
+ Button, Default];
5738
5762
 
5739
5763
  class FormFields {
5740
5764
  constructor() {
@@ -5904,8 +5928,7 @@ class ConditionChecker {
5904
5928
  const {
5905
5929
  getFilterPath = (field, indexes) => this._pathRegistry.getValuePath(field, {
5906
5930
  indexes
5907
- }),
5908
- leafNodeDeletionOnly = false
5931
+ })
5909
5932
  } = options;
5910
5933
  const _applyConditionsWithinScope = (rootField, scopeContext, startHidden = false) => {
5911
5934
  const {
@@ -5936,7 +5959,7 @@ class ConditionChecker {
5936
5959
  context.isHidden = startHidden || context.isHidden || conditional && this._checkHideCondition(conditional, localExpressionContext);
5937
5960
 
5938
5961
  // if a field is repeatable and visible, we need to implement custom recursion on its children
5939
- if (isRepeatable && (!context.isHidden || leafNodeDeletionOnly)) {
5962
+ if (isRepeatable && !context.isHidden) {
5940
5963
  // prevent the regular recursion behavior of executeRecursivelyOnFields
5941
5964
  context.preventRecursion = true;
5942
5965
  const repeaterValuePath = this._pathRegistry.getValuePath(field, {
@@ -5968,7 +5991,7 @@ class ConditionChecker {
5968
5991
  }
5969
5992
 
5970
5993
  // if we have a hidden repeatable field, and the data structure allows, we clear it directly at the root and stop recursion
5971
- if (context.isHidden && !leafNodeDeletionOnly && isRepeatable) {
5994
+ if (context.isHidden && isRepeatable) {
5972
5995
  context.preventRecursion = true;
5973
5996
  this._cleanlyClearDataAtPath(getFilterPath(field, indexes), workingData);
5974
5997
  }
@@ -6058,6 +6081,49 @@ const ExpressionLanguageModule = {
6058
6081
  conditionChecker: ['type', ConditionChecker]
6059
6082
  };
6060
6083
 
6084
+ class ExpressionLoopPreventer {
6085
+ constructor(eventBus) {
6086
+ this._computedExpressions = [];
6087
+ eventBus.on('field.updated', ({
6088
+ shouldNotRecompute
6089
+ }) => {
6090
+ if (shouldNotRecompute) {
6091
+ return;
6092
+ }
6093
+ this.reset();
6094
+ });
6095
+ eventBus.on('import.done', this.reset.bind(this));
6096
+ eventBus.on('reset', this.reset.bind(this));
6097
+ }
6098
+
6099
+ /**
6100
+ * Checks if the expression field has already been computed, and registers it if not.
6101
+ *
6102
+ * @param {any} expressionField
6103
+ * @returns {boolean} - whether the expression field has already been computed within the current cycle
6104
+ */
6105
+ registerExpressionExecution(expressionField) {
6106
+ if (this._computedExpressions.includes(expressionField)) {
6107
+ return false;
6108
+ }
6109
+ this._computedExpressions.push(expressionField);
6110
+ return true;
6111
+ }
6112
+
6113
+ /**
6114
+ * Resets the list of computed expressions.
6115
+ */
6116
+ reset() {
6117
+ this._computedExpressions = [];
6118
+ }
6119
+ }
6120
+ ExpressionLoopPreventer.$inject = ['eventBus'];
6121
+
6122
+ const ExpressionFieldModule = {
6123
+ __init__: ['expressionLoopPreventer'],
6124
+ expressionLoopPreventer: ['type', ExpressionLoopPreventer]
6125
+ };
6126
+
6061
6127
  class MarkdownRenderer {
6062
6128
  /**
6063
6129
  * Render markdown to HTML.
@@ -8232,6 +8298,62 @@ class FormFieldRegistry {
8232
8298
  }
8233
8299
  FormFieldRegistry.$inject = ['eventBus'];
8234
8300
 
8301
+ class FormFieldInstanceRegistry {
8302
+ constructor(eventBus, formFieldRegistry, formFields) {
8303
+ this._eventBus = eventBus;
8304
+ this._formFieldRegistry = formFieldRegistry;
8305
+ this._formFields = formFields;
8306
+ this._formFieldInstances = {};
8307
+ eventBus.on('form.clear', () => this.clear());
8308
+ }
8309
+ add(instance) {
8310
+ const {
8311
+ id,
8312
+ expressionContextInfo,
8313
+ valuePath,
8314
+ indexes
8315
+ } = instance;
8316
+ const instanceId = [id, ...Object.values(indexes || {})].join('_');
8317
+ if (this._formFieldInstances[instanceId]) {
8318
+ throw new Error('this form field instance is already registered');
8319
+ }
8320
+ this._formFieldInstances[instanceId] = {
8321
+ id,
8322
+ instanceId,
8323
+ expressionContextInfo,
8324
+ valuePath,
8325
+ indexes
8326
+ };
8327
+ return instanceId;
8328
+ }
8329
+ remove(instanceId) {
8330
+ if (!this._formFieldInstances[instanceId]) {
8331
+ return;
8332
+ }
8333
+ delete this._formFieldInstances[instanceId];
8334
+ }
8335
+ getAll() {
8336
+ return Object.values(this._formFieldInstances);
8337
+ }
8338
+ getAllKeyed() {
8339
+ return this.getAll().filter(({
8340
+ id
8341
+ }) => {
8342
+ const {
8343
+ type
8344
+ } = this._formFieldRegistry.get(id);
8345
+ const {
8346
+ config
8347
+ } = this._formFields.get(type);
8348
+ return config.keyed;
8349
+ });
8350
+ }
8351
+ clear() {
8352
+ this._formFieldInstances = {};
8353
+ }
8354
+ }
8355
+ FormFieldInstanceRegistry.$inject = ['eventBus', 'formFieldRegistry', 'formFields'];
8356
+
8235
8357
  function Renderer(config, eventBus, form, injector) {
8236
8358
  const App = () => {
8237
8359
  const [state, setState] = hooks.useState(form._getState());
@@ -8296,6 +8418,7 @@ const CoreModule = {
8296
8418
  importer: ['type', Importer],
8297
8419
  fieldFactory: ['type', FieldFactory],
8298
8420
  formFieldRegistry: ['type', FormFieldRegistry],
8421
+ formFieldInstanceRegistry: ['type', FormFieldInstanceRegistry],
8299
8422
  pathRegistry: ['type', PathRegistry],
8300
8423
  formLayouter: ['type', FormLayouter],
8301
8424
  validator: ['type', Validator]
@@ -8470,73 +8593,39 @@ class Form {
8470
8593
  * @returns {Errors}
8471
8594
  */
8472
8595
  validate() {
8473
- const formFields = this.get('formFields'),
8474
- formFieldRegistry = this.get('formFieldRegistry'),
8475
- pathRegistry = this.get('pathRegistry'),
8596
+ const formFieldRegistry = this.get('formFieldRegistry'),
8597
+ formFieldInstanceRegistry = this.get('formFieldInstanceRegistry'),
8476
8598
  validator = this.get('validator');
8477
8599
  const {
8478
8600
  data
8479
8601
  } = this._getState();
8480
- const getErrorPath = (field, indexes) => [field.id, ...Object.values(indexes || {})];
8481
- function validateFieldRecursively(errors, field, indexes) {
8482
- const {
8483
- disabled,
8484
- type,
8485
- isRepeating
8486
- } = field;
8487
- const {
8488
- config: fieldConfig
8489
- } = formFields.get(type);
8602
+ const errors = {};
8603
+ const getErrorPath = (id, indexes) => [id, ...Object.values(indexes || {})];
8604
+ formFieldInstanceRegistry.getAllKeyed().forEach(({
8605
+ id,
8606
+ valuePath,
8607
+ indexes
8608
+ }) => {
8609
+ const field = formFieldRegistry.get(id);
8490
8610
 
8491
8611
  // (1) Skip disabled fields
8492
- if (disabled) {
8612
+ if (field.disabled) {
8493
8613
  return;
8494
8614
  }
8495
8615
 
8496
8616
  // (2) Validate the field
8497
- const valuePath = pathRegistry.getValuePath(field, {
8498
- indexes
8499
- });
8500
- const valueData = minDash.get(data, valuePath);
8501
- const fieldErrors = validator.validateField(field, valueData);
8617
+ const value = minDash.get(data, valuePath);
8618
+ const fieldErrors = validator.validateField(field, value);
8502
8619
  if (fieldErrors.length) {
8503
- minDash.set(errors, getErrorPath(field, indexes), fieldErrors);
8504
- }
8505
-
8506
- // (3) Process parents
8507
- if (!Array.isArray(field.components)) {
8508
- return;
8509
- }
8510
-
8511
- // (4a) Recurse repeatable parents both across the indexes of repetition and the children
8512
- if (fieldConfig.repeatable && isRepeating) {
8513
- if (!Array.isArray(valueData)) {
8514
- return;
8515
- }
8516
- valueData.forEach((_, index) => {
8517
- field.components.forEach(component => {
8518
- validateFieldRecursively(errors, component, {
8519
- ...indexes,
8520
- [field.id]: index
8521
- });
8522
- });
8523
- });
8524
- return;
8620
+ minDash.set(errors, getErrorPath(field.id, indexes), fieldErrors);
8525
8621
  }
8526
-
8527
- // (4b) Recurse non-repeatable parents only across the children
8528
- field.components.forEach(component => validateFieldRecursively(errors, component, indexes));
8529
- }
8530
- const workingErrors = {};
8531
- validateFieldRecursively(workingErrors, formFieldRegistry.getForm());
8532
- const filteredErrors = this._applyConditions(workingErrors, data, {
8533
- getFilterPath: getErrorPath,
8534
- leafNodeDeletionOnly: true
8535
8622
  });
8536
8623
  this._setState({
8537
- errors: filteredErrors
8624
+ errors
8538
8625
  });
8539
- return filteredErrors;
8626
+
8627
+ // @ts-ignore
8628
+ return errors;
8540
8629
  }
8541
8630
 
8542
8631
  /**
@@ -8631,7 +8720,7 @@ class Form {
8631
8720
  /**
8632
8721
  * @internal
8633
8722
  *
8634
- * @param { { add?: boolean, field: any, indexes: object, remove?: number, value?: any } } update
8723
+ * @param { { field: any, indexes: object, value: any } } update
8635
8724
  */
8636
8725
  _update(update) {
8637
8726
  const {
@@ -8651,6 +8740,7 @@ class Form {
8651
8740
  });
8652
8741
  minDash.set(data, valuePath, value);
8653
8742
  minDash.set(errors, [field.id, ...Object.values(indexes || {})], fieldErrors.length ? fieldErrors : undefined);
8743
+ this._emit('field.updated', update);
8654
8744
  this._setState({
8655
8745
  data: clone(data),
8656
8746
  errors: clone(errors)
@@ -8676,10 +8766,10 @@ class Form {
8676
8766
  }
8677
8767
 
8678
8768
  /**
8679
- * @internal
8680
- */
8769
+ * @internal
8770
+ */
8681
8771
  _getModules() {
8682
- return [ExpressionLanguageModule, MarkdownRendererModule, ViewerCommandsModule, RepeatRenderModule];
8772
+ return [ExpressionLanguageModule, ExpressionFieldModule, MarkdownRendererModule, ViewerCommandsModule, RepeatRenderModule];
8683
8773
  }
8684
8774
 
8685
8775
  /**
@@ -8694,65 +8784,24 @@ class Form {
8694
8784
  */
8695
8785
  _getSubmitData() {
8696
8786
  const formFieldRegistry = this.get('formFieldRegistry');
8697
- const formFields = this.get('formFields');
8698
- const pathRegistry = this.get('pathRegistry');
8787
+ const formFieldInstanceRegistry = this.get('formFieldInstanceRegistry');
8699
8788
  const formData = this._getState().data;
8700
- function collectSubmitDataRecursively(submitData, formField, indexes) {
8789
+ const submitData = {};
8790
+ formFieldInstanceRegistry.getAllKeyed().forEach(formFieldInstance => {
8701
8791
  const {
8702
- disabled,
8703
- type
8704
- } = formField;
8792
+ id,
8793
+ valuePath
8794
+ } = formFieldInstance;
8705
8795
  const {
8706
- config: fieldConfig
8707
- } = formFields.get(type);
8708
-
8709
- // (1) Process keyed fields
8710
- if (!disabled && fieldConfig.keyed) {
8711
- const valuePath = pathRegistry.getValuePath(formField, {
8712
- indexes
8713
- });
8714
- const value = minDash.get(formData, valuePath);
8715
- minDash.set(submitData, valuePath, value);
8716
- }
8717
-
8718
- // (2) Process parents
8719
- if (!Array.isArray(formField.components)) {
8720
- return;
8721
- }
8722
-
8723
- // (3a) Recurse repeatable parents both across the indexes of repetition and the children
8724
- if (fieldConfig.repeatable && formField.isRepeating) {
8725
- const valueData = minDash.get(formData, pathRegistry.getValuePath(formField, {
8726
- indexes
8727
- }));
8728
- if (!Array.isArray(valueData)) {
8729
- return;
8730
- }
8731
- valueData.forEach((_, index) => {
8732
- formField.components.forEach(component => {
8733
- collectSubmitDataRecursively(submitData, component, {
8734
- ...indexes,
8735
- [formField.id]: index
8736
- });
8737
- });
8738
- });
8796
+ disabled
8797
+ } = formFieldRegistry.get(id);
8798
+ if (disabled) {
8739
8799
  return;
8740
8800
  }
8741
-
8742
- // (3b) Recurse non-repeatable parents only across the children
8743
- formField.components.forEach(component => collectSubmitDataRecursively(submitData, component, indexes));
8744
- }
8745
- const workingSubmitData = {};
8746
- collectSubmitDataRecursively(workingSubmitData, formFieldRegistry.getForm(), {});
8747
- return this._applyConditions(workingSubmitData, formData);
8748
- }
8749
-
8750
- /**
8751
- * @internal
8752
- */
8753
- _applyConditions(toFilter, data, options = {}) {
8754
- const conditionChecker = this.get('conditionChecker');
8755
- return conditionChecker.applyConditions(toFilter, data, options);
8801
+ const value = minDash.get(formData, valuePath);
8802
+ minDash.set(submitData, valuePath, value);
8803
+ });
8804
+ return submitData;
8756
8805
  }
8757
8806
 
8758
8807
  /**
@@ -8847,16 +8896,16 @@ class Form {
8847
8896
 
8848
8897
  const schemaVersion = 16;
8849
8898
 
8850
- /**
8851
- * @typedef { import('./types').CreateFormOptions } CreateFormOptions
8899
+ /**
8900
+ * @typedef { import('./types').CreateFormOptions } CreateFormOptions
8852
8901
  */
8853
8902
 
8854
- /**
8855
- * Create a form.
8856
- *
8857
- * @param {CreateFormOptions} options
8858
- *
8859
- * @return {Promise<Form>}
8903
+ /**
8904
+ * Create a form.
8905
+ *
8906
+ * @param {CreateFormOptions} options
8907
+ *
8908
+ * @return {Promise<Form>}
8860
8909
  */
8861
8910
  function createForm(options) {
8862
8911
  const {
@@ -8886,7 +8935,9 @@ exports.Description = Description;
8886
8935
  exports.DynamicList = DynamicList;
8887
8936
  exports.Errors = Errors;
8888
8937
  exports.ExpressionField = ExpressionField;
8938
+ exports.ExpressionFieldModule = ExpressionFieldModule;
8889
8939
  exports.ExpressionLanguageModule = ExpressionLanguageModule;
8940
+ exports.ExpressionLoopPreventer = ExpressionLoopPreventer;
8890
8941
  exports.FeelExpressionLanguage = FeelExpressionLanguage;
8891
8942
  exports.FeelersTemplating = FeelersTemplating;
8892
8943
  exports.FieldFactory = FieldFactory;