@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.cjs CHANGED
@@ -437,7 +437,7 @@ EventBus.prototype._invokeListener = function (event, args, listener) {
437
437
  * * after: [ 1500, 1500, (new=1300), 1000, 1000, (new=1000) ]
438
438
  *
439
439
  * @param {string} event
440
- * @param {EventBusListener} listener
440
+ * @param {EventBusListener} newListener
441
441
  */
442
442
  EventBus.prototype._addListener = function (event, newListener) {
443
443
  var listener = this._getListeners(event),
@@ -765,6 +765,28 @@ function createEmptyImage() {
765
765
  return img;
766
766
  }
767
767
 
768
+ function EditorIFrame(props) {
769
+ const {
770
+ field
771
+ } = props;
772
+ const Icon = formJsViewer.iconsByType(field.type);
773
+ return jsxRuntime.jsx("div", {
774
+ class: editorFormFieldClasses(field.type),
775
+ children: jsxRuntime.jsx("div", {
776
+ class: "fjs-iframe-placeholder",
777
+ children: jsxRuntime.jsxs("p", {
778
+ class: "fjs-iframe-placeholder-text",
779
+ children: [jsxRuntime.jsx(Icon, {
780
+ width: "32",
781
+ height: "24",
782
+ viewBox: "0 0 56 56"
783
+ }), "iFrame"]
784
+ })
785
+ })
786
+ });
787
+ }
788
+ EditorIFrame.config = formJsViewer.IFrame.config;
789
+
768
790
  const DragAndDropContext = preact.createContext({
769
791
  drake: null
770
792
  });
@@ -810,15 +832,15 @@ function useDebounce(fn, dependencies = []) {
810
832
  return callback;
811
833
  }
812
834
 
813
- var _path$4;
814
- 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); }
835
+ var _path$5;
836
+ 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); }
815
837
  var SvgClose = function SvgClose(props) {
816
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$4({
838
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$5({
817
839
  xmlns: "http://www.w3.org/2000/svg",
818
840
  width: 16,
819
841
  height: 16,
820
842
  fill: "currentColor"
821
- }, props), _path$4 || (_path$4 = /*#__PURE__*/React__namespace.createElement("path", {
843
+ }, props), _path$5 || (_path$5 = /*#__PURE__*/React__namespace.createElement("path", {
822
844
  fillRule: "evenodd",
823
845
  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",
824
846
  clipRule: "evenodd"
@@ -826,10 +848,10 @@ var SvgClose = function SvgClose(props) {
826
848
  };
827
849
  var CloseIcon = SvgClose;
828
850
 
829
- var _path$3, _path2$1;
830
- 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); }
851
+ var _path$4, _path2$1;
852
+ 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); }
831
853
  var SvgDelete = function SvgDelete(props) {
832
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$3({
854
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$4({
833
855
  xmlns: "http://www.w3.org/2000/svg",
834
856
  width: 16,
835
857
  height: 16,
@@ -850,7 +872,7 @@ var SvgDelete = function SvgDelete(props) {
850
872
  mixBlendMode: "multiply"
851
873
  },
852
874
  transform: "translate(.536)"
853
- }), _path$3 || (_path$3 = /*#__PURE__*/React__namespace.createElement("path", {
875
+ }), _path$4 || (_path$4 = /*#__PURE__*/React__namespace.createElement("path", {
854
876
  fill: "currentcolor",
855
877
  d: "M7.536 6h-1v6h1V6Zm3 0h-1v6h1V6Z"
856
878
  })), _path2$1 || (_path2$1 = /*#__PURE__*/React__namespace.createElement("path", {
@@ -860,17 +882,17 @@ var SvgDelete = function SvgDelete(props) {
860
882
  };
861
883
  var DeleteIcon$1 = SvgDelete;
862
884
 
863
- var _path$2;
864
- function _extends$2() { _extends$2 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$2.apply(this, arguments); }
885
+ var _path$3;
886
+ 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); }
865
887
  var SvgDraggable = function SvgDraggable(props) {
866
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$2({
888
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$3({
867
889
  xmlns: "http://www.w3.org/2000/svg",
868
890
  xmlSpace: "preserve",
869
891
  width: 16,
870
892
  height: 16,
871
893
  fill: "currentcolor",
872
894
  viewBox: "0 0 32 32"
873
- }, props), _path$2 || (_path$2 = /*#__PURE__*/React__namespace.createElement("path", {
895
+ }, props), _path$3 || (_path$3 = /*#__PURE__*/React__namespace.createElement("path", {
874
896
  d: "M10 6h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4z"
875
897
  })), /*#__PURE__*/React__namespace.createElement("path", {
876
898
  d: "M0 0h32v32H0z",
@@ -881,30 +903,30 @@ var SvgDraggable = function SvgDraggable(props) {
881
903
  };
882
904
  var DraggableIcon = SvgDraggable;
883
905
 
884
- var _path$1;
885
- 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); }
906
+ var _path$2;
907
+ 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); }
886
908
  var SvgSearch = function SvgSearch(props) {
887
- return /*#__PURE__*/React__namespace.createElement("svg", _extends$1({
909
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$2({
888
910
  xmlns: "http://www.w3.org/2000/svg",
889
911
  width: 15,
890
912
  height: 15,
891
913
  fill: "none"
892
- }, props), _path$1 || (_path$1 = /*#__PURE__*/React__namespace.createElement("path", {
914
+ }, props), _path$2 || (_path$2 = /*#__PURE__*/React__namespace.createElement("path", {
893
915
  fill: "currentColor",
894
916
  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"
895
917
  })));
896
918
  };
897
919
  var SearchIcon = SvgSearch;
898
920
 
899
- var _path, _rect, _mask, _path2, _path3, _path4, _path5, _path6;
900
- 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); }
921
+ var _path$1, _rect, _mask, _path2, _path3, _path4, _path5, _path6;
922
+ 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); }
901
923
  var SvgEmptyForm = function SvgEmptyForm(props) {
902
- return /*#__PURE__*/React__namespace.createElement("svg", _extends({
924
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$1({
903
925
  xmlns: "http://www.w3.org/2000/svg",
904
926
  width: 126,
905
927
  height: 96,
906
928
  fill: "none"
907
- }, props), _path || (_path = /*#__PURE__*/React__namespace.createElement("path", {
929
+ }, props), _path$1 || (_path$1 = /*#__PURE__*/React__namespace.createElement("path", {
908
930
  fill: "#FF832B",
909
931
  fillRule: "evenodd",
910
932
  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",
@@ -999,7 +1021,71 @@ function EditorText(props) {
999
1021
  }
1000
1022
  EditorText.config = formJsViewer.Text.config;
1001
1023
 
1002
- const editorFormFields = [EditorText];
1024
+ function EditorTable(props) {
1025
+ const {
1026
+ columnsExpression,
1027
+ columns,
1028
+ id,
1029
+ label
1030
+ } = props.field;
1031
+ const shouldUseMockColumns = typeof columnsExpression === 'string' && columnsExpression.length > 0 || Array.isArray(columns) && columns.length === 0;
1032
+ const editorColumns = shouldUseMockColumns ? [{
1033
+ key: '1',
1034
+ label: 'Column 1'
1035
+ }, {
1036
+ key: '2',
1037
+ label: 'Column 2'
1038
+ }, {
1039
+ key: '3',
1040
+ label: 'Column 3'
1041
+ }] : columns;
1042
+ const prefixId = `fjs-form-${id}`;
1043
+ return jsxRuntime.jsxs("div", {
1044
+ class: editorFormFieldClasses('table', {
1045
+ disabled: true
1046
+ }),
1047
+ children: [jsxRuntime.jsx(formJsViewer.Label, {
1048
+ id: prefixId,
1049
+ label: label
1050
+ }), jsxRuntime.jsx("div", {
1051
+ class: "fjs-table-middle-container",
1052
+ children: jsxRuntime.jsx("div", {
1053
+ class: "fjs-table-inner-container",
1054
+ children: jsxRuntime.jsxs("table", {
1055
+ class: classnames('fjs-table', 'fjs-disabled'),
1056
+ id: prefixId,
1057
+ children: [jsxRuntime.jsx("thead", {
1058
+ class: "fjs-table-head",
1059
+ children: jsxRuntime.jsx("tr", {
1060
+ class: "fjs-table-tr",
1061
+ children: editorColumns.map(({
1062
+ key,
1063
+ label
1064
+ }) => jsxRuntime.jsx("th", {
1065
+ class: "fjs-table-th",
1066
+ children: label
1067
+ }, key))
1068
+ })
1069
+ }), jsxRuntime.jsx("tbody", {
1070
+ class: "fjs-table-body",
1071
+ children: jsxRuntime.jsx("tr", {
1072
+ class: "fjs-table-tr",
1073
+ children: editorColumns.map(({
1074
+ key
1075
+ }) => jsxRuntime.jsx("td", {
1076
+ class: "fjs-table-td",
1077
+ children: "Content"
1078
+ }, key))
1079
+ })
1080
+ })]
1081
+ })
1082
+ })
1083
+ })]
1084
+ });
1085
+ }
1086
+ EditorTable.config = formJsViewer.Table.config;
1087
+
1088
+ const editorFormFields = [EditorIFrame, EditorText, EditorTable];
1003
1089
 
1004
1090
  class EditorFormFields extends formJsViewer.FormFields {
1005
1091
  constructor() {
@@ -1298,6 +1384,9 @@ const PALETTE_GROUPS = [{
1298
1384
  }, {
1299
1385
  label: 'Presentation',
1300
1386
  id: 'presentation'
1387
+ }, {
1388
+ label: 'Containers',
1389
+ id: 'container'
1301
1390
  }, {
1302
1391
  label: 'Action',
1303
1392
  id: 'action'
@@ -1536,7 +1625,7 @@ class Dragging {
1536
1625
  }
1537
1626
 
1538
1627
  /**
1539
- * Calculcates position in form schema given the dropped place.
1628
+ * Calculates position in form schema given the dropped place.
1540
1629
  *
1541
1630
  * @param { FormRow } targetRow
1542
1631
  * @param { any } targetFormField
@@ -1596,13 +1685,18 @@ class Dragging {
1596
1685
  if (targetParentPath.join('.') !== currentParentPath.join('.')) {
1597
1686
  const isDropAllowedByPathRegistry = this._pathRegistry.executeRecursivelyOnFields(formField, ({
1598
1687
  field,
1599
- isClosed
1688
+ isClosed,
1689
+ isRepeatable
1600
1690
  }) => {
1601
1691
  const options = {
1602
1692
  cutoffNode: currentParentFormField.id
1603
1693
  };
1604
1694
  const fieldPath = this._pathRegistry.getValuePath(field, options);
1605
- return this._pathRegistry.canClaimPath([...targetParentPath, ...fieldPath], isClosed);
1695
+ return this._pathRegistry.canClaimPath([...targetParentPath, ...fieldPath], {
1696
+ isClosed,
1697
+ isRepeatable,
1698
+ knownAncestorIds: formJsViewer.getAncestryList(targetParentId, this._formFieldRegistry)
1699
+ });
1606
1700
  });
1607
1701
  if (!isDropAllowedByPathRegistry) {
1608
1702
  return 'Drop not allowed by path registry';
@@ -1968,24 +2062,40 @@ function ContextPad(props) {
1968
2062
  children: props.children
1969
2063
  });
1970
2064
  }
1971
- function Empty() {
2065
+ function Empty(props) {
2066
+ if (props.field.type === 'default') {
2067
+ return jsxRuntime.jsx("div", {
2068
+ class: "fjs-empty-editor",
2069
+ children: jsxRuntime.jsxs("div", {
2070
+ class: "fjs-empty-editor-card",
2071
+ children: [jsxRuntime.jsx(EmptyFormIcon, {}), jsxRuntime.jsx("h2", {
2072
+ children: "Build your form"
2073
+ }), jsxRuntime.jsx("span", {
2074
+ children: "Drag and drop components here to start designing."
2075
+ }), jsxRuntime.jsx("span", {
2076
+ children: "Use the preview window to test your form."
2077
+ })]
2078
+ })
2079
+ });
2080
+ }
2081
+ if (props.field.type === 'group') {
2082
+ return jsxRuntime.jsx("div", {
2083
+ class: "fjs-empty-component",
2084
+ children: jsxRuntime.jsx("span", {
2085
+ children: "Drag and drop components here."
2086
+ })
2087
+ });
2088
+ }
2089
+ if (props.field.type === 'dynamiclist') {
2090
+ return jsxRuntime.jsx("div", {
2091
+ class: "fjs-empty-component",
2092
+ children: jsxRuntime.jsxs("span", {
2093
+ children: ["Drag and drop components here ", jsxRuntime.jsx("br", {}), " to create a repeatable list item."]
2094
+ })
2095
+ });
2096
+ }
1972
2097
  return null;
1973
2098
  }
1974
- function EmptyRoot(props) {
1975
- return jsxRuntime.jsx("div", {
1976
- class: "fjs-empty-editor",
1977
- children: jsxRuntime.jsxs("div", {
1978
- class: "fjs-empty-editor-card",
1979
- children: [jsxRuntime.jsx(EmptyFormIcon, {}), jsxRuntime.jsx("h2", {
1980
- children: "Build your form"
1981
- }), jsxRuntime.jsx("span", {
1982
- children: "Drag and drop components here to start designing."
1983
- }), jsxRuntime.jsx("span", {
1984
- children: "Use the preview window to test your form."
1985
- })]
1986
- })
1987
- });
1988
- }
1989
2099
  function Element$1(props) {
1990
2100
  const eventBus = useService$1('eventBus'),
1991
2101
  formEditor = useService$1('formEditor'),
@@ -1994,8 +2104,7 @@ function Element$1(props) {
1994
2104
  modeling = useService$1('modeling'),
1995
2105
  selection = useService$1('selection');
1996
2106
  const {
1997
- hoveredId,
1998
- setHoveredId
2107
+ hoverInfo
1999
2108
  } = hooks.useContext(formJsViewer.FormRenderContext);
2000
2109
  const {
2001
2110
  field
@@ -2006,6 +2115,7 @@ function Element$1(props) {
2006
2115
  showOutline
2007
2116
  } = field;
2008
2117
  const ref = hooks.useRef();
2118
+ const [hovered, setHovered] = hooks.useState(false);
2009
2119
  function scrollIntoView({
2010
2120
  selection
2011
2121
  }) {
@@ -2034,19 +2144,23 @@ function Element$1(props) {
2034
2144
  // properly focus on field
2035
2145
  ref.current.focus();
2036
2146
  }
2037
- const classes = [];
2038
- if (props.class) {
2039
- classes.push(...props.class.split(' '));
2040
- }
2041
- if (selection.isSelected(field)) {
2042
- classes.push('fjs-editor-selected');
2043
- }
2044
- if (showOutline) {
2045
- classes.push('fjs-outlined');
2046
- }
2047
- if (hoveredId === field.id) {
2048
- classes.push('fjs-editor-hovered');
2049
- }
2147
+ const isSelected = selection.isSelected(field);
2148
+ const classString = hooks.useMemo(() => {
2149
+ const classes = [];
2150
+ if (props.class) {
2151
+ classes.push(...props.class.split(' '));
2152
+ }
2153
+ if (isSelected) {
2154
+ classes.push('fjs-editor-selected');
2155
+ }
2156
+ if (showOutline) {
2157
+ classes.push('fjs-outlined');
2158
+ }
2159
+ if (hovered) {
2160
+ classes.push('fjs-editor-hovered');
2161
+ }
2162
+ return classes.join(' ');
2163
+ }, [hovered, isSelected, props.class, showOutline]);
2050
2164
  const onRemove = event => {
2051
2165
  event.stopPropagation();
2052
2166
  const parentField = formFieldRegistry.get(field._parent);
@@ -2060,15 +2174,18 @@ function Element$1(props) {
2060
2174
  }
2061
2175
  };
2062
2176
  return jsxRuntime.jsxs("div", {
2063
- class: classes.join(' '),
2177
+ class: classString,
2064
2178
  "data-id": id,
2065
2179
  "data-field-type": type,
2066
2180
  tabIndex: type === 'default' ? -1 : 0,
2067
2181
  onClick: onClick,
2068
2182
  onKeyPress: onKeyPress,
2069
2183
  onMouseOver: e => {
2070
- // @ts-ignore
2071
- setHoveredId(field.id);
2184
+ if (hoverInfo.cleanup) {
2185
+ hoverInfo.cleanup();
2186
+ }
2187
+ setHovered(true);
2188
+ hoverInfo.cleanup = () => setHovered(false);
2072
2189
  e.stopPropagation();
2073
2190
  },
2074
2191
  ref: ref,
@@ -2139,6 +2256,7 @@ function Row(props) {
2139
2256
  children: jsxRuntime.jsx(DraggableIcon, {})
2140
2257
  }), jsxRuntime.jsx("div", {
2141
2258
  class: classes.join(' '),
2259
+ style: props.style,
2142
2260
  "data-row-id": id,
2143
2261
  children: props.children
2144
2262
  })]
@@ -2244,17 +2362,14 @@ function FormEditor$1(props) {
2244
2362
  // keep deprecated event to ensure backward compatibility
2245
2363
  eventBus.fire('formEditor.rendered');
2246
2364
  }, []);
2247
- const [hoveredId, setHoveredId] = hooks.useState(null);
2248
2365
  const formRenderContext = hooks.useMemo(() => ({
2249
2366
  Children,
2250
2367
  Column,
2251
2368
  Element: Element$1,
2252
2369
  Empty,
2253
- EmptyRoot,
2254
2370
  Row,
2255
- hoveredId,
2256
- setHoveredId
2257
- }), [hoveredId]);
2371
+ hoverInfo: {}
2372
+ }), []);
2258
2373
  const formContext = hooks.useMemo(() => ({
2259
2374
  getService(type, strict = true) {
2260
2375
  // TODO(philippfromme): clean up
@@ -2615,7 +2730,8 @@ EditorActions.prototype.trigger = function (action, opts) {
2615
2730
  * The key of the object will be the name of the action.
2616
2731
  *
2617
2732
  * @example
2618
- * ´´´
2733
+ *
2734
+ * ```javascript
2619
2735
  * var actions = {
2620
2736
  * spaceTool: function() {
2621
2737
  * spaceTool.activateSelection();
@@ -2628,7 +2744,7 @@ EditorActions.prototype.trigger = function (action, opts) {
2628
2744
  * editorActions.register(actions);
2629
2745
  *
2630
2746
  * editorActions.isRegistered('spaceTool'); // true
2631
- * ´´´
2747
+ * ```
2632
2748
  *
2633
2749
  * @param {Object} actions
2634
2750
  */
@@ -2767,6 +2883,7 @@ function hasModifier(event) {
2767
2883
 
2768
2884
  /**
2769
2885
  * @param {KeyboardEvent} event
2886
+ * @return {boolean}
2770
2887
  */
2771
2888
  function isCmd(event) {
2772
2889
  // ensure we don't react to AltGr
@@ -2782,6 +2899,7 @@ function isCmd(event) {
2782
2899
  *
2783
2900
  * @param {string|string[]} keys
2784
2901
  * @param {KeyboardEvent} event
2902
+ * @return {boolean}
2785
2903
  */
2786
2904
  function isKey(keys, event) {
2787
2905
  keys = minDash.isArray(keys) ? keys : [keys];
@@ -2794,21 +2912,39 @@ function isKey(keys, event) {
2794
2912
  function isShift(event) {
2795
2913
  return event.shiftKey;
2796
2914
  }
2915
+
2916
+ /**
2917
+ * @param {KeyboardEvent} event
2918
+ */
2797
2919
  function isCopy(event) {
2798
2920
  return isCmd(event) && isKey(KEYS_COPY, event);
2799
2921
  }
2922
+
2923
+ /**
2924
+ * @param {KeyboardEvent} event
2925
+ */
2800
2926
  function isPaste(event) {
2801
2927
  return isCmd(event) && isKey(KEYS_PASTE, event);
2802
2928
  }
2929
+
2930
+ /**
2931
+ * @param {KeyboardEvent} event
2932
+ */
2803
2933
  function isUndo(event) {
2804
2934
  return isCmd(event) && !isShift(event) && isKey(KEYS_UNDO, event);
2805
2935
  }
2936
+
2937
+ /**
2938
+ * @param {KeyboardEvent} event
2939
+ */
2806
2940
  function isRedo(event) {
2807
2941
  return isCmd(event) && (isKey(KEYS_REDO, event) || isKey(KEYS_UNDO, event) && isShift(event));
2808
2942
  }
2809
2943
 
2810
2944
  /**
2811
2945
  * @typedef {import('../../core/EventBus').default} EventBus
2946
+ *
2947
+ * @typedef {({ keyEvent: KeyboardEvent }) => any} Listener
2812
2948
  */
2813
2949
 
2814
2950
  var KEYDOWN_EVENT = 'keyboard.keydown',
@@ -2838,6 +2974,7 @@ var DEFAULT_PRIORITY$2 = 1000;
2838
2974
  * `keyboard.bindTo` configuration option.
2839
2975
  *
2840
2976
  * @param {Object} config
2977
+ * @param {EventTarget} [config.bindTo]
2841
2978
  * @param {EventBus} eventBus
2842
2979
  */
2843
2980
  function Keyboard(config, eventBus) {
@@ -2904,6 +3041,12 @@ Keyboard.prototype._getAllowedModifiers = function (element) {
2904
3041
  }
2905
3042
  return modifierContainer.getAttribute(HANDLE_MODIFIER_ATTRIBUTE).split(',');
2906
3043
  };
3044
+
3045
+ /**
3046
+ * Bind keyboard events to the given DOM node.
3047
+ *
3048
+ * @param {EventTarget} node
3049
+ */
2907
3050
  Keyboard.prototype.bind = function (node) {
2908
3051
  // make sure that the keyboard is only bound once to the DOM
2909
3052
  this.unbind();
@@ -2914,6 +3057,10 @@ Keyboard.prototype.bind = function (node) {
2914
3057
  minDom.event.bind(node, 'keyup', this._keyupHandler);
2915
3058
  this._fire('bind');
2916
3059
  };
3060
+
3061
+ /**
3062
+ * @return {EventTarget}
3063
+ */
2917
3064
  Keyboard.prototype.getBinding = function () {
2918
3065
  return this._node;
2919
3066
  };
@@ -2928,6 +3075,10 @@ Keyboard.prototype.unbind = function () {
2928
3075
  }
2929
3076
  this._node = null;
2930
3077
  };
3078
+
3079
+ /**
3080
+ * @param {string} event
3081
+ */
2931
3082
  Keyboard.prototype._fire = function (event) {
2932
3083
  this._eventBus.fire('keyboard.' + event, {
2933
3084
  node: this._node
@@ -2940,8 +3091,8 @@ Keyboard.prototype._fire = function (event) {
2940
3091
  * provided, the default value of 1000 is used.
2941
3092
  *
2942
3093
  * @param {number} [priority]
2943
- * @param {Function} listener
2944
- * @param {string} type
3094
+ * @param {Listener} listener
3095
+ * @param {string} [type='keyboard.keydown']
2945
3096
  */
2946
3097
  Keyboard.prototype.addListener = function (priority, listener, type) {
2947
3098
  if (minDash.isFunction(priority)) {
@@ -2951,6 +3102,13 @@ Keyboard.prototype.addListener = function (priority, listener, type) {
2951
3102
  }
2952
3103
  this._eventBus.on(type || KEYDOWN_EVENT, priority, listener);
2953
3104
  };
3105
+
3106
+ /**
3107
+ * Remove a listener function.
3108
+ *
3109
+ * @param {Listener} listener
3110
+ * @param {string} [type='keyboard.keydown']
3111
+ */
2954
3112
  Keyboard.prototype.removeListener = function (listener, type) {
2955
3113
  this._eventBus.off(type || KEYDOWN_EVENT, listener);
2956
3114
  };
@@ -3411,9 +3569,14 @@ class MoveFormFieldHandler {
3411
3569
  // (7) Reregister form field (and children) from path registry
3412
3570
  this._pathRegistry.executeRecursivelyOnFields(formField, ({
3413
3571
  field,
3414
- isClosed
3572
+ isClosed,
3573
+ isRepeatable
3415
3574
  }) => {
3416
- this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), isClosed);
3575
+ this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), {
3576
+ isClosed,
3577
+ isRepeatable,
3578
+ claimerId: field.id
3579
+ });
3417
3580
  });
3418
3581
  }
3419
3582
 
@@ -3544,7 +3707,10 @@ class UpdateKeyClaimHandler {
3544
3707
  };
3545
3708
  const valuePath = this._pathRegistry.getValuePath(formField, options);
3546
3709
  if (claiming) {
3547
- this._pathRegistry.claimPath(valuePath, true);
3710
+ this._pathRegistry.claimPath(valuePath, {
3711
+ isClosed: true,
3712
+ claimerId: formField.id
3713
+ });
3548
3714
  } else {
3549
3715
  this._pathRegistry.unclaimPath(valuePath);
3550
3716
  }
@@ -3555,12 +3721,16 @@ class UpdateKeyClaimHandler {
3555
3721
  revert(context) {
3556
3722
  const {
3557
3723
  claiming,
3724
+ formField,
3558
3725
  valuePath
3559
3726
  } = context;
3560
3727
  if (claiming) {
3561
3728
  this._pathRegistry.unclaimPath(valuePath);
3562
3729
  } else {
3563
- this._pathRegistry.claimPath(valuePath, true);
3730
+ this._pathRegistry.claimPath(valuePath, {
3731
+ isClosed: true,
3732
+ claimerId: formField.id
3733
+ });
3564
3734
  }
3565
3735
  }
3566
3736
  }
@@ -3589,24 +3759,34 @@ class UpdatePathClaimHandler {
3589
3759
  if (claiming) {
3590
3760
  this._pathRegistry.executeRecursivelyOnFields(formField, ({
3591
3761
  field,
3592
- isClosed
3762
+ isClosed,
3763
+ isRepeatable
3593
3764
  }) => {
3594
3765
  const valuePath = this._pathRegistry.getValuePath(field, options);
3595
3766
  valuePaths.push({
3596
3767
  valuePath,
3597
- isClosed
3768
+ isClosed,
3769
+ isRepeatable,
3770
+ claimerId: field.id
3771
+ });
3772
+ this._pathRegistry.claimPath(valuePath, {
3773
+ isClosed,
3774
+ isRepeatable,
3775
+ claimerId: field.id
3598
3776
  });
3599
- this._pathRegistry.claimPath(valuePath, isClosed);
3600
3777
  });
3601
3778
  } else {
3602
3779
  this._pathRegistry.executeRecursivelyOnFields(formField, ({
3603
3780
  field,
3604
- isClosed
3781
+ isClosed,
3782
+ isRepeatable
3605
3783
  }) => {
3606
3784
  const valuePath = this._pathRegistry.getValuePath(field, options);
3607
3785
  valuePaths.push({
3608
3786
  valuePath,
3609
- isClosed
3787
+ isClosed,
3788
+ isRepeatable,
3789
+ claimerId: field.id
3610
3790
  });
3611
3791
  this._pathRegistry.unclaimPath(valuePath);
3612
3792
  });
@@ -3629,9 +3809,15 @@ class UpdatePathClaimHandler {
3629
3809
  } else {
3630
3810
  valuePaths.forEach(({
3631
3811
  valuePath,
3632
- isClosed
3812
+ isClosed,
3813
+ isRepeatable,
3814
+ claimerId
3633
3815
  }) => {
3634
- this._pathRegistry.claimPath(valuePath, isClosed);
3816
+ this._pathRegistry.claimPath(valuePath, {
3817
+ isClosed,
3818
+ isRepeatable,
3819
+ claimerId
3820
+ });
3635
3821
  });
3636
3822
  }
3637
3823
  }
@@ -4164,7 +4350,7 @@ class ValidateBehavior extends CommandInterceptor {
4164
4350
  }
4165
4351
  ValidateBehavior.$inject = ['eventBus'];
4166
4352
 
4167
- class ValuesSourceBehavior extends CommandInterceptor {
4353
+ class OptionsSourceBehavior extends CommandInterceptor {
4168
4354
  constructor(eventBus) {
4169
4355
  super(eventBus);
4170
4356
 
@@ -4184,15 +4370,15 @@ class ValuesSourceBehavior extends CommandInterceptor {
4184
4370
  }
4185
4371
 
4186
4372
  // clean up value sources that are not to going to be set
4187
- Object.values(formJsViewer.VALUES_SOURCES).forEach(source => {
4188
- const path = formJsViewer.VALUES_SOURCES_PATHS[source];
4373
+ Object.values(formJsViewer.OPTIONS_SOURCES).forEach(source => {
4374
+ const path = formJsViewer.OPTIONS_SOURCES_PATHS[source];
4189
4375
  if (minDash.get(properties, path) == undefined) {
4190
- newProperties[formJsViewer.VALUES_SOURCES_PATHS[source]] = undefined;
4376
+ newProperties[formJsViewer.OPTIONS_SOURCES_PATHS[source]] = undefined;
4191
4377
  }
4192
4378
  });
4193
4379
 
4194
4380
  // clean up default value
4195
- if (minDash.get(properties, formJsViewer.VALUES_SOURCES_PATHS[formJsViewer.VALUES_SOURCES.EXPRESSION]) !== undefined || minDash.get(properties, formJsViewer.VALUES_SOURCES_PATHS[formJsViewer.VALUES_SOURCES.INPUT]) !== undefined) {
4381
+ if (minDash.get(properties, formJsViewer.OPTIONS_SOURCES_PATHS[formJsViewer.OPTIONS_SOURCES.EXPRESSION]) !== undefined || minDash.get(properties, formJsViewer.OPTIONS_SOURCES_PATHS[formJsViewer.OPTIONS_SOURCES.INPUT]) !== undefined) {
4196
4382
  newProperties['defaultValue'] = undefined;
4197
4383
  }
4198
4384
  context.properties = {
@@ -4202,23 +4388,85 @@ class ValuesSourceBehavior extends CommandInterceptor {
4202
4388
  }, true);
4203
4389
  }
4204
4390
  }
4205
- ValuesSourceBehavior.$inject = ['eventBus'];
4391
+ OptionsSourceBehavior.$inject = ['eventBus'];
4206
4392
 
4207
4393
  // helper ///////////////////
4208
4394
 
4209
4395
  function isValuesSourceUpdate(properties) {
4210
- return Object.values(formJsViewer.VALUES_SOURCES_PATHS).some(path => {
4396
+ return Object.values(formJsViewer.OPTIONS_SOURCES_PATHS).some(path => {
4211
4397
  return minDash.get(properties, path) !== undefined;
4212
4398
  });
4213
4399
  }
4214
4400
 
4401
+ const COLUMNS_SOURCE_PROPERTIES = {
4402
+ columns: 'columns',
4403
+ columnsExpression: 'columnsExpression'
4404
+ };
4405
+ class ColumnsSourceBehavior extends CommandInterceptor {
4406
+ constructor(eventBus) {
4407
+ super(eventBus);
4408
+ this.preExecute('formField.edit', function (context) {
4409
+ const {
4410
+ properties,
4411
+ oldProperties
4412
+ } = context;
4413
+ const isColumnSourceUpdate = Object.values(COLUMNS_SOURCE_PROPERTIES).some(path => {
4414
+ return minDash.get(properties, [path]) !== undefined;
4415
+ });
4416
+ if (!isColumnSourceUpdate) {
4417
+ return;
4418
+ }
4419
+ const columns = minDash.get(properties, [COLUMNS_SOURCE_PROPERTIES.columns]);
4420
+ const oldColumns = minDash.get(oldProperties, [COLUMNS_SOURCE_PROPERTIES.columns]);
4421
+ const columnsExpression = minDash.get(properties, [COLUMNS_SOURCE_PROPERTIES.columnsExpression]);
4422
+ const oldColumnsExpression = minDash.get(oldProperties, [COLUMNS_SOURCE_PROPERTIES.columnsExpression]);
4423
+ if (minDash.isArray(columns) && !minDash.isDefined(oldColumns)) {
4424
+ context.properties = {
4425
+ ...properties,
4426
+ columnsExpression: undefined
4427
+ };
4428
+ return;
4429
+ }
4430
+ if (minDash.isString(columnsExpression) && !minDash.isString(oldColumnsExpression)) {
4431
+ context.properties = {
4432
+ ...properties,
4433
+ columns: undefined
4434
+ };
4435
+ return;
4436
+ }
4437
+ }, true);
4438
+ }
4439
+ }
4440
+ ColumnsSourceBehavior.$inject = ['eventBus'];
4441
+
4442
+ class TableDataSourceBehavior extends CommandInterceptor {
4443
+ constructor(eventBus) {
4444
+ super(eventBus);
4445
+ this.preExecute('formField.add', function (context) {
4446
+ const {
4447
+ formField
4448
+ } = context;
4449
+ if (minDash.get(formField, ['type']) !== 'table') {
4450
+ return;
4451
+ }
4452
+ context.formField = {
4453
+ ...formField,
4454
+ dataSource: `=${formField.id}`
4455
+ };
4456
+ }, true);
4457
+ }
4458
+ }
4459
+ TableDataSourceBehavior.$inject = ['eventBus'];
4460
+
4215
4461
  var behaviorModule = {
4216
- __init__: ['idBehavior', 'keyBehavior', 'pathBehavior', 'validateBehavior', 'valuesSourceBehavior'],
4462
+ __init__: ['idBehavior', 'keyBehavior', 'pathBehavior', 'validateBehavior', 'optionsSourceBehavior', 'columnsSourceBehavior', 'tableDataSourceBehavior'],
4217
4463
  idBehavior: ['type', IdBehavior],
4218
4464
  keyBehavior: ['type', KeyBehavior],
4219
4465
  pathBehavior: ['type', PathBehavior],
4220
4466
  validateBehavior: ['type', ValidateBehavior],
4221
- valuesSourceBehavior: ['type', ValuesSourceBehavior]
4467
+ optionsSourceBehavior: ['type', OptionsSourceBehavior],
4468
+ columnsSourceBehavior: ['type', ColumnsSourceBehavior],
4469
+ tableDataSourceBehavior: ['type', TableDataSourceBehavior]
4222
4470
  };
4223
4471
 
4224
4472
  /**
@@ -8539,11 +8787,15 @@ function textToLabel(text) {
8539
8787
  }
8540
8788
  return null;
8541
8789
  }
8790
+
8791
+ /**
8792
+ * @param {string} path
8793
+ */
8542
8794
  function isValidDotPath(path) {
8543
8795
  return /^\w+(\.\w+)*$/.test(path);
8544
8796
  }
8545
8797
  const INPUTS = ['checkbox', 'checklist', 'datetime', 'number', 'radio', 'select', 'taglist', 'textfield', 'textarea'];
8546
- const VALUES_INPUTS = ['checklist', 'radio', 'select', 'taglist'];
8798
+ const OPTIONS_INPUTS = ['checklist', 'radio', 'select', 'taglist'];
8547
8799
  function hasEntryConfigured(formFieldDefinition, entryId) {
8548
8800
  const {
8549
8801
  propertiesPanelEntries = []
@@ -8553,7 +8805,7 @@ function hasEntryConfigured(formFieldDefinition, entryId) {
8553
8805
  }
8554
8806
  return propertiesPanelEntries.some(id => id === entryId);
8555
8807
  }
8556
- function hasValuesGroupsConfigured(formFieldDefinition) {
8808
+ function hasOptionsGroupsConfigured(formFieldDefinition) {
8557
8809
  const {
8558
8810
  propertiesPanelEntries = []
8559
8811
  } = formFieldDefinition;
@@ -8563,6 +8815,13 @@ function hasValuesGroupsConfigured(formFieldDefinition) {
8563
8815
  return propertiesPanelEntries.some(id => id === 'values');
8564
8816
  }
8565
8817
 
8818
+ /**
8819
+ * @param {string} path
8820
+ */
8821
+ function hasIntegerPathSegment(path) {
8822
+ return path.split('.').some(segment => /^\d+$/.test(segment));
8823
+ }
8824
+
8566
8825
  function useService (type, strict) {
8567
8826
  const {
8568
8827
  getService
@@ -9068,7 +9327,7 @@ function DefaultOptionEntry(props) {
9068
9327
  function isDefaultVisible(matchers) {
9069
9328
  return field => {
9070
9329
  // Only make default values available when they are statically defined
9071
- if (!INPUTS.includes(type) || VALUES_INPUTS.includes(type) && !field.values) {
9330
+ if (!INPUTS.includes(type) || OPTIONS_INPUTS.includes(type) && !field.values) {
9072
9331
  return false;
9073
9332
  }
9074
9333
  return matchers(field);
@@ -9414,20 +9673,27 @@ function containsSpace(value) {
9414
9673
  function KeyEntry(props) {
9415
9674
  const {
9416
9675
  editField,
9417
- field
9676
+ field,
9677
+ getService
9418
9678
  } = props;
9419
9679
  const entries = [];
9420
9680
  entries.push({
9421
9681
  id: 'key',
9422
- component: Key$1,
9682
+ component: Key$2,
9423
9683
  editField: editField,
9424
9684
  field: field,
9425
9685
  isEdited: isEdited,
9426
- isDefaultVisible: field => INPUTS.includes(field.type)
9686
+ isDefaultVisible: field => {
9687
+ const formFields = getService('formFields');
9688
+ const {
9689
+ config
9690
+ } = formFields.get(field.type);
9691
+ return config.keyed;
9692
+ }
9427
9693
  });
9428
9694
  return entries;
9429
9695
  }
9430
- function Key$1(props) {
9696
+ function Key$2(props) {
9431
9697
  const {
9432
9698
  editField,
9433
9699
  field,
@@ -9449,14 +9715,13 @@ function Key$1(props) {
9449
9715
  if (value === field.key) {
9450
9716
  return null;
9451
9717
  }
9452
- if (minDash.isUndefined(value) || !value.length) {
9718
+ if (!minDash.isString(value) || value.length === 0) {
9453
9719
  return 'Must not be empty.';
9454
9720
  }
9455
- if (value && !isValidDotPath(value)) {
9721
+ if (!isValidDotPath(value)) {
9456
9722
  return 'Must be a variable or a dot separated path.';
9457
9723
  }
9458
- const hasIntegerPathSegment = value.split('.').some(segment => /^\d+$/.test(segment));
9459
- if (hasIntegerPathSegment) {
9724
+ if (hasIntegerPathSegment(value)) {
9460
9725
  return 'Must not contain numerical path segments.';
9461
9726
  }
9462
9727
  const replacements = {
@@ -9469,8 +9734,14 @@ function Key$1(props) {
9469
9734
 
9470
9735
  // unclaim temporarily to avoid self-conflicts
9471
9736
  pathRegistry.unclaimPath(oldPath);
9472
- const canClaim = pathRegistry.canClaimPath(newPath, true);
9473
- pathRegistry.claimPath(oldPath, true);
9737
+ const canClaim = pathRegistry.canClaimPath(newPath, {
9738
+ isClosed: true,
9739
+ claimerId: field.id
9740
+ });
9741
+ pathRegistry.claimPath(oldPath, {
9742
+ isClosed: true,
9743
+ claimerId: field.id
9744
+ });
9474
9745
  return canClaim ? null : 'Must not conflict with other key/path assignments.';
9475
9746
  };
9476
9747
  return TextfieldEntry({
@@ -9489,13 +9760,15 @@ function Key$1(props) {
9489
9760
  function PathEntry(props) {
9490
9761
  const {
9491
9762
  editField,
9492
- field
9763
+ field,
9764
+ getService
9493
9765
  } = props;
9494
9766
  const {
9495
9767
  type
9496
9768
  } = field;
9497
9769
  const entries = [];
9498
- if (type === 'group') {
9770
+ const formFieldDefinition = getService('formFields').get(type);
9771
+ if (formFieldDefinition && formFieldDefinition.config.pathed) {
9499
9772
  entries.push({
9500
9773
  id: 'path',
9501
9774
  component: Path,
@@ -9514,6 +9787,8 @@ function Path(props) {
9514
9787
  } = props;
9515
9788
  const debounce = useService('debounce');
9516
9789
  const pathRegistry = useService('pathRegistry');
9790
+ const fieldConfig = useService('formFields').get(field.type).config;
9791
+ const isRepeating = fieldConfig.repeatable && field.isRepeating;
9517
9792
  const path = ['path'];
9518
9793
  const getValue = () => {
9519
9794
  return minDash.get(field, path, '');
@@ -9525,32 +9800,53 @@ function Path(props) {
9525
9800
  return editField(field, path, value);
9526
9801
  };
9527
9802
  const validate = value => {
9528
- if (!value || value === field.path) {
9803
+ if (!value && isRepeating) {
9804
+ return 'Must not be empty';
9805
+ }
9806
+
9807
+ // Early return for empty value in non-repeating cases or if the field path hasn't changed
9808
+ if (!value && !isRepeating || value === field.path) {
9529
9809
  return null;
9530
9810
  }
9531
- if (value && !isValidDotPath(value)) {
9532
- return 'Must be empty, a variable or a dot separated path';
9811
+
9812
+ // Validate dot-separated path format
9813
+ if (!isValidDotPath(value)) {
9814
+ const msg = isRepeating ? 'Must be a variable or a dot-separated path' : 'Must be empty, a variable or a dot-separated path';
9815
+ return msg;
9533
9816
  }
9534
- const hasIntegerPathSegment = value && value.split('.').some(segment => /^\d+$/.test(segment));
9817
+
9818
+ // Check for integer segments in the path
9819
+ const hasIntegerPathSegment = value.split('.').some(segment => /^\d+$/.test(segment));
9535
9820
  if (hasIntegerPathSegment) {
9536
9821
  return 'Must not contain numerical path segments.';
9537
9822
  }
9538
- const options = value && {
9823
+
9824
+ // Check for path collisions
9825
+ const options = {
9539
9826
  replacements: {
9540
- [field.id]: [value]
9827
+ [field.id]: value.split('.')
9541
9828
  }
9542
- } || {};
9829
+ };
9543
9830
  const canClaim = pathRegistry.executeRecursivelyOnFields(field, ({
9544
9831
  field,
9545
- isClosed
9832
+ isClosed,
9833
+ isRepeatable
9546
9834
  }) => {
9547
9835
  const path = pathRegistry.getValuePath(field, options);
9548
- return pathRegistry.canClaimPath(path, isClosed);
9836
+ return pathRegistry.canClaimPath(path, {
9837
+ isClosed,
9838
+ isRepeatable,
9839
+ claimerId: field.id
9840
+ });
9549
9841
  });
9550
9842
  if (!canClaim) {
9551
- return 'Must not cause two binding paths to colide';
9843
+ return 'Must not cause two binding paths to collide';
9552
9844
  }
9845
+
9846
+ // If all checks pass
9847
+ return null;
9553
9848
  };
9849
+ 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.';
9554
9850
  return TextfieldEntry({
9555
9851
  debounce,
9556
9852
  description: 'Where the child variables of this component are pathed to.',
@@ -9558,7 +9854,7 @@ function Path(props) {
9558
9854
  getValue,
9559
9855
  id,
9560
9856
  label: 'Path',
9561
- tooltip: 'Routes the children of this component into a form variable, may be left empty to route at the root level.',
9857
+ tooltip,
9562
9858
  setValue,
9563
9859
  validate
9564
9860
  });
@@ -9571,6 +9867,8 @@ function simpleBoolEntryFactory(options) {
9571
9867
  description,
9572
9868
  path,
9573
9869
  props,
9870
+ getValue,
9871
+ setValue,
9574
9872
  isDefaultVisible
9575
9873
  } = options;
9576
9874
  const {
@@ -9585,8 +9883,10 @@ function simpleBoolEntryFactory(options) {
9585
9883
  editField,
9586
9884
  description,
9587
9885
  component: SimpleBoolComponent,
9588
- isEdited: isEdited$5,
9589
- isDefaultVisible
9886
+ isEdited: isEdited$8,
9887
+ isDefaultVisible,
9888
+ getValue,
9889
+ setValue
9590
9890
  };
9591
9891
  }
9592
9892
  const SimpleBoolComponent = props => {
@@ -9596,57 +9896,162 @@ const SimpleBoolComponent = props => {
9596
9896
  path,
9597
9897
  field,
9598
9898
  editField,
9899
+ getValue = () => minDash.get(field, path, ''),
9900
+ setValue = value => editField(field, path, value || false),
9599
9901
  description
9600
9902
  } = props;
9601
- const getValue = () => minDash.get(field, path, '');
9602
- const setValue = value => editField(field, path, value || false);
9603
- return CheckboxEntry({
9903
+ return ToggleSwitchEntry({
9604
9904
  element: field,
9605
9905
  getValue,
9606
9906
  id,
9607
9907
  label,
9608
9908
  setValue,
9909
+ inline: true,
9609
9910
  description
9610
9911
  });
9611
9912
  };
9612
9913
 
9613
- function GroupEntries(props) {
9914
+ function simpleSelectEntryFactory(options) {
9915
+ const {
9916
+ id,
9917
+ label,
9918
+ path,
9919
+ props,
9920
+ optionsArray
9921
+ } = options;
9614
9922
  const {
9923
+ editField,
9615
9924
  field
9616
9925
  } = props;
9617
- const {
9618
- type
9619
- } = field;
9620
- if (type !== 'group') {
9621
- return [];
9622
- }
9623
- const entries = [simpleBoolEntryFactory({
9624
- id: 'showOutline',
9625
- path: ['showOutline'],
9626
- label: 'Show outline',
9627
- props
9628
- })];
9629
- return entries;
9926
+ return {
9927
+ id,
9928
+ label,
9929
+ path,
9930
+ field,
9931
+ editField,
9932
+ optionsArray,
9933
+ component: SimpleSelectComponent,
9934
+ isEdited: isEdited$3
9935
+ };
9630
9936
  }
9631
-
9632
- function LabelEntry(props) {
9937
+ const SimpleSelectComponent = props => {
9633
9938
  const {
9939
+ id,
9940
+ label,
9941
+ path,
9634
9942
  field,
9635
- editField
9636
- } = props;
9637
- const entries = [];
9638
- entries.push({
9639
- id: 'date-label',
9640
- component: DateLabel,
9641
9943
  editField,
9642
- field,
9643
- isEdited: isEdited$6,
9644
- isDefaultVisible: function (field) {
9645
- return field.type === 'datetime' && (field.subtype === formJsViewer.DATETIME_SUBTYPES.DATE || field.subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME);
9646
- }
9647
- });
9648
- entries.push({
9649
- id: 'time-label',
9944
+ optionsArray
9945
+ } = props;
9946
+ const getValue = () => minDash.get(field, path, '');
9947
+ const setValue = value => editField(field, path, value);
9948
+ const getOptions = () => optionsArray;
9949
+ return SelectEntry({
9950
+ label,
9951
+ element: field,
9952
+ getOptions,
9953
+ getValue,
9954
+ id,
9955
+ setValue
9956
+ });
9957
+ };
9958
+
9959
+ function simpleRangeIntegerEntryFactory(options) {
9960
+ const {
9961
+ id,
9962
+ label,
9963
+ path,
9964
+ props,
9965
+ min,
9966
+ max,
9967
+ defaultValue
9968
+ } = options;
9969
+ const {
9970
+ editField,
9971
+ field
9972
+ } = props;
9973
+ return {
9974
+ id,
9975
+ label,
9976
+ path,
9977
+ field,
9978
+ editField,
9979
+ min,
9980
+ max,
9981
+ defaultValue,
9982
+ component: SimpleRangeIntegerEntry,
9983
+ isEdited: isEdited$7
9984
+ };
9985
+ }
9986
+ const SimpleRangeIntegerEntry = props => {
9987
+ const {
9988
+ id,
9989
+ label,
9990
+ path,
9991
+ field,
9992
+ editField,
9993
+ min,
9994
+ max,
9995
+ defaultValue
9996
+ } = props;
9997
+ const debounce = useService('debounce');
9998
+ const getValue = () => {
9999
+ const value = minDash.get(field, path, defaultValue);
10000
+ return Number.isInteger(value) ? value : defaultValue;
10001
+ };
10002
+ const setValue = value => {
10003
+ editField(field, path, value);
10004
+ };
10005
+ return NumberFieldEntry({
10006
+ debounce,
10007
+ label,
10008
+ element: field,
10009
+ step: 1,
10010
+ min,
10011
+ max,
10012
+ getValue,
10013
+ id,
10014
+ setValue
10015
+ });
10016
+ };
10017
+
10018
+ function GroupAppearanceEntry(props) {
10019
+ const {
10020
+ field
10021
+ } = props;
10022
+ const {
10023
+ type
10024
+ } = field;
10025
+ if (!['group', 'dynamiclist'].includes(type)) {
10026
+ return [];
10027
+ }
10028
+ const entries = [simpleBoolEntryFactory({
10029
+ id: 'showOutline',
10030
+ path: ['showOutline'],
10031
+ label: 'Show outline',
10032
+ props
10033
+ })];
10034
+ return entries;
10035
+ }
10036
+
10037
+ function LabelEntry(props) {
10038
+ const {
10039
+ field,
10040
+ editField
10041
+ } = props;
10042
+ const entries = [];
10043
+ entries.push({
10044
+ id: 'date-label',
10045
+ component: DateLabel,
10046
+ editField,
10047
+ field,
10048
+ isEdited: isEdited$6,
10049
+ isDefaultVisible: function (field) {
10050
+ return field.type === 'datetime' && (field.subtype === formJsViewer.DATETIME_SUBTYPES.DATE || field.subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME);
10051
+ }
10052
+ });
10053
+ entries.push({
10054
+ id: 'time-label',
9650
10055
  component: TimeLabel,
9651
10056
  editField,
9652
10057
  field,
@@ -9657,15 +10062,15 @@ function LabelEntry(props) {
9657
10062
  });
9658
10063
  entries.push({
9659
10064
  id: 'label',
9660
- component: Label$1,
10065
+ component: Label$2,
9661
10066
  editField,
9662
10067
  field,
9663
10068
  isEdited: isEdited$6,
9664
- isDefaultVisible: field => INPUTS.includes(field.type) || field.type === 'button' || field.type === 'group'
10069
+ isDefaultVisible: field => [...INPUTS, 'button', 'group', 'table', 'iframe', 'dynamiclist'].includes(field.type)
9665
10070
  });
9666
10071
  return entries;
9667
10072
  }
9668
- function Label$1(props) {
10073
+ function Label$2(props) {
9669
10074
  const {
9670
10075
  editField,
9671
10076
  field,
@@ -9682,7 +10087,7 @@ function Label$1(props) {
9682
10087
  const setValue = value => {
9683
10088
  return editField(field, path, value || '');
9684
10089
  };
9685
- const label = field.type === 'group' ? 'Group label' : 'Field label';
10090
+ const label = getLabelText(field.type);
9686
10091
  return FeelTemplatingEntry({
9687
10092
  debounce,
9688
10093
  element: field,
@@ -9751,6 +10156,170 @@ function TimeLabel(props) {
9751
10156
  });
9752
10157
  }
9753
10158
 
10159
+ // helpers //////////
10160
+
10161
+ /**
10162
+ * @param {string} type
10163
+ * @returns {string}
10164
+ */
10165
+ function getLabelText(type) {
10166
+ switch (type) {
10167
+ case 'group':
10168
+ case 'dynamiclist':
10169
+ return 'Group label';
10170
+ case 'table':
10171
+ return 'Table label';
10172
+ case 'iframe':
10173
+ return 'Title';
10174
+ default:
10175
+ return 'Field label';
10176
+ }
10177
+ }
10178
+
10179
+ function HeightEntry(props) {
10180
+ const {
10181
+ editField,
10182
+ field,
10183
+ id,
10184
+ description,
10185
+ isDefaultVisible,
10186
+ defaultValue
10187
+ } = props;
10188
+ const entries = [];
10189
+ entries.push({
10190
+ id: id + '-height',
10191
+ component: Height,
10192
+ description,
10193
+ isEdited: isEdited$7,
10194
+ editField,
10195
+ field,
10196
+ defaultValue,
10197
+ isDefaultVisible: field => {
10198
+ if (minDash.isFunction(isDefaultVisible)) {
10199
+ return isDefaultVisible(field);
10200
+ }
10201
+ return field.type === 'spacer';
10202
+ }
10203
+ });
10204
+ return entries;
10205
+ }
10206
+ function Height(props) {
10207
+ const {
10208
+ description,
10209
+ editField,
10210
+ field,
10211
+ id,
10212
+ defaultValue = 60 // default value for spacer
10213
+ } = props;
10214
+ const debounce = useService('debounce');
10215
+ const getValue = e => minDash.get(field, ['height'], defaultValue);
10216
+ const setValue = (value, error) => {
10217
+ if (error) {
10218
+ return;
10219
+ }
10220
+ editField(field, ['height'], value);
10221
+ };
10222
+ return NumberFieldEntry({
10223
+ debounce,
10224
+ description,
10225
+ label: 'Height',
10226
+ element: field,
10227
+ id,
10228
+ getValue,
10229
+ setValue,
10230
+ validate: value => {
10231
+ if (value === undefined || value === null) return;
10232
+ if (value < 1) return 'Should be greater than zero.';
10233
+ if (!Number.isInteger(value)) return 'Should be an integer.';
10234
+ }
10235
+ });
10236
+ }
10237
+
10238
+ function IFrameHeightEntry(props) {
10239
+ return [...HeightEntry({
10240
+ ...props,
10241
+ defaultValue: 300,
10242
+ description: 'Height of the container in pixels.',
10243
+ isDefaultVisible: field => field.type === 'iframe'
10244
+ })];
10245
+ }
10246
+
10247
+ const HTTPS_PATTERN = /^(https):\/\/*/i; // eslint-disable-line no-useless-escape
10248
+
10249
+ function IFrameUrlEntry(props) {
10250
+ const {
10251
+ editField,
10252
+ field
10253
+ } = props;
10254
+ const entries = [];
10255
+ entries.push({
10256
+ id: 'url',
10257
+ component: Url,
10258
+ editField: editField,
10259
+ field: field,
10260
+ isEdited: isEdited$6,
10261
+ isDefaultVisible: field => field.type === 'iframe'
10262
+ });
10263
+ return entries;
10264
+ }
10265
+ function Url(props) {
10266
+ const {
10267
+ editField,
10268
+ field,
10269
+ id
10270
+ } = props;
10271
+ const debounce = useService('debounce');
10272
+ const variables = useVariables().map(name => ({
10273
+ name
10274
+ }));
10275
+ const path = ['url'];
10276
+ const getValue = () => {
10277
+ return minDash.get(field, path, '');
10278
+ };
10279
+ const setValue = value => {
10280
+ return editField(field, path, value);
10281
+ };
10282
+ const validate = value => {
10283
+ if (!value) {
10284
+ return;
10285
+ }
10286
+ if (!HTTPS_PATTERN.test(value)) {
10287
+ return 'For security reasons the URL must start with "https".';
10288
+ }
10289
+ };
10290
+ return FeelTemplatingEntry({
10291
+ debounce,
10292
+ element: field,
10293
+ feel: 'optional',
10294
+ getValue,
10295
+ id,
10296
+ label: 'URL',
10297
+ setValue,
10298
+ singleLine: true,
10299
+ tooltip: getTooltip(),
10300
+ validate,
10301
+ variables
10302
+ });
10303
+ }
10304
+
10305
+ // helper //////////////////////
10306
+
10307
+ function getTooltip() {
10308
+ return jsxRuntime.jsxs(jsxRuntime.Fragment, {
10309
+ children: [jsxRuntime.jsx("p", {
10310
+ 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)."
10311
+ }), jsxRuntime.jsx("p", {
10312
+ children: "Please make sure that the URL is safe as it might impose security risks."
10313
+ }), jsxRuntime.jsxs("p", {
10314
+ children: ["Not all external sources can be displayed in the iFrame. Read more about it in the ", jsxRuntime.jsx("a", {
10315
+ target: "_blank",
10316
+ href: "https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options",
10317
+ children: "X-FRAME-OPTIONS documentation"
10318
+ }), "."]
10319
+ })]
10320
+ });
10321
+ }
10322
+
9754
10323
  function SourceEntry(props) {
9755
10324
  const {
9756
10325
  editField,
@@ -9759,7 +10328,7 @@ function SourceEntry(props) {
9759
10328
  const entries = [];
9760
10329
  entries.push({
9761
10330
  id: 'source',
9762
- component: Source,
10331
+ component: Source$1,
9763
10332
  editField: editField,
9764
10333
  field: field,
9765
10334
  isEdited: isEdited$6,
@@ -9767,7 +10336,7 @@ function SourceEntry(props) {
9767
10336
  });
9768
10337
  return entries;
9769
10338
  }
9770
- function Source(props) {
10339
+ function Source$1(props) {
9771
10340
  const {
9772
10341
  editField,
9773
10342
  field,
@@ -9812,18 +10381,6 @@ function TextEntry(props) {
9812
10381
  isEdited: isEdited$6,
9813
10382
  isDefaultVisible: field => field.type === 'text'
9814
10383
  }];
9815
-
9816
- // todo: skipped to make the release without too much risk
9817
- // if (templating.isTemplate(field.text)) {
9818
- // entries.push(simpleBoolEntryFactory({
9819
- // id: 'strict',
9820
- // path: [ 'strict' ],
9821
- // label: 'Strict templating',
9822
- // description: 'Enforces types to be correct',
9823
- // props
9824
- // }));
9825
- // }
9826
-
9827
10384
  return entries;
9828
10385
  }
9829
10386
  function Text(props) {
@@ -9863,7 +10420,7 @@ function Text(props) {
9863
10420
  });
9864
10421
  }
9865
10422
 
9866
- function SpacerEntry(props) {
10423
+ function NumberEntries(props) {
9867
10424
  const {
9868
10425
  editField,
9869
10426
  field,
@@ -9871,99 +10428,53 @@ function SpacerEntry(props) {
9871
10428
  } = props;
9872
10429
  const entries = [];
9873
10430
  entries.push({
9874
- id: id + '-height',
9875
- component: SpacerHeight,
10431
+ id: id + '-decimalDigits',
10432
+ component: NumberDecimalDigits,
9876
10433
  isEdited: isEdited$7,
9877
10434
  editField,
9878
10435
  field,
9879
- isDefaultVisible: field => field.type === 'spacer'
10436
+ isDefaultVisible: field => field.type === 'number'
10437
+ });
10438
+ entries.push({
10439
+ id: id + '-step',
10440
+ component: NumberArrowStep,
10441
+ isEdited: isEdited,
10442
+ editField,
10443
+ field,
10444
+ isDefaultVisible: field => field.type === 'number'
9880
10445
  });
9881
10446
  return entries;
9882
10447
  }
9883
- function SpacerHeight(props) {
10448
+ function NumberDecimalDigits(props) {
9884
10449
  const {
9885
10450
  editField,
9886
10451
  field,
9887
10452
  id
9888
10453
  } = props;
9889
10454
  const debounce = useService('debounce');
9890
- const getValue = e => minDash.get(field, ['height']);
10455
+ const getValue = e => minDash.get(field, ['decimalDigits']);
9891
10456
  const setValue = (value, error) => {
9892
10457
  if (error) {
9893
10458
  return;
9894
10459
  }
9895
- editField(field, ['height'], value);
10460
+ editField(field, ['decimalDigits'], value);
9896
10461
  };
9897
10462
  return NumberFieldEntry({
9898
10463
  debounce,
9899
- label: 'Height',
10464
+ label: 'Decimal digits',
9900
10465
  element: field,
9901
- id,
10466
+ step: 'any',
9902
10467
  getValue,
10468
+ id,
9903
10469
  setValue,
9904
10470
  validate: value => {
9905
10471
  if (value === undefined || value === null) return;
9906
- if (value < 1) return 'Should be greater than zero.';
10472
+ if (value < 0) return 'Should be greater than or equal to zero.';
9907
10473
  if (!Number.isInteger(value)) return 'Should be an integer.';
9908
10474
  }
9909
10475
  });
9910
10476
  }
9911
-
9912
- function NumberEntries(props) {
9913
- const {
9914
- editField,
9915
- field,
9916
- id
9917
- } = props;
9918
- const entries = [];
9919
- entries.push({
9920
- id: id + '-decimalDigits',
9921
- component: NumberDecimalDigits,
9922
- isEdited: isEdited$7,
9923
- editField,
9924
- field,
9925
- isDefaultVisible: field => field.type === 'number'
9926
- });
9927
- entries.push({
9928
- id: id + '-step',
9929
- component: NumberArrowStep,
9930
- isEdited: isEdited,
9931
- editField,
9932
- field,
9933
- isDefaultVisible: field => field.type === 'number'
9934
- });
9935
- return entries;
9936
- }
9937
- function NumberDecimalDigits(props) {
9938
- const {
9939
- editField,
9940
- field,
9941
- id
9942
- } = props;
9943
- const debounce = useService('debounce');
9944
- const getValue = e => minDash.get(field, ['decimalDigits']);
9945
- const setValue = (value, error) => {
9946
- if (error) {
9947
- return;
9948
- }
9949
- editField(field, ['decimalDigits'], value);
9950
- };
9951
- return NumberFieldEntry({
9952
- debounce,
9953
- label: 'Decimal digits',
9954
- element: field,
9955
- step: 'any',
9956
- getValue,
9957
- id,
9958
- setValue,
9959
- validate: value => {
9960
- if (value === undefined || value === null) return;
9961
- if (value < 0) return 'Should be greater than or equal to zero.';
9962
- if (!Number.isInteger(value)) return 'Should be an integer.';
9963
- }
9964
- });
9965
- }
9966
- function NumberArrowStep(props) {
10477
+ function NumberArrowStep(props) {
9967
10478
  const {
9968
10479
  editField,
9969
10480
  field,
@@ -10297,7 +10808,7 @@ function ValueEntry(props) {
10297
10808
  validateFactory
10298
10809
  } = props;
10299
10810
  const entries = [{
10300
- component: Label,
10811
+ component: Label$1,
10301
10812
  editField,
10302
10813
  field,
10303
10814
  id: idPrefix + '-label',
@@ -10315,7 +10826,7 @@ function ValueEntry(props) {
10315
10826
  }];
10316
10827
  return entries;
10317
10828
  }
10318
- function Label(props) {
10829
+ function Label$1(props) {
10319
10830
  const {
10320
10831
  editField,
10321
10832
  field,
@@ -10383,7 +10894,7 @@ function CustomValueEntry(props) {
10383
10894
  validateFactory
10384
10895
  } = props;
10385
10896
  const entries = [{
10386
- component: Key,
10897
+ component: Key$1,
10387
10898
  editField,
10388
10899
  field,
10389
10900
  id: idPrefix + '-key',
@@ -10401,7 +10912,7 @@ function CustomValueEntry(props) {
10401
10912
  }];
10402
10913
  return entries;
10403
10914
  }
10404
- function Key(props) {
10915
+ function Key$1(props) {
10405
10916
  const {
10406
10917
  editField,
10407
10918
  field,
@@ -10526,7 +11037,7 @@ function AutoFocusSelectEntry(props) {
10526
11037
  });
10527
11038
  }
10528
11039
 
10529
- function ValuesSourceSelectEntry(props) {
11040
+ function OptionsSourceSelectEntry(props) {
10530
11041
  const {
10531
11042
  editField,
10532
11043
  field,
@@ -10546,25 +11057,25 @@ function ValuesSourceSelect(props) {
10546
11057
  field,
10547
11058
  id
10548
11059
  } = props;
10549
- const getValue = formJsViewer.getValuesSource;
11060
+ const getValue = formJsViewer.getOptionsSource;
10550
11061
  const setValue = value => {
10551
11062
  let newField = field;
10552
11063
  const newProperties = {};
10553
- newProperties[formJsViewer.VALUES_SOURCES_PATHS[value]] = formJsViewer.VALUES_SOURCES_DEFAULTS[value];
11064
+ newProperties[formJsViewer.OPTIONS_SOURCES_PATHS[value]] = formJsViewer.OPTIONS_SOURCES_DEFAULTS[value];
10554
11065
  newField = editField(field, newProperties);
10555
11066
  return newField;
10556
11067
  };
10557
- const getValuesSourceOptions = () => {
10558
- return Object.values(formJsViewer.VALUES_SOURCES).map(valueSource => ({
10559
- label: formJsViewer.VALUES_SOURCES_LABELS[valueSource],
11068
+ const getOptionsSourceOptions = () => {
11069
+ return Object.values(formJsViewer.OPTIONS_SOURCES).map(valueSource => ({
11070
+ label: formJsViewer.OPTIONS_SOURCES_LABELS[valueSource],
10560
11071
  value: valueSource
10561
11072
  }));
10562
11073
  };
10563
11074
  return AutoFocusSelectEntry({
10564
- autoFocusEntry: getAutoFocusEntryId(field),
11075
+ autoFocusEntry: getAutoFocusEntryId$1(field),
10565
11076
  label: 'Type',
10566
11077
  element: field,
10567
- getOptions: getValuesSourceOptions,
11078
+ getOptions: getOptionsSourceOptions,
10568
11079
  getValue,
10569
11080
  id,
10570
11081
  setValue
@@ -10573,19 +11084,19 @@ function ValuesSourceSelect(props) {
10573
11084
 
10574
11085
  // helpers //////////
10575
11086
 
10576
- function getAutoFocusEntryId(field) {
10577
- const valuesSource = formJsViewer.getValuesSource(field);
10578
- if (valuesSource === formJsViewer.VALUES_SOURCES.EXPRESSION) {
10579
- return `${field.id}-valuesExpression-expression`;
10580
- } else if (valuesSource === formJsViewer.VALUES_SOURCES.INPUT) {
10581
- return `${field.id}-dynamicValues-key`;
10582
- } else if (valuesSource === formJsViewer.VALUES_SOURCES.STATIC) {
10583
- return `${field.id}-staticValues-0-label`;
11087
+ function getAutoFocusEntryId$1(field) {
11088
+ const valuesSource = formJsViewer.getOptionsSource(field);
11089
+ if (valuesSource === formJsViewer.OPTIONS_SOURCES.EXPRESSION) {
11090
+ return 'optionsExpression-expression';
11091
+ } else if (valuesSource === formJsViewer.OPTIONS_SOURCES.INPUT) {
11092
+ return 'dynamicOptions-key';
11093
+ } else if (valuesSource === formJsViewer.OPTIONS_SOURCES.STATIC) {
11094
+ return 'staticOptions-0-label';
10584
11095
  }
10585
11096
  return null;
10586
11097
  }
10587
11098
 
10588
- function InputKeyValuesSourceEntry(props) {
11099
+ function InputKeyOptionsSourceEntry(props) {
10589
11100
  const {
10590
11101
  editField,
10591
11102
  field,
@@ -10606,7 +11117,7 @@ function InputValuesKey(props) {
10606
11117
  id
10607
11118
  } = props;
10608
11119
  const debounce = useService('debounce');
10609
- const path = formJsViewer.VALUES_SOURCES_PATHS[formJsViewer.VALUES_SOURCES.INPUT];
11120
+ const path = formJsViewer.OPTIONS_SOURCES_PATHS[formJsViewer.OPTIONS_SOURCES.INPUT];
10610
11121
  const schema = '[\n {\n "label": "dollar",\n "value": "$"\n }\n]';
10611
11122
  const tooltip = jsxRuntime.jsxs("div", {
10612
11123
  children: ["The input property may be an array of simple values or alternatively follow this schema:", jsxRuntime.jsx("pre", {
@@ -10644,7 +11155,7 @@ function InputValuesKey(props) {
10644
11155
  });
10645
11156
  }
10646
11157
 
10647
- function StaticValuesSourceEntry(props) {
11158
+ function StaticOptionsSourceEntry(props) {
10648
11159
  const {
10649
11160
  editField,
10650
11161
  field,
@@ -10657,10 +11168,10 @@ function StaticValuesSourceEntry(props) {
10657
11168
  e.stopPropagation();
10658
11169
  const index = values.length + 1;
10659
11170
  const entry = getIndexedEntry(index, values);
10660
- editField(field, formJsViewer.VALUES_SOURCES_PATHS[formJsViewer.VALUES_SOURCES.STATIC], arrayAdd(values, values.length, entry));
11171
+ editField(field, formJsViewer.OPTIONS_SOURCES_PATHS[formJsViewer.OPTIONS_SOURCES.STATIC], arrayAdd(values, values.length, entry));
10661
11172
  };
10662
11173
  const removeEntry = entry => {
10663
- editField(field, formJsViewer.VALUES_SOURCES_PATHS[formJsViewer.VALUES_SOURCES.STATIC], minDash.without(values, entry));
11174
+ editField(field, formJsViewer.OPTIONS_SOURCES_PATHS[formJsViewer.OPTIONS_SOURCES.STATIC], minDash.without(values, entry));
10664
11175
  };
10665
11176
  const validateFactory = (key, getValue) => {
10666
11177
  return value => {
@@ -10850,6 +11361,76 @@ function Readonly(props) {
10850
11361
  });
10851
11362
  }
10852
11363
 
11364
+ function LayouterAppearanceEntry(props) {
11365
+ const {
11366
+ field
11367
+ } = props;
11368
+ if (!['group', 'dynamiclist'].includes(field.type)) {
11369
+ return [];
11370
+ }
11371
+ const entries = [simpleSelectEntryFactory({
11372
+ id: 'verticalAlignment',
11373
+ path: ['verticalAlignment'],
11374
+ label: 'Vertical alignment',
11375
+ optionsArray: [{
11376
+ value: 'start',
11377
+ label: 'Top'
11378
+ }, {
11379
+ value: 'center',
11380
+ label: 'Center'
11381
+ }, {
11382
+ value: 'end',
11383
+ label: 'Bottom'
11384
+ }],
11385
+ props
11386
+ })];
11387
+ return entries;
11388
+ }
11389
+
11390
+ function RepeatableEntry(props) {
11391
+ const {
11392
+ field,
11393
+ getService
11394
+ } = props;
11395
+ const {
11396
+ type
11397
+ } = field;
11398
+ const formFieldDefinition = getService('formFields').get(type);
11399
+ if (!formFieldDefinition || !formFieldDefinition.config.repeatable) {
11400
+ return [];
11401
+ }
11402
+ const entries = [simpleRangeIntegerEntryFactory({
11403
+ id: 'defaultRepetitions',
11404
+ path: ['defaultRepetitions'],
11405
+ label: 'Default number of items',
11406
+ min: 0,
11407
+ max: 20,
11408
+ props
11409
+ }), simpleBoolEntryFactory({
11410
+ id: 'allowAddRemove',
11411
+ path: ['allowAddRemove'],
11412
+ label: 'Allow add/delete items',
11413
+ props
11414
+ }), simpleBoolEntryFactory({
11415
+ id: 'disableCollapse',
11416
+ path: ['disableCollapse'],
11417
+ label: 'Disable collapse',
11418
+ props
11419
+ })];
11420
+ if (!field.disableCollapse) {
11421
+ const nonCollapseItemsEntry = simpleRangeIntegerEntryFactory({
11422
+ id: 'nonCollapsedItems',
11423
+ path: ['nonCollapsedItems'],
11424
+ label: 'Number of non-collapsing items',
11425
+ min: 1,
11426
+ defaultValue: 5,
11427
+ props
11428
+ });
11429
+ entries.push(nonCollapseItemsEntry);
11430
+ }
11431
+ return entries;
11432
+ }
11433
+
10853
11434
  function ConditionEntry(props) {
10854
11435
  const {
10855
11436
  editField,
@@ -10860,10 +11441,391 @@ function ConditionEntry(props) {
10860
11441
  component: Condition,
10861
11442
  editField: editField,
10862
11443
  field: field,
10863
- isEdited: isEdited$6
10864
- }];
11444
+ isEdited: isEdited$6
11445
+ }];
11446
+ }
11447
+ function Condition(props) {
11448
+ const {
11449
+ editField,
11450
+ field,
11451
+ id
11452
+ } = props;
11453
+ const debounce = useService('debounce');
11454
+ const variables = useVariables().map(name => ({
11455
+ name
11456
+ }));
11457
+ const path = ['conditional', 'hide'];
11458
+ const getValue = () => {
11459
+ return minDash.get(field, path, '');
11460
+ };
11461
+ const setValue = value => {
11462
+ if (!value) {
11463
+ return editField(field, 'conditional', undefined);
11464
+ }
11465
+ return editField(field, 'conditional', {
11466
+ hide: value
11467
+ });
11468
+ };
11469
+ return FeelEntry({
11470
+ debounce,
11471
+ description: 'Condition under which the field is hidden',
11472
+ element: field,
11473
+ feel: 'required',
11474
+ getValue,
11475
+ id,
11476
+ label: 'Hide if',
11477
+ setValue,
11478
+ variables
11479
+ });
11480
+ }
11481
+
11482
+ function OptionsExpressionEntry(props) {
11483
+ const {
11484
+ editField,
11485
+ field,
11486
+ id
11487
+ } = props;
11488
+ return [{
11489
+ id: id + '-expression',
11490
+ component: OptionsExpression,
11491
+ isEdited: isEdited$6,
11492
+ editField,
11493
+ field
11494
+ }];
11495
+ }
11496
+ function OptionsExpression(props) {
11497
+ const {
11498
+ editField,
11499
+ field,
11500
+ id
11501
+ } = props;
11502
+ const debounce = useService('debounce');
11503
+ const variables = useVariables().map(name => ({
11504
+ name
11505
+ }));
11506
+ const path = formJsViewer.OPTIONS_SOURCES_PATHS[formJsViewer.OPTIONS_SOURCES.EXPRESSION];
11507
+ const schema = '[\n {\n "label": "dollar",\n "value": "$"\n }\n]';
11508
+ const tooltip = jsxRuntime.jsxs("div", {
11509
+ children: ["The expression may result in an array of simple values or alternatively follow this schema:", jsxRuntime.jsx("pre", {
11510
+ children: jsxRuntime.jsx("code", {
11511
+ children: schema
11512
+ })
11513
+ })]
11514
+ });
11515
+ const getValue = () => minDash.get(field, path, '');
11516
+ const setValue = value => editField(field, path, value || '');
11517
+ return FeelEntry({
11518
+ debounce,
11519
+ description: 'Define an expression to populate the options from.',
11520
+ tooltip,
11521
+ element: field,
11522
+ feel: 'required',
11523
+ getValue,
11524
+ id,
11525
+ label: 'Options expression',
11526
+ setValue,
11527
+ variables
11528
+ });
11529
+ }
11530
+
11531
+ function TableDataSourceEntry(props) {
11532
+ const {
11533
+ editField,
11534
+ field
11535
+ } = props;
11536
+ const entries = [];
11537
+ entries.push({
11538
+ id: 'dataSource',
11539
+ component: Source,
11540
+ editField: editField,
11541
+ field: field,
11542
+ isEdited: isEdited$6,
11543
+ isDefaultVisible: field => field.type === 'table'
11544
+ });
11545
+ return entries;
11546
+ }
11547
+ function Source(props) {
11548
+ const {
11549
+ editField,
11550
+ field,
11551
+ id
11552
+ } = props;
11553
+ const debounce = useService('debounce');
11554
+ const variables = useVariables().map(name => ({
11555
+ name
11556
+ }));
11557
+ const path = ['dataSource'];
11558
+ const getValue = () => {
11559
+ return minDash.get(field, path, field.id);
11560
+ };
11561
+ const setValue = (value, error) => {
11562
+ if (error) {
11563
+ return;
11564
+ }
11565
+ editField(field, path, value);
11566
+ };
11567
+
11568
+ /**
11569
+ * @param {string|void} value
11570
+ * @returns {string|null}
11571
+ */
11572
+ const validate = value => {
11573
+ if (!minDash.isString(value) || value.length === 0) {
11574
+ return 'Must not be empty.';
11575
+ }
11576
+ if (value.startsWith('=')) {
11577
+ return null;
11578
+ }
11579
+ if (!isValidDotPath(value)) {
11580
+ return 'Must be a variable or a dot separated path.';
11581
+ }
11582
+ if (hasIntegerPathSegment(value)) {
11583
+ return 'Must not contain numerical path segments.';
11584
+ }
11585
+ return null;
11586
+ };
11587
+ return FeelTemplatingEntry({
11588
+ debounce,
11589
+ description: 'Specify the source from which to populate the table',
11590
+ element: field,
11591
+ feel: 'required',
11592
+ getValue,
11593
+ id,
11594
+ label: 'Data source',
11595
+ tooltip: 'Enter a form input variable that contains the data for the table or define an expression to populate the data dynamically.',
11596
+ setValue,
11597
+ singleLine: true,
11598
+ variables,
11599
+ validate
11600
+ });
11601
+ }
11602
+
11603
+ function PaginationEntry(props) {
11604
+ const {
11605
+ editField,
11606
+ field
11607
+ } = props;
11608
+ const entries = [];
11609
+ entries.push({
11610
+ id: 'pagination',
11611
+ component: Pagination,
11612
+ editField: editField,
11613
+ field: field,
11614
+ isEdited: isEdited$8,
11615
+ isDefaultVisible: field => field.type === 'table'
11616
+ });
11617
+ return entries;
11618
+ }
11619
+ function Pagination(props) {
11620
+ const {
11621
+ editField,
11622
+ field,
11623
+ id
11624
+ } = props;
11625
+ const defaultRowCount = 10;
11626
+ const path = ['rowCount'];
11627
+ const getValue = () => {
11628
+ return minDash.isNumber(minDash.get(field, path));
11629
+ };
11630
+
11631
+ /**
11632
+ * @param {boolean} value
11633
+ */
11634
+ const setValue = value => {
11635
+ value ? editField(field, path, defaultRowCount) : editField(field, path, undefined);
11636
+ };
11637
+ return ToggleSwitchEntry({
11638
+ element: field,
11639
+ getValue,
11640
+ id,
11641
+ label: 'Pagination',
11642
+ inline: true,
11643
+ setValue
11644
+ });
11645
+ }
11646
+
11647
+ const path$2 = ['rowCount'];
11648
+ function RowCountEntry(props) {
11649
+ const {
11650
+ editField,
11651
+ field
11652
+ } = props;
11653
+ const entries = [];
11654
+ entries.push({
11655
+ id: 'rowCount',
11656
+ component: RowCount,
11657
+ isEdited: isEdited$7,
11658
+ editField,
11659
+ field,
11660
+ isDefaultVisible: field => field.type === 'table' && minDash.isNumber(minDash.get(field, path$2))
11661
+ });
11662
+ return entries;
11663
+ }
11664
+ function RowCount(props) {
11665
+ const {
11666
+ editField,
11667
+ field,
11668
+ id
11669
+ } = props;
11670
+ const debounce = useService('debounce');
11671
+ const getValue = () => minDash.get(field, path$2);
11672
+
11673
+ /**
11674
+ * @param {number|void} value
11675
+ * @param {string|null} error
11676
+ * @returns {void}
11677
+ */
11678
+ const setValue = (value, error) => {
11679
+ if (error) {
11680
+ return;
11681
+ }
11682
+ editField(field, path$2, value);
11683
+ };
11684
+
11685
+ /**
11686
+ * @param {string|void} value
11687
+ * @returns {string|null}
11688
+ */
11689
+ const validate = value => {
11690
+ if (minDash.isNil(value)) {
11691
+ return null;
11692
+ }
11693
+ if (!minDash.isNumber(value)) {
11694
+ return 'Must be number';
11695
+ }
11696
+ if (!Number.isInteger(value)) {
11697
+ return 'Should be an integer.';
11698
+ }
11699
+ if (value < 1) {
11700
+ return 'Should be greater than zero.';
11701
+ }
11702
+ return null;
11703
+ };
11704
+ return NumberFieldEntry({
11705
+ debounce,
11706
+ label: 'Number of rows per page',
11707
+ element: field,
11708
+ id,
11709
+ getValue,
11710
+ setValue,
11711
+ validate
11712
+ });
11713
+ }
11714
+
11715
+ const OPTIONS = {
11716
+ static: {
11717
+ label: 'List of items',
11718
+ value: 'static'
11719
+ },
11720
+ expression: {
11721
+ label: 'Expression',
11722
+ value: 'expression'
11723
+ }
11724
+ };
11725
+ const SELECT_OPTIONS = Object.values(OPTIONS);
11726
+ const COLUMNS_PATH = ['columns'];
11727
+ const COLUMNS_EXPRESSION_PATH = ['columnsExpression'];
11728
+ function HeadersSourceSelectEntry(props) {
11729
+ const {
11730
+ editField,
11731
+ field,
11732
+ id
11733
+ } = props;
11734
+ return [{
11735
+ id: id + '-select',
11736
+ component: HeadersSourceSelect,
11737
+ isEdited: isEdited$3,
11738
+ editField,
11739
+ field
11740
+ }];
11741
+ }
11742
+ function HeadersSourceSelect(props) {
11743
+ const {
11744
+ editField,
11745
+ field,
11746
+ id
11747
+ } = props;
11748
+
11749
+ /**
11750
+ * @returns {string|void}
11751
+ */
11752
+ const getValue = () => {
11753
+ const columns = minDash.get(field, COLUMNS_PATH);
11754
+ const columnsExpression = minDash.get(field, COLUMNS_EXPRESSION_PATH);
11755
+ if (minDash.isString(columnsExpression)) {
11756
+ return OPTIONS.expression.value;
11757
+ }
11758
+ if (minDash.isArray(columns)) {
11759
+ return OPTIONS.static.value;
11760
+ }
11761
+ };
11762
+
11763
+ /**
11764
+ * @param {string|void} value
11765
+ */
11766
+ const setValue = value => {
11767
+ switch (value) {
11768
+ case OPTIONS.static.value:
11769
+ editField(field, {
11770
+ columns: [{
11771
+ label: 'Column',
11772
+ key: 'inputVariable'
11773
+ }]
11774
+ });
11775
+ break;
11776
+ case OPTIONS.expression.value:
11777
+ editField(field, {
11778
+ columnsExpression: '='
11779
+ });
11780
+ break;
11781
+ }
11782
+ };
11783
+ const getValuesSourceOptions = () => {
11784
+ return SELECT_OPTIONS;
11785
+ };
11786
+ return AutoFocusSelectEntry({
11787
+ autoFocusEntry: getAutoFocusEntryId(field),
11788
+ label: 'Type',
11789
+ element: field,
11790
+ getOptions: getValuesSourceOptions,
11791
+ getValue,
11792
+ id,
11793
+ setValue
11794
+ });
11795
+ }
11796
+
11797
+ // helpers //////////
11798
+
11799
+ function getAutoFocusEntryId(field) {
11800
+ const columns = minDash.get(field, COLUMNS_PATH);
11801
+ const columnsExpression = minDash.get(field, COLUMNS_EXPRESSION_PATH);
11802
+ if (minDash.isString(columnsExpression)) {
11803
+ return `${field.id}-columnsExpression`;
11804
+ }
11805
+ if (minDash.isArray(columns)) {
11806
+ return `${field.id}-columns-0-label`;
11807
+ }
11808
+ return null;
11809
+ }
11810
+
11811
+ const PATH = ['columnsExpression'];
11812
+ function ColumnsExpressionEntry(props) {
11813
+ const {
11814
+ editField,
11815
+ field
11816
+ } = props;
11817
+ const entries = [];
11818
+ entries.push({
11819
+ id: `${field.id}-columnsExpression`,
11820
+ component: ColumnsExpression,
11821
+ editField: editField,
11822
+ field: field,
11823
+ isEdited: isEdited$6,
11824
+ isDefaultVisible: field => field.type === 'table' && minDash.isString(minDash.get(field, PATH))
11825
+ });
11826
+ return entries;
10865
11827
  }
10866
- function Condition(props) {
11828
+ function ColumnsExpression(props) {
10867
11829
  const {
10868
11830
  editField,
10869
11831
  field,
@@ -10873,78 +11835,208 @@ function Condition(props) {
10873
11835
  const variables = useVariables().map(name => ({
10874
11836
  name
10875
11837
  }));
10876
- const path = ['conditional', 'hide'];
10877
11838
  const getValue = () => {
10878
- return minDash.get(field, path, '');
11839
+ return minDash.get(field, PATH);
10879
11840
  };
10880
- const setValue = value => {
10881
- if (!value) {
10882
- return editField(field, 'conditional', undefined);
11841
+
11842
+ /**
11843
+ * @param {string|void} value
11844
+ * @param {string|void} error
11845
+ * @returns {void}
11846
+ */
11847
+ const setValue = (value, error) => {
11848
+ if (error) {
11849
+ return;
10883
11850
  }
10884
- return editField(field, 'conditional', {
10885
- hide: value
10886
- });
11851
+ editField(field, PATH, value);
10887
11852
  };
10888
- return FeelEntry({
11853
+
11854
+ /**
11855
+ * @param {string|void} value
11856
+ * @returns {string|null}
11857
+ */
11858
+ const validate = value => {
11859
+ if (!minDash.isString(value) || value.length === 0 || value === '=') {
11860
+ return 'Must not be empty.';
11861
+ }
11862
+ return null;
11863
+ };
11864
+ const schema = '[\n {\n "key": "column_1",\n "label": "Column 1"\n }\n]';
11865
+ const tooltip = jsxRuntime.jsxs("div", {
11866
+ children: ["The expression may result in an array of simple values or alternatively follow this schema:", jsxRuntime.jsx("pre", {
11867
+ children: jsxRuntime.jsx("code", {
11868
+ children: schema
11869
+ })
11870
+ })]
11871
+ });
11872
+ return FeelTemplatingEntry({
10889
11873
  debounce,
10890
- description: 'Condition under which the field is hidden',
11874
+ description: 'Specify an expression to populate column items',
10891
11875
  element: field,
10892
11876
  feel: 'required',
10893
11877
  getValue,
10894
11878
  id,
10895
- label: 'Hide if',
11879
+ label: 'Expression',
11880
+ tooltip,
10896
11881
  setValue,
10897
- variables
11882
+ singleLine: true,
11883
+ variables,
11884
+ validate
10898
11885
  });
10899
11886
  }
10900
11887
 
10901
- function ValuesExpressionEntry(props) {
11888
+ const path$1 = 'columns';
11889
+ const labelPath = 'label';
11890
+ const keyPath = 'key';
11891
+ function ColumnEntry(props) {
10902
11892
  const {
10903
11893
  editField,
10904
11894
  field,
10905
- id
11895
+ idPrefix,
11896
+ index,
11897
+ validateFactory
10906
11898
  } = props;
10907
- return [{
10908
- id: id + '-expression',
10909
- component: ValuesExpression,
10910
- isEdited: isEdited$6,
11899
+ const entries = [{
11900
+ component: Label,
10911
11901
  editField,
10912
- field
11902
+ field,
11903
+ id: idPrefix + '-label',
11904
+ idPrefix,
11905
+ index,
11906
+ validateFactory
11907
+ }, {
11908
+ component: Key,
11909
+ editField,
11910
+ field,
11911
+ id: idPrefix + '-key',
11912
+ idPrefix,
11913
+ index,
11914
+ validateFactory
10913
11915
  }];
11916
+ return entries;
10914
11917
  }
10915
- function ValuesExpression(props) {
11918
+ function Label(props) {
10916
11919
  const {
10917
11920
  editField,
10918
11921
  field,
10919
- id
11922
+ id,
11923
+ index
10920
11924
  } = props;
10921
11925
  const debounce = useService('debounce');
10922
- const variables = useVariables().map(name => ({
10923
- name
10924
- }));
10925
- const path = formJsViewer.VALUES_SOURCES_PATHS[formJsViewer.VALUES_SOURCES.EXPRESSION];
10926
- const schema = '[\n {\n "label": "dollar",\n "value": "$"\n }\n]';
10927
- const tooltip = jsxRuntime.jsxs("div", {
10928
- children: ["The expression may result in an array of simple values or alternatively follow this schema:", jsxRuntime.jsx("pre", {
10929
- children: jsxRuntime.jsx("code", {
10930
- children: schema
10931
- })
10932
- })]
11926
+
11927
+ /**
11928
+ * @param {string|void} value
11929
+ * @param {string|void} error
11930
+ * @returns {void}
11931
+ */
11932
+ const setValue = (value, error) => {
11933
+ if (error) {
11934
+ return;
11935
+ }
11936
+ const columns = minDash.get(field, [path$1]);
11937
+ editField(field, path$1, minDash.set(columns, [index, labelPath], value));
11938
+ };
11939
+ const getValue = () => {
11940
+ return minDash.get(field, [path$1, index, labelPath]);
11941
+ };
11942
+ return TextfieldEntry({
11943
+ debounce,
11944
+ element: field,
11945
+ getValue,
11946
+ id,
11947
+ label: 'Label',
11948
+ setValue
10933
11949
  });
10934
- const getValue = () => minDash.get(field, path, '');
10935
- const setValue = value => editField(field, path, value || '');
10936
- return FeelEntry({
11950
+ }
11951
+ function Key(props) {
11952
+ const {
11953
+ editField,
11954
+ field,
11955
+ id,
11956
+ index
11957
+ } = props;
11958
+ const debounce = useService('debounce');
11959
+
11960
+ /**
11961
+ * @param {string|void} value
11962
+ * @param {string|void} error
11963
+ * @returns {void}
11964
+ */
11965
+ const setValue = (value, error) => {
11966
+ if (error) {
11967
+ return;
11968
+ }
11969
+ const columns = minDash.get(field, [path$1]);
11970
+ editField(field, path$1, minDash.set(columns, [index, keyPath], value));
11971
+ };
11972
+ const getValue = () => {
11973
+ return minDash.get(field, [path$1, index, keyPath]);
11974
+ };
11975
+ return TextfieldEntry({
10937
11976
  debounce,
10938
- description: 'Define an expression to populate the options from.',
10939
- tooltip,
10940
11977
  element: field,
10941
- feel: 'required',
10942
11978
  getValue,
10943
11979
  id,
10944
- label: 'Options expression',
11980
+ label: 'Key',
10945
11981
  setValue,
10946
- variables
11982
+ validate
11983
+ });
11984
+ }
11985
+
11986
+ // helpers //////////////////////
11987
+
11988
+ /**
11989
+ * @param {string|void} value
11990
+ * @returns {string|null}
11991
+ */
11992
+ function validate(value) {
11993
+ if (!minDash.isString(value) || value.length === 0) {
11994
+ return 'Must not be empty.';
11995
+ }
11996
+ return null;
11997
+ }
11998
+
11999
+ const path = ['columns'];
12000
+ function StaticColumnsSourceEntry(props) {
12001
+ const {
12002
+ editField,
12003
+ field,
12004
+ id: idPrefix
12005
+ } = props;
12006
+ const {
12007
+ columns
12008
+ } = field;
12009
+ const addEntry = event => {
12010
+ event.stopPropagation();
12011
+ const entry = {
12012
+ label: 'Column',
12013
+ key: 'inputVariable'
12014
+ };
12015
+ editField(field, path, arrayAdd(columns, columns.length, entry));
12016
+ };
12017
+ const removeEntry = entry => {
12018
+ editField(field, path, minDash.without(columns, entry));
12019
+ };
12020
+ const items = columns.map((entry, index) => {
12021
+ const id = `${idPrefix}-${index}`;
12022
+ return {
12023
+ id,
12024
+ label: entry.label || entry.key,
12025
+ entries: ColumnEntry({
12026
+ editField,
12027
+ field,
12028
+ idPrefix: id,
12029
+ index
12030
+ }),
12031
+ autoFocusEntry: `${id}-label`,
12032
+ remove: () => removeEntry(entry)
12033
+ };
10947
12034
  });
12035
+ return {
12036
+ items,
12037
+ add: addEntry,
12038
+ shouldSort: false
12039
+ };
10948
12040
  }
10949
12041
 
10950
12042
  function GeneralGroup(field, editField, getService) {
@@ -10959,13 +12051,16 @@ function GeneralGroup(field, editField, getService) {
10959
12051
  editField
10960
12052
  }), ...KeyEntry({
10961
12053
  field,
10962
- editField
12054
+ editField,
12055
+ getService
10963
12056
  }), ...PathEntry({
10964
12057
  field,
10965
- editField
10966
- }), ...GroupEntries({
12058
+ editField,
12059
+ getService
12060
+ }), ...RepeatableEntry({
10967
12061
  field,
10968
- editField
12062
+ editField,
12063
+ getService
10969
12064
  }), ...DefaultOptionEntry({
10970
12065
  field,
10971
12066
  editField
@@ -10979,7 +12074,13 @@ function GeneralGroup(field, editField, getService) {
10979
12074
  field,
10980
12075
  editField,
10981
12076
  getService
10982
- }), ...SpacerEntry({
12077
+ }), ...IFrameUrlEntry({
12078
+ field,
12079
+ editField
12080
+ }), ...IFrameHeightEntry({
12081
+ field,
12082
+ editField
12083
+ }), ...HeightEntry({
10983
12084
  field,
10984
12085
  editField
10985
12086
  }), ...NumberEntries({
@@ -11000,6 +12101,15 @@ function GeneralGroup(field, editField, getService) {
11000
12101
  }), ...ReadonlyEntry({
11001
12102
  field,
11002
12103
  editField
12104
+ }), ...TableDataSourceEntry({
12105
+ field,
12106
+ editField
12107
+ }), ...PaginationEntry({
12108
+ field,
12109
+ editField
12110
+ }), ...RowCountEntry({
12111
+ field,
12112
+ editField
11003
12113
  })];
11004
12114
  if (entries.length === 0) {
11005
12115
  return null;
@@ -11290,67 +12400,66 @@ function ValidationType(props) {
11290
12400
  });
11291
12401
  }
11292
12402
 
11293
- function ValuesGroups(field, editField, getService) {
12403
+ function OptionsGroups(field, editField, getService) {
11294
12404
  const {
11295
- type,
11296
- id: fieldId
12405
+ type
11297
12406
  } = field;
11298
12407
  const formFields = getService('formFields');
11299
12408
  const fieldDefinition = formFields.get(type).config;
11300
- if (!VALUES_INPUTS.includes(type) && !hasValuesGroupsConfigured(fieldDefinition)) {
12409
+ if (!OPTIONS_INPUTS.includes(type) && !hasOptionsGroupsConfigured(fieldDefinition)) {
11301
12410
  return [];
11302
12411
  }
11303
12412
  const context = {
11304
12413
  editField,
11305
12414
  field
11306
12415
  };
11307
- const valuesSourceId = `${fieldId}-valuesSource`;
12416
+ const id = 'valuesSource';
11308
12417
 
11309
12418
  /**
11310
12419
  * @type {Array<Group|ListGroup>}
11311
12420
  */
11312
12421
  const groups = [{
11313
- id: valuesSourceId,
12422
+ id,
11314
12423
  label: 'Options source',
11315
12424
  tooltip: getValuesTooltip(),
11316
12425
  component: Group,
11317
- entries: ValuesSourceSelectEntry({
12426
+ entries: OptionsSourceSelectEntry({
11318
12427
  ...context,
11319
- id: valuesSourceId
12428
+ id
11320
12429
  })
11321
12430
  }];
11322
- const valuesSource = formJsViewer.getValuesSource(field);
11323
- if (valuesSource === formJsViewer.VALUES_SOURCES.INPUT) {
11324
- const dynamicValuesId = `${fieldId}-dynamicValues`;
12431
+ const valuesSource = formJsViewer.getOptionsSource(field);
12432
+ if (valuesSource === formJsViewer.OPTIONS_SOURCES.INPUT) {
12433
+ const id = 'dynamicOptions';
11325
12434
  groups.push({
11326
- id: dynamicValuesId,
12435
+ id,
11327
12436
  label: 'Dynamic options',
11328
12437
  component: Group,
11329
- entries: InputKeyValuesSourceEntry({
12438
+ entries: InputKeyOptionsSourceEntry({
11330
12439
  ...context,
11331
- id: dynamicValuesId
12440
+ id
11332
12441
  })
11333
12442
  });
11334
- } else if (valuesSource === formJsViewer.VALUES_SOURCES.STATIC) {
11335
- const staticValuesId = `${fieldId}-staticValues`;
12443
+ } else if (valuesSource === formJsViewer.OPTIONS_SOURCES.STATIC) {
12444
+ const id = 'staticOptions';
11336
12445
  groups.push({
11337
- id: staticValuesId,
12446
+ id,
11338
12447
  label: 'Static options',
11339
12448
  component: ListGroup,
11340
- ...StaticValuesSourceEntry({
12449
+ ...StaticOptionsSourceEntry({
11341
12450
  ...context,
11342
- id: staticValuesId
12451
+ id
11343
12452
  })
11344
12453
  });
11345
- } else if (valuesSource === formJsViewer.VALUES_SOURCES.EXPRESSION) {
11346
- const valuesExpressionId = `${fieldId}-valuesExpression`;
12454
+ } else if (valuesSource === formJsViewer.OPTIONS_SOURCES.EXPRESSION) {
12455
+ const id = 'optionsExpression';
11347
12456
  groups.push({
11348
- id: valuesExpressionId,
12457
+ id,
11349
12458
  label: 'Options expression',
11350
12459
  component: Group,
11351
- entries: ValuesExpressionEntry({
12460
+ entries: OptionsExpressionEntry({
11352
12461
  ...context,
11353
- id: valuesExpressionId
12462
+ id
11354
12463
  })
11355
12464
  });
11356
12465
  }
@@ -11400,7 +12509,7 @@ function CustomPropertiesGroup(field, editField) {
11400
12509
  event.stopPropagation();
11401
12510
  return editField(field, ['properties'], removeKey(properties, key));
11402
12511
  };
11403
- const id = `${field.id}-property-${index}`;
12512
+ const id = `property-${index}`;
11404
12513
  return {
11405
12514
  autoFocusEntry: id + '-key',
11406
12515
  entries: CustomValueEntry({
@@ -11449,10 +12558,16 @@ function removeKey(properties, oldKey) {
11449
12558
  }, {});
11450
12559
  }
11451
12560
 
11452
- function AppearanceGroup(field, editField) {
12561
+ function AppearanceGroup(field, editField, getService) {
11453
12562
  const entries = [...AdornerEntry({
11454
12563
  field,
11455
12564
  editField
12565
+ }), ...GroupAppearanceEntry({
12566
+ field,
12567
+ editField
12568
+ }), ...LayouterAppearanceEntry({
12569
+ field,
12570
+ editField
11456
12571
  })];
11457
12572
  if (!entries.length) {
11458
12573
  return null;
@@ -11503,6 +12618,55 @@ function ConditionGroup(field, editField) {
11503
12618
  };
11504
12619
  }
11505
12620
 
12621
+ function TableHeaderGroups(field, editField) {
12622
+ const {
12623
+ type,
12624
+ id: fieldId
12625
+ } = field;
12626
+ if (type !== 'table') {
12627
+ return [];
12628
+ }
12629
+ const areStaticColumnsEnabled = minDash.isArray(minDash.get(field, ['columns']));
12630
+
12631
+ /**
12632
+ * @type {Array<Group>}
12633
+ */
12634
+ const groups = [{
12635
+ id: `${fieldId}-columnsSource`,
12636
+ label: 'Headers source',
12637
+ tooltip: TOOLTIP_TEXT,
12638
+ component: Group,
12639
+ entries: [...HeadersSourceSelectEntry({
12640
+ field,
12641
+ editField
12642
+ }), ...ColumnsExpressionEntry({
12643
+ field,
12644
+ editField
12645
+ })]
12646
+ }];
12647
+ if (areStaticColumnsEnabled) {
12648
+ const id = `${fieldId}-columns`;
12649
+ groups.push({
12650
+ id,
12651
+ label: 'Header items',
12652
+ component: ListGroup,
12653
+ ...StaticColumnsSourceEntry({
12654
+ field,
12655
+ editField,
12656
+ id
12657
+ })
12658
+ });
12659
+ }
12660
+ return groups;
12661
+ }
12662
+
12663
+ // helpers //////////
12664
+
12665
+ const TOOLTIP_TEXT = `"List of items" defines a constant, predefined set of form options.
12666
+
12667
+ "Expression" defines options that are populated from a FEEL expression.
12668
+ `;
12669
+
11506
12670
  class PropertiesProvider {
11507
12671
  constructor(propertiesPanel, injector) {
11508
12672
  this._injector = injector;
@@ -11538,7 +12702,7 @@ class PropertiesProvider {
11538
12702
  return groups;
11539
12703
  }
11540
12704
  const getService = (type, strict = true) => this._injector.get(type, strict);
11541
- 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);
12705
+ 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);
11542
12706
  this._filterVisibleEntries(groups, field, getService);
11543
12707
 
11544
12708
  // contract: if a group has no entries or items, it should not be displayed at all
@@ -11604,6 +12768,58 @@ var RenderInjectionModule = {
11604
12768
  renderInjector: ['type', RenderInjector]
11605
12769
  };
11606
12770
 
12771
+ var _path;
12772
+ 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); }
12773
+ var SvgRepeat = function SvgRepeat(props) {
12774
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends({
12775
+ xmlns: "http://www.w3.org/2000/svg",
12776
+ width: 16,
12777
+ height: 16,
12778
+ fill: "none"
12779
+ }, props), _path || (_path = /*#__PURE__*/React__namespace.createElement("path", {
12780
+ fill: "currentColor",
12781
+ 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"
12782
+ })));
12783
+ };
12784
+ var RepeatSvg = SvgRepeat;
12785
+
12786
+ class RepeatRenderManager {
12787
+ constructor(formFields, formFieldRegistry) {
12788
+ this._formFields = formFields;
12789
+ this._formFieldRegistry = formFieldRegistry;
12790
+ this.RepeatFooter = this.RepeatFooter.bind(this);
12791
+ }
12792
+
12793
+ /**
12794
+ * Checks whether a field should be repeatable.
12795
+ *
12796
+ * @param {string} id - The id of the field to check
12797
+ * @returns {boolean} - True if repeatable, false otherwise
12798
+ */
12799
+ isFieldRepeating(id) {
12800
+ if (!id) {
12801
+ return false;
12802
+ }
12803
+ const formField = this._formFieldRegistry.get(id);
12804
+ const formFieldDefinition = this._formFields.get(formField.type);
12805
+ return formFieldDefinition.config.repeatable && formField.isRepeating;
12806
+ }
12807
+ RepeatFooter() {
12808
+ return jsxRuntime.jsxs("div", {
12809
+ className: "fjs-repeat-render-footer",
12810
+ children: [jsxRuntime.jsx(RepeatSvg, {}), jsxRuntime.jsx("span", {
12811
+ children: "Repeatable"
12812
+ })]
12813
+ });
12814
+ }
12815
+ }
12816
+ RepeatRenderManager.$inject = ['formFields', 'formFieldRegistry'];
12817
+
12818
+ var RepeatRenderManagerModule = {
12819
+ __init__: ['repeatRenderManager'],
12820
+ repeatRenderManager: ['type', RepeatRenderManager]
12821
+ };
12822
+
11607
12823
  class EditorTemplating {
11608
12824
  // same rules as viewer templating
11609
12825
  isTemplate(value) {
@@ -11875,7 +13091,7 @@ class FormEditor {
11875
13091
  * @internal
11876
13092
  */
11877
13093
  _getModules() {
11878
- return [ModelingModule, EditorActionsModule, DraggingModule, KeyboardModule, SelectionModule, PaletteModule, ExpressionLanguageModule, formJsViewer.MarkdownModule, PropertiesPanelModule, RenderInjectionModule];
13094
+ return [ModelingModule, EditorActionsModule, DraggingModule, KeyboardModule, SelectionModule, PaletteModule, ExpressionLanguageModule, formJsViewer.MarkdownModule, PropertiesPanelModule, RenderInjectionModule, RepeatRenderManagerModule];
11879
13095
  }
11880
13096
 
11881
13097
  /**