@bpmn-io/form-js-editor 1.0.0-alpha.0 → 1.0.0-alpha.10

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 (42) hide show
  1. package/dist/assets/dragula.css +22 -0
  2. package/dist/assets/form-js-editor-base.css +25 -7
  3. package/dist/assets/form-js-editor.css +36 -12
  4. package/dist/assets/properties-panel.css +1115 -0
  5. package/dist/index.cjs +1234 -633
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.es.js +1252 -669
  8. package/dist/index.es.js.map +1 -1
  9. package/dist/types/FormEditor.d.ts +5 -2
  10. package/dist/types/features/SectionModuleBase.d.ts +40 -0
  11. package/dist/types/features/expression-language/EditorTemplating.d.ts +8 -0
  12. package/dist/types/features/expression-language/index.d.ts +2 -2
  13. package/dist/types/features/palette/PaletteModule.d.ts +8 -0
  14. package/dist/types/features/palette/components/Palette.d.ts +1 -1
  15. package/dist/types/features/palette/index.d.ts +2 -2
  16. package/dist/types/features/properties-panel/PropertiesPanel.d.ts +1 -1
  17. package/dist/types/features/properties-panel/PropertiesPanelHeaderProvider.d.ts +1 -1
  18. package/dist/types/features/properties-panel/components/AutoFocusSelect.d.ts +1 -0
  19. package/dist/types/features/properties-panel/components/index.d.ts +1 -0
  20. package/dist/types/features/properties-panel/entries/InputKeyValuesSourceEntry.d.ts +1 -1
  21. package/dist/types/features/properties-panel/entries/LabelEntry.d.ts +5 -5
  22. package/dist/types/features/properties-panel/entries/ValuesExpressionEntry.d.ts +10 -0
  23. package/dist/types/features/properties-panel/entries/ValuesSourceSelectEntry.d.ts +1 -1
  24. package/dist/types/features/properties-panel/entries/index.d.ts +1 -0
  25. package/dist/types/features/properties-panel/hooks/index.d.ts +1 -1
  26. package/dist/types/features/properties-panel/index.d.ts +2 -2
  27. package/dist/types/features/render-injection/RenderInjector.d.ts +30 -0
  28. package/dist/types/features/render-injection/components/InjectedRendersRoot.d.ts +2 -0
  29. package/dist/types/features/render-injection/index.d.ts +6 -0
  30. package/dist/types/features/render-injection/slot-fill/Fill.d.ts +2 -0
  31. package/dist/types/features/render-injection/slot-fill/FillContext.d.ts +5 -0
  32. package/dist/types/features/render-injection/slot-fill/Slot.d.ts +8 -0
  33. package/dist/types/features/render-injection/slot-fill/SlotContext.d.ts +4 -0
  34. package/dist/types/features/render-injection/slot-fill/SlotFillRoot.d.ts +2 -0
  35. package/dist/types/features/render-injection/slot-fill/index.d.ts +5 -0
  36. package/dist/types/render/components/FieldDragPreview.d.ts +1 -1
  37. package/dist/types/render/components/FieldResizer.d.ts +1 -1
  38. package/dist/types/render/components/FormEditor.d.ts +1 -1
  39. package/dist/types/render/components/ModularSection.d.ts +2 -0
  40. package/dist/types/render/components/editor-form-fields/EditorText.d.ts +1 -1
  41. package/package.json +6 -4
  42. package/dist/types/features/palette/PaletteRenderer.d.ts +0 -33
package/dist/index.cjs CHANGED
@@ -15,6 +15,25 @@ var feelers = require('feelers');
15
15
  var FeelEditor = require('@bpmn-io/feel-editor');
16
16
  var Big = require('big.js');
17
17
 
18
+ function _interopNamespaceDefault(e) {
19
+ var n = Object.create(null);
20
+ if (e) {
21
+ Object.keys(e).forEach(function (k) {
22
+ if (k !== 'default') {
23
+ var d = Object.getOwnPropertyDescriptor(e, k);
24
+ Object.defineProperty(n, k, d.get ? d : {
25
+ enumerable: true,
26
+ get: function () { return e[k]; }
27
+ });
28
+ }
29
+ });
30
+ }
31
+ n.default = e;
32
+ return Object.freeze(n);
33
+ }
34
+
35
+ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
36
+
18
37
  var FN_REF = '__fn';
19
38
  var DEFAULT_PRIORITY$2 = 1000;
20
39
  var slice = Array.prototype.slice;
@@ -934,83 +953,91 @@ function useService$1 (type, strict) {
934
953
  return getService(type, strict);
935
954
  }
936
955
 
956
+ var _path$3;
937
957
  function _extends$3() { _extends$3 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$3.apply(this, arguments); }
938
- var CloseIcon = (({
939
- styles = {},
940
- ...props
941
- }) => /*#__PURE__*/React.createElement("svg", _extends$3({
942
- width: "16",
943
- height: "16",
944
- fill: "currentColor",
945
- xmlns: "http://www.w3.org/2000/svg"
946
- }, props), /*#__PURE__*/React.createElement("path", {
947
- fillRule: "evenodd",
948
- clipRule: "evenodd",
949
- d: "M12 4.7l-.7-.7L8 7.3 4.7 4l-.7.7L7.3 8 4 11.3l.7.7L8 8.7l3.3 3.3.7-.7L8.7 8 12 4.7z"
950
- })));
958
+ var SvgClose = function SvgClose(props) {
959
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$3({
960
+ xmlns: "http://www.w3.org/2000/svg",
961
+ width: 16,
962
+ height: 16,
963
+ fill: "currentColor"
964
+ }, props), _path$3 || (_path$3 = /*#__PURE__*/React__namespace.createElement("path", {
965
+ fillRule: "evenodd",
966
+ d: "m12 4.7-.7-.7L8 7.3 4.7 4l-.7.7L7.3 8 4 11.3l.7.7L8 8.7l3.3 3.3.7-.7L8.7 8 12 4.7Z",
967
+ clipRule: "evenodd"
968
+ })));
969
+ };
970
+ var CloseIcon = SvgClose;
951
971
 
972
+ var _path$2, _path2;
952
973
  function _extends$2() { _extends$2 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$2.apply(this, arguments); }
953
- var DeleteIcon$1 = (({
954
- styles = {},
955
- ...props
956
- }) => /*#__PURE__*/React.createElement("svg", _extends$2({
957
- xmlns: "http://www.w3.org/2000/svg",
958
- width: "16",
959
- height: "16",
960
- fill: "none"
961
- }, props), /*#__PURE__*/React.createElement("rect", {
962
- width: "16",
963
- height: "16",
964
- x: ".536",
965
- fill: "#fff",
966
- rx: "3",
967
- style: {
968
- mixBlendMode: "multiply"
969
- }
970
- }), /*#__PURE__*/React.createElement("path", {
971
- fill: "#fff",
972
- d: "M.536 0h16v16h-16z",
973
- style: {
974
- mixBlendMode: "multiply"
975
- }
976
- }), /*#__PURE__*/React.createElement("path", {
977
- fill: "currentcolor",
978
- d: "M7.536 6h-1v6h1V6zm3 0h-1v6h1V6z"
979
- }), /*#__PURE__*/React.createElement("path", {
980
- fill: "currentcolor",
981
- d: "M2.536 3v1h1v10a1 1 0 001 1h8a1 1 0 001-1V4h1V3h-12zm2 11V4h8v10h-8zm6-13h-4v1h4V1z"
982
- })));
974
+ var SvgDelete = function SvgDelete(props) {
975
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$2({
976
+ xmlns: "http://www.w3.org/2000/svg",
977
+ width: 16,
978
+ height: 16,
979
+ fill: "none"
980
+ }, props), /*#__PURE__*/React__namespace.createElement("rect", {
981
+ width: 16,
982
+ height: 16,
983
+ x: 0.536,
984
+ fill: "#fff",
985
+ rx: 3,
986
+ style: {
987
+ mixBlendMode: "multiply"
988
+ }
989
+ }), /*#__PURE__*/React__namespace.createElement("path", {
990
+ fill: "#fff",
991
+ d: "M0 0h16v16H0z",
992
+ style: {
993
+ mixBlendMode: "multiply"
994
+ },
995
+ transform: "translate(.536)"
996
+ }), _path$2 || (_path$2 = /*#__PURE__*/React__namespace.createElement("path", {
997
+ fill: "currentcolor",
998
+ d: "M7.536 6h-1v6h1V6Zm3 0h-1v6h1V6Z"
999
+ })), _path2 || (_path2 = /*#__PURE__*/React__namespace.createElement("path", {
1000
+ fill: "currentcolor",
1001
+ d: "M2.536 3v1h1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4h1V3h-12Zm2 11V4h8v10h-8Zm6-13h-4v1h4V1Z"
1002
+ })));
1003
+ };
1004
+ var DeleteIcon$1 = SvgDelete;
983
1005
 
1006
+ var _path$1;
984
1007
  function _extends$1() { _extends$1 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$1.apply(this, arguments); }
985
- var DraggableIcon = (({
986
- styles = {},
987
- ...props
988
- }) => /*#__PURE__*/React.createElement("svg", _extends$1({
989
- xmlns: "http://www.w3.org/2000/svg",
990
- width: "16",
991
- height: "16",
992
- fill: "currentcolor",
993
- viewBox: "0 0 32 32"
994
- }, props), /*#__PURE__*/React.createElement("path", {
995
- d: "M10 6h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4z"
996
- }), /*#__PURE__*/React.createElement("path", {
997
- d: "M0 0h32v32H0z",
998
- fill: "none"
999
- })));
1008
+ var SvgDraggable = function SvgDraggable(props) {
1009
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends$1({
1010
+ xmlns: "http://www.w3.org/2000/svg",
1011
+ xmlSpace: "preserve",
1012
+ width: 16,
1013
+ height: 16,
1014
+ fill: "currentcolor",
1015
+ viewBox: "0 0 32 32"
1016
+ }, props), _path$1 || (_path$1 = /*#__PURE__*/React__namespace.createElement("path", {
1017
+ d: "M10 6h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4z"
1018
+ })), /*#__PURE__*/React__namespace.createElement("path", {
1019
+ d: "M0 0h32v32H0z",
1020
+ style: {
1021
+ fill: "none"
1022
+ }
1023
+ }));
1024
+ };
1025
+ var DraggableIcon = SvgDraggable;
1000
1026
 
1027
+ var _path;
1001
1028
  function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
1002
- var SearchIcon = (({
1003
- styles = {},
1004
- ...props
1005
- }) => /*#__PURE__*/React.createElement("svg", _extends({
1006
- width: "15",
1007
- height: "15",
1008
- fill: "none",
1009
- xmlns: "http://www.w3.org/2000/svg"
1010
- }, props), /*#__PURE__*/React.createElement("path", {
1011
- d: "M14.5 13.793l-3.776-3.776a5.508 5.508 0 10-.707.707l3.776 3.776.707-.707zM2 6.5a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0z",
1012
- fill: "currentColor"
1013
- })));
1029
+ var SvgSearch = function SvgSearch(props) {
1030
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends({
1031
+ xmlns: "http://www.w3.org/2000/svg",
1032
+ width: 15,
1033
+ height: 15,
1034
+ fill: "none"
1035
+ }, props), _path || (_path = /*#__PURE__*/React__namespace.createElement("path", {
1036
+ fill: "currentColor",
1037
+ d: "m14.5 13.793-3.776-3.776a5.508 5.508 0 1 0-.707.707l3.776 3.776.707-.707ZM2 6.5a4.5 4.5 0 1 1 9 0 4.5 4.5 0 0 1-9 0Z"
1038
+ })));
1039
+ };
1040
+ var SearchIcon = SvgSearch;
1014
1041
 
1015
1042
  function EditorText(props) {
1016
1043
  const {
@@ -1071,6 +1098,228 @@ class EditorFormFields extends formJsViewer.FormFields {
1071
1098
  }
1072
1099
  }
1073
1100
 
1101
+ var ModularSection = (props => {
1102
+ const {
1103
+ rootClass,
1104
+ RootElement,
1105
+ section,
1106
+ children
1107
+ } = props;
1108
+ const eventBus = useService$1('eventBus');
1109
+ const sectionConfig = useService$1(`config.${section}`);
1110
+ const [parent, setParent] = hooks.useState(sectionConfig && sectionConfig.parent || null);
1111
+ const [shouldRender, setShouldRender] = hooks.useState(true);
1112
+ const ParentElement = hooks.useMemo(() => {
1113
+ if (parent === null) {
1114
+ return null;
1115
+ }
1116
+ if (typeof parent === 'string') {
1117
+ const element = document.querySelector(parent);
1118
+ if (!element) {
1119
+ throw new Error(`Target root element with selector '${parent}' not found for section '${section}'`);
1120
+ }
1121
+ return document.querySelector(parent);
1122
+ }
1123
+
1124
+ // @ts-ignore
1125
+ if (!(parent instanceof Element)) {
1126
+ throw new Error(`Target root element for section '${section}' must be a valid selector or DOM element`);
1127
+ }
1128
+ return parent;
1129
+ }, [section, parent]);
1130
+ hooks.useEffect(() => {
1131
+ const onAttach = ({
1132
+ container
1133
+ }) => {
1134
+ setParent(container);
1135
+ setShouldRender(true);
1136
+ };
1137
+ const onDetach = () => {
1138
+ setParent(null);
1139
+ setShouldRender(false);
1140
+ };
1141
+ const onReset = () => {
1142
+ setParent(null);
1143
+ setShouldRender(true);
1144
+ };
1145
+ eventBus.on(`${section}.attach`, onAttach);
1146
+ eventBus.on(`${section}.detach`, onDetach);
1147
+ eventBus.on(`${section}.reset`, onReset);
1148
+ eventBus.fire(`${section}.section.rendered`);
1149
+ return () => {
1150
+ eventBus.off(`${section}.attach`, onAttach);
1151
+ eventBus.off(`${section}.detach`, onDetach);
1152
+ eventBus.off(`${section}.reset`, onReset);
1153
+ eventBus.fire(`${section}.section.destroyed`);
1154
+ };
1155
+ }, [eventBus, section]);
1156
+ hooks.useEffect(() => {
1157
+ if (shouldRender) {
1158
+ eventBus.fire(`${section}.rendered`, {
1159
+ element: ParentElement
1160
+ });
1161
+ return () => {
1162
+ eventBus.fire(`${section}.destroyed`, {
1163
+ element: ParentElement
1164
+ });
1165
+ };
1166
+ }
1167
+ }, [eventBus, section, shouldRender, ParentElement]);
1168
+ const Root = hooks.useCallback(({
1169
+ children
1170
+ }) => RootElement ? jsxRuntime.jsx(RootElement, {
1171
+ children: children
1172
+ }) : jsxRuntime.jsx("div", {
1173
+ className: rootClass,
1174
+ children: children
1175
+ }), [rootClass, RootElement]);
1176
+ return shouldRender ? parent ? React.createPortal(jsxRuntime.jsx(Root, {
1177
+ children: children
1178
+ }), ParentElement) : jsxRuntime.jsx(Root, {
1179
+ children: children
1180
+ }) : null;
1181
+ });
1182
+
1183
+ const FillContext = preact.createContext({
1184
+ addFill(uid, props) {
1185
+ throw new Error('FillContext.addFill() uninitialized');
1186
+ },
1187
+ removeFill(uid) {
1188
+ throw new Error('FillContext.addFill() uninitialized');
1189
+ }
1190
+ });
1191
+ var FillContext$1 = FillContext;
1192
+
1193
+ var Fill = (props => {
1194
+ const uid = React.useRef(Symbol('fill_uid'));
1195
+ const fillContext = React.useContext(FillContext$1);
1196
+ React.useEffect(() => {
1197
+ if (!fillContext) {
1198
+ return;
1199
+ }
1200
+ fillContext.addFill({
1201
+ id: uid,
1202
+ ...props
1203
+ });
1204
+ return () => {
1205
+ fillContext.removeFill(uid);
1206
+ };
1207
+ }, [fillContext, props]);
1208
+ return null;
1209
+ });
1210
+
1211
+ const SlotContext = preact.createContext({
1212
+ fills: []
1213
+ });
1214
+ var SlotContext$1 = SlotContext;
1215
+
1216
+ var Slot = (props => {
1217
+ const {
1218
+ name,
1219
+ fillRoot = FillFragment,
1220
+ groupFn = _groupByGroupName,
1221
+ separatorFn = key => null,
1222
+ limit
1223
+ } = props;
1224
+ const {
1225
+ fills
1226
+ } = hooks.useContext(SlotContext$1);
1227
+ const filtered = hooks.useMemo(() => fills.filter(fill => fill.slot === name), [fills, name]);
1228
+ const cropped = hooks.useMemo(() => limit ? filtered.slice(0, limit) : filtered, [filtered, limit]);
1229
+ const groups = hooks.useMemo(() => groupFn(cropped), [cropped, groupFn]);
1230
+ const fillsAndSeparators = hooks.useMemo(() => {
1231
+ return buildFills(groups, fillRoot, separatorFn);
1232
+ }, [groups, fillRoot, separatorFn]);
1233
+ return fillsAndSeparators;
1234
+ });
1235
+
1236
+ /**
1237
+ * Creates a Fragment for a fill.
1238
+ *
1239
+ * @param {Object} fill Fill to be rendered
1240
+ * @returns {Object} Preact Fragment containing fill's children
1241
+ */
1242
+ const FillFragment = fill => jsxRuntime.jsx(preact.Fragment, {
1243
+ children: fill.children
1244
+ }, fill.id);
1245
+
1246
+ /**
1247
+ * Creates an array of fills, with separators inserted between groups.
1248
+ *
1249
+ * @param {Array} groups Groups of fills
1250
+ * @param {Function} fillRenderer Function to create a fill
1251
+ * @param {Function} separatorRenderer Function to create a separator
1252
+ * @returns {Array} Array of fills and separators
1253
+ */
1254
+ const buildFills = (groups, fillRenderer, separatorRenderer) => {
1255
+ const result = [];
1256
+ groups.forEach((array, idx) => {
1257
+ if (idx !== 0) {
1258
+ const separator = separatorRenderer(`__separator_${idx}`);
1259
+ if (separator) {
1260
+ result.push(separator);
1261
+ }
1262
+ }
1263
+ array.forEach(fill => {
1264
+ result.push(fillRenderer(fill));
1265
+ });
1266
+ });
1267
+ return result;
1268
+ };
1269
+
1270
+ /**
1271
+ * Groups fills by group name property.
1272
+ */
1273
+ const _groupByGroupName = fills => {
1274
+ const groups = [];
1275
+ const groupsById = {};
1276
+ fills.forEach(function (fill) {
1277
+ const {
1278
+ group: groupName = 'z_default'
1279
+ } = fill;
1280
+ let group = groupsById[groupName];
1281
+ if (!group) {
1282
+ groupsById[groupName] = group = [];
1283
+ groups.push(group);
1284
+ }
1285
+ group.push(fill);
1286
+ });
1287
+ groups.forEach(group => group.sort(_comparePriority));
1288
+ return Object.keys(groupsById).sort().map(id => groupsById[id]);
1289
+ };
1290
+
1291
+ /**
1292
+ * Compares fills by priority.
1293
+ */
1294
+ const _comparePriority = (a, b) => {
1295
+ return (b.priority || 0) - (a.priority || 0);
1296
+ };
1297
+
1298
+ var SlotFillRoot = (props => {
1299
+ const [fills, setFills] = hooks.useState([]);
1300
+ const fillContext = hooks.useMemo(() => ({
1301
+ addFill: fill => {
1302
+ setFills(fills => [...fills.filter(f => f.id !== fill.id), fill]);
1303
+ props.onSetFill && props.onSetFill(fill);
1304
+ },
1305
+ removeFill: id => {
1306
+ setFills(fills => fills.filter(f => f.id !== id));
1307
+ props.onRemoveFill && props.onRemoveFill(id);
1308
+ }
1309
+ }), []);
1310
+ const slotContext = hooks.useMemo(() => ({
1311
+ fills
1312
+ }), [fills]);
1313
+ return jsxRuntime.jsx(SlotContext$1.Provider, {
1314
+ value: slotContext,
1315
+ children: jsxRuntime.jsx(FillContext$1.Provider, {
1316
+ /* @ts-expect-error */
1317
+ value: fillContext,
1318
+ children: props.children
1319
+ })
1320
+ });
1321
+ });
1322
+
1074
1323
  const PALETTE_ENTRIES = formJsViewer.formFields.filter(({
1075
1324
  config: fieldConfig
1076
1325
  }) => fieldConfig.type !== 'default').map(({
@@ -1188,9 +1437,19 @@ function Palette(props) {
1188
1437
  class: "fjs-palette-no-entries",
1189
1438
  children: "No components found."
1190
1439
  })]
1440
+ }), jsxRuntime.jsx("div", {
1441
+ class: "fjs-palette-footer",
1442
+ children: jsxRuntime.jsx(Slot, {
1443
+ name: "editor-palette__footer",
1444
+ fillRoot: FillRoot
1445
+ })
1191
1446
  })]
1192
1447
  });
1193
1448
  }
1449
+ const FillRoot = fill => jsxRuntime.jsx("div", {
1450
+ className: "fjs-palette-footer-fill",
1451
+ children: fill.children
1452
+ });
1194
1453
 
1195
1454
  // helpers ///////
1196
1455
 
@@ -1217,6 +1476,25 @@ function getIndefiniteArticle(type) {
1217
1476
  return 'a';
1218
1477
  }
1219
1478
 
1479
+ var InjectedRendersRoot = (() => {
1480
+ const renderInjector = useService$1('renderInjector');
1481
+ const injectedRenderers = renderInjector.fetchRenderers();
1482
+ const injectedProps = hooks.useMemo(() => ({
1483
+ useService: useService$1,
1484
+ components: {
1485
+ Fill,
1486
+ Slot
1487
+ }
1488
+ }), []);
1489
+ return jsxRuntime.jsx(preact.Fragment, {
1490
+ children: injectedRenderers.map(({
1491
+ Renderer
1492
+ }) => jsxRuntime.jsx(Renderer, {
1493
+ ...injectedProps
1494
+ }))
1495
+ });
1496
+ });
1497
+
1220
1498
  const CURSOR_CLS_PATTERN = /^fjs-cursor-.*$/;
1221
1499
  function set(mode) {
1222
1500
  const classes = minDom.classes(document.body);
@@ -1672,7 +1950,7 @@ function ContextPad(props) {
1672
1950
  function Empty(props) {
1673
1951
  return null;
1674
1952
  }
1675
- function Element(props) {
1953
+ function Element$1(props) {
1676
1954
  const eventBus = useService$1('eventBus'),
1677
1955
  formEditor = useService$1('formEditor'),
1678
1956
  formFieldRegistry = useService$1('formFieldRegistry'),
@@ -1835,8 +2113,6 @@ function FormEditor$1(props) {
1835
2113
  formEditor = useService$1('formEditor'),
1836
2114
  injector = useService$1('injector'),
1837
2115
  selection = useService$1('selection'),
1838
- palette = useService$1('palette'),
1839
- paletteConfig = useService$1('config.palette'),
1840
2116
  propertiesPanel = useService$1('propertiesPanel'),
1841
2117
  propertiesPanelConfig = useService$1('config.propertiesPanel');
1842
2118
  const {
@@ -1847,7 +2123,6 @@ function FormEditor$1(props) {
1847
2123
  ariaLabel
1848
2124
  } = properties;
1849
2125
  const formContainerRef = hooks.useRef(null);
1850
- const paletteRef = hooks.useRef(null);
1851
2126
  const propertiesPanelRef = hooks.useRef(null);
1852
2127
  const [, setSelection] = hooks.useState(schema);
1853
2128
  hooks.useEffect(() => {
@@ -1908,7 +2183,7 @@ function FormEditor$1(props) {
1908
2183
  eventBus.off('drag.start', onDragStart);
1909
2184
  eventBus.off('drag.end', onDragEnd);
1910
2185
  };
1911
- }, []);
2186
+ }, [dragging, eventBus]);
1912
2187
 
1913
2188
  // fire event after render to notify interested parties
1914
2189
  hooks.useEffect(() => {
@@ -1917,7 +2192,7 @@ function FormEditor$1(props) {
1917
2192
  const formRenderContext = {
1918
2193
  Children,
1919
2194
  Column,
1920
- Element,
2195
+ Element: Element$1,
1921
2196
  Empty,
1922
2197
  Row
1923
2198
  };
@@ -1946,14 +2221,6 @@ function FormEditor$1(props) {
1946
2221
  const onSubmit = hooks.useCallback(() => {}, []);
1947
2222
  const onReset = hooks.useCallback(() => {}, []);
1948
2223
 
1949
- // attach default palette
1950
- const hasDefaultPalette = defaultPalette(paletteConfig);
1951
- hooks.useEffect(() => {
1952
- if (hasDefaultPalette) {
1953
- palette.attachTo(paletteRef.current);
1954
- }
1955
- }, [palette, paletteRef, hasDefaultPalette]);
1956
-
1957
2224
  // attach default properties panel
1958
2225
  const hasDefaultPropertiesPanel = defaultPropertiesPanel(propertiesPanelConfig);
1959
2226
  hooks.useEffect(() => {
@@ -1961,31 +2228,38 @@ function FormEditor$1(props) {
1961
2228
  propertiesPanel.attachTo(propertiesPanelRef.current);
1962
2229
  }
1963
2230
  }, [propertiesPanelRef, propertiesPanel, hasDefaultPropertiesPanel]);
1964
- return jsxRuntime.jsxs("div", {
2231
+ return jsxRuntime.jsx("div", {
1965
2232
  class: "fjs-form-editor",
1966
- children: [jsxRuntime.jsxs(DragAndDropContext$1.Provider, {
1967
- value: dragAndDropContext,
1968
- children: [hasDefaultPalette && jsxRuntime.jsx("div", {
1969
- class: "fjs-editor-palette-container",
1970
- ref: paletteRef
1971
- }), jsxRuntime.jsx("div", {
1972
- ref: formContainerRef,
1973
- class: "fjs-form-container",
1974
- children: jsxRuntime.jsx(formJsViewer.FormContext.Provider, {
1975
- value: formContext,
1976
- children: jsxRuntime.jsx(formJsViewer.FormRenderContext.Provider, {
1977
- value: formRenderContext,
1978
- children: jsxRuntime.jsx(formJsViewer.FormComponent, {
1979
- onSubmit: onSubmit,
1980
- onReset: onReset
2233
+ children: jsxRuntime.jsxs(SlotFillRoot, {
2234
+ children: [jsxRuntime.jsxs(DragAndDropContext$1.Provider, {
2235
+ value: dragAndDropContext,
2236
+ children: [jsxRuntime.jsx(ModularSection, {
2237
+ rootClass: "fjs-palette-container",
2238
+ section: "palette",
2239
+ children: jsxRuntime.jsx(Palette, {})
2240
+ }), jsxRuntime.jsx("div", {
2241
+ ref: formContainerRef,
2242
+ class: "fjs-form-container",
2243
+ children: jsxRuntime.jsx(formJsViewer.FormContext.Provider, {
2244
+ value: formContext,
2245
+ children: jsxRuntime.jsx(formJsViewer.FormRenderContext.Provider, {
2246
+ value: formRenderContext,
2247
+ children: jsxRuntime.jsx(formJsViewer.FormComponent, {
2248
+ onSubmit: onSubmit,
2249
+ onReset: onReset
2250
+ })
1981
2251
  })
1982
2252
  })
1983
- })
1984
- }), jsxRuntime.jsx(CreatePreview, {})]
1985
- }), hasDefaultPropertiesPanel && jsxRuntime.jsx("div", {
1986
- class: "fjs-editor-properties-container",
1987
- ref: propertiesPanelRef
1988
- })]
2253
+ }), jsxRuntime.jsx(CreatePreview, {})]
2254
+ }), hasDefaultPropertiesPanel && jsxRuntime.jsx("div", {
2255
+ class: "fjs-editor-properties-container",
2256
+ ref: propertiesPanelRef
2257
+ }), jsxRuntime.jsx(ModularSection, {
2258
+ rootClass: "fjs-render-injector-container",
2259
+ section: "renderInjector",
2260
+ children: jsxRuntime.jsx(InjectedRendersRoot, {})
2261
+ })]
2262
+ })
1989
2263
  });
1990
2264
  }
1991
2265
  function getFormFieldIndex(parent, formField) {
@@ -2052,15 +2326,12 @@ function CreatePreview(props) {
2052
2326
 
2053
2327
  // helper //////
2054
2328
 
2055
- function defaultPalette(paletteConfig) {
2056
- return !(paletteConfig && paletteConfig.parent);
2329
+ function findPaletteEntry(type) {
2330
+ return PALETTE_ENTRIES.find(entry => entry.type === type);
2057
2331
  }
2058
2332
  function defaultPropertiesPanel(propertiesPanelConfig) {
2059
2333
  return !(propertiesPanelConfig && propertiesPanelConfig.parent);
2060
2334
  }
2061
- function findPaletteEntry(type) {
2062
- return PALETTE_ENTRIES.find(entry => entry.type === type);
2063
- }
2064
2335
 
2065
2336
  class Renderer {
2066
2337
  constructor(renderConfig, eventBus, formEditor, injector) {
@@ -4202,69 +4473,80 @@ var SelectionModule = {
4202
4473
  selectionBehavior: ['type', SelectionBehavior]
4203
4474
  };
4204
4475
 
4205
- class PaletteRenderer {
4206
- constructor(paletteConfig, eventBus) {
4207
- const {
4208
- parent
4209
- } = paletteConfig || {};
4476
+ /**
4477
+ * Base class for sectionable UI modules.
4478
+ *
4479
+ * @property {EventBus} _eventBus - EventBus instance used for event handling.
4480
+ * @property {string} managerType - Type of the render manager. Used to form event names.
4481
+ *
4482
+ * @class SectionModuleBase
4483
+ */
4484
+ class SectionModuleBase {
4485
+ /**
4486
+ * Create a SectionModuleBase instance.
4487
+ *
4488
+ * @param {any} eventBus - The EventBus instance used for event handling.
4489
+ * @param {string} sectionKey - The type of render manager. Used to form event names.
4490
+ *
4491
+ * @constructor
4492
+ */
4493
+ constructor(eventBus, sectionKey) {
4210
4494
  this._eventBus = eventBus;
4211
- this._container = minDom.domify('<div class="fjs-palette-container"></div>');
4212
- if (parent) {
4213
- this.attachTo(parent);
4214
- }
4215
- this._eventBus.once('formEditor.rendered', 500, () => {
4216
- this._render();
4495
+ this._sectionKey = sectionKey;
4496
+ this._eventBus.on(`${this._sectionKey}.section.rendered`, () => {
4497
+ this.isSectionRendered = true;
4498
+ });
4499
+ this._eventBus.on(`${this._sectionKey}.section.destroyed`, () => {
4500
+ this.isSectionRendered = false;
4217
4501
  });
4218
4502
  }
4219
4503
 
4220
4504
  /**
4221
- * Attach the palette to a parent node.
4505
+ * Attach the managed section to a parent node.
4222
4506
  *
4223
- * @param {HTMLElement} container
4507
+ * @param {HTMLElement} container - The parent node to attach to.
4224
4508
  */
4225
4509
  attachTo(container) {
4226
- if (!container) {
4227
- throw new Error('container required');
4228
- }
4229
- if (typeof container === 'string') {
4230
- container = minDom.query(container);
4231
- }
4232
-
4233
- // (1) detach from old parent
4234
- this.detach();
4235
-
4236
- // (2) append to parent container
4237
- container.appendChild(this._container);
4238
-
4239
- // (3) notify interested parties
4240
- this._eventBus.fire('palette.attach');
4510
+ this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.attach`, {
4511
+ container
4512
+ }));
4241
4513
  }
4242
4514
 
4243
4515
  /**
4244
- * Detach the palette from its parent node.
4516
+ * Detach the managed section from its parent node.
4245
4517
  */
4246
4518
  detach() {
4247
- const parentNode = this._container.parentNode;
4248
- if (parentNode) {
4249
- parentNode.removeChild(this._container);
4250
- this._eventBus.fire('palette.detach');
4251
- }
4519
+ this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.detach`));
4252
4520
  }
4253
- _render() {
4254
- preact.render(jsxRuntime.jsx(Palette, {}), this._container);
4255
- this._eventBus.fire('palette.rendered');
4521
+
4522
+ /**
4523
+ * Reset the managed section to its initial state.
4524
+ */
4525
+ reset() {
4526
+ this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.reset`));
4256
4527
  }
4257
- _destroy() {
4258
- if (this._container) {
4259
- preact.render(null, this._container);
4260
- this._eventBus.fire('palette.destroyed');
4528
+
4529
+ /**
4530
+ * Circumvents timing issues.
4531
+ */
4532
+ _onceSectionRendered(callback) {
4533
+ if (this.isSectionRendered) {
4534
+ callback();
4535
+ } else {
4536
+ this._eventBus.once(`${this._sectionKey}.section.rendered`, callback);
4261
4537
  }
4262
4538
  }
4263
4539
  }
4264
- PaletteRenderer.$inject = ['config.palette', 'eventBus'];
4540
+
4541
+ let PaletteModule$1 = class PaletteModule extends SectionModuleBase {
4542
+ constructor(eventBus) {
4543
+ super(eventBus, 'palette');
4544
+ }
4545
+ };
4546
+ PaletteModule$1.$inject = ['eventBus'];
4265
4547
 
4266
4548
  var PaletteModule = {
4267
- palette: ['type', PaletteRenderer]
4549
+ palette: ['type', PaletteModule$1]
4268
4550
  };
4269
4551
 
4270
4552
  var ArrowIcon = function ArrowIcon(props) {
@@ -5728,77 +6010,236 @@ function isEdited$7(node) {
5728
6010
  function prefixId$6(id) {
5729
6011
  return `bio-properties-panel-${id}`;
5730
6012
  }
5731
- const noop$1 = () => {};
5732
- function FeelTextfield(props) {
6013
+ function NumberField(props) {
5733
6014
  const {
5734
6015
  debounce,
6016
+ disabled,
6017
+ displayLabel = true,
5735
6018
  id,
6019
+ inputRef,
5736
6020
  label,
6021
+ max,
6022
+ min,
5737
6023
  onInput,
5738
- onError,
5739
- feel,
6024
+ step,
5740
6025
  value = '',
5741
- disabled = false,
5742
- variables,
5743
- tooltipContainer,
5744
- OptionalComponent = OptionalFeelInput
6026
+ onFocus,
6027
+ onBlur
5745
6028
  } = props;
5746
- const [localValue, _setLocalValue] = hooks.useState(value);
5747
- const editorRef = useShowEntryEvent(id);
5748
- const containerRef = hooks.useRef();
5749
- const feelActive = minDash.isString(localValue) && localValue.startsWith('=') || feel === 'required';
5750
- const feelOnlyValue = minDash.isString(localValue) && localValue.startsWith('=') ? localValue.substring(1) : localValue;
5751
- const [focus, _setFocus] = hooks.useState(undefined);
5752
- const setFocus = (offset = 0) => {
5753
- const hasFocus = containerRef.current.contains(document.activeElement);
5754
-
5755
- // Keep caret position if it is already focused, otherwise focus at the end
5756
- const position = hasFocus ? document.activeElement.selectionStart : Infinity;
5757
- _setFocus(position + offset);
5758
- };
6029
+ const [localValue, setLocalValue] = hooks.useState(value);
5759
6030
  const handleInputCallback = hooks.useMemo(() => {
5760
- return debounce(newValue => {
5761
- onInput(newValue);
6031
+ return debounce(event => {
6032
+ const {
6033
+ validity,
6034
+ value
6035
+ } = event.target;
6036
+ if (validity.valid) {
6037
+ onInput(value ? parseFloat(value) : undefined);
6038
+ }
5762
6039
  });
5763
6040
  }, [onInput, debounce]);
5764
- const setLocalValue = newValue => {
5765
- _setLocalValue(newValue);
5766
- if (!newValue || newValue === '=') {
5767
- handleInputCallback(undefined);
5768
- } else {
5769
- handleInputCallback(newValue);
5770
- }
5771
- };
5772
- const handleFeelToggle = useStaticCallback(() => {
5773
- if (feel === 'required') {
5774
- return;
5775
- }
5776
- if (!feelActive) {
5777
- setLocalValue('=' + localValue);
5778
- } else {
5779
- setLocalValue(feelOnlyValue);
5780
- }
5781
- });
5782
- const handleLocalInput = newValue => {
5783
- if (feelActive) {
5784
- newValue = '=' + newValue;
5785
- }
5786
- if (newValue === localValue) {
5787
- return;
5788
- }
5789
- setLocalValue(newValue);
5790
- if (!feelActive && minDash.isString(newValue) && newValue.startsWith('=')) {
5791
- // focus is behind `=` sign that will be removed
5792
- setFocus(-1);
5793
- }
6041
+ const handleInput = e => {
6042
+ handleInputCallback(e);
6043
+ setLocalValue(e.target.value);
5794
6044
  };
5795
- const handleLint = useStaticCallback(lint => {
5796
- if (!(lint && lint.length)) {
5797
- onError(undefined);
6045
+ hooks.useEffect(() => {
6046
+ if (value === localValue) {
5798
6047
  return;
5799
6048
  }
5800
- const error = lint[0];
5801
- const message = `${error.source}: ${error.message}`;
6049
+ setLocalValue(value);
6050
+ }, [value]);
6051
+ return jsxRuntime.jsxs("div", {
6052
+ class: "bio-properties-panel-numberfield",
6053
+ children: [displayLabel && jsxRuntime.jsx("label", {
6054
+ for: prefixId$5(id),
6055
+ class: "bio-properties-panel-label",
6056
+ children: label
6057
+ }), jsxRuntime.jsx("input", {
6058
+ id: prefixId$5(id),
6059
+ ref: inputRef,
6060
+ type: "number",
6061
+ name: id,
6062
+ spellCheck: "false",
6063
+ autoComplete: "off",
6064
+ disabled: disabled,
6065
+ class: "bio-properties-panel-input",
6066
+ max: max,
6067
+ min: min,
6068
+ onInput: handleInput,
6069
+ onFocus: onFocus,
6070
+ onBlur: onBlur,
6071
+ step: step,
6072
+ value: localValue
6073
+ })]
6074
+ });
6075
+ }
6076
+
6077
+ /**
6078
+ * @param {Object} props
6079
+ * @param {Boolean} props.debounce
6080
+ * @param {String} props.description
6081
+ * @param {Boolean} props.disabled
6082
+ * @param {Object} props.element
6083
+ * @param {Function} props.getValue
6084
+ * @param {String} props.id
6085
+ * @param {String} props.label
6086
+ * @param {String} props.max
6087
+ * @param {String} props.min
6088
+ * @param {Function} props.setValue
6089
+ * @param {Function} props.onFocus
6090
+ * @param {Function} props.onBlur
6091
+ * @param {String} props.step
6092
+ * @param {Function} props.validate
6093
+ */
6094
+ function NumberFieldEntry(props) {
6095
+ const {
6096
+ debounce,
6097
+ description,
6098
+ disabled,
6099
+ element,
6100
+ getValue,
6101
+ id,
6102
+ label,
6103
+ max,
6104
+ min,
6105
+ setValue,
6106
+ step,
6107
+ onFocus,
6108
+ onBlur,
6109
+ validate
6110
+ } = props;
6111
+ const [cachedInvalidValue, setCachedInvalidValue] = hooks.useState(null);
6112
+ const globalError = useError(id);
6113
+ const [localError, setLocalError] = hooks.useState(null);
6114
+ let value = getValue(element);
6115
+ const previousValue = usePrevious(value);
6116
+ hooks.useEffect(() => {
6117
+ if (minDash.isFunction(validate)) {
6118
+ const newValidationError = validate(value) || null;
6119
+ setLocalError(newValidationError);
6120
+ }
6121
+ }, [value]);
6122
+ const onInput = newValue => {
6123
+ let newValidationError = null;
6124
+ if (minDash.isFunction(validate)) {
6125
+ newValidationError = validate(newValue) || null;
6126
+ }
6127
+ if (newValidationError) {
6128
+ setCachedInvalidValue(newValue);
6129
+ } else {
6130
+ setValue(newValue);
6131
+ }
6132
+ setLocalError(newValidationError);
6133
+ };
6134
+ if (previousValue === value && localError) {
6135
+ value = cachedInvalidValue;
6136
+ }
6137
+ const error = globalError || localError;
6138
+ return jsxRuntime.jsxs("div", {
6139
+ class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
6140
+ "data-entry-id": id,
6141
+ children: [jsxRuntime.jsx(NumberField, {
6142
+ debounce: debounce,
6143
+ disabled: disabled,
6144
+ id: id,
6145
+ label: label,
6146
+ onFocus: onFocus,
6147
+ onBlur: onBlur,
6148
+ onInput: onInput,
6149
+ max: max,
6150
+ min: min,
6151
+ step: step,
6152
+ value: value
6153
+ }, element), error && jsxRuntime.jsx("div", {
6154
+ class: "bio-properties-panel-error",
6155
+ children: error
6156
+ }), jsxRuntime.jsx(Description$1, {
6157
+ forId: id,
6158
+ element: element,
6159
+ value: description
6160
+ })]
6161
+ });
6162
+ }
6163
+ function isEdited$6(node) {
6164
+ return node && !!node.value;
6165
+ }
6166
+
6167
+ // helpers /////////////////
6168
+
6169
+ function prefixId$5(id) {
6170
+ return `bio-properties-panel-${id}`;
6171
+ }
6172
+ const noop$1 = () => {};
6173
+ function FeelTextfield(props) {
6174
+ const {
6175
+ debounce,
6176
+ id,
6177
+ label,
6178
+ onInput,
6179
+ onError,
6180
+ feel,
6181
+ value = '',
6182
+ disabled = false,
6183
+ variables,
6184
+ tooltipContainer,
6185
+ OptionalComponent = OptionalFeelInput
6186
+ } = props;
6187
+ const [localValue, _setLocalValue] = hooks.useState(value);
6188
+ const editorRef = useShowEntryEvent(id);
6189
+ const containerRef = hooks.useRef();
6190
+ const feelActive = minDash.isString(localValue) && localValue.startsWith('=') || feel === 'required';
6191
+ const feelOnlyValue = minDash.isString(localValue) && localValue.startsWith('=') ? localValue.substring(1) : localValue;
6192
+ const [focus, _setFocus] = hooks.useState(undefined);
6193
+ const setFocus = (offset = 0) => {
6194
+ const hasFocus = containerRef.current.contains(document.activeElement);
6195
+
6196
+ // Keep caret position if it is already focused, otherwise focus at the end
6197
+ const position = hasFocus ? document.activeElement.selectionStart : Infinity;
6198
+ _setFocus(position + offset);
6199
+ };
6200
+ const handleInputCallback = hooks.useMemo(() => {
6201
+ return debounce(newValue => {
6202
+ onInput(newValue);
6203
+ });
6204
+ }, [onInput, debounce]);
6205
+ const setLocalValue = newValue => {
6206
+ _setLocalValue(newValue);
6207
+ if (!newValue || newValue === '=') {
6208
+ handleInputCallback(undefined);
6209
+ } else {
6210
+ handleInputCallback(newValue);
6211
+ }
6212
+ };
6213
+ const handleFeelToggle = useStaticCallback(() => {
6214
+ if (feel === 'required') {
6215
+ return;
6216
+ }
6217
+ if (!feelActive) {
6218
+ setLocalValue('=' + localValue);
6219
+ } else {
6220
+ setLocalValue(feelOnlyValue);
6221
+ }
6222
+ });
6223
+ const handleLocalInput = newValue => {
6224
+ if (feelActive) {
6225
+ newValue = '=' + newValue;
6226
+ }
6227
+ if (newValue === localValue) {
6228
+ return;
6229
+ }
6230
+ setLocalValue(newValue);
6231
+ if (!feelActive && minDash.isString(newValue) && newValue.startsWith('=')) {
6232
+ // focus is behind `=` sign that will be removed
6233
+ setFocus(-1);
6234
+ }
6235
+ };
6236
+ const handleLint = useStaticCallback(lint => {
6237
+ if (!(lint && lint.length)) {
6238
+ onError(undefined);
6239
+ return;
6240
+ }
6241
+ const error = lint[0];
6242
+ const message = `${error.source}: ${error.message}`;
5802
6243
  onError(message);
5803
6244
  });
5804
6245
  hooks.useEffect(() => {
@@ -5854,7 +6295,7 @@ function FeelTextfield(props) {
5854
6295
  'feel-active': feelActive
5855
6296
  }),
5856
6297
  children: [jsxRuntime.jsxs("label", {
5857
- for: prefixId$5(id),
6298
+ for: prefixId$4(id),
5858
6299
  class: "bio-properties-panel-label",
5859
6300
  onClick: () => setFocus(),
5860
6301
  children: [label, jsxRuntime.jsx(FeelIcon, {
@@ -5871,7 +6312,7 @@ function FeelTextfield(props) {
5871
6312
  disabled: feel !== 'optional' || disabled,
5872
6313
  onClick: handleFeelToggle
5873
6314
  }), feelActive ? jsxRuntime.jsx(CodeEditor, {
5874
- id: prefixId$5(id),
6315
+ id: prefixId$4(id),
5875
6316
  name: id,
5876
6317
  onInput: handleLocalInput,
5877
6318
  disabled: disabled,
@@ -5888,7 +6329,7 @@ function FeelTextfield(props) {
5888
6329
  ...props,
5889
6330
  onInput: handleLocalInput,
5890
6331
  contentAttributes: {
5891
- 'id': prefixId$5(id),
6332
+ 'id': prefixId$4(id),
5892
6333
  'aria-label': label
5893
6334
  },
5894
6335
  value: localValue,
@@ -5926,7 +6367,7 @@ const OptionalFeelInput = React.forwardRef((props, ref) => {
5926
6367
  }
5927
6368
  };
5928
6369
  return jsxRuntime.jsx("input", {
5929
- id: prefixId$5(id),
6370
+ id: prefixId$4(id),
5930
6371
  type: "text",
5931
6372
  ref: inputRef,
5932
6373
  name: id,
@@ -5940,6 +6381,53 @@ const OptionalFeelInput = React.forwardRef((props, ref) => {
5940
6381
  value: value || ''
5941
6382
  });
5942
6383
  });
6384
+ const OptionalFeelNumberField = React.forwardRef((props, ref) => {
6385
+ const {
6386
+ id,
6387
+ debounce,
6388
+ disabled,
6389
+ onInput,
6390
+ value,
6391
+ min,
6392
+ max,
6393
+ step,
6394
+ onFocus,
6395
+ onBlur
6396
+ } = props;
6397
+ const inputRef = hooks.useRef();
6398
+
6399
+ // To be consistent with the FEEL editor, set focus at start of input
6400
+ // this ensures clean editing experience when switching with the keyboard
6401
+ ref.current = {
6402
+ focus: position => {
6403
+ const input = inputRef.current;
6404
+ if (!input) {
6405
+ return;
6406
+ }
6407
+ input.focus();
6408
+ if (typeof position === 'number' && position !== Infinity) {
6409
+ if (position > value.length) {
6410
+ position = value.length;
6411
+ }
6412
+ input.setSelectionRange(position, position);
6413
+ }
6414
+ }
6415
+ };
6416
+ return jsxRuntime.jsx(NumberField, {
6417
+ id: id,
6418
+ debounce: debounce,
6419
+ disabled: disabled,
6420
+ displayLabel: false,
6421
+ inputRef: inputRef,
6422
+ max: max,
6423
+ min: min,
6424
+ onInput: onInput,
6425
+ step: step,
6426
+ value: value,
6427
+ onFocus: onFocus,
6428
+ onBlur: onBlur
6429
+ });
6430
+ });
5943
6431
  React.forwardRef((props, ref) => {
5944
6432
  const {
5945
6433
  id,
@@ -5964,7 +6452,7 @@ React.forwardRef((props, ref) => {
5964
6452
  }
5965
6453
  };
5966
6454
  return jsxRuntime.jsx("textarea", {
5967
- id: prefixId$5(id),
6455
+ id: prefixId$4(id),
5968
6456
  type: "text",
5969
6457
  ref: inputRef,
5970
6458
  name: id,
@@ -6040,7 +6528,7 @@ React.forwardRef((props, ref) => {
6040
6528
  };
6041
6529
  return jsxRuntime.jsx("input", {
6042
6530
  ref: inputRef,
6043
- id: prefixId$5(id),
6531
+ id: prefixId$4(id),
6044
6532
  name: id,
6045
6533
  onFocus: onFocus,
6046
6534
  onBlur: onBlur,
@@ -6080,261 +6568,30 @@ function FeelEntry(props) {
6080
6568
  disabled,
6081
6569
  feel,
6082
6570
  label,
6083
- getValue,
6084
- setValue,
6085
- tooltipContainer,
6086
- hostLanguage,
6087
- singleLine,
6088
- validate,
6089
- show = noop$1,
6090
- example,
6091
- variables,
6092
- onFocus,
6093
- onBlur
6094
- } = props;
6095
- const [cachedInvalidValue, setCachedInvalidValue] = hooks.useState(null);
6096
- const [validationError, setValidationError] = hooks.useState(null);
6097
- const [localError, setLocalError] = hooks.useState(null);
6098
- let value = getValue(element);
6099
- const previousValue = usePrevious(value);
6100
- hooks.useEffect(() => {
6101
- if (minDash.isFunction(validate)) {
6102
- const newValidationError = validate(value) || null;
6103
- setValidationError(newValidationError);
6104
- }
6105
- }, [value]);
6106
- const onInput = useStaticCallback(newValue => {
6107
- let newValidationError = null;
6108
- if (minDash.isFunction(validate)) {
6109
- newValidationError = validate(newValue) || null;
6110
- }
6111
- if (newValidationError) {
6112
- setCachedInvalidValue(newValue);
6113
- } else {
6114
- // don't create multiple commandStack entries for the same value
6115
- if (newValue !== value) {
6116
- setValue(newValue);
6117
- }
6118
- }
6119
- setValidationError(newValidationError);
6120
- });
6121
- const onError = hooks.useCallback(err => {
6122
- setLocalError(err);
6123
- }, []);
6124
- if (previousValue === value && validationError) {
6125
- value = cachedInvalidValue;
6126
- }
6127
- const temporaryError = useError(id);
6128
- const error = localError || temporaryError || validationError;
6129
- return jsxRuntime.jsxs("div", {
6130
- class: classnames(props.class, 'bio-properties-panel-entry', error ? 'has-error' : ''),
6131
- "data-entry-id": id,
6132
- children: [jsxRuntime.jsx(FeelTextfield, {
6133
- debounce: debounce,
6134
- disabled: disabled,
6135
- feel: feel,
6136
- id: id,
6137
- label: label,
6138
- onInput: onInput,
6139
- onError: onError,
6140
- onFocus: onFocus,
6141
- onBlur: onBlur,
6142
- example: example,
6143
- hostLanguage: hostLanguage,
6144
- singleLine: singleLine,
6145
- show: show,
6146
- value: value,
6147
- variables: variables,
6148
- tooltipContainer: tooltipContainer,
6149
- OptionalComponent: props.OptionalComponent
6150
- }, element), error && jsxRuntime.jsx("div", {
6151
- class: "bio-properties-panel-error",
6152
- children: error
6153
- }), jsxRuntime.jsx(Description$1, {
6154
- forId: id,
6155
- element: element,
6156
- value: description
6157
- })]
6158
- });
6159
- }
6160
-
6161
- /**
6162
- * @param {Object} props
6163
- * @param {Object} props.element
6164
- * @param {String} props.id
6165
- * @param {String} props.description
6166
- * @param {Boolean} props.debounce
6167
- * @param {Boolean} props.disabled
6168
- * @param {Boolean} props.feel
6169
- * @param {String} props.label
6170
- * @param {Function} props.getValue
6171
- * @param {Function} props.setValue
6172
- * @param {Function} props.tooltipContainer
6173
- * @param {Function} props.validate
6174
- * @param {Function} props.show
6175
- * @param {Function} props.example
6176
- * @param {Function} props.variables
6177
- * @param {Function} props.onFocus
6178
- * @param {Function} props.onBlur
6179
- */
6180
- function FeelToggleSwitchEntry(props) {
6181
- return jsxRuntime.jsx(FeelEntry, {
6182
- class: "bio-properties-panel-feel-toggle-switch",
6183
- OptionalComponent: OptionalFeelToggleSwitch,
6184
- ...props
6185
- });
6186
- }
6187
-
6188
- /**
6189
- * @param {Object} props
6190
- * @param {Object} props.element
6191
- * @param {String} props.id
6192
- * @param {String} props.description
6193
- * @param {String} props.hostLanguage
6194
- * @param {Boolean} props.singleLine
6195
- * @param {Boolean} props.debounce
6196
- * @param {Boolean} props.disabled
6197
- * @param {Boolean} props.feel
6198
- * @param {String} props.label
6199
- * @param {Function} props.getValue
6200
- * @param {Function} props.setValue
6201
- * @param {Function} props.tooltipContainer
6202
- * @param {Function} props.validate
6203
- * @param {Function} props.show
6204
- * @param {Function} props.example
6205
- * @param {Function} props.variables
6206
- * @param {Function} props.onFocus
6207
- * @param {Function} props.onBlur
6208
- */
6209
- function FeelTemplatingEntry(props) {
6210
- return jsxRuntime.jsx(FeelEntry, {
6211
- class: "bio-properties-panel-feel-templating",
6212
- OptionalComponent: CodeEditor$1,
6213
- ...props
6214
- });
6215
- }
6216
- function isEdited$6(node) {
6217
- if (!node) {
6218
- return false;
6219
- }
6220
- if (node.type === 'checkbox') {
6221
- return !!node.checked || node.classList.contains('edited');
6222
- }
6223
- return !!node.value || node.classList.contains('edited');
6224
- }
6225
-
6226
- // helpers /////////////////
6227
-
6228
- function prefixId$5(id) {
6229
- return `bio-properties-panel-${id}`;
6230
- }
6231
- function NumberField(props) {
6232
- const {
6233
- debounce,
6234
- disabled,
6235
- id,
6236
- label,
6237
- max,
6238
- min,
6239
- onInput,
6240
- step,
6241
- value = '',
6242
- onFocus,
6243
- onBlur
6244
- } = props;
6245
- const [localValue, setLocalValue] = hooks.useState(value);
6246
- const handleInputCallback = hooks.useMemo(() => {
6247
- return debounce(event => {
6248
- const {
6249
- validity,
6250
- value
6251
- } = event.target;
6252
- if (validity.valid) {
6253
- onInput(value ? parseFloat(value) : undefined);
6254
- }
6255
- });
6256
- }, [onInput, debounce]);
6257
- const handleInput = e => {
6258
- handleInputCallback(e);
6259
- setLocalValue(e.target.value);
6260
- };
6261
- hooks.useEffect(() => {
6262
- if (value === localValue) {
6263
- return;
6264
- }
6265
- setLocalValue(value);
6266
- }, [value]);
6267
- return jsxRuntime.jsxs("div", {
6268
- class: "bio-properties-panel-numberfield",
6269
- children: [jsxRuntime.jsx("label", {
6270
- for: prefixId$4(id),
6271
- class: "bio-properties-panel-label",
6272
- children: label
6273
- }), jsxRuntime.jsx("input", {
6274
- id: prefixId$4(id),
6275
- type: "number",
6276
- name: id,
6277
- spellCheck: "false",
6278
- autoComplete: "off",
6279
- disabled: disabled,
6280
- class: "bio-properties-panel-input",
6281
- max: max,
6282
- min: min,
6283
- onInput: handleInput,
6284
- onFocus: onFocus,
6285
- onBlur: onBlur,
6286
- step: step,
6287
- value: localValue
6288
- })]
6289
- });
6290
- }
6291
-
6292
- /**
6293
- * @param {Object} props
6294
- * @param {Boolean} props.debounce
6295
- * @param {String} props.description
6296
- * @param {Boolean} props.disabled
6297
- * @param {Object} props.element
6298
- * @param {Function} props.getValue
6299
- * @param {String} props.id
6300
- * @param {String} props.label
6301
- * @param {String} props.max
6302
- * @param {String} props.min
6303
- * @param {Function} props.setValue
6304
- * @param {Function} props.onFocus
6305
- * @param {Function} props.onBlur
6306
- * @param {String} props.step
6307
- * @param {Function} props.validate
6308
- */
6309
- function NumberFieldEntry(props) {
6310
- const {
6311
- debounce,
6312
- description,
6313
- disabled,
6314
- element,
6315
- getValue,
6316
- id,
6317
- label,
6318
- max,
6319
- min,
6571
+ getValue,
6320
6572
  setValue,
6321
- step,
6573
+ tooltipContainer,
6574
+ hostLanguage,
6575
+ singleLine,
6576
+ validate,
6577
+ show = noop$1,
6578
+ example,
6579
+ variables,
6322
6580
  onFocus,
6323
- onBlur,
6324
- validate
6581
+ onBlur
6325
6582
  } = props;
6326
6583
  const [cachedInvalidValue, setCachedInvalidValue] = hooks.useState(null);
6327
- const globalError = useError(id);
6584
+ const [validationError, setValidationError] = hooks.useState(null);
6328
6585
  const [localError, setLocalError] = hooks.useState(null);
6329
6586
  let value = getValue(element);
6330
6587
  const previousValue = usePrevious(value);
6331
6588
  hooks.useEffect(() => {
6332
6589
  if (minDash.isFunction(validate)) {
6333
6590
  const newValidationError = validate(value) || null;
6334
- setLocalError(newValidationError);
6591
+ setValidationError(newValidationError);
6335
6592
  }
6336
6593
  }, [value]);
6337
- const onInput = newValue => {
6594
+ const onInput = useStaticCallback(newValue => {
6338
6595
  let newValidationError = null;
6339
6596
  if (minDash.isFunction(validate)) {
6340
6597
  newValidationError = validate(newValue) || null;
@@ -6342,30 +6599,45 @@ function NumberFieldEntry(props) {
6342
6599
  if (newValidationError) {
6343
6600
  setCachedInvalidValue(newValue);
6344
6601
  } else {
6345
- setValue(newValue);
6602
+ // don't create multiple commandStack entries for the same value
6603
+ if (newValue !== value) {
6604
+ setValue(newValue);
6605
+ }
6346
6606
  }
6347
- setLocalError(newValidationError);
6348
- };
6349
- if (previousValue === value && localError) {
6607
+ setValidationError(newValidationError);
6608
+ });
6609
+ const onError = hooks.useCallback(err => {
6610
+ setLocalError(err);
6611
+ }, []);
6612
+ if (previousValue === value && validationError) {
6350
6613
  value = cachedInvalidValue;
6351
6614
  }
6352
- const error = globalError || localError;
6615
+ const temporaryError = useError(id);
6616
+ const error = localError || temporaryError || validationError;
6353
6617
  return jsxRuntime.jsxs("div", {
6354
- class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
6618
+ class: classnames(props.class, 'bio-properties-panel-entry', error ? 'has-error' : ''),
6355
6619
  "data-entry-id": id,
6356
- children: [jsxRuntime.jsx(NumberField, {
6620
+ children: [preact.createElement(FeelTextfield, {
6621
+ ...props,
6357
6622
  debounce: debounce,
6358
6623
  disabled: disabled,
6624
+ feel: feel,
6359
6625
  id: id,
6626
+ key: element,
6360
6627
  label: label,
6628
+ onInput: onInput,
6629
+ onError: onError,
6361
6630
  onFocus: onFocus,
6362
6631
  onBlur: onBlur,
6363
- onInput: onInput,
6364
- max: max,
6365
- min: min,
6366
- step: step,
6367
- value: value
6368
- }, element), error && jsxRuntime.jsx("div", {
6632
+ example: example,
6633
+ hostLanguage: hostLanguage,
6634
+ singleLine: singleLine,
6635
+ show: show,
6636
+ value: value,
6637
+ variables: variables,
6638
+ tooltipContainer: tooltipContainer,
6639
+ OptionalComponent: props.OptionalComponent
6640
+ }), error && jsxRuntime.jsx("div", {
6369
6641
  class: "bio-properties-panel-error",
6370
6642
  children: error
6371
6643
  }), jsxRuntime.jsx(Description$1, {
@@ -6375,8 +6647,100 @@ function NumberFieldEntry(props) {
6375
6647
  })]
6376
6648
  });
6377
6649
  }
6378
- function isEdited$4(node) {
6379
- return node && !!node.value;
6650
+
6651
+ /**
6652
+ * @param {Object} props
6653
+ * @param {Object} props.element
6654
+ * @param {String} props.id
6655
+ * @param {String} props.description
6656
+ * @param {Boolean} props.debounce
6657
+ * @param {Boolean} props.disabled
6658
+ * @param {String} props.max
6659
+ * @param {String} props.min
6660
+ * @param {String} props.step
6661
+ * @param {Boolean} props.feel
6662
+ * @param {String} props.label
6663
+ * @param {Function} props.getValue
6664
+ * @param {Function} props.setValue
6665
+ * @param {Function} props.tooltipContainer
6666
+ * @param {Function} props.validate
6667
+ * @param {Function} props.show
6668
+ * @param {Function} props.example
6669
+ * @param {Function} props.variables
6670
+ * @param {Function} props.onFocus
6671
+ * @param {Function} props.onBlur
6672
+ */
6673
+ function FeelNumberEntry(props) {
6674
+ return jsxRuntime.jsx(FeelEntry, {
6675
+ class: "bio-properties-panel-feel-number",
6676
+ OptionalComponent: OptionalFeelNumberField,
6677
+ ...props
6678
+ });
6679
+ }
6680
+
6681
+ /**
6682
+ * @param {Object} props
6683
+ * @param {Object} props.element
6684
+ * @param {String} props.id
6685
+ * @param {String} props.description
6686
+ * @param {Boolean} props.debounce
6687
+ * @param {Boolean} props.disabled
6688
+ * @param {Boolean} props.feel
6689
+ * @param {String} props.label
6690
+ * @param {Function} props.getValue
6691
+ * @param {Function} props.setValue
6692
+ * @param {Function} props.tooltipContainer
6693
+ * @param {Function} props.validate
6694
+ * @param {Function} props.show
6695
+ * @param {Function} props.example
6696
+ * @param {Function} props.variables
6697
+ * @param {Function} props.onFocus
6698
+ * @param {Function} props.onBlur
6699
+ */
6700
+ function FeelToggleSwitchEntry(props) {
6701
+ return jsxRuntime.jsx(FeelEntry, {
6702
+ class: "bio-properties-panel-feel-toggle-switch",
6703
+ OptionalComponent: OptionalFeelToggleSwitch,
6704
+ ...props
6705
+ });
6706
+ }
6707
+
6708
+ /**
6709
+ * @param {Object} props
6710
+ * @param {Object} props.element
6711
+ * @param {String} props.id
6712
+ * @param {String} props.description
6713
+ * @param {String} props.hostLanguage
6714
+ * @param {Boolean} props.singleLine
6715
+ * @param {Boolean} props.debounce
6716
+ * @param {Boolean} props.disabled
6717
+ * @param {Boolean} props.feel
6718
+ * @param {String} props.label
6719
+ * @param {Function} props.getValue
6720
+ * @param {Function} props.setValue
6721
+ * @param {Function} props.tooltipContainer
6722
+ * @param {Function} props.validate
6723
+ * @param {Function} props.show
6724
+ * @param {Function} props.example
6725
+ * @param {Function} props.variables
6726
+ * @param {Function} props.onFocus
6727
+ * @param {Function} props.onBlur
6728
+ */
6729
+ function FeelTemplatingEntry(props) {
6730
+ return jsxRuntime.jsx(FeelEntry, {
6731
+ class: "bio-properties-panel-feel-templating",
6732
+ OptionalComponent: CodeEditor$1,
6733
+ ...props
6734
+ });
6735
+ }
6736
+ function isEdited$5(node) {
6737
+ if (!node) {
6738
+ return false;
6739
+ }
6740
+ if (node.type === 'checkbox') {
6741
+ return !!node.checked || node.classList.contains('edited');
6742
+ }
6743
+ return !!node.value || node.classList.contains('edited');
6380
6744
  }
6381
6745
 
6382
6746
  // helpers /////////////////
@@ -7025,7 +7389,7 @@ function AltTextEntry(props) {
7025
7389
  component: AltText,
7026
7390
  editField: editField,
7027
7391
  field: field,
7028
- isEdited: isEdited$6
7392
+ isEdited: isEdited$5
7029
7393
  });
7030
7394
  }
7031
7395
  return entries;
@@ -7047,7 +7411,7 @@ function AltText(props) {
7047
7411
  const setValue = value => {
7048
7412
  return editField(field, path, value);
7049
7413
  };
7050
- return FeelEntry({
7414
+ return FeelTemplatingEntry({
7051
7415
  debounce,
7052
7416
  element: field,
7053
7417
  feel: 'optional',
@@ -7055,6 +7419,7 @@ function AltText(props) {
7055
7419
  id,
7056
7420
  label: 'Alternative text',
7057
7421
  setValue,
7422
+ singleLine: true,
7058
7423
  variables
7059
7424
  });
7060
7425
  }
@@ -7143,7 +7508,7 @@ function DescriptionEntry(props) {
7143
7508
  component: Description,
7144
7509
  editField: editField,
7145
7510
  field: field,
7146
- isEdited: isEdited
7511
+ isEdited: isEdited$5
7147
7512
  });
7148
7513
  }
7149
7514
  return entries;
@@ -7155,6 +7520,9 @@ function Description(props) {
7155
7520
  id
7156
7521
  } = props;
7157
7522
  const debounce = useService('debounce');
7523
+ const variables = useVariables().map(name => ({
7524
+ name
7525
+ }));
7158
7526
  const path = ['description'];
7159
7527
  const getValue = () => {
7160
7528
  return minDash.get(field, path, '');
@@ -7162,13 +7530,15 @@ function Description(props) {
7162
7530
  const setValue = value => {
7163
7531
  return editField(field, path, value);
7164
7532
  };
7165
- return TextfieldEntry({
7533
+ return FeelTemplatingEntry({
7166
7534
  debounce,
7167
7535
  element: field,
7168
7536
  getValue,
7169
7537
  id,
7170
7538
  label: 'Field description',
7171
- setValue
7539
+ singleLine: true,
7540
+ setValue,
7541
+ variables
7172
7542
  });
7173
7543
  }
7174
7544
 
@@ -7587,127 +7957,129 @@ function Key$1(props) {
7587
7957
  });
7588
7958
  }
7589
7959
 
7590
- function simpleStringEntryFactory(options) {
7591
- const {
7592
- id,
7593
- label,
7594
- path,
7595
- props
7596
- } = options;
7960
+ function LabelEntry(props) {
7597
7961
  const {
7598
- editField,
7599
- field
7600
- } = props;
7601
- return {
7602
- id,
7603
- label,
7604
- path,
7605
7962
  field,
7606
- editField,
7607
- component: SimpleStringComponent,
7608
- isEdited: isEdited
7609
- };
7963
+ editField
7964
+ } = props;
7965
+ const {
7966
+ type,
7967
+ subtype
7968
+ } = field;
7969
+ const entries = [];
7970
+ if (type === 'datetime') {
7971
+ if (subtype === formJsViewer.DATETIME_SUBTYPES.DATE || subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME) {
7972
+ entries.push({
7973
+ id: 'date-label',
7974
+ component: DateLabel,
7975
+ editField,
7976
+ field,
7977
+ isEdited: isEdited$5
7978
+ });
7979
+ }
7980
+ if (subtype === formJsViewer.DATETIME_SUBTYPES.TIME || subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME) {
7981
+ entries.push({
7982
+ id: 'time-label',
7983
+ component: TimeLabel,
7984
+ editField,
7985
+ field,
7986
+ isEdited: isEdited$5
7987
+ });
7988
+ }
7989
+ } else if (INPUTS.includes(type) || type === 'button') {
7990
+ entries.push({
7991
+ id: 'label',
7992
+ component: Label$1,
7993
+ editField,
7994
+ field,
7995
+ isEdited: isEdited$5
7996
+ });
7997
+ }
7998
+ return entries;
7610
7999
  }
7611
- const SimpleStringComponent = props => {
8000
+ function Label$1(props) {
7612
8001
  const {
7613
- id,
7614
- label,
7615
- path,
8002
+ editField,
7616
8003
  field,
7617
- editField
8004
+ id
7618
8005
  } = props;
7619
8006
  const debounce = useService('debounce');
7620
- const getValue = () => minDash.get(field, path, '');
7621
- const setValue = value => editField(field, path, value);
7622
- return TextfieldEntry({
8007
+ const variables = useVariables().map(name => ({
8008
+ name
8009
+ }));
8010
+ const path = ['label'];
8011
+ const getValue = () => {
8012
+ return minDash.get(field, path, '');
8013
+ };
8014
+ const setValue = value => {
8015
+ return editField(field, path, value);
8016
+ };
8017
+ return FeelTemplatingEntry({
7623
8018
  debounce,
7624
8019
  element: field,
7625
8020
  getValue,
7626
8021
  id,
7627
- label,
7628
- setValue
8022
+ label: 'Field label',
8023
+ singleLine: true,
8024
+ setValue,
8025
+ variables
7629
8026
  });
7630
- };
7631
-
7632
- function simpleBoolEntryFactory(options) {
7633
- const {
7634
- id,
7635
- label,
7636
- description,
7637
- path,
7638
- props
7639
- } = options;
8027
+ }
8028
+ function DateLabel(props) {
7640
8029
  const {
7641
8030
  editField,
7642
- field
7643
- } = props;
7644
- return {
7645
- id,
7646
- label,
7647
- path,
7648
8031
  field,
7649
- editField,
7650
- description,
7651
- component: SimpleBoolComponent,
7652
- isEdited: isEdited$8
8032
+ id
8033
+ } = props;
8034
+ const debounce = useService('debounce');
8035
+ const variables = useVariables().map(name => ({
8036
+ name
8037
+ }));
8038
+ const path = formJsViewer.DATE_LABEL_PATH;
8039
+ const getValue = () => {
8040
+ return minDash.get(field, path, '');
7653
8041
  };
8042
+ const setValue = value => {
8043
+ return editField(field, path, value);
8044
+ };
8045
+ return FeelTemplatingEntry({
8046
+ debounce,
8047
+ element: field,
8048
+ getValue,
8049
+ id,
8050
+ label: 'Date label',
8051
+ singleLine: true,
8052
+ setValue,
8053
+ variables
8054
+ });
7654
8055
  }
7655
- const SimpleBoolComponent = props => {
8056
+ function TimeLabel(props) {
7656
8057
  const {
7657
- id,
7658
- label,
7659
- path,
7660
- field,
7661
8058
  editField,
7662
- description
8059
+ field,
8060
+ id
7663
8061
  } = props;
7664
- const getValue = () => minDash.get(field, path, '');
7665
- const setValue = value => editField(field, path, value);
7666
- return CheckboxEntry({
8062
+ const debounce = useService('debounce');
8063
+ const variables = useVariables().map(name => ({
8064
+ name
8065
+ }));
8066
+ const path = formJsViewer.TIME_LABEL_PATH;
8067
+ const getValue = () => {
8068
+ return minDash.get(field, path, '');
8069
+ };
8070
+ const setValue = value => {
8071
+ return editField(field, path, value);
8072
+ };
8073
+ return FeelTemplatingEntry({
8074
+ debounce,
7667
8075
  element: field,
7668
8076
  getValue,
7669
8077
  id,
7670
- label,
8078
+ label: 'Time label',
8079
+ singleLine: true,
7671
8080
  setValue,
7672
- description
8081
+ variables
7673
8082
  });
7674
- };
7675
-
7676
- function LabelEntry(props) {
7677
- const {
7678
- field
7679
- } = props;
7680
- const {
7681
- type,
7682
- subtype
7683
- } = field;
7684
- const entries = [];
7685
- if (type === 'datetime') {
7686
- if (subtype === formJsViewer.DATETIME_SUBTYPES.DATE || subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME) {
7687
- entries.push(simpleStringEntryFactory({
7688
- id: 'date-label',
7689
- path: formJsViewer.DATE_LABEL_PATH,
7690
- label: 'Date label',
7691
- props
7692
- }));
7693
- }
7694
- if (subtype === formJsViewer.DATETIME_SUBTYPES.TIME || subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME) {
7695
- entries.push(simpleStringEntryFactory({
7696
- id: 'time-label',
7697
- path: formJsViewer.TIME_LABEL_PATH,
7698
- label: 'Time label',
7699
- props
7700
- }));
7701
- }
7702
- } else if (INPUTS.includes(type) || type === 'button') {
7703
- entries.push(simpleStringEntryFactory({
7704
- id: 'label',
7705
- path: ['label'],
7706
- label: 'Field label',
7707
- props
7708
- }));
7709
- }
7710
- return entries;
7711
8083
  }
7712
8084
 
7713
8085
  function SourceEntry(props) {
@@ -7725,7 +8097,7 @@ function SourceEntry(props) {
7725
8097
  component: Source,
7726
8098
  editField: editField,
7727
8099
  field: field,
7728
- isEdited: isEdited$6
8100
+ isEdited: isEdited$5
7729
8101
  });
7730
8102
  }
7731
8103
  return entries;
@@ -7747,7 +8119,7 @@ function Source(props) {
7747
8119
  const setValue = value => {
7748
8120
  return editField(field, path, value);
7749
8121
  };
7750
- return FeelEntry({
8122
+ return FeelTemplatingEntry({
7751
8123
  debounce,
7752
8124
  description: 'Expression or static value (link/data URI)',
7753
8125
  element: field,
@@ -7756,6 +8128,7 @@ function Source(props) {
7756
8128
  id,
7757
8129
  label: 'Image source',
7758
8130
  setValue,
8131
+ singleLine: true,
7759
8132
  variables
7760
8133
  });
7761
8134
  }
@@ -7780,7 +8153,7 @@ function TextEntry(props) {
7780
8153
  component: Text,
7781
8154
  editField: editField,
7782
8155
  field: field,
7783
- isEdited: isEdited$6
8156
+ isEdited: isEdited$5
7784
8157
  }];
7785
8158
 
7786
8159
  // todo: skipped to make the release without too much risk
@@ -7849,7 +8222,7 @@ function NumberEntries(props) {
7849
8222
  entries.push({
7850
8223
  id: id + '-decimalDigits',
7851
8224
  component: NumberDecimalDigits,
7852
- isEdited: isEdited$4,
8225
+ isEdited: isEdited$6,
7853
8226
  editField,
7854
8227
  field
7855
8228
  });
@@ -8216,6 +8589,50 @@ function TimeFormatSelect(props) {
8216
8589
  });
8217
8590
  }
8218
8591
 
8592
+ function simpleBoolEntryFactory(options) {
8593
+ const {
8594
+ id,
8595
+ label,
8596
+ description,
8597
+ path,
8598
+ props
8599
+ } = options;
8600
+ const {
8601
+ editField,
8602
+ field
8603
+ } = props;
8604
+ return {
8605
+ id,
8606
+ label,
8607
+ path,
8608
+ field,
8609
+ editField,
8610
+ description,
8611
+ component: SimpleBoolComponent,
8612
+ isEdited: isEdited$8
8613
+ };
8614
+ }
8615
+ const SimpleBoolComponent = props => {
8616
+ const {
8617
+ id,
8618
+ label,
8619
+ path,
8620
+ field,
8621
+ editField,
8622
+ description
8623
+ } = props;
8624
+ const getValue = () => minDash.get(field, path, '');
8625
+ const setValue = value => editField(field, path, value);
8626
+ return CheckboxEntry({
8627
+ element: field,
8628
+ getValue,
8629
+ id,
8630
+ label,
8631
+ setValue,
8632
+ description
8633
+ });
8634
+ };
8635
+
8219
8636
  function SelectEntries(props) {
8220
8637
  const {
8221
8638
  field
@@ -8436,6 +8853,34 @@ function updateKey(properties, oldKey, newKey) {
8436
8853
  }, {});
8437
8854
  }
8438
8855
 
8856
+ function AutoFocusSelectEntry(props) {
8857
+ const {
8858
+ autoFocusEntry,
8859
+ element,
8860
+ getValue
8861
+ } = props;
8862
+ const value = getValue(element);
8863
+ const prevValue = usePrevious(value);
8864
+ const eventBus = useService('eventBus');
8865
+
8866
+ // auto focus specifc other entry when selected value changed
8867
+ hooks.useEffect(() => {
8868
+ if (autoFocusEntry && prevValue && value !== prevValue) {
8869
+ // @Note(pinussilvestrus): There is an issue in the properties
8870
+ // panel so we have to wait a bit before showing the entry.
8871
+ // Cf. https://github.com/camunda/linting/blob/4f5328e2722f73ae60ae584c5f576eaec3999cb2/lib/modeler/Linting.js#L37
8872
+ setTimeout(() => {
8873
+ eventBus.fire('propertiesPanel.showEntry', {
8874
+ id: autoFocusEntry
8875
+ });
8876
+ });
8877
+ }
8878
+ }, [value, autoFocusEntry, prevValue, eventBus]);
8879
+ return jsxRuntime.jsx(SelectEntry, {
8880
+ ...props
8881
+ });
8882
+ }
8883
+
8439
8884
  function ValuesSourceSelectEntry(props) {
8440
8885
  const {
8441
8886
  editField,
@@ -8474,7 +8919,8 @@ function ValuesSourceSelect(props) {
8474
8919
  value: valueSource
8475
8920
  }));
8476
8921
  };
8477
- return SelectEntry({
8922
+ return AutoFocusSelectEntry({
8923
+ autoFocusEntry: getAutoFocusEntryId(field),
8478
8924
  label: 'Type',
8479
8925
  element: field,
8480
8926
  getOptions: getValuesSourceOptions,
@@ -8484,6 +8930,20 @@ function ValuesSourceSelect(props) {
8484
8930
  });
8485
8931
  }
8486
8932
 
8933
+ // helpers //////////
8934
+
8935
+ function getAutoFocusEntryId(field) {
8936
+ const valuesSource = formJsViewer.getValuesSource(field);
8937
+ if (valuesSource === formJsViewer.VALUES_SOURCES.EXPRESSION) {
8938
+ return `${field.id}-valuesExpression-expression`;
8939
+ } else if (valuesSource === formJsViewer.VALUES_SOURCES.INPUT) {
8940
+ return `${field.id}-dynamicValues-key`;
8941
+ } else if (valuesSource === formJsViewer.VALUES_SOURCES.STATIC) {
8942
+ return `${field.id}-staticValues-0-label`;
8943
+ }
8944
+ return null;
8945
+ }
8946
+
8487
8947
  function InputKeyValuesSourceEntry(props) {
8488
8948
  const {
8489
8949
  editField,
@@ -8640,7 +9100,7 @@ function AdornerEntry(props) {
8640
9100
  entries.push({
8641
9101
  id: 'prefix-adorner',
8642
9102
  component: PrefixAdorner,
8643
- isEdited: isEdited,
9103
+ isEdited: isEdited$5,
8644
9104
  editField,
8645
9105
  field,
8646
9106
  onChange,
@@ -8649,7 +9109,7 @@ function AdornerEntry(props) {
8649
9109
  entries.push({
8650
9110
  id: 'suffix-adorner',
8651
9111
  component: SuffixAdorner,
8652
- isEdited: isEdited,
9112
+ isEdited: isEdited$5,
8653
9113
  editField,
8654
9114
  field,
8655
9115
  onChange,
@@ -8666,13 +9126,19 @@ function PrefixAdorner(props) {
8666
9126
  getValue
8667
9127
  } = props;
8668
9128
  const debounce = useService('debounce');
8669
- return TextfieldEntry({
9129
+ const variables = useVariables().map(name => ({
9130
+ name
9131
+ }));
9132
+ return FeelTemplatingEntry({
8670
9133
  debounce,
8671
9134
  element: field,
9135
+ feel: 'optional',
8672
9136
  getValue: getValue('prefixAdorner'),
8673
9137
  id,
8674
9138
  label: 'Prefix',
8675
- setValue: onChange('prefixAdorner')
9139
+ setValue: onChange('prefixAdorner'),
9140
+ singleLine: true,
9141
+ variables
8676
9142
  });
8677
9143
  }
8678
9144
  function SuffixAdorner(props) {
@@ -8683,13 +9149,18 @@ function SuffixAdorner(props) {
8683
9149
  getValue
8684
9150
  } = props;
8685
9151
  const debounce = useService('debounce');
8686
- return TextfieldEntry({
9152
+ const variables = useVariables().map(name => ({
9153
+ name
9154
+ }));
9155
+ return FeelTemplatingEntry({
8687
9156
  debounce,
8688
9157
  element: field,
8689
9158
  getValue: getValue('suffixAdorner'),
8690
9159
  id,
8691
9160
  label: 'Suffix',
8692
- setValue: onChange('suffixAdorner')
9161
+ setValue: onChange('suffixAdorner'),
9162
+ singleLine: true,
9163
+ variables
8693
9164
  });
8694
9165
  }
8695
9166
 
@@ -8708,7 +9179,7 @@ function ReadonlyEntry(props) {
8708
9179
  component: Readonly,
8709
9180
  editField: editField,
8710
9181
  field: field,
8711
- isEdited: isEdited$6
9182
+ isEdited: isEdited$5
8712
9183
  });
8713
9184
  }
8714
9185
  return entries;
@@ -8752,7 +9223,7 @@ function ConditionEntry(props) {
8752
9223
  component: Condition,
8753
9224
  editField: editField,
8754
9225
  field: field,
8755
- isEdited: isEdited$6
9226
+ isEdited: isEdited$5
8756
9227
  }];
8757
9228
  }
8758
9229
  function Condition(props) {
@@ -8790,6 +9261,55 @@ function Condition(props) {
8790
9261
  });
8791
9262
  }
8792
9263
 
9264
+ function ValuesExpressionEntry(props) {
9265
+ const {
9266
+ editField,
9267
+ field,
9268
+ id
9269
+ } = props;
9270
+ return [{
9271
+ id: id + '-expression',
9272
+ component: ValuesExpression,
9273
+ label: 'Values expression',
9274
+ isEdited: isEdited$5,
9275
+ editField,
9276
+ field
9277
+ }];
9278
+ }
9279
+ function ValuesExpression(props) {
9280
+ const {
9281
+ editField,
9282
+ field,
9283
+ id
9284
+ } = props;
9285
+ const debounce = useService('debounce');
9286
+ const variables = useVariables().map(name => ({
9287
+ name
9288
+ }));
9289
+ const path = formJsViewer.VALUES_SOURCES_PATHS[formJsViewer.VALUES_SOURCES.EXPRESSION];
9290
+ const schema = '[\n {\n "label": "dollar",\n "value": "$"\n }\n]';
9291
+ const description = jsxRuntime.jsxs("div", {
9292
+ children: ["Define an expression to populate the options from.", jsxRuntime.jsx("br", {}), jsxRuntime.jsx("br", {}), "The expression may result in an array of simple values or alternatively follow this schema:", jsxRuntime.jsx("pre", {
9293
+ children: jsxRuntime.jsx("code", {
9294
+ children: schema
9295
+ })
9296
+ })]
9297
+ });
9298
+ const getValue = () => minDash.get(field, path, '');
9299
+ const setValue = value => editField(field, path, value || '');
9300
+ return FeelEntry({
9301
+ debounce,
9302
+ description,
9303
+ element: field,
9304
+ feel: 'required',
9305
+ getValue,
9306
+ id,
9307
+ label: 'Options expression',
9308
+ setValue,
9309
+ variables
9310
+ });
9311
+ }
9312
+
8793
9313
  function GeneralGroup(field, editField, getService) {
8794
9314
  const entries = [...IdEntry({
8795
9315
  field,
@@ -8934,14 +9454,14 @@ function ValidationGroup(field, editField) {
8934
9454
  component: MinLength,
8935
9455
  getValue,
8936
9456
  field,
8937
- isEdited: isEdited$4,
9457
+ isEdited: isEdited$5,
8938
9458
  onChange
8939
9459
  }, {
8940
9460
  id: 'maxLength',
8941
9461
  component: MaxLength,
8942
9462
  getValue,
8943
9463
  field,
8944
- isEdited: isEdited$4,
9464
+ isEdited: isEdited$5,
8945
9465
  onChange
8946
9466
  });
8947
9467
  }
@@ -8961,14 +9481,14 @@ function ValidationGroup(field, editField) {
8961
9481
  component: Min,
8962
9482
  getValue,
8963
9483
  field,
8964
- isEdited: isEdited$4,
9484
+ isEdited: isEdited$5,
8965
9485
  onChange
8966
9486
  }, {
8967
9487
  id: 'max',
8968
9488
  component: Max,
8969
9489
  getValue,
8970
9490
  field,
8971
- isEdited: isEdited$4,
9491
+ isEdited: isEdited$5,
8972
9492
  onChange
8973
9493
  });
8974
9494
  }
@@ -9001,14 +9521,19 @@ function MinLength(props) {
9001
9521
  onChange
9002
9522
  } = props;
9003
9523
  const debounce = useService('debounce');
9004
- return NumberFieldEntry({
9524
+ const variables = useVariables().map(name => ({
9525
+ name
9526
+ }));
9527
+ return FeelNumberEntry({
9005
9528
  debounce,
9006
9529
  element: field,
9530
+ feel: 'optional',
9007
9531
  getValue: getValue('minLength'),
9008
9532
  id,
9009
9533
  label: 'Minimum length',
9010
9534
  min: 0,
9011
- setValue: onChange('minLength')
9535
+ setValue: onChange('minLength'),
9536
+ variables
9012
9537
  });
9013
9538
  }
9014
9539
  function MaxLength(props) {
@@ -9019,14 +9544,19 @@ function MaxLength(props) {
9019
9544
  onChange
9020
9545
  } = props;
9021
9546
  const debounce = useService('debounce');
9022
- return NumberFieldEntry({
9547
+ const variables = useVariables().map(name => ({
9548
+ name
9549
+ }));
9550
+ return FeelNumberEntry({
9023
9551
  debounce,
9024
9552
  element: field,
9553
+ feel: 'optional',
9025
9554
  getValue: getValue('maxLength'),
9026
9555
  id,
9027
9556
  label: 'Maximum length',
9028
9557
  min: 0,
9029
- setValue: onChange('maxLength')
9558
+ setValue: onChange('maxLength'),
9559
+ variables
9030
9560
  });
9031
9561
  }
9032
9562
  function Pattern(props) {
@@ -9054,14 +9584,19 @@ function Min(props) {
9054
9584
  onChange
9055
9585
  } = props;
9056
9586
  const debounce = useService('debounce');
9057
- return NumberFieldEntry({
9587
+ const variables = useVariables().map(name => ({
9588
+ name
9589
+ }));
9590
+ return FeelNumberEntry({
9058
9591
  debounce,
9059
9592
  element: field,
9593
+ feel: 'optional',
9060
9594
  id,
9061
9595
  label: 'Minimum',
9062
9596
  step: 'any',
9063
9597
  getValue: getValue('min'),
9064
- setValue: onChange('min')
9598
+ setValue: onChange('min'),
9599
+ variables
9065
9600
  });
9066
9601
  }
9067
9602
  function Max(props) {
@@ -9072,14 +9607,19 @@ function Max(props) {
9072
9607
  onChange
9073
9608
  } = props;
9074
9609
  const debounce = useService('debounce');
9075
- return NumberFieldEntry({
9610
+ const variables = useVariables().map(name => ({
9611
+ name
9612
+ }));
9613
+ return FeelNumberEntry({
9076
9614
  debounce,
9077
9615
  element: field,
9616
+ feel: 'optional',
9078
9617
  id,
9079
9618
  label: 'Maximum',
9080
9619
  step: 'any',
9081
9620
  getValue: getValue('max'),
9082
- setValue: onChange('max')
9621
+ setValue: onChange('max'),
9622
+ variables
9083
9623
  });
9084
9624
  }
9085
9625
  function ValidationType(props) {
@@ -9153,6 +9693,17 @@ function ValuesGroups(field, editField) {
9153
9693
  id: staticValuesId
9154
9694
  })
9155
9695
  });
9696
+ } else if (valuesSource === formJsViewer.VALUES_SOURCES.EXPRESSION) {
9697
+ const valuesExpressionId = `${fieldId}-valuesExpression`;
9698
+ groups.push({
9699
+ id: valuesExpressionId,
9700
+ label: 'Options expression',
9701
+ component: Group,
9702
+ entries: ValuesExpressionEntry({
9703
+ ...context,
9704
+ id: valuesExpressionId
9705
+ })
9706
+ });
9156
9707
  }
9157
9708
  return groups;
9158
9709
  }
@@ -9312,47 +9863,37 @@ function FormPropertiesPanel(props) {
9312
9863
  } = props;
9313
9864
  const formEditor = injector.get('formEditor');
9314
9865
  const modeling = injector.get('modeling');
9315
- const selection = injector.get('selection');
9316
- const {
9317
- schema
9318
- } = formEditor._getState();
9866
+ const selectionModule = injector.get('selection');
9319
9867
  const [state, setState] = hooks.useState({
9320
- selectedFormField: selection.get() || schema
9868
+ selectedFormField: selectionModule.get() || formEditor._getState().schema
9321
9869
  });
9322
- const _update = field => {
9870
+ const selectedFormField = state.selectedFormField;
9871
+ const refresh = hooks.useCallback(field => {
9872
+ // TODO(skaiir): rework state management, re-rendering the whole properties panel is not the way to go
9873
+ // https://github.com/bpmn-io/form-js/issues/686
9323
9874
  setState({
9324
- ...state,
9325
- selectedFormField: field
9875
+ selectedFormField: selectionModule.get() || formEditor._getState().schema
9326
9876
  });
9327
9877
 
9328
9878
  // notify interested parties on property panel updates
9329
9879
  eventBus.fire('propertiesPanel.updated', {
9330
9880
  formField: field
9331
9881
  });
9332
- };
9333
- hooks.useLayoutEffect(() => {
9334
- function onSelectionChange(event) {
9335
- _update(event.selection || schema);
9336
- }
9337
- eventBus.on('selection.changed', onSelectionChange);
9338
- return () => {
9339
- eventBus.off('selection.changed', onSelectionChange);
9340
- };
9341
- }, []);
9882
+ }, [eventBus, formEditor, selectionModule]);
9342
9883
  hooks.useLayoutEffect(() => {
9343
- const onFieldChanged = () => {
9344
- /**
9345
- * TODO(pinussilvestrus): update with actual updated element,
9346
- * once we have a proper updater/change support
9347
- */
9348
- _update(selection.get() || schema);
9349
- };
9350
- eventBus.on('changed', onFieldChanged);
9884
+ /**
9885
+ * TODO(pinussilvestrus): update with actual updated element,
9886
+ * once we have a proper updater/change support
9887
+ */
9888
+ eventBus.on('changed', refresh);
9889
+ eventBus.on('import.done', refresh);
9890
+ eventBus.on('selection.changed', refresh);
9351
9891
  return () => {
9352
- eventBus.off('changed', onFieldChanged);
9892
+ eventBus.off('changed', refresh);
9893
+ eventBus.off('import.done', refresh);
9894
+ eventBus.off('selection.changed', refresh);
9353
9895
  };
9354
- }, []);
9355
- const selectedFormField = state.selectedFormField;
9896
+ }, [eventBus, refresh]);
9356
9897
  const getService = (type, strict = true) => injector.get(type, strict);
9357
9898
  const propertiesPanelContext = {
9358
9899
  getService
@@ -9448,10 +9989,70 @@ var PropertiesPanelModule = {
9448
9989
  propertiesPanel: ['type', PropertiesPanelRenderer]
9449
9990
  };
9450
9991
 
9992
+ /**
9993
+ * Manages the rendering of visual plugins.
9994
+ * @constructor
9995
+ * @param {Object} eventBus - Event bus for the application.
9996
+ */
9997
+ class RenderInjector extends SectionModuleBase {
9998
+ constructor(eventBus) {
9999
+ super(eventBus, 'renderInjector');
10000
+ this._eventBus = eventBus;
10001
+ this.registeredRenderers = [];
10002
+ }
10003
+
10004
+ /**
10005
+ * Inject a new renderer into the injector.
10006
+ * @param {string} identifier - Identifier for the renderer.
10007
+ * @param {Function} Renderer - The renderer function.
10008
+ */
10009
+ attachRenderer(identifier, Renderer) {
10010
+ this.registeredRenderers = [...this.registeredRenderers, {
10011
+ identifier,
10012
+ Renderer
10013
+ }];
10014
+ }
10015
+
10016
+ /**
10017
+ * Detach a renderer from the by key injector.
10018
+ * @param {string} identifier - Identifier for the renderer.
10019
+ */
10020
+ detachRenderer(identifier) {
10021
+ this.registeredRenderers = this.registeredRenderers.filter(r => r.identifier !== identifier);
10022
+ }
10023
+
10024
+ /**
10025
+ * Returns the registered renderers.
10026
+ * @returns {Array} Array of registered renderers.
10027
+ */
10028
+ fetchRenderers() {
10029
+ return this.registeredRenderers;
10030
+ }
10031
+ }
10032
+ RenderInjector.$inject = ['eventBus'];
10033
+
10034
+ var RenderInjectionModule = {
10035
+ __init__: ['renderInjector'],
10036
+ renderInjector: ['type', RenderInjector]
10037
+ };
10038
+
10039
+ class EditorTemplating {
10040
+ // same rules as viewer templating
10041
+ isTemplate(value) {
10042
+ return minDash.isString(value) && (value.startsWith('=') || /{{/.test(value));
10043
+ }
10044
+
10045
+ // return the template raw, as we usually just want to display that
10046
+ evaluate(template) {
10047
+ return template;
10048
+ }
10049
+ }
10050
+ EditorTemplating.$inject = [];
10051
+
9451
10052
  var ExpressionLanguageModule = {
9452
10053
  __init__: ['expressionLanguage', 'templating'],
9453
10054
  expressionLanguage: ['type', formJsViewer.FeelExpressionLanguage],
9454
- templating: ['type', formJsViewer.FeelersTemplating]
10055
+ templating: ['type', EditorTemplating]
9455
10056
  };
9456
10057
 
9457
10058
  const ids = new Ids([32, 36, 1]);
@@ -9706,7 +10307,7 @@ class FormEditor {
9706
10307
  * @internal
9707
10308
  */
9708
10309
  _getModules() {
9709
- return [ModelingModule, EditorActionsModule, DraggingModule, KeyboardModule, SelectionModule, PaletteModule, ExpressionLanguageModule, formJsViewer.MarkdownModule, PropertiesPanelModule];
10310
+ return [ModelingModule, EditorActionsModule, DraggingModule, KeyboardModule, SelectionModule, PaletteModule, ExpressionLanguageModule, formJsViewer.MarkdownModule, PropertiesPanelModule, RenderInjectionModule];
9710
10311
  }
9711
10312
 
9712
10313
  /**