@bpmn-io/form-js-editor 1.5.0-alpha.0 → 1.6.0

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 (91) hide show
  1. package/dist/assets/form-js-editor-base.css +44 -13
  2. package/dist/assets/form-js-editor.css +44 -13
  3. package/dist/index.cjs +1550 -334
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.es.js +1552 -336
  6. package/dist/index.es.js.map +1 -1
  7. package/dist/types/FormEditor.d.ts +16 -4
  8. package/dist/types/core/Debounce.d.ts +1 -1
  9. package/dist/types/core/FormLayoutValidator.d.ts +2 -2
  10. package/dist/types/core/index.d.ts +9 -9
  11. package/dist/types/features/dragging/Dragging.d.ts +3 -3
  12. package/dist/types/features/dragging/index.d.ts +2 -2
  13. package/dist/types/features/editor-actions/FormEditorActions.d.ts +1 -1
  14. package/dist/types/features/editor-actions/index.d.ts +2 -2
  15. package/dist/types/features/expression-language/EditorTemplating.d.ts +1 -1
  16. package/dist/types/features/expression-language/index.d.ts +3 -3
  17. package/dist/types/features/keyboard/FormEditorKeyboardBindings.d.ts +1 -1
  18. package/dist/types/features/keyboard/index.d.ts +3 -3
  19. package/dist/types/features/modeling/FormLayoutUpdater.d.ts +1 -1
  20. package/dist/types/features/modeling/Modeling.d.ts +1 -1
  21. package/dist/types/features/modeling/behavior/ColumnsSourceBehavior.d.ts +7 -0
  22. package/dist/types/features/modeling/behavior/IdBehavior.d.ts +1 -1
  23. package/dist/types/features/modeling/behavior/KeyBehavior.d.ts +1 -1
  24. package/dist/types/features/modeling/behavior/OptionsSourceBehavior.d.ts +8 -0
  25. package/dist/types/features/modeling/behavior/PathBehavior.d.ts +1 -1
  26. package/dist/types/features/modeling/behavior/TableDataSourceBehavior.d.ts +7 -0
  27. package/dist/types/features/modeling/behavior/ValidateBehavior.d.ts +1 -1
  28. package/dist/types/features/modeling/behavior/index.d.ts +11 -7
  29. package/dist/types/features/modeling/cmd/AddFormFieldHandler.d.ts +1 -1
  30. package/dist/types/features/modeling/cmd/EditFormFieldHandler.d.ts +1 -1
  31. package/dist/types/features/modeling/cmd/MoveFormFieldHandler.d.ts +1 -1
  32. package/dist/types/features/modeling/cmd/RemoveFormFieldHandler.d.ts +1 -1
  33. package/dist/types/features/modeling/cmd/UpdateIdClaimHandler.d.ts +1 -1
  34. package/dist/types/features/modeling/cmd/UpdateKeyClaimHandler.d.ts +1 -1
  35. package/dist/types/features/modeling/cmd/UpdatePathClaimHandler.d.ts +1 -1
  36. package/dist/types/features/modeling/index.d.ts +7 -5
  37. package/dist/types/features/palette/PaletteModule.d.ts +1 -1
  38. package/dist/types/features/palette/index.d.ts +1 -1
  39. package/dist/types/features/properties-panel/PropertiesPanelRenderer.d.ts +1 -1
  40. package/dist/types/features/properties-panel/PropertiesProvider.d.ts +1 -1
  41. package/dist/types/features/properties-panel/Util.d.ts +11 -3
  42. package/dist/types/features/properties-panel/entries/ColumnEntry.d.ts +11 -0
  43. package/dist/types/features/properties-panel/entries/ColumnsExpressionEntry.d.ts +10 -0
  44. package/dist/types/features/properties-panel/entries/ConditionEntry.d.ts +1 -1
  45. package/dist/types/features/properties-panel/entries/{GroupEntries.d.ts → GroupAppearanceEntry.d.ts} +3 -1
  46. package/dist/types/features/properties-panel/entries/HeadersSourceSelectEntry.d.ts +9 -0
  47. package/dist/types/features/properties-panel/entries/HeightEntry.d.ts +12 -0
  48. package/dist/types/features/properties-panel/entries/IFrameHeightEntry.d.ts +10 -0
  49. package/dist/types/features/properties-panel/entries/IFrameUrlEntry.d.ts +10 -0
  50. package/dist/types/features/properties-panel/entries/{InputKeyValuesSourceEntry.d.ts → InputKeyOptionsSourceEntry.d.ts} +1 -1
  51. package/dist/types/features/properties-panel/entries/LayouterAppearanceEntry.d.ts +10 -0
  52. package/dist/types/features/properties-panel/entries/OptionsExpressionEntry.d.ts +9 -0
  53. package/dist/types/features/properties-panel/entries/{ValuesSourceSelectEntry.d.ts → OptionsSourceSelectEntry.d.ts} +1 -1
  54. package/dist/types/features/properties-panel/entries/PaginationEntry.d.ts +10 -0
  55. package/dist/types/features/properties-panel/entries/RepeatableEntry.d.ts +24 -0
  56. package/dist/types/features/properties-panel/entries/RowCountEntry.d.ts +10 -0
  57. package/dist/types/features/properties-panel/entries/SelectEntries.d.ts +2 -0
  58. package/dist/types/features/properties-panel/entries/StaticColumnsSourceEntry.d.ts +5 -0
  59. package/dist/types/features/properties-panel/entries/{StaticValuesSourceEntry.d.ts → StaticOptionsSourceEntry.d.ts} +1 -1
  60. package/dist/types/features/properties-panel/entries/TableDataSourceEntry.d.ts +10 -0
  61. package/dist/types/features/properties-panel/entries/factories/index.d.ts +3 -0
  62. package/dist/types/features/properties-panel/entries/factories/simpleBoolEntryFactory.d.ts +2 -0
  63. package/dist/types/features/properties-panel/entries/factories/simpleRangeIntegerEntryFactory.d.ts +12 -0
  64. package/dist/types/features/properties-panel/entries/factories/simpleSelectEntryFactory.d.ts +10 -0
  65. package/dist/types/features/properties-panel/entries/factories/zeroPositiveIntegerEntryFactory.d.ts +9 -0
  66. package/dist/types/features/properties-panel/entries/index.d.ts +17 -7
  67. package/dist/types/features/properties-panel/groups/AppearanceGroup.d.ts +24 -3
  68. package/dist/types/features/properties-panel/groups/GeneralGroup.d.ts +13 -0
  69. package/dist/types/features/properties-panel/groups/OptionsGroups.d.ts +1 -0
  70. package/dist/types/features/properties-panel/groups/TableHeaderGroups.d.ts +1 -0
  71. package/dist/types/features/properties-panel/groups/index.d.ts +2 -1
  72. package/dist/types/features/properties-panel/index.d.ts +4 -4
  73. package/dist/types/features/render-injection/RenderInjector.d.ts +1 -1
  74. package/dist/types/features/render-injection/index.d.ts +2 -2
  75. package/dist/types/features/repeat-render/EditorRepeatRenderManager.d.ts +17 -0
  76. package/dist/types/features/repeat-render/index.d.ts +7 -0
  77. package/dist/types/features/selection/Selection.d.ts +1 -1
  78. package/dist/types/features/selection/SelectionBehavior.d.ts +1 -1
  79. package/dist/types/features/selection/index.d.ts +3 -3
  80. package/dist/types/render/Renderer.d.ts +1 -1
  81. package/dist/types/render/components/ModularSection.d.ts +1 -1
  82. package/dist/types/render/components/editor-form-fields/EditorIFrame.d.ts +6 -0
  83. package/dist/types/render/components/editor-form-fields/EditorTable.d.ts +10 -0
  84. package/dist/types/render/components/editor-form-fields/EditorText.d.ts +1 -1
  85. package/dist/types/render/components/editor-form-fields/index.d.ts +3 -1
  86. package/dist/types/render/index.d.ts +3 -3
  87. package/package.json +3 -3
  88. package/dist/types/features/modeling/behavior/ValuesSourceBehavior.d.ts +0 -8
  89. package/dist/types/features/properties-panel/entries/SpacerEntry.d.ts +0 -10
  90. package/dist/types/features/properties-panel/entries/ValuesExpressionEntry.d.ts +0 -9
  91. package/dist/types/features/properties-panel/groups/ValuesGroups.d.ts +0 -1
package/dist/index.es.js CHANGED
@@ -1,7 +1,7 @@
1
- import { FormFieldRegistry as FormFieldRegistry$1, iconsByType, 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';
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';
5
5
  import classnames from 'classnames';
6
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';
@@ -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),
@@ -745,6 +745,28 @@ function createEmptyImage() {
745
745
  return img;
746
746
  }
747
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
+
748
770
  const DragAndDropContext = createContext({
749
771
  drake: null
750
772
  });
@@ -790,15 +812,15 @@ function useDebounce(fn, dependencies = []) {
790
812
  return callback;
791
813
  }
792
814
 
793
- var _path$4;
794
- 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); }
795
817
  var SvgClose = function SvgClose(props) {
796
- return /*#__PURE__*/React.createElement("svg", _extends$4({
818
+ return /*#__PURE__*/React.createElement("svg", _extends$5({
797
819
  xmlns: "http://www.w3.org/2000/svg",
798
820
  width: 16,
799
821
  height: 16,
800
822
  fill: "currentColor"
801
- }, props), _path$4 || (_path$4 = /*#__PURE__*/React.createElement("path", {
823
+ }, props), _path$5 || (_path$5 = /*#__PURE__*/React.createElement("path", {
802
824
  fillRule: "evenodd",
803
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",
804
826
  clipRule: "evenodd"
@@ -806,10 +828,10 @@ var SvgClose = function SvgClose(props) {
806
828
  };
807
829
  var CloseIcon = SvgClose;
808
830
 
809
- var _path$3, _path2$1;
810
- 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); }
811
833
  var SvgDelete = function SvgDelete(props) {
812
- return /*#__PURE__*/React.createElement("svg", _extends$3({
834
+ return /*#__PURE__*/React.createElement("svg", _extends$4({
813
835
  xmlns: "http://www.w3.org/2000/svg",
814
836
  width: 16,
815
837
  height: 16,
@@ -830,7 +852,7 @@ var SvgDelete = function SvgDelete(props) {
830
852
  mixBlendMode: "multiply"
831
853
  },
832
854
  transform: "translate(.536)"
833
- }), _path$3 || (_path$3 = /*#__PURE__*/React.createElement("path", {
855
+ }), _path$4 || (_path$4 = /*#__PURE__*/React.createElement("path", {
834
856
  fill: "currentcolor",
835
857
  d: "M7.536 6h-1v6h1V6Zm3 0h-1v6h1V6Z"
836
858
  })), _path2$1 || (_path2$1 = /*#__PURE__*/React.createElement("path", {
@@ -840,17 +862,17 @@ var SvgDelete = function SvgDelete(props) {
840
862
  };
841
863
  var DeleteIcon$1 = SvgDelete;
842
864
 
843
- var _path$2;
844
- 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); }
845
867
  var SvgDraggable = function SvgDraggable(props) {
846
- return /*#__PURE__*/React.createElement("svg", _extends$2({
868
+ return /*#__PURE__*/React.createElement("svg", _extends$3({
847
869
  xmlns: "http://www.w3.org/2000/svg",
848
870
  xmlSpace: "preserve",
849
871
  width: 16,
850
872
  height: 16,
851
873
  fill: "currentcolor",
852
874
  viewBox: "0 0 32 32"
853
- }, props), _path$2 || (_path$2 = /*#__PURE__*/React.createElement("path", {
875
+ }, props), _path$3 || (_path$3 = /*#__PURE__*/React.createElement("path", {
854
876
  d: "M10 6h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4z"
855
877
  })), /*#__PURE__*/React.createElement("path", {
856
878
  d: "M0 0h32v32H0z",
@@ -861,30 +883,30 @@ var SvgDraggable = function SvgDraggable(props) {
861
883
  };
862
884
  var DraggableIcon = SvgDraggable;
863
885
 
864
- var _path$1;
865
- 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); }
866
888
  var SvgSearch = function SvgSearch(props) {
867
- return /*#__PURE__*/React.createElement("svg", _extends$1({
889
+ return /*#__PURE__*/React.createElement("svg", _extends$2({
868
890
  xmlns: "http://www.w3.org/2000/svg",
869
891
  width: 15,
870
892
  height: 15,
871
893
  fill: "none"
872
- }, props), _path$1 || (_path$1 = /*#__PURE__*/React.createElement("path", {
894
+ }, props), _path$2 || (_path$2 = /*#__PURE__*/React.createElement("path", {
873
895
  fill: "currentColor",
874
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"
875
897
  })));
876
898
  };
877
899
  var SearchIcon = SvgSearch;
878
900
 
879
- var _path, _rect, _mask, _path2, _path3, _path4, _path5, _path6;
880
- 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); }
881
903
  var SvgEmptyForm = function SvgEmptyForm(props) {
882
- return /*#__PURE__*/React.createElement("svg", _extends({
904
+ return /*#__PURE__*/React.createElement("svg", _extends$1({
883
905
  xmlns: "http://www.w3.org/2000/svg",
884
906
  width: 126,
885
907
  height: 96,
886
908
  fill: "none"
887
- }, props), _path || (_path = /*#__PURE__*/React.createElement("path", {
909
+ }, props), _path$1 || (_path$1 = /*#__PURE__*/React.createElement("path", {
888
910
  fill: "#FF832B",
889
911
  fillRule: "evenodd",
890
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",
@@ -979,7 +1001,71 @@ function EditorText(props) {
979
1001
  }
980
1002
  EditorText.config = Text$1.config;
981
1003
 
982
- const editorFormFields = [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];
983
1069
 
984
1070
  class EditorFormFields extends FormFields {
985
1071
  constructor() {
@@ -1278,6 +1364,9 @@ const PALETTE_GROUPS = [{
1278
1364
  }, {
1279
1365
  label: 'Presentation',
1280
1366
  id: 'presentation'
1367
+ }, {
1368
+ label: 'Containers',
1369
+ id: 'container'
1281
1370
  }, {
1282
1371
  label: 'Action',
1283
1372
  id: 'action'
@@ -1516,7 +1605,7 @@ class Dragging {
1516
1605
  }
1517
1606
 
1518
1607
  /**
1519
- * Calculcates position in form schema given the dropped place.
1608
+ * Calculates position in form schema given the dropped place.
1520
1609
  *
1521
1610
  * @param { FormRow } targetRow
1522
1611
  * @param { any } targetFormField
@@ -1576,13 +1665,18 @@ class Dragging {
1576
1665
  if (targetParentPath.join('.') !== currentParentPath.join('.')) {
1577
1666
  const isDropAllowedByPathRegistry = this._pathRegistry.executeRecursivelyOnFields(formField, ({
1578
1667
  field,
1579
- isClosed
1668
+ isClosed,
1669
+ isRepeatable
1580
1670
  }) => {
1581
1671
  const options = {
1582
1672
  cutoffNode: currentParentFormField.id
1583
1673
  };
1584
1674
  const fieldPath = this._pathRegistry.getValuePath(field, options);
1585
- 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
+ });
1586
1680
  });
1587
1681
  if (!isDropAllowedByPathRegistry) {
1588
1682
  return 'Drop not allowed by path registry';
@@ -1948,24 +2042,40 @@ function ContextPad(props) {
1948
2042
  children: props.children
1949
2043
  });
1950
2044
  }
1951
- 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
+ }
1952
2077
  return null;
1953
2078
  }
1954
- function EmptyRoot(props) {
1955
- return jsx("div", {
1956
- class: "fjs-empty-editor",
1957
- children: jsxs("div", {
1958
- class: "fjs-empty-editor-card",
1959
- children: [jsx(EmptyFormIcon, {}), jsx("h2", {
1960
- children: "Build your form"
1961
- }), jsx("span", {
1962
- children: "Drag and drop components here to start designing."
1963
- }), jsx("span", {
1964
- children: "Use the preview window to test your form."
1965
- })]
1966
- })
1967
- });
1968
- }
1969
2079
  function Element$1(props) {
1970
2080
  const eventBus = useService$1('eventBus'),
1971
2081
  formEditor = useService$1('formEditor'),
@@ -1974,8 +2084,7 @@ function Element$1(props) {
1974
2084
  modeling = useService$1('modeling'),
1975
2085
  selection = useService$1('selection');
1976
2086
  const {
1977
- hoveredId,
1978
- setHoveredId
2087
+ hoverInfo
1979
2088
  } = useContext(FormRenderContext);
1980
2089
  const {
1981
2090
  field
@@ -1986,6 +2095,7 @@ function Element$1(props) {
1986
2095
  showOutline
1987
2096
  } = field;
1988
2097
  const ref = useRef();
2098
+ const [hovered, setHovered] = useState(false);
1989
2099
  function scrollIntoView({
1990
2100
  selection
1991
2101
  }) {
@@ -2014,19 +2124,23 @@ function Element$1(props) {
2014
2124
  // properly focus on field
2015
2125
  ref.current.focus();
2016
2126
  }
2017
- const classes = [];
2018
- if (props.class) {
2019
- classes.push(...props.class.split(' '));
2020
- }
2021
- if (selection.isSelected(field)) {
2022
- classes.push('fjs-editor-selected');
2023
- }
2024
- if (showOutline) {
2025
- classes.push('fjs-outlined');
2026
- }
2027
- if (hoveredId === field.id) {
2028
- classes.push('fjs-editor-hovered');
2029
- }
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]);
2030
2144
  const onRemove = event => {
2031
2145
  event.stopPropagation();
2032
2146
  const parentField = formFieldRegistry.get(field._parent);
@@ -2040,15 +2154,18 @@ function Element$1(props) {
2040
2154
  }
2041
2155
  };
2042
2156
  return jsxs("div", {
2043
- class: classes.join(' '),
2157
+ class: classString,
2044
2158
  "data-id": id,
2045
2159
  "data-field-type": type,
2046
2160
  tabIndex: type === 'default' ? -1 : 0,
2047
2161
  onClick: onClick,
2048
2162
  onKeyPress: onKeyPress,
2049
2163
  onMouseOver: e => {
2050
- // @ts-ignore
2051
- setHoveredId(field.id);
2164
+ if (hoverInfo.cleanup) {
2165
+ hoverInfo.cleanup();
2166
+ }
2167
+ setHovered(true);
2168
+ hoverInfo.cleanup = () => setHovered(false);
2052
2169
  e.stopPropagation();
2053
2170
  },
2054
2171
  ref: ref,
@@ -2119,6 +2236,7 @@ function Row(props) {
2119
2236
  children: jsx(DraggableIcon, {})
2120
2237
  }), jsx("div", {
2121
2238
  class: classes.join(' '),
2239
+ style: props.style,
2122
2240
  "data-row-id": id,
2123
2241
  children: props.children
2124
2242
  })]
@@ -2224,17 +2342,14 @@ function FormEditor$1(props) {
2224
2342
  // keep deprecated event to ensure backward compatibility
2225
2343
  eventBus.fire('formEditor.rendered');
2226
2344
  }, []);
2227
- const [hoveredId, setHoveredId] = useState(null);
2228
2345
  const formRenderContext = useMemo(() => ({
2229
2346
  Children,
2230
2347
  Column,
2231
2348
  Element: Element$1,
2232
2349
  Empty,
2233
- EmptyRoot,
2234
2350
  Row,
2235
- hoveredId,
2236
- setHoveredId
2237
- }), [hoveredId]);
2351
+ hoverInfo: {}
2352
+ }), []);
2238
2353
  const formContext = useMemo(() => ({
2239
2354
  getService(type, strict = true) {
2240
2355
  // TODO(philippfromme): clean up
@@ -2595,7 +2710,8 @@ EditorActions.prototype.trigger = function (action, opts) {
2595
2710
  * The key of the object will be the name of the action.
2596
2711
  *
2597
2712
  * @example
2598
- * ´´´
2713
+ *
2714
+ * ```javascript
2599
2715
  * var actions = {
2600
2716
  * spaceTool: function() {
2601
2717
  * spaceTool.activateSelection();
@@ -2608,7 +2724,7 @@ EditorActions.prototype.trigger = function (action, opts) {
2608
2724
  * editorActions.register(actions);
2609
2725
  *
2610
2726
  * editorActions.isRegistered('spaceTool'); // true
2611
- * ´´´
2727
+ * ```
2612
2728
  *
2613
2729
  * @param {Object} actions
2614
2730
  */
@@ -2747,6 +2863,7 @@ function hasModifier(event) {
2747
2863
 
2748
2864
  /**
2749
2865
  * @param {KeyboardEvent} event
2866
+ * @return {boolean}
2750
2867
  */
2751
2868
  function isCmd(event) {
2752
2869
  // ensure we don't react to AltGr
@@ -2762,6 +2879,7 @@ function isCmd(event) {
2762
2879
  *
2763
2880
  * @param {string|string[]} keys
2764
2881
  * @param {KeyboardEvent} event
2882
+ * @return {boolean}
2765
2883
  */
2766
2884
  function isKey(keys, event) {
2767
2885
  keys = isArray(keys) ? keys : [keys];
@@ -2774,21 +2892,39 @@ function isKey(keys, event) {
2774
2892
  function isShift(event) {
2775
2893
  return event.shiftKey;
2776
2894
  }
2895
+
2896
+ /**
2897
+ * @param {KeyboardEvent} event
2898
+ */
2777
2899
  function isCopy(event) {
2778
2900
  return isCmd(event) && isKey(KEYS_COPY, event);
2779
2901
  }
2902
+
2903
+ /**
2904
+ * @param {KeyboardEvent} event
2905
+ */
2780
2906
  function isPaste(event) {
2781
2907
  return isCmd(event) && isKey(KEYS_PASTE, event);
2782
2908
  }
2909
+
2910
+ /**
2911
+ * @param {KeyboardEvent} event
2912
+ */
2783
2913
  function isUndo(event) {
2784
2914
  return isCmd(event) && !isShift(event) && isKey(KEYS_UNDO, event);
2785
2915
  }
2916
+
2917
+ /**
2918
+ * @param {KeyboardEvent} event
2919
+ */
2786
2920
  function isRedo(event) {
2787
2921
  return isCmd(event) && (isKey(KEYS_REDO, event) || isKey(KEYS_UNDO, event) && isShift(event));
2788
2922
  }
2789
2923
 
2790
2924
  /**
2791
2925
  * @typedef {import('../../core/EventBus').default} EventBus
2926
+ *
2927
+ * @typedef {({ keyEvent: KeyboardEvent }) => any} Listener
2792
2928
  */
2793
2929
 
2794
2930
  var KEYDOWN_EVENT = 'keyboard.keydown',
@@ -2818,6 +2954,7 @@ var DEFAULT_PRIORITY$2 = 1000;
2818
2954
  * `keyboard.bindTo` configuration option.
2819
2955
  *
2820
2956
  * @param {Object} config
2957
+ * @param {EventTarget} [config.bindTo]
2821
2958
  * @param {EventBus} eventBus
2822
2959
  */
2823
2960
  function Keyboard(config, eventBus) {
@@ -2884,6 +3021,12 @@ Keyboard.prototype._getAllowedModifiers = function (element) {
2884
3021
  }
2885
3022
  return modifierContainer.getAttribute(HANDLE_MODIFIER_ATTRIBUTE).split(',');
2886
3023
  };
3024
+
3025
+ /**
3026
+ * Bind keyboard events to the given DOM node.
3027
+ *
3028
+ * @param {EventTarget} node
3029
+ */
2887
3030
  Keyboard.prototype.bind = function (node) {
2888
3031
  // make sure that the keyboard is only bound once to the DOM
2889
3032
  this.unbind();
@@ -2894,6 +3037,10 @@ Keyboard.prototype.bind = function (node) {
2894
3037
  event.bind(node, 'keyup', this._keyupHandler);
2895
3038
  this._fire('bind');
2896
3039
  };
3040
+
3041
+ /**
3042
+ * @return {EventTarget}
3043
+ */
2897
3044
  Keyboard.prototype.getBinding = function () {
2898
3045
  return this._node;
2899
3046
  };
@@ -2908,6 +3055,10 @@ Keyboard.prototype.unbind = function () {
2908
3055
  }
2909
3056
  this._node = null;
2910
3057
  };
3058
+
3059
+ /**
3060
+ * @param {string} event
3061
+ */
2911
3062
  Keyboard.prototype._fire = function (event) {
2912
3063
  this._eventBus.fire('keyboard.' + event, {
2913
3064
  node: this._node
@@ -2920,8 +3071,8 @@ Keyboard.prototype._fire = function (event) {
2920
3071
  * provided, the default value of 1000 is used.
2921
3072
  *
2922
3073
  * @param {number} [priority]
2923
- * @param {Function} listener
2924
- * @param {string} type
3074
+ * @param {Listener} listener
3075
+ * @param {string} [type='keyboard.keydown']
2925
3076
  */
2926
3077
  Keyboard.prototype.addListener = function (priority, listener, type) {
2927
3078
  if (isFunction(priority)) {
@@ -2931,6 +3082,13 @@ Keyboard.prototype.addListener = function (priority, listener, type) {
2931
3082
  }
2932
3083
  this._eventBus.on(type || KEYDOWN_EVENT, priority, listener);
2933
3084
  };
3085
+
3086
+ /**
3087
+ * Remove a listener function.
3088
+ *
3089
+ * @param {Listener} listener
3090
+ * @param {string} [type='keyboard.keydown']
3091
+ */
2934
3092
  Keyboard.prototype.removeListener = function (listener, type) {
2935
3093
  this._eventBus.off(type || KEYDOWN_EVENT, listener);
2936
3094
  };
@@ -3391,9 +3549,14 @@ class MoveFormFieldHandler {
3391
3549
  // (7) Reregister form field (and children) from path registry
3392
3550
  this._pathRegistry.executeRecursivelyOnFields(formField, ({
3393
3551
  field,
3394
- isClosed
3552
+ isClosed,
3553
+ isRepeatable
3395
3554
  }) => {
3396
- 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
+ });
3397
3560
  });
3398
3561
  }
3399
3562
 
@@ -3524,7 +3687,10 @@ class UpdateKeyClaimHandler {
3524
3687
  };
3525
3688
  const valuePath = this._pathRegistry.getValuePath(formField, options);
3526
3689
  if (claiming) {
3527
- this._pathRegistry.claimPath(valuePath, true);
3690
+ this._pathRegistry.claimPath(valuePath, {
3691
+ isClosed: true,
3692
+ claimerId: formField.id
3693
+ });
3528
3694
  } else {
3529
3695
  this._pathRegistry.unclaimPath(valuePath);
3530
3696
  }
@@ -3535,12 +3701,16 @@ class UpdateKeyClaimHandler {
3535
3701
  revert(context) {
3536
3702
  const {
3537
3703
  claiming,
3704
+ formField,
3538
3705
  valuePath
3539
3706
  } = context;
3540
3707
  if (claiming) {
3541
3708
  this._pathRegistry.unclaimPath(valuePath);
3542
3709
  } else {
3543
- this._pathRegistry.claimPath(valuePath, true);
3710
+ this._pathRegistry.claimPath(valuePath, {
3711
+ isClosed: true,
3712
+ claimerId: formField.id
3713
+ });
3544
3714
  }
3545
3715
  }
3546
3716
  }
@@ -3569,24 +3739,34 @@ class UpdatePathClaimHandler {
3569
3739
  if (claiming) {
3570
3740
  this._pathRegistry.executeRecursivelyOnFields(formField, ({
3571
3741
  field,
3572
- isClosed
3742
+ isClosed,
3743
+ isRepeatable
3573
3744
  }) => {
3574
3745
  const valuePath = this._pathRegistry.getValuePath(field, options);
3575
3746
  valuePaths.push({
3576
3747
  valuePath,
3577
- isClosed
3748
+ isClosed,
3749
+ isRepeatable,
3750
+ claimerId: field.id
3751
+ });
3752
+ this._pathRegistry.claimPath(valuePath, {
3753
+ isClosed,
3754
+ isRepeatable,
3755
+ claimerId: field.id
3578
3756
  });
3579
- this._pathRegistry.claimPath(valuePath, isClosed);
3580
3757
  });
3581
3758
  } else {
3582
3759
  this._pathRegistry.executeRecursivelyOnFields(formField, ({
3583
3760
  field,
3584
- isClosed
3761
+ isClosed,
3762
+ isRepeatable
3585
3763
  }) => {
3586
3764
  const valuePath = this._pathRegistry.getValuePath(field, options);
3587
3765
  valuePaths.push({
3588
3766
  valuePath,
3589
- isClosed
3767
+ isClosed,
3768
+ isRepeatable,
3769
+ claimerId: field.id
3590
3770
  });
3591
3771
  this._pathRegistry.unclaimPath(valuePath);
3592
3772
  });
@@ -3609,9 +3789,15 @@ class UpdatePathClaimHandler {
3609
3789
  } else {
3610
3790
  valuePaths.forEach(({
3611
3791
  valuePath,
3612
- isClosed
3792
+ isClosed,
3793
+ isRepeatable,
3794
+ claimerId
3613
3795
  }) => {
3614
- this._pathRegistry.claimPath(valuePath, isClosed);
3796
+ this._pathRegistry.claimPath(valuePath, {
3797
+ isClosed,
3798
+ isRepeatable,
3799
+ claimerId
3800
+ });
3615
3801
  });
3616
3802
  }
3617
3803
  }
@@ -4144,7 +4330,7 @@ class ValidateBehavior extends CommandInterceptor {
4144
4330
  }
4145
4331
  ValidateBehavior.$inject = ['eventBus'];
4146
4332
 
4147
- class ValuesSourceBehavior extends CommandInterceptor {
4333
+ class OptionsSourceBehavior extends CommandInterceptor {
4148
4334
  constructor(eventBus) {
4149
4335
  super(eventBus);
4150
4336
 
@@ -4164,15 +4350,15 @@ class ValuesSourceBehavior extends CommandInterceptor {
4164
4350
  }
4165
4351
 
4166
4352
  // clean up value sources that are not to going to be set
4167
- Object.values(VALUES_SOURCES).forEach(source => {
4168
- const path = VALUES_SOURCES_PATHS[source];
4353
+ Object.values(OPTIONS_SOURCES).forEach(source => {
4354
+ const path = OPTIONS_SOURCES_PATHS[source];
4169
4355
  if (get(properties, path) == undefined) {
4170
- newProperties[VALUES_SOURCES_PATHS[source]] = undefined;
4356
+ newProperties[OPTIONS_SOURCES_PATHS[source]] = undefined;
4171
4357
  }
4172
4358
  });
4173
4359
 
4174
4360
  // clean up default value
4175
- 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) {
4176
4362
  newProperties['defaultValue'] = undefined;
4177
4363
  }
4178
4364
  context.properties = {
@@ -4182,23 +4368,85 @@ class ValuesSourceBehavior extends CommandInterceptor {
4182
4368
  }, true);
4183
4369
  }
4184
4370
  }
4185
- ValuesSourceBehavior.$inject = ['eventBus'];
4371
+ OptionsSourceBehavior.$inject = ['eventBus'];
4186
4372
 
4187
4373
  // helper ///////////////////
4188
4374
 
4189
4375
  function isValuesSourceUpdate(properties) {
4190
- return Object.values(VALUES_SOURCES_PATHS).some(path => {
4376
+ return Object.values(OPTIONS_SOURCES_PATHS).some(path => {
4191
4377
  return get(properties, path) !== undefined;
4192
4378
  });
4193
4379
  }
4194
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
+
4195
4441
  var behaviorModule = {
4196
- __init__: ['idBehavior', 'keyBehavior', 'pathBehavior', 'validateBehavior', 'valuesSourceBehavior'],
4442
+ __init__: ['idBehavior', 'keyBehavior', 'pathBehavior', 'validateBehavior', 'optionsSourceBehavior', 'columnsSourceBehavior', 'tableDataSourceBehavior'],
4197
4443
  idBehavior: ['type', IdBehavior],
4198
4444
  keyBehavior: ['type', KeyBehavior],
4199
4445
  pathBehavior: ['type', PathBehavior],
4200
4446
  validateBehavior: ['type', ValidateBehavior],
4201
- valuesSourceBehavior: ['type', ValuesSourceBehavior]
4447
+ optionsSourceBehavior: ['type', OptionsSourceBehavior],
4448
+ columnsSourceBehavior: ['type', ColumnsSourceBehavior],
4449
+ tableDataSourceBehavior: ['type', TableDataSourceBehavior]
4202
4450
  };
4203
4451
 
4204
4452
  /**
@@ -8519,11 +8767,15 @@ function textToLabel(text) {
8519
8767
  }
8520
8768
  return null;
8521
8769
  }
8770
+
8771
+ /**
8772
+ * @param {string} path
8773
+ */
8522
8774
  function isValidDotPath(path) {
8523
8775
  return /^\w+(\.\w+)*$/.test(path);
8524
8776
  }
8525
8777
  const INPUTS = ['checkbox', 'checklist', 'datetime', 'number', 'radio', 'select', 'taglist', 'textfield', 'textarea'];
8526
- const VALUES_INPUTS = ['checklist', 'radio', 'select', 'taglist'];
8778
+ const OPTIONS_INPUTS = ['checklist', 'radio', 'select', 'taglist'];
8527
8779
  function hasEntryConfigured(formFieldDefinition, entryId) {
8528
8780
  const {
8529
8781
  propertiesPanelEntries = []
@@ -8533,7 +8785,7 @@ function hasEntryConfigured(formFieldDefinition, entryId) {
8533
8785
  }
8534
8786
  return propertiesPanelEntries.some(id => id === entryId);
8535
8787
  }
8536
- function hasValuesGroupsConfigured(formFieldDefinition) {
8788
+ function hasOptionsGroupsConfigured(formFieldDefinition) {
8537
8789
  const {
8538
8790
  propertiesPanelEntries = []
8539
8791
  } = formFieldDefinition;
@@ -8543,6 +8795,13 @@ function hasValuesGroupsConfigured(formFieldDefinition) {
8543
8795
  return propertiesPanelEntries.some(id => id === 'values');
8544
8796
  }
8545
8797
 
8798
+ /**
8799
+ * @param {string} path
8800
+ */
8801
+ function hasIntegerPathSegment(path) {
8802
+ return path.split('.').some(segment => /^\d+$/.test(segment));
8803
+ }
8804
+
8546
8805
  function useService (type, strict) {
8547
8806
  const {
8548
8807
  getService
@@ -9048,7 +9307,7 @@ function DefaultOptionEntry(props) {
9048
9307
  function isDefaultVisible(matchers) {
9049
9308
  return field => {
9050
9309
  // Only make default values available when they are statically defined
9051
- if (!INPUTS.includes(type) || VALUES_INPUTS.includes(type) && !field.values) {
9310
+ if (!INPUTS.includes(type) || OPTIONS_INPUTS.includes(type) && !field.values) {
9052
9311
  return false;
9053
9312
  }
9054
9313
  return matchers(field);
@@ -9394,20 +9653,27 @@ function containsSpace(value) {
9394
9653
  function KeyEntry(props) {
9395
9654
  const {
9396
9655
  editField,
9397
- field
9656
+ field,
9657
+ getService
9398
9658
  } = props;
9399
9659
  const entries = [];
9400
9660
  entries.push({
9401
9661
  id: 'key',
9402
- component: Key$1,
9662
+ component: Key$2,
9403
9663
  editField: editField,
9404
9664
  field: field,
9405
9665
  isEdited: isEdited,
9406
- 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
+ }
9407
9673
  });
9408
9674
  return entries;
9409
9675
  }
9410
- function Key$1(props) {
9676
+ function Key$2(props) {
9411
9677
  const {
9412
9678
  editField,
9413
9679
  field,
@@ -9429,14 +9695,13 @@ function Key$1(props) {
9429
9695
  if (value === field.key) {
9430
9696
  return null;
9431
9697
  }
9432
- if (isUndefined(value) || !value.length) {
9698
+ if (!isString(value) || value.length === 0) {
9433
9699
  return 'Must not be empty.';
9434
9700
  }
9435
- if (value && !isValidDotPath(value)) {
9701
+ if (!isValidDotPath(value)) {
9436
9702
  return 'Must be a variable or a dot separated path.';
9437
9703
  }
9438
- const hasIntegerPathSegment = value.split('.').some(segment => /^\d+$/.test(segment));
9439
- if (hasIntegerPathSegment) {
9704
+ if (hasIntegerPathSegment(value)) {
9440
9705
  return 'Must not contain numerical path segments.';
9441
9706
  }
9442
9707
  const replacements = {
@@ -9449,8 +9714,14 @@ function Key$1(props) {
9449
9714
 
9450
9715
  // unclaim temporarily to avoid self-conflicts
9451
9716
  pathRegistry.unclaimPath(oldPath);
9452
- const canClaim = pathRegistry.canClaimPath(newPath, true);
9453
- 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
+ });
9454
9725
  return canClaim ? null : 'Must not conflict with other key/path assignments.';
9455
9726
  };
9456
9727
  return TextfieldEntry({
@@ -9469,13 +9740,15 @@ function Key$1(props) {
9469
9740
  function PathEntry(props) {
9470
9741
  const {
9471
9742
  editField,
9472
- field
9743
+ field,
9744
+ getService
9473
9745
  } = props;
9474
9746
  const {
9475
9747
  type
9476
9748
  } = field;
9477
9749
  const entries = [];
9478
- if (type === 'group') {
9750
+ const formFieldDefinition = getService('formFields').get(type);
9751
+ if (formFieldDefinition && formFieldDefinition.config.pathed) {
9479
9752
  entries.push({
9480
9753
  id: 'path',
9481
9754
  component: Path,
@@ -9494,6 +9767,8 @@ function Path(props) {
9494
9767
  } = props;
9495
9768
  const debounce = useService('debounce');
9496
9769
  const pathRegistry = useService('pathRegistry');
9770
+ const fieldConfig = useService('formFields').get(field.type).config;
9771
+ const isRepeating = fieldConfig.repeatable && field.isRepeating;
9497
9772
  const path = ['path'];
9498
9773
  const getValue = () => {
9499
9774
  return get(field, path, '');
@@ -9505,32 +9780,53 @@ function Path(props) {
9505
9780
  return editField(field, path, value);
9506
9781
  };
9507
9782
  const validate = value => {
9508
- 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) {
9509
9789
  return null;
9510
9790
  }
9511
- if (value && !isValidDotPath(value)) {
9512
- 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;
9513
9796
  }
9514
- 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));
9515
9800
  if (hasIntegerPathSegment) {
9516
9801
  return 'Must not contain numerical path segments.';
9517
9802
  }
9518
- const options = value && {
9803
+
9804
+ // Check for path collisions
9805
+ const options = {
9519
9806
  replacements: {
9520
- [field.id]: [value]
9807
+ [field.id]: value.split('.')
9521
9808
  }
9522
- } || {};
9809
+ };
9523
9810
  const canClaim = pathRegistry.executeRecursivelyOnFields(field, ({
9524
9811
  field,
9525
- isClosed
9812
+ isClosed,
9813
+ isRepeatable
9526
9814
  }) => {
9527
9815
  const path = pathRegistry.getValuePath(field, options);
9528
- return pathRegistry.canClaimPath(path, isClosed);
9816
+ return pathRegistry.canClaimPath(path, {
9817
+ isClosed,
9818
+ isRepeatable,
9819
+ claimerId: field.id
9820
+ });
9529
9821
  });
9530
9822
  if (!canClaim) {
9531
- return 'Must not cause two binding paths to colide';
9823
+ return 'Must not cause two binding paths to collide';
9532
9824
  }
9825
+
9826
+ // If all checks pass
9827
+ return null;
9533
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.';
9534
9830
  return TextfieldEntry({
9535
9831
  debounce,
9536
9832
  description: 'Where the child variables of this component are pathed to.',
@@ -9538,7 +9834,7 @@ function Path(props) {
9538
9834
  getValue,
9539
9835
  id,
9540
9836
  label: 'Path',
9541
- tooltip: 'Routes the children of this component into a form variable, may be left empty to route at the root level.',
9837
+ tooltip,
9542
9838
  setValue,
9543
9839
  validate
9544
9840
  });
@@ -9551,6 +9847,8 @@ function simpleBoolEntryFactory(options) {
9551
9847
  description,
9552
9848
  path,
9553
9849
  props,
9850
+ getValue,
9851
+ setValue,
9554
9852
  isDefaultVisible
9555
9853
  } = options;
9556
9854
  const {
@@ -9565,8 +9863,10 @@ function simpleBoolEntryFactory(options) {
9565
9863
  editField,
9566
9864
  description,
9567
9865
  component: SimpleBoolComponent,
9568
- isEdited: isEdited$5,
9569
- isDefaultVisible
9866
+ isEdited: isEdited$8,
9867
+ isDefaultVisible,
9868
+ getValue,
9869
+ setValue
9570
9870
  };
9571
9871
  }
9572
9872
  const SimpleBoolComponent = props => {
@@ -9576,57 +9876,162 @@ const SimpleBoolComponent = props => {
9576
9876
  path,
9577
9877
  field,
9578
9878
  editField,
9879
+ getValue = () => get(field, path, ''),
9880
+ setValue = value => editField(field, path, value || false),
9579
9881
  description
9580
9882
  } = props;
9581
- const getValue = () => get(field, path, '');
9582
- const setValue = value => editField(field, path, value || false);
9583
- return CheckboxEntry({
9883
+ return ToggleSwitchEntry({
9584
9884
  element: field,
9585
9885
  getValue,
9586
9886
  id,
9587
9887
  label,
9588
9888
  setValue,
9889
+ inline: true,
9589
9890
  description
9590
9891
  });
9591
9892
  };
9592
9893
 
9593
- function GroupEntries(props) {
9894
+ function simpleSelectEntryFactory(options) {
9895
+ const {
9896
+ id,
9897
+ label,
9898
+ path,
9899
+ props,
9900
+ optionsArray
9901
+ } = options;
9594
9902
  const {
9903
+ editField,
9595
9904
  field
9596
9905
  } = props;
9597
- const {
9598
- type
9599
- } = field;
9600
- if (type !== 'group') {
9601
- return [];
9602
- }
9603
- const entries = [simpleBoolEntryFactory({
9604
- id: 'showOutline',
9605
- path: ['showOutline'],
9606
- label: 'Show outline',
9607
- props
9608
- })];
9609
- return entries;
9906
+ return {
9907
+ id,
9908
+ label,
9909
+ path,
9910
+ field,
9911
+ editField,
9912
+ optionsArray,
9913
+ component: SimpleSelectComponent,
9914
+ isEdited: isEdited$3
9915
+ };
9610
9916
  }
9611
-
9612
- function LabelEntry(props) {
9917
+ const SimpleSelectComponent = props => {
9613
9918
  const {
9919
+ id,
9920
+ label,
9921
+ path,
9614
9922
  field,
9615
- editField
9616
- } = props;
9617
- const entries = [];
9618
- entries.push({
9619
- id: 'date-label',
9620
- component: DateLabel,
9621
9923
  editField,
9622
- field,
9623
- isEdited: isEdited$6,
9624
- isDefaultVisible: function (field) {
9625
- return field.type === 'datetime' && (field.subtype === DATETIME_SUBTYPES.DATE || field.subtype === DATETIME_SUBTYPES.DATETIME);
9626
- }
9627
- });
9628
- entries.push({
9629
- id: 'time-label',
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) {
9999
+ const {
10000
+ field
10001
+ } = props;
10002
+ const {
10003
+ type
10004
+ } = field;
10005
+ if (!['group', 'dynamiclist'].includes(type)) {
10006
+ return [];
10007
+ }
10008
+ const entries = [simpleBoolEntryFactory({
10009
+ id: 'showOutline',
10010
+ path: ['showOutline'],
10011
+ label: 'Show outline',
10012
+ props
10013
+ })];
10014
+ return entries;
10015
+ }
10016
+
10017
+ function LabelEntry(props) {
10018
+ const {
10019
+ field,
10020
+ editField
10021
+ } = props;
10022
+ const entries = [];
10023
+ entries.push({
10024
+ id: 'date-label',
10025
+ component: DateLabel,
10026
+ editField,
10027
+ field,
10028
+ isEdited: isEdited$6,
10029
+ isDefaultVisible: function (field) {
10030
+ return field.type === 'datetime' && (field.subtype === DATETIME_SUBTYPES.DATE || field.subtype === DATETIME_SUBTYPES.DATETIME);
10031
+ }
10032
+ });
10033
+ entries.push({
10034
+ id: 'time-label',
9630
10035
  component: TimeLabel,
9631
10036
  editField,
9632
10037
  field,
@@ -9637,15 +10042,15 @@ function LabelEntry(props) {
9637
10042
  });
9638
10043
  entries.push({
9639
10044
  id: 'label',
9640
- component: Label$1,
10045
+ component: Label$2,
9641
10046
  editField,
9642
10047
  field,
9643
10048
  isEdited: isEdited$6,
9644
- isDefaultVisible: field => INPUTS.includes(field.type) || field.type === 'button' || field.type === 'group'
10049
+ isDefaultVisible: field => [...INPUTS, 'button', 'group', 'table', 'iframe', 'dynamiclist'].includes(field.type)
9645
10050
  });
9646
10051
  return entries;
9647
10052
  }
9648
- function Label$1(props) {
10053
+ function Label$2(props) {
9649
10054
  const {
9650
10055
  editField,
9651
10056
  field,
@@ -9662,7 +10067,7 @@ function Label$1(props) {
9662
10067
  const setValue = value => {
9663
10068
  return editField(field, path, value || '');
9664
10069
  };
9665
- const label = field.type === 'group' ? 'Group label' : 'Field label';
10070
+ const label = getLabelText(field.type);
9666
10071
  return FeelTemplatingEntry({
9667
10072
  debounce,
9668
10073
  element: field,
@@ -9731,6 +10136,170 @@ function TimeLabel(props) {
9731
10136
  });
9732
10137
  }
9733
10138
 
10139
+ // helpers //////////
10140
+
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';
10156
+ }
10157
+ }
10158
+
10159
+ function HeightEntry(props) {
10160
+ const {
10161
+ editField,
10162
+ field,
10163
+ id,
10164
+ description,
10165
+ isDefaultVisible,
10166
+ defaultValue
10167
+ } = props;
10168
+ const entries = [];
10169
+ entries.push({
10170
+ id: id + '-height',
10171
+ component: Height,
10172
+ description,
10173
+ isEdited: isEdited$7,
10174
+ editField,
10175
+ field,
10176
+ defaultValue,
10177
+ isDefaultVisible: field => {
10178
+ if (isFunction(isDefaultVisible)) {
10179
+ return isDefaultVisible(field);
10180
+ }
10181
+ return field.type === 'spacer';
10182
+ }
10183
+ });
10184
+ return entries;
10185
+ }
10186
+ function Height(props) {
10187
+ const {
10188
+ description,
10189
+ editField,
10190
+ field,
10191
+ id,
10192
+ defaultValue = 60 // default value for spacer
10193
+ } = props;
10194
+ const debounce = useService('debounce');
10195
+ const getValue = e => get(field, ['height'], defaultValue);
10196
+ const setValue = (value, error) => {
10197
+ if (error) {
10198
+ return;
10199
+ }
10200
+ editField(field, ['height'], value);
10201
+ };
10202
+ return NumberFieldEntry({
10203
+ debounce,
10204
+ description,
10205
+ label: 'Height',
10206
+ element: field,
10207
+ id,
10208
+ getValue,
10209
+ setValue,
10210
+ validate: value => {
10211
+ if (value === undefined || value === null) return;
10212
+ if (value < 1) return 'Should be greater than zero.';
10213
+ if (!Number.isInteger(value)) return 'Should be an integer.';
10214
+ }
10215
+ });
10216
+ }
10217
+
10218
+ function IFrameHeightEntry(props) {
10219
+ return [...HeightEntry({
10220
+ ...props,
10221
+ defaultValue: 300,
10222
+ description: 'Height of the container in pixels.',
10223
+ isDefaultVisible: field => field.type === 'iframe'
10224
+ })];
10225
+ }
10226
+
10227
+ const HTTPS_PATTERN = /^(https):\/\/*/i; // eslint-disable-line no-useless-escape
10228
+
10229
+ function IFrameUrlEntry(props) {
10230
+ const {
10231
+ editField,
10232
+ field
10233
+ } = props;
10234
+ const entries = [];
10235
+ entries.push({
10236
+ id: 'url',
10237
+ component: Url,
10238
+ editField: editField,
10239
+ field: field,
10240
+ isEdited: isEdited$6,
10241
+ isDefaultVisible: field => field.type === 'iframe'
10242
+ });
10243
+ return entries;
10244
+ }
10245
+ function Url(props) {
10246
+ const {
10247
+ editField,
10248
+ field,
10249
+ id
10250
+ } = props;
10251
+ const debounce = useService('debounce');
10252
+ const variables = useVariables().map(name => ({
10253
+ name
10254
+ }));
10255
+ const path = ['url'];
10256
+ const getValue = () => {
10257
+ return get(field, path, '');
10258
+ };
10259
+ const setValue = value => {
10260
+ return editField(field, path, value);
10261
+ };
10262
+ const validate = value => {
10263
+ if (!value) {
10264
+ return;
10265
+ }
10266
+ if (!HTTPS_PATTERN.test(value)) {
10267
+ return 'For security reasons the URL must start with "https".';
10268
+ }
10269
+ };
10270
+ return FeelTemplatingEntry({
10271
+ debounce,
10272
+ element: field,
10273
+ feel: 'optional',
10274
+ getValue,
10275
+ id,
10276
+ label: 'URL',
10277
+ setValue,
10278
+ singleLine: true,
10279
+ tooltip: getTooltip(),
10280
+ validate,
10281
+ variables
10282
+ });
10283
+ }
10284
+
10285
+ // helper //////////////////////
10286
+
10287
+ function getTooltip() {
10288
+ return jsxs(Fragment$1, {
10289
+ children: [jsx("p", {
10290
+ children: "Enter a HTTPS URL to a source or populate it dynamically via a template or an expression (e.g., to pass a value from the variable)."
10291
+ }), jsx("p", {
10292
+ children: "Please make sure that the URL is safe as it might impose security risks."
10293
+ }), jsxs("p", {
10294
+ children: ["Not all external sources can be displayed in the iFrame. Read more about it in the ", jsx("a", {
10295
+ target: "_blank",
10296
+ href: "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options",
10297
+ children: "X-FRAME-OPTIONS documentation"
10298
+ }), "."]
10299
+ })]
10300
+ });
10301
+ }
10302
+
9734
10303
  function SourceEntry(props) {
9735
10304
  const {
9736
10305
  editField,
@@ -9739,7 +10308,7 @@ function SourceEntry(props) {
9739
10308
  const entries = [];
9740
10309
  entries.push({
9741
10310
  id: 'source',
9742
- component: Source,
10311
+ component: Source$1,
9743
10312
  editField: editField,
9744
10313
  field: field,
9745
10314
  isEdited: isEdited$6,
@@ -9747,7 +10316,7 @@ function SourceEntry(props) {
9747
10316
  });
9748
10317
  return entries;
9749
10318
  }
9750
- function Source(props) {
10319
+ function Source$1(props) {
9751
10320
  const {
9752
10321
  editField,
9753
10322
  field,
@@ -9792,18 +10361,6 @@ function TextEntry(props) {
9792
10361
  isEdited: isEdited$6,
9793
10362
  isDefaultVisible: field => field.type === 'text'
9794
10363
  }];
9795
-
9796
- // todo: skipped to make the release without too much risk
9797
- // if (templating.isTemplate(field.text)) {
9798
- // entries.push(simpleBoolEntryFactory({
9799
- // id: 'strict',
9800
- // path: [ 'strict' ],
9801
- // label: 'Strict templating',
9802
- // description: 'Enforces types to be correct',
9803
- // props
9804
- // }));
9805
- // }
9806
-
9807
10364
  return entries;
9808
10365
  }
9809
10366
  function Text(props) {
@@ -9843,7 +10400,7 @@ function Text(props) {
9843
10400
  });
9844
10401
  }
9845
10402
 
9846
- function SpacerEntry(props) {
10403
+ function NumberEntries(props) {
9847
10404
  const {
9848
10405
  editField,
9849
10406
  field,
@@ -9851,99 +10408,53 @@ function SpacerEntry(props) {
9851
10408
  } = props;
9852
10409
  const entries = [];
9853
10410
  entries.push({
9854
- id: id + '-height',
9855
- component: SpacerHeight,
10411
+ id: id + '-decimalDigits',
10412
+ component: NumberDecimalDigits,
9856
10413
  isEdited: isEdited$7,
9857
10414
  editField,
9858
10415
  field,
9859
- isDefaultVisible: field => field.type === 'spacer'
10416
+ isDefaultVisible: field => field.type === 'number'
10417
+ });
10418
+ entries.push({
10419
+ id: id + '-step',
10420
+ component: NumberArrowStep,
10421
+ isEdited: isEdited,
10422
+ editField,
10423
+ field,
10424
+ isDefaultVisible: field => field.type === 'number'
9860
10425
  });
9861
10426
  return entries;
9862
10427
  }
9863
- function SpacerHeight(props) {
10428
+ function NumberDecimalDigits(props) {
9864
10429
  const {
9865
10430
  editField,
9866
10431
  field,
9867
10432
  id
9868
10433
  } = props;
9869
10434
  const debounce = useService('debounce');
9870
- const getValue = e => get(field, ['height']);
10435
+ const getValue = e => get(field, ['decimalDigits']);
9871
10436
  const setValue = (value, error) => {
9872
10437
  if (error) {
9873
10438
  return;
9874
10439
  }
9875
- editField(field, ['height'], value);
10440
+ editField(field, ['decimalDigits'], value);
9876
10441
  };
9877
10442
  return NumberFieldEntry({
9878
10443
  debounce,
9879
- label: 'Height',
10444
+ label: 'Decimal digits',
9880
10445
  element: field,
9881
- id,
10446
+ step: 'any',
9882
10447
  getValue,
10448
+ id,
9883
10449
  setValue,
9884
10450
  validate: value => {
9885
10451
  if (value === undefined || value === null) return;
9886
- if (value < 1) return 'Should be greater than zero.';
10452
+ if (value < 0) return 'Should be greater than or equal to zero.';
9887
10453
  if (!Number.isInteger(value)) return 'Should be an integer.';
9888
10454
  }
9889
10455
  });
9890
10456
  }
9891
-
9892
- function NumberEntries(props) {
9893
- const {
9894
- editField,
9895
- field,
9896
- id
9897
- } = props;
9898
- const entries = [];
9899
- entries.push({
9900
- id: id + '-decimalDigits',
9901
- component: NumberDecimalDigits,
9902
- isEdited: isEdited$7,
9903
- editField,
9904
- field,
9905
- isDefaultVisible: field => field.type === 'number'
9906
- });
9907
- entries.push({
9908
- id: id + '-step',
9909
- component: NumberArrowStep,
9910
- isEdited: isEdited,
9911
- editField,
9912
- field,
9913
- isDefaultVisible: field => field.type === 'number'
9914
- });
9915
- return entries;
9916
- }
9917
- function NumberDecimalDigits(props) {
9918
- const {
9919
- editField,
9920
- field,
9921
- id
9922
- } = props;
9923
- const debounce = useService('debounce');
9924
- const getValue = e => get(field, ['decimalDigits']);
9925
- const setValue = (value, error) => {
9926
- if (error) {
9927
- return;
9928
- }
9929
- editField(field, ['decimalDigits'], value);
9930
- };
9931
- return NumberFieldEntry({
9932
- debounce,
9933
- label: 'Decimal digits',
9934
- element: field,
9935
- step: 'any',
9936
- getValue,
9937
- id,
9938
- setValue,
9939
- validate: value => {
9940
- if (value === undefined || value === null) return;
9941
- if (value < 0) return 'Should be greater than or equal to zero.';
9942
- if (!Number.isInteger(value)) return 'Should be an integer.';
9943
- }
9944
- });
9945
- }
9946
- function NumberArrowStep(props) {
10457
+ function NumberArrowStep(props) {
9947
10458
  const {
9948
10459
  editField,
9949
10460
  field,
@@ -10277,7 +10788,7 @@ function ValueEntry(props) {
10277
10788
  validateFactory
10278
10789
  } = props;
10279
10790
  const entries = [{
10280
- component: Label,
10791
+ component: Label$1,
10281
10792
  editField,
10282
10793
  field,
10283
10794
  id: idPrefix + '-label',
@@ -10295,7 +10806,7 @@ function ValueEntry(props) {
10295
10806
  }];
10296
10807
  return entries;
10297
10808
  }
10298
- function Label(props) {
10809
+ function Label$1(props) {
10299
10810
  const {
10300
10811
  editField,
10301
10812
  field,
@@ -10363,7 +10874,7 @@ function CustomValueEntry(props) {
10363
10874
  validateFactory
10364
10875
  } = props;
10365
10876
  const entries = [{
10366
- component: Key,
10877
+ component: Key$1,
10367
10878
  editField,
10368
10879
  field,
10369
10880
  id: idPrefix + '-key',
@@ -10381,7 +10892,7 @@ function CustomValueEntry(props) {
10381
10892
  }];
10382
10893
  return entries;
10383
10894
  }
10384
- function Key(props) {
10895
+ function Key$1(props) {
10385
10896
  const {
10386
10897
  editField,
10387
10898
  field,
@@ -10506,7 +11017,7 @@ function AutoFocusSelectEntry(props) {
10506
11017
  });
10507
11018
  }
10508
11019
 
10509
- function ValuesSourceSelectEntry(props) {
11020
+ function OptionsSourceSelectEntry(props) {
10510
11021
  const {
10511
11022
  editField,
10512
11023
  field,
@@ -10526,25 +11037,25 @@ function ValuesSourceSelect(props) {
10526
11037
  field,
10527
11038
  id
10528
11039
  } = props;
10529
- const getValue = getValuesSource;
11040
+ const getValue = getOptionsSource;
10530
11041
  const setValue = value => {
10531
11042
  let newField = field;
10532
11043
  const newProperties = {};
10533
- newProperties[VALUES_SOURCES_PATHS[value]] = VALUES_SOURCES_DEFAULTS[value];
11044
+ newProperties[OPTIONS_SOURCES_PATHS[value]] = OPTIONS_SOURCES_DEFAULTS[value];
10534
11045
  newField = editField(field, newProperties);
10535
11046
  return newField;
10536
11047
  };
10537
- const getValuesSourceOptions = () => {
10538
- return Object.values(VALUES_SOURCES).map(valueSource => ({
10539
- label: VALUES_SOURCES_LABELS[valueSource],
11048
+ const getOptionsSourceOptions = () => {
11049
+ return Object.values(OPTIONS_SOURCES).map(valueSource => ({
11050
+ label: OPTIONS_SOURCES_LABELS[valueSource],
10540
11051
  value: valueSource
10541
11052
  }));
10542
11053
  };
10543
11054
  return AutoFocusSelectEntry({
10544
- autoFocusEntry: getAutoFocusEntryId(field),
11055
+ autoFocusEntry: getAutoFocusEntryId$1(field),
10545
11056
  label: 'Type',
10546
11057
  element: field,
10547
- getOptions: getValuesSourceOptions,
11058
+ getOptions: getOptionsSourceOptions,
10548
11059
  getValue,
10549
11060
  id,
10550
11061
  setValue
@@ -10553,19 +11064,19 @@ function ValuesSourceSelect(props) {
10553
11064
 
10554
11065
  // helpers //////////
10555
11066
 
10556
- function getAutoFocusEntryId(field) {
10557
- const valuesSource = getValuesSource(field);
10558
- if (valuesSource === VALUES_SOURCES.EXPRESSION) {
10559
- return `${field.id}-valuesExpression-expression`;
10560
- } else if (valuesSource === VALUES_SOURCES.INPUT) {
10561
- return `${field.id}-dynamicValues-key`;
10562
- } else if (valuesSource === VALUES_SOURCES.STATIC) {
10563
- 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';
10564
11075
  }
10565
11076
  return null;
10566
11077
  }
10567
11078
 
10568
- function InputKeyValuesSourceEntry(props) {
11079
+ function InputKeyOptionsSourceEntry(props) {
10569
11080
  const {
10570
11081
  editField,
10571
11082
  field,
@@ -10586,7 +11097,7 @@ function InputValuesKey(props) {
10586
11097
  id
10587
11098
  } = props;
10588
11099
  const debounce = useService('debounce');
10589
- const path = VALUES_SOURCES_PATHS[VALUES_SOURCES.INPUT];
11100
+ const path = OPTIONS_SOURCES_PATHS[OPTIONS_SOURCES.INPUT];
10590
11101
  const schema = '[\n {\n "label": "dollar",\n "value": "$"\n }\n]';
10591
11102
  const tooltip = jsxs("div", {
10592
11103
  children: ["The input property may be an array of simple values or alternatively follow this schema:", jsx("pre", {
@@ -10624,7 +11135,7 @@ function InputValuesKey(props) {
10624
11135
  });
10625
11136
  }
10626
11137
 
10627
- function StaticValuesSourceEntry(props) {
11138
+ function StaticOptionsSourceEntry(props) {
10628
11139
  const {
10629
11140
  editField,
10630
11141
  field,
@@ -10637,10 +11148,10 @@ function StaticValuesSourceEntry(props) {
10637
11148
  e.stopPropagation();
10638
11149
  const index = values.length + 1;
10639
11150
  const entry = getIndexedEntry(index, values);
10640
- 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));
10641
11152
  };
10642
11153
  const removeEntry = entry => {
10643
- editField(field, VALUES_SOURCES_PATHS[VALUES_SOURCES.STATIC], without(values, entry));
11154
+ editField(field, OPTIONS_SOURCES_PATHS[OPTIONS_SOURCES.STATIC], without(values, entry));
10644
11155
  };
10645
11156
  const validateFactory = (key, getValue) => {
10646
11157
  return value => {
@@ -10830,6 +11341,76 @@ function Readonly(props) {
10830
11341
  });
10831
11342
  }
10832
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
+
10833
11414
  function ConditionEntry(props) {
10834
11415
  const {
10835
11416
  editField,
@@ -10840,10 +11421,391 @@ function ConditionEntry(props) {
10840
11421
  component: Condition,
10841
11422
  editField: editField,
10842
11423
  field: field,
10843
- isEdited: isEdited$6
10844
- }];
11424
+ isEdited: isEdited$6
11425
+ }];
11426
+ }
11427
+ function Condition(props) {
11428
+ const {
11429
+ editField,
11430
+ field,
11431
+ id
11432
+ } = props;
11433
+ const debounce = useService('debounce');
11434
+ const variables = useVariables().map(name => ({
11435
+ name
11436
+ }));
11437
+ const path = ['conditional', 'hide'];
11438
+ const getValue = () => {
11439
+ return get(field, path, '');
11440
+ };
11441
+ const setValue = value => {
11442
+ if (!value) {
11443
+ return editField(field, 'conditional', undefined);
11444
+ }
11445
+ return editField(field, 'conditional', {
11446
+ hide: value
11447
+ });
11448
+ };
11449
+ return FeelEntry({
11450
+ debounce,
11451
+ description: 'Condition under which the field is hidden',
11452
+ element: field,
11453
+ feel: 'required',
11454
+ getValue,
11455
+ id,
11456
+ label: 'Hide if',
11457
+ setValue,
11458
+ variables
11459
+ });
11460
+ }
11461
+
11462
+ function OptionsExpressionEntry(props) {
11463
+ const {
11464
+ editField,
11465
+ field,
11466
+ id
11467
+ } = props;
11468
+ return [{
11469
+ id: id + '-expression',
11470
+ component: OptionsExpression,
11471
+ isEdited: isEdited$6,
11472
+ editField,
11473
+ field
11474
+ }];
11475
+ }
11476
+ function OptionsExpression(props) {
11477
+ const {
11478
+ editField,
11479
+ field,
11480
+ id
11481
+ } = props;
11482
+ const debounce = useService('debounce');
11483
+ const variables = useVariables().map(name => ({
11484
+ name
11485
+ }));
11486
+ const path = OPTIONS_SOURCES_PATHS[OPTIONS_SOURCES.EXPRESSION];
11487
+ const schema = '[\n {\n "label": "dollar",\n "value": "$"\n }\n]';
11488
+ const tooltip = jsxs("div", {
11489
+ children: ["The expression may result in an array of simple values or alternatively follow this schema:", jsx("pre", {
11490
+ children: jsx("code", {
11491
+ children: schema
11492
+ })
11493
+ })]
11494
+ });
11495
+ const getValue = () => get(field, path, '');
11496
+ const setValue = value => editField(field, path, value || '');
11497
+ return FeelEntry({
11498
+ debounce,
11499
+ description: 'Define an expression to populate the options from.',
11500
+ tooltip,
11501
+ element: field,
11502
+ feel: 'required',
11503
+ getValue,
11504
+ id,
11505
+ label: 'Options expression',
11506
+ setValue,
11507
+ variables
11508
+ });
11509
+ }
11510
+
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;
10845
11807
  }
10846
- function Condition(props) {
11808
+ function ColumnsExpression(props) {
10847
11809
  const {
10848
11810
  editField,
10849
11811
  field,
@@ -10853,78 +11815,208 @@ function Condition(props) {
10853
11815
  const variables = useVariables().map(name => ({
10854
11816
  name
10855
11817
  }));
10856
- const path = ['conditional', 'hide'];
10857
11818
  const getValue = () => {
10858
- return get(field, path, '');
11819
+ return get(field, PATH);
10859
11820
  };
10860
- const setValue = value => {
10861
- if (!value) {
10862
- return editField(field, 'conditional', undefined);
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;
10863
11830
  }
10864
- return editField(field, 'conditional', {
10865
- hide: value
10866
- });
11831
+ editField(field, PATH, value);
10867
11832
  };
10868
- return FeelEntry({
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({
10869
11853
  debounce,
10870
- description: 'Condition under which the field is hidden',
11854
+ description: 'Specify an expression to populate column items',
10871
11855
  element: field,
10872
11856
  feel: 'required',
10873
11857
  getValue,
10874
11858
  id,
10875
- label: 'Hide if',
11859
+ label: 'Expression',
11860
+ tooltip,
10876
11861
  setValue,
10877
- variables
11862
+ singleLine: true,
11863
+ variables,
11864
+ validate
10878
11865
  });
10879
11866
  }
10880
11867
 
10881
- function ValuesExpressionEntry(props) {
11868
+ const path$1 = 'columns';
11869
+ const labelPath = 'label';
11870
+ const keyPath = 'key';
11871
+ function ColumnEntry(props) {
10882
11872
  const {
10883
11873
  editField,
10884
11874
  field,
10885
- id
11875
+ idPrefix,
11876
+ index,
11877
+ validateFactory
10886
11878
  } = props;
10887
- return [{
10888
- id: id + '-expression',
10889
- component: ValuesExpression,
10890
- isEdited: isEdited$6,
11879
+ const entries = [{
11880
+ component: Label,
10891
11881
  editField,
10892
- field
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
10893
11895
  }];
11896
+ return entries;
10894
11897
  }
10895
- function ValuesExpression(props) {
11898
+ function Label(props) {
10896
11899
  const {
10897
11900
  editField,
10898
11901
  field,
10899
- id
11902
+ id,
11903
+ index
10900
11904
  } = props;
10901
11905
  const debounce = useService('debounce');
10902
- const variables = useVariables().map(name => ({
10903
- name
10904
- }));
10905
- const path = VALUES_SOURCES_PATHS[VALUES_SOURCES.EXPRESSION];
10906
- const schema = '[\n {\n "label": "dollar",\n "value": "$"\n }\n]';
10907
- const tooltip = jsxs("div", {
10908
- children: ["The expression may result in an array of simple values or alternatively follow this schema:", jsx("pre", {
10909
- children: jsx("code", {
10910
- children: schema
10911
- })
10912
- })]
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
10913
11929
  });
10914
- const getValue = () => get(field, path, '');
10915
- const setValue = value => editField(field, path, value || '');
10916
- return FeelEntry({
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({
10917
11956
  debounce,
10918
- description: 'Define an expression to populate the options from.',
10919
- tooltip,
10920
11957
  element: field,
10921
- feel: 'required',
10922
11958
  getValue,
10923
11959
  id,
10924
- label: 'Options expression',
11960
+ label: 'Key',
10925
11961
  setValue,
10926
- variables
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
+ };
10927
12014
  });
12015
+ return {
12016
+ items,
12017
+ add: addEntry,
12018
+ shouldSort: false
12019
+ };
10928
12020
  }
10929
12021
 
10930
12022
  function GeneralGroup(field, editField, getService) {
@@ -10939,13 +12031,16 @@ function GeneralGroup(field, editField, getService) {
10939
12031
  editField
10940
12032
  }), ...KeyEntry({
10941
12033
  field,
10942
- editField
12034
+ editField,
12035
+ getService
10943
12036
  }), ...PathEntry({
10944
12037
  field,
10945
- editField
10946
- }), ...GroupEntries({
12038
+ editField,
12039
+ getService
12040
+ }), ...RepeatableEntry({
10947
12041
  field,
10948
- editField
12042
+ editField,
12043
+ getService
10949
12044
  }), ...DefaultOptionEntry({
10950
12045
  field,
10951
12046
  editField
@@ -10959,7 +12054,13 @@ function GeneralGroup(field, editField, getService) {
10959
12054
  field,
10960
12055
  editField,
10961
12056
  getService
10962
- }), ...SpacerEntry({
12057
+ }), ...IFrameUrlEntry({
12058
+ field,
12059
+ editField
12060
+ }), ...IFrameHeightEntry({
12061
+ field,
12062
+ editField
12063
+ }), ...HeightEntry({
10963
12064
  field,
10964
12065
  editField
10965
12066
  }), ...NumberEntries({
@@ -10980,6 +12081,15 @@ function GeneralGroup(field, editField, getService) {
10980
12081
  }), ...ReadonlyEntry({
10981
12082
  field,
10982
12083
  editField
12084
+ }), ...TableDataSourceEntry({
12085
+ field,
12086
+ editField
12087
+ }), ...PaginationEntry({
12088
+ field,
12089
+ editField
12090
+ }), ...RowCountEntry({
12091
+ field,
12092
+ editField
10983
12093
  })];
10984
12094
  if (entries.length === 0) {
10985
12095
  return null;
@@ -11270,67 +12380,66 @@ function ValidationType(props) {
11270
12380
  });
11271
12381
  }
11272
12382
 
11273
- function ValuesGroups(field, editField, getService) {
12383
+ function OptionsGroups(field, editField, getService) {
11274
12384
  const {
11275
- type,
11276
- id: fieldId
12385
+ type
11277
12386
  } = field;
11278
12387
  const formFields = getService('formFields');
11279
12388
  const fieldDefinition = formFields.get(type).config;
11280
- if (!VALUES_INPUTS.includes(type) && !hasValuesGroupsConfigured(fieldDefinition)) {
12389
+ if (!OPTIONS_INPUTS.includes(type) && !hasOptionsGroupsConfigured(fieldDefinition)) {
11281
12390
  return [];
11282
12391
  }
11283
12392
  const context = {
11284
12393
  editField,
11285
12394
  field
11286
12395
  };
11287
- const valuesSourceId = `${fieldId}-valuesSource`;
12396
+ const id = 'valuesSource';
11288
12397
 
11289
12398
  /**
11290
12399
  * @type {Array<Group|ListGroup>}
11291
12400
  */
11292
12401
  const groups = [{
11293
- id: valuesSourceId,
12402
+ id,
11294
12403
  label: 'Options source',
11295
12404
  tooltip: getValuesTooltip(),
11296
12405
  component: Group,
11297
- entries: ValuesSourceSelectEntry({
12406
+ entries: OptionsSourceSelectEntry({
11298
12407
  ...context,
11299
- id: valuesSourceId
12408
+ id
11300
12409
  })
11301
12410
  }];
11302
- const valuesSource = getValuesSource(field);
11303
- if (valuesSource === VALUES_SOURCES.INPUT) {
11304
- const dynamicValuesId = `${fieldId}-dynamicValues`;
12411
+ const valuesSource = getOptionsSource(field);
12412
+ if (valuesSource === OPTIONS_SOURCES.INPUT) {
12413
+ const id = 'dynamicOptions';
11305
12414
  groups.push({
11306
- id: dynamicValuesId,
12415
+ id,
11307
12416
  label: 'Dynamic options',
11308
12417
  component: Group,
11309
- entries: InputKeyValuesSourceEntry({
12418
+ entries: InputKeyOptionsSourceEntry({
11310
12419
  ...context,
11311
- id: dynamicValuesId
12420
+ id
11312
12421
  })
11313
12422
  });
11314
- } else if (valuesSource === VALUES_SOURCES.STATIC) {
11315
- const staticValuesId = `${fieldId}-staticValues`;
12423
+ } else if (valuesSource === OPTIONS_SOURCES.STATIC) {
12424
+ const id = 'staticOptions';
11316
12425
  groups.push({
11317
- id: staticValuesId,
12426
+ id,
11318
12427
  label: 'Static options',
11319
12428
  component: ListGroup,
11320
- ...StaticValuesSourceEntry({
12429
+ ...StaticOptionsSourceEntry({
11321
12430
  ...context,
11322
- id: staticValuesId
12431
+ id
11323
12432
  })
11324
12433
  });
11325
- } else if (valuesSource === VALUES_SOURCES.EXPRESSION) {
11326
- const valuesExpressionId = `${fieldId}-valuesExpression`;
12434
+ } else if (valuesSource === OPTIONS_SOURCES.EXPRESSION) {
12435
+ const id = 'optionsExpression';
11327
12436
  groups.push({
11328
- id: valuesExpressionId,
12437
+ id,
11329
12438
  label: 'Options expression',
11330
12439
  component: Group,
11331
- entries: ValuesExpressionEntry({
12440
+ entries: OptionsExpressionEntry({
11332
12441
  ...context,
11333
- id: valuesExpressionId
12442
+ id
11334
12443
  })
11335
12444
  });
11336
12445
  }
@@ -11380,7 +12489,7 @@ function CustomPropertiesGroup(field, editField) {
11380
12489
  event.stopPropagation();
11381
12490
  return editField(field, ['properties'], removeKey(properties, key));
11382
12491
  };
11383
- const id = `${field.id}-property-${index}`;
12492
+ const id = `property-${index}`;
11384
12493
  return {
11385
12494
  autoFocusEntry: id + '-key',
11386
12495
  entries: CustomValueEntry({
@@ -11429,10 +12538,16 @@ function removeKey(properties, oldKey) {
11429
12538
  }, {});
11430
12539
  }
11431
12540
 
11432
- function AppearanceGroup(field, editField) {
12541
+ function AppearanceGroup(field, editField, getService) {
11433
12542
  const entries = [...AdornerEntry({
11434
12543
  field,
11435
12544
  editField
12545
+ }), ...GroupAppearanceEntry({
12546
+ field,
12547
+ editField
12548
+ }), ...LayouterAppearanceEntry({
12549
+ field,
12550
+ editField
11436
12551
  })];
11437
12552
  if (!entries.length) {
11438
12553
  return null;
@@ -11483,6 +12598,55 @@ function ConditionGroup(field, editField) {
11483
12598
  };
11484
12599
  }
11485
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
+
11486
12650
  class PropertiesProvider {
11487
12651
  constructor(propertiesPanel, injector) {
11488
12652
  this._injector = injector;
@@ -11518,7 +12682,7 @@ class PropertiesProvider {
11518
12682
  return groups;
11519
12683
  }
11520
12684
  const getService = (type, strict = true) => this._injector.get(type, strict);
11521
- 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);
11522
12686
  this._filterVisibleEntries(groups, field, getService);
11523
12687
 
11524
12688
  // contract: if a group has no entries or items, it should not be displayed at all
@@ -11584,6 +12748,58 @@ var RenderInjectionModule = {
11584
12748
  renderInjector: ['type', RenderInjector]
11585
12749
  };
11586
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
+
11587
12803
  class EditorTemplating {
11588
12804
  // same rules as viewer templating
11589
12805
  isTemplate(value) {
@@ -11855,7 +13071,7 @@ class FormEditor {
11855
13071
  * @internal
11856
13072
  */
11857
13073
  _getModules() {
11858
- return [ModelingModule, EditorActionsModule, DraggingModule, KeyboardModule, SelectionModule, PaletteModule, ExpressionLanguageModule, MarkdownModule, PropertiesPanelModule, RenderInjectionModule];
13074
+ return [ModelingModule, EditorActionsModule, DraggingModule, KeyboardModule, SelectionModule, PaletteModule, ExpressionLanguageModule, MarkdownModule, PropertiesPanelModule, RenderInjectionModule, RepeatRenderManagerModule];
11859
13075
  }
11860
13076
 
11861
13077
  /**