@bpmn-io/form-js-editor 1.6.4 → 1.7.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 (140) hide show
  1. package/dist/assets/form-js-editor-base.css +23 -2
  2. package/dist/assets/form-js-editor.css +60 -21
  3. package/dist/assets/properties-panel.css +40 -21
  4. package/dist/index.cjs +1770 -1432
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.es.js +1774 -1436
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/types/FormEditor.d.ts +29 -29
  9. package/dist/types/core/Debounce.d.ts +2 -3
  10. package/dist/types/core/EventBus.d.ts +1 -1
  11. package/dist/types/core/FormFieldRegistry.d.ts +1 -1
  12. package/dist/types/core/FormLayoutValidator.d.ts +7 -8
  13. package/dist/types/core/FormLayouter.d.ts +1 -1
  14. package/dist/types/core/index.d.ts +8 -9
  15. package/dist/types/features/SectionModuleBase.d.ts +1 -1
  16. package/dist/types/features/dragging/Dragging.d.ts +13 -14
  17. package/dist/types/features/dragging/index.d.ts +2 -3
  18. package/dist/types/features/editor-actions/FormEditorActions.d.ts +2 -3
  19. package/dist/types/features/editor-actions/index.d.ts +2 -3
  20. package/dist/types/features/expression-language/EditorTemplating.d.ts +2 -3
  21. package/dist/types/features/expression-language/index.d.ts +2 -3
  22. package/dist/types/features/keyboard/FormEditorKeyboardBindings.d.ts +2 -3
  23. package/dist/types/features/keyboard/index.d.ts +2 -3
  24. package/dist/types/features/modeling/FormLayoutUpdater.d.ts +2 -3
  25. package/dist/types/features/modeling/Modeling.d.ts +9 -10
  26. package/dist/types/features/modeling/behavior/IdBehavior.d.ts +2 -3
  27. package/dist/types/features/modeling/behavior/KeyBehavior.d.ts +2 -3
  28. package/dist/types/features/modeling/behavior/OptionsSourceBehavior.d.ts +2 -3
  29. package/dist/types/features/modeling/behavior/PathBehavior.d.ts +2 -3
  30. package/dist/types/features/modeling/behavior/ValidateBehavior.d.ts +2 -3
  31. package/dist/types/features/modeling/behavior/index.d.ts +6 -7
  32. package/dist/types/features/modeling/cmd/AddFormFieldHandler.d.ts +7 -8
  33. package/dist/types/features/modeling/cmd/EditFormFieldHandler.d.ts +7 -8
  34. package/dist/types/features/modeling/cmd/MoveFormFieldHandler.d.ts +9 -10
  35. package/dist/types/features/modeling/cmd/RemoveFormFieldHandler.d.ts +7 -8
  36. package/dist/types/features/modeling/cmd/UpdateIdClaimHandler.d.ts +5 -6
  37. package/dist/types/features/modeling/cmd/UpdateKeyClaimHandler.d.ts +3 -4
  38. package/dist/types/features/modeling/cmd/UpdatePathClaimHandler.d.ts +3 -4
  39. package/dist/types/features/modeling/index.d.ts +8 -9
  40. package/dist/types/features/palette/PaletteRenderer.d.ts +7 -0
  41. package/dist/types/features/palette/components/Palette.d.ts +2 -2
  42. package/dist/types/features/palette/components/PaletteEntry.d.ts +1 -1
  43. package/dist/types/features/palette/index.d.ts +3 -4
  44. package/dist/types/features/properties-panel/PropertiesPanel.d.ts +1 -1
  45. package/dist/types/features/properties-panel/PropertiesPanelRenderer.d.ts +4 -5
  46. package/dist/types/features/properties-panel/PropertiesProvider.d.ts +2 -3
  47. package/dist/types/features/properties-panel/Util.d.ts +6 -1
  48. package/dist/types/features/properties-panel/components/AutoFocusSelect.d.ts +1 -1
  49. package/dist/types/features/properties-panel/components/index.d.ts +1 -1
  50. package/dist/types/features/properties-panel/context/FormPropertiesPanelContext.d.ts +2 -2
  51. package/dist/types/features/properties-panel/context/index.d.ts +1 -1
  52. package/dist/types/features/properties-panel/entries/ActionEntry.d.ts +1 -1
  53. package/dist/types/features/properties-panel/entries/AdornerEntry.d.ts +1 -1
  54. package/dist/types/features/properties-panel/entries/AltTextEntry.d.ts +1 -1
  55. package/dist/types/features/properties-panel/entries/ColumnEntry.d.ts +0 -1
  56. package/dist/types/features/properties-panel/entries/ColumnsEntry.d.ts +1 -1
  57. package/dist/types/features/properties-panel/entries/ConditionEntry.d.ts +1 -1
  58. package/dist/types/features/properties-panel/entries/CustomValueEntry.d.ts +1 -1
  59. package/dist/types/features/properties-panel/entries/DateTimeConstraintsEntry.d.ts +1 -1
  60. package/dist/types/features/properties-panel/entries/DateTimeEntry.d.ts +1 -1
  61. package/dist/types/features/properties-panel/entries/{DateTimeSerializationEntry.d.ts → DateTimeFormatEntry.d.ts} +1 -1
  62. package/dist/types/features/properties-panel/entries/DefaultValueEntry.d.ts +1 -1
  63. package/dist/types/features/properties-panel/entries/DescriptionEntry.d.ts +1 -1
  64. package/dist/types/features/properties-panel/entries/DisabledEntry.d.ts +1 -1
  65. package/dist/types/features/properties-panel/entries/GroupAppearanceEntry.d.ts +1 -1
  66. package/dist/types/features/properties-panel/entries/HeightEntry.d.ts +1 -1
  67. package/dist/types/features/properties-panel/entries/HtmlEntry.d.ts +10 -0
  68. package/dist/types/features/properties-panel/entries/IFrameHeightEntry.d.ts +1 -1
  69. package/dist/types/features/properties-panel/entries/IFrameUrlEntry.d.ts +1 -1
  70. package/dist/types/features/properties-panel/entries/IdEntry.d.ts +1 -1
  71. package/dist/types/features/properties-panel/entries/ImageSourceEntry.d.ts +1 -1
  72. package/dist/types/features/properties-panel/entries/InputKeyOptionsSourceEntry.d.ts +1 -1
  73. package/dist/types/features/properties-panel/entries/KeyEntry.d.ts +1 -1
  74. package/dist/types/features/properties-panel/entries/LabelEntry.d.ts +1 -1
  75. package/dist/types/features/properties-panel/entries/LayouterAppearanceEntry.d.ts +1 -1
  76. package/dist/types/features/properties-panel/entries/NumberEntries.d.ts +1 -1
  77. package/dist/types/features/properties-panel/entries/NumberSerializationEntry.d.ts +1 -1
  78. package/dist/types/features/properties-panel/entries/OptionsExpressionEntry.d.ts +1 -1
  79. package/dist/types/features/properties-panel/entries/OptionsSourceSelectEntry.d.ts +1 -1
  80. package/dist/types/features/properties-panel/entries/PathEntry.d.ts +1 -1
  81. package/dist/types/features/properties-panel/entries/ReadonlyEntry.d.ts +1 -1
  82. package/dist/types/features/properties-panel/entries/RepeatableEntry.d.ts +1 -2
  83. package/dist/types/features/properties-panel/entries/SelectEntries.d.ts +1 -1
  84. package/dist/types/features/properties-panel/entries/StaticOptionsSourceEntry.d.ts +1 -1
  85. package/dist/types/features/properties-panel/entries/TextEntry.d.ts +1 -1
  86. package/dist/types/features/properties-panel/entries/ValueEntry.d.ts +1 -1
  87. package/dist/types/features/properties-panel/entries/factories/index.d.ts +4 -4
  88. package/dist/types/features/properties-panel/entries/factories/simpleBoolEntryFactory.d.ts +1 -1
  89. package/dist/types/features/properties-panel/entries/factories/simpleRangeIntegerEntryFactory.d.ts +0 -1
  90. package/dist/types/features/properties-panel/entries/factories/simpleSelectEntryFactory.d.ts +1 -1
  91. package/dist/types/features/properties-panel/entries/factories/simpleStringEntryFactory.d.ts +1 -1
  92. package/dist/types/features/properties-panel/entries/factories/zeroPositiveIntegerEntryFactory.d.ts +1 -1
  93. package/dist/types/features/properties-panel/entries/index.d.ts +34 -33
  94. package/dist/types/features/properties-panel/groups/AppearanceGroup.d.ts +1 -1
  95. package/dist/types/features/properties-panel/groups/ConstraintsGroup.d.ts +1 -1
  96. package/dist/types/features/properties-panel/groups/CustomPropertiesGroup.d.ts +1 -1
  97. package/dist/types/features/properties-panel/groups/GeneralGroup.d.ts +1 -2
  98. package/dist/types/features/properties-panel/groups/LayoutGroup.d.ts +1 -1
  99. package/dist/types/features/properties-panel/groups/OptionsGroups.d.ts +1 -1
  100. package/dist/types/features/properties-panel/groups/SecurityAttributesGroup.d.ts +20 -0
  101. package/dist/types/features/properties-panel/groups/SerializationGroup.d.ts +1 -1
  102. package/dist/types/features/properties-panel/groups/ValidationGroup.d.ts +1 -1
  103. package/dist/types/features/properties-panel/groups/index.d.ts +9 -8
  104. package/dist/types/features/properties-panel/hooks/index.d.ts +1 -1
  105. package/dist/types/features/properties-panel/hooks/usePropertiesPanelService.d.ts +1 -1
  106. package/dist/types/features/properties-panel/index.d.ts +4 -5
  107. package/dist/types/features/render-injection/RenderInjector.d.ts +3 -4
  108. package/dist/types/features/render-injection/components/InjectedRendersRoot.d.ts +1 -2
  109. package/dist/types/features/render-injection/index.d.ts +2 -3
  110. package/dist/types/features/render-injection/slot-fill/Fill.d.ts +1 -2
  111. package/dist/types/features/render-injection/slot-fill/FillContext.d.ts +1 -2
  112. package/dist/types/features/render-injection/slot-fill/Slot.d.ts +1 -2
  113. package/dist/types/features/render-injection/slot-fill/SlotContext.d.ts +1 -2
  114. package/dist/types/features/render-injection/slot-fill/SlotFillRoot.d.ts +1 -2
  115. package/dist/types/features/render-injection/slot-fill/index.d.ts +5 -5
  116. package/dist/types/features/repeat-render/EditorRepeatRenderManager.d.ts +2 -3
  117. package/dist/types/features/repeat-render/index.d.ts +2 -3
  118. package/dist/types/features/selection/Selection.d.ts +2 -3
  119. package/dist/types/features/selection/SelectionBehavior.d.ts +2 -3
  120. package/dist/types/features/selection/index.d.ts +3 -4
  121. package/dist/types/index.d.ts +1 -1
  122. package/dist/types/render/EditorFormFields.d.ts +1 -1
  123. package/dist/types/render/Renderer.d.ts +6 -7
  124. package/dist/types/render/components/FormEditor.d.ts +1 -1
  125. package/dist/types/render/components/ModularSection.d.ts +1 -2
  126. package/dist/types/render/components/editor-form-fields/EditorHtml.d.ts +5 -0
  127. package/dist/types/render/components/editor-form-fields/EditorIFrame.d.ts +2 -3
  128. package/dist/types/render/components/editor-form-fields/EditorTable.d.ts +2 -3
  129. package/dist/types/render/components/editor-form-fields/EditorText.d.ts +2 -3
  130. package/dist/types/render/components/editor-form-fields/index.d.ts +5 -4
  131. package/dist/types/render/context/DragAndDropContext.d.ts +1 -2
  132. package/dist/types/render/context/FormEditorContext.d.ts +2 -2
  133. package/dist/types/render/context/index.d.ts +2 -2
  134. package/dist/types/render/hooks/index.d.ts +3 -3
  135. package/dist/types/render/hooks/useDebounce.d.ts +4 -1
  136. package/dist/types/render/hooks/usePrevious.d.ts +1 -1
  137. package/dist/types/render/hooks/useService.d.ts +1 -1
  138. package/dist/types/render/index.d.ts +3 -4
  139. package/package.json +6 -6
  140. package/dist/types/features/palette/PaletteModule.d.ts +0 -8
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var formJsViewer = require('@bpmn-io/form-js-viewer');
4
3
  var Ids = require('ids');
4
+ var formJsViewer = require('@bpmn-io/form-js-viewer');
5
5
  var minDash = require('min-dash');
6
6
  var classnames = require('classnames');
7
7
  var jsxRuntime = require('preact/jsx-runtime');
@@ -604,8 +604,8 @@ class FormLayoutValidator {
604
604
  /**
605
605
  * @constructor
606
606
  *
607
- * @param { import('./FormLayouter').default } formLayouter
608
- * @param { import('./FormFieldRegistry').default } formFieldRegistry
607
+ * @param { import('./FormLayouter').FormLayouter } formLayouter
608
+ * @param { import('./FormFieldRegistry').FormFieldRegistry } formFieldRegistry
609
609
  */
610
610
  constructor(formLayouter, formFieldRegistry) {
611
611
  this._formLayouter = formLayouter;
@@ -767,13 +767,21 @@ function createEmptyImage() {
767
767
 
768
768
  function EditorIFrame(props) {
769
769
  const {
770
- field
770
+ field,
771
+ domId
771
772
  } = props;
773
+ const {
774
+ label
775
+ } = field;
772
776
  const Icon = formJsViewer.iconsByType(field.type);
773
- return jsxRuntime.jsx("div", {
777
+ return jsxRuntime.jsxs("div", {
774
778
  class: editorFormFieldClasses(field.type),
775
- children: jsxRuntime.jsx("div", {
779
+ children: [jsxRuntime.jsx(formJsViewer.Label, {
780
+ id: domId,
781
+ label: label
782
+ }), jsxRuntime.jsx("div", {
776
783
  class: "fjs-iframe-placeholder",
784
+ id: domId,
777
785
  children: jsxRuntime.jsxs("p", {
778
786
  class: "fjs-iframe-placeholder-text",
779
787
  children: [jsxRuntime.jsx(Icon, {
@@ -782,7 +790,7 @@ function EditorIFrame(props) {
782
790
  viewBox: "0 0 56 56"
783
791
  }), "iFrame"]
784
792
  })
785
- })
793
+ })]
786
794
  });
787
795
  }
788
796
  EditorIFrame.config = formJsViewer.IFrame.config;
@@ -790,7 +798,6 @@ EditorIFrame.config = formJsViewer.IFrame.config;
790
798
  const DragAndDropContext = preact.createContext({
791
799
  drake: null
792
800
  });
793
- var DragAndDropContext$1 = DragAndDropContext;
794
801
 
795
802
  /**
796
803
  * @param {string} type
@@ -802,26 +809,28 @@ function getService$1(type, strict) {}
802
809
  const FormEditorContext = preact.createContext({
803
810
  getService: getService$1
804
811
  });
805
- var FormEditorContext$1 = FormEditorContext;
806
812
 
807
- function useService$1 (type, strict) {
813
+ function useService$1(type, strict) {
808
814
  const {
809
815
  getService
810
- } = hooks.useContext(FormEditorContext$1);
816
+ } = hooks.useContext(FormEditorContext);
811
817
  return getService(type, strict);
812
818
  }
813
819
 
814
- function usePrevious$1(value) {
815
- const ref = hooks.useRef();
816
- hooks.useEffect(() => ref.current = value);
820
+ function usePrevious$1(value, defaultValue = null) {
821
+ const ref = hooks.useRef(defaultValue);
822
+ hooks.useEffect(() => ref.current = value, [value]);
817
823
  return ref.current;
818
824
  }
819
825
 
820
- function useDebounce(fn, dependencies = []) {
826
+ /**
827
+ * @param {Function} fn - function to debounce
828
+ */
829
+ function useDebounce(fn) {
821
830
  const debounce = useService$1('debounce');
822
831
  const callback = hooks.useMemo(() => {
823
832
  return debounce(fn);
824
- }, dependencies);
833
+ }, [debounce, fn]);
825
834
 
826
835
  // cleanup async side-effect if callback #flush is provided.
827
836
  hooks.useEffect(() => {
@@ -1021,6 +1030,54 @@ function EditorText(props) {
1021
1030
  }
1022
1031
  EditorText.config = formJsViewer.Text.config;
1023
1032
 
1033
+ function EditorHtml(props) {
1034
+ const {
1035
+ type,
1036
+ content = ''
1037
+ } = props.field;
1038
+ const Icon = formJsViewer.iconsByType(type);
1039
+ const templating = useService$1('templating');
1040
+ const expressionLanguage = useService$1('expressionLanguage');
1041
+ if (!content || !content.trim()) {
1042
+ return jsxRuntime.jsx("div", {
1043
+ class: editorFormFieldClasses(type),
1044
+ children: jsxRuntime.jsxs("div", {
1045
+ class: "fjs-form-field-placeholder",
1046
+ children: [jsxRuntime.jsx(Icon, {
1047
+ viewBox: "0 0 54 54"
1048
+ }), "Html is empty"]
1049
+ })
1050
+ });
1051
+ }
1052
+ if (expressionLanguage.isExpression(content)) {
1053
+ return jsxRuntime.jsx("div", {
1054
+ class: editorFormFieldClasses(type),
1055
+ children: jsxRuntime.jsxs("div", {
1056
+ class: "fjs-form-field-placeholder",
1057
+ children: [jsxRuntime.jsx(Icon, {
1058
+ viewBox: "0 0 54 54"
1059
+ }), "Html is populated by an expression"]
1060
+ })
1061
+ });
1062
+ }
1063
+ if (templating.isTemplate(content)) {
1064
+ return jsxRuntime.jsx("div", {
1065
+ class: editorFormFieldClasses(type),
1066
+ children: jsxRuntime.jsxs("div", {
1067
+ class: "fjs-form-field-placeholder",
1068
+ children: [jsxRuntime.jsx(Icon, {
1069
+ viewBox: "0 0 54 54"
1070
+ }), "Html is templated"]
1071
+ })
1072
+ });
1073
+ }
1074
+ return jsxRuntime.jsx(formJsViewer.Html, {
1075
+ ...props,
1076
+ disableLinks: true
1077
+ });
1078
+ }
1079
+ EditorHtml.config = formJsViewer.Html.config;
1080
+
1024
1081
  function EditorTable(props) {
1025
1082
  const {
1026
1083
  columnsExpression,
@@ -1085,7 +1142,7 @@ function EditorTable(props) {
1085
1142
  }
1086
1143
  EditorTable.config = formJsViewer.Table.config;
1087
1144
 
1088
- const editorFormFields = [EditorIFrame, EditorText, EditorTable];
1145
+ const editorFormFields = [EditorIFrame, EditorText, EditorHtml, EditorTable];
1089
1146
 
1090
1147
  class EditorFormFields extends formJsViewer.FormFields {
1091
1148
  constructor() {
@@ -1096,7 +1153,7 @@ class EditorFormFields extends formJsViewer.FormFields {
1096
1153
  }
1097
1154
  }
1098
1155
 
1099
- var ModularSection = (props => {
1156
+ const ModularSection = props => {
1100
1157
  const {
1101
1158
  rootClass,
1102
1159
  RootElement,
@@ -1176,7 +1233,7 @@ var ModularSection = (props => {
1176
1233
  }), ParentElement) : jsxRuntime.jsx(Root, {
1177
1234
  children: children
1178
1235
  }) : null;
1179
- });
1236
+ };
1180
1237
 
1181
1238
  const FillContext = preact.createContext({
1182
1239
  addFill(uid, props) {
@@ -1186,11 +1243,10 @@ const FillContext = preact.createContext({
1186
1243
  throw new Error('FillContext.addFill() uninitialized');
1187
1244
  }
1188
1245
  });
1189
- var FillContext$1 = FillContext;
1190
1246
 
1191
- var Fill = (props => {
1247
+ const Fill = props => {
1192
1248
  const uid = React.useRef(Symbol('fill_uid'));
1193
- const fillContext = React.useContext(FillContext$1);
1249
+ const fillContext = React.useContext(FillContext);
1194
1250
  React.useEffect(() => {
1195
1251
  if (!fillContext) {
1196
1252
  return;
@@ -1204,14 +1260,13 @@ var Fill = (props => {
1204
1260
  };
1205
1261
  }, [fillContext, props]);
1206
1262
  return null;
1207
- });
1263
+ };
1208
1264
 
1209
1265
  const SlotContext = preact.createContext({
1210
1266
  fills: []
1211
1267
  });
1212
- var SlotContext$1 = SlotContext;
1213
1268
 
1214
- var Slot = (props => {
1269
+ const Slot = props => {
1215
1270
  const {
1216
1271
  name,
1217
1272
  fillRoot = FillFragment,
@@ -1221,7 +1276,7 @@ var Slot = (props => {
1221
1276
  } = props;
1222
1277
  const {
1223
1278
  fills
1224
- } = hooks.useContext(SlotContext$1);
1279
+ } = hooks.useContext(SlotContext);
1225
1280
  const filtered = hooks.useMemo(() => fills.filter(fill => fill.slot === name), [fills, name]);
1226
1281
  const cropped = hooks.useMemo(() => limit ? filtered.slice(0, limit) : filtered, [filtered, limit]);
1227
1282
  const groups = hooks.useMemo(() => groupFn(cropped), [cropped, groupFn]);
@@ -1229,7 +1284,7 @@ var Slot = (props => {
1229
1284
  return buildFills(groups, fillRoot, separatorFn);
1230
1285
  }, [groups, fillRoot, separatorFn]);
1231
1286
  return fillsAndSeparators;
1232
- });
1287
+ };
1233
1288
 
1234
1289
  /**
1235
1290
  * Creates a Fragment for a fill.
@@ -1293,30 +1348,35 @@ const _comparePriority = (a, b) => {
1293
1348
  return (b.priority || 0) - (a.priority || 0);
1294
1349
  };
1295
1350
 
1296
- var SlotFillRoot = (props => {
1351
+ const noop = () => {};
1352
+ const SlotFillRoot = props => {
1297
1353
  const [fills, setFills] = hooks.useState([]);
1354
+ const {
1355
+ onSetFill = noop,
1356
+ onRemoveFill = noop
1357
+ } = props;
1298
1358
  const fillContext = hooks.useMemo(() => ({
1299
1359
  addFill: fill => {
1300
1360
  setFills(fills => [...fills.filter(f => f.id !== fill.id), fill]);
1301
- props.onSetFill && props.onSetFill(fill);
1361
+ onSetFill(fill);
1302
1362
  },
1303
1363
  removeFill: id => {
1304
1364
  setFills(fills => fills.filter(f => f.id !== id));
1305
- props.onRemoveFill && props.onRemoveFill(id);
1365
+ onRemoveFill(id);
1306
1366
  }
1307
- }), []);
1367
+ }), [onRemoveFill, onSetFill]);
1308
1368
  const slotContext = hooks.useMemo(() => ({
1309
1369
  fills
1310
1370
  }), [fills]);
1311
- return jsxRuntime.jsx(SlotContext$1.Provider, {
1371
+ return jsxRuntime.jsx(SlotContext.Provider, {
1312
1372
  value: slotContext,
1313
- children: jsxRuntime.jsx(FillContext$1.Provider, {
1373
+ children: jsxRuntime.jsx(FillContext.Provider, {
1314
1374
  /* @ts-expect-error */
1315
1375
  value: fillContext,
1316
1376
  children: props.children
1317
1377
  })
1318
1378
  });
1319
- });
1379
+ };
1320
1380
 
1321
1381
  function PaletteEntry(props) {
1322
1382
  const {
@@ -1559,7 +1619,7 @@ function getPaletteIcon(entry) {
1559
1619
  return Icon;
1560
1620
  }
1561
1621
 
1562
- var InjectedRendersRoot = (() => {
1622
+ const InjectedRendersRoot = () => {
1563
1623
  const renderInjector = useService$1('renderInjector');
1564
1624
  const injectedRenderers = renderInjector.fetchRenderers();
1565
1625
  const injectedProps = hooks.useMemo(() => ({
@@ -1576,7 +1636,7 @@ var InjectedRendersRoot = (() => {
1576
1636
  ...injectedProps
1577
1637
  }))
1578
1638
  });
1579
- });
1639
+ };
1580
1640
 
1581
1641
  const CURSOR_CLS_PATTERN = /^fjs-cursor-.*$/;
1582
1642
  function set(mode) {
@@ -1608,11 +1668,11 @@ class Dragging {
1608
1668
  /**
1609
1669
  * @constructor
1610
1670
  *
1611
- * @param { import('../../core/FormFieldRegistry').default } formFieldRegistry
1612
- * @param { import('../../core/FormLayouter').default } formLayouter
1613
- * @param { import('../../core/FormLayoutValidator').default } formLayoutValidator
1614
- * @param { import('../../core/EventBus').default } eventBus
1615
- * @param { import('../modeling/Modeling').default } modeling
1671
+ * @param { import('../../core/FormFieldRegistry').FormFieldRegistry } formFieldRegistry
1672
+ * @param { import('../../core/FormLayouter').FormLayouter } formLayouter
1673
+ * @param { import('../../core/FormLayoutValidator').FormLayoutValidator } formLayoutValidator
1674
+ * @param { import('../../core/EventBus').EventBus } eventBus
1675
+ * @param { import('../modeling/Modeling').Modeling } modeling
1616
1676
  * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
1617
1677
  */
1618
1678
  constructor(formFieldRegistry, formLayouter, formLayoutValidator, eventBus, modeling, pathRegistry) {
@@ -2062,37 +2122,36 @@ function ContextPad(props) {
2062
2122
  children: props.children
2063
2123
  });
2064
2124
  }
2125
+ function EmptyGroup() {
2126
+ return jsxRuntime.jsx("div", {
2127
+ class: "fjs-empty-component",
2128
+ children: jsxRuntime.jsx("span", {
2129
+ class: "fjs-empty-component-text",
2130
+ children: "Drag and drop components here."
2131
+ })
2132
+ });
2133
+ }
2134
+ function EmptyForm() {
2135
+ return jsxRuntime.jsx("div", {
2136
+ class: "fjs-empty-editor",
2137
+ children: jsxRuntime.jsxs("div", {
2138
+ class: "fjs-empty-editor-card",
2139
+ children: [jsxRuntime.jsx(EmptyFormIcon, {}), jsxRuntime.jsx("h2", {
2140
+ children: "Build your form"
2141
+ }), jsxRuntime.jsx("span", {
2142
+ children: "Drag and drop components here to start designing."
2143
+ }), jsxRuntime.jsx("span", {
2144
+ children: "Use the preview window to test your form."
2145
+ })]
2146
+ })
2147
+ });
2148
+ }
2065
2149
  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
- });
2150
+ if (['group', 'dynamiclist'].includes(props.field.type)) {
2151
+ return jsxRuntime.jsx(EmptyGroup, {});
2088
2152
  }
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
- });
2153
+ if (props.field.type === 'default') {
2154
+ return jsxRuntime.jsx(EmptyForm, {});
2096
2155
  }
2097
2156
  return null;
2098
2157
  }
@@ -2116,22 +2175,22 @@ function Element$1(props) {
2116
2175
  } = field;
2117
2176
  const ref = hooks.useRef();
2118
2177
  const [hovered, setHovered] = hooks.useState(false);
2119
- function scrollIntoView({
2120
- selection
2121
- }) {
2122
- if (!selection || selection.id !== id || !ref.current) {
2123
- return;
2124
- }
2125
- const elementBounds = ref.current.getBoundingClientRect(),
2126
- containerBounds = formEditor._container.getBoundingClientRect();
2127
- if (elementBounds.top < 0 || elementBounds.top > containerBounds.bottom) {
2128
- ref.current.scrollIntoView();
2129
- }
2130
- }
2131
2178
  hooks.useEffect(() => {
2179
+ function scrollIntoView({
2180
+ selection
2181
+ }) {
2182
+ if (!selection || selection.id !== id || !ref.current) {
2183
+ return;
2184
+ }
2185
+ const elementBounds = ref.current.getBoundingClientRect(),
2186
+ containerBounds = formEditor._container.getBoundingClientRect();
2187
+ if (elementBounds.top < 0 || elementBounds.top > containerBounds.bottom) {
2188
+ ref.current.scrollIntoView();
2189
+ }
2190
+ }
2132
2191
  eventBus.on('selection.changed', scrollIntoView);
2133
2192
  return () => eventBus.off('selection.changed', scrollIntoView);
2134
- }, [id]);
2193
+ }, [eventBus, formEditor._container, id]);
2135
2194
  hooks.useLayoutEffect(() => {
2136
2195
  if (selection.isSelected(field)) {
2137
2196
  ref.current.focus();
@@ -2279,7 +2338,7 @@ function Column(props) {
2279
2338
  children: props.children
2280
2339
  });
2281
2340
  }
2282
- function FormEditor$1(props) {
2341
+ function FormEditor$1() {
2283
2342
  const dragging = useService$1('dragging'),
2284
2343
  eventBus = useService$1('eventBus'),
2285
2344
  formEditor = useService$1('formEditor'),
@@ -2297,16 +2356,19 @@ function FormEditor$1(props) {
2297
2356
  const formContainerRef = hooks.useRef(null);
2298
2357
  const propertiesPanelRef = hooks.useRef(null);
2299
2358
  const [, setSelection] = hooks.useState(schema);
2359
+ const [hasInitialized, setHasInitialized] = hooks.useState(false);
2300
2360
  hooks.useEffect(() => {
2301
2361
  function handleSelectionChanged(event) {
2302
2362
  setSelection(event.selection || schema);
2303
2363
  }
2304
2364
  eventBus.on('selection.changed', handleSelectionChanged);
2305
- setSelection(selection.get() || schema);
2306
2365
  return () => {
2307
2366
  eventBus.off('selection.changed', handleSelectionChanged);
2308
2367
  };
2309
- }, [schema, selection]);
2368
+ }, [eventBus, schema]);
2369
+ hooks.useEffect(() => {
2370
+ setSelection(selection.get() || schema);
2371
+ }, [selection, schema]);
2310
2372
  const [drake, setDrake] = hooks.useState(null);
2311
2373
  const dragAndDropContext = {
2312
2374
  drake
@@ -2357,11 +2419,15 @@ function FormEditor$1(props) {
2357
2419
 
2358
2420
  // fire event after render to notify interested parties
2359
2421
  hooks.useEffect(() => {
2422
+ if (hasInitialized) {
2423
+ return;
2424
+ }
2425
+ setHasInitialized(true);
2360
2426
  eventBus.fire('rendered');
2361
2427
 
2362
2428
  // keep deprecated event to ensure backward compatibility
2363
2429
  eventBus.fire('formEditor.rendered');
2364
- }, []);
2430
+ }, [eventBus, hasInitialized]);
2365
2431
  const formRenderContext = hooks.useMemo(() => ({
2366
2432
  Children,
2367
2433
  Column,
@@ -2405,7 +2471,7 @@ function FormEditor$1(props) {
2405
2471
  return jsxRuntime.jsx("div", {
2406
2472
  class: "fjs-form-editor",
2407
2473
  children: jsxRuntime.jsxs(SlotFillRoot, {
2408
- children: [jsxRuntime.jsxs(DragAndDropContext$1.Provider, {
2474
+ children: [jsxRuntime.jsxs(DragAndDropContext.Provider, {
2409
2475
  value: dragAndDropContext,
2410
2476
  children: [jsxRuntime.jsx(ModularSection, {
2411
2477
  rootClass: "fjs-palette-container",
@@ -2451,56 +2517,56 @@ function getFormFieldIndex(parent, formField) {
2451
2517
  function CreatePreview(props) {
2452
2518
  const {
2453
2519
  drake
2454
- } = hooks.useContext(DragAndDropContext$1);
2520
+ } = hooks.useContext(DragAndDropContext);
2455
2521
  const formFields = useService$1('formFields');
2456
- function handleCloned(clone, original, type) {
2457
- const fieldType = clone.dataset.fieldType;
2522
+ hooks.useEffect(() => {
2523
+ if (!drake) {
2524
+ return;
2525
+ }
2526
+ function handleCloned(clone, original, type) {
2527
+ const fieldType = clone.dataset.fieldType;
2458
2528
 
2459
- // (1) field preview
2460
- if (fieldType) {
2461
- const paletteEntry = findPaletteEntry(fieldType, formFields);
2462
- if (!paletteEntry) {
2463
- return;
2464
- }
2465
- const {
2466
- label
2467
- } = paletteEntry;
2468
- const Icon = getPaletteIcon(paletteEntry);
2469
- clone.innerHTML = '';
2470
- clone.class = 'gu-mirror';
2471
- clone.classList.add('fjs-field-preview-container');
2472
- if (original.classList.contains('fjs-palette-field')) {
2473
- // default to auto columns when creating from palette
2474
- clone.classList.add('cds--col');
2475
- }
2529
+ // (1) field preview
2530
+ if (fieldType) {
2531
+ const paletteEntry = findPaletteEntry(fieldType, formFields);
2532
+ if (!paletteEntry) {
2533
+ return;
2534
+ }
2535
+ const {
2536
+ label
2537
+ } = paletteEntry;
2538
+ const Icon = getPaletteIcon(paletteEntry);
2539
+ clone.innerHTML = '';
2540
+ clone.class = 'gu-mirror';
2541
+ clone.classList.add('fjs-field-preview-container');
2542
+ if (original.classList.contains('fjs-palette-field')) {
2543
+ // default to auto columns when creating from palette
2544
+ clone.classList.add('cds--col');
2545
+ }
2476
2546
 
2477
- // todo(pinussilvestrus): dragula, how to mitigate cursor position
2478
- // https://github.com/bevacqua/dragula/issues/285
2479
- preact.render(jsxRuntime.jsx(FieldDragPreview, {
2480
- label: label,
2481
- Icon: Icon
2482
- }), clone);
2483
- } else {
2484
- // (2) row preview
2547
+ // todo(pinussilvestrus): dragula, how to mitigate cursor position
2548
+ // https://github.com/bevacqua/dragula/issues/285
2549
+ preact.render(jsxRuntime.jsx(FieldDragPreview, {
2550
+ label: label,
2551
+ Icon: Icon
2552
+ }), clone);
2553
+ } else {
2554
+ // (2) row preview
2485
2555
 
2486
- // remove elements from copy (context pad, row dragger, ...)
2487
- ['fjs-context-pad', 'fjs-row-dragger', 'fjs-debug-columns'].forEach(cls => {
2488
- const cloneNode = clone.querySelectorAll('.' + cls);
2489
- cloneNode.length && cloneNode.forEach(e => e.remove());
2490
- });
2556
+ // remove elements from copy (context pad, row dragger, ...)
2557
+ ['fjs-context-pad', 'fjs-row-dragger', 'fjs-debug-columns'].forEach(cls => {
2558
+ const cloneNode = clone.querySelectorAll('.' + cls);
2559
+ cloneNode.length && cloneNode.forEach(e => e.remove());
2560
+ });
2491
2561
 
2492
- // mirror grid
2493
- clone.classList.add('cds--grid');
2494
- clone.classList.add('cds--grid--condensed');
2495
- }
2496
- }
2497
- hooks.useEffect(() => {
2498
- if (!drake) {
2499
- return;
2562
+ // mirror grid
2563
+ clone.classList.add('cds--grid');
2564
+ clone.classList.add('cds--grid--condensed');
2565
+ }
2500
2566
  }
2501
2567
  drake.on('cloned', handleCloned);
2502
2568
  return () => drake.off('cloned', handleCloned);
2503
- }, [drake]);
2569
+ }, [drake, formFields]);
2504
2570
  return null;
2505
2571
  }
2506
2572
 
@@ -2544,7 +2610,7 @@ class Renderer {
2544
2610
  }
2545
2611
  return jsxRuntime.jsx("div", {
2546
2612
  class: `fjs-container fjs-editor-container ${compact ? 'fjs-editor-compact' : ''}`,
2547
- children: jsxRuntime.jsx(FormEditorContext$1.Provider, {
2613
+ children: jsxRuntime.jsx(FormEditorContext.Provider, {
2548
2614
  value: formEditorContext,
2549
2615
  children: jsxRuntime.jsx(FormEditor$1, {})
2550
2616
  })
@@ -2560,14 +2626,14 @@ class Renderer {
2560
2626
  }
2561
2627
  Renderer.$inject = ['config.renderer', 'eventBus', 'formEditor', 'injector'];
2562
2628
 
2563
- var renderModule = {
2629
+ const RenderModule = {
2564
2630
  __init__: ['formFields', 'renderer'],
2565
2631
  formFields: ['type', EditorFormFields],
2566
2632
  renderer: ['type', Renderer]
2567
2633
  };
2568
2634
 
2569
- var core = {
2570
- __depends__: [renderModule],
2635
+ const CoreModule = {
2636
+ __depends__: [RenderModule],
2571
2637
  debounce: ['factory', DebounceFactory],
2572
2638
  eventBus: ['type', EventBus],
2573
2639
  importer: ['type', formJsViewer.Importer],
@@ -2809,7 +2875,7 @@ function error(action, message) {
2809
2875
  /**
2810
2876
  * @type { import('didi').ModuleDeclaration }
2811
2877
  */
2812
- var EditorActionsModule$1 = {
2878
+ var BaseEditorActionsModule = {
2813
2879
  __init__: ['editorActions'],
2814
2880
  editorActions: ['type', EditorActions]
2815
2881
  };
@@ -2858,14 +2924,28 @@ class FormEditorActions extends EditorActions {
2858
2924
  }
2859
2925
  FormEditorActions.$inject = ['eventBus', 'injector'];
2860
2926
 
2861
- var EditorActionsModule = {
2862
- __depends__: [EditorActionsModule$1],
2927
+ const EditorActionsModule = {
2928
+ __depends__: [BaseEditorActionsModule],
2863
2929
  editorActions: ['type', FormEditorActions]
2864
2930
  };
2865
2931
 
2866
- var DraggingModule = {
2867
- __init__: ['dragging'],
2868
- dragging: ['type', Dragging]
2932
+ class EditorTemplating {
2933
+ // same rules as viewer templating
2934
+ isTemplate(value) {
2935
+ return minDash.isString(value) && (value.startsWith('=') || /{{/.test(value));
2936
+ }
2937
+
2938
+ // return the template raw, as we usually just want to display that
2939
+ evaluate(template) {
2940
+ return template;
2941
+ }
2942
+ }
2943
+ EditorTemplating.$inject = [];
2944
+
2945
+ const EditorExpressionLanguageModule = {
2946
+ __init__: ['expressionLanguage', 'templating'],
2947
+ expressionLanguage: ['type', formJsViewer.FeelExpressionLanguage],
2948
+ templating: ['type', EditorTemplating]
2869
2949
  };
2870
2950
 
2871
2951
  var KEYS_COPY = ['c', 'C'];
@@ -3257,7 +3337,7 @@ KeyboardBindings.prototype.registerBindings = function (keyboard, editorActions)
3257
3337
  /**
3258
3338
  * @type { import('didi').ModuleDeclaration }
3259
3339
  */
3260
- var KeyboardModule$1 = {
3340
+ var KeyboardModule = {
3261
3341
  __init__: ['keyboard', 'keyboardBindings'],
3262
3342
  keyboard: ['type', Keyboard],
3263
3343
  keyboardBindings: ['type', KeyboardBindings]
@@ -3308,640 +3388,487 @@ class FormEditorKeyboardBindings {
3308
3388
  }
3309
3389
  FormEditorKeyboardBindings.$inject = ['eventBus', 'keyboard'];
3310
3390
 
3311
- var KeyboardModule = {
3312
- __depends__: [KeyboardModule$1],
3391
+ const FormEditorKeyboardModule = {
3392
+ __depends__: [KeyboardModule],
3313
3393
  __init__: ['keyboardBindings'],
3314
3394
  keyboardBindings: ['type', FormEditorKeyboardBindings]
3315
3395
  };
3316
3396
 
3317
- function arrayAdd$1(array, index, item) {
3318
- array.splice(index, 0, item);
3319
- return array;
3320
- }
3321
- function arrayRemove(array, index) {
3322
- array.splice(index, 1);
3323
- return array;
3324
- }
3325
- function updatePath(formFieldRegistry, formField, index) {
3326
- const parent = formFieldRegistry.get(formField._parent);
3327
- refreshPathsRecursively(formField, [...parent._path, 'components', index]);
3328
- return formField;
3329
- }
3330
- function refreshPathsRecursively(formField, path) {
3331
- formField._path = path;
3332
- const components = formField.components || [];
3333
- components.forEach((component, index) => {
3334
- refreshPathsRecursively(component, [...path, 'components', index]);
3335
- });
3336
- }
3337
- function updateRow(formField, rowId) {
3338
- formField.layout = {
3339
- ...(formField.layout || {}),
3340
- row: rowId
3341
- };
3342
- return formField;
3343
- }
3344
-
3345
- class AddFormFieldHandler {
3346
- /**
3347
- * @constructor
3348
- * @param { import('../../../FormEditor').default } formEditor
3349
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3350
- */
3351
- constructor(formEditor, formFieldRegistry) {
3352
- this._formEditor = formEditor;
3353
- this._formFieldRegistry = formFieldRegistry;
3354
- }
3355
- execute(context) {
3356
- const {
3357
- formField,
3358
- targetFormField,
3359
- targetIndex
3360
- } = context;
3361
- const {
3362
- schema
3363
- } = this._formEditor._getState();
3364
- const targetPath = [...targetFormField._path, 'components'];
3365
- formField._parent = targetFormField.id;
3366
-
3367
- // (1) Add new form field
3368
- arrayAdd$1(minDash.get(schema, targetPath), targetIndex, formField);
3369
-
3370
- // (2) Update internal paths of new form field and its siblings (and their children)
3371
- minDash.get(schema, targetPath).forEach((formField, index) => updatePath(this._formFieldRegistry, formField, index));
3372
-
3373
- // (3) Add new form field to form field registry
3374
- this._formFieldRegistry.add(formField);
3375
-
3376
- // TODO: Create updater/change support that automatically updates paths and schema on command execution
3377
- this._formEditor._setState({
3378
- schema
3379
- });
3380
- }
3381
- revert(context) {
3382
- const {
3383
- formField,
3384
- targetFormField,
3385
- targetIndex
3386
- } = context;
3387
- const {
3388
- schema
3389
- } = this._formEditor._getState();
3390
- const targetPath = [...targetFormField._path, 'components'];
3397
+ const DraggingModule = {
3398
+ __init__: ['dragging'],
3399
+ dragging: ['type', Dragging]
3400
+ };
3391
3401
 
3392
- // (1) Remove new form field
3393
- arrayRemove(minDash.get(schema, targetPath), targetIndex);
3402
+ /**
3403
+ * @typedef {import('didi').Injector} Injector
3404
+ *
3405
+ * @typedef {import('../core/Types').ElementLike} ElementLike
3406
+ *
3407
+ * @typedef {import('../core/EventBus').default} EventBus
3408
+ * @typedef {import('./CommandHandler').default} CommandHandler
3409
+ *
3410
+ * @typedef { any } CommandContext
3411
+ * @typedef { {
3412
+ * new (...args: any[]) : CommandHandler
3413
+ * } } CommandHandlerConstructor
3414
+ * @typedef { {
3415
+ * [key: string]: CommandHandler;
3416
+ * } } CommandHandlerMap
3417
+ * @typedef { {
3418
+ * command: string;
3419
+ * context: any;
3420
+ * id?: any;
3421
+ * } } CommandStackAction
3422
+ * @typedef { {
3423
+ * actions: CommandStackAction[];
3424
+ * dirty: ElementLike[];
3425
+ * trigger: 'execute' | 'undo' | 'redo' | 'clear' | null;
3426
+ * atomic?: boolean;
3427
+ * } } CurrentExecution
3428
+ */
3394
3429
 
3395
- // (2) Update internal paths of new form field and its siblings (and their children)
3396
- minDash.get(schema, targetPath).forEach((formField, index) => updatePath(this._formFieldRegistry, formField, index));
3430
+ /**
3431
+ * A service that offers un- and redoable execution of commands.
3432
+ *
3433
+ * The command stack is responsible for executing modeling actions
3434
+ * in a un- and redoable manner. To do this it delegates the actual
3435
+ * command execution to {@link CommandHandler}s.
3436
+ *
3437
+ * Command handlers provide {@link CommandHandler#execute(ctx)} and
3438
+ * {@link CommandHandler#revert(ctx)} methods to un- and redo a command
3439
+ * identified by a command context.
3440
+ *
3441
+ *
3442
+ * ## Life-Cycle events
3443
+ *
3444
+ * In the process the command stack fires a number of life-cycle events
3445
+ * that other components to participate in the command execution.
3446
+ *
3447
+ * * preExecute
3448
+ * * preExecuted
3449
+ * * execute
3450
+ * * executed
3451
+ * * postExecute
3452
+ * * postExecuted
3453
+ * * revert
3454
+ * * reverted
3455
+ *
3456
+ * A special event is used for validating, whether a command can be
3457
+ * performed prior to its execution.
3458
+ *
3459
+ * * canExecute
3460
+ *
3461
+ * Each of the events is fired as `commandStack.{eventName}` and
3462
+ * `commandStack.{commandName}.{eventName}`, respectively. This gives
3463
+ * components fine grained control on where to hook into.
3464
+ *
3465
+ * The event object fired transports `command`, the name of the
3466
+ * command and `context`, the command context.
3467
+ *
3468
+ *
3469
+ * ## Creating Command Handlers
3470
+ *
3471
+ * Command handlers should provide the {@link CommandHandler#execute(ctx)}
3472
+ * and {@link CommandHandler#revert(ctx)} methods to implement
3473
+ * redoing and undoing of a command.
3474
+ *
3475
+ * A command handler _must_ ensure undo is performed properly in order
3476
+ * not to break the undo chain. It must also return the shapes that
3477
+ * got changed during the `execute` and `revert` operations.
3478
+ *
3479
+ * Command handlers may execute other modeling operations (and thus
3480
+ * commands) in their `preExecute(d)` and `postExecute(d)` phases. The command
3481
+ * stack will properly group all commands together into a logical unit
3482
+ * that may be re- and undone atomically.
3483
+ *
3484
+ * Command handlers must not execute other commands from within their
3485
+ * core implementation (`execute`, `revert`).
3486
+ *
3487
+ *
3488
+ * ## Change Tracking
3489
+ *
3490
+ * During the execution of the CommandStack it will keep track of all
3491
+ * elements that have been touched during the command's execution.
3492
+ *
3493
+ * At the end of the CommandStack execution it will notify interested
3494
+ * components via an 'elements.changed' event with all the dirty
3495
+ * elements.
3496
+ *
3497
+ * The event can be picked up by components that are interested in the fact
3498
+ * that elements have been changed. One use case for this is updating
3499
+ * their graphical representation after moving / resizing or deletion.
3500
+ *
3501
+ * @see CommandHandler
3502
+ *
3503
+ * @param {EventBus} eventBus
3504
+ * @param {Injector} injector
3505
+ */
3506
+ function CommandStack(eventBus, injector) {
3507
+ /**
3508
+ * A map of all registered command handlers.
3509
+ *
3510
+ * @type {CommandHandlerMap}
3511
+ */
3512
+ this._handlerMap = {};
3397
3513
 
3398
- // (3) Remove new form field from form field registry
3399
- this._formFieldRegistry.remove(formField);
3514
+ /**
3515
+ * A stack containing all re/undoable actions on the diagram
3516
+ *
3517
+ * @type {CommandStackAction[]}
3518
+ */
3519
+ this._stack = [];
3400
3520
 
3401
- // TODO: Create updater/change support that automatically updates paths and schema on command execution
3402
- this._formEditor._setState({
3403
- schema
3404
- });
3405
- }
3406
- }
3407
- AddFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3521
+ /**
3522
+ * The current index on the stack
3523
+ *
3524
+ * @type {number}
3525
+ */
3526
+ this._stackIdx = -1;
3408
3527
 
3409
- class EditFormFieldHandler {
3410
3528
  /**
3411
- * @constructor
3412
- * @param { import('../../../FormEditor').default } formEditor
3413
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3529
+ * Current active commandStack execution
3530
+ *
3531
+ * @type {CurrentExecution}
3414
3532
  */
3415
- constructor(formEditor, formFieldRegistry) {
3416
- this._formEditor = formEditor;
3417
- this._formFieldRegistry = formFieldRegistry;
3418
- }
3419
- execute(context) {
3420
- const {
3421
- formField,
3422
- properties
3423
- } = context;
3424
- let {
3425
- schema
3426
- } = this._formEditor._getState();
3427
- const oldProperties = {};
3428
- for (let key in properties) {
3429
- oldProperties[key] = formField[key];
3430
- const property = properties[key];
3431
- if (key === 'id') {
3432
- if (property !== formField.id) {
3433
- this._formFieldRegistry.updateId(formField, property);
3434
- }
3435
- } else {
3436
- formField[key] = property;
3437
- }
3438
- }
3439
- context.oldProperties = oldProperties;
3533
+ this._currentExecution = {
3534
+ actions: [],
3535
+ dirty: [],
3536
+ trigger: null
3537
+ };
3440
3538
 
3441
- // TODO: Create updater/change support that automatically updates paths and schema on command execution
3442
- this._formEditor._setState({
3443
- schema
3444
- });
3445
- return formField;
3446
- }
3447
- revert(context) {
3448
- const {
3449
- formField,
3450
- oldProperties
3451
- } = context;
3452
- let {
3453
- schema
3454
- } = this._formEditor._getState();
3455
- for (let key in oldProperties) {
3456
- const property = oldProperties[key];
3457
- if (key === 'id') {
3458
- if (property !== formField.id) {
3459
- this._formFieldRegistry.updateId(formField, property);
3460
- }
3461
- } else {
3462
- formField[key] = property;
3463
- }
3464
- }
3539
+ /**
3540
+ * @type {Injector}
3541
+ */
3542
+ this._injector = injector;
3465
3543
 
3466
- // TODO: Create updater/change support that automatically updates paths and schema on command execution
3467
- this._formEditor._setState({
3468
- schema
3469
- });
3470
- return formField;
3471
- }
3472
- }
3473
- EditFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3544
+ /**
3545
+ * @type EventBus
3546
+ */
3547
+ this._eventBus = eventBus;
3474
3548
 
3475
- class MoveFormFieldHandler {
3476
3549
  /**
3477
- * @constructor
3478
- * @param { import('../../../FormEditor').default } formEditor
3479
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3480
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3481
- * @param { import('@bpmn-io/form-js-viewer').FormLayouter } formLayouter
3550
+ * @type { number }
3482
3551
  */
3483
- constructor(formEditor, formFieldRegistry, pathRegistry, formLayouter) {
3484
- this._formEditor = formEditor;
3485
- this._formFieldRegistry = formFieldRegistry;
3486
- this._pathRegistry = pathRegistry;
3487
- this._formLayouter = formLayouter;
3552
+ this._uid = 1;
3553
+ eventBus.on(['diagram.destroy', 'diagram.clear'], function () {
3554
+ this.clear(false);
3555
+ }, this);
3556
+ }
3557
+ CommandStack.$inject = ['eventBus', 'injector'];
3558
+
3559
+ /**
3560
+ * Execute a command.
3561
+ *
3562
+ * @param {string} command The command to execute.
3563
+ * @param {CommandContext} context The context with which to execute the command.
3564
+ */
3565
+ CommandStack.prototype.execute = function (command, context) {
3566
+ if (!command) {
3567
+ throw new Error('command required');
3488
3568
  }
3489
- execute(context) {
3490
- this.moveFormField(context);
3569
+ this._currentExecution.trigger = 'execute';
3570
+ const action = {
3571
+ command: command,
3572
+ context: context
3573
+ };
3574
+ this._pushAction(action);
3575
+ this._internalExecute(action);
3576
+ this._popAction();
3577
+ };
3578
+
3579
+ /**
3580
+ * Check whether a command can be executed.
3581
+ *
3582
+ * Implementors may hook into the mechanism on two ways:
3583
+ *
3584
+ * * in event listeners:
3585
+ *
3586
+ * Users may prevent the execution via an event listener.
3587
+ * It must prevent the default action for `commandStack.(<command>.)canExecute` events.
3588
+ *
3589
+ * * in command handlers:
3590
+ *
3591
+ * If the method {@link CommandHandler#canExecute} is implemented in a handler
3592
+ * it will be called to figure out whether the execution is allowed.
3593
+ *
3594
+ * @param {string} command The command to execute.
3595
+ * @param {CommandContext} context The context with which to execute the command.
3596
+ *
3597
+ * @return {boolean} Whether the command can be executed with the given context.
3598
+ */
3599
+ CommandStack.prototype.canExecute = function (command, context) {
3600
+ const action = {
3601
+ command: command,
3602
+ context: context
3603
+ };
3604
+ const handler = this._getHandler(command);
3605
+ let result = this._fire(command, 'canExecute', action);
3606
+
3607
+ // handler#canExecute will only be called if no listener
3608
+ // decided on a result already
3609
+ if (result === undefined) {
3610
+ if (!handler) {
3611
+ return false;
3612
+ }
3613
+ if (handler.canExecute) {
3614
+ result = handler.canExecute(context);
3615
+ }
3491
3616
  }
3492
- revert(context) {
3493
- let {
3494
- sourceFormField,
3495
- targetFormField,
3496
- sourceIndex,
3497
- targetIndex,
3498
- sourceRow,
3499
- targetRow
3500
- } = context;
3501
- this.moveFormField({
3502
- sourceFormField: targetFormField,
3503
- targetFormField: sourceFormField,
3504
- sourceIndex: targetIndex,
3505
- targetIndex: sourceIndex,
3506
- sourceRow: targetRow,
3507
- targetRow: sourceRow
3508
- }, true);
3617
+ return result;
3618
+ };
3619
+
3620
+ /**
3621
+ * Clear the command stack, erasing all undo / redo history.
3622
+ *
3623
+ * @param {boolean} [emit=true] Whether to fire an event. Defaults to `true`.
3624
+ */
3625
+ CommandStack.prototype.clear = function (emit) {
3626
+ this._stack.length = 0;
3627
+ this._stackIdx = -1;
3628
+ if (emit !== false) {
3629
+ this._fire('changed', {
3630
+ trigger: 'clear'
3631
+ });
3509
3632
  }
3510
- moveFormField(context, revert) {
3511
- let {
3512
- sourceFormField,
3513
- targetFormField,
3514
- sourceIndex,
3515
- targetIndex,
3516
- targetRow
3517
- } = context;
3518
- let {
3519
- schema
3520
- } = this._formEditor._getState();
3521
- const sourcePath = [...sourceFormField._path, 'components'];
3522
- if (sourceFormField.id === targetFormField.id) {
3523
- if (revert) {
3524
- if (sourceIndex > targetIndex) {
3525
- sourceIndex--;
3526
- }
3527
- } else {
3528
- if (sourceIndex < targetIndex) {
3529
- targetIndex--;
3530
- }
3633
+ };
3634
+
3635
+ /**
3636
+ * Undo last command(s)
3637
+ */
3638
+ CommandStack.prototype.undo = function () {
3639
+ let action = this._getUndoAction(),
3640
+ next;
3641
+ if (action) {
3642
+ this._currentExecution.trigger = 'undo';
3643
+ this._pushAction(action);
3644
+ while (action) {
3645
+ this._internalUndo(action);
3646
+ next = this._getUndoAction();
3647
+ if (!next || next.id !== action.id) {
3648
+ break;
3531
3649
  }
3532
- const formField = minDash.get(schema, [...sourcePath, sourceIndex]);
3650
+ action = next;
3651
+ }
3652
+ this._popAction();
3653
+ }
3654
+ };
3533
3655
 
3534
- // (1) Add to row or create new one
3535
- updateRow(formField, targetRow ? targetRow.id : this._formLayouter.nextRowId());
3656
+ /**
3657
+ * Redo last command(s)
3658
+ */
3659
+ CommandStack.prototype.redo = function () {
3660
+ let action = this._getRedoAction(),
3661
+ next;
3662
+ if (action) {
3663
+ this._currentExecution.trigger = 'redo';
3664
+ this._pushAction(action);
3665
+ while (action) {
3666
+ this._internalExecute(action, true);
3667
+ next = this._getRedoAction();
3668
+ if (!next || next.id !== action.id) {
3669
+ break;
3670
+ }
3671
+ action = next;
3672
+ }
3673
+ this._popAction();
3674
+ }
3675
+ };
3536
3676
 
3537
- // (2) Move form field
3538
- arrayMove.mutate(minDash.get(schema, sourcePath), sourceIndex, targetIndex);
3677
+ /**
3678
+ * Register a handler instance with the command stack.
3679
+ *
3680
+ * @param {string} command Command to be executed.
3681
+ * @param {CommandHandler} handler Handler to execute the command.
3682
+ */
3683
+ CommandStack.prototype.register = function (command, handler) {
3684
+ this._setHandler(command, handler);
3685
+ };
3539
3686
 
3540
- // (3) Update internal paths of new form field and its siblings (and their children)
3541
- minDash.get(schema, sourcePath).forEach((formField, index) => updatePath(this._formFieldRegistry, formField, index));
3542
- } else {
3543
- const formField = minDash.get(schema, [...sourcePath, sourceIndex]);
3687
+ /**
3688
+ * Register a handler type with the command stack by instantiating it and
3689
+ * injecting its dependencies.
3690
+ *
3691
+ * @param {string} command Command to be executed.
3692
+ * @param {CommandHandlerConstructor} handlerCls Constructor to instantiate a {@link CommandHandler}.
3693
+ */
3694
+ CommandStack.prototype.registerHandler = function (command, handlerCls) {
3695
+ if (!command || !handlerCls) {
3696
+ throw new Error('command and handlerCls must be defined');
3697
+ }
3698
+ const handler = this._injector.instantiate(handlerCls);
3699
+ this.register(command, handler);
3700
+ };
3544
3701
 
3545
- // (1) Deregister form field (and children) from path registry
3546
- this._pathRegistry.executeRecursivelyOnFields(formField, ({
3547
- field
3548
- }) => {
3549
- this._pathRegistry.unclaimPath(this._pathRegistry.getValuePath(field));
3550
- });
3551
- formField._parent = targetFormField.id;
3702
+ /**
3703
+ * @return {boolean}
3704
+ */
3705
+ CommandStack.prototype.canUndo = function () {
3706
+ return !!this._getUndoAction();
3707
+ };
3552
3708
 
3553
- // (2) Remove form field
3554
- arrayRemove(minDash.get(schema, sourcePath), sourceIndex);
3709
+ /**
3710
+ * @return {boolean}
3711
+ */
3712
+ CommandStack.prototype.canRedo = function () {
3713
+ return !!this._getRedoAction();
3714
+ };
3555
3715
 
3556
- // (3) Update internal paths of siblings (and their children)
3557
- minDash.get(schema, sourcePath).forEach((formField, index) => updatePath(this._formFieldRegistry, formField, index));
3558
- const targetPath = [...targetFormField._path, 'components'];
3716
+ // stack access //////////////////////
3559
3717
 
3560
- // (4) Add to row or create new one
3561
- updateRow(formField, targetRow ? targetRow.id : this._formLayouter.nextRowId());
3718
+ CommandStack.prototype._getRedoAction = function () {
3719
+ return this._stack[this._stackIdx + 1];
3720
+ };
3721
+ CommandStack.prototype._getUndoAction = function () {
3722
+ return this._stack[this._stackIdx];
3723
+ };
3562
3724
 
3563
- // (5) Add form field
3564
- arrayAdd$1(minDash.get(schema, targetPath), targetIndex, formField);
3725
+ // internal functionality //////////////////////
3565
3726
 
3566
- // (6) Update internal paths of siblings (and their children)
3567
- minDash.get(schema, targetPath).forEach((formField, index) => updatePath(this._formFieldRegistry, formField, index));
3727
+ CommandStack.prototype._internalUndo = function (action) {
3728
+ const command = action.command,
3729
+ context = action.context;
3730
+ const handler = this._getHandler(command);
3568
3731
 
3569
- // (7) Reregister form field (and children) from path registry
3570
- this._pathRegistry.executeRecursivelyOnFields(formField, ({
3571
- field,
3572
- isClosed,
3573
- isRepeatable
3574
- }) => {
3575
- this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), {
3576
- isClosed,
3577
- isRepeatable,
3578
- claimerId: field.id
3579
- });
3580
- });
3732
+ // guard against illegal nested command stack invocations
3733
+ this._atomicDo(() => {
3734
+ this._fire(command, 'revert', action);
3735
+ if (handler.revert) {
3736
+ this._markDirty(handler.revert(context));
3581
3737
  }
3582
-
3583
- // TODO: Create updater/change support that automatically updates paths and schema on command execution
3584
- this._formEditor._setState({
3585
- schema
3586
- });
3738
+ this._revertedAction(action);
3739
+ this._fire(command, 'reverted', action);
3740
+ });
3741
+ };
3742
+ CommandStack.prototype._fire = function (command, qualifier, event) {
3743
+ if (arguments.length < 3) {
3744
+ event = qualifier;
3745
+ qualifier = null;
3587
3746
  }
3588
- }
3589
- MoveFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry', 'pathRegistry', 'formLayouter'];
3590
-
3591
- class RemoveFormFieldHandler {
3592
- /**
3593
- * @constructor
3594
- * @param { import('../../../FormEditor').default } formEditor
3595
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3596
- */
3597
- constructor(formEditor, formFieldRegistry) {
3598
- this._formEditor = formEditor;
3599
- this._formFieldRegistry = formFieldRegistry;
3747
+ const names = qualifier ? [command + '.' + qualifier, qualifier] : [command];
3748
+ let result;
3749
+ event = this._eventBus.createEvent(event);
3750
+ for (const name of names) {
3751
+ result = this._eventBus.fire('commandStack.' + name, event);
3752
+ if (event.cancelBubble) {
3753
+ break;
3754
+ }
3600
3755
  }
3601
- execute(context) {
3602
- const {
3603
- sourceFormField,
3604
- sourceIndex
3605
- } = context;
3606
- let {
3607
- schema
3608
- } = this._formEditor._getState();
3609
- const sourcePath = [...sourceFormField._path, 'components'];
3610
- const formField = context.formField = minDash.get(schema, [...sourcePath, sourceIndex]);
3611
-
3612
- // (1) Remove form field
3613
- arrayRemove(minDash.get(schema, sourcePath), sourceIndex);
3614
-
3615
- // (2) Update internal paths of its siblings (and their children)
3616
- minDash.get(schema, sourcePath).forEach((formField, index) => updatePath(this._formFieldRegistry, formField, index));
3617
-
3618
- // (3) Remove form field and children from form field registry
3619
- formJsViewer.runRecursively(formField, formField => this._formFieldRegistry.remove(formField));
3620
-
3621
- // TODO: Create updater/change support that automatically updates paths and schema on command execution
3622
- this._formEditor._setState({
3623
- schema
3624
- });
3756
+ return result;
3757
+ };
3758
+ CommandStack.prototype._createId = function () {
3759
+ return this._uid++;
3760
+ };
3761
+ CommandStack.prototype._atomicDo = function (fn) {
3762
+ const execution = this._currentExecution;
3763
+ execution.atomic = true;
3764
+ try {
3765
+ fn();
3766
+ } finally {
3767
+ execution.atomic = false;
3625
3768
  }
3626
- revert(context) {
3627
- const {
3628
- formField,
3629
- sourceFormField,
3630
- sourceIndex
3631
- } = context;
3632
- let {
3633
- schema
3634
- } = this._formEditor._getState();
3635
- const sourcePath = [...sourceFormField._path, 'components'];
3636
-
3637
- // (1) Add form field
3638
- arrayAdd$1(minDash.get(schema, sourcePath), sourceIndex, formField);
3639
-
3640
- // (2) Update internal paths of its siblings (and their children)
3641
- minDash.get(schema, sourcePath).forEach((formField, index) => updatePath(this._formFieldRegistry, formField, index));
3642
-
3643
- // (3) Add form field and children to form field registry
3644
- formJsViewer.runRecursively(formField, formField => this._formFieldRegistry.add(formField));
3645
-
3646
- // TODO: Create updater/change support that automatically updates paths and schema on command execution
3647
- this._formEditor._setState({
3648
- schema
3649
- });
3650
- }
3651
- }
3652
- RemoveFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3653
-
3654
- class UpdateIdClaimHandler {
3655
- /**
3656
- * @constructor
3657
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3658
- */
3659
- constructor(formFieldRegistry) {
3660
- this._formFieldRegistry = formFieldRegistry;
3661
- }
3662
- execute(context) {
3663
- const {
3664
- claiming,
3665
- formField,
3666
- id
3667
- } = context;
3668
- if (claiming) {
3669
- this._formFieldRegistry._ids.claim(id, formField);
3670
- } else {
3671
- this._formFieldRegistry._ids.unclaim(id);
3672
- }
3769
+ };
3770
+ CommandStack.prototype._internalExecute = function (action, redo) {
3771
+ const command = action.command,
3772
+ context = action.context;
3773
+ const handler = this._getHandler(command);
3774
+ if (!handler) {
3775
+ throw new Error('no command handler registered for <' + command + '>');
3673
3776
  }
3674
- revert(context) {
3675
- const {
3676
- claiming,
3677
- formField,
3678
- id
3679
- } = context;
3680
- if (claiming) {
3681
- this._formFieldRegistry._ids.unclaim(id);
3682
- } else {
3683
- this._formFieldRegistry._ids.claim(id, formField);
3777
+ this._pushAction(action);
3778
+ if (!redo) {
3779
+ this._fire(command, 'preExecute', action);
3780
+ if (handler.preExecute) {
3781
+ handler.preExecute(context);
3684
3782
  }
3783
+ this._fire(command, 'preExecuted', action);
3685
3784
  }
3686
- }
3687
- UpdateIdClaimHandler.$inject = ['formFieldRegistry'];
3688
3785
 
3689
- class UpdateKeyClaimHandler {
3690
- /**
3691
- * @constructor
3692
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3693
- */
3694
- constructor(pathRegistry) {
3695
- this._pathRegistry = pathRegistry;
3696
- }
3697
- execute(context) {
3698
- const {
3699
- claiming,
3700
- formField,
3701
- key
3702
- } = context;
3703
- const options = {
3704
- replacements: {
3705
- [formField.id]: key
3706
- }
3707
- };
3708
- const valuePath = this._pathRegistry.getValuePath(formField, options);
3709
- if (claiming) {
3710
- this._pathRegistry.claimPath(valuePath, {
3711
- isClosed: true,
3712
- claimerId: formField.id
3713
- });
3714
- } else {
3715
- this._pathRegistry.unclaimPath(valuePath);
3786
+ // guard against illegal nested command stack invocations
3787
+ this._atomicDo(() => {
3788
+ this._fire(command, 'execute', action);
3789
+ if (handler.execute) {
3790
+ // actual execute + mark return results as dirty
3791
+ this._markDirty(handler.execute(context));
3716
3792
  }
3717
3793
 
3718
- // cache path for revert
3719
- context.valuePath = valuePath;
3720
- }
3721
- revert(context) {
3722
- const {
3723
- claiming,
3724
- formField,
3725
- valuePath
3726
- } = context;
3727
- if (claiming) {
3728
- this._pathRegistry.unclaimPath(valuePath);
3729
- } else {
3730
- this._pathRegistry.claimPath(valuePath, {
3731
- isClosed: true,
3732
- claimerId: formField.id
3733
- });
3794
+ // log to stack
3795
+ this._executedAction(action, redo);
3796
+ this._fire(command, 'executed', action);
3797
+ });
3798
+ if (!redo) {
3799
+ this._fire(command, 'postExecute', action);
3800
+ if (handler.postExecute) {
3801
+ handler.postExecute(context);
3734
3802
  }
3803
+ this._fire(command, 'postExecuted', action);
3735
3804
  }
3736
- }
3737
- UpdateKeyClaimHandler.$inject = ['pathRegistry'];
3738
-
3739
- class UpdatePathClaimHandler {
3740
- /**
3741
- * @constructor
3742
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3743
- */
3744
- constructor(pathRegistry) {
3745
- this._pathRegistry = pathRegistry;
3746
- }
3747
- execute(context) {
3748
- const {
3749
- claiming,
3750
- formField,
3751
- path
3752
- } = context;
3753
- const options = {
3754
- replacements: {
3755
- [formField.id]: path
3756
- }
3757
- };
3758
- const valuePaths = [];
3759
- if (claiming) {
3760
- this._pathRegistry.executeRecursivelyOnFields(formField, ({
3761
- field,
3762
- isClosed,
3763
- isRepeatable
3764
- }) => {
3765
- const valuePath = this._pathRegistry.getValuePath(field, options);
3766
- valuePaths.push({
3767
- valuePath,
3768
- isClosed,
3769
- isRepeatable,
3770
- claimerId: field.id
3771
- });
3772
- this._pathRegistry.claimPath(valuePath, {
3773
- isClosed,
3774
- isRepeatable,
3775
- claimerId: field.id
3776
- });
3777
- });
3778
- } else {
3779
- this._pathRegistry.executeRecursivelyOnFields(formField, ({
3780
- field,
3781
- isClosed,
3782
- isRepeatable
3783
- }) => {
3784
- const valuePath = this._pathRegistry.getValuePath(field, options);
3785
- valuePaths.push({
3786
- valuePath,
3787
- isClosed,
3788
- isRepeatable,
3789
- claimerId: field.id
3790
- });
3791
- this._pathRegistry.unclaimPath(valuePath);
3792
- });
3793
- }
3794
-
3795
- // cache path info for revert
3796
- context.valuePaths = valuePaths;
3805
+ this._popAction();
3806
+ };
3807
+ CommandStack.prototype._pushAction = function (action) {
3808
+ const execution = this._currentExecution,
3809
+ actions = execution.actions;
3810
+ const baseAction = actions[0];
3811
+ if (execution.atomic) {
3812
+ throw new Error('illegal invocation in <execute> or <revert> phase (action: ' + action.command + ')');
3797
3813
  }
3798
- revert(context) {
3799
- const {
3800
- claiming,
3801
- valuePaths
3802
- } = context;
3803
- if (claiming) {
3804
- valuePaths.forEach(({
3805
- valuePath
3806
- }) => {
3807
- this._pathRegistry.unclaimPath(valuePath);
3808
- });
3809
- } else {
3810
- valuePaths.forEach(({
3811
- valuePath,
3812
- isClosed,
3813
- isRepeatable,
3814
- claimerId
3815
- }) => {
3816
- this._pathRegistry.claimPath(valuePath, {
3817
- isClosed,
3818
- isRepeatable,
3819
- claimerId
3820
- });
3821
- });
3822
- }
3814
+ if (!action.id) {
3815
+ action.id = baseAction && baseAction.id || this._createId();
3823
3816
  }
3824
- }
3825
- UpdatePathClaimHandler.$inject = ['pathRegistry'];
3826
-
3827
- class Modeling {
3828
- constructor(commandStack, eventBus, formEditor, formFieldRegistry, fieldFactory) {
3829
- this._commandStack = commandStack;
3830
- this._formEditor = formEditor;
3831
- this._formFieldRegistry = formFieldRegistry;
3832
- this._fieldFactory = fieldFactory;
3833
- eventBus.on('form.init', () => {
3834
- this.registerHandlers();
3817
+ actions.push(action);
3818
+ };
3819
+ CommandStack.prototype._popAction = function () {
3820
+ const execution = this._currentExecution,
3821
+ trigger = execution.trigger,
3822
+ actions = execution.actions,
3823
+ dirty = execution.dirty;
3824
+ actions.pop();
3825
+ if (!actions.length) {
3826
+ this._eventBus.fire('elements.changed', {
3827
+ elements: minDash.uniqueBy('id', dirty.reverse())
3835
3828
  });
3836
- }
3837
- registerHandlers() {
3838
- Object.entries(this.getHandlers()).forEach(([id, handler]) => {
3839
- this._commandStack.registerHandler(id, handler);
3829
+ dirty.length = 0;
3830
+ this._fire('changed', {
3831
+ trigger: trigger
3840
3832
  });
3833
+ execution.trigger = null;
3841
3834
  }
3842
- getHandlers() {
3843
- return {
3844
- 'formField.add': AddFormFieldHandler,
3845
- 'formField.edit': EditFormFieldHandler,
3846
- 'formField.move': MoveFormFieldHandler,
3847
- 'formField.remove': RemoveFormFieldHandler,
3848
- 'id.updateClaim': UpdateIdClaimHandler,
3849
- 'key.updateClaim': UpdateKeyClaimHandler,
3850
- 'path.updateClaim': UpdatePathClaimHandler
3851
- };
3852
- }
3853
- addFormField(attrs, targetFormField, targetIndex) {
3854
- const formField = this._fieldFactory.create(attrs);
3855
- const context = {
3856
- formField,
3857
- targetFormField,
3858
- targetIndex
3859
- };
3860
- this._commandStack.execute('formField.add', context);
3861
- return formField;
3862
- }
3863
- editFormField(formField, properties, value) {
3864
- if (!minDash.isObject(properties)) {
3865
- properties = {
3866
- [properties]: value
3867
- };
3868
- }
3869
- const context = {
3870
- formField,
3871
- properties
3872
- };
3873
- this._commandStack.execute('formField.edit', context);
3874
- }
3875
- moveFormField(formField, sourceFormField, targetFormField, sourceIndex, targetIndex, sourceRow, targetRow) {
3876
- const context = {
3877
- formField,
3878
- sourceFormField,
3879
- targetFormField,
3880
- sourceIndex,
3881
- targetIndex,
3882
- sourceRow,
3883
- targetRow
3884
- };
3885
- this._commandStack.execute('formField.move', context);
3886
- }
3887
- removeFormField(formField, sourceFormField, sourceIndex) {
3888
- const context = {
3889
- formField,
3890
- sourceFormField,
3891
- sourceIndex
3892
- };
3893
- this._commandStack.execute('formField.remove', context);
3894
- }
3895
- claimId(formField, id) {
3896
- const context = {
3897
- formField,
3898
- id,
3899
- claiming: true
3900
- };
3901
- this._commandStack.execute('id.updateClaim', context);
3902
- }
3903
- unclaimId(formField, id) {
3904
- const context = {
3905
- formField,
3906
- id,
3907
- claiming: false
3908
- };
3909
- this._commandStack.execute('id.updateClaim', context);
3910
- }
3911
- claimKey(formField, key) {
3912
- const context = {
3913
- formField,
3914
- key,
3915
- claiming: true
3916
- };
3917
- this._commandStack.execute('key.updateClaim', context);
3835
+ };
3836
+ CommandStack.prototype._markDirty = function (elements) {
3837
+ const execution = this._currentExecution;
3838
+ if (!elements) {
3839
+ return;
3918
3840
  }
3919
- unclaimKey(formField, key) {
3920
- const context = {
3921
- formField,
3922
- key,
3923
- claiming: false
3924
- };
3925
- this._commandStack.execute('key.updateClaim', context);
3841
+ elements = minDash.isArray(elements) ? elements : [elements];
3842
+ execution.dirty = execution.dirty.concat(elements);
3843
+ };
3844
+ CommandStack.prototype._executedAction = function (action, redo) {
3845
+ const stackIdx = ++this._stackIdx;
3846
+ if (!redo) {
3847
+ this._stack.splice(stackIdx, this._stack.length, action);
3926
3848
  }
3927
- claimPath(formField, path) {
3928
- const context = {
3929
- formField,
3930
- path,
3931
- claiming: true
3932
- };
3933
- this._commandStack.execute('path.updateClaim', context);
3849
+ };
3850
+ CommandStack.prototype._revertedAction = function (action) {
3851
+ this._stackIdx--;
3852
+ };
3853
+ CommandStack.prototype._getHandler = function (command) {
3854
+ return this._handlerMap[command];
3855
+ };
3856
+ CommandStack.prototype._setHandler = function (command, handler) {
3857
+ if (!command || !handler) {
3858
+ throw new Error('command and handler required');
3934
3859
  }
3935
- unclaimPath(formField, path) {
3936
- const context = {
3937
- formField,
3938
- path,
3939
- claiming: false
3940
- };
3941
- this._commandStack.execute('path.updateClaim', context);
3860
+ if (this._handlerMap[command]) {
3861
+ throw new Error('overriding handler for command <' + command + '>');
3942
3862
  }
3943
- }
3944
- Modeling.$inject = ['commandStack', 'eventBus', 'formEditor', 'formFieldRegistry', 'fieldFactory'];
3863
+ this._handlerMap[command] = handler;
3864
+ };
3865
+
3866
+ /**
3867
+ * @type { import('didi').ModuleDeclaration }
3868
+ */
3869
+ var commandModule = {
3870
+ commandStack: ['type', CommandStack]
3871
+ };
3945
3872
 
3946
3873
  /**
3947
3874
  * @typedef {import('../core/Types').ElementLike} ElementLike
@@ -4171,53 +4098,6 @@ function createHook(hook) {
4171
4098
  return hookFn;
4172
4099
  }
4173
4100
 
4174
- class FormLayoutUpdater extends CommandInterceptor {
4175
- constructor(eventBus, formLayouter, modeling, formEditor) {
4176
- super(eventBus);
4177
- this._eventBus = eventBus;
4178
- this._formLayouter = formLayouter;
4179
- this._modeling = modeling;
4180
- this._formEditor = formEditor;
4181
-
4182
- // @ts-ignore
4183
- this.preExecute(['formField.add', 'formField.remove', 'formField.move', 'id.updateClaim'], event => this.updateRowIds(event));
4184
-
4185
- // we need that as the state got updates
4186
- // on the next tick (not in post execute)
4187
- eventBus.on('changed', context => {
4188
- const {
4189
- schema
4190
- } = context;
4191
- this.updateLayout(schema);
4192
- });
4193
- }
4194
- updateLayout(schema) {
4195
- this._formLayouter.clear();
4196
- this._formLayouter.calculateLayout(formJsViewer.clone(schema));
4197
- }
4198
- updateRowIds(event) {
4199
- const {
4200
- schema
4201
- } = this._formEditor._getState();
4202
- const setRowIds = parent => {
4203
- if (!parent.components || !parent.components.length) {
4204
- return;
4205
- }
4206
- parent.components.forEach(formField => {
4207
- const row = this._formLayouter.getRowForField(formField);
4208
- updateRow(formField, row.id);
4209
-
4210
- // handle children recursively
4211
- setRowIds(formField);
4212
- });
4213
- };
4214
-
4215
- // make sure rows are persisted in schema (e.g. for migration case)
4216
- setRowIds(schema);
4217
- }
4218
- }
4219
- FormLayoutUpdater.$inject = ['eventBus', 'formLayouter', 'modeling', 'formEditor'];
4220
-
4221
4101
  class IdBehavior extends CommandInterceptor {
4222
4102
  constructor(eventBus, modeling) {
4223
4103
  super(eventBus);
@@ -4458,7 +4338,7 @@ class TableDataSourceBehavior extends CommandInterceptor {
4458
4338
  }
4459
4339
  TableDataSourceBehavior.$inject = ['eventBus'];
4460
4340
 
4461
- var behaviorModule = {
4341
+ const BehaviorModule = {
4462
4342
  __init__: ['idBehavior', 'keyBehavior', 'pathBehavior', 'validateBehavior', 'optionsSourceBehavior', 'columnsSourceBehavior', 'tableDataSourceBehavior'],
4463
4343
  idBehavior: ['type', IdBehavior],
4464
4344
  keyBehavior: ['type', KeyBehavior],
@@ -4469,479 +4349,684 @@ var behaviorModule = {
4469
4349
  tableDataSourceBehavior: ['type', TableDataSourceBehavior]
4470
4350
  };
4471
4351
 
4472
- /**
4473
- * @typedef {import('didi').Injector} Injector
4474
- *
4475
- * @typedef {import('../core/Types').ElementLike} ElementLike
4476
- *
4477
- * @typedef {import('../core/EventBus').default} EventBus
4478
- * @typedef {import('./CommandHandler').default} CommandHandler
4479
- *
4480
- * @typedef { any } CommandContext
4481
- * @typedef { {
4482
- * new (...args: any[]) : CommandHandler
4483
- * } } CommandHandlerConstructor
4484
- * @typedef { {
4485
- * [key: string]: CommandHandler;
4486
- * } } CommandHandlerMap
4487
- * @typedef { {
4488
- * command: string;
4489
- * context: any;
4490
- * id?: any;
4491
- * } } CommandStackAction
4492
- * @typedef { {
4493
- * actions: CommandStackAction[];
4494
- * dirty: ElementLike[];
4495
- * trigger: 'execute' | 'undo' | 'redo' | 'clear' | null;
4496
- * atomic?: boolean;
4497
- * } } CurrentExecution
4498
- */
4352
+ function arrayAdd$1(array, index, item) {
4353
+ array.splice(index, 0, item);
4354
+ return array;
4355
+ }
4356
+ function arrayRemove(array, index) {
4357
+ array.splice(index, 1);
4358
+ return array;
4359
+ }
4360
+ function updatePath(formFieldRegistry, formField, index) {
4361
+ const parent = formFieldRegistry.get(formField._parent);
4362
+ refreshPathsRecursively(formField, [...parent._path, 'components', index]);
4363
+ return formField;
4364
+ }
4365
+ function refreshPathsRecursively(formField, path) {
4366
+ formField._path = path;
4367
+ const components = formField.components || [];
4368
+ components.forEach((component, index) => {
4369
+ refreshPathsRecursively(component, [...path, 'components', index]);
4370
+ });
4371
+ }
4372
+ function updateRow(formField, rowId) {
4373
+ formField.layout = {
4374
+ ...(formField.layout || {}),
4375
+ row: rowId
4376
+ };
4377
+ return formField;
4378
+ }
4499
4379
 
4500
- /**
4501
- * A service that offers un- and redoable execution of commands.
4502
- *
4503
- * The command stack is responsible for executing modeling actions
4504
- * in a un- and redoable manner. To do this it delegates the actual
4505
- * command execution to {@link CommandHandler}s.
4506
- *
4507
- * Command handlers provide {@link CommandHandler#execute(ctx)} and
4508
- * {@link CommandHandler#revert(ctx)} methods to un- and redo a command
4509
- * identified by a command context.
4510
- *
4511
- *
4512
- * ## Life-Cycle events
4513
- *
4514
- * In the process the command stack fires a number of life-cycle events
4515
- * that other components to participate in the command execution.
4516
- *
4517
- * * preExecute
4518
- * * preExecuted
4519
- * * execute
4520
- * * executed
4521
- * * postExecute
4522
- * * postExecuted
4523
- * * revert
4524
- * * reverted
4525
- *
4526
- * A special event is used for validating, whether a command can be
4527
- * performed prior to its execution.
4528
- *
4529
- * * canExecute
4530
- *
4531
- * Each of the events is fired as `commandStack.{eventName}` and
4532
- * `commandStack.{commandName}.{eventName}`, respectively. This gives
4533
- * components fine grained control on where to hook into.
4534
- *
4535
- * The event object fired transports `command`, the name of the
4536
- * command and `context`, the command context.
4537
- *
4538
- *
4539
- * ## Creating Command Handlers
4540
- *
4541
- * Command handlers should provide the {@link CommandHandler#execute(ctx)}
4542
- * and {@link CommandHandler#revert(ctx)} methods to implement
4543
- * redoing and undoing of a command.
4544
- *
4545
- * A command handler _must_ ensure undo is performed properly in order
4546
- * not to break the undo chain. It must also return the shapes that
4547
- * got changed during the `execute` and `revert` operations.
4548
- *
4549
- * Command handlers may execute other modeling operations (and thus
4550
- * commands) in their `preExecute(d)` and `postExecute(d)` phases. The command
4551
- * stack will properly group all commands together into a logical unit
4552
- * that may be re- and undone atomically.
4553
- *
4554
- * Command handlers must not execute other commands from within their
4555
- * core implementation (`execute`, `revert`).
4556
- *
4557
- *
4558
- * ## Change Tracking
4559
- *
4560
- * During the execution of the CommandStack it will keep track of all
4561
- * elements that have been touched during the command's execution.
4562
- *
4563
- * At the end of the CommandStack execution it will notify interested
4564
- * components via an 'elements.changed' event with all the dirty
4565
- * elements.
4566
- *
4567
- * The event can be picked up by components that are interested in the fact
4568
- * that elements have been changed. One use case for this is updating
4569
- * their graphical representation after moving / resizing or deletion.
4570
- *
4571
- * @see CommandHandler
4572
- *
4573
- * @param {EventBus} eventBus
4574
- * @param {Injector} injector
4575
- */
4576
- function CommandStack(eventBus, injector) {
4380
+ class FormLayoutUpdater extends CommandInterceptor {
4381
+ constructor(eventBus, formLayouter, modeling, formEditor) {
4382
+ super(eventBus);
4383
+ this._eventBus = eventBus;
4384
+ this._formLayouter = formLayouter;
4385
+ this._modeling = modeling;
4386
+ this._formEditor = formEditor;
4387
+
4388
+ // @ts-ignore
4389
+ this.preExecute(['formField.add', 'formField.remove', 'formField.move', 'id.updateClaim'], event => this.updateRowIds(event));
4390
+
4391
+ // we need that as the state got updates
4392
+ // on the next tick (not in post execute)
4393
+ eventBus.on('changed', context => {
4394
+ const {
4395
+ schema
4396
+ } = context;
4397
+ this.updateLayout(schema);
4398
+ });
4399
+ }
4400
+ updateLayout(schema) {
4401
+ this._formLayouter.clear();
4402
+ this._formLayouter.calculateLayout(formJsViewer.clone(schema));
4403
+ }
4404
+ updateRowIds(event) {
4405
+ const {
4406
+ schema
4407
+ } = this._formEditor._getState();
4408
+ const setRowIds = parent => {
4409
+ if (!parent.components || !parent.components.length) {
4410
+ return;
4411
+ }
4412
+ parent.components.forEach(formField => {
4413
+ const row = this._formLayouter.getRowForField(formField);
4414
+ updateRow(formField, row.id);
4415
+
4416
+ // handle children recursively
4417
+ setRowIds(formField);
4418
+ });
4419
+ };
4420
+
4421
+ // make sure rows are persisted in schema (e.g. for migration case)
4422
+ setRowIds(schema);
4423
+ }
4424
+ }
4425
+ FormLayoutUpdater.$inject = ['eventBus', 'formLayouter', 'modeling', 'formEditor'];
4426
+
4427
+ class AddFormFieldHandler {
4577
4428
  /**
4578
- * A map of all registered command handlers.
4579
- *
4580
- * @type {CommandHandlerMap}
4429
+ * @constructor
4430
+ * @param { import('../../../FormEditor').FormEditor } formEditor
4431
+ * @param { import('../../../core/FormFieldRegistry').FormFieldRegistry } formFieldRegistry
4581
4432
  */
4582
- this._handlerMap = {};
4433
+ constructor(formEditor, formFieldRegistry) {
4434
+ this._formEditor = formEditor;
4435
+ this._formFieldRegistry = formFieldRegistry;
4436
+ }
4437
+ execute(context) {
4438
+ const {
4439
+ formField,
4440
+ targetFormField,
4441
+ targetIndex
4442
+ } = context;
4443
+ const {
4444
+ schema
4445
+ } = this._formEditor._getState();
4446
+ const targetPath = [...targetFormField._path, 'components'];
4447
+ formField._parent = targetFormField.id;
4448
+
4449
+ // (1) Add new form field
4450
+ arrayAdd$1(minDash.get(schema, targetPath), targetIndex, formField);
4451
+
4452
+ // (2) Update internal paths of new form field and its siblings (and their children)
4453
+ minDash.get(schema, targetPath).forEach((formField, index) => updatePath(this._formFieldRegistry, formField, index));
4454
+
4455
+ // (3) Add new form field to form field registry
4456
+ this._formFieldRegistry.add(formField);
4457
+
4458
+ // TODO: Create updater/change support that automatically updates paths and schema on command execution
4459
+ this._formEditor._setState({
4460
+ schema
4461
+ });
4462
+ }
4463
+ revert(context) {
4464
+ const {
4465
+ formField,
4466
+ targetFormField,
4467
+ targetIndex
4468
+ } = context;
4469
+ const {
4470
+ schema
4471
+ } = this._formEditor._getState();
4472
+ const targetPath = [...targetFormField._path, 'components'];
4473
+
4474
+ // (1) Remove new form field
4475
+ arrayRemove(minDash.get(schema, targetPath), targetIndex);
4476
+
4477
+ // (2) Update internal paths of new form field and its siblings (and their children)
4478
+ minDash.get(schema, targetPath).forEach((formField, index) => updatePath(this._formFieldRegistry, formField, index));
4479
+
4480
+ // (3) Remove new form field from form field registry
4481
+ this._formFieldRegistry.remove(formField);
4482
+
4483
+ // TODO: Create updater/change support that automatically updates paths and schema on command execution
4484
+ this._formEditor._setState({
4485
+ schema
4486
+ });
4487
+ }
4488
+ }
4489
+ AddFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
4490
+
4491
+ class EditFormFieldHandler {
4492
+ /**
4493
+ * @constructor
4494
+ * @param { import('../../../FormEditor').FormEditor } formEditor
4495
+ * @param { import('../../../core/FormFieldRegistry').FormFieldRegistry } formFieldRegistry
4496
+ */
4497
+ constructor(formEditor, formFieldRegistry) {
4498
+ this._formEditor = formEditor;
4499
+ this._formFieldRegistry = formFieldRegistry;
4500
+ }
4501
+ execute(context) {
4502
+ const {
4503
+ formField,
4504
+ properties
4505
+ } = context;
4506
+ let {
4507
+ schema
4508
+ } = this._formEditor._getState();
4509
+ const oldProperties = {};
4510
+ for (let key in properties) {
4511
+ oldProperties[key] = formField[key];
4512
+ const property = properties[key];
4513
+ if (key === 'id') {
4514
+ if (property !== formField.id) {
4515
+ this._formFieldRegistry.updateId(formField, property);
4516
+ }
4517
+ } else {
4518
+ formField[key] = property;
4519
+ }
4520
+ }
4521
+ context.oldProperties = oldProperties;
4522
+
4523
+ // TODO: Create updater/change support that automatically updates paths and schema on command execution
4524
+ this._formEditor._setState({
4525
+ schema
4526
+ });
4527
+ return formField;
4528
+ }
4529
+ revert(context) {
4530
+ const {
4531
+ formField,
4532
+ oldProperties
4533
+ } = context;
4534
+ let {
4535
+ schema
4536
+ } = this._formEditor._getState();
4537
+ for (let key in oldProperties) {
4538
+ const property = oldProperties[key];
4539
+ if (key === 'id') {
4540
+ if (property !== formField.id) {
4541
+ this._formFieldRegistry.updateId(formField, property);
4542
+ }
4543
+ } else {
4544
+ formField[key] = property;
4545
+ }
4546
+ }
4547
+
4548
+ // TODO: Create updater/change support that automatically updates paths and schema on command execution
4549
+ this._formEditor._setState({
4550
+ schema
4551
+ });
4552
+ return formField;
4553
+ }
4554
+ }
4555
+ EditFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
4556
+
4557
+ class MoveFormFieldHandler {
4558
+ /**
4559
+ * @constructor
4560
+ * @param { import('../../../FormEditor').FormEditor } formEditor
4561
+ * @param { import('../../../core/FormFieldRegistry').FormFieldRegistry } formFieldRegistry
4562
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
4563
+ * @param { import('@bpmn-io/form-js-viewer').FormLayouter } formLayouter
4564
+ */
4565
+ constructor(formEditor, formFieldRegistry, pathRegistry, formLayouter) {
4566
+ this._formEditor = formEditor;
4567
+ this._formFieldRegistry = formFieldRegistry;
4568
+ this._pathRegistry = pathRegistry;
4569
+ this._formLayouter = formLayouter;
4570
+ }
4571
+ execute(context) {
4572
+ this.moveFormField(context);
4573
+ }
4574
+ revert(context) {
4575
+ let {
4576
+ sourceFormField,
4577
+ targetFormField,
4578
+ sourceIndex,
4579
+ targetIndex,
4580
+ sourceRow,
4581
+ targetRow
4582
+ } = context;
4583
+ this.moveFormField({
4584
+ sourceFormField: targetFormField,
4585
+ targetFormField: sourceFormField,
4586
+ sourceIndex: targetIndex,
4587
+ targetIndex: sourceIndex,
4588
+ sourceRow: targetRow,
4589
+ targetRow: sourceRow
4590
+ }, true);
4591
+ }
4592
+ moveFormField(context, revert) {
4593
+ let {
4594
+ sourceFormField,
4595
+ targetFormField,
4596
+ sourceIndex,
4597
+ targetIndex,
4598
+ targetRow
4599
+ } = context;
4600
+ let {
4601
+ schema
4602
+ } = this._formEditor._getState();
4603
+ const sourcePath = [...sourceFormField._path, 'components'];
4604
+ if (sourceFormField.id === targetFormField.id) {
4605
+ if (revert) {
4606
+ if (sourceIndex > targetIndex) {
4607
+ sourceIndex--;
4608
+ }
4609
+ } else {
4610
+ if (sourceIndex < targetIndex) {
4611
+ targetIndex--;
4612
+ }
4613
+ }
4614
+ const formField = minDash.get(schema, [...sourcePath, sourceIndex]);
4615
+
4616
+ // (1) Add to row or create new one
4617
+ updateRow(formField, targetRow ? targetRow.id : this._formLayouter.nextRowId());
4618
+
4619
+ // (2) Move form field
4620
+ arrayMove.mutate(minDash.get(schema, sourcePath), sourceIndex, targetIndex);
4621
+
4622
+ // (3) Update internal paths of new form field and its siblings (and their children)
4623
+ minDash.get(schema, sourcePath).forEach((formField, index) => updatePath(this._formFieldRegistry, formField, index));
4624
+ } else {
4625
+ const formField = minDash.get(schema, [...sourcePath, sourceIndex]);
4626
+
4627
+ // (1) Deregister form field (and children) from path registry
4628
+ this._pathRegistry.executeRecursivelyOnFields(formField, ({
4629
+ field
4630
+ }) => {
4631
+ this._pathRegistry.unclaimPath(this._pathRegistry.getValuePath(field));
4632
+ });
4633
+ formField._parent = targetFormField.id;
4634
+
4635
+ // (2) Remove form field
4636
+ arrayRemove(minDash.get(schema, sourcePath), sourceIndex);
4637
+
4638
+ // (3) Update internal paths of siblings (and their children)
4639
+ minDash.get(schema, sourcePath).forEach((formField, index) => updatePath(this._formFieldRegistry, formField, index));
4640
+ const targetPath = [...targetFormField._path, 'components'];
4641
+
4642
+ // (4) Add to row or create new one
4643
+ updateRow(formField, targetRow ? targetRow.id : this._formLayouter.nextRowId());
4583
4644
 
4584
- /**
4585
- * A stack containing all re/undoable actions on the diagram
4586
- *
4587
- * @type {CommandStackAction[]}
4588
- */
4589
- this._stack = [];
4645
+ // (5) Add form field
4646
+ arrayAdd$1(minDash.get(schema, targetPath), targetIndex, formField);
4590
4647
 
4591
- /**
4592
- * The current index on the stack
4593
- *
4594
- * @type {number}
4595
- */
4596
- this._stackIdx = -1;
4648
+ // (6) Update internal paths of siblings (and their children)
4649
+ minDash.get(schema, targetPath).forEach((formField, index) => updatePath(this._formFieldRegistry, formField, index));
4597
4650
 
4598
- /**
4599
- * Current active commandStack execution
4600
- *
4601
- * @type {CurrentExecution}
4602
- */
4603
- this._currentExecution = {
4604
- actions: [],
4605
- dirty: [],
4606
- trigger: null
4607
- };
4651
+ // (7) Reregister form field (and children) from path registry
4652
+ this._pathRegistry.executeRecursivelyOnFields(formField, ({
4653
+ field,
4654
+ isClosed,
4655
+ isRepeatable
4656
+ }) => {
4657
+ this._pathRegistry.claimPath(this._pathRegistry.getValuePath(field), {
4658
+ isClosed,
4659
+ isRepeatable,
4660
+ claimerId: field.id
4661
+ });
4662
+ });
4663
+ }
4608
4664
 
4609
- /**
4610
- * @type {Injector}
4611
- */
4612
- this._injector = injector;
4665
+ // TODO: Create updater/change support that automatically updates paths and schema on command execution
4666
+ this._formEditor._setState({
4667
+ schema
4668
+ });
4669
+ }
4670
+ }
4671
+ MoveFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry', 'pathRegistry', 'formLayouter'];
4613
4672
 
4673
+ class RemoveFormFieldHandler {
4614
4674
  /**
4615
- * @type EventBus
4675
+ * @constructor
4676
+ * @param { import('../../../FormEditor').FormEditor } formEditor
4677
+ * @param { import('../../../core/FormFieldRegistry').FormFieldRegistry } formFieldRegistry
4616
4678
  */
4617
- this._eventBus = eventBus;
4679
+ constructor(formEditor, formFieldRegistry) {
4680
+ this._formEditor = formEditor;
4681
+ this._formFieldRegistry = formFieldRegistry;
4682
+ }
4683
+ execute(context) {
4684
+ const {
4685
+ sourceFormField,
4686
+ sourceIndex
4687
+ } = context;
4688
+ let {
4689
+ schema
4690
+ } = this._formEditor._getState();
4691
+ const sourcePath = [...sourceFormField._path, 'components'];
4692
+ const formField = context.formField = minDash.get(schema, [...sourcePath, sourceIndex]);
4618
4693
 
4619
- /**
4620
- * @type { number }
4621
- */
4622
- this._uid = 1;
4623
- eventBus.on(['diagram.destroy', 'diagram.clear'], function () {
4624
- this.clear(false);
4625
- }, this);
4626
- }
4627
- CommandStack.$inject = ['eventBus', 'injector'];
4694
+ // (1) Remove form field
4695
+ arrayRemove(minDash.get(schema, sourcePath), sourceIndex);
4628
4696
 
4629
- /**
4630
- * Execute a command.
4631
- *
4632
- * @param {string} command The command to execute.
4633
- * @param {CommandContext} context The context with which to execute the command.
4634
- */
4635
- CommandStack.prototype.execute = function (command, context) {
4636
- if (!command) {
4637
- throw new Error('command required');
4697
+ // (2) Update internal paths of its siblings (and their children)
4698
+ minDash.get(schema, sourcePath).forEach((formField, index) => updatePath(this._formFieldRegistry, formField, index));
4699
+
4700
+ // (3) Remove form field and children from form field registry
4701
+ formJsViewer.runRecursively(formField, formField => this._formFieldRegistry.remove(formField));
4702
+
4703
+ // TODO: Create updater/change support that automatically updates paths and schema on command execution
4704
+ this._formEditor._setState({
4705
+ schema
4706
+ });
4638
4707
  }
4639
- this._currentExecution.trigger = 'execute';
4640
- const action = {
4641
- command: command,
4642
- context: context
4643
- };
4644
- this._pushAction(action);
4645
- this._internalExecute(action);
4646
- this._popAction();
4647
- };
4708
+ revert(context) {
4709
+ const {
4710
+ formField,
4711
+ sourceFormField,
4712
+ sourceIndex
4713
+ } = context;
4714
+ let {
4715
+ schema
4716
+ } = this._formEditor._getState();
4717
+ const sourcePath = [...sourceFormField._path, 'components'];
4648
4718
 
4649
- /**
4650
- * Check whether a command can be executed.
4651
- *
4652
- * Implementors may hook into the mechanism on two ways:
4653
- *
4654
- * * in event listeners:
4655
- *
4656
- * Users may prevent the execution via an event listener.
4657
- * It must prevent the default action for `commandStack.(<command>.)canExecute` events.
4658
- *
4659
- * * in command handlers:
4660
- *
4661
- * If the method {@link CommandHandler#canExecute} is implemented in a handler
4662
- * it will be called to figure out whether the execution is allowed.
4663
- *
4664
- * @param {string} command The command to execute.
4665
- * @param {CommandContext} context The context with which to execute the command.
4666
- *
4667
- * @return {boolean} Whether the command can be executed with the given context.
4668
- */
4669
- CommandStack.prototype.canExecute = function (command, context) {
4670
- const action = {
4671
- command: command,
4672
- context: context
4673
- };
4674
- const handler = this._getHandler(command);
4675
- let result = this._fire(command, 'canExecute', action);
4719
+ // (1) Add form field
4720
+ arrayAdd$1(minDash.get(schema, sourcePath), sourceIndex, formField);
4676
4721
 
4677
- // handler#canExecute will only be called if no listener
4678
- // decided on a result already
4679
- if (result === undefined) {
4680
- if (!handler) {
4681
- return false;
4722
+ // (2) Update internal paths of its siblings (and their children)
4723
+ minDash.get(schema, sourcePath).forEach((formField, index) => updatePath(this._formFieldRegistry, formField, index));
4724
+
4725
+ // (3) Add form field and children to form field registry
4726
+ formJsViewer.runRecursively(formField, formField => this._formFieldRegistry.add(formField));
4727
+
4728
+ // TODO: Create updater/change support that automatically updates paths and schema on command execution
4729
+ this._formEditor._setState({
4730
+ schema
4731
+ });
4732
+ }
4733
+ }
4734
+ RemoveFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
4735
+
4736
+ class UpdateIdClaimHandler {
4737
+ /**
4738
+ * @constructor
4739
+ * @param { import('../../../core/FormFieldRegistry').FormFieldRegistry } formFieldRegistry
4740
+ */
4741
+ constructor(formFieldRegistry) {
4742
+ this._formFieldRegistry = formFieldRegistry;
4743
+ }
4744
+ execute(context) {
4745
+ const {
4746
+ claiming,
4747
+ formField,
4748
+ id
4749
+ } = context;
4750
+ if (claiming) {
4751
+ this._formFieldRegistry._ids.claim(id, formField);
4752
+ } else {
4753
+ this._formFieldRegistry._ids.unclaim(id);
4682
4754
  }
4683
- if (handler.canExecute) {
4684
- result = handler.canExecute(context);
4755
+ }
4756
+ revert(context) {
4757
+ const {
4758
+ claiming,
4759
+ formField,
4760
+ id
4761
+ } = context;
4762
+ if (claiming) {
4763
+ this._formFieldRegistry._ids.unclaim(id);
4764
+ } else {
4765
+ this._formFieldRegistry._ids.claim(id, formField);
4685
4766
  }
4686
4767
  }
4687
- return result;
4688
- };
4768
+ }
4769
+ UpdateIdClaimHandler.$inject = ['formFieldRegistry'];
4689
4770
 
4690
- /**
4691
- * Clear the command stack, erasing all undo / redo history.
4692
- *
4693
- * @param {boolean} [emit=true] Whether to fire an event. Defaults to `true`.
4694
- */
4695
- CommandStack.prototype.clear = function (emit) {
4696
- this._stack.length = 0;
4697
- this._stackIdx = -1;
4698
- if (emit !== false) {
4699
- this._fire('changed', {
4700
- trigger: 'clear'
4701
- });
4771
+ class UpdateKeyClaimHandler {
4772
+ /**
4773
+ * @constructor
4774
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
4775
+ */
4776
+ constructor(pathRegistry) {
4777
+ this._pathRegistry = pathRegistry;
4702
4778
  }
4703
- };
4704
-
4705
- /**
4706
- * Undo last command(s)
4707
- */
4708
- CommandStack.prototype.undo = function () {
4709
- let action = this._getUndoAction(),
4710
- next;
4711
- if (action) {
4712
- this._currentExecution.trigger = 'undo';
4713
- this._pushAction(action);
4714
- while (action) {
4715
- this._internalUndo(action);
4716
- next = this._getUndoAction();
4717
- if (!next || next.id !== action.id) {
4718
- break;
4779
+ execute(context) {
4780
+ const {
4781
+ claiming,
4782
+ formField,
4783
+ key
4784
+ } = context;
4785
+ const options = {
4786
+ replacements: {
4787
+ [formField.id]: key
4719
4788
  }
4720
- action = next;
4789
+ };
4790
+ const valuePath = this._pathRegistry.getValuePath(formField, options);
4791
+ if (claiming) {
4792
+ this._pathRegistry.claimPath(valuePath, {
4793
+ isClosed: true,
4794
+ claimerId: formField.id
4795
+ });
4796
+ } else {
4797
+ this._pathRegistry.unclaimPath(valuePath);
4798
+ }
4799
+
4800
+ // cache path for revert
4801
+ context.valuePath = valuePath;
4802
+ }
4803
+ revert(context) {
4804
+ const {
4805
+ claiming,
4806
+ formField,
4807
+ valuePath
4808
+ } = context;
4809
+ if (claiming) {
4810
+ this._pathRegistry.unclaimPath(valuePath);
4811
+ } else {
4812
+ this._pathRegistry.claimPath(valuePath, {
4813
+ isClosed: true,
4814
+ claimerId: formField.id
4815
+ });
4721
4816
  }
4722
- this._popAction();
4723
4817
  }
4724
- };
4818
+ }
4819
+ UpdateKeyClaimHandler.$inject = ['pathRegistry'];
4725
4820
 
4726
- /**
4727
- * Redo last command(s)
4728
- */
4729
- CommandStack.prototype.redo = function () {
4730
- let action = this._getRedoAction(),
4731
- next;
4732
- if (action) {
4733
- this._currentExecution.trigger = 'redo';
4734
- this._pushAction(action);
4735
- while (action) {
4736
- this._internalExecute(action, true);
4737
- next = this._getRedoAction();
4738
- if (!next || next.id !== action.id) {
4739
- break;
4821
+ class UpdatePathClaimHandler {
4822
+ /**
4823
+ * @constructor
4824
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
4825
+ */
4826
+ constructor(pathRegistry) {
4827
+ this._pathRegistry = pathRegistry;
4828
+ }
4829
+ execute(context) {
4830
+ const {
4831
+ claiming,
4832
+ formField,
4833
+ path
4834
+ } = context;
4835
+ const options = {
4836
+ replacements: {
4837
+ [formField.id]: path
4740
4838
  }
4741
- action = next;
4839
+ };
4840
+ const valuePaths = [];
4841
+ if (claiming) {
4842
+ this._pathRegistry.executeRecursivelyOnFields(formField, ({
4843
+ field,
4844
+ isClosed,
4845
+ isRepeatable
4846
+ }) => {
4847
+ const valuePath = this._pathRegistry.getValuePath(field, options);
4848
+ valuePaths.push({
4849
+ valuePath,
4850
+ isClosed,
4851
+ isRepeatable,
4852
+ claimerId: field.id
4853
+ });
4854
+ this._pathRegistry.claimPath(valuePath, {
4855
+ isClosed,
4856
+ isRepeatable,
4857
+ claimerId: field.id
4858
+ });
4859
+ });
4860
+ } else {
4861
+ this._pathRegistry.executeRecursivelyOnFields(formField, ({
4862
+ field,
4863
+ isClosed,
4864
+ isRepeatable
4865
+ }) => {
4866
+ const valuePath = this._pathRegistry.getValuePath(field, options);
4867
+ valuePaths.push({
4868
+ valuePath,
4869
+ isClosed,
4870
+ isRepeatable,
4871
+ claimerId: field.id
4872
+ });
4873
+ this._pathRegistry.unclaimPath(valuePath);
4874
+ });
4742
4875
  }
4743
- this._popAction();
4744
- }
4745
- };
4746
-
4747
- /**
4748
- * Register a handler instance with the command stack.
4749
- *
4750
- * @param {string} command Command to be executed.
4751
- * @param {CommandHandler} handler Handler to execute the command.
4752
- */
4753
- CommandStack.prototype.register = function (command, handler) {
4754
- this._setHandler(command, handler);
4755
- };
4756
4876
 
4757
- /**
4758
- * Register a handler type with the command stack by instantiating it and
4759
- * injecting its dependencies.
4760
- *
4761
- * @param {string} command Command to be executed.
4762
- * @param {CommandHandlerConstructor} handlerCls Constructor to instantiate a {@link CommandHandler}.
4763
- */
4764
- CommandStack.prototype.registerHandler = function (command, handlerCls) {
4765
- if (!command || !handlerCls) {
4766
- throw new Error('command and handlerCls must be defined');
4877
+ // cache path info for revert
4878
+ context.valuePaths = valuePaths;
4767
4879
  }
4768
- const handler = this._injector.instantiate(handlerCls);
4769
- this.register(command, handler);
4770
- };
4771
-
4772
- /**
4773
- * @return {boolean}
4774
- */
4775
- CommandStack.prototype.canUndo = function () {
4776
- return !!this._getUndoAction();
4777
- };
4778
-
4779
- /**
4780
- * @return {boolean}
4781
- */
4782
- CommandStack.prototype.canRedo = function () {
4783
- return !!this._getRedoAction();
4784
- };
4785
-
4786
- // stack access //////////////////////
4787
-
4788
- CommandStack.prototype._getRedoAction = function () {
4789
- return this._stack[this._stackIdx + 1];
4790
- };
4791
- CommandStack.prototype._getUndoAction = function () {
4792
- return this._stack[this._stackIdx];
4793
- };
4794
-
4795
- // internal functionality //////////////////////
4796
-
4797
- CommandStack.prototype._internalUndo = function (action) {
4798
- const command = action.command,
4799
- context = action.context;
4800
- const handler = this._getHandler(command);
4801
-
4802
- // guard against illegal nested command stack invocations
4803
- this._atomicDo(() => {
4804
- this._fire(command, 'revert', action);
4805
- if (handler.revert) {
4806
- this._markDirty(handler.revert(context));
4880
+ revert(context) {
4881
+ const {
4882
+ claiming,
4883
+ valuePaths
4884
+ } = context;
4885
+ if (claiming) {
4886
+ valuePaths.forEach(({
4887
+ valuePath
4888
+ }) => {
4889
+ this._pathRegistry.unclaimPath(valuePath);
4890
+ });
4891
+ } else {
4892
+ valuePaths.forEach(({
4893
+ valuePath,
4894
+ isClosed,
4895
+ isRepeatable,
4896
+ claimerId
4897
+ }) => {
4898
+ this._pathRegistry.claimPath(valuePath, {
4899
+ isClosed,
4900
+ isRepeatable,
4901
+ claimerId
4902
+ });
4903
+ });
4807
4904
  }
4808
- this._revertedAction(action);
4809
- this._fire(command, 'reverted', action);
4810
- });
4811
- };
4812
- CommandStack.prototype._fire = function (command, qualifier, event) {
4813
- if (arguments.length < 3) {
4814
- event = qualifier;
4815
- qualifier = null;
4816
4905
  }
4817
- const names = qualifier ? [command + '.' + qualifier, qualifier] : [command];
4818
- let result;
4819
- event = this._eventBus.createEvent(event);
4820
- for (const name of names) {
4821
- result = this._eventBus.fire('commandStack.' + name, event);
4822
- if (event.cancelBubble) {
4823
- break;
4824
- }
4906
+ }
4907
+ UpdatePathClaimHandler.$inject = ['pathRegistry'];
4908
+
4909
+ class Modeling {
4910
+ constructor(commandStack, eventBus, formEditor, formFieldRegistry, fieldFactory) {
4911
+ this._commandStack = commandStack;
4912
+ this._formEditor = formEditor;
4913
+ this._formFieldRegistry = formFieldRegistry;
4914
+ this._fieldFactory = fieldFactory;
4915
+ eventBus.on('form.init', () => {
4916
+ this.registerHandlers();
4917
+ });
4825
4918
  }
4826
- return result;
4827
- };
4828
- CommandStack.prototype._createId = function () {
4829
- return this._uid++;
4830
- };
4831
- CommandStack.prototype._atomicDo = function (fn) {
4832
- const execution = this._currentExecution;
4833
- execution.atomic = true;
4834
- try {
4835
- fn();
4836
- } finally {
4837
- execution.atomic = false;
4919
+ registerHandlers() {
4920
+ Object.entries(this.getHandlers()).forEach(([id, handler]) => {
4921
+ this._commandStack.registerHandler(id, handler);
4922
+ });
4838
4923
  }
4839
- };
4840
- CommandStack.prototype._internalExecute = function (action, redo) {
4841
- const command = action.command,
4842
- context = action.context;
4843
- const handler = this._getHandler(command);
4844
- if (!handler) {
4845
- throw new Error('no command handler registered for <' + command + '>');
4924
+ getHandlers() {
4925
+ return {
4926
+ 'formField.add': AddFormFieldHandler,
4927
+ 'formField.edit': EditFormFieldHandler,
4928
+ 'formField.move': MoveFormFieldHandler,
4929
+ 'formField.remove': RemoveFormFieldHandler,
4930
+ 'id.updateClaim': UpdateIdClaimHandler,
4931
+ 'key.updateClaim': UpdateKeyClaimHandler,
4932
+ 'path.updateClaim': UpdatePathClaimHandler
4933
+ };
4846
4934
  }
4847
- this._pushAction(action);
4848
- if (!redo) {
4849
- this._fire(command, 'preExecute', action);
4850
- if (handler.preExecute) {
4851
- handler.preExecute(context);
4935
+ addFormField(attrs, targetFormField, targetIndex) {
4936
+ const formField = this._fieldFactory.create(attrs);
4937
+ const context = {
4938
+ formField,
4939
+ targetFormField,
4940
+ targetIndex
4941
+ };
4942
+ this._commandStack.execute('formField.add', context);
4943
+ return formField;
4944
+ }
4945
+ editFormField(formField, properties, value) {
4946
+ if (!minDash.isObject(properties)) {
4947
+ properties = {
4948
+ [properties]: value
4949
+ };
4852
4950
  }
4853
- this._fire(command, 'preExecuted', action);
4951
+ const context = {
4952
+ formField,
4953
+ properties
4954
+ };
4955
+ this._commandStack.execute('formField.edit', context);
4854
4956
  }
4855
-
4856
- // guard against illegal nested command stack invocations
4857
- this._atomicDo(() => {
4858
- this._fire(command, 'execute', action);
4859
- if (handler.execute) {
4860
- // actual execute + mark return results as dirty
4861
- this._markDirty(handler.execute(context));
4862
- }
4863
-
4864
- // log to stack
4865
- this._executedAction(action, redo);
4866
- this._fire(command, 'executed', action);
4867
- });
4868
- if (!redo) {
4869
- this._fire(command, 'postExecute', action);
4870
- if (handler.postExecute) {
4871
- handler.postExecute(context);
4872
- }
4873
- this._fire(command, 'postExecuted', action);
4957
+ moveFormField(formField, sourceFormField, targetFormField, sourceIndex, targetIndex, sourceRow, targetRow) {
4958
+ const context = {
4959
+ formField,
4960
+ sourceFormField,
4961
+ targetFormField,
4962
+ sourceIndex,
4963
+ targetIndex,
4964
+ sourceRow,
4965
+ targetRow
4966
+ };
4967
+ this._commandStack.execute('formField.move', context);
4874
4968
  }
4875
- this._popAction();
4876
- };
4877
- CommandStack.prototype._pushAction = function (action) {
4878
- const execution = this._currentExecution,
4879
- actions = execution.actions;
4880
- const baseAction = actions[0];
4881
- if (execution.atomic) {
4882
- throw new Error('illegal invocation in <execute> or <revert> phase (action: ' + action.command + ')');
4969
+ removeFormField(formField, sourceFormField, sourceIndex) {
4970
+ const context = {
4971
+ formField,
4972
+ sourceFormField,
4973
+ sourceIndex
4974
+ };
4975
+ this._commandStack.execute('formField.remove', context);
4883
4976
  }
4884
- if (!action.id) {
4885
- action.id = baseAction && baseAction.id || this._createId();
4977
+ claimId(formField, id) {
4978
+ const context = {
4979
+ formField,
4980
+ id,
4981
+ claiming: true
4982
+ };
4983
+ this._commandStack.execute('id.updateClaim', context);
4886
4984
  }
4887
- actions.push(action);
4888
- };
4889
- CommandStack.prototype._popAction = function () {
4890
- const execution = this._currentExecution,
4891
- trigger = execution.trigger,
4892
- actions = execution.actions,
4893
- dirty = execution.dirty;
4894
- actions.pop();
4895
- if (!actions.length) {
4896
- this._eventBus.fire('elements.changed', {
4897
- elements: minDash.uniqueBy('id', dirty.reverse())
4898
- });
4899
- dirty.length = 0;
4900
- this._fire('changed', {
4901
- trigger: trigger
4902
- });
4903
- execution.trigger = null;
4985
+ unclaimId(formField, id) {
4986
+ const context = {
4987
+ formField,
4988
+ id,
4989
+ claiming: false
4990
+ };
4991
+ this._commandStack.execute('id.updateClaim', context);
4904
4992
  }
4905
- };
4906
- CommandStack.prototype._markDirty = function (elements) {
4907
- const execution = this._currentExecution;
4908
- if (!elements) {
4909
- return;
4993
+ claimKey(formField, key) {
4994
+ const context = {
4995
+ formField,
4996
+ key,
4997
+ claiming: true
4998
+ };
4999
+ this._commandStack.execute('key.updateClaim', context);
4910
5000
  }
4911
- elements = minDash.isArray(elements) ? elements : [elements];
4912
- execution.dirty = execution.dirty.concat(elements);
4913
- };
4914
- CommandStack.prototype._executedAction = function (action, redo) {
4915
- const stackIdx = ++this._stackIdx;
4916
- if (!redo) {
4917
- this._stack.splice(stackIdx, this._stack.length, action);
5001
+ unclaimKey(formField, key) {
5002
+ const context = {
5003
+ formField,
5004
+ key,
5005
+ claiming: false
5006
+ };
5007
+ this._commandStack.execute('key.updateClaim', context);
4918
5008
  }
4919
- };
4920
- CommandStack.prototype._revertedAction = function (action) {
4921
- this._stackIdx--;
4922
- };
4923
- CommandStack.prototype._getHandler = function (command) {
4924
- return this._handlerMap[command];
4925
- };
4926
- CommandStack.prototype._setHandler = function (command, handler) {
4927
- if (!command || !handler) {
4928
- throw new Error('command and handler required');
5009
+ claimPath(formField, path) {
5010
+ const context = {
5011
+ formField,
5012
+ path,
5013
+ claiming: true
5014
+ };
5015
+ this._commandStack.execute('path.updateClaim', context);
4929
5016
  }
4930
- if (this._handlerMap[command]) {
4931
- throw new Error('overriding handler for command <' + command + '>');
5017
+ unclaimPath(formField, path) {
5018
+ const context = {
5019
+ formField,
5020
+ path,
5021
+ claiming: false
5022
+ };
5023
+ this._commandStack.execute('path.updateClaim', context);
4932
5024
  }
4933
- this._handlerMap[command] = handler;
4934
- };
4935
-
4936
- /**
4937
- * @type { import('didi').ModuleDeclaration }
4938
- */
4939
- var commandModule = {
4940
- commandStack: ['type', CommandStack]
4941
- };
5025
+ }
5026
+ Modeling.$inject = ['commandStack', 'eventBus', 'formEditor', 'formFieldRegistry', 'fieldFactory'];
4942
5027
 
4943
- var ModelingModule = {
4944
- __depends__: [behaviorModule, commandModule],
5028
+ const ModelingModule = {
5029
+ __depends__: [BehaviorModule, commandModule],
4945
5030
  __init__: ['formLayoutUpdater', 'modeling'],
4946
5031
  formLayoutUpdater: ['type', FormLayoutUpdater],
4947
5032
  modeling: ['type', Modeling]
@@ -5012,7 +5097,7 @@ class SelectionBehavior {
5012
5097
  }
5013
5098
  SelectionBehavior.$inject = ['eventBus', 'selection'];
5014
5099
 
5015
- var SelectionModule = {
5100
+ const SelectionModule = {
5016
5101
  __init__: ['selection', 'selectionBehavior'],
5017
5102
  selection: ['type', Selection],
5018
5103
  selectionBehavior: ['type', SelectionBehavior]
@@ -5083,15 +5168,15 @@ class SectionModuleBase {
5083
5168
  }
5084
5169
  }
5085
5170
 
5086
- let PaletteModule$1 = class PaletteModule extends SectionModuleBase {
5171
+ class PaletteRenderer extends SectionModuleBase {
5087
5172
  constructor(eventBus) {
5088
5173
  super(eventBus, 'palette');
5089
5174
  }
5090
- };
5091
- PaletteModule$1.$inject = ['eventBus'];
5175
+ }
5176
+ PaletteRenderer.$inject = ['eventBus'];
5092
5177
 
5093
- var PaletteModule = {
5094
- palette: ['type', PaletteModule$1]
5178
+ const PaletteModule = {
5179
+ palette: ['type', PaletteRenderer]
5095
5180
  };
5096
5181
 
5097
5182
  var ArrowIcon = function ArrowIcon(props) {
@@ -5218,6 +5303,24 @@ HelpIcon.defaultProps = {
5218
5303
  xmlns: "http://www.w3.org/2000/svg",
5219
5304
  viewBox: "0 0 32 32"
5220
5305
  };
5306
+ var PopupIcon = function PopupIcon(props) {
5307
+ return jsxRuntime.jsxs("svg", {
5308
+ ...props,
5309
+ children: [jsxRuntime.jsx("path", {
5310
+ fill: "currentColor",
5311
+ d: "M28 4H10a2.006 2.006 0 0 0-2 2v14a2.006 2.006 0 0 0 2 2h18a2.006 2.006 0 0 0 2-2V6a2.006 2.006 0 0 0-2-2Zm0 16H10V6h18Z"
5312
+ }), jsxRuntime.jsx("path", {
5313
+ fill: "currentColor",
5314
+ d: "M18 26H4V16h2v-2H4a2.006 2.006 0 0 0-2 2v10a2.006 2.006 0 0 0 2 2h14a2.006 2.006 0 0 0 2-2v-2h-2Z"
5315
+ })]
5316
+ });
5317
+ };
5318
+ PopupIcon.defaultProps = {
5319
+ xmlns: "http://www.w3.org/2000/svg",
5320
+ width: "16",
5321
+ height: "16",
5322
+ viewBox: "0 0 32 32"
5323
+ };
5221
5324
  function Header(props) {
5222
5325
  const {
5223
5326
  element,
@@ -5288,6 +5391,7 @@ const ErrorsContext = preact.createContext({
5288
5391
  *
5289
5392
  * @returns void
5290
5393
  */
5394
+
5291
5395
  const EventContext = preact.createContext({
5292
5396
  eventBus: null
5293
5397
  });
@@ -5343,7 +5447,9 @@ function Tooltip(props) {
5343
5447
  const {
5344
5448
  forId,
5345
5449
  value,
5346
- parent
5450
+ parent,
5451
+ direction = 'right',
5452
+ position
5347
5453
  } = props;
5348
5454
  const [visible, setShow] = hooks.useState(false);
5349
5455
  const [focusedViaKeyboard, setFocusedViaKeyboard] = hooks.useState(false);
@@ -5412,11 +5518,11 @@ function Tooltip(props) {
5412
5518
  }, [wrapperRef.current, visible, focusedViaKeyboard]);
5413
5519
  const renderTooltip = () => {
5414
5520
  return jsxRuntime.jsxs("div", {
5415
- class: "bio-properties-panel-tooltip",
5521
+ class: `bio-properties-panel-tooltip ${direction}`,
5416
5522
  role: "tooltip",
5417
5523
  id: "bio-properties-panel-tooltip",
5418
5524
  "aria-labelledby": forId,
5419
- style: getTooltipPosition(wrapperRef.current),
5525
+ style: position || getTooltipPosition(wrapperRef.current),
5420
5526
  ref: tooltipRef,
5421
5527
  onClick: e => e.stopPropagation(),
5422
5528
  children: [jsxRuntime.jsx("div", {
@@ -6030,7 +6136,7 @@ const CodeEditor = React.forwardRef((props, ref) => {
6030
6136
  tooltipContainer: tooltipContainer,
6031
6137
  value: localValue,
6032
6138
  variables: variables,
6033
- extensions: [...(enableGutters ? [view.lineNumbers()] : [])]
6139
+ extensions: [...(enableGutters ? [view.lineNumbers()] : []), view.EditorView.lineWrapping]
6034
6140
  });
6035
6141
  setEditor(editor);
6036
6142
  return () => {
@@ -6073,7 +6179,7 @@ const CodeEditor = React.forwardRef((props, ref) => {
6073
6179
  title: "Open pop-up editor",
6074
6180
  class: "bio-properties-panel-open-feel-popup",
6075
6181
  onClick: () => onPopupOpen(),
6076
- children: jsxRuntime.jsx(ExternalLinkIcon, {})
6182
+ children: jsxRuntime.jsx(PopupIcon, {})
6077
6183
  })]
6078
6184
  });
6079
6185
  });
@@ -6284,6 +6390,7 @@ function PopupComponent(props, globalRef) {
6284
6390
  }
6285
6391
  return () => focusTrapRef.current && focusTrapRef.current.deactivate();
6286
6392
  }, [popupRef]);
6393
+ useEvent('propertiesPanel.detach', onClose);
6287
6394
  return React.createPortal(jsxRuntime.jsx("div", {
6288
6395
  "aria-label": title,
6289
6396
  tabIndex: -1,
@@ -6462,7 +6569,13 @@ function FEELPopupRoot(props) {
6462
6569
  setSourceElement(_sourceElement);
6463
6570
  emit('open');
6464
6571
  };
6465
- const handleClose = () => {
6572
+ const handleClose = (event = {}) => {
6573
+ const {
6574
+ id
6575
+ } = event;
6576
+ if (id && id !== source) {
6577
+ return;
6578
+ }
6466
6579
  setOpen(false);
6467
6580
  setSource(null);
6468
6581
  };
@@ -6632,7 +6745,7 @@ function FeelPopupComponent(props) {
6632
6745
  }), jsxRuntime.jsx(Popup.Footer, {
6633
6746
  children: jsxRuntime.jsx("button", {
6634
6747
  type: "button",
6635
- onClick: onClose,
6748
+ onClick: () => onClose(),
6636
6749
  title: "Close pop-up editor",
6637
6750
  class: "bio-properties-panel-feel-popup__close-btn",
6638
6751
  children: "Close"
@@ -6808,18 +6921,14 @@ function NumberField(props) {
6808
6921
  } = props;
6809
6922
  const [localValue, setLocalValue] = hooks.useState(value);
6810
6923
  const handleInputCallback = hooks.useMemo(() => {
6811
- return debounce(event => {
6812
- const {
6813
- validity,
6814
- value
6815
- } = event.target;
6816
- if (validity.valid) {
6817
- onInput(value ? parseFloat(value) : undefined);
6924
+ return debounce(target => {
6925
+ if (target.validity.valid) {
6926
+ onInput(target.value ? parseFloat(target.value) : undefined);
6818
6927
  }
6819
6928
  });
6820
6929
  }, [onInput, debounce]);
6821
6930
  const handleInput = e => {
6822
- handleInputCallback(e);
6931
+ handleInputCallback(e.target);
6823
6932
  setLocalValue(e.target.value);
6824
6933
  };
6825
6934
  hooks.useEffect(() => {
@@ -6896,7 +7005,7 @@ function NumberFieldEntry(props) {
6896
7005
  const newValidationError = validate(value) || null;
6897
7006
  setLocalError(newValidationError);
6898
7007
  }
6899
- }, [value]);
7008
+ }, [value, validate]);
6900
7009
  const onInput = newValue => {
6901
7010
  let newValidationError = null;
6902
7011
  if (minDash.isFunction(validate)) {
@@ -6941,7 +7050,7 @@ function prefixId$6(id) {
6941
7050
  return `bio-properties-panel-${id}`;
6942
7051
  }
6943
7052
  const noop$2 = () => {};
6944
- function FeelTextfield(props) {
7053
+ function FeelTextfieldComponent(props) {
6945
7054
  const {
6946
7055
  debounce,
6947
7056
  id,
@@ -7018,9 +7127,7 @@ function FeelTextfield(props) {
7018
7127
  onError(undefined);
7019
7128
  return;
7020
7129
  }
7021
- const error = lint[0];
7022
- const message = `${error.source}: ${error.message}`;
7023
- onError(message);
7130
+ onError('Unparsable FEEL expression.');
7024
7131
  });
7025
7132
  const handlePopupOpen = (type = 'feel') => {
7026
7133
  const popupOptions = {
@@ -7143,6 +7250,7 @@ function FeelTextfield(props) {
7143
7250
  })]
7144
7251
  });
7145
7252
  }
7253
+ const FeelTextfield = withAutoClosePopup(FeelTextfieldComponent);
7146
7254
  const OptionalFeelInput = React.forwardRef((props, ref) => {
7147
7255
  const {
7148
7256
  id,
@@ -7395,7 +7503,7 @@ function FeelEntry(props) {
7395
7503
  const newValidationError = validate(value) || null;
7396
7504
  setValidationError(newValidationError);
7397
7505
  }
7398
- }, [value]);
7506
+ }, [value, validate]);
7399
7507
  const onInput = useStaticCallback(newValue => {
7400
7508
  let newValidationError = null;
7401
7509
  if (minDash.isFunction(validate)) {
@@ -7567,6 +7675,27 @@ function getPopupTitle(element, label) {
7567
7675
  }
7568
7676
  return `${popupTitle}${label}`;
7569
7677
  }
7678
+ function withAutoClosePopup(Component) {
7679
+ return function (props) {
7680
+ const {
7681
+ id
7682
+ } = props;
7683
+ const {
7684
+ close
7685
+ } = hooks.useContext(FeelPopupContext);
7686
+ const closePopup = useStaticCallback(close);
7687
+ hooks.useEffect(() => {
7688
+ return () => {
7689
+ closePopup({
7690
+ id
7691
+ });
7692
+ };
7693
+ }, []);
7694
+ return jsxRuntime.jsx(Component, {
7695
+ ...props
7696
+ });
7697
+ };
7698
+ }
7570
7699
  const DEFAULT_LAYOUT = {};
7571
7700
  const DEFAULT_DESCRIPTION = {};
7572
7701
  const DEFAULT_TOOLTIP = {};
@@ -7651,7 +7780,7 @@ const DEFAULT_TOOLTIP = {};
7651
7780
  * @param {HTMLElement} [props.feelPopupContainer]
7652
7781
  * @param {Object} [props.eventBus]
7653
7782
  */
7654
- function PropertiesPanel(props) {
7783
+ function PropertiesPanel$1(props) {
7655
7784
  const {
7656
7785
  element,
7657
7786
  headerProvider,
@@ -8370,7 +8499,7 @@ function SelectEntry(props) {
8370
8499
  const newValidationError = validate(value) || null;
8371
8500
  setLocalError(newValidationError);
8372
8501
  }
8373
- }, [value]);
8502
+ }, [value, validate]);
8374
8503
  const onChange = newValue => {
8375
8504
  let newValidationError = null;
8376
8505
  if (minDash.isFunction(validate)) {
@@ -8438,12 +8567,10 @@ function TextArea(props) {
8438
8567
  const [localValue, setLocalValue] = hooks.useState(value);
8439
8568
  const ref = useShowEntryEvent(id);
8440
8569
  const handleInputCallback = hooks.useMemo(() => {
8441
- return debounce(({
8442
- target
8443
- }) => onInput(target.value.length ? target.value : undefined));
8570
+ return debounce(target => onInput(target.value.length ? target.value : undefined));
8444
8571
  }, [onInput, debounce]);
8445
8572
  const handleInput = e => {
8446
- handleInputCallback(e);
8573
+ handleInputCallback(e.target);
8447
8574
  autoResize && resizeToContents(e.target);
8448
8575
  setLocalValue(e.target.value);
8449
8576
  };
@@ -8526,7 +8653,7 @@ function TextAreaEntry(props) {
8526
8653
  const newValidationError = validate(value) || null;
8527
8654
  setLocalError(newValidationError);
8528
8655
  }
8529
- }, [value]);
8656
+ }, [value, validate]);
8530
8657
  const onInput = newValue => {
8531
8658
  let newValidationError = null;
8532
8659
  if (minDash.isFunction(validate)) {
@@ -8587,12 +8714,10 @@ function Textfield(props) {
8587
8714
  const [localValue, setLocalValue] = hooks.useState(value || '');
8588
8715
  const ref = useShowEntryEvent(id);
8589
8716
  const handleInputCallback = hooks.useMemo(() => {
8590
- return debounce(({
8591
- target
8592
- }) => onInput(target.value.length ? target.value : undefined));
8717
+ return debounce(target => onInput(target.value.length ? target.value : undefined));
8593
8718
  }, [onInput, debounce]);
8594
8719
  const handleInput = e => {
8595
- handleInputCallback(e);
8720
+ handleInputCallback(e.target);
8596
8721
  setLocalValue(e.target.value);
8597
8722
  };
8598
8723
  hooks.useEffect(() => {
@@ -8667,7 +8792,7 @@ function TextfieldEntry(props) {
8667
8792
  const newValidationError = validate(value) || null;
8668
8793
  setLocalError(newValidationError);
8669
8794
  }
8670
- }, [value]);
8795
+ }, [value, validate]);
8671
8796
  const onInput = newValue => {
8672
8797
  let newValidationError = null;
8673
8798
  if (minDash.isFunction(validate)) {
@@ -8757,10 +8882,9 @@ var index = {
8757
8882
  * @returns {any}
8758
8883
  */
8759
8884
  function getService(type, strict) {}
8760
- const PropertiesPanelContext = preact.createContext({
8885
+ const FormPropertiesPanelContext = preact.createContext({
8761
8886
  getService
8762
8887
  });
8763
- var FormPropertiesPanelContext = PropertiesPanelContext;
8764
8888
 
8765
8889
  function arrayAdd(array, index, item) {
8766
8890
  const copy = [...array];
@@ -8772,6 +8896,12 @@ function countDecimals(number) {
8772
8896
  if (num.toString() === num.toFixed(0)) return 0;
8773
8897
  return num.toFixed().split('.')[1].length || 0;
8774
8898
  }
8899
+
8900
+ /**
8901
+ *
8902
+ * @param {unknown} value
8903
+ * @returns {boolean}
8904
+ */
8775
8905
  function isValidNumber(value) {
8776
8906
  return (typeof value === 'number' || typeof value === 'string') && value !== '' && !isNaN(Number(value));
8777
8907
  }
@@ -8794,6 +8924,7 @@ function textToLabel(text) {
8794
8924
  function isValidDotPath(path) {
8795
8925
  return /^\w+(\.\w+)*$/.test(path);
8796
8926
  }
8927
+ const LABELED_NON_INPUTS = ['button', 'group', 'dynamiclist', 'iframe', 'table'];
8797
8928
  const INPUTS = ['checkbox', 'checklist', 'datetime', 'number', 'radio', 'select', 'taglist', 'textfield', 'textarea'];
8798
8929
  const OPTIONS_INPUTS = ['checklist', 'radio', 'select', 'taglist'];
8799
8930
  function hasEntryConfigured(formFieldDefinition, entryId) {
@@ -8822,7 +8953,7 @@ function hasIntegerPathSegment(path) {
8822
8953
  return path.split('.').some(segment => /^\d+$/.test(segment));
8823
8954
  }
8824
8955
 
8825
- function useService (type, strict) {
8956
+ function useService(type, strict) {
8826
8957
  const {
8827
8958
  getService
8828
8959
  } = hooks.useContext(FormPropertiesPanelContext);
@@ -8840,12 +8971,13 @@ function useVariables() {
8840
8971
  return formJsViewer.getSchemaVariables(schema);
8841
8972
  }
8842
8973
 
8974
+ const headerlessTypes = ['spacer', 'separator', 'html'];
8843
8975
  const PropertiesPanelHeaderProvider = {
8844
8976
  getElementLabel: field => {
8845
8977
  const {
8846
8978
  type
8847
8979
  } = field;
8848
- if (type === 'spacer') {
8980
+ if (headerlessTypes.includes(type)) {
8849
8981
  return '';
8850
8982
  }
8851
8983
  if (type === 'text') {
@@ -8914,7 +9046,7 @@ const PropertiesPanelPlaceholderProvider = {
8914
9046
  }
8915
9047
  };
8916
9048
 
8917
- function FormPropertiesPanel(props) {
9049
+ function PropertiesPanel(props) {
8918
9050
  const {
8919
9051
  eventBus,
8920
9052
  getProviders,
@@ -8984,7 +9116,7 @@ function FormPropertiesPanel(props) {
8984
9116
  onBlurCapture: onBlur,
8985
9117
  children: jsxRuntime.jsx(FormPropertiesPanelContext.Provider, {
8986
9118
  value: propertiesPanelContext,
8987
- children: jsxRuntime.jsx(PropertiesPanel, {
9119
+ children: jsxRuntime.jsx(PropertiesPanel$1, {
8988
9120
  element: selectedFormField,
8989
9121
  eventBus: eventBus,
8990
9122
  groups: groups,
@@ -9000,7 +9132,7 @@ const DEFAULT_PRIORITY = 1000;
9000
9132
 
9001
9133
  /**
9002
9134
  * @typedef { { parent: Element } } PropertiesPanelConfig
9003
- * @typedef { import('../../core/EventBus').default } EventBus
9135
+ * @typedef { import('../../core/EventBus').EventBus } EventBus
9004
9136
  * @typedef { import('../../types').Injector } Injector
9005
9137
  * @typedef { { getGroups: ({ formField, editFormField }) => ({ groups}) => Array } } PropertiesProvider
9006
9138
  */
@@ -9060,7 +9192,7 @@ class PropertiesPanelRenderer {
9060
9192
  }
9061
9193
  }
9062
9194
  _render() {
9063
- preact.render(jsxRuntime.jsx(FormPropertiesPanel, {
9195
+ preact.render(jsxRuntime.jsx(PropertiesPanel, {
9064
9196
  getProviders: this._getProviders.bind(this),
9065
9197
  eventBus: this._eventBus,
9066
9198
  injector: this._injector
@@ -9220,9 +9352,9 @@ function Columns(props) {
9220
9352
  } = props;
9221
9353
  const debounce = useService('debounce');
9222
9354
  const formLayoutValidator = useService('formLayoutValidator');
9223
- const validate = value => {
9355
+ const validate = hooks.useCallback(value => {
9224
9356
  return formLayoutValidator.validateField(field, value ? parseInt(value) : null);
9225
- };
9357
+ }, [field, formLayoutValidator]);
9226
9358
  const setValue = (value, error) => {
9227
9359
  if (error) {
9228
9360
  return;
@@ -9315,7 +9447,7 @@ function Description(props) {
9315
9447
  }
9316
9448
 
9317
9449
  const EMPTY_OPTION = null;
9318
- function DefaultOptionEntry(props) {
9450
+ function DefaultValueEntry(props) {
9319
9451
  const {
9320
9452
  editField,
9321
9453
  field
@@ -9333,26 +9465,26 @@ function DefaultOptionEntry(props) {
9333
9465
  return matchers(field);
9334
9466
  };
9335
9467
  }
9336
- const defaultOptions = {
9468
+ const defaulValueBase = {
9337
9469
  editField,
9338
9470
  field,
9339
9471
  id: 'defaultValue',
9340
9472
  label: 'Default value'
9341
9473
  };
9342
9474
  entries.push({
9343
- ...defaultOptions,
9475
+ ...defaulValueBase,
9344
9476
  component: DefaultValueCheckbox,
9345
9477
  isEdited: isEdited$3,
9346
9478
  isDefaultVisible: isDefaultVisible(field => field.type === 'checkbox')
9347
9479
  });
9348
9480
  entries.push({
9349
- ...defaultOptions,
9481
+ ...defaulValueBase,
9350
9482
  component: DefaultValueNumber,
9351
9483
  isEdited: isEdited,
9352
9484
  isDefaultVisible: isDefaultVisible(field => field.type === 'number')
9353
9485
  });
9354
9486
  entries.push({
9355
- ...defaultOptions,
9487
+ ...defaulValueBase,
9356
9488
  component: DefaultValueSingleSelect,
9357
9489
  isEdited: isEdited$3,
9358
9490
  isDefaultVisible: isDefaultVisible(field => field.type === 'radio' || field.type === 'select')
@@ -9361,13 +9493,13 @@ function DefaultOptionEntry(props) {
9361
9493
  // todo(Skaiir): implement a multiselect equivalent (cf. https://github.com/bpmn-io/form-js/issues/265)
9362
9494
 
9363
9495
  entries.push({
9364
- ...defaultOptions,
9496
+ ...defaulValueBase,
9365
9497
  component: DefaultValueTextfield,
9366
9498
  isEdited: isEdited,
9367
9499
  isDefaultVisible: isDefaultVisible(field => field.type === 'textfield')
9368
9500
  });
9369
9501
  entries.push({
9370
- ...defaultOptions,
9502
+ ...defaulValueBase,
9371
9503
  component: DefaultValueTextarea,
9372
9504
  isEdited: isEdited$1,
9373
9505
  isDefaultVisible: isDefaultVisible(field => field.type === 'textarea')
@@ -9440,6 +9572,17 @@ function DefaultValueNumber(props) {
9440
9572
  return editField(field, path, newValue);
9441
9573
  };
9442
9574
  const decimalDigitsSet = decimalDigits || decimalDigits === 0;
9575
+ const validate = hooks.useCallback(value => {
9576
+ if (value === undefined || value === null) {
9577
+ return;
9578
+ }
9579
+ if (!isValidNumber(value)) {
9580
+ return 'Should be a valid number';
9581
+ }
9582
+ if (decimalDigitsSet && countDecimals(value) > decimalDigits) {
9583
+ return `Should not contain more than ${decimalDigits} decimal digits`;
9584
+ }
9585
+ }, [decimalDigitsSet, decimalDigits]);
9443
9586
  return TextfieldEntry({
9444
9587
  debounce,
9445
9588
  label,
@@ -9447,11 +9590,7 @@ function DefaultValueNumber(props) {
9447
9590
  getValue,
9448
9591
  id,
9449
9592
  setValue,
9450
- validate: value => {
9451
- if (value === undefined || value === null) return;
9452
- if (!isValidNumber(value)) return 'Should be a valid number';
9453
- if (decimalDigitsSet && countDecimals(value) > decimalDigits) return `Should not contain more than ${decimalDigits} decimal digits`;
9454
- }
9593
+ validate
9455
9594
  });
9456
9595
  }
9457
9596
  function DefaultValueSingleSelect(props) {
@@ -9625,8 +9764,8 @@ function Id(props) {
9625
9764
  }
9626
9765
  return editField(field, path, value);
9627
9766
  };
9628
- const validate = value => {
9629
- if (minDash.isUndefined(value) || !value.length) {
9767
+ const validate = hooks.useCallback(value => {
9768
+ if (typeof value !== 'string' || value.length === 0) {
9630
9769
  return 'Must not be empty.';
9631
9770
  }
9632
9771
  const assigned = formFieldRegistry._ids.assigned(value);
@@ -9634,7 +9773,7 @@ function Id(props) {
9634
9773
  return 'Must be unique.';
9635
9774
  }
9636
9775
  return validateId(value) || null;
9637
- };
9776
+ }, [formFieldRegistry, field]);
9638
9777
  return TextfieldEntry({
9639
9778
  debounce,
9640
9779
  element: field,
@@ -9711,7 +9850,7 @@ function Key$2(props) {
9711
9850
  }
9712
9851
  return editField(field, path, value);
9713
9852
  };
9714
- const validate = value => {
9853
+ const validate = hooks.useCallback(value => {
9715
9854
  if (value === field.key) {
9716
9855
  return null;
9717
9856
  }
@@ -9743,7 +9882,7 @@ function Key$2(props) {
9743
9882
  claimerId: field.id
9744
9883
  });
9745
9884
  return canClaim ? null : 'Must not conflict with other key/path assignments.';
9746
- };
9885
+ }, [field, pathRegistry]);
9747
9886
  return TextfieldEntry({
9748
9887
  debounce,
9749
9888
  description: 'Binds to a form variable',
@@ -9799,7 +9938,7 @@ function Path(props) {
9799
9938
  }
9800
9939
  return editField(field, path, value);
9801
9940
  };
9802
- const validate = value => {
9941
+ const validate = hooks.useCallback(value => {
9803
9942
  if (!value && isRepeating) {
9804
9943
  return 'Must not be empty';
9805
9944
  }
@@ -9845,7 +9984,7 @@ function Path(props) {
9845
9984
 
9846
9985
  // If all checks pass
9847
9986
  return null;
9848
- };
9987
+ }, [field, isRepeating, pathRegistry]);
9849
9988
  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.';
9850
9989
  return TextfieldEntry({
9851
9990
  debounce,
@@ -9963,8 +10102,7 @@ function simpleRangeIntegerEntryFactory(options) {
9963
10102
  path,
9964
10103
  props,
9965
10104
  min,
9966
- max,
9967
- defaultValue
10105
+ max
9968
10106
  } = options;
9969
10107
  const {
9970
10108
  editField,
@@ -9978,9 +10116,8 @@ function simpleRangeIntegerEntryFactory(options) {
9978
10116
  editField,
9979
10117
  min,
9980
10118
  max,
9981
- defaultValue,
9982
10119
  component: SimpleRangeIntegerEntry,
9983
- isEdited: isEdited$7
10120
+ isEdited: isEdited
9984
10121
  };
9985
10122
  }
9986
10123
  const SimpleRangeIntegerEntry = props => {
@@ -9990,28 +10127,43 @@ const SimpleRangeIntegerEntry = props => {
9990
10127
  path,
9991
10128
  field,
9992
10129
  editField,
9993
- min,
9994
- max,
9995
- defaultValue
10130
+ min = Number.MIN_SAFE_INTEGER,
10131
+ max = Number.MAX_SAFE_INTEGER
9996
10132
  } = props;
9997
10133
  const debounce = useService('debounce');
9998
10134
  const getValue = () => {
9999
- const value = minDash.get(field, path, defaultValue);
10000
- return Number.isInteger(value) ? value : defaultValue;
10135
+ const value = minDash.get(field, path);
10136
+ const isValid = isValidNumber(value) && Number.isInteger(value);
10137
+ return isValid ? value : null;
10001
10138
  };
10002
- const setValue = value => {
10003
- editField(field, path, value);
10139
+ const setValue = (value, error) => {
10140
+ if (error) {
10141
+ return;
10142
+ }
10143
+ editField(field, path, Number(value));
10004
10144
  };
10005
- return NumberFieldEntry({
10145
+ const validate = hooks.useCallback(value => {
10146
+ if (value === undefined || value === null || value === '') {
10147
+ return;
10148
+ }
10149
+ if (!Number.isInteger(Number(value))) {
10150
+ return 'Should be an integer.';
10151
+ }
10152
+ if (Big(value).cmp(min) < 0) {
10153
+ return `Should be at least ${min}.`;
10154
+ }
10155
+ if (Big(value).cmp(max) > 0) {
10156
+ return `Should be at most ${max}.`;
10157
+ }
10158
+ }, [min, max]);
10159
+ return TextfieldEntry({
10006
10160
  debounce,
10007
10161
  label,
10008
10162
  element: field,
10009
- step: 1,
10010
- min,
10011
- max,
10012
10163
  getValue,
10013
10164
  id,
10014
- setValue
10165
+ setValue,
10166
+ validate
10015
10167
  });
10016
10168
  };
10017
10169
 
@@ -10060,13 +10212,16 @@ function LabelEntry(props) {
10060
10212
  return field.type === 'datetime' && (field.subtype === formJsViewer.DATETIME_SUBTYPES.TIME || field.subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME);
10061
10213
  }
10062
10214
  });
10215
+ const isSimplyLabled = field => {
10216
+ return [...INPUTS.filter(input => input !== 'datetime'), ...LABELED_NON_INPUTS].includes(field.type);
10217
+ };
10063
10218
  entries.push({
10064
10219
  id: 'label',
10065
10220
  component: Label$2,
10066
10221
  editField,
10067
10222
  field,
10068
10223
  isEdited: isEdited$6,
10069
- isDefaultVisible: field => [...INPUTS, 'button', 'group', 'table', 'iframe', 'dynamiclist'].includes(field.type)
10224
+ isDefaultVisible: isSimplyLabled
10070
10225
  });
10071
10226
  return entries;
10072
10227
  }
@@ -10227,14 +10382,28 @@ function Height(props) {
10227
10382
  id,
10228
10383
  getValue,
10229
10384
  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
- }
10385
+ validate: validate$7
10235
10386
  });
10236
10387
  }
10237
10388
 
10389
+ // helpers //////////
10390
+
10391
+ /**
10392
+ * @param {number|void} value
10393
+ * @returns {string|null}
10394
+ */
10395
+ const validate$7 = value => {
10396
+ if (typeof value !== 'number') {
10397
+ return null;
10398
+ }
10399
+ if (!Number.isInteger(value)) {
10400
+ return 'Should be an integer.';
10401
+ }
10402
+ if (value < 1) {
10403
+ return 'Should be greater than zero.';
10404
+ }
10405
+ };
10406
+
10238
10407
  function IFrameHeightEntry(props) {
10239
10408
  return [...HeightEntry({
10240
10409
  ...props,
@@ -10279,14 +10448,6 @@ function Url(props) {
10279
10448
  const setValue = value => {
10280
10449
  return editField(field, path, value);
10281
10450
  };
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
10451
  return FeelTemplatingEntry({
10291
10452
  debounce,
10292
10453
  element: field,
@@ -10296,15 +10457,15 @@ function Url(props) {
10296
10457
  label: 'URL',
10297
10458
  setValue,
10298
10459
  singleLine: true,
10299
- tooltip: getTooltip(),
10300
- validate,
10460
+ tooltip: getTooltip$1(),
10461
+ validate: validate$6,
10301
10462
  variables
10302
10463
  });
10303
10464
  }
10304
10465
 
10305
10466
  // helper //////////////////////
10306
10467
 
10307
- function getTooltip() {
10468
+ function getTooltip$1() {
10308
10469
  return jsxRuntime.jsxs(jsxRuntime.Fragment, {
10309
10470
  children: [jsxRuntime.jsx("p", {
10310
10471
  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)."
@@ -10320,7 +10481,20 @@ function getTooltip() {
10320
10481
  });
10321
10482
  }
10322
10483
 
10323
- function SourceEntry(props) {
10484
+ /**
10485
+ * @param {string|void} value
10486
+ * @returns {string|null}
10487
+ */
10488
+ const validate$6 = value => {
10489
+ if (!value || value.startsWith('=')) {
10490
+ return;
10491
+ }
10492
+ if (!HTTPS_PATTERN.test(value)) {
10493
+ return 'For security reasons the URL must start with "https".';
10494
+ }
10495
+ };
10496
+
10497
+ function ImageSourceEntry(props) {
10324
10498
  const {
10325
10499
  editField,
10326
10500
  field
@@ -10400,16 +10574,9 @@ function Text(props) {
10400
10574
  const setValue = value => {
10401
10575
  return editField(field, path, value || '');
10402
10576
  };
10403
- const description = hooks.useMemo(() => jsxRuntime.jsxs(jsxRuntime.Fragment, {
10404
- children: ["Supports markdown and templating. ", jsxRuntime.jsx("a", {
10405
- href: "https://docs.camunda.io/docs/components/modeler/forms/form-element-library/forms-element-library-text/",
10406
- target: "_blank",
10407
- children: "Learn more"
10408
- })]
10409
- }), []);
10410
10577
  return FeelTemplatingEntry({
10411
10578
  debounce,
10412
- description,
10579
+ description: description$1,
10413
10580
  element: field,
10414
10581
  getValue,
10415
10582
  id,
@@ -10419,6 +10586,85 @@ function Text(props) {
10419
10586
  variables
10420
10587
  });
10421
10588
  }
10589
+ const description$1 = jsxRuntime.jsxs(jsxRuntime.Fragment, {
10590
+ children: ["Supports markdown and templating. ", jsxRuntime.jsx("a", {
10591
+ href: "https://docs.camunda.io/docs/components/modeler/forms/form-element-library/forms-element-library-text/",
10592
+ target: "_blank",
10593
+ children: "Learn more"
10594
+ })]
10595
+ });
10596
+
10597
+ function HtmlEntry(props) {
10598
+ const {
10599
+ editField,
10600
+ field
10601
+ } = props;
10602
+ const entries = [{
10603
+ id: 'content',
10604
+ component: Content,
10605
+ editField: editField,
10606
+ field: field,
10607
+ isEdited: isEdited$6,
10608
+ isDefaultVisible: field => field.type === 'html'
10609
+ }];
10610
+ return entries;
10611
+ }
10612
+ function Content(props) {
10613
+ const {
10614
+ editField,
10615
+ field,
10616
+ id
10617
+ } = props;
10618
+ const debounce = useService('debounce');
10619
+ const variables = useVariables().map(name => ({
10620
+ name
10621
+ }));
10622
+ const path = ['content'];
10623
+ const getValue = () => {
10624
+ return minDash.get(field, path, '');
10625
+ };
10626
+ const setValue = value => {
10627
+ return editField(field, path, value || '');
10628
+ };
10629
+ return FeelTemplatingEntry({
10630
+ debounce,
10631
+ description,
10632
+ element: field,
10633
+ getValue,
10634
+ id,
10635
+ label: 'Content',
10636
+ hostLanguage: 'html',
10637
+ validate: validate$5,
10638
+ setValue,
10639
+ variables
10640
+ });
10641
+ }
10642
+
10643
+ // helpers //////////
10644
+
10645
+ const description = jsxRuntime.jsxs(jsxRuntime.Fragment, {
10646
+ children: ["Supports HTML, styling, and templating. Styles are automatically scoped to the HTML component. ", jsxRuntime.jsx("a", {
10647
+ href: "https://docs.camunda.io/docs/components/modeler/forms/form-element-library/forms-element-library-html/",
10648
+ target: "_blank",
10649
+ children: "Learn more"
10650
+ })]
10651
+ });
10652
+
10653
+ /**
10654
+ * @param {string|void} value
10655
+ * @returns {string|null}
10656
+ */
10657
+ const validate$5 = value => {
10658
+ // allow empty state
10659
+ if (typeof value !== 'string' || value === '') {
10660
+ return null;
10661
+ }
10662
+
10663
+ // allow expressions
10664
+ if (value.startsWith('=')) {
10665
+ return null;
10666
+ }
10667
+ };
10422
10668
 
10423
10669
  function NumberEntries(props) {
10424
10670
  const {
@@ -10467,11 +10713,7 @@ function NumberDecimalDigits(props) {
10467
10713
  getValue,
10468
10714
  id,
10469
10715
  setValue,
10470
- validate: value => {
10471
- if (value === undefined || value === null) return;
10472
- if (value < 0) return 'Should be greater than or equal to zero.';
10473
- if (!Number.isInteger(value)) return 'Should be an integer.';
10474
- }
10716
+ validate: validateNumberEntries
10475
10717
  });
10476
10718
  }
10477
10719
  function NumberArrowStep(props) {
@@ -10501,6 +10743,26 @@ function NumberArrowStep(props) {
10501
10743
  editField(field, ['increment'], clearLeadingZeroes(value));
10502
10744
  };
10503
10745
  const decimalDigitsSet = decimalDigits || decimalDigits === 0;
10746
+ const validate = hooks.useCallback(value => {
10747
+ if (value === undefined || value === null) {
10748
+ return;
10749
+ }
10750
+ if (!isValidNumber(value)) {
10751
+ return 'Should be a valid number.';
10752
+ }
10753
+ if (Big(value).cmp(0) <= 0) {
10754
+ return 'Should be greater than zero.';
10755
+ }
10756
+ if (decimalDigitsSet) {
10757
+ const minimumValue = Big(`1e-${decimalDigits}`);
10758
+ if (Big(value).cmp(minimumValue) < 0) {
10759
+ return `Should be at least ${minimumValue.toString()}.`;
10760
+ }
10761
+ if (countDecimals(value) > decimalDigits) {
10762
+ return `Should not contain more than ${decimalDigits} decimal digits.`;
10763
+ }
10764
+ }
10765
+ }, [decimalDigitsSet, decimalDigits]);
10504
10766
  return TextfieldEntry({
10505
10767
  debounce,
10506
10768
  label: 'Increment',
@@ -10508,19 +10770,28 @@ function NumberArrowStep(props) {
10508
10770
  getValue,
10509
10771
  id,
10510
10772
  setValue,
10511
- validate: value => {
10512
- if (value === undefined || value === null) return;
10513
- if (!isValidNumber(value)) return 'Should be a valid number.';
10514
- if (Big(value).cmp(0) <= 0) return 'Should be greater than zero.';
10515
- if (decimalDigitsSet) {
10516
- const minimumValue = Big(`1e-${decimalDigits}`);
10517
- if (Big(value).cmp(minimumValue) < 0) return `Should be at least ${minimumValue.toString()}.`;
10518
- if (countDecimals(value) > decimalDigits) return `Should not contain more than ${decimalDigits} decimal digits.`;
10519
- }
10520
- }
10773
+ validate
10521
10774
  });
10522
10775
  }
10523
10776
 
10777
+ // helpers //////////
10778
+
10779
+ /**
10780
+ * @param {number|void} value
10781
+ * @returns {string|void}
10782
+ */
10783
+ const validateNumberEntries = value => {
10784
+ if (typeof value !== 'number') {
10785
+ return;
10786
+ }
10787
+ if (!Number.isInteger(value)) {
10788
+ return 'Should be an integer.';
10789
+ }
10790
+ if (value < 0) {
10791
+ return 'Should be greater than or equal to zero.';
10792
+ }
10793
+ };
10794
+
10524
10795
  function NumberSerializationEntry(props) {
10525
10796
  const {
10526
10797
  editField,
@@ -10845,6 +11116,7 @@ function Label$1(props) {
10845
11116
  const getValue = () => {
10846
11117
  return minDash.get(field, ['values', index, 'label']);
10847
11118
  };
11119
+ const validate = hooks.useMemo(() => validateFactory(minDash.get(field, ['values', index, 'label']), entry => entry.label), [field, index, validateFactory]);
10848
11120
  return TextfieldEntry({
10849
11121
  debounce,
10850
11122
  element: field,
@@ -10852,7 +11124,7 @@ function Label$1(props) {
10852
11124
  id,
10853
11125
  label: 'Label',
10854
11126
  setValue,
10855
- validate: validateFactory(getValue(), entry => entry.label)
11127
+ validate
10856
11128
  });
10857
11129
  }
10858
11130
  function Value$1(props) {
@@ -10874,6 +11146,7 @@ function Value$1(props) {
10874
11146
  const getValue = () => {
10875
11147
  return minDash.get(field, ['values', index, 'value']);
10876
11148
  };
11149
+ const validate = hooks.useMemo(() => validateFactory(minDash.get(field, ['values', index, 'value']), entry => entry.value), [field, index, validateFactory]);
10877
11150
  return TextfieldEntry({
10878
11151
  debounce,
10879
11152
  element: field,
@@ -10881,7 +11154,7 @@ function Value$1(props) {
10881
11154
  id,
10882
11155
  label: 'Value',
10883
11156
  setValue,
10884
- validate: validateFactory(getValue(), entry => entry.value)
11157
+ validate
10885
11158
  });
10886
11159
  }
10887
11160
 
@@ -10932,6 +11205,7 @@ function Key$1(props) {
10932
11205
  const getValue = () => {
10933
11206
  return Object.keys(minDash.get(field, ['properties']))[index];
10934
11207
  };
11208
+ const validate = hooks.useMemo(() => validateFactory(Object.keys(minDash.get(field, ['properties']))[index]), [validateFactory, field, index]);
10935
11209
  return TextfieldEntry({
10936
11210
  debounce,
10937
11211
  element: field,
@@ -10939,7 +11213,7 @@ function Key$1(props) {
10939
11213
  id,
10940
11214
  label: 'Key',
10941
11215
  setValue,
10942
- validate: validateFactory(getValue())
11216
+ validate
10943
11217
  });
10944
11218
  }
10945
11219
  function Value(props) {
@@ -11133,15 +11407,6 @@ function InputValuesKey(props) {
11133
11407
  }
11134
11408
  editField(field, path, value || '');
11135
11409
  };
11136
- const validate = value => {
11137
- if (minDash.isUndefined(value) || !value.length) {
11138
- return 'Must not be empty.';
11139
- }
11140
- if (/\s/.test(value)) {
11141
- return 'Must not contain spaces.';
11142
- }
11143
- return null;
11144
- };
11145
11410
  return TextfieldEntry({
11146
11411
  debounce,
11147
11412
  description: 'Define which input property to populate the values from',
@@ -11151,10 +11416,26 @@ function InputValuesKey(props) {
11151
11416
  id,
11152
11417
  label: 'Input values key',
11153
11418
  setValue,
11154
- validate
11419
+ validate: validate$4
11155
11420
  });
11156
11421
  }
11157
11422
 
11423
+ // helpers //////////
11424
+
11425
+ /**
11426
+ * @param {string|void} value
11427
+ * @returns {string|null}
11428
+ */
11429
+ const validate$4 = value => {
11430
+ if (typeof value !== 'string' || value.length === 0) {
11431
+ return 'Must not be empty.';
11432
+ }
11433
+ if (/\s/.test(value)) {
11434
+ return 'Must not contain spaces.';
11435
+ }
11436
+ return null;
11437
+ };
11438
+
11158
11439
  function StaticOptionsSourceEntry(props) {
11159
11440
  const {
11160
11441
  editField,
@@ -11178,7 +11459,7 @@ function StaticOptionsSourceEntry(props) {
11178
11459
  if (value === key) {
11179
11460
  return;
11180
11461
  }
11181
- if (minDash.isUndefined(value) || !value.length) {
11462
+ if (typeof value !== 'string' || value.length === 0) {
11182
11463
  return 'Must not be empty.';
11183
11464
  }
11184
11465
  const isValueAssigned = values.find(entry => getValue(entry) === value);
@@ -11403,7 +11684,7 @@ function RepeatableEntry(props) {
11403
11684
  id: 'defaultRepetitions',
11404
11685
  path: ['defaultRepetitions'],
11405
11686
  label: 'Default number of items',
11406
- min: 0,
11687
+ min: 1,
11407
11688
  max: 20,
11408
11689
  props
11409
11690
  }), simpleBoolEntryFactory({
@@ -11564,26 +11845,6 @@ function Source(props) {
11564
11845
  }
11565
11846
  editField(field, path, value);
11566
11847
  };
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
11848
  return FeelTemplatingEntry({
11588
11849
  debounce,
11589
11850
  description: 'Specify the source from which to populate the table',
@@ -11596,10 +11857,32 @@ function Source(props) {
11596
11857
  setValue,
11597
11858
  singleLine: true,
11598
11859
  variables,
11599
- validate
11860
+ validate: validate$3
11600
11861
  });
11601
11862
  }
11602
11863
 
11864
+ // helper ////////////////
11865
+
11866
+ /**
11867
+ * @param {string|void} value
11868
+ * @returns {string|null}
11869
+ */
11870
+ const validate$3 = value => {
11871
+ if (!minDash.isString(value) || value.length === 0) {
11872
+ return 'Must not be empty.';
11873
+ }
11874
+ if (value.startsWith('=')) {
11875
+ return null;
11876
+ }
11877
+ if (!isValidDotPath(value)) {
11878
+ return 'Must be a variable or a dot separated path.';
11879
+ }
11880
+ if (hasIntegerPathSegment(value)) {
11881
+ return 'Must not contain numerical path segments.';
11882
+ }
11883
+ return null;
11884
+ };
11885
+
11603
11886
  function PaginationEntry(props) {
11604
11887
  const {
11605
11888
  editField,
@@ -11681,26 +11964,6 @@ function RowCount(props) {
11681
11964
  }
11682
11965
  editField(field, path$2, value);
11683
11966
  };
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
11967
  return NumberFieldEntry({
11705
11968
  debounce,
11706
11969
  label: 'Number of rows per page',
@@ -11708,10 +11971,32 @@ function RowCount(props) {
11708
11971
  id,
11709
11972
  getValue,
11710
11973
  setValue,
11711
- validate
11974
+ validate: validate$2
11712
11975
  });
11713
11976
  }
11714
11977
 
11978
+ // helpers //////////
11979
+
11980
+ /**
11981
+ * @param {string|void} value
11982
+ * @returns {string|null}
11983
+ */
11984
+ const validate$2 = value => {
11985
+ if (minDash.isNil(value)) {
11986
+ return null;
11987
+ }
11988
+ if (!minDash.isNumber(value)) {
11989
+ return 'Must be number';
11990
+ }
11991
+ if (!Number.isInteger(value)) {
11992
+ return 'Should be an integer.';
11993
+ }
11994
+ if (value < 1) {
11995
+ return 'Should be greater than zero.';
11996
+ }
11997
+ return null;
11998
+ };
11999
+
11715
12000
  const OPTIONS = {
11716
12001
  static: {
11717
12002
  label: 'List of items',
@@ -11850,17 +12135,6 @@ function ColumnsExpression(props) {
11850
12135
  }
11851
12136
  editField(field, PATH, value);
11852
12137
  };
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
12138
  const schema = '[\n {\n "key": "column_1",\n "label": "Column 1"\n }\n]';
11865
12139
  const tooltip = jsxRuntime.jsxs("div", {
11866
12140
  children: ["The expression may result in an array of simple values or alternatively follow this schema:", jsxRuntime.jsx("pre", {
@@ -11881,10 +12155,23 @@ function ColumnsExpression(props) {
11881
12155
  setValue,
11882
12156
  singleLine: true,
11883
12157
  variables,
11884
- validate
12158
+ validate: validate$1
11885
12159
  });
11886
12160
  }
11887
12161
 
12162
+ // helpers //////////
12163
+
12164
+ /**
12165
+ * @param {string|void} value
12166
+ * @returns {string|null}
12167
+ */
12168
+ const validate$1 = value => {
12169
+ if (!minDash.isString(value) || value.length === 0 || value === '=') {
12170
+ return 'Must not be empty.';
12171
+ }
12172
+ return null;
12173
+ };
12174
+
11888
12175
  const path$1 = 'columns';
11889
12176
  const labelPath = 'label';
11890
12177
  const keyPath = 'key';
@@ -11893,8 +12180,7 @@ function ColumnEntry(props) {
11893
12180
  editField,
11894
12181
  field,
11895
12182
  idPrefix,
11896
- index,
11897
- validateFactory
12183
+ index
11898
12184
  } = props;
11899
12185
  const entries = [{
11900
12186
  component: Label,
@@ -11902,16 +12188,14 @@ function ColumnEntry(props) {
11902
12188
  field,
11903
12189
  id: idPrefix + '-label',
11904
12190
  idPrefix,
11905
- index,
11906
- validateFactory
12191
+ index
11907
12192
  }, {
11908
12193
  component: Key,
11909
12194
  editField,
11910
12195
  field,
11911
12196
  id: idPrefix + '-key',
11912
12197
  idPrefix,
11913
- index,
11914
- validateFactory
12198
+ index
11915
12199
  }];
11916
12200
  return entries;
11917
12201
  }
@@ -12061,7 +12345,7 @@ function GeneralGroup(field, editField, getService) {
12061
12345
  field,
12062
12346
  editField,
12063
12347
  getService
12064
- }), ...DefaultOptionEntry({
12348
+ }), ...DefaultValueEntry({
12065
12349
  field,
12066
12350
  editField
12067
12351
  }), ...ActionEntry({
@@ -12074,6 +12358,10 @@ function GeneralGroup(field, editField, getService) {
12074
12358
  field,
12075
12359
  editField,
12076
12360
  getService
12361
+ }), ...HtmlEntry({
12362
+ field,
12363
+ editField,
12364
+ getService
12077
12365
  }), ...IFrameUrlEntry({
12078
12366
  field,
12079
12367
  editField
@@ -12086,7 +12374,7 @@ function GeneralGroup(field, editField, getService) {
12086
12374
  }), ...NumberEntries({
12087
12375
  field,
12088
12376
  editField
12089
- }), ...SourceEntry({
12377
+ }), ...ImageSourceEntry({
12090
12378
  field,
12091
12379
  editField
12092
12380
  }), ...AltTextEntry({
@@ -12496,7 +12784,7 @@ function CustomPropertiesGroup(field, editField) {
12496
12784
  if (value === key) {
12497
12785
  return;
12498
12786
  }
12499
- if (minDash.isUndefined(value) || !value.length) {
12787
+ if (typeof value !== 'string' || value.length === 0) {
12500
12788
  return 'Must not be empty.';
12501
12789
  }
12502
12790
  if (minDash.has(properties, value)) {
@@ -12600,6 +12888,75 @@ function LayoutGroup(field, editField) {
12600
12888
  };
12601
12889
  }
12602
12890
 
12891
+ function SecurityAttributesGroup(field, editField) {
12892
+ const {
12893
+ type
12894
+ } = field;
12895
+ if (type !== 'iframe') {
12896
+ return null;
12897
+ }
12898
+ const entries = createEntries({
12899
+ field,
12900
+ editField
12901
+ });
12902
+ if (!entries.length) {
12903
+ return null;
12904
+ }
12905
+ return {
12906
+ id: 'securityAttributes',
12907
+ label: 'Security attributes',
12908
+ entries,
12909
+ tooltip: getTooltip()
12910
+ };
12911
+ }
12912
+ function createEntries(props) {
12913
+ const {
12914
+ editField,
12915
+ field
12916
+ } = props;
12917
+ const securityEntries = formJsViewer.SECURITY_ATTRIBUTES_DEFINITIONS.map(definition => {
12918
+ const {
12919
+ label,
12920
+ property
12921
+ } = definition;
12922
+ return simpleBoolEntryFactory({
12923
+ id: property,
12924
+ label: label,
12925
+ isDefaultVisible: field => field.type === 'iframe',
12926
+ path: ['security', property],
12927
+ props,
12928
+ getValue: () => minDash.get(field, ['security', property]),
12929
+ setValue: value => {
12930
+ const security = minDash.get(field, ['security'], {});
12931
+ editField(field, ['security'], minDash.set(security, [property], value));
12932
+ }
12933
+ });
12934
+ });
12935
+ return [{
12936
+ component: Advisory
12937
+ }, ...securityEntries];
12938
+ }
12939
+ const Advisory = props => {
12940
+ return jsxRuntime.jsx("div", {
12941
+ class: "bio-properties-panel-description fjs-properties-panel-detached-description",
12942
+ children: "These options can incur security risks, especially if used in combination with dynamic links. Ensure that you are aware of them, that you trust the source url and only enable what your use case requires."
12943
+ });
12944
+ };
12945
+
12946
+ // helpers //////////
12947
+
12948
+ function getTooltip() {
12949
+ return jsxRuntime.jsx(jsxRuntime.Fragment, {
12950
+ children: jsxRuntime.jsxs("p", {
12951
+ children: ["Allow the iframe to access more functionality of your browser, details regarding the various options can be found in the ", jsxRuntime.jsx("a", {
12952
+ target: "_blank",
12953
+ href: "https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe",
12954
+ children: "MDN iFrame documentation."
12955
+ })]
12956
+ })
12957
+ });
12958
+ }
12959
+
12603
12960
  function ConditionGroup(field, editField) {
12604
12961
  const {
12605
12962
  type
@@ -12702,7 +13059,7 @@ class PropertiesProvider {
12702
13059
  return groups;
12703
13060
  }
12704
13061
  const getService = (type, strict = true) => this._injector.get(type, strict);
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);
13062
+ groups = [...groups, GeneralGroup(field, editField, getService), ...TableHeaderGroups(field, editField), SecurityAttributesGroup(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);
12706
13063
  this._filterVisibleEntries(groups, field, getService);
12707
13064
 
12708
13065
  // contract: if a group has no entries or items, it should not be displayed at all
@@ -12714,7 +13071,7 @@ class PropertiesProvider {
12714
13071
  }
12715
13072
  PropertiesProvider.$inject = ['propertiesPanel', 'injector'];
12716
13073
 
12717
- var PropertiesPanelModule = {
13074
+ const PropertiesPanelModule = {
12718
13075
  __depends__: [index],
12719
13076
  __init__: ['propertiesPanel', 'propertiesProvider'],
12720
13077
  propertiesPanel: ['type', PropertiesPanelRenderer],
@@ -12763,7 +13120,7 @@ class RenderInjector extends SectionModuleBase {
12763
13120
  }
12764
13121
  RenderInjector.$inject = ['eventBus'];
12765
13122
 
12766
- var RenderInjectionModule = {
13123
+ const RenderInjectionModule = {
12767
13124
  __init__: ['renderInjector'],
12768
13125
  renderInjector: ['type', RenderInjector]
12769
13126
  };
@@ -12783,7 +13140,7 @@ var SvgRepeat = function SvgRepeat(props) {
12783
13140
  };
12784
13141
  var RepeatSvg = SvgRepeat;
12785
13142
 
12786
- class RepeatRenderManager {
13143
+ class EditorRepeatRenderManager {
12787
13144
  constructor(formFields, formFieldRegistry) {
12788
13145
  this._formFields = formFields;
12789
13146
  this._formFieldRegistry = formFieldRegistry;
@@ -12813,30 +13170,11 @@ class RepeatRenderManager {
12813
13170
  });
12814
13171
  }
12815
13172
  }
12816
- RepeatRenderManager.$inject = ['formFields', 'formFieldRegistry'];
13173
+ EditorRepeatRenderManager.$inject = ['formFields', 'formFieldRegistry'];
12817
13174
 
12818
- var RepeatRenderManagerModule = {
13175
+ const RepeatRenderModule = {
12819
13176
  __init__: ['repeatRenderManager'],
12820
- repeatRenderManager: ['type', RepeatRenderManager]
12821
- };
12822
-
12823
- class EditorTemplating {
12824
- // same rules as viewer templating
12825
- isTemplate(value) {
12826
- return minDash.isString(value) && (value.startsWith('=') || /{{/.test(value));
12827
- }
12828
-
12829
- // return the template raw, as we usually just want to display that
12830
- evaluate(template) {
12831
- return template;
12832
- }
12833
- }
12834
- EditorTemplating.$inject = [];
12835
-
12836
- var ExpressionLanguageModule = {
12837
- __init__: ['expressionLanguage', 'templating'],
12838
- expressionLanguage: ['type', formJsViewer.FeelExpressionLanguage],
12839
- templating: ['type', EditorTemplating]
13177
+ repeatRenderManager: ['type', EditorRepeatRenderManager]
12840
13178
  };
12841
13179
 
12842
13180
  const ids = new Ids([32, 36, 1]);
@@ -13060,7 +13398,7 @@ class FormEditor {
13060
13398
  config: ['value', enrichedConfig]
13061
13399
  }, {
13062
13400
  formEditor: ['value', this]
13063
- }, core, ...modules, ...additionalModules]);
13401
+ }, CoreModule, ...modules, ...additionalModules]);
13064
13402
  }
13065
13403
 
13066
13404
  /**
@@ -13092,7 +13430,7 @@ class FormEditor {
13092
13430
  * @internal
13093
13431
  */
13094
13432
  _getModules() {
13095
- return [ModelingModule, EditorActionsModule, DraggingModule, KeyboardModule, SelectionModule, PaletteModule, ExpressionLanguageModule, formJsViewer.MarkdownModule, PropertiesPanelModule, RenderInjectionModule, RepeatRenderManagerModule];
13433
+ return [ModelingModule, EditorActionsModule, FormEditorKeyboardModule, DraggingModule, SelectionModule, PaletteModule, EditorExpressionLanguageModule, formJsViewer.MarkdownRendererModule, PropertiesPanelModule, RenderInjectionModule, RepeatRenderModule];
13096
13434
  }
13097
13435
 
13098
13436
  /**