@bpmn-io/form-js-editor 1.5.0 → 1.6.1

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.
Files changed (50) hide show
  1. package/LICENSE +22 -22
  2. package/README.md +152 -152
  3. package/dist/assets/form-js-editor-base.css +863 -832
  4. package/dist/assets/form-js-editor.css +38 -7
  5. package/dist/index.cjs +1619 -549
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.es.js +1621 -551
  8. package/dist/index.es.js.map +1 -1
  9. package/dist/types/FormEditor.d.ts +36 -4
  10. package/dist/types/features/dragging/Dragging.d.ts +1 -1
  11. package/dist/types/features/modeling/behavior/ColumnsSourceBehavior.d.ts +7 -0
  12. package/dist/types/features/modeling/behavior/OptionsSourceBehavior.d.ts +8 -0
  13. package/dist/types/features/modeling/behavior/TableDataSourceBehavior.d.ts +7 -0
  14. package/dist/types/features/modeling/behavior/index.d.ts +6 -2
  15. package/dist/types/features/modeling/index.d.ts +3 -1
  16. package/dist/types/features/properties-panel/Util.d.ts +11 -3
  17. package/dist/types/features/properties-panel/entries/ColumnEntry.d.ts +11 -0
  18. package/dist/types/features/properties-panel/entries/ColumnsExpressionEntry.d.ts +10 -0
  19. package/dist/types/features/properties-panel/entries/ConditionEntry.d.ts +1 -1
  20. package/dist/types/features/properties-panel/entries/{GroupEntries.d.ts → GroupAppearanceEntry.d.ts} +1 -1
  21. package/dist/types/features/properties-panel/entries/HeadersSourceSelectEntry.d.ts +9 -0
  22. package/dist/types/features/properties-panel/entries/{InputKeyValuesSourceEntry.d.ts → InputKeyOptionsSourceEntry.d.ts} +1 -1
  23. package/dist/types/features/properties-panel/entries/LayouterAppearanceEntry.d.ts +10 -0
  24. package/dist/types/features/properties-panel/entries/OptionsExpressionEntry.d.ts +9 -0
  25. package/dist/types/features/properties-panel/entries/{ValuesSourceSelectEntry.d.ts → OptionsSourceSelectEntry.d.ts} +1 -1
  26. package/dist/types/features/properties-panel/entries/PaginationEntry.d.ts +10 -0
  27. package/dist/types/features/properties-panel/entries/RepeatableEntry.d.ts +24 -0
  28. package/dist/types/features/properties-panel/entries/RowCountEntry.d.ts +10 -0
  29. package/dist/types/features/properties-panel/entries/StaticColumnsSourceEntry.d.ts +5 -0
  30. package/dist/types/features/properties-panel/entries/{StaticValuesSourceEntry.d.ts → StaticOptionsSourceEntry.d.ts} +1 -1
  31. package/dist/types/features/properties-panel/entries/TableDataSourceEntry.d.ts +10 -0
  32. package/dist/types/features/properties-panel/entries/factories/index.d.ts +3 -0
  33. package/dist/types/features/properties-panel/entries/factories/simpleRangeIntegerEntryFactory.d.ts +12 -0
  34. package/dist/types/features/properties-panel/entries/factories/simpleSelectEntryFactory.d.ts +10 -0
  35. package/dist/types/features/properties-panel/entries/factories/zeroPositiveIntegerEntryFactory.d.ts +9 -0
  36. package/dist/types/features/properties-panel/entries/index.d.ts +14 -6
  37. package/dist/types/features/properties-panel/groups/AppearanceGroup.d.ts +24 -3
  38. package/dist/types/features/properties-panel/groups/GeneralGroup.d.ts +11 -0
  39. package/dist/types/features/properties-panel/groups/OptionsGroups.d.ts +1 -0
  40. package/dist/types/features/properties-panel/groups/TableHeaderGroups.d.ts +1 -0
  41. package/dist/types/features/properties-panel/groups/index.d.ts +2 -1
  42. package/dist/types/features/repeat-render/EditorRepeatRenderManager.d.ts +17 -0
  43. package/dist/types/features/repeat-render/index.d.ts +7 -0
  44. package/dist/types/render/components/editor-form-fields/EditorTable.d.ts +10 -0
  45. package/dist/types/render/components/editor-form-fields/index.d.ts +2 -1
  46. package/dist/types/types.d.ts +28 -28
  47. package/package.json +3 -3
  48. package/dist/types/features/modeling/behavior/ValuesSourceBehavior.d.ts +0 -8
  49. package/dist/types/features/properties-panel/entries/ValuesExpressionEntry.d.ts +0 -9
  50. package/dist/types/features/properties-panel/groups/ValuesGroups.d.ts +0 -1
package/dist/index.es.js CHANGED
@@ -1,9 +1,9 @@
1
- import { FormFieldRegistry as FormFieldRegistry$1, iconsByType, IFrame, Text as Text$1, FormFields, sanitizeImageSource, FormContext, FormRenderContext, FormComponent, Importer, PathRegistry, FormLayouter, FieldFactory, runRecursively, clone, VALUES_SOURCES, VALUES_SOURCES_PATHS, getSchemaVariables, DATETIME_SUBTYPES, DATE_LABEL_PATH, TIME_LABEL_PATH, DATETIME_SUBTYPE_PATH, DATETIME_SUBTYPES_LABELS, TIME_SERIALISING_FORMAT_PATH, TIME_SERIALISING_FORMATS, TIME_INTERVAL_PATH, TIME_USE24H_PATH, DATE_DISALLOW_PAST_PATH, TIME_SERIALISINGFORMAT_LABELS, getValuesSource, VALUES_SOURCES_DEFAULTS, VALUES_SOURCES_LABELS, FeelExpressionLanguage, createFormContainer, createInjector, MarkdownModule, schemaVersion } from '@bpmn-io/form-js-viewer';
1
+ import { FormFieldRegistry as FormFieldRegistry$1, iconsByType, IFrame, Text as Text$1, Label as Label$3, Table, FormFields, sanitizeImageSource, getAncestryList, FormContext, FormRenderContext, FormComponent, Importer, PathRegistry, FormLayouter, FieldFactory, runRecursively, clone, OPTIONS_SOURCES, OPTIONS_SOURCES_PATHS, getSchemaVariables, DATETIME_SUBTYPES, DATE_LABEL_PATH, TIME_LABEL_PATH, DATETIME_SUBTYPE_PATH, DATETIME_SUBTYPES_LABELS, TIME_SERIALISING_FORMAT_PATH, TIME_SERIALISING_FORMATS, TIME_INTERVAL_PATH, TIME_USE24H_PATH, DATE_DISALLOW_PAST_PATH, TIME_SERIALISINGFORMAT_LABELS, getOptionsSource, OPTIONS_SOURCES_DEFAULTS, OPTIONS_SOURCES_LABELS, FeelExpressionLanguage, createFormContainer, createInjector, MarkdownModule, schemaVersion } from '@bpmn-io/form-js-viewer';
2
2
  export { schemaVersion } from '@bpmn-io/form-js-viewer';
3
3
  import Ids from 'ids';
4
- import { isArray, isFunction, isNumber, bind, assign, debounce, forEach, get, isObject, uniqueBy, sortBy, find, isString, set as set$1, reduce, isUndefined, without, has } from 'min-dash';
5
- import { jsx, jsxs, Fragment as Fragment$1 } from 'preact/jsx-runtime';
4
+ import { isArray, isFunction, isNumber, bind, assign, debounce, forEach, get, isObject, isDefined, isString, uniqueBy, sortBy, find, set as set$1, reduce, isUndefined, without, isNil, has } from 'min-dash';
6
5
  import classnames from 'classnames';
6
+ import { jsx, jsxs, Fragment as Fragment$1 } from 'preact/jsx-runtime';
7
7
  import { useContext, useRef, useEffect, useMemo, useState, useCallback, useLayoutEffect } from 'preact/hooks';
8
8
  import { createContext, Fragment, render, createElement } from 'preact';
9
9
  import * as React from 'preact/compat';
@@ -417,7 +417,7 @@ EventBus.prototype._invokeListener = function (event, args, listener) {
417
417
  * * after: [ 1500, 1500, (new=1300), 1000, 1000, (new=1000) ]
418
418
  *
419
419
  * @param {string} event
420
- * @param {EventBusListener} listener
420
+ * @param {EventBusListener} newListener
421
421
  */
422
422
  EventBus.prototype._addListener = function (event, newListener) {
423
423
  var listener = this._getListeners(event),
@@ -517,10 +517,10 @@ function invokeFunction(fn, args) {
517
517
  return fn.apply(null, args);
518
518
  }
519
519
 
520
- /**
521
- * A factory to create a configurable debouncer.
522
- *
523
- * @param {number|boolean} [config=true]
520
+ /**
521
+ * A factory to create a configurable debouncer.
522
+ *
523
+ * @param {number|boolean} [config=true]
524
524
  */
525
525
  function DebounceFactory(config = true) {
526
526
  const timeout = typeof config === 'number' ? config : config ? 300 : 0;
@@ -533,11 +533,11 @@ function DebounceFactory(config = true) {
533
533
  DebounceFactory.$inject = ['config.debounce'];
534
534
 
535
535
  class FormFieldRegistry extends FormFieldRegistry$1 {
536
- /**
537
- * Updates a form fields id.
538
- *
539
- * @param {Object} formField
540
- * @param {string} newId
536
+ /**
537
+ * Updates a form fields id.
538
+ *
539
+ * @param {Object} formField
540
+ * @param {string} newId
541
541
  */
542
542
  updateId(formField, newId) {
543
543
  this._validateId(newId);
@@ -558,13 +558,13 @@ class FormFieldRegistry extends FormFieldRegistry$1 {
558
558
  }
559
559
  }
560
560
 
561
- /**
562
- * Validate the suitability of the given id and signals a problem
563
- * with an exception.
564
- *
565
- * @param {string} id
566
- *
567
- * @throws {Error} if id is empty or already assigned
561
+ /**
562
+ * Validate the suitability of the given id and signals a problem
563
+ * with an exception.
564
+ *
565
+ * @param {string} id
566
+ *
567
+ * @throws {Error} if id is empty or already assigned
568
568
  */
569
569
  _validateId(id) {
570
570
  if (!id) {
@@ -581,11 +581,11 @@ const MAX_COLUMNS = 16;
581
581
  const MIN_COLUMNS = 2;
582
582
  const MAX_FIELDS_PER_ROW = 4;
583
583
  class FormLayoutValidator {
584
- /**
585
- * @constructor
586
- *
587
- * @param { import('./FormLayouter').default } formLayouter
588
- * @param { import('./FormFieldRegistry').default } formFieldRegistry
584
+ /**
585
+ * @constructor
586
+ *
587
+ * @param { import('./FormLayouter').default } formLayouter
588
+ * @param { import('./FormFieldRegistry').default } formFieldRegistry
589
589
  */
590
590
  constructor(formLayouter, formFieldRegistry) {
591
591
  this._formLayouter = formLayouter;
@@ -644,25 +644,6 @@ function calculateMaxColumnsWithAuto(autoCols) {
644
644
  return MAX_COLUMNS_PER_ROW - autoCols * 2;
645
645
  }
646
646
 
647
- function EditorIFrame(props) {
648
- const {
649
- field
650
- } = props;
651
- const Icon = iconsByType(field.type);
652
- return jsx("div", {
653
- class: "fjs-iframe-placeholder",
654
- children: jsxs("p", {
655
- class: "fjs-iframe-placeholder-text",
656
- children: [jsx(Icon, {
657
- width: "32",
658
- height: "24",
659
- viewBox: "0 0 56 56"
660
- }), "iFrame"]
661
- })
662
- });
663
- }
664
- EditorIFrame.config = IFrame.config;
665
-
666
647
  const emptyImage = createEmptyImage();
667
648
  function editorFormFieldClasses(type, {
668
649
  disabled = false
@@ -675,21 +656,21 @@ function editorFormFieldClasses(type, {
675
656
  });
676
657
  }
677
658
 
678
- /**
679
- * Add a dragger that calls back the passed function with
680
- * { event, delta } on drag.
681
- *
682
- * @example
683
- *
684
- * function dragMove(event, delta) {
685
- * // we are dragging (!!)
686
- * }
687
- *
688
- * domElement.addEventListener('dragstart', dragger(dragMove));
689
- *
690
- * @param {Function} fn
691
- *
692
- * @return {Function} drag start callback function
659
+ /**
660
+ * Add a dragger that calls back the passed function with
661
+ * { event, delta } on drag.
662
+ *
663
+ * @example
664
+ *
665
+ * function dragMove(event, delta) {
666
+ * // we are dragging (!!)
667
+ * }
668
+ *
669
+ * domElement.addEventListener('dragstart', dragger(dragMove));
670
+ *
671
+ * @param {Function} fn
672
+ *
673
+ * @return {Function} drag start callback function
693
674
  */
694
675
  function createDragger$1(fn) {
695
676
  let self;
@@ -730,12 +711,12 @@ function createDragger$1(fn) {
730
711
  return onDragStart;
731
712
  }
732
713
 
733
- /**
734
- * Throttle function call according UI update cycle.
735
- *
736
- * @param {Function} fn
737
- *
738
- * @return {Function} throttled fn
714
+ /**
715
+ * Throttle function call according UI update cycle.
716
+ *
717
+ * @param {Function} fn
718
+ *
719
+ * @return {Function} throttled fn
739
720
  */
740
721
  function throttle(fn) {
741
722
  let active = false;
@@ -764,16 +745,38 @@ function createEmptyImage() {
764
745
  return img;
765
746
  }
766
747
 
748
+ function EditorIFrame(props) {
749
+ const {
750
+ field
751
+ } = props;
752
+ const Icon = iconsByType(field.type);
753
+ return jsx("div", {
754
+ class: editorFormFieldClasses(field.type),
755
+ children: jsx("div", {
756
+ class: "fjs-iframe-placeholder",
757
+ children: jsxs("p", {
758
+ class: "fjs-iframe-placeholder-text",
759
+ children: [jsx(Icon, {
760
+ width: "32",
761
+ height: "24",
762
+ viewBox: "0 0 56 56"
763
+ }), "iFrame"]
764
+ })
765
+ })
766
+ });
767
+ }
768
+ EditorIFrame.config = IFrame.config;
769
+
767
770
  const DragAndDropContext = createContext({
768
771
  drake: null
769
772
  });
770
773
  var DragAndDropContext$1 = DragAndDropContext;
771
774
 
772
- /**
773
- * @param {string} type
774
- * @param {boolean} [strict]
775
- *
776
- * @returns {any}
775
+ /**
776
+ * @param {string} type
777
+ * @param {boolean} [strict]
778
+ *
779
+ * @returns {any}
777
780
  */
778
781
  function getService$1(type, strict) {}
779
782
  const FormEditorContext = createContext({
@@ -809,15 +812,15 @@ function useDebounce(fn, dependencies = []) {
809
812
  return callback;
810
813
  }
811
814
 
812
- var _path$4;
813
- function _extends$4() { _extends$4 = 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$4.apply(this, arguments); }
815
+ var _path$5;
816
+ function _extends$5() { _extends$5 = 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$5.apply(this, arguments); }
814
817
  var SvgClose = function SvgClose(props) {
815
- return /*#__PURE__*/React.createElement("svg", _extends$4({
818
+ return /*#__PURE__*/React.createElement("svg", _extends$5({
816
819
  xmlns: "http://www.w3.org/2000/svg",
817
820
  width: 16,
818
821
  height: 16,
819
822
  fill: "currentColor"
820
- }, props), _path$4 || (_path$4 = /*#__PURE__*/React.createElement("path", {
823
+ }, props), _path$5 || (_path$5 = /*#__PURE__*/React.createElement("path", {
821
824
  fillRule: "evenodd",
822
825
  d: "m12 4.7-.7-.7L8 7.3 4.7 4l-.7.7L7.3 8 4 11.3l.7.7L8 8.7l3.3 3.3.7-.7L8.7 8 12 4.7Z",
823
826
  clipRule: "evenodd"
@@ -825,10 +828,10 @@ var SvgClose = function SvgClose(props) {
825
828
  };
826
829
  var CloseIcon = SvgClose;
827
830
 
828
- var _path$3, _path2$1;
829
- function _extends$3() { _extends$3 = 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$3.apply(this, arguments); }
831
+ var _path$4, _path2$1;
832
+ function _extends$4() { _extends$4 = 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$4.apply(this, arguments); }
830
833
  var SvgDelete = function SvgDelete(props) {
831
- return /*#__PURE__*/React.createElement("svg", _extends$3({
834
+ return /*#__PURE__*/React.createElement("svg", _extends$4({
832
835
  xmlns: "http://www.w3.org/2000/svg",
833
836
  width: 16,
834
837
  height: 16,
@@ -849,7 +852,7 @@ var SvgDelete = function SvgDelete(props) {
849
852
  mixBlendMode: "multiply"
850
853
  },
851
854
  transform: "translate(.536)"
852
- }), _path$3 || (_path$3 = /*#__PURE__*/React.createElement("path", {
855
+ }), _path$4 || (_path$4 = /*#__PURE__*/React.createElement("path", {
853
856
  fill: "currentcolor",
854
857
  d: "M7.536 6h-1v6h1V6Zm3 0h-1v6h1V6Z"
855
858
  })), _path2$1 || (_path2$1 = /*#__PURE__*/React.createElement("path", {
@@ -859,17 +862,17 @@ var SvgDelete = function SvgDelete(props) {
859
862
  };
860
863
  var DeleteIcon$1 = SvgDelete;
861
864
 
862
- var _path$2;
863
- function _extends$2() { _extends$2 = 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$2.apply(this, arguments); }
865
+ var _path$3;
866
+ function _extends$3() { _extends$3 = 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$3.apply(this, arguments); }
864
867
  var SvgDraggable = function SvgDraggable(props) {
865
- return /*#__PURE__*/React.createElement("svg", _extends$2({
868
+ return /*#__PURE__*/React.createElement("svg", _extends$3({
866
869
  xmlns: "http://www.w3.org/2000/svg",
867
870
  xmlSpace: "preserve",
868
871
  width: 16,
869
872
  height: 16,
870
873
  fill: "currentcolor",
871
874
  viewBox: "0 0 32 32"
872
- }, props), _path$2 || (_path$2 = /*#__PURE__*/React.createElement("path", {
875
+ }, props), _path$3 || (_path$3 = /*#__PURE__*/React.createElement("path", {
873
876
  d: "M10 6h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4z"
874
877
  })), /*#__PURE__*/React.createElement("path", {
875
878
  d: "M0 0h32v32H0z",
@@ -880,30 +883,30 @@ var SvgDraggable = function SvgDraggable(props) {
880
883
  };
881
884
  var DraggableIcon = SvgDraggable;
882
885
 
883
- var _path$1;
884
- function _extends$1() { _extends$1 = 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$1.apply(this, arguments); }
886
+ var _path$2;
887
+ function _extends$2() { _extends$2 = 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$2.apply(this, arguments); }
885
888
  var SvgSearch = function SvgSearch(props) {
886
- return /*#__PURE__*/React.createElement("svg", _extends$1({
889
+ return /*#__PURE__*/React.createElement("svg", _extends$2({
887
890
  xmlns: "http://www.w3.org/2000/svg",
888
891
  width: 15,
889
892
  height: 15,
890
893
  fill: "none"
891
- }, props), _path$1 || (_path$1 = /*#__PURE__*/React.createElement("path", {
894
+ }, props), _path$2 || (_path$2 = /*#__PURE__*/React.createElement("path", {
892
895
  fill: "currentColor",
893
896
  d: "m14.5 13.793-3.776-3.776a5.508 5.508 0 1 0-.707.707l3.776 3.776.707-.707ZM2 6.5a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0Z"
894
897
  })));
895
898
  };
896
899
  var SearchIcon = SvgSearch;
897
900
 
898
- var _path, _rect, _mask, _path2, _path3, _path4, _path5, _path6;
899
- function _extends() { _extends = 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.apply(this, arguments); }
901
+ var _path$1, _rect, _mask, _path2, _path3, _path4, _path5, _path6;
902
+ function _extends$1() { _extends$1 = 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$1.apply(this, arguments); }
900
903
  var SvgEmptyForm = function SvgEmptyForm(props) {
901
- return /*#__PURE__*/React.createElement("svg", _extends({
904
+ return /*#__PURE__*/React.createElement("svg", _extends$1({
902
905
  xmlns: "http://www.w3.org/2000/svg",
903
906
  width: 126,
904
907
  height: 96,
905
908
  fill: "none"
906
- }, props), _path || (_path = /*#__PURE__*/React.createElement("path", {
909
+ }, props), _path$1 || (_path$1 = /*#__PURE__*/React.createElement("path", {
907
910
  fill: "#FF832B",
908
911
  fillRule: "evenodd",
909
912
  d: "M70 78v8a3 3 0 0 1-3 3h-8v-5h6v-6h5Zm0-16h-5V46h5v16Zm0-32h-5v-6h-6v-5h8a3 3 0 0 1 3 3v8ZM43 19v5H27v-5h16Zm-32 0v5H5v6H0v-8a3 3 0 0 1 3-3h8ZM0 46h5v16H0V46Zm0 32h5v6h6v5H3a3 3 0 0 1-3-3v-8Zm27 11v-5h16v5H27Z",
@@ -998,7 +1001,71 @@ function EditorText(props) {
998
1001
  }
999
1002
  EditorText.config = Text$1.config;
1000
1003
 
1001
- const editorFormFields = [EditorIFrame, EditorText];
1004
+ function EditorTable(props) {
1005
+ const {
1006
+ columnsExpression,
1007
+ columns,
1008
+ id,
1009
+ label
1010
+ } = props.field;
1011
+ const shouldUseMockColumns = typeof columnsExpression === 'string' && columnsExpression.length > 0 || Array.isArray(columns) && columns.length === 0;
1012
+ const editorColumns = shouldUseMockColumns ? [{
1013
+ key: '1',
1014
+ label: 'Column 1'
1015
+ }, {
1016
+ key: '2',
1017
+ label: 'Column 2'
1018
+ }, {
1019
+ key: '3',
1020
+ label: 'Column 3'
1021
+ }] : columns;
1022
+ const prefixId = `fjs-form-${id}`;
1023
+ return jsxs("div", {
1024
+ class: editorFormFieldClasses('table', {
1025
+ disabled: true
1026
+ }),
1027
+ children: [jsx(Label$3, {
1028
+ id: prefixId,
1029
+ label: label
1030
+ }), jsx("div", {
1031
+ class: "fjs-table-middle-container",
1032
+ children: jsx("div", {
1033
+ class: "fjs-table-inner-container",
1034
+ children: jsxs("table", {
1035
+ class: classnames('fjs-table', 'fjs-disabled'),
1036
+ id: prefixId,
1037
+ children: [jsx("thead", {
1038
+ class: "fjs-table-head",
1039
+ children: jsx("tr", {
1040
+ class: "fjs-table-tr",
1041
+ children: editorColumns.map(({
1042
+ key,
1043
+ label
1044
+ }) => jsx("th", {
1045
+ class: "fjs-table-th",
1046
+ children: label
1047
+ }, key))
1048
+ })
1049
+ }), jsx("tbody", {
1050
+ class: "fjs-table-body",
1051
+ children: jsx("tr", {
1052
+ class: "fjs-table-tr",
1053
+ children: editorColumns.map(({
1054
+ key
1055
+ }) => jsx("td", {
1056
+ class: "fjs-table-td",
1057
+ children: "Content"
1058
+ }, key))
1059
+ })
1060
+ })]
1061
+ })
1062
+ })
1063
+ })]
1064
+ });
1065
+ }
1066
+ EditorTable.config = Table.config;
1067
+
1068
+ const editorFormFields = [EditorIFrame, EditorText, EditorTable];
1002
1069
 
1003
1070
  class EditorFormFields extends FormFields {
1004
1071
  constructor() {
@@ -1144,23 +1211,23 @@ var Slot = (props => {
1144
1211
  return fillsAndSeparators;
1145
1212
  });
1146
1213
 
1147
- /**
1148
- * Creates a Fragment for a fill.
1149
- *
1150
- * @param {Object} fill Fill to be rendered
1151
- * @returns {Object} Preact Fragment containing fill's children
1214
+ /**
1215
+ * Creates a Fragment for a fill.
1216
+ *
1217
+ * @param {Object} fill Fill to be rendered
1218
+ * @returns {Object} Preact Fragment containing fill's children
1152
1219
  */
1153
1220
  const FillFragment = fill => jsx(Fragment, {
1154
1221
  children: fill.children
1155
1222
  }, fill.id);
1156
1223
 
1157
- /**
1158
- * Creates an array of fills, with separators inserted between groups.
1159
- *
1160
- * @param {Array} groups Groups of fills
1161
- * @param {Function} fillRenderer Function to create a fill
1162
- * @param {Function} separatorRenderer Function to create a separator
1163
- * @returns {Array} Array of fills and separators
1224
+ /**
1225
+ * Creates an array of fills, with separators inserted between groups.
1226
+ *
1227
+ * @param {Array} groups Groups of fills
1228
+ * @param {Function} fillRenderer Function to create a fill
1229
+ * @param {Function} separatorRenderer Function to create a separator
1230
+ * @returns {Array} Array of fills and separators
1164
1231
  */
1165
1232
  const buildFills = (groups, fillRenderer, separatorRenderer) => {
1166
1233
  const result = [];
@@ -1178,8 +1245,8 @@ const buildFills = (groups, fillRenderer, separatorRenderer) => {
1178
1245
  return result;
1179
1246
  };
1180
1247
 
1181
- /**
1182
- * Groups fills by group name property.
1248
+ /**
1249
+ * Groups fills by group name property.
1183
1250
  */
1184
1251
  const _groupByGroupName = fills => {
1185
1252
  const groups = [];
@@ -1199,8 +1266,8 @@ const _groupByGroupName = fills => {
1199
1266
  return Object.keys(groupsById).sort().map(id => groupsById[id]);
1200
1267
  };
1201
1268
 
1202
- /**
1203
- * Compares fills by priority.
1269
+ /**
1270
+ * Compares fills by priority.
1204
1271
  */
1205
1272
  const _comparePriority = (a, b) => {
1206
1273
  return (b.priority || 0) - (a.priority || 0);
@@ -1418,11 +1485,11 @@ function groupEntries(entries) {
1418
1485
  return groups.filter(g => g.entries.length);
1419
1486
  }
1420
1487
 
1421
- /**
1422
- * Returns a list of palette entries.
1423
- *
1424
- * @param {FormFields} formFields
1425
- * @returns {Array<PaletteEntry>}
1488
+ /**
1489
+ * Returns a list of palette entries.
1490
+ *
1491
+ * @param {FormFields} formFields
1492
+ * @returns {Array<PaletteEntry>}
1426
1493
  */
1427
1494
  function collectPaletteEntries(formFields) {
1428
1495
  return Object.entries(formFields._formFields).map(([type, formField]) => {
@@ -1441,12 +1508,12 @@ function collectPaletteEntries(formFields) {
1441
1508
  }) => type !== 'default');
1442
1509
  }
1443
1510
 
1444
- /**
1445
- * There are various options to specify an icon for a palette entry.
1446
- *
1447
- * a) via `iconUrl` property in a form field config
1448
- * b) via `icon` property in a form field config
1449
- * c) via statically defined iconsByType (fallback)
1511
+ /**
1512
+ * There are various options to specify an icon for a palette entry.
1513
+ *
1514
+ * a) via `iconUrl` property in a form field config
1515
+ * b) via `icon` property in a form field config
1516
+ * c) via statically defined iconsByType (fallback)
1450
1517
  */
1451
1518
  function getPaletteIcon(entry) {
1452
1519
  const {
@@ -1513,20 +1580,20 @@ const DRAG_NO_DROP_CLS = 'fjs-no-drop';
1513
1580
  const DRAG_NO_MOVE_CLS = 'fjs-no-move';
1514
1581
  const ERROR_DROP_CLS = 'fjs-error-drop';
1515
1582
 
1516
- /**
1517
- * @typedef { { id: String, components: Array<any> } } FormRow
1583
+ /**
1584
+ * @typedef { { id: String, components: Array<any> } } FormRow
1518
1585
  */
1519
1586
 
1520
1587
  class Dragging {
1521
- /**
1522
- * @constructor
1523
- *
1524
- * @param { import('../../core/FormFieldRegistry').default } formFieldRegistry
1525
- * @param { import('../../core/FormLayouter').default } formLayouter
1526
- * @param { import('../../core/FormLayoutValidator').default } formLayoutValidator
1527
- * @param { import('../../core/EventBus').default } eventBus
1528
- * @param { import('../modeling/Modeling').default } modeling
1529
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
1588
+ /**
1589
+ * @constructor
1590
+ *
1591
+ * @param { import('../../core/FormFieldRegistry').default } formFieldRegistry
1592
+ * @param { import('../../core/FormLayouter').default } formLayouter
1593
+ * @param { import('../../core/FormLayoutValidator').default } formLayoutValidator
1594
+ * @param { import('../../core/EventBus').default } eventBus
1595
+ * @param { import('../modeling/Modeling').default } modeling
1596
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
1530
1597
  */
1531
1598
  constructor(formFieldRegistry, formLayouter, formLayoutValidator, eventBus, modeling, pathRegistry) {
1532
1599
  this._formFieldRegistry = formFieldRegistry;
@@ -1537,13 +1604,13 @@ class Dragging {
1537
1604
  this._pathRegistry = pathRegistry;
1538
1605
  }
1539
1606
 
1540
- /**
1541
- * Calculcates position in form schema given the dropped place.
1542
- *
1543
- * @param { FormRow } targetRow
1544
- * @param { any } targetFormField
1545
- * @param { HTMLElement } sibling
1546
- * @returns { number }
1607
+ /**
1608
+ * Calculates position in form schema given the dropped place.
1609
+ *
1610
+ * @param { FormRow } targetRow
1611
+ * @param { any } targetFormField
1612
+ * @param { HTMLElement } sibling
1613
+ * @returns { number }
1547
1614
  */
1548
1615
  getTargetIndex(targetRow, targetFormField, sibling) {
1549
1616
  /** @type HTMLElement */
@@ -1598,13 +1665,18 @@ class Dragging {
1598
1665
  if (targetParentPath.join('.') !== currentParentPath.join('.')) {
1599
1666
  const isDropAllowedByPathRegistry = this._pathRegistry.executeRecursivelyOnFields(formField, ({
1600
1667
  field,
1601
- isClosed
1668
+ isClosed,
1669
+ isRepeatable
1602
1670
  }) => {
1603
1671
  const options = {
1604
1672
  cutoffNode: currentParentFormField.id
1605
1673
  };
1606
1674
  const fieldPath = this._pathRegistry.getValuePath(field, options);
1607
- return this._pathRegistry.canClaimPath([...targetParentPath, ...fieldPath], isClosed);
1675
+ return this._pathRegistry.canClaimPath([...targetParentPath, ...fieldPath], {
1676
+ isClosed,
1677
+ isRepeatable,
1678
+ knownAncestorIds: getAncestryList(targetParentId, this._formFieldRegistry)
1679
+ });
1608
1680
  });
1609
1681
  if (!isDropAllowedByPathRegistry) {
1610
1682
  return 'Drop not allowed by path registry';
@@ -1684,8 +1756,8 @@ class Dragging {
1684
1756
  }
1685
1757
  }
1686
1758
 
1687
- /**
1688
- * @param { { container: Array<string>, direction: string, mirrorContainer: string } } options
1759
+ /**
1760
+ * @param { { container: Array<string>, direction: string, mirrorContainer: string } } options
1689
1761
  */
1690
1762
  createDragulaInstance(options) {
1691
1763
  const {
@@ -1970,24 +2042,40 @@ function ContextPad(props) {
1970
2042
  children: props.children
1971
2043
  });
1972
2044
  }
1973
- function Empty() {
2045
+ function Empty(props) {
2046
+ if (props.field.type === 'default') {
2047
+ return jsx("div", {
2048
+ class: "fjs-empty-editor",
2049
+ children: jsxs("div", {
2050
+ class: "fjs-empty-editor-card",
2051
+ children: [jsx(EmptyFormIcon, {}), jsx("h2", {
2052
+ children: "Build your form"
2053
+ }), jsx("span", {
2054
+ children: "Drag and drop components here to start designing."
2055
+ }), jsx("span", {
2056
+ children: "Use the preview window to test your form."
2057
+ })]
2058
+ })
2059
+ });
2060
+ }
2061
+ if (props.field.type === 'group') {
2062
+ return jsx("div", {
2063
+ class: "fjs-empty-component",
2064
+ children: jsx("span", {
2065
+ children: "Drag and drop components here."
2066
+ })
2067
+ });
2068
+ }
2069
+ if (props.field.type === 'dynamiclist') {
2070
+ return jsx("div", {
2071
+ class: "fjs-empty-component",
2072
+ children: jsxs("span", {
2073
+ children: ["Drag and drop components here ", jsx("br", {}), " to create a repeatable list item."]
2074
+ })
2075
+ });
2076
+ }
1974
2077
  return null;
1975
2078
  }
1976
- function EmptyRoot(props) {
1977
- return jsx("div", {
1978
- class: "fjs-empty-editor",
1979
- children: jsxs("div", {
1980
- class: "fjs-empty-editor-card",
1981
- children: [jsx(EmptyFormIcon, {}), jsx("h2", {
1982
- children: "Build your form"
1983
- }), jsx("span", {
1984
- children: "Drag and drop components here to start designing."
1985
- }), jsx("span", {
1986
- children: "Use the preview window to test your form."
1987
- })]
1988
- })
1989
- });
1990
- }
1991
2079
  function Element$1(props) {
1992
2080
  const eventBus = useService$1('eventBus'),
1993
2081
  formEditor = useService$1('formEditor'),
@@ -1996,8 +2084,7 @@ function Element$1(props) {
1996
2084
  modeling = useService$1('modeling'),
1997
2085
  selection = useService$1('selection');
1998
2086
  const {
1999
- hoveredId,
2000
- setHoveredId
2087
+ hoverInfo
2001
2088
  } = useContext(FormRenderContext);
2002
2089
  const {
2003
2090
  field
@@ -2008,6 +2095,7 @@ function Element$1(props) {
2008
2095
  showOutline
2009
2096
  } = field;
2010
2097
  const ref = useRef();
2098
+ const [hovered, setHovered] = useState(false);
2011
2099
  function scrollIntoView({
2012
2100
  selection
2013
2101
  }) {
@@ -2036,19 +2124,23 @@ function Element$1(props) {
2036
2124
  // properly focus on field
2037
2125
  ref.current.focus();
2038
2126
  }
2039
- const classes = [];
2040
- if (props.class) {
2041
- classes.push(...props.class.split(' '));
2042
- }
2043
- if (selection.isSelected(field)) {
2044
- classes.push('fjs-editor-selected');
2045
- }
2046
- if (showOutline) {
2047
- classes.push('fjs-outlined');
2048
- }
2049
- if (hoveredId === field.id) {
2050
- classes.push('fjs-editor-hovered');
2051
- }
2127
+ const isSelected = selection.isSelected(field);
2128
+ const classString = useMemo(() => {
2129
+ const classes = [];
2130
+ if (props.class) {
2131
+ classes.push(...props.class.split(' '));
2132
+ }
2133
+ if (isSelected) {
2134
+ classes.push('fjs-editor-selected');
2135
+ }
2136
+ if (showOutline) {
2137
+ classes.push('fjs-outlined');
2138
+ }
2139
+ if (hovered) {
2140
+ classes.push('fjs-editor-hovered');
2141
+ }
2142
+ return classes.join(' ');
2143
+ }, [hovered, isSelected, props.class, showOutline]);
2052
2144
  const onRemove = event => {
2053
2145
  event.stopPropagation();
2054
2146
  const parentField = formFieldRegistry.get(field._parent);
@@ -2062,15 +2154,18 @@ function Element$1(props) {
2062
2154
  }
2063
2155
  };
2064
2156
  return jsxs("div", {
2065
- class: classes.join(' '),
2157
+ class: classString,
2066
2158
  "data-id": id,
2067
2159
  "data-field-type": type,
2068
2160
  tabIndex: type === 'default' ? -1 : 0,
2069
2161
  onClick: onClick,
2070
2162
  onKeyPress: onKeyPress,
2071
2163
  onMouseOver: e => {
2072
- // @ts-ignore
2073
- setHoveredId(field.id);
2164
+ if (hoverInfo.cleanup) {
2165
+ hoverInfo.cleanup();
2166
+ }
2167
+ setHovered(true);
2168
+ hoverInfo.cleanup = () => setHovered(false);
2074
2169
  e.stopPropagation();
2075
2170
  },
2076
2171
  ref: ref,
@@ -2101,7 +2196,7 @@ function DebugColumns(props) {
2101
2196
  return null;
2102
2197
  }
2103
2198
  return jsx("div", {
2104
- style: "width: fit-content; padding: 2px 6px; height: 16px; background: var(--color-blue-205-100-95); display: flex; justify-content: center; align-items: center; position: absolute; bottom: -2px; z-index: 2; font-size: 10px; right: 3px;",
2199
+ style: "width: fit-content;\r padding: 2px 6px;\r height: 16px;\r background: var(--color-blue-205-100-95);\r display: flex;\r justify-content: center;\r align-items: center;\r position: absolute;\r bottom: -2px;\r z-index: 2;\r font-size: 10px;\r right: 3px;",
2105
2200
  class: "fjs-debug-columns",
2106
2201
  children: (field.layout || {}).columns || 'auto'
2107
2202
  });
@@ -2141,6 +2236,7 @@ function Row(props) {
2141
2236
  children: jsx(DraggableIcon, {})
2142
2237
  }), jsx("div", {
2143
2238
  class: classes.join(' '),
2239
+ style: props.style,
2144
2240
  "data-row-id": id,
2145
2241
  children: props.children
2146
2242
  })]
@@ -2246,17 +2342,14 @@ function FormEditor$1(props) {
2246
2342
  // keep deprecated event to ensure backward compatibility
2247
2343
  eventBus.fire('formEditor.rendered');
2248
2344
  }, []);
2249
- const [hoveredId, setHoveredId] = useState(null);
2250
2345
  const formRenderContext = useMemo(() => ({
2251
2346
  Children,
2252
2347
  Column,
2253
2348
  Element: Element$1,
2254
2349
  Empty,
2255
- EmptyRoot,
2256
2350
  Row,
2257
- hoveredId,
2258
- setHoveredId
2259
- }), [hoveredId]);
2351
+ hoverInfo: {}
2352
+ }), []);
2260
2353
  const formContext = useMemo(() => ({
2261
2354
  getService(type, strict = true) {
2262
2355
  // TODO(philippfromme): clean up
@@ -2617,7 +2710,8 @@ EditorActions.prototype.trigger = function (action, opts) {
2617
2710
  * The key of the object will be the name of the action.
2618
2711
  *
2619
2712
  * @example
2620
- * ´´´
2713
+ *
2714
+ * ```javascript
2621
2715
  * var actions = {
2622
2716
  * spaceTool: function() {
2623
2717
  * spaceTool.activateSelection();
@@ -2630,7 +2724,7 @@ EditorActions.prototype.trigger = function (action, opts) {
2630
2724
  * editorActions.register(actions);
2631
2725
  *
2632
2726
  * editorActions.isRegistered('spaceTool'); // true
2633
- * ´´´
2727
+ * ```
2634
2728
  *
2635
2729
  * @param {Object} actions
2636
2730
  */
@@ -2769,6 +2863,7 @@ function hasModifier(event) {
2769
2863
 
2770
2864
  /**
2771
2865
  * @param {KeyboardEvent} event
2866
+ * @return {boolean}
2772
2867
  */
2773
2868
  function isCmd(event) {
2774
2869
  // ensure we don't react to AltGr
@@ -2784,6 +2879,7 @@ function isCmd(event) {
2784
2879
  *
2785
2880
  * @param {string|string[]} keys
2786
2881
  * @param {KeyboardEvent} event
2882
+ * @return {boolean}
2787
2883
  */
2788
2884
  function isKey(keys, event) {
2789
2885
  keys = isArray(keys) ? keys : [keys];
@@ -2796,21 +2892,39 @@ function isKey(keys, event) {
2796
2892
  function isShift(event) {
2797
2893
  return event.shiftKey;
2798
2894
  }
2895
+
2896
+ /**
2897
+ * @param {KeyboardEvent} event
2898
+ */
2799
2899
  function isCopy(event) {
2800
2900
  return isCmd(event) && isKey(KEYS_COPY, event);
2801
2901
  }
2902
+
2903
+ /**
2904
+ * @param {KeyboardEvent} event
2905
+ */
2802
2906
  function isPaste(event) {
2803
2907
  return isCmd(event) && isKey(KEYS_PASTE, event);
2804
2908
  }
2909
+
2910
+ /**
2911
+ * @param {KeyboardEvent} event
2912
+ */
2805
2913
  function isUndo(event) {
2806
2914
  return isCmd(event) && !isShift(event) && isKey(KEYS_UNDO, event);
2807
2915
  }
2916
+
2917
+ /**
2918
+ * @param {KeyboardEvent} event
2919
+ */
2808
2920
  function isRedo(event) {
2809
2921
  return isCmd(event) && (isKey(KEYS_REDO, event) || isKey(KEYS_UNDO, event) && isShift(event));
2810
2922
  }
2811
2923
 
2812
2924
  /**
2813
2925
  * @typedef {import('../../core/EventBus').default} EventBus
2926
+ *
2927
+ * @typedef {({ keyEvent: KeyboardEvent }) => any} Listener
2814
2928
  */
2815
2929
 
2816
2930
  var KEYDOWN_EVENT = 'keyboard.keydown',
@@ -2840,6 +2954,7 @@ var DEFAULT_PRIORITY$2 = 1000;
2840
2954
  * `keyboard.bindTo` configuration option.
2841
2955
  *
2842
2956
  * @param {Object} config
2957
+ * @param {EventTarget} [config.bindTo]
2843
2958
  * @param {EventBus} eventBus
2844
2959
  */
2845
2960
  function Keyboard(config, eventBus) {
@@ -2906,6 +3021,12 @@ Keyboard.prototype._getAllowedModifiers = function (element) {
2906
3021
  }
2907
3022
  return modifierContainer.getAttribute(HANDLE_MODIFIER_ATTRIBUTE).split(',');
2908
3023
  };
3024
+
3025
+ /**
3026
+ * Bind keyboard events to the given DOM node.
3027
+ *
3028
+ * @param {EventTarget} node
3029
+ */
2909
3030
  Keyboard.prototype.bind = function (node) {
2910
3031
  // make sure that the keyboard is only bound once to the DOM
2911
3032
  this.unbind();
@@ -2916,6 +3037,10 @@ Keyboard.prototype.bind = function (node) {
2916
3037
  event.bind(node, 'keyup', this._keyupHandler);
2917
3038
  this._fire('bind');
2918
3039
  };
3040
+
3041
+ /**
3042
+ * @return {EventTarget}
3043
+ */
2919
3044
  Keyboard.prototype.getBinding = function () {
2920
3045
  return this._node;
2921
3046
  };
@@ -2930,6 +3055,10 @@ Keyboard.prototype.unbind = function () {
2930
3055
  }
2931
3056
  this._node = null;
2932
3057
  };
3058
+
3059
+ /**
3060
+ * @param {string} event
3061
+ */
2933
3062
  Keyboard.prototype._fire = function (event) {
2934
3063
  this._eventBus.fire('keyboard.' + event, {
2935
3064
  node: this._node
@@ -2942,8 +3071,8 @@ Keyboard.prototype._fire = function (event) {
2942
3071
  * provided, the default value of 1000 is used.
2943
3072
  *
2944
3073
  * @param {number} [priority]
2945
- * @param {Function} listener
2946
- * @param {string} type
3074
+ * @param {Listener} listener
3075
+ * @param {string} [type='keyboard.keydown']
2947
3076
  */
2948
3077
  Keyboard.prototype.addListener = function (priority, listener, type) {
2949
3078
  if (isFunction(priority)) {
@@ -2953,6 +3082,13 @@ Keyboard.prototype.addListener = function (priority, listener, type) {
2953
3082
  }
2954
3083
  this._eventBus.on(type || KEYDOWN_EVENT, priority, listener);
2955
3084
  };
3085
+
3086
+ /**
3087
+ * Remove a listener function.
3088
+ *
3089
+ * @param {Listener} listener
3090
+ * @param {string} [type='keyboard.keydown']
3091
+ */
2956
3092
  Keyboard.prototype.removeListener = function (listener, type) {
2957
3093
  this._eventBus.off(type || KEYDOWN_EVENT, listener);
2958
3094
  };
@@ -3187,10 +3323,10 @@ function updateRow(formField, rowId) {
3187
3323
  }
3188
3324
 
3189
3325
  class AddFormFieldHandler {
3190
- /**
3191
- * @constructor
3192
- * @param { import('../../../FormEditor').default } formEditor
3193
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3326
+ /**
3327
+ * @constructor
3328
+ * @param { import('../../../FormEditor').default } formEditor
3329
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3194
3330
  */
3195
3331
  constructor(formEditor, formFieldRegistry) {
3196
3332
  this._formEditor = formEditor;
@@ -3251,10 +3387,10 @@ class AddFormFieldHandler {
3251
3387
  AddFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3252
3388
 
3253
3389
  class EditFormFieldHandler {
3254
- /**
3255
- * @constructor
3256
- * @param { import('../../../FormEditor').default } formEditor
3257
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3390
+ /**
3391
+ * @constructor
3392
+ * @param { import('../../../FormEditor').default } formEditor
3393
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3258
3394
  */
3259
3395
  constructor(formEditor, formFieldRegistry) {
3260
3396
  this._formEditor = formEditor;
@@ -3317,12 +3453,12 @@ class EditFormFieldHandler {
3317
3453
  EditFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3318
3454
 
3319
3455
  class MoveFormFieldHandler {
3320
- /**
3321
- * @constructor
3322
- * @param { import('../../../FormEditor').default } formEditor
3323
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3324
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3325
- * @param { import('@bpmn-io/form-js-viewer').FormLayouter } formLayouter
3456
+ /**
3457
+ * @constructor
3458
+ * @param { import('../../../FormEditor').default } formEditor
3459
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3460
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3461
+ * @param { import('@bpmn-io/form-js-viewer').FormLayouter } formLayouter
3326
3462
  */
3327
3463
  constructor(formEditor, formFieldRegistry, pathRegistry, formLayouter) {
3328
3464
  this._formEditor = formEditor;
@@ -3413,9 +3549,14 @@ class MoveFormFieldHandler {
3413
3549
  // (7) Reregister form field (and children) from path registry
3414
3550
  this._pathRegistry.executeRecursivelyOnFields(formField, ({
3415
3551
  field,
3416
- isClosed
3552
+ isClosed,
3553
+ isRepeatable
3417
3554
  }) => {
3418
- this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), isClosed);
3555
+ this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), {
3556
+ isClosed,
3557
+ isRepeatable,
3558
+ claimerId: field.id
3559
+ });
3419
3560
  });
3420
3561
  }
3421
3562
 
@@ -3428,10 +3569,10 @@ class MoveFormFieldHandler {
3428
3569
  MoveFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry', 'pathRegistry', 'formLayouter'];
3429
3570
 
3430
3571
  class RemoveFormFieldHandler {
3431
- /**
3432
- * @constructor
3433
- * @param { import('../../../FormEditor').default } formEditor
3434
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3572
+ /**
3573
+ * @constructor
3574
+ * @param { import('../../../FormEditor').default } formEditor
3575
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3435
3576
  */
3436
3577
  constructor(formEditor, formFieldRegistry) {
3437
3578
  this._formEditor = formEditor;
@@ -3491,9 +3632,9 @@ class RemoveFormFieldHandler {
3491
3632
  RemoveFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3492
3633
 
3493
3634
  class UpdateIdClaimHandler {
3494
- /**
3495
- * @constructor
3496
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3635
+ /**
3636
+ * @constructor
3637
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3497
3638
  */
3498
3639
  constructor(formFieldRegistry) {
3499
3640
  this._formFieldRegistry = formFieldRegistry;
@@ -3526,9 +3667,9 @@ class UpdateIdClaimHandler {
3526
3667
  UpdateIdClaimHandler.$inject = ['formFieldRegistry'];
3527
3668
 
3528
3669
  class UpdateKeyClaimHandler {
3529
- /**
3530
- * @constructor
3531
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3670
+ /**
3671
+ * @constructor
3672
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3532
3673
  */
3533
3674
  constructor(pathRegistry) {
3534
3675
  this._pathRegistry = pathRegistry;
@@ -3546,7 +3687,10 @@ class UpdateKeyClaimHandler {
3546
3687
  };
3547
3688
  const valuePath = this._pathRegistry.getValuePath(formField, options);
3548
3689
  if (claiming) {
3549
- this._pathRegistry.claimPath(valuePath, true);
3690
+ this._pathRegistry.claimPath(valuePath, {
3691
+ isClosed: true,
3692
+ claimerId: formField.id
3693
+ });
3550
3694
  } else {
3551
3695
  this._pathRegistry.unclaimPath(valuePath);
3552
3696
  }
@@ -3557,21 +3701,25 @@ class UpdateKeyClaimHandler {
3557
3701
  revert(context) {
3558
3702
  const {
3559
3703
  claiming,
3704
+ formField,
3560
3705
  valuePath
3561
3706
  } = context;
3562
3707
  if (claiming) {
3563
3708
  this._pathRegistry.unclaimPath(valuePath);
3564
3709
  } else {
3565
- this._pathRegistry.claimPath(valuePath, true);
3710
+ this._pathRegistry.claimPath(valuePath, {
3711
+ isClosed: true,
3712
+ claimerId: formField.id
3713
+ });
3566
3714
  }
3567
3715
  }
3568
3716
  }
3569
3717
  UpdateKeyClaimHandler.$inject = ['pathRegistry'];
3570
3718
 
3571
3719
  class UpdatePathClaimHandler {
3572
- /**
3573
- * @constructor
3574
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3720
+ /**
3721
+ * @constructor
3722
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3575
3723
  */
3576
3724
  constructor(pathRegistry) {
3577
3725
  this._pathRegistry = pathRegistry;
@@ -3591,24 +3739,34 @@ class UpdatePathClaimHandler {
3591
3739
  if (claiming) {
3592
3740
  this._pathRegistry.executeRecursivelyOnFields(formField, ({
3593
3741
  field,
3594
- isClosed
3742
+ isClosed,
3743
+ isRepeatable
3595
3744
  }) => {
3596
3745
  const valuePath = this._pathRegistry.getValuePath(field, options);
3597
3746
  valuePaths.push({
3598
3747
  valuePath,
3599
- isClosed
3748
+ isClosed,
3749
+ isRepeatable,
3750
+ claimerId: field.id
3751
+ });
3752
+ this._pathRegistry.claimPath(valuePath, {
3753
+ isClosed,
3754
+ isRepeatable,
3755
+ claimerId: field.id
3600
3756
  });
3601
- this._pathRegistry.claimPath(valuePath, isClosed);
3602
3757
  });
3603
3758
  } else {
3604
3759
  this._pathRegistry.executeRecursivelyOnFields(formField, ({
3605
3760
  field,
3606
- isClosed
3761
+ isClosed,
3762
+ isRepeatable
3607
3763
  }) => {
3608
3764
  const valuePath = this._pathRegistry.getValuePath(field, options);
3609
3765
  valuePaths.push({
3610
3766
  valuePath,
3611
- isClosed
3767
+ isClosed,
3768
+ isRepeatable,
3769
+ claimerId: field.id
3612
3770
  });
3613
3771
  this._pathRegistry.unclaimPath(valuePath);
3614
3772
  });
@@ -3631,9 +3789,15 @@ class UpdatePathClaimHandler {
3631
3789
  } else {
3632
3790
  valuePaths.forEach(({
3633
3791
  valuePath,
3634
- isClosed
3792
+ isClosed,
3793
+ isRepeatable,
3794
+ claimerId
3635
3795
  }) => {
3636
- this._pathRegistry.claimPath(valuePath, isClosed);
3796
+ this._pathRegistry.claimPath(valuePath, {
3797
+ isClosed,
3798
+ isRepeatable,
3799
+ claimerId
3800
+ });
3637
3801
  });
3638
3802
  }
3639
3803
  }
@@ -4142,8 +4306,8 @@ class ValidateBehavior extends CommandInterceptor {
4142
4306
  constructor(eventBus) {
4143
4307
  super(eventBus);
4144
4308
 
4145
- /**
4146
- * Remove custom validation if <validationType> is about to be added.
4309
+ /**
4310
+ * Remove custom validation if <validationType> is about to be added.
4147
4311
  */
4148
4312
  this.preExecute('formField.edit', function (context) {
4149
4313
  const {
@@ -4166,15 +4330,15 @@ class ValidateBehavior extends CommandInterceptor {
4166
4330
  }
4167
4331
  ValidateBehavior.$inject = ['eventBus'];
4168
4332
 
4169
- class ValuesSourceBehavior extends CommandInterceptor {
4333
+ class OptionsSourceBehavior extends CommandInterceptor {
4170
4334
  constructor(eventBus) {
4171
4335
  super(eventBus);
4172
4336
 
4173
- /**
4174
- * Cleanup properties on changing the values source.
4175
- *
4176
- * 1) Remove other sources, e.g. set `values` => remove `valuesKey` and `valuesExpression`
4177
- * 2) Remove default values for all other values sources
4337
+ /**
4338
+ * Cleanup properties on changing the values source.
4339
+ *
4340
+ * 1) Remove other sources, e.g. set `values` => remove `valuesKey` and `valuesExpression`
4341
+ * 2) Remove default values for all other values sources
4178
4342
  */
4179
4343
  this.preExecute('formField.edit', function (context) {
4180
4344
  const {
@@ -4186,15 +4350,15 @@ class ValuesSourceBehavior extends CommandInterceptor {
4186
4350
  }
4187
4351
 
4188
4352
  // clean up value sources that are not to going to be set
4189
- Object.values(VALUES_SOURCES).forEach(source => {
4190
- const path = VALUES_SOURCES_PATHS[source];
4353
+ Object.values(OPTIONS_SOURCES).forEach(source => {
4354
+ const path = OPTIONS_SOURCES_PATHS[source];
4191
4355
  if (get(properties, path) == undefined) {
4192
- newProperties[VALUES_SOURCES_PATHS[source]] = undefined;
4356
+ newProperties[OPTIONS_SOURCES_PATHS[source]] = undefined;
4193
4357
  }
4194
4358
  });
4195
4359
 
4196
4360
  // clean up default value
4197
- if (get(properties, VALUES_SOURCES_PATHS[VALUES_SOURCES.EXPRESSION]) !== undefined || get(properties, VALUES_SOURCES_PATHS[VALUES_SOURCES.INPUT]) !== undefined) {
4361
+ if (get(properties, OPTIONS_SOURCES_PATHS[OPTIONS_SOURCES.EXPRESSION]) !== undefined || get(properties, OPTIONS_SOURCES_PATHS[OPTIONS_SOURCES.INPUT]) !== undefined) {
4198
4362
  newProperties['defaultValue'] = undefined;
4199
4363
  }
4200
4364
  context.properties = {
@@ -4204,23 +4368,85 @@ class ValuesSourceBehavior extends CommandInterceptor {
4204
4368
  }, true);
4205
4369
  }
4206
4370
  }
4207
- ValuesSourceBehavior.$inject = ['eventBus'];
4371
+ OptionsSourceBehavior.$inject = ['eventBus'];
4208
4372
 
4209
4373
  // helper ///////////////////
4210
4374
 
4211
4375
  function isValuesSourceUpdate(properties) {
4212
- return Object.values(VALUES_SOURCES_PATHS).some(path => {
4376
+ return Object.values(OPTIONS_SOURCES_PATHS).some(path => {
4213
4377
  return get(properties, path) !== undefined;
4214
4378
  });
4215
4379
  }
4216
4380
 
4381
+ const COLUMNS_SOURCE_PROPERTIES = {
4382
+ columns: 'columns',
4383
+ columnsExpression: 'columnsExpression'
4384
+ };
4385
+ class ColumnsSourceBehavior extends CommandInterceptor {
4386
+ constructor(eventBus) {
4387
+ super(eventBus);
4388
+ this.preExecute('formField.edit', function (context) {
4389
+ const {
4390
+ properties,
4391
+ oldProperties
4392
+ } = context;
4393
+ const isColumnSourceUpdate = Object.values(COLUMNS_SOURCE_PROPERTIES).some(path => {
4394
+ return get(properties, [path]) !== undefined;
4395
+ });
4396
+ if (!isColumnSourceUpdate) {
4397
+ return;
4398
+ }
4399
+ const columns = get(properties, [COLUMNS_SOURCE_PROPERTIES.columns]);
4400
+ const oldColumns = get(oldProperties, [COLUMNS_SOURCE_PROPERTIES.columns]);
4401
+ const columnsExpression = get(properties, [COLUMNS_SOURCE_PROPERTIES.columnsExpression]);
4402
+ const oldColumnsExpression = get(oldProperties, [COLUMNS_SOURCE_PROPERTIES.columnsExpression]);
4403
+ if (isArray(columns) && !isDefined(oldColumns)) {
4404
+ context.properties = {
4405
+ ...properties,
4406
+ columnsExpression: undefined
4407
+ };
4408
+ return;
4409
+ }
4410
+ if (isString(columnsExpression) && !isString(oldColumnsExpression)) {
4411
+ context.properties = {
4412
+ ...properties,
4413
+ columns: undefined
4414
+ };
4415
+ return;
4416
+ }
4417
+ }, true);
4418
+ }
4419
+ }
4420
+ ColumnsSourceBehavior.$inject = ['eventBus'];
4421
+
4422
+ class TableDataSourceBehavior extends CommandInterceptor {
4423
+ constructor(eventBus) {
4424
+ super(eventBus);
4425
+ this.preExecute('formField.add', function (context) {
4426
+ const {
4427
+ formField
4428
+ } = context;
4429
+ if (get(formField, ['type']) !== 'table') {
4430
+ return;
4431
+ }
4432
+ context.formField = {
4433
+ ...formField,
4434
+ dataSource: `=${formField.id}`
4435
+ };
4436
+ }, true);
4437
+ }
4438
+ }
4439
+ TableDataSourceBehavior.$inject = ['eventBus'];
4440
+
4217
4441
  var behaviorModule = {
4218
- __init__: ['idBehavior', 'keyBehavior', 'pathBehavior', 'validateBehavior', 'valuesSourceBehavior'],
4442
+ __init__: ['idBehavior', 'keyBehavior', 'pathBehavior', 'validateBehavior', 'optionsSourceBehavior', 'columnsSourceBehavior', 'tableDataSourceBehavior'],
4219
4443
  idBehavior: ['type', IdBehavior],
4220
4444
  keyBehavior: ['type', KeyBehavior],
4221
4445
  pathBehavior: ['type', PathBehavior],
4222
4446
  validateBehavior: ['type', ValidateBehavior],
4223
- valuesSourceBehavior: ['type', ValuesSourceBehavior]
4447
+ optionsSourceBehavior: ['type', OptionsSourceBehavior],
4448
+ columnsSourceBehavior: ['type', ColumnsSourceBehavior],
4449
+ tableDataSourceBehavior: ['type', TableDataSourceBehavior]
4224
4450
  };
4225
4451
 
4226
4452
  /**
@@ -4772,22 +4998,22 @@ var SelectionModule = {
4772
4998
  selectionBehavior: ['type', SelectionBehavior]
4773
4999
  };
4774
5000
 
4775
- /**
4776
- * Base class for sectionable UI modules.
4777
- *
4778
- * @property {EventBus} _eventBus - EventBus instance used for event handling.
4779
- * @property {string} managerType - Type of the render manager. Used to form event names.
4780
- *
4781
- * @class SectionModuleBase
5001
+ /**
5002
+ * Base class for sectionable UI modules.
5003
+ *
5004
+ * @property {EventBus} _eventBus - EventBus instance used for event handling.
5005
+ * @property {string} managerType - Type of the render manager. Used to form event names.
5006
+ *
5007
+ * @class SectionModuleBase
4782
5008
  */
4783
5009
  class SectionModuleBase {
4784
- /**
4785
- * Create a SectionModuleBase instance.
4786
- *
4787
- * @param {any} eventBus - The EventBus instance used for event handling.
4788
- * @param {string} sectionKey - The type of render manager. Used to form event names.
4789
- *
4790
- * @constructor
5010
+ /**
5011
+ * Create a SectionModuleBase instance.
5012
+ *
5013
+ * @param {any} eventBus - The EventBus instance used for event handling.
5014
+ * @param {string} sectionKey - The type of render manager. Used to form event names.
5015
+ *
5016
+ * @constructor
4791
5017
  */
4792
5018
  constructor(eventBus, sectionKey) {
4793
5019
  this._eventBus = eventBus;
@@ -4800,10 +5026,10 @@ class SectionModuleBase {
4800
5026
  });
4801
5027
  }
4802
5028
 
4803
- /**
4804
- * Attach the managed section to a parent node.
4805
- *
4806
- * @param {HTMLElement} container - The parent node to attach to.
5029
+ /**
5030
+ * Attach the managed section to a parent node.
5031
+ *
5032
+ * @param {HTMLElement} container - The parent node to attach to.
4807
5033
  */
4808
5034
  attachTo(container) {
4809
5035
  this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.attach`, {
@@ -4811,22 +5037,22 @@ class SectionModuleBase {
4811
5037
  }));
4812
5038
  }
4813
5039
 
4814
- /**
4815
- * Detach the managed section from its parent node.
5040
+ /**
5041
+ * Detach the managed section from its parent node.
4816
5042
  */
4817
5043
  detach() {
4818
5044
  this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.detach`));
4819
5045
  }
4820
5046
 
4821
- /**
4822
- * Reset the managed section to its initial state.
5047
+ /**
5048
+ * Reset the managed section to its initial state.
4823
5049
  */
4824
5050
  reset() {
4825
5051
  this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.reset`));
4826
5052
  }
4827
5053
 
4828
- /**
4829
- * Circumvents timing issues.
5054
+ /**
5055
+ * Circumvents timing issues.
4830
5056
  */
4831
5057
  _onceSectionRendered(callback) {
4832
5058
  if (this.isSectionRendered) {
@@ -8504,11 +8730,11 @@ var index = {
8504
8730
  feelPopup: ['type', FeelPopupModule]
8505
8731
  };
8506
8732
 
8507
- /**
8508
- * @param {string} type
8509
- * @param {boolean} [strict]
8510
- *
8511
- * @returns {any}
8733
+ /**
8734
+ * @param {string} type
8735
+ * @param {boolean} [strict]
8736
+ *
8737
+ * @returns {any}
8512
8738
  */
8513
8739
  function getService(type, strict) {}
8514
8740
  const PropertiesPanelContext = createContext({
@@ -8541,11 +8767,15 @@ function textToLabel(text) {
8541
8767
  }
8542
8768
  return null;
8543
8769
  }
8770
+
8771
+ /**
8772
+ * @param {string} path
8773
+ */
8544
8774
  function isValidDotPath(path) {
8545
8775
  return /^\w+(\.\w+)*$/.test(path);
8546
8776
  }
8547
8777
  const INPUTS = ['checkbox', 'checklist', 'datetime', 'number', 'radio', 'select', 'taglist', 'textfield', 'textarea'];
8548
- const VALUES_INPUTS = ['checklist', 'radio', 'select', 'taglist'];
8778
+ const OPTIONS_INPUTS = ['checklist', 'radio', 'select', 'taglist'];
8549
8779
  function hasEntryConfigured(formFieldDefinition, entryId) {
8550
8780
  const {
8551
8781
  propertiesPanelEntries = []
@@ -8555,7 +8785,7 @@ function hasEntryConfigured(formFieldDefinition, entryId) {
8555
8785
  }
8556
8786
  return propertiesPanelEntries.some(id => id === entryId);
8557
8787
  }
8558
- function hasValuesGroupsConfigured(formFieldDefinition) {
8788
+ function hasOptionsGroupsConfigured(formFieldDefinition) {
8559
8789
  const {
8560
8790
  propertiesPanelEntries = []
8561
8791
  } = formFieldDefinition;
@@ -8565,6 +8795,13 @@ function hasValuesGroupsConfigured(formFieldDefinition) {
8565
8795
  return propertiesPanelEntries.some(id => id === 'values');
8566
8796
  }
8567
8797
 
8798
+ /**
8799
+ * @param {string} path
8800
+ */
8801
+ function hasIntegerPathSegment(path) {
8802
+ return path.split('.').some(segment => /^\d+$/.test(segment));
8803
+ }
8804
+
8568
8805
  function useService (type, strict) {
8569
8806
  const {
8570
8807
  getService
@@ -8572,10 +8809,10 @@ function useService (type, strict) {
8572
8809
  return getService(type, strict);
8573
8810
  }
8574
8811
 
8575
- /**
8576
- * Retrieve list of variables from the form schema.
8577
- *
8578
- * @returns { string[] } list of variables used in form schema
8812
+ /**
8813
+ * Retrieve list of variables from the form schema.
8814
+ *
8815
+ * @returns { string[] } list of variables used in form schema
8579
8816
  */
8580
8817
  function useVariables() {
8581
8818
  const form = useService('formEditor');
@@ -8641,8 +8878,8 @@ const PropertiesPanelHeaderProvider = {
8641
8878
  }
8642
8879
  };
8643
8880
 
8644
- /**
8645
- * Provide placeholders for empty and multiple state.
8881
+ /**
8882
+ * Provide placeholders for empty and multiple state.
8646
8883
  */
8647
8884
  const PropertiesPanelPlaceholderProvider = {
8648
8885
  getEmpty: () => {
@@ -8687,9 +8924,9 @@ function FormPropertiesPanel(props) {
8687
8924
  });
8688
8925
  }, [eventBus, formEditor, selectionModule]);
8689
8926
  useLayoutEffect(() => {
8690
- /**
8691
- * TODO(pinussilvestrus): update with actual updated element,
8692
- * once we have a proper updater/change support
8927
+ /**
8928
+ * TODO(pinussilvestrus): update with actual updated element,
8929
+ * once we have a proper updater/change support
8693
8930
  */
8694
8931
  eventBus.on('changed', refresh);
8695
8932
  eventBus.on('import.done', refresh);
@@ -8741,17 +8978,17 @@ function FormPropertiesPanel(props) {
8741
8978
 
8742
8979
  const DEFAULT_PRIORITY = 1000;
8743
8980
 
8744
- /**
8745
- * @typedef { { parent: Element } } PropertiesPanelConfig
8746
- * @typedef { import('../../core/EventBus').default } EventBus
8747
- * @typedef { import('../../types').Injector } Injector
8748
- * @typedef { { getGroups: ({ formField, editFormField }) => ({ groups}) => Array } } PropertiesProvider
8981
+ /**
8982
+ * @typedef { { parent: Element } } PropertiesPanelConfig
8983
+ * @typedef { import('../../core/EventBus').default } EventBus
8984
+ * @typedef { import('../../types').Injector } Injector
8985
+ * @typedef { { getGroups: ({ formField, editFormField }) => ({ groups}) => Array } } PropertiesProvider
8749
8986
  */
8750
8987
 
8751
- /**
8752
- * @param {PropertiesPanelConfig} propertiesPanelConfig
8753
- * @param {Injector} injector
8754
- * @param {EventBus} eventBus
8988
+ /**
8989
+ * @param {PropertiesPanelConfig} propertiesPanelConfig
8990
+ * @param {Injector} injector
8991
+ * @param {EventBus} eventBus
8755
8992
  */
8756
8993
  class PropertiesPanelRenderer {
8757
8994
  constructor(propertiesPanelConfig, injector, eventBus) {
@@ -8769,10 +9006,10 @@ class PropertiesPanelRenderer {
8769
9006
  });
8770
9007
  }
8771
9008
 
8772
- /**
8773
- * Attach the properties panel to a parent node.
8774
- *
8775
- * @param {HTMLElement} container
9009
+ /**
9010
+ * Attach the properties panel to a parent node.
9011
+ *
9012
+ * @param {HTMLElement} container
8776
9013
  */
8777
9014
  attachTo(container) {
8778
9015
  if (!container) {
@@ -8792,8 +9029,8 @@ class PropertiesPanelRenderer {
8792
9029
  this._eventBus.fire('propertiesPanel.attach');
8793
9030
  }
8794
9031
 
8795
- /**
8796
- * Detach the properties panel from its parent node.
9032
+ /**
9033
+ * Detach the properties panel from its parent node.
8797
9034
  */
8798
9035
  detach() {
8799
9036
  const parentNode = this._container.parentNode;
@@ -8817,11 +9054,11 @@ class PropertiesPanelRenderer {
8817
9054
  }
8818
9055
  }
8819
9056
 
8820
- /**
8821
- * Register a new properties provider to the properties panel.
8822
- *
8823
- * @param {PropertiesProvider} provider
8824
- * @param {Number} [priority]
9057
+ /**
9058
+ * Register a new properties provider to the properties panel.
9059
+ *
9060
+ * @param {PropertiesProvider} provider
9061
+ * @param {Number} [priority]
8825
9062
  */
8826
9063
  registerProvider(provider, priority) {
8827
9064
  if (!priority) {
@@ -9070,7 +9307,7 @@ function DefaultOptionEntry(props) {
9070
9307
  function isDefaultVisible(matchers) {
9071
9308
  return field => {
9072
9309
  // Only make default values available when they are statically defined
9073
- if (!INPUTS.includes(type) || VALUES_INPUTS.includes(type) && !field.values) {
9310
+ if (!INPUTS.includes(type) || OPTIONS_INPUTS.includes(type) && !field.values) {
9074
9311
  return false;
9075
9312
  }
9076
9313
  return matchers(field);
@@ -9416,20 +9653,27 @@ function containsSpace(value) {
9416
9653
  function KeyEntry(props) {
9417
9654
  const {
9418
9655
  editField,
9419
- field
9656
+ field,
9657
+ getService
9420
9658
  } = props;
9421
9659
  const entries = [];
9422
9660
  entries.push({
9423
9661
  id: 'key',
9424
- component: Key$1,
9662
+ component: Key$2,
9425
9663
  editField: editField,
9426
9664
  field: field,
9427
9665
  isEdited: isEdited,
9428
- isDefaultVisible: field => INPUTS.includes(field.type)
9666
+ isDefaultVisible: field => {
9667
+ const formFields = getService('formFields');
9668
+ const {
9669
+ config
9670
+ } = formFields.get(field.type);
9671
+ return config.keyed;
9672
+ }
9429
9673
  });
9430
9674
  return entries;
9431
9675
  }
9432
- function Key$1(props) {
9676
+ function Key$2(props) {
9433
9677
  const {
9434
9678
  editField,
9435
9679
  field,
@@ -9451,14 +9695,13 @@ function Key$1(props) {
9451
9695
  if (value === field.key) {
9452
9696
  return null;
9453
9697
  }
9454
- if (isUndefined(value) || !value.length) {
9698
+ if (!isString(value) || value.length === 0) {
9455
9699
  return 'Must not be empty.';
9456
9700
  }
9457
- if (value && !isValidDotPath(value)) {
9701
+ if (!isValidDotPath(value)) {
9458
9702
  return 'Must be a variable or a dot separated path.';
9459
9703
  }
9460
- const hasIntegerPathSegment = value.split('.').some(segment => /^\d+$/.test(segment));
9461
- if (hasIntegerPathSegment) {
9704
+ if (hasIntegerPathSegment(value)) {
9462
9705
  return 'Must not contain numerical path segments.';
9463
9706
  }
9464
9707
  const replacements = {
@@ -9471,8 +9714,14 @@ function Key$1(props) {
9471
9714
 
9472
9715
  // unclaim temporarily to avoid self-conflicts
9473
9716
  pathRegistry.unclaimPath(oldPath);
9474
- const canClaim = pathRegistry.canClaimPath(newPath, true);
9475
- pathRegistry.claimPath(oldPath, true);
9717
+ const canClaim = pathRegistry.canClaimPath(newPath, {
9718
+ isClosed: true,
9719
+ claimerId: field.id
9720
+ });
9721
+ pathRegistry.claimPath(oldPath, {
9722
+ isClosed: true,
9723
+ claimerId: field.id
9724
+ });
9476
9725
  return canClaim ? null : 'Must not conflict with other key/path assignments.';
9477
9726
  };
9478
9727
  return TextfieldEntry({
@@ -9491,13 +9740,15 @@ function Key$1(props) {
9491
9740
  function PathEntry(props) {
9492
9741
  const {
9493
9742
  editField,
9494
- field
9743
+ field,
9744
+ getService
9495
9745
  } = props;
9496
9746
  const {
9497
9747
  type
9498
9748
  } = field;
9499
9749
  const entries = [];
9500
- if (type === 'group') {
9750
+ const formFieldDefinition = getService('formFields').get(type);
9751
+ if (formFieldDefinition && formFieldDefinition.config.pathed) {
9501
9752
  entries.push({
9502
9753
  id: 'path',
9503
9754
  component: Path,
@@ -9516,6 +9767,8 @@ function Path(props) {
9516
9767
  } = props;
9517
9768
  const debounce = useService('debounce');
9518
9769
  const pathRegistry = useService('pathRegistry');
9770
+ const fieldConfig = useService('formFields').get(field.type).config;
9771
+ const isRepeating = fieldConfig.repeatable && field.isRepeating;
9519
9772
  const path = ['path'];
9520
9773
  const getValue = () => {
9521
9774
  return get(field, path, '');
@@ -9527,32 +9780,53 @@ function Path(props) {
9527
9780
  return editField(field, path, value);
9528
9781
  };
9529
9782
  const validate = value => {
9530
- if (!value || value === field.path) {
9783
+ if (!value && isRepeating) {
9784
+ return 'Must not be empty';
9785
+ }
9786
+
9787
+ // Early return for empty value in non-repeating cases or if the field path hasn't changed
9788
+ if (!value && !isRepeating || value === field.path) {
9531
9789
  return null;
9532
9790
  }
9533
- if (value && !isValidDotPath(value)) {
9534
- return 'Must be empty, a variable or a dot separated path';
9791
+
9792
+ // Validate dot-separated path format
9793
+ if (!isValidDotPath(value)) {
9794
+ const msg = isRepeating ? 'Must be a variable or a dot-separated path' : 'Must be empty, a variable or a dot-separated path';
9795
+ return msg;
9535
9796
  }
9536
- const hasIntegerPathSegment = value && value.split('.').some(segment => /^\d+$/.test(segment));
9797
+
9798
+ // Check for integer segments in the path
9799
+ const hasIntegerPathSegment = value.split('.').some(segment => /^\d+$/.test(segment));
9537
9800
  if (hasIntegerPathSegment) {
9538
9801
  return 'Must not contain numerical path segments.';
9539
9802
  }
9540
- const options = value && {
9803
+
9804
+ // Check for path collisions
9805
+ const options = {
9541
9806
  replacements: {
9542
- [field.id]: [value]
9807
+ [field.id]: value.split('.')
9543
9808
  }
9544
- } || {};
9809
+ };
9545
9810
  const canClaim = pathRegistry.executeRecursivelyOnFields(field, ({
9546
9811
  field,
9547
- isClosed
9812
+ isClosed,
9813
+ isRepeatable
9548
9814
  }) => {
9549
9815
  const path = pathRegistry.getValuePath(field, options);
9550
- return pathRegistry.canClaimPath(path, isClosed);
9816
+ return pathRegistry.canClaimPath(path, {
9817
+ isClosed,
9818
+ isRepeatable,
9819
+ claimerId: field.id
9820
+ });
9551
9821
  });
9552
9822
  if (!canClaim) {
9553
- return 'Must not cause two binding paths to colide';
9823
+ return 'Must not cause two binding paths to collide';
9554
9824
  }
9825
+
9826
+ // If all checks pass
9827
+ return null;
9555
9828
  };
9829
+ const tooltip = isRepeating ? 'Routes the children of this component into a form variable, may be left empty to route at the root level.' : 'Routes the children of this component into a form variable.';
9556
9830
  return TextfieldEntry({
9557
9831
  debounce,
9558
9832
  description: 'Where the child variables of this component are pathed to.',
@@ -9560,7 +9834,7 @@ function Path(props) {
9560
9834
  getValue,
9561
9835
  id,
9562
9836
  label: 'Path',
9563
- tooltip: 'Routes the children of this component into a form variable, may be left empty to route at the root level.',
9837
+ tooltip,
9564
9838
  setValue,
9565
9839
  validate
9566
9840
  });
@@ -9617,14 +9891,118 @@ const SimpleBoolComponent = props => {
9617
9891
  });
9618
9892
  };
9619
9893
 
9620
- function GroupEntries(props) {
9894
+ function simpleSelectEntryFactory(options) {
9895
+ const {
9896
+ id,
9897
+ label,
9898
+ path,
9899
+ props,
9900
+ optionsArray
9901
+ } = options;
9902
+ const {
9903
+ editField,
9904
+ field
9905
+ } = props;
9906
+ return {
9907
+ id,
9908
+ label,
9909
+ path,
9910
+ field,
9911
+ editField,
9912
+ optionsArray,
9913
+ component: SimpleSelectComponent,
9914
+ isEdited: isEdited$3
9915
+ };
9916
+ }
9917
+ const SimpleSelectComponent = props => {
9918
+ const {
9919
+ id,
9920
+ label,
9921
+ path,
9922
+ field,
9923
+ editField,
9924
+ optionsArray
9925
+ } = props;
9926
+ const getValue = () => get(field, path, '');
9927
+ const setValue = value => editField(field, path, value);
9928
+ const getOptions = () => optionsArray;
9929
+ return SelectEntry({
9930
+ label,
9931
+ element: field,
9932
+ getOptions,
9933
+ getValue,
9934
+ id,
9935
+ setValue
9936
+ });
9937
+ };
9938
+
9939
+ function simpleRangeIntegerEntryFactory(options) {
9940
+ const {
9941
+ id,
9942
+ label,
9943
+ path,
9944
+ props,
9945
+ min,
9946
+ max,
9947
+ defaultValue
9948
+ } = options;
9949
+ const {
9950
+ editField,
9951
+ field
9952
+ } = props;
9953
+ return {
9954
+ id,
9955
+ label,
9956
+ path,
9957
+ field,
9958
+ editField,
9959
+ min,
9960
+ max,
9961
+ defaultValue,
9962
+ component: SimpleRangeIntegerEntry,
9963
+ isEdited: isEdited$7
9964
+ };
9965
+ }
9966
+ const SimpleRangeIntegerEntry = props => {
9967
+ const {
9968
+ id,
9969
+ label,
9970
+ path,
9971
+ field,
9972
+ editField,
9973
+ min,
9974
+ max,
9975
+ defaultValue
9976
+ } = props;
9977
+ const debounce = useService('debounce');
9978
+ const getValue = () => {
9979
+ const value = get(field, path, defaultValue);
9980
+ return Number.isInteger(value) ? value : defaultValue;
9981
+ };
9982
+ const setValue = value => {
9983
+ editField(field, path, value);
9984
+ };
9985
+ return NumberFieldEntry({
9986
+ debounce,
9987
+ label,
9988
+ element: field,
9989
+ step: 1,
9990
+ min,
9991
+ max,
9992
+ getValue,
9993
+ id,
9994
+ setValue
9995
+ });
9996
+ };
9997
+
9998
+ function GroupAppearanceEntry(props) {
9621
9999
  const {
9622
10000
  field
9623
10001
  } = props;
9624
10002
  const {
9625
10003
  type
9626
10004
  } = field;
9627
- if (type !== 'group') {
10005
+ if (!['group', 'dynamiclist'].includes(type)) {
9628
10006
  return [];
9629
10007
  }
9630
10008
  const entries = [simpleBoolEntryFactory({
@@ -9664,15 +10042,15 @@ function LabelEntry(props) {
9664
10042
  });
9665
10043
  entries.push({
9666
10044
  id: 'label',
9667
- component: Label$1,
10045
+ component: Label$2,
9668
10046
  editField,
9669
10047
  field,
9670
10048
  isEdited: isEdited$6,
9671
- isDefaultVisible: field => INPUTS.includes(field.type) || field.type === 'button' || field.type === 'group' || field.type === 'iframe'
10049
+ isDefaultVisible: field => [...INPUTS, 'button', 'group', 'table', 'iframe', 'dynamiclist'].includes(field.type)
9672
10050
  });
9673
10051
  return entries;
9674
10052
  }
9675
- function Label$1(props) {
10053
+ function Label$2(props) {
9676
10054
  const {
9677
10055
  editField,
9678
10056
  field,
@@ -9689,7 +10067,7 @@ function Label$1(props) {
9689
10067
  const setValue = value => {
9690
10068
  return editField(field, path, value || '');
9691
10069
  };
9692
- const label = getLabelText(field);
10070
+ const label = getLabelText(field.type);
9693
10071
  return FeelTemplatingEntry({
9694
10072
  debounce,
9695
10073
  element: field,
@@ -9760,17 +10138,22 @@ function TimeLabel(props) {
9760
10138
 
9761
10139
  // helpers //////////
9762
10140
 
9763
- function getLabelText(field) {
9764
- const {
9765
- type
9766
- } = field;
9767
- if (type === 'group') {
9768
- return 'Group label';
9769
- }
9770
- if (type === 'iframe') {
9771
- return 'Title';
10141
+ /**
10142
+ * @param {string} type
10143
+ * @returns {string}
10144
+ */
10145
+ function getLabelText(type) {
10146
+ switch (type) {
10147
+ case 'group':
10148
+ case 'dynamiclist':
10149
+ return 'Group label';
10150
+ case 'table':
10151
+ return 'Table label';
10152
+ case 'iframe':
10153
+ return 'Title';
10154
+ default:
10155
+ return 'Field label';
9772
10156
  }
9773
- return 'Field label';
9774
10157
  }
9775
10158
 
9776
10159
  function HeightEntry(props) {
@@ -9925,7 +10308,7 @@ function SourceEntry(props) {
9925
10308
  const entries = [];
9926
10309
  entries.push({
9927
10310
  id: 'source',
9928
- component: Source,
10311
+ component: Source$1,
9929
10312
  editField: editField,
9930
10313
  field: field,
9931
10314
  isEdited: isEdited$6,
@@ -9933,7 +10316,7 @@ function SourceEntry(props) {
9933
10316
  });
9934
10317
  return entries;
9935
10318
  }
9936
- function Source(props) {
10319
+ function Source$1(props) {
9937
10320
  const {
9938
10321
  editField,
9939
10322
  field,
@@ -9978,18 +10361,6 @@ function TextEntry(props) {
9978
10361
  isEdited: isEdited$6,
9979
10362
  isDefaultVisible: field => field.type === 'text'
9980
10363
  }];
9981
-
9982
- // todo: skipped to make the release without too much risk
9983
- // if (templating.isTemplate(field.text)) {
9984
- // entries.push(simpleBoolEntryFactory({
9985
- // id: 'strict',
9986
- // path: [ 'strict' ],
9987
- // label: 'Strict templating',
9988
- // description: 'Enforces types to be correct',
9989
- // props
9990
- // }));
9991
- // }
9992
-
9993
10364
  return entries;
9994
10365
  }
9995
10366
  function Text(props) {
@@ -10417,7 +10788,7 @@ function ValueEntry(props) {
10417
10788
  validateFactory
10418
10789
  } = props;
10419
10790
  const entries = [{
10420
- component: Label,
10791
+ component: Label$1,
10421
10792
  editField,
10422
10793
  field,
10423
10794
  id: idPrefix + '-label',
@@ -10435,7 +10806,7 @@ function ValueEntry(props) {
10435
10806
  }];
10436
10807
  return entries;
10437
10808
  }
10438
- function Label(props) {
10809
+ function Label$1(props) {
10439
10810
  const {
10440
10811
  editField,
10441
10812
  field,
@@ -10503,7 +10874,7 @@ function CustomValueEntry(props) {
10503
10874
  validateFactory
10504
10875
  } = props;
10505
10876
  const entries = [{
10506
- component: Key,
10877
+ component: Key$1,
10507
10878
  editField,
10508
10879
  field,
10509
10880
  id: idPrefix + '-key',
@@ -10521,7 +10892,7 @@ function CustomValueEntry(props) {
10521
10892
  }];
10522
10893
  return entries;
10523
10894
  }
10524
- function Key(props) {
10895
+ function Key$1(props) {
10525
10896
  const {
10526
10897
  editField,
10527
10898
  field,
@@ -10583,14 +10954,14 @@ function Value(props) {
10583
10954
 
10584
10955
  // helpers //////////
10585
10956
 
10586
- /**
10587
- * Returns copy of object with updated value.
10588
- *
10589
- * @param {Object} properties
10590
- * @param {string} key
10591
- * @param {string} value
10592
- *
10593
- * @returns {Object}
10957
+ /**
10958
+ * Returns copy of object with updated value.
10959
+ *
10960
+ * @param {Object} properties
10961
+ * @param {string} key
10962
+ * @param {string} value
10963
+ *
10964
+ * @returns {Object}
10594
10965
  */
10595
10966
  function updateValue(properties, key, value) {
10596
10967
  return {
@@ -10599,14 +10970,14 @@ function updateValue(properties, key, value) {
10599
10970
  };
10600
10971
  }
10601
10972
 
10602
- /**
10603
- * Returns copy of object with updated key.
10604
- *
10605
- * @param {Object} properties
10606
- * @param {string} oldKey
10607
- * @param {string} newKey
10608
- *
10609
- * @returns {Object}
10973
+ /**
10974
+ * Returns copy of object with updated key.
10975
+ *
10976
+ * @param {Object} properties
10977
+ * @param {string} oldKey
10978
+ * @param {string} newKey
10979
+ *
10980
+ * @returns {Object}
10610
10981
  */
10611
10982
  function updateKey(properties, oldKey, newKey) {
10612
10983
  return Object.entries(properties).reduce((newProperties, entry) => {
@@ -10646,7 +11017,7 @@ function AutoFocusSelectEntry(props) {
10646
11017
  });
10647
11018
  }
10648
11019
 
10649
- function ValuesSourceSelectEntry(props) {
11020
+ function OptionsSourceSelectEntry(props) {
10650
11021
  const {
10651
11022
  editField,
10652
11023
  field,
@@ -10666,25 +11037,25 @@ function ValuesSourceSelect(props) {
10666
11037
  field,
10667
11038
  id
10668
11039
  } = props;
10669
- const getValue = getValuesSource;
11040
+ const getValue = getOptionsSource;
10670
11041
  const setValue = value => {
10671
11042
  let newField = field;
10672
11043
  const newProperties = {};
10673
- newProperties[VALUES_SOURCES_PATHS[value]] = VALUES_SOURCES_DEFAULTS[value];
11044
+ newProperties[OPTIONS_SOURCES_PATHS[value]] = OPTIONS_SOURCES_DEFAULTS[value];
10674
11045
  newField = editField(field, newProperties);
10675
11046
  return newField;
10676
11047
  };
10677
- const getValuesSourceOptions = () => {
10678
- return Object.values(VALUES_SOURCES).map(valueSource => ({
10679
- label: VALUES_SOURCES_LABELS[valueSource],
11048
+ const getOptionsSourceOptions = () => {
11049
+ return Object.values(OPTIONS_SOURCES).map(valueSource => ({
11050
+ label: OPTIONS_SOURCES_LABELS[valueSource],
10680
11051
  value: valueSource
10681
11052
  }));
10682
11053
  };
10683
11054
  return AutoFocusSelectEntry({
10684
- autoFocusEntry: getAutoFocusEntryId(field),
11055
+ autoFocusEntry: getAutoFocusEntryId$1(field),
10685
11056
  label: 'Type',
10686
11057
  element: field,
10687
- getOptions: getValuesSourceOptions,
11058
+ getOptions: getOptionsSourceOptions,
10688
11059
  getValue,
10689
11060
  id,
10690
11061
  setValue
@@ -10693,19 +11064,19 @@ function ValuesSourceSelect(props) {
10693
11064
 
10694
11065
  // helpers //////////
10695
11066
 
10696
- function getAutoFocusEntryId(field) {
10697
- const valuesSource = getValuesSource(field);
10698
- if (valuesSource === VALUES_SOURCES.EXPRESSION) {
10699
- return `${field.id}-valuesExpression-expression`;
10700
- } else if (valuesSource === VALUES_SOURCES.INPUT) {
10701
- return `${field.id}-dynamicValues-key`;
10702
- } else if (valuesSource === VALUES_SOURCES.STATIC) {
10703
- return `${field.id}-staticValues-0-label`;
11067
+ function getAutoFocusEntryId$1(field) {
11068
+ const valuesSource = getOptionsSource(field);
11069
+ if (valuesSource === OPTIONS_SOURCES.EXPRESSION) {
11070
+ return 'optionsExpression-expression';
11071
+ } else if (valuesSource === OPTIONS_SOURCES.INPUT) {
11072
+ return 'dynamicOptions-key';
11073
+ } else if (valuesSource === OPTIONS_SOURCES.STATIC) {
11074
+ return 'staticOptions-0-label';
10704
11075
  }
10705
11076
  return null;
10706
11077
  }
10707
11078
 
10708
- function InputKeyValuesSourceEntry(props) {
11079
+ function InputKeyOptionsSourceEntry(props) {
10709
11080
  const {
10710
11081
  editField,
10711
11082
  field,
@@ -10726,7 +11097,7 @@ function InputValuesKey(props) {
10726
11097
  id
10727
11098
  } = props;
10728
11099
  const debounce = useService('debounce');
10729
- const path = VALUES_SOURCES_PATHS[VALUES_SOURCES.INPUT];
11100
+ const path = OPTIONS_SOURCES_PATHS[OPTIONS_SOURCES.INPUT];
10730
11101
  const schema = '[\n {\n "label": "dollar",\n "value": "$"\n }\n]';
10731
11102
  const tooltip = jsxs("div", {
10732
11103
  children: ["The input property may be an array of simple values or alternatively follow this schema:", jsx("pre", {
@@ -10764,7 +11135,7 @@ function InputValuesKey(props) {
10764
11135
  });
10765
11136
  }
10766
11137
 
10767
- function StaticValuesSourceEntry(props) {
11138
+ function StaticOptionsSourceEntry(props) {
10768
11139
  const {
10769
11140
  editField,
10770
11141
  field,
@@ -10777,10 +11148,10 @@ function StaticValuesSourceEntry(props) {
10777
11148
  e.stopPropagation();
10778
11149
  const index = values.length + 1;
10779
11150
  const entry = getIndexedEntry(index, values);
10780
- editField(field, VALUES_SOURCES_PATHS[VALUES_SOURCES.STATIC], arrayAdd(values, values.length, entry));
11151
+ editField(field, OPTIONS_SOURCES_PATHS[OPTIONS_SOURCES.STATIC], arrayAdd(values, values.length, entry));
10781
11152
  };
10782
11153
  const removeEntry = entry => {
10783
- editField(field, VALUES_SOURCES_PATHS[VALUES_SOURCES.STATIC], without(values, entry));
11154
+ editField(field, OPTIONS_SOURCES_PATHS[OPTIONS_SOURCES.STATIC], without(values, entry));
10784
11155
  };
10785
11156
  const validateFactory = (key, getValue) => {
10786
11157
  return value => {
@@ -10970,6 +11341,76 @@ function Readonly(props) {
10970
11341
  });
10971
11342
  }
10972
11343
 
11344
+ function LayouterAppearanceEntry(props) {
11345
+ const {
11346
+ field
11347
+ } = props;
11348
+ if (!['group', 'dynamiclist'].includes(field.type)) {
11349
+ return [];
11350
+ }
11351
+ const entries = [simpleSelectEntryFactory({
11352
+ id: 'verticalAlignment',
11353
+ path: ['verticalAlignment'],
11354
+ label: 'Vertical alignment',
11355
+ optionsArray: [{
11356
+ value: 'start',
11357
+ label: 'Top'
11358
+ }, {
11359
+ value: 'center',
11360
+ label: 'Center'
11361
+ }, {
11362
+ value: 'end',
11363
+ label: 'Bottom'
11364
+ }],
11365
+ props
11366
+ })];
11367
+ return entries;
11368
+ }
11369
+
11370
+ function RepeatableEntry(props) {
11371
+ const {
11372
+ field,
11373
+ getService
11374
+ } = props;
11375
+ const {
11376
+ type
11377
+ } = field;
11378
+ const formFieldDefinition = getService('formFields').get(type);
11379
+ if (!formFieldDefinition || !formFieldDefinition.config.repeatable) {
11380
+ return [];
11381
+ }
11382
+ const entries = [simpleRangeIntegerEntryFactory({
11383
+ id: 'defaultRepetitions',
11384
+ path: ['defaultRepetitions'],
11385
+ label: 'Default number of items',
11386
+ min: 0,
11387
+ max: 20,
11388
+ props
11389
+ }), simpleBoolEntryFactory({
11390
+ id: 'allowAddRemove',
11391
+ path: ['allowAddRemove'],
11392
+ label: 'Allow add/delete items',
11393
+ props
11394
+ }), simpleBoolEntryFactory({
11395
+ id: 'disableCollapse',
11396
+ path: ['disableCollapse'],
11397
+ label: 'Disable collapse',
11398
+ props
11399
+ })];
11400
+ if (!field.disableCollapse) {
11401
+ const nonCollapseItemsEntry = simpleRangeIntegerEntryFactory({
11402
+ id: 'nonCollapsedItems',
11403
+ path: ['nonCollapsedItems'],
11404
+ label: 'Number of non-collapsing items',
11405
+ min: 1,
11406
+ defaultValue: 5,
11407
+ props
11408
+ });
11409
+ entries.push(nonCollapseItemsEntry);
11410
+ }
11411
+ return entries;
11412
+ }
11413
+
10973
11414
  function ConditionEntry(props) {
10974
11415
  const {
10975
11416
  editField,
@@ -11018,7 +11459,7 @@ function Condition(props) {
11018
11459
  });
11019
11460
  }
11020
11461
 
11021
- function ValuesExpressionEntry(props) {
11462
+ function OptionsExpressionEntry(props) {
11022
11463
  const {
11023
11464
  editField,
11024
11465
  field,
@@ -11026,13 +11467,13 @@ function ValuesExpressionEntry(props) {
11026
11467
  } = props;
11027
11468
  return [{
11028
11469
  id: id + '-expression',
11029
- component: ValuesExpression,
11470
+ component: OptionsExpression,
11030
11471
  isEdited: isEdited$6,
11031
11472
  editField,
11032
11473
  field
11033
11474
  }];
11034
11475
  }
11035
- function ValuesExpression(props) {
11476
+ function OptionsExpression(props) {
11036
11477
  const {
11037
11478
  editField,
11038
11479
  field,
@@ -11042,7 +11483,7 @@ function ValuesExpression(props) {
11042
11483
  const variables = useVariables().map(name => ({
11043
11484
  name
11044
11485
  }));
11045
- const path = VALUES_SOURCES_PATHS[VALUES_SOURCES.EXPRESSION];
11486
+ const path = OPTIONS_SOURCES_PATHS[OPTIONS_SOURCES.EXPRESSION];
11046
11487
  const schema = '[\n {\n "label": "dollar",\n "value": "$"\n }\n]';
11047
11488
  const tooltip = jsxs("div", {
11048
11489
  children: ["The expression may result in an array of simple values or alternatively follow this schema:", jsx("pre", {
@@ -11067,36 +11508,550 @@ function ValuesExpression(props) {
11067
11508
  });
11068
11509
  }
11069
11510
 
11070
- function GeneralGroup(field, editField, getService) {
11071
- const entries = [...IdEntry({
11072
- field,
11073
- editField
11074
- }), ...LabelEntry({
11075
- field,
11076
- editField
11077
- }), ...DescriptionEntry({
11078
- field,
11079
- editField
11080
- }), ...KeyEntry({
11081
- field,
11082
- editField
11083
- }), ...PathEntry({
11084
- field,
11085
- editField
11086
- }), ...GroupEntries({
11087
- field,
11088
- editField
11089
- }), ...DefaultOptionEntry({
11090
- field,
11091
- editField
11092
- }), ...ActionEntry({
11093
- field,
11094
- editField
11095
- }), ...DateTimeEntry({
11096
- field,
11097
- editField
11098
- }), ...TextEntry({
11099
- field,
11511
+ function TableDataSourceEntry(props) {
11512
+ const {
11513
+ editField,
11514
+ field
11515
+ } = props;
11516
+ const entries = [];
11517
+ entries.push({
11518
+ id: 'dataSource',
11519
+ component: Source,
11520
+ editField: editField,
11521
+ field: field,
11522
+ isEdited: isEdited$6,
11523
+ isDefaultVisible: field => field.type === 'table'
11524
+ });
11525
+ return entries;
11526
+ }
11527
+ function Source(props) {
11528
+ const {
11529
+ editField,
11530
+ field,
11531
+ id
11532
+ } = props;
11533
+ const debounce = useService('debounce');
11534
+ const variables = useVariables().map(name => ({
11535
+ name
11536
+ }));
11537
+ const path = ['dataSource'];
11538
+ const getValue = () => {
11539
+ return get(field, path, field.id);
11540
+ };
11541
+ const setValue = (value, error) => {
11542
+ if (error) {
11543
+ return;
11544
+ }
11545
+ editField(field, path, value);
11546
+ };
11547
+
11548
+ /**
11549
+ * @param {string|void} value
11550
+ * @returns {string|null}
11551
+ */
11552
+ const validate = value => {
11553
+ if (!isString(value) || value.length === 0) {
11554
+ return 'Must not be empty.';
11555
+ }
11556
+ if (value.startsWith('=')) {
11557
+ return null;
11558
+ }
11559
+ if (!isValidDotPath(value)) {
11560
+ return 'Must be a variable or a dot separated path.';
11561
+ }
11562
+ if (hasIntegerPathSegment(value)) {
11563
+ return 'Must not contain numerical path segments.';
11564
+ }
11565
+ return null;
11566
+ };
11567
+ return FeelTemplatingEntry({
11568
+ debounce,
11569
+ description: 'Specify the source from which to populate the table',
11570
+ element: field,
11571
+ feel: 'required',
11572
+ getValue,
11573
+ id,
11574
+ label: 'Data source',
11575
+ tooltip: 'Enter a form input variable that contains the data for the table or define an expression to populate the data dynamically.',
11576
+ setValue,
11577
+ singleLine: true,
11578
+ variables,
11579
+ validate
11580
+ });
11581
+ }
11582
+
11583
+ function PaginationEntry(props) {
11584
+ const {
11585
+ editField,
11586
+ field
11587
+ } = props;
11588
+ const entries = [];
11589
+ entries.push({
11590
+ id: 'pagination',
11591
+ component: Pagination,
11592
+ editField: editField,
11593
+ field: field,
11594
+ isEdited: isEdited$8,
11595
+ isDefaultVisible: field => field.type === 'table'
11596
+ });
11597
+ return entries;
11598
+ }
11599
+ function Pagination(props) {
11600
+ const {
11601
+ editField,
11602
+ field,
11603
+ id
11604
+ } = props;
11605
+ const defaultRowCount = 10;
11606
+ const path = ['rowCount'];
11607
+ const getValue = () => {
11608
+ return isNumber(get(field, path));
11609
+ };
11610
+
11611
+ /**
11612
+ * @param {boolean} value
11613
+ */
11614
+ const setValue = value => {
11615
+ value ? editField(field, path, defaultRowCount) : editField(field, path, undefined);
11616
+ };
11617
+ return ToggleSwitchEntry({
11618
+ element: field,
11619
+ getValue,
11620
+ id,
11621
+ label: 'Pagination',
11622
+ inline: true,
11623
+ setValue
11624
+ });
11625
+ }
11626
+
11627
+ const path$2 = ['rowCount'];
11628
+ function RowCountEntry(props) {
11629
+ const {
11630
+ editField,
11631
+ field
11632
+ } = props;
11633
+ const entries = [];
11634
+ entries.push({
11635
+ id: 'rowCount',
11636
+ component: RowCount,
11637
+ isEdited: isEdited$7,
11638
+ editField,
11639
+ field,
11640
+ isDefaultVisible: field => field.type === 'table' && isNumber(get(field, path$2))
11641
+ });
11642
+ return entries;
11643
+ }
11644
+ function RowCount(props) {
11645
+ const {
11646
+ editField,
11647
+ field,
11648
+ id
11649
+ } = props;
11650
+ const debounce = useService('debounce');
11651
+ const getValue = () => get(field, path$2);
11652
+
11653
+ /**
11654
+ * @param {number|void} value
11655
+ * @param {string|null} error
11656
+ * @returns {void}
11657
+ */
11658
+ const setValue = (value, error) => {
11659
+ if (error) {
11660
+ return;
11661
+ }
11662
+ editField(field, path$2, value);
11663
+ };
11664
+
11665
+ /**
11666
+ * @param {string|void} value
11667
+ * @returns {string|null}
11668
+ */
11669
+ const validate = value => {
11670
+ if (isNil(value)) {
11671
+ return null;
11672
+ }
11673
+ if (!isNumber(value)) {
11674
+ return 'Must be number';
11675
+ }
11676
+ if (!Number.isInteger(value)) {
11677
+ return 'Should be an integer.';
11678
+ }
11679
+ if (value < 1) {
11680
+ return 'Should be greater than zero.';
11681
+ }
11682
+ return null;
11683
+ };
11684
+ return NumberFieldEntry({
11685
+ debounce,
11686
+ label: 'Number of rows per page',
11687
+ element: field,
11688
+ id,
11689
+ getValue,
11690
+ setValue,
11691
+ validate
11692
+ });
11693
+ }
11694
+
11695
+ const OPTIONS = {
11696
+ static: {
11697
+ label: 'List of items',
11698
+ value: 'static'
11699
+ },
11700
+ expression: {
11701
+ label: 'Expression',
11702
+ value: 'expression'
11703
+ }
11704
+ };
11705
+ const SELECT_OPTIONS = Object.values(OPTIONS);
11706
+ const COLUMNS_PATH = ['columns'];
11707
+ const COLUMNS_EXPRESSION_PATH = ['columnsExpression'];
11708
+ function HeadersSourceSelectEntry(props) {
11709
+ const {
11710
+ editField,
11711
+ field,
11712
+ id
11713
+ } = props;
11714
+ return [{
11715
+ id: id + '-select',
11716
+ component: HeadersSourceSelect,
11717
+ isEdited: isEdited$3,
11718
+ editField,
11719
+ field
11720
+ }];
11721
+ }
11722
+ function HeadersSourceSelect(props) {
11723
+ const {
11724
+ editField,
11725
+ field,
11726
+ id
11727
+ } = props;
11728
+
11729
+ /**
11730
+ * @returns {string|void}
11731
+ */
11732
+ const getValue = () => {
11733
+ const columns = get(field, COLUMNS_PATH);
11734
+ const columnsExpression = get(field, COLUMNS_EXPRESSION_PATH);
11735
+ if (isString(columnsExpression)) {
11736
+ return OPTIONS.expression.value;
11737
+ }
11738
+ if (isArray(columns)) {
11739
+ return OPTIONS.static.value;
11740
+ }
11741
+ };
11742
+
11743
+ /**
11744
+ * @param {string|void} value
11745
+ */
11746
+ const setValue = value => {
11747
+ switch (value) {
11748
+ case OPTIONS.static.value:
11749
+ editField(field, {
11750
+ columns: [{
11751
+ label: 'Column',
11752
+ key: 'inputVariable'
11753
+ }]
11754
+ });
11755
+ break;
11756
+ case OPTIONS.expression.value:
11757
+ editField(field, {
11758
+ columnsExpression: '='
11759
+ });
11760
+ break;
11761
+ }
11762
+ };
11763
+ const getValuesSourceOptions = () => {
11764
+ return SELECT_OPTIONS;
11765
+ };
11766
+ return AutoFocusSelectEntry({
11767
+ autoFocusEntry: getAutoFocusEntryId(field),
11768
+ label: 'Type',
11769
+ element: field,
11770
+ getOptions: getValuesSourceOptions,
11771
+ getValue,
11772
+ id,
11773
+ setValue
11774
+ });
11775
+ }
11776
+
11777
+ // helpers //////////
11778
+
11779
+ function getAutoFocusEntryId(field) {
11780
+ const columns = get(field, COLUMNS_PATH);
11781
+ const columnsExpression = get(field, COLUMNS_EXPRESSION_PATH);
11782
+ if (isString(columnsExpression)) {
11783
+ return `${field.id}-columnsExpression`;
11784
+ }
11785
+ if (isArray(columns)) {
11786
+ return `${field.id}-columns-0-label`;
11787
+ }
11788
+ return null;
11789
+ }
11790
+
11791
+ const PATH = ['columnsExpression'];
11792
+ function ColumnsExpressionEntry(props) {
11793
+ const {
11794
+ editField,
11795
+ field
11796
+ } = props;
11797
+ const entries = [];
11798
+ entries.push({
11799
+ id: `${field.id}-columnsExpression`,
11800
+ component: ColumnsExpression,
11801
+ editField: editField,
11802
+ field: field,
11803
+ isEdited: isEdited$6,
11804
+ isDefaultVisible: field => field.type === 'table' && isString(get(field, PATH))
11805
+ });
11806
+ return entries;
11807
+ }
11808
+ function ColumnsExpression(props) {
11809
+ const {
11810
+ editField,
11811
+ field,
11812
+ id
11813
+ } = props;
11814
+ const debounce = useService('debounce');
11815
+ const variables = useVariables().map(name => ({
11816
+ name
11817
+ }));
11818
+ const getValue = () => {
11819
+ return get(field, PATH);
11820
+ };
11821
+
11822
+ /**
11823
+ * @param {string|void} value
11824
+ * @param {string|void} error
11825
+ * @returns {void}
11826
+ */
11827
+ const setValue = (value, error) => {
11828
+ if (error) {
11829
+ return;
11830
+ }
11831
+ editField(field, PATH, value);
11832
+ };
11833
+
11834
+ /**
11835
+ * @param {string|void} value
11836
+ * @returns {string|null}
11837
+ */
11838
+ const validate = value => {
11839
+ if (!isString(value) || value.length === 0 || value === '=') {
11840
+ return 'Must not be empty.';
11841
+ }
11842
+ return null;
11843
+ };
11844
+ const schema = '[\n {\n "key": "column_1",\n "label": "Column 1"\n }\n]';
11845
+ const tooltip = jsxs("div", {
11846
+ children: ["The expression may result in an array of simple values or alternatively follow this schema:", jsx("pre", {
11847
+ children: jsx("code", {
11848
+ children: schema
11849
+ })
11850
+ })]
11851
+ });
11852
+ return FeelTemplatingEntry({
11853
+ debounce,
11854
+ description: 'Specify an expression to populate column items',
11855
+ element: field,
11856
+ feel: 'required',
11857
+ getValue,
11858
+ id,
11859
+ label: 'Expression',
11860
+ tooltip,
11861
+ setValue,
11862
+ singleLine: true,
11863
+ variables,
11864
+ validate
11865
+ });
11866
+ }
11867
+
11868
+ const path$1 = 'columns';
11869
+ const labelPath = 'label';
11870
+ const keyPath = 'key';
11871
+ function ColumnEntry(props) {
11872
+ const {
11873
+ editField,
11874
+ field,
11875
+ idPrefix,
11876
+ index,
11877
+ validateFactory
11878
+ } = props;
11879
+ const entries = [{
11880
+ component: Label,
11881
+ editField,
11882
+ field,
11883
+ id: idPrefix + '-label',
11884
+ idPrefix,
11885
+ index,
11886
+ validateFactory
11887
+ }, {
11888
+ component: Key,
11889
+ editField,
11890
+ field,
11891
+ id: idPrefix + '-key',
11892
+ idPrefix,
11893
+ index,
11894
+ validateFactory
11895
+ }];
11896
+ return entries;
11897
+ }
11898
+ function Label(props) {
11899
+ const {
11900
+ editField,
11901
+ field,
11902
+ id,
11903
+ index
11904
+ } = props;
11905
+ const debounce = useService('debounce');
11906
+
11907
+ /**
11908
+ * @param {string|void} value
11909
+ * @param {string|void} error
11910
+ * @returns {void}
11911
+ */
11912
+ const setValue = (value, error) => {
11913
+ if (error) {
11914
+ return;
11915
+ }
11916
+ const columns = get(field, [path$1]);
11917
+ editField(field, path$1, set$1(columns, [index, labelPath], value));
11918
+ };
11919
+ const getValue = () => {
11920
+ return get(field, [path$1, index, labelPath]);
11921
+ };
11922
+ return TextfieldEntry({
11923
+ debounce,
11924
+ element: field,
11925
+ getValue,
11926
+ id,
11927
+ label: 'Label',
11928
+ setValue
11929
+ });
11930
+ }
11931
+ function Key(props) {
11932
+ const {
11933
+ editField,
11934
+ field,
11935
+ id,
11936
+ index
11937
+ } = props;
11938
+ const debounce = useService('debounce');
11939
+
11940
+ /**
11941
+ * @param {string|void} value
11942
+ * @param {string|void} error
11943
+ * @returns {void}
11944
+ */
11945
+ const setValue = (value, error) => {
11946
+ if (error) {
11947
+ return;
11948
+ }
11949
+ const columns = get(field, [path$1]);
11950
+ editField(field, path$1, set$1(columns, [index, keyPath], value));
11951
+ };
11952
+ const getValue = () => {
11953
+ return get(field, [path$1, index, keyPath]);
11954
+ };
11955
+ return TextfieldEntry({
11956
+ debounce,
11957
+ element: field,
11958
+ getValue,
11959
+ id,
11960
+ label: 'Key',
11961
+ setValue,
11962
+ validate
11963
+ });
11964
+ }
11965
+
11966
+ // helpers //////////////////////
11967
+
11968
+ /**
11969
+ * @param {string|void} value
11970
+ * @returns {string|null}
11971
+ */
11972
+ function validate(value) {
11973
+ if (!isString(value) || value.length === 0) {
11974
+ return 'Must not be empty.';
11975
+ }
11976
+ return null;
11977
+ }
11978
+
11979
+ const path = ['columns'];
11980
+ function StaticColumnsSourceEntry(props) {
11981
+ const {
11982
+ editField,
11983
+ field,
11984
+ id: idPrefix
11985
+ } = props;
11986
+ const {
11987
+ columns
11988
+ } = field;
11989
+ const addEntry = event => {
11990
+ event.stopPropagation();
11991
+ const entry = {
11992
+ label: 'Column',
11993
+ key: 'inputVariable'
11994
+ };
11995
+ editField(field, path, arrayAdd(columns, columns.length, entry));
11996
+ };
11997
+ const removeEntry = entry => {
11998
+ editField(field, path, without(columns, entry));
11999
+ };
12000
+ const items = columns.map((entry, index) => {
12001
+ const id = `${idPrefix}-${index}`;
12002
+ return {
12003
+ id,
12004
+ label: entry.label || entry.key,
12005
+ entries: ColumnEntry({
12006
+ editField,
12007
+ field,
12008
+ idPrefix: id,
12009
+ index
12010
+ }),
12011
+ autoFocusEntry: `${id}-label`,
12012
+ remove: () => removeEntry(entry)
12013
+ };
12014
+ });
12015
+ return {
12016
+ items,
12017
+ add: addEntry,
12018
+ shouldSort: false
12019
+ };
12020
+ }
12021
+
12022
+ function GeneralGroup(field, editField, getService) {
12023
+ const entries = [...IdEntry({
12024
+ field,
12025
+ editField
12026
+ }), ...LabelEntry({
12027
+ field,
12028
+ editField
12029
+ }), ...DescriptionEntry({
12030
+ field,
12031
+ editField
12032
+ }), ...KeyEntry({
12033
+ field,
12034
+ editField,
12035
+ getService
12036
+ }), ...PathEntry({
12037
+ field,
12038
+ editField,
12039
+ getService
12040
+ }), ...RepeatableEntry({
12041
+ field,
12042
+ editField,
12043
+ getService
12044
+ }), ...DefaultOptionEntry({
12045
+ field,
12046
+ editField
12047
+ }), ...ActionEntry({
12048
+ field,
12049
+ editField
12050
+ }), ...DateTimeEntry({
12051
+ field,
12052
+ editField
12053
+ }), ...TextEntry({
12054
+ field,
11100
12055
  editField,
11101
12056
  getService
11102
12057
  }), ...IFrameUrlEntry({
@@ -11126,6 +12081,15 @@ function GeneralGroup(field, editField, getService) {
11126
12081
  }), ...ReadonlyEntry({
11127
12082
  field,
11128
12083
  editField
12084
+ }), ...TableDataSourceEntry({
12085
+ field,
12086
+ editField
12087
+ }), ...PaginationEntry({
12088
+ field,
12089
+ editField
12090
+ }), ...RowCountEntry({
12091
+ field,
12092
+ editField
11129
12093
  })];
11130
12094
  if (entries.length === 0) {
11131
12095
  return null;
@@ -11416,67 +12380,66 @@ function ValidationType(props) {
11416
12380
  });
11417
12381
  }
11418
12382
 
11419
- function ValuesGroups(field, editField, getService) {
12383
+ function OptionsGroups(field, editField, getService) {
11420
12384
  const {
11421
- type,
11422
- id: fieldId
12385
+ type
11423
12386
  } = field;
11424
12387
  const formFields = getService('formFields');
11425
12388
  const fieldDefinition = formFields.get(type).config;
11426
- if (!VALUES_INPUTS.includes(type) && !hasValuesGroupsConfigured(fieldDefinition)) {
12389
+ if (!OPTIONS_INPUTS.includes(type) && !hasOptionsGroupsConfigured(fieldDefinition)) {
11427
12390
  return [];
11428
12391
  }
11429
12392
  const context = {
11430
12393
  editField,
11431
12394
  field
11432
12395
  };
11433
- const valuesSourceId = `${fieldId}-valuesSource`;
12396
+ const id = 'valuesSource';
11434
12397
 
11435
- /**
11436
- * @type {Array<Group|ListGroup>}
12398
+ /**
12399
+ * @type {Array<Group|ListGroup>}
11437
12400
  */
11438
12401
  const groups = [{
11439
- id: valuesSourceId,
12402
+ id,
11440
12403
  label: 'Options source',
11441
12404
  tooltip: getValuesTooltip(),
11442
12405
  component: Group,
11443
- entries: ValuesSourceSelectEntry({
12406
+ entries: OptionsSourceSelectEntry({
11444
12407
  ...context,
11445
- id: valuesSourceId
12408
+ id
11446
12409
  })
11447
12410
  }];
11448
- const valuesSource = getValuesSource(field);
11449
- if (valuesSource === VALUES_SOURCES.INPUT) {
11450
- const dynamicValuesId = `${fieldId}-dynamicValues`;
12411
+ const valuesSource = getOptionsSource(field);
12412
+ if (valuesSource === OPTIONS_SOURCES.INPUT) {
12413
+ const id = 'dynamicOptions';
11451
12414
  groups.push({
11452
- id: dynamicValuesId,
12415
+ id,
11453
12416
  label: 'Dynamic options',
11454
12417
  component: Group,
11455
- entries: InputKeyValuesSourceEntry({
12418
+ entries: InputKeyOptionsSourceEntry({
11456
12419
  ...context,
11457
- id: dynamicValuesId
12420
+ id
11458
12421
  })
11459
12422
  });
11460
- } else if (valuesSource === VALUES_SOURCES.STATIC) {
11461
- const staticValuesId = `${fieldId}-staticValues`;
12423
+ } else if (valuesSource === OPTIONS_SOURCES.STATIC) {
12424
+ const id = 'staticOptions';
11462
12425
  groups.push({
11463
- id: staticValuesId,
12426
+ id,
11464
12427
  label: 'Static options',
11465
12428
  component: ListGroup,
11466
- ...StaticValuesSourceEntry({
12429
+ ...StaticOptionsSourceEntry({
11467
12430
  ...context,
11468
- id: staticValuesId
12431
+ id
11469
12432
  })
11470
12433
  });
11471
- } else if (valuesSource === VALUES_SOURCES.EXPRESSION) {
11472
- const valuesExpressionId = `${fieldId}-valuesExpression`;
12434
+ } else if (valuesSource === OPTIONS_SOURCES.EXPRESSION) {
12435
+ const id = 'optionsExpression';
11473
12436
  groups.push({
11474
- id: valuesExpressionId,
12437
+ id,
11475
12438
  label: 'Options expression',
11476
12439
  component: Group,
11477
- entries: ValuesExpressionEntry({
12440
+ entries: OptionsExpressionEntry({
11478
12441
  ...context,
11479
- id: valuesExpressionId
12442
+ id
11480
12443
  })
11481
12444
  });
11482
12445
  }
@@ -11526,7 +12489,7 @@ function CustomPropertiesGroup(field, editField) {
11526
12489
  event.stopPropagation();
11527
12490
  return editField(field, ['properties'], removeKey(properties, key));
11528
12491
  };
11529
- const id = `${field.id}-property-${index}`;
12492
+ const id = `property-${index}`;
11530
12493
  return {
11531
12494
  autoFocusEntry: id + '-key',
11532
12495
  entries: CustomValueEntry({
@@ -11554,13 +12517,13 @@ function CustomPropertiesGroup(field, editField) {
11554
12517
 
11555
12518
  // helpers //////////
11556
12519
 
11557
- /**
11558
- * Returns copy of object without key.
11559
- *
11560
- * @param {Object} properties
11561
- * @param {string} oldKey
11562
- *
11563
- * @returns {Object}
12520
+ /**
12521
+ * Returns copy of object without key.
12522
+ *
12523
+ * @param {Object} properties
12524
+ * @param {string} oldKey
12525
+ *
12526
+ * @returns {Object}
11564
12527
  */
11565
12528
  function removeKey(properties, oldKey) {
11566
12529
  return Object.entries(properties).reduce((newProperties, entry) => {
@@ -11575,10 +12538,16 @@ function removeKey(properties, oldKey) {
11575
12538
  }, {});
11576
12539
  }
11577
12540
 
11578
- function AppearanceGroup(field, editField) {
12541
+ function AppearanceGroup(field, editField, getService) {
11579
12542
  const entries = [...AdornerEntry({
11580
12543
  field,
11581
12544
  editField
12545
+ }), ...GroupAppearanceEntry({
12546
+ field,
12547
+ editField
12548
+ }), ...LayouterAppearanceEntry({
12549
+ field,
12550
+ editField
11582
12551
  })];
11583
12552
  if (!entries.length) {
11584
12553
  return null;
@@ -11629,6 +12598,55 @@ function ConditionGroup(field, editField) {
11629
12598
  };
11630
12599
  }
11631
12600
 
12601
+ function TableHeaderGroups(field, editField) {
12602
+ const {
12603
+ type,
12604
+ id: fieldId
12605
+ } = field;
12606
+ if (type !== 'table') {
12607
+ return [];
12608
+ }
12609
+ const areStaticColumnsEnabled = isArray(get(field, ['columns']));
12610
+
12611
+ /**
12612
+ * @type {Array<Group>}
12613
+ */
12614
+ const groups = [{
12615
+ id: `${fieldId}-columnsSource`,
12616
+ label: 'Headers source',
12617
+ tooltip: TOOLTIP_TEXT,
12618
+ component: Group,
12619
+ entries: [...HeadersSourceSelectEntry({
12620
+ field,
12621
+ editField
12622
+ }), ...ColumnsExpressionEntry({
12623
+ field,
12624
+ editField
12625
+ })]
12626
+ }];
12627
+ if (areStaticColumnsEnabled) {
12628
+ const id = `${fieldId}-columns`;
12629
+ groups.push({
12630
+ id,
12631
+ label: 'Header items',
12632
+ component: ListGroup,
12633
+ ...StaticColumnsSourceEntry({
12634
+ field,
12635
+ editField,
12636
+ id
12637
+ })
12638
+ });
12639
+ }
12640
+ return groups;
12641
+ }
12642
+
12643
+ // helpers //////////
12644
+
12645
+ const TOOLTIP_TEXT = `"List of items" defines a constant, predefined set of form options.
12646
+
12647
+ "Expression" defines options that are populated from a FEEL expression.
12648
+ `;
12649
+
11632
12650
  class PropertiesProvider {
11633
12651
  constructor(propertiesPanel, injector) {
11634
12652
  this._injector = injector;
@@ -11664,7 +12682,7 @@ class PropertiesProvider {
11664
12682
  return groups;
11665
12683
  }
11666
12684
  const getService = (type, strict = true) => this._injector.get(type, strict);
11667
- groups = [...groups, GeneralGroup(field, editField, getService), ConditionGroup(field, editField), LayoutGroup(field, editField), AppearanceGroup(field, editField), SerializationGroup(field, editField), ...ValuesGroups(field, editField, getService), ConstraintsGroup(field, editField), ValidationGroup(field, editField), CustomPropertiesGroup(field, editField)].filter(group => group != null);
12685
+ groups = [...groups, GeneralGroup(field, editField, getService), ...TableHeaderGroups(field, editField), ConditionGroup(field, editField), LayoutGroup(field, editField), AppearanceGroup(field, editField), SerializationGroup(field, editField), ...OptionsGroups(field, editField, getService), ConstraintsGroup(field, editField), ValidationGroup(field, editField), CustomPropertiesGroup(field, editField)].filter(group => group != null);
11668
12686
  this._filterVisibleEntries(groups, field, getService);
11669
12687
 
11670
12688
  // contract: if a group has no entries or items, it should not be displayed at all
@@ -11683,10 +12701,10 @@ var PropertiesPanelModule = {
11683
12701
  propertiesProvider: ['type', PropertiesProvider]
11684
12702
  };
11685
12703
 
11686
- /**
11687
- * Manages the rendering of visual plugins.
11688
- * @constructor
11689
- * @param {Object} eventBus - Event bus for the application.
12704
+ /**
12705
+ * Manages the rendering of visual plugins.
12706
+ * @constructor
12707
+ * @param {Object} eventBus - Event bus for the application.
11690
12708
  */
11691
12709
  class RenderInjector extends SectionModuleBase {
11692
12710
  constructor(eventBus) {
@@ -11695,10 +12713,10 @@ class RenderInjector extends SectionModuleBase {
11695
12713
  this.registeredRenderers = [];
11696
12714
  }
11697
12715
 
11698
- /**
11699
- * Inject a new renderer into the injector.
11700
- * @param {string} identifier - Identifier for the renderer.
11701
- * @param {Function} Renderer - The renderer function.
12716
+ /**
12717
+ * Inject a new renderer into the injector.
12718
+ * @param {string} identifier - Identifier for the renderer.
12719
+ * @param {Function} Renderer - The renderer function.
11702
12720
  */
11703
12721
  attachRenderer(identifier, Renderer) {
11704
12722
  this.registeredRenderers = [...this.registeredRenderers, {
@@ -11707,17 +12725,17 @@ class RenderInjector extends SectionModuleBase {
11707
12725
  }];
11708
12726
  }
11709
12727
 
11710
- /**
11711
- * Detach a renderer from the by key injector.
11712
- * @param {string} identifier - Identifier for the renderer.
12728
+ /**
12729
+ * Detach a renderer from the by key injector.
12730
+ * @param {string} identifier - Identifier for the renderer.
11713
12731
  */
11714
12732
  detachRenderer(identifier) {
11715
12733
  this.registeredRenderers = this.registeredRenderers.filter(r => r.identifier !== identifier);
11716
12734
  }
11717
12735
 
11718
- /**
11719
- * Returns the registered renderers.
11720
- * @returns {Array} Array of registered renderers.
12736
+ /**
12737
+ * Returns the registered renderers.
12738
+ * @returns {Array} Array of registered renderers.
11721
12739
  */
11722
12740
  fetchRenderers() {
11723
12741
  return this.registeredRenderers;
@@ -11730,6 +12748,58 @@ var RenderInjectionModule = {
11730
12748
  renderInjector: ['type', RenderInjector]
11731
12749
  };
11732
12750
 
12751
+ var _path;
12752
+ function _extends() { _extends = 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.apply(this, arguments); }
12753
+ var SvgRepeat = function SvgRepeat(props) {
12754
+ return /*#__PURE__*/React.createElement("svg", _extends({
12755
+ xmlns: "http://www.w3.org/2000/svg",
12756
+ width: 16,
12757
+ height: 16,
12758
+ fill: "none"
12759
+ }, props), _path || (_path = /*#__PURE__*/React.createElement("path", {
12760
+ fill: "currentColor",
12761
+ d: "M3 3h10.086l-1.793-1.793L12 .5l3 3-3 3-.707-.707L13.086 4H3v3.5H2V4a1.001 1.001 0 0 1 1-1ZM4.707 10.207 2.914 12H13V8.5h1V12a1.002 1.002 0 0 1-1 1H2.914l1.793 1.793L4 15.5l-3-3 3-3 .707.707Z"
12762
+ })));
12763
+ };
12764
+ var RepeatSvg = SvgRepeat;
12765
+
12766
+ class RepeatRenderManager {
12767
+ constructor(formFields, formFieldRegistry) {
12768
+ this._formFields = formFields;
12769
+ this._formFieldRegistry = formFieldRegistry;
12770
+ this.RepeatFooter = this.RepeatFooter.bind(this);
12771
+ }
12772
+
12773
+ /**
12774
+ * Checks whether a field should be repeatable.
12775
+ *
12776
+ * @param {string} id - The id of the field to check
12777
+ * @returns {boolean} - True if repeatable, false otherwise
12778
+ */
12779
+ isFieldRepeating(id) {
12780
+ if (!id) {
12781
+ return false;
12782
+ }
12783
+ const formField = this._formFieldRegistry.get(id);
12784
+ const formFieldDefinition = this._formFields.get(formField.type);
12785
+ return formFieldDefinition.config.repeatable && formField.isRepeating;
12786
+ }
12787
+ RepeatFooter() {
12788
+ return jsxs("div", {
12789
+ className: "fjs-repeat-render-footer",
12790
+ children: [jsx(RepeatSvg, {}), jsx("span", {
12791
+ children: "Repeatable"
12792
+ })]
12793
+ });
12794
+ }
12795
+ }
12796
+ RepeatRenderManager.$inject = ['formFields', 'formFieldRegistry'];
12797
+
12798
+ var RepeatRenderManagerModule = {
12799
+ __init__: ['repeatRenderManager'],
12800
+ repeatRenderManager: ['type', RepeatRenderManager]
12801
+ };
12802
+
11733
12803
  class EditorTemplating {
11734
12804
  // same rules as viewer templating
11735
12805
  isTemplate(value) {
@@ -11751,48 +12821,48 @@ var ExpressionLanguageModule = {
11751
12821
 
11752
12822
  const ids = new Ids([32, 36, 1]);
11753
12823
 
11754
- /**
11755
- * @typedef { import('./types').Injector } Injector
11756
- * @typedef { import('./types').Module } Module
11757
- * @typedef { import('./types').Schema } Schema
11758
- *
11759
- * @typedef { import('./types').FormEditorOptions } FormEditorOptions
11760
- * @typedef { import('./types').FormEditorProperties } FormEditorProperties
11761
- *
11762
- * @typedef { {
11763
- * properties: FormEditorProperties,
11764
- * schema: Schema
11765
- * } } State
11766
- *
11767
- * @typedef { (type:string, priority:number, handler:Function) => void } OnEventWithPriority
11768
- * @typedef { (type:string, handler:Function) => void } OnEventWithOutPriority
11769
- * @typedef { OnEventWithPriority & OnEventWithOutPriority } OnEventType
12824
+ /**
12825
+ * @typedef { import('./types').Injector } Injector
12826
+ * @typedef { import('./types').Module } Module
12827
+ * @typedef { import('./types').Schema } Schema
12828
+ *
12829
+ * @typedef { import('./types').FormEditorOptions } FormEditorOptions
12830
+ * @typedef { import('./types').FormEditorProperties } FormEditorProperties
12831
+ *
12832
+ * @typedef { {
12833
+ * properties: FormEditorProperties,
12834
+ * schema: Schema
12835
+ * } } State
12836
+ *
12837
+ * @typedef { (type:string, priority:number, handler:Function) => void } OnEventWithPriority
12838
+ * @typedef { (type:string, handler:Function) => void } OnEventWithOutPriority
12839
+ * @typedef { OnEventWithPriority & OnEventWithOutPriority } OnEventType
11770
12840
  */
11771
12841
 
11772
- /**
11773
- * The form editor.
12842
+ /**
12843
+ * The form editor.
11774
12844
  */
11775
12845
  class FormEditor {
11776
- /**
11777
- * @constructor
11778
- * @param {FormEditorOptions} options
12846
+ /**
12847
+ * @constructor
12848
+ * @param {FormEditorOptions} options
11779
12849
  */
11780
12850
  constructor(options = {}) {
11781
- /**
11782
- * @public
11783
- * @type {OnEventType}
12851
+ /**
12852
+ * @public
12853
+ * @type {OnEventType}
11784
12854
  */
11785
12855
  this.on = this._onEvent;
11786
12856
 
11787
- /**
11788
- * @public
11789
- * @type {String}
12857
+ /**
12858
+ * @public
12859
+ * @type {String}
11790
12860
  */
11791
12861
  this._id = ids.next();
11792
12862
 
11793
- /**
11794
- * @private
11795
- * @type {Element}
12863
+ /**
12864
+ * @private
12865
+ * @type {Element}
11796
12866
  */
11797
12867
  this._container = createFormContainer();
11798
12868
  this._container.setAttribute('input-handle-modified-keys', 'z,y');
@@ -11803,15 +12873,15 @@ class FormEditor {
11803
12873
  properties = {}
11804
12874
  } = options;
11805
12875
 
11806
- /**
11807
- * @private
11808
- * @type {any}
12876
+ /**
12877
+ * @private
12878
+ * @type {any}
11809
12879
  */
11810
12880
  this.exporter = exporter;
11811
12881
 
11812
- /**
11813
- * @private
11814
- * @type {State}
12882
+ /**
12883
+ * @private
12884
+ * @type {State}
11815
12885
  */
11816
12886
  this._state = {
11817
12887
  properties,
@@ -11840,10 +12910,10 @@ class FormEditor {
11840
12910
  this._detach(false);
11841
12911
  }
11842
12912
 
11843
- /**
11844
- * @param {Schema} schema
11845
- *
11846
- * @return {Promise<{ warnings: Array<any> }>}
12913
+ /**
12914
+ * @param {Schema} schema
12915
+ *
12916
+ * @return {Promise<{ warnings: Array<any> }>}
11847
12917
  */
11848
12918
  importSchema(schema) {
11849
12919
  return new Promise((resolve, reject) => {
@@ -11872,15 +12942,15 @@ class FormEditor {
11872
12942
  });
11873
12943
  }
11874
12944
 
11875
- /**
11876
- * @returns {Schema}
12945
+ /**
12946
+ * @returns {Schema}
11877
12947
  */
11878
12948
  saveSchema() {
11879
12949
  return this.getSchema();
11880
12950
  }
11881
12951
 
11882
- /**
11883
- * @returns {Schema}
12952
+ /**
12953
+ * @returns {Schema}
11884
12954
  */
11885
12955
  getSchema() {
11886
12956
  const {
@@ -11889,8 +12959,8 @@ class FormEditor {
11889
12959
  return exportSchema(schema, this.exporter, schemaVersion);
11890
12960
  }
11891
12961
 
11892
- /**
11893
- * @param {Element|string} parentNode
12962
+ /**
12963
+ * @param {Element|string} parentNode
11894
12964
  */
11895
12965
  attachTo(parentNode) {
11896
12966
  if (!parentNode) {
@@ -11908,10 +12978,10 @@ class FormEditor {
11908
12978
  this._detach();
11909
12979
  }
11910
12980
 
11911
- /**
11912
- * @internal
11913
- *
11914
- * @param {boolean} [emit]
12981
+ /**
12982
+ * @internal
12983
+ *
12984
+ * @param {boolean} [emit]
11915
12985
  */
11916
12986
  _detach(emit = true) {
11917
12987
  const container = this._container,
@@ -11925,9 +12995,9 @@ class FormEditor {
11925
12995
  parentNode.removeChild(container);
11926
12996
  }
11927
12997
 
11928
- /**
11929
- * @param {any} property
11930
- * @param {any} value
12998
+ /**
12999
+ * @param {any} property
13000
+ * @param {any} value
11931
13001
  */
11932
13002
  setProperty(property, value) {
11933
13003
  const properties = set$1(this._getState().properties, [property], value);
@@ -11936,21 +13006,21 @@ class FormEditor {
11936
13006
  });
11937
13007
  }
11938
13008
 
11939
- /**
11940
- * @param {string} type
11941
- * @param {Function} handler
13009
+ /**
13010
+ * @param {string} type
13011
+ * @param {Function} handler
11942
13012
  */
11943
13013
  off(type, handler) {
11944
13014
  this.get('eventBus').off(type, handler);
11945
13015
  }
11946
13016
 
11947
- /**
11948
- * @internal
11949
- *
11950
- * @param {FormEditorOptions} options
11951
- * @param {Element} container
11952
- *
11953
- * @returns {Injector}
13017
+ /**
13018
+ * @internal
13019
+ *
13020
+ * @param {FormEditorOptions} options
13021
+ * @param {Element} container
13022
+ *
13023
+ * @returns {Injector}
11954
13024
  */
11955
13025
  _createInjector(options, container) {
11956
13026
  const {
@@ -11972,22 +13042,22 @@ class FormEditor {
11972
13042
  }, core, ...modules, ...additionalModules]);
11973
13043
  }
11974
13044
 
11975
- /**
11976
- * @internal
13045
+ /**
13046
+ * @internal
11977
13047
  */
11978
13048
  _emit(type, data) {
11979
13049
  this.get('eventBus').fire(type, data);
11980
13050
  }
11981
13051
 
11982
- /**
11983
- * @internal
13052
+ /**
13053
+ * @internal
11984
13054
  */
11985
13055
  _getState() {
11986
13056
  return this._state;
11987
13057
  }
11988
13058
 
11989
- /**
11990
- * @internal
13059
+ /**
13060
+ * @internal
11991
13061
  */
11992
13062
  _setState(state) {
11993
13063
  this._state = {
@@ -11997,15 +13067,15 @@ class FormEditor {
11997
13067
  this._emit('changed', this._getState());
11998
13068
  }
11999
13069
 
12000
- /**
12001
- * @internal
13070
+ /**
13071
+ * @internal
12002
13072
  */
12003
13073
  _getModules() {
12004
- return [ModelingModule, EditorActionsModule, DraggingModule, KeyboardModule, SelectionModule, PaletteModule, ExpressionLanguageModule, MarkdownModule, PropertiesPanelModule, RenderInjectionModule];
13074
+ return [ModelingModule, EditorActionsModule, DraggingModule, KeyboardModule, SelectionModule, PaletteModule, ExpressionLanguageModule, MarkdownModule, PropertiesPanelModule, RenderInjectionModule, RepeatRenderManagerModule];
12005
13075
  }
12006
13076
 
12007
- /**
12008
- * @internal
13077
+ /**
13078
+ * @internal
12009
13079
  */
12010
13080
  _onEvent(type, priority, handler) {
12011
13081
  this.get('eventBus').on(type, priority, handler);