@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.es.js CHANGED
@@ -1,12 +1,13 @@
1
- import { FormFieldRegistry as FormFieldRegistry$1, clone, iconsByType, Text as Text$1, FormFields, formFields, FormContext, FormRenderContext, FormComponent, FormLayouter, getSchemaVariables, DATETIME_SUBTYPES, DATE_LABEL_PATH, TIME_LABEL_PATH, DATETIME_SUBTYPE_PATH, DATETIME_SUBTYPES_LABELS, TIME_SERIALISING_FORMAT_PATH, TIME_SERIALISING_FORMATS, TIME_INTERVAL_PATH, TIME_USE24H_PATH, DATE_DISALLOW_PAST_PATH, TIME_SERIALISINGFORMAT_LABELS, getValuesSource, VALUES_SOURCES, VALUES_SOURCES_DEFAULTS, VALUES_SOURCES_PATHS, VALUES_SOURCES_LABELS, FeelExpressionLanguage, FeelersTemplating, createFormContainer, createInjector, MarkdownModule, schemaVersion } from '@bpmn-io/form-js-viewer';
1
+ import { FormFieldRegistry as FormFieldRegistry$1, clone, iconsByType, Text as Text$1, FormFields, formFields, FormContext, FormRenderContext, FormComponent, FormLayouter, getSchemaVariables, DATETIME_SUBTYPES, DATE_LABEL_PATH, TIME_LABEL_PATH, DATETIME_SUBTYPE_PATH, DATETIME_SUBTYPES_LABELS, TIME_SERIALISING_FORMAT_PATH, TIME_SERIALISING_FORMATS, TIME_INTERVAL_PATH, TIME_USE24H_PATH, DATE_DISALLOW_PAST_PATH, TIME_SERIALISINGFORMAT_LABELS, getValuesSource, VALUES_SOURCES, VALUES_SOURCES_DEFAULTS, VALUES_SOURCES_PATHS, VALUES_SOURCES_LABELS, FeelExpressionLanguage, createFormContainer, createInjector, MarkdownModule, schemaVersion } from '@bpmn-io/form-js-viewer';
2
2
  export { schemaVersion } from '@bpmn-io/form-js-viewer';
3
3
  import Ids from 'ids';
4
4
  import { isArray, isFunction, isNumber, bind, assign, debounce, forEach, get, isObject, uniqueBy, sortBy, find, set as set$1, isString, isUndefined, without, has } from 'min-dash';
5
5
  import classnames from 'classnames';
6
- import { jsx, jsxs, Fragment } from 'preact/jsx-runtime';
7
- import { useContext, useState, useRef, useCallback, useEffect, useLayoutEffect, useMemo } from 'preact/hooks';
8
- import { createContext, render, createElement } from 'preact';
9
- import React, { forwardRef } from 'preact/compat';
6
+ import { jsx, jsxs, Fragment as Fragment$1 } from 'preact/jsx-runtime';
7
+ import { useContext, useState, useMemo, useEffect, useCallback, useRef as useRef$1, useLayoutEffect } from 'preact/hooks';
8
+ import { createContext, Fragment, render, createElement } from 'preact';
9
+ import * as React from 'preact/compat';
10
+ import { createPortal, useRef, useContext as useContext$1, useEffect as useEffect$1, forwardRef } from 'preact/compat';
10
11
  import dragula from 'dragula';
11
12
  import { classes, query, closest, event, matches, domify } from 'min-dom';
12
13
  import { mutate } from 'array-move';
@@ -933,83 +934,91 @@ function useService$1 (type, strict) {
933
934
  return getService(type, strict);
934
935
  }
935
936
 
937
+ var _path$3;
936
938
  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); }
937
- var CloseIcon = (({
938
- styles = {},
939
- ...props
940
- }) => /*#__PURE__*/React.createElement("svg", _extends$3({
941
- width: "16",
942
- height: "16",
943
- fill: "currentColor",
944
- xmlns: "http://www.w3.org/2000/svg"
945
- }, props), /*#__PURE__*/React.createElement("path", {
946
- fillRule: "evenodd",
947
- clipRule: "evenodd",
948
- 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"
949
- })));
939
+ var SvgClose = function SvgClose(props) {
940
+ return /*#__PURE__*/React.createElement("svg", _extends$3({
941
+ xmlns: "http://www.w3.org/2000/svg",
942
+ width: 16,
943
+ height: 16,
944
+ fill: "currentColor"
945
+ }, props), _path$3 || (_path$3 = /*#__PURE__*/React.createElement("path", {
946
+ fillRule: "evenodd",
947
+ 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",
948
+ clipRule: "evenodd"
949
+ })));
950
+ };
951
+ var CloseIcon = SvgClose;
950
952
 
953
+ var _path$2, _path2;
951
954
  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); }
952
- var DeleteIcon$1 = (({
953
- styles = {},
954
- ...props
955
- }) => /*#__PURE__*/React.createElement("svg", _extends$2({
956
- xmlns: "http://www.w3.org/2000/svg",
957
- width: "16",
958
- height: "16",
959
- fill: "none"
960
- }, props), /*#__PURE__*/React.createElement("rect", {
961
- width: "16",
962
- height: "16",
963
- x: ".536",
964
- fill: "#fff",
965
- rx: "3",
966
- style: {
967
- mixBlendMode: "multiply"
968
- }
969
- }), /*#__PURE__*/React.createElement("path", {
970
- fill: "#fff",
971
- d: "M.536 0h16v16h-16z",
972
- style: {
973
- mixBlendMode: "multiply"
974
- }
975
- }), /*#__PURE__*/React.createElement("path", {
976
- fill: "currentcolor",
977
- d: "M7.536 6h-1v6h1V6zm3 0h-1v6h1V6z"
978
- }), /*#__PURE__*/React.createElement("path", {
979
- fill: "currentcolor",
980
- d: "M2.536 3v1h1v10a1 1 0 001 1h8a1 1 0 001-1V4h1V3h-12zm2 11V4h8v10h-8zm6-13h-4v1h4V1z"
981
- })));
955
+ var SvgDelete = function SvgDelete(props) {
956
+ return /*#__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: 0.536,
965
+ fill: "#fff",
966
+ rx: 3,
967
+ style: {
968
+ mixBlendMode: "multiply"
969
+ }
970
+ }), /*#__PURE__*/React.createElement("path", {
971
+ fill: "#fff",
972
+ d: "M0 0h16v16H0z",
973
+ style: {
974
+ mixBlendMode: "multiply"
975
+ },
976
+ transform: "translate(.536)"
977
+ }), _path$2 || (_path$2 = /*#__PURE__*/React.createElement("path", {
978
+ fill: "currentcolor",
979
+ d: "M7.536 6h-1v6h1V6Zm3 0h-1v6h1V6Z"
980
+ })), _path2 || (_path2 = /*#__PURE__*/React.createElement("path", {
981
+ fill: "currentcolor",
982
+ d: "M2.536 3v1h1v10a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V4h1V3h-12Zm2 11V4h8v10h-8Zm6-13h-4v1h4V1Z"
983
+ })));
984
+ };
985
+ var DeleteIcon$1 = SvgDelete;
982
986
 
987
+ var _path$1;
983
988
  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); }
984
- var DraggableIcon = (({
985
- styles = {},
986
- ...props
987
- }) => /*#__PURE__*/React.createElement("svg", _extends$1({
988
- xmlns: "http://www.w3.org/2000/svg",
989
- width: "16",
990
- height: "16",
991
- fill: "currentcolor",
992
- viewBox: "0 0 32 32"
993
- }, props), /*#__PURE__*/React.createElement("path", {
994
- d: "M10 6h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4z"
995
- }), /*#__PURE__*/React.createElement("path", {
996
- d: "M0 0h32v32H0z",
997
- fill: "none"
998
- })));
989
+ var SvgDraggable = function SvgDraggable(props) {
990
+ return /*#__PURE__*/React.createElement("svg", _extends$1({
991
+ xmlns: "http://www.w3.org/2000/svg",
992
+ xmlSpace: "preserve",
993
+ width: 16,
994
+ height: 16,
995
+ fill: "currentcolor",
996
+ viewBox: "0 0 32 32"
997
+ }, props), _path$1 || (_path$1 = /*#__PURE__*/React.createElement("path", {
998
+ d: "M10 6h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4zm-8 8h4v4h-4zm8 0h4v4h-4z"
999
+ })), /*#__PURE__*/React.createElement("path", {
1000
+ d: "M0 0h32v32H0z",
1001
+ style: {
1002
+ fill: "none"
1003
+ }
1004
+ }));
1005
+ };
1006
+ var DraggableIcon = SvgDraggable;
999
1007
 
1008
+ var _path;
1000
1009
  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); }
1001
- var SearchIcon = (({
1002
- styles = {},
1003
- ...props
1004
- }) => /*#__PURE__*/React.createElement("svg", _extends({
1005
- width: "15",
1006
- height: "15",
1007
- fill: "none",
1008
- xmlns: "http://www.w3.org/2000/svg"
1009
- }, props), /*#__PURE__*/React.createElement("path", {
1010
- 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",
1011
- fill: "currentColor"
1012
- })));
1010
+ var SvgSearch = function SvgSearch(props) {
1011
+ return /*#__PURE__*/React.createElement("svg", _extends({
1012
+ xmlns: "http://www.w3.org/2000/svg",
1013
+ width: 15,
1014
+ height: 15,
1015
+ fill: "none"
1016
+ }, props), _path || (_path = /*#__PURE__*/React.createElement("path", {
1017
+ fill: "currentColor",
1018
+ 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"
1019
+ })));
1020
+ };
1021
+ var SearchIcon = SvgSearch;
1013
1022
 
1014
1023
  function EditorText(props) {
1015
1024
  const {
@@ -1070,6 +1079,228 @@ class EditorFormFields extends FormFields {
1070
1079
  }
1071
1080
  }
1072
1081
 
1082
+ var ModularSection = (props => {
1083
+ const {
1084
+ rootClass,
1085
+ RootElement,
1086
+ section,
1087
+ children
1088
+ } = props;
1089
+ const eventBus = useService$1('eventBus');
1090
+ const sectionConfig = useService$1(`config.${section}`);
1091
+ const [parent, setParent] = useState(sectionConfig && sectionConfig.parent || null);
1092
+ const [shouldRender, setShouldRender] = useState(true);
1093
+ const ParentElement = useMemo(() => {
1094
+ if (parent === null) {
1095
+ return null;
1096
+ }
1097
+ if (typeof parent === 'string') {
1098
+ const element = document.querySelector(parent);
1099
+ if (!element) {
1100
+ throw new Error(`Target root element with selector '${parent}' not found for section '${section}'`);
1101
+ }
1102
+ return document.querySelector(parent);
1103
+ }
1104
+
1105
+ // @ts-ignore
1106
+ if (!(parent instanceof Element)) {
1107
+ throw new Error(`Target root element for section '${section}' must be a valid selector or DOM element`);
1108
+ }
1109
+ return parent;
1110
+ }, [section, parent]);
1111
+ useEffect(() => {
1112
+ const onAttach = ({
1113
+ container
1114
+ }) => {
1115
+ setParent(container);
1116
+ setShouldRender(true);
1117
+ };
1118
+ const onDetach = () => {
1119
+ setParent(null);
1120
+ setShouldRender(false);
1121
+ };
1122
+ const onReset = () => {
1123
+ setParent(null);
1124
+ setShouldRender(true);
1125
+ };
1126
+ eventBus.on(`${section}.attach`, onAttach);
1127
+ eventBus.on(`${section}.detach`, onDetach);
1128
+ eventBus.on(`${section}.reset`, onReset);
1129
+ eventBus.fire(`${section}.section.rendered`);
1130
+ return () => {
1131
+ eventBus.off(`${section}.attach`, onAttach);
1132
+ eventBus.off(`${section}.detach`, onDetach);
1133
+ eventBus.off(`${section}.reset`, onReset);
1134
+ eventBus.fire(`${section}.section.destroyed`);
1135
+ };
1136
+ }, [eventBus, section]);
1137
+ useEffect(() => {
1138
+ if (shouldRender) {
1139
+ eventBus.fire(`${section}.rendered`, {
1140
+ element: ParentElement
1141
+ });
1142
+ return () => {
1143
+ eventBus.fire(`${section}.destroyed`, {
1144
+ element: ParentElement
1145
+ });
1146
+ };
1147
+ }
1148
+ }, [eventBus, section, shouldRender, ParentElement]);
1149
+ const Root = useCallback(({
1150
+ children
1151
+ }) => RootElement ? jsx(RootElement, {
1152
+ children: children
1153
+ }) : jsx("div", {
1154
+ className: rootClass,
1155
+ children: children
1156
+ }), [rootClass, RootElement]);
1157
+ return shouldRender ? parent ? createPortal(jsx(Root, {
1158
+ children: children
1159
+ }), ParentElement) : jsx(Root, {
1160
+ children: children
1161
+ }) : null;
1162
+ });
1163
+
1164
+ const FillContext = createContext({
1165
+ addFill(uid, props) {
1166
+ throw new Error('FillContext.addFill() uninitialized');
1167
+ },
1168
+ removeFill(uid) {
1169
+ throw new Error('FillContext.addFill() uninitialized');
1170
+ }
1171
+ });
1172
+ var FillContext$1 = FillContext;
1173
+
1174
+ var Fill = (props => {
1175
+ const uid = useRef(Symbol('fill_uid'));
1176
+ const fillContext = useContext$1(FillContext$1);
1177
+ useEffect$1(() => {
1178
+ if (!fillContext) {
1179
+ return;
1180
+ }
1181
+ fillContext.addFill({
1182
+ id: uid,
1183
+ ...props
1184
+ });
1185
+ return () => {
1186
+ fillContext.removeFill(uid);
1187
+ };
1188
+ }, [fillContext, props]);
1189
+ return null;
1190
+ });
1191
+
1192
+ const SlotContext = createContext({
1193
+ fills: []
1194
+ });
1195
+ var SlotContext$1 = SlotContext;
1196
+
1197
+ var Slot = (props => {
1198
+ const {
1199
+ name,
1200
+ fillRoot = FillFragment,
1201
+ groupFn = _groupByGroupName,
1202
+ separatorFn = key => null,
1203
+ limit
1204
+ } = props;
1205
+ const {
1206
+ fills
1207
+ } = useContext(SlotContext$1);
1208
+ const filtered = useMemo(() => fills.filter(fill => fill.slot === name), [fills, name]);
1209
+ const cropped = useMemo(() => limit ? filtered.slice(0, limit) : filtered, [filtered, limit]);
1210
+ const groups = useMemo(() => groupFn(cropped), [cropped, groupFn]);
1211
+ const fillsAndSeparators = useMemo(() => {
1212
+ return buildFills(groups, fillRoot, separatorFn);
1213
+ }, [groups, fillRoot, separatorFn]);
1214
+ return fillsAndSeparators;
1215
+ });
1216
+
1217
+ /**
1218
+ * Creates a Fragment for a fill.
1219
+ *
1220
+ * @param {Object} fill Fill to be rendered
1221
+ * @returns {Object} Preact Fragment containing fill's children
1222
+ */
1223
+ const FillFragment = fill => jsx(Fragment, {
1224
+ children: fill.children
1225
+ }, fill.id);
1226
+
1227
+ /**
1228
+ * Creates an array of fills, with separators inserted between groups.
1229
+ *
1230
+ * @param {Array} groups Groups of fills
1231
+ * @param {Function} fillRenderer Function to create a fill
1232
+ * @param {Function} separatorRenderer Function to create a separator
1233
+ * @returns {Array} Array of fills and separators
1234
+ */
1235
+ const buildFills = (groups, fillRenderer, separatorRenderer) => {
1236
+ const result = [];
1237
+ groups.forEach((array, idx) => {
1238
+ if (idx !== 0) {
1239
+ const separator = separatorRenderer(`__separator_${idx}`);
1240
+ if (separator) {
1241
+ result.push(separator);
1242
+ }
1243
+ }
1244
+ array.forEach(fill => {
1245
+ result.push(fillRenderer(fill));
1246
+ });
1247
+ });
1248
+ return result;
1249
+ };
1250
+
1251
+ /**
1252
+ * Groups fills by group name property.
1253
+ */
1254
+ const _groupByGroupName = fills => {
1255
+ const groups = [];
1256
+ const groupsById = {};
1257
+ fills.forEach(function (fill) {
1258
+ const {
1259
+ group: groupName = 'z_default'
1260
+ } = fill;
1261
+ let group = groupsById[groupName];
1262
+ if (!group) {
1263
+ groupsById[groupName] = group = [];
1264
+ groups.push(group);
1265
+ }
1266
+ group.push(fill);
1267
+ });
1268
+ groups.forEach(group => group.sort(_comparePriority));
1269
+ return Object.keys(groupsById).sort().map(id => groupsById[id]);
1270
+ };
1271
+
1272
+ /**
1273
+ * Compares fills by priority.
1274
+ */
1275
+ const _comparePriority = (a, b) => {
1276
+ return (b.priority || 0) - (a.priority || 0);
1277
+ };
1278
+
1279
+ var SlotFillRoot = (props => {
1280
+ const [fills, setFills] = useState([]);
1281
+ const fillContext = useMemo(() => ({
1282
+ addFill: fill => {
1283
+ setFills(fills => [...fills.filter(f => f.id !== fill.id), fill]);
1284
+ props.onSetFill && props.onSetFill(fill);
1285
+ },
1286
+ removeFill: id => {
1287
+ setFills(fills => fills.filter(f => f.id !== id));
1288
+ props.onRemoveFill && props.onRemoveFill(id);
1289
+ }
1290
+ }), []);
1291
+ const slotContext = useMemo(() => ({
1292
+ fills
1293
+ }), [fills]);
1294
+ return jsx(SlotContext$1.Provider, {
1295
+ value: slotContext,
1296
+ children: jsx(FillContext$1.Provider, {
1297
+ /* @ts-expect-error */
1298
+ value: fillContext,
1299
+ children: props.children
1300
+ })
1301
+ });
1302
+ });
1303
+
1073
1304
  const PALETTE_ENTRIES = formFields.filter(({
1074
1305
  config: fieldConfig
1075
1306
  }) => fieldConfig.type !== 'default').map(({
@@ -1097,7 +1328,7 @@ const PALETTE_GROUPS = [{
1097
1328
  function Palette(props) {
1098
1329
  const [entries, setEntries] = useState(PALETTE_ENTRIES);
1099
1330
  const [searchTerm, setSearchTerm] = useState('');
1100
- const inputRef = useRef();
1331
+ const inputRef = useRef$1();
1101
1332
  const groups = groupEntries(entries);
1102
1333
  const simplifyString = useCallback(str => {
1103
1334
  return str.toLowerCase().replace(/\s+/g, '');
@@ -1187,9 +1418,19 @@ function Palette(props) {
1187
1418
  class: "fjs-palette-no-entries",
1188
1419
  children: "No components found."
1189
1420
  })]
1421
+ }), jsx("div", {
1422
+ class: "fjs-palette-footer",
1423
+ children: jsx(Slot, {
1424
+ name: "editor-palette__footer",
1425
+ fillRoot: FillRoot
1426
+ })
1190
1427
  })]
1191
1428
  });
1192
1429
  }
1430
+ const FillRoot = fill => jsx("div", {
1431
+ className: "fjs-palette-footer-fill",
1432
+ children: fill.children
1433
+ });
1193
1434
 
1194
1435
  // helpers ///////
1195
1436
 
@@ -1216,6 +1457,25 @@ function getIndefiniteArticle(type) {
1216
1457
  return 'a';
1217
1458
  }
1218
1459
 
1460
+ var InjectedRendersRoot = (() => {
1461
+ const renderInjector = useService$1('renderInjector');
1462
+ const injectedRenderers = renderInjector.fetchRenderers();
1463
+ const injectedProps = useMemo(() => ({
1464
+ useService: useService$1,
1465
+ components: {
1466
+ Fill,
1467
+ Slot
1468
+ }
1469
+ }), []);
1470
+ return jsx(Fragment, {
1471
+ children: injectedRenderers.map(({
1472
+ Renderer
1473
+ }) => jsx(Renderer, {
1474
+ ...injectedProps
1475
+ }))
1476
+ });
1477
+ });
1478
+
1219
1479
  const CURSOR_CLS_PATTERN = /^fjs-cursor-.*$/;
1220
1480
  function set(mode) {
1221
1481
  const classes$1 = classes(document.body);
@@ -1538,13 +1798,13 @@ function FieldResizer(props) {
1538
1798
  field,
1539
1799
  position
1540
1800
  } = props;
1541
- const ref = useRef(null);
1801
+ const ref = useRef$1(null);
1542
1802
  const formLayoutValidator = useService$1('formLayoutValidator');
1543
1803
  const modeling = useService$1('modeling');
1544
1804
 
1545
1805
  // we can't use state as we need to
1546
1806
  // manipulate this inside dragging events
1547
- const context = useRef({
1807
+ const context = useRef$1({
1548
1808
  startColumns: 0,
1549
1809
  newColumns: 0
1550
1810
  });
@@ -1671,7 +1931,7 @@ function ContextPad(props) {
1671
1931
  function Empty(props) {
1672
1932
  return null;
1673
1933
  }
1674
- function Element(props) {
1934
+ function Element$1(props) {
1675
1935
  const eventBus = useService$1('eventBus'),
1676
1936
  formEditor = useService$1('formEditor'),
1677
1937
  formFieldRegistry = useService$1('formFieldRegistry'),
@@ -1684,7 +1944,7 @@ function Element(props) {
1684
1944
  id,
1685
1945
  type
1686
1946
  } = field;
1687
- const ref = useRef();
1947
+ const ref = useRef$1();
1688
1948
  function scrollIntoView({
1689
1949
  selection
1690
1950
  }) {
@@ -1834,8 +2094,6 @@ function FormEditor$1(props) {
1834
2094
  formEditor = useService$1('formEditor'),
1835
2095
  injector = useService$1('injector'),
1836
2096
  selection = useService$1('selection'),
1837
- palette = useService$1('palette'),
1838
- paletteConfig = useService$1('config.palette'),
1839
2097
  propertiesPanel = useService$1('propertiesPanel'),
1840
2098
  propertiesPanelConfig = useService$1('config.propertiesPanel');
1841
2099
  const {
@@ -1845,9 +2103,8 @@ function FormEditor$1(props) {
1845
2103
  const {
1846
2104
  ariaLabel
1847
2105
  } = properties;
1848
- const formContainerRef = useRef(null);
1849
- const paletteRef = useRef(null);
1850
- const propertiesPanelRef = useRef(null);
2106
+ const formContainerRef = useRef$1(null);
2107
+ const propertiesPanelRef = useRef$1(null);
1851
2108
  const [, setSelection] = useState(schema);
1852
2109
  useEffect(() => {
1853
2110
  function handleSelectionChanged(event) {
@@ -1907,7 +2164,7 @@ function FormEditor$1(props) {
1907
2164
  eventBus.off('drag.start', onDragStart);
1908
2165
  eventBus.off('drag.end', onDragEnd);
1909
2166
  };
1910
- }, []);
2167
+ }, [dragging, eventBus]);
1911
2168
 
1912
2169
  // fire event after render to notify interested parties
1913
2170
  useEffect(() => {
@@ -1916,7 +2173,7 @@ function FormEditor$1(props) {
1916
2173
  const formRenderContext = {
1917
2174
  Children,
1918
2175
  Column,
1919
- Element,
2176
+ Element: Element$1,
1920
2177
  Empty,
1921
2178
  Row
1922
2179
  };
@@ -1945,14 +2202,6 @@ function FormEditor$1(props) {
1945
2202
  const onSubmit = useCallback(() => {}, []);
1946
2203
  const onReset = useCallback(() => {}, []);
1947
2204
 
1948
- // attach default palette
1949
- const hasDefaultPalette = defaultPalette(paletteConfig);
1950
- useEffect(() => {
1951
- if (hasDefaultPalette) {
1952
- palette.attachTo(paletteRef.current);
1953
- }
1954
- }, [palette, paletteRef, hasDefaultPalette]);
1955
-
1956
2205
  // attach default properties panel
1957
2206
  const hasDefaultPropertiesPanel = defaultPropertiesPanel(propertiesPanelConfig);
1958
2207
  useEffect(() => {
@@ -1960,31 +2209,38 @@ function FormEditor$1(props) {
1960
2209
  propertiesPanel.attachTo(propertiesPanelRef.current);
1961
2210
  }
1962
2211
  }, [propertiesPanelRef, propertiesPanel, hasDefaultPropertiesPanel]);
1963
- return jsxs("div", {
2212
+ return jsx("div", {
1964
2213
  class: "fjs-form-editor",
1965
- children: [jsxs(DragAndDropContext$1.Provider, {
1966
- value: dragAndDropContext,
1967
- children: [hasDefaultPalette && jsx("div", {
1968
- class: "fjs-editor-palette-container",
1969
- ref: paletteRef
1970
- }), jsx("div", {
1971
- ref: formContainerRef,
1972
- class: "fjs-form-container",
1973
- children: jsx(FormContext.Provider, {
1974
- value: formContext,
1975
- children: jsx(FormRenderContext.Provider, {
1976
- value: formRenderContext,
1977
- children: jsx(FormComponent, {
1978
- onSubmit: onSubmit,
1979
- onReset: onReset
2214
+ children: jsxs(SlotFillRoot, {
2215
+ children: [jsxs(DragAndDropContext$1.Provider, {
2216
+ value: dragAndDropContext,
2217
+ children: [jsx(ModularSection, {
2218
+ rootClass: "fjs-palette-container",
2219
+ section: "palette",
2220
+ children: jsx(Palette, {})
2221
+ }), jsx("div", {
2222
+ ref: formContainerRef,
2223
+ class: "fjs-form-container",
2224
+ children: jsx(FormContext.Provider, {
2225
+ value: formContext,
2226
+ children: jsx(FormRenderContext.Provider, {
2227
+ value: formRenderContext,
2228
+ children: jsx(FormComponent, {
2229
+ onSubmit: onSubmit,
2230
+ onReset: onReset
2231
+ })
1980
2232
  })
1981
2233
  })
1982
- })
1983
- }), jsx(CreatePreview, {})]
1984
- }), hasDefaultPropertiesPanel && jsx("div", {
1985
- class: "fjs-editor-properties-container",
1986
- ref: propertiesPanelRef
1987
- })]
2234
+ }), jsx(CreatePreview, {})]
2235
+ }), hasDefaultPropertiesPanel && jsx("div", {
2236
+ class: "fjs-editor-properties-container",
2237
+ ref: propertiesPanelRef
2238
+ }), jsx(ModularSection, {
2239
+ rootClass: "fjs-render-injector-container",
2240
+ section: "renderInjector",
2241
+ children: jsx(InjectedRendersRoot, {})
2242
+ })]
2243
+ })
1988
2244
  });
1989
2245
  }
1990
2246
  function getFormFieldIndex(parent, formField) {
@@ -2051,15 +2307,12 @@ function CreatePreview(props) {
2051
2307
 
2052
2308
  // helper //////
2053
2309
 
2054
- function defaultPalette(paletteConfig) {
2055
- return !(paletteConfig && paletteConfig.parent);
2310
+ function findPaletteEntry(type) {
2311
+ return PALETTE_ENTRIES.find(entry => entry.type === type);
2056
2312
  }
2057
2313
  function defaultPropertiesPanel(propertiesPanelConfig) {
2058
2314
  return !(propertiesPanelConfig && propertiesPanelConfig.parent);
2059
2315
  }
2060
- function findPaletteEntry(type) {
2061
- return PALETTE_ENTRIES.find(entry => entry.type === type);
2062
- }
2063
2316
 
2064
2317
  class Renderer {
2065
2318
  constructor(renderConfig, eventBus, formEditor, injector) {
@@ -4201,69 +4454,80 @@ var SelectionModule = {
4201
4454
  selectionBehavior: ['type', SelectionBehavior]
4202
4455
  };
4203
4456
 
4204
- class PaletteRenderer {
4205
- constructor(paletteConfig, eventBus) {
4206
- const {
4207
- parent
4208
- } = paletteConfig || {};
4457
+ /**
4458
+ * Base class for sectionable UI modules.
4459
+ *
4460
+ * @property {EventBus} _eventBus - EventBus instance used for event handling.
4461
+ * @property {string} managerType - Type of the render manager. Used to form event names.
4462
+ *
4463
+ * @class SectionModuleBase
4464
+ */
4465
+ class SectionModuleBase {
4466
+ /**
4467
+ * Create a SectionModuleBase instance.
4468
+ *
4469
+ * @param {any} eventBus - The EventBus instance used for event handling.
4470
+ * @param {string} sectionKey - The type of render manager. Used to form event names.
4471
+ *
4472
+ * @constructor
4473
+ */
4474
+ constructor(eventBus, sectionKey) {
4209
4475
  this._eventBus = eventBus;
4210
- this._container = domify('<div class="fjs-palette-container"></div>');
4211
- if (parent) {
4212
- this.attachTo(parent);
4213
- }
4214
- this._eventBus.once('formEditor.rendered', 500, () => {
4215
- this._render();
4476
+ this._sectionKey = sectionKey;
4477
+ this._eventBus.on(`${this._sectionKey}.section.rendered`, () => {
4478
+ this.isSectionRendered = true;
4479
+ });
4480
+ this._eventBus.on(`${this._sectionKey}.section.destroyed`, () => {
4481
+ this.isSectionRendered = false;
4216
4482
  });
4217
4483
  }
4218
4484
 
4219
4485
  /**
4220
- * Attach the palette to a parent node.
4486
+ * Attach the managed section to a parent node.
4221
4487
  *
4222
- * @param {HTMLElement} container
4488
+ * @param {HTMLElement} container - The parent node to attach to.
4223
4489
  */
4224
4490
  attachTo(container) {
4225
- if (!container) {
4226
- throw new Error('container required');
4227
- }
4228
- if (typeof container === 'string') {
4229
- container = query(container);
4230
- }
4231
-
4232
- // (1) detach from old parent
4233
- this.detach();
4234
-
4235
- // (2) append to parent container
4236
- container.appendChild(this._container);
4237
-
4238
- // (3) notify interested parties
4239
- this._eventBus.fire('palette.attach');
4491
+ this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.attach`, {
4492
+ container
4493
+ }));
4240
4494
  }
4241
4495
 
4242
4496
  /**
4243
- * Detach the palette from its parent node.
4497
+ * Detach the managed section from its parent node.
4244
4498
  */
4245
4499
  detach() {
4246
- const parentNode = this._container.parentNode;
4247
- if (parentNode) {
4248
- parentNode.removeChild(this._container);
4249
- this._eventBus.fire('palette.detach');
4250
- }
4500
+ this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.detach`));
4251
4501
  }
4252
- _render() {
4253
- render(jsx(Palette, {}), this._container);
4254
- this._eventBus.fire('palette.rendered');
4502
+
4503
+ /**
4504
+ * Reset the managed section to its initial state.
4505
+ */
4506
+ reset() {
4507
+ this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.reset`));
4255
4508
  }
4256
- _destroy() {
4257
- if (this._container) {
4258
- render(null, this._container);
4259
- this._eventBus.fire('palette.destroyed');
4509
+
4510
+ /**
4511
+ * Circumvents timing issues.
4512
+ */
4513
+ _onceSectionRendered(callback) {
4514
+ if (this.isSectionRendered) {
4515
+ callback();
4516
+ } else {
4517
+ this._eventBus.once(`${this._sectionKey}.section.rendered`, callback);
4260
4518
  }
4261
4519
  }
4262
4520
  }
4263
- PaletteRenderer.$inject = ['config.palette', 'eventBus'];
4521
+
4522
+ let PaletteModule$1 = class PaletteModule extends SectionModuleBase {
4523
+ constructor(eventBus) {
4524
+ super(eventBus, 'palette');
4525
+ }
4526
+ };
4527
+ PaletteModule$1.$inject = ['eventBus'];
4264
4528
 
4265
4529
  var PaletteModule = {
4266
- palette: ['type', PaletteRenderer]
4530
+ palette: ['type', PaletteModule$1]
4267
4531
  };
4268
4532
 
4269
4533
  var ArrowIcon = function ArrowIcon(props) {
@@ -4461,7 +4725,7 @@ function useEvent(event, callback, eventBus) {
4461
4725
  eventBus
4462
4726
  } = eventContext);
4463
4727
  }
4464
- const didMount = useRef(false);
4728
+ const didMount = useRef$1(false);
4465
4729
 
4466
4730
  // (1) subscribe immediately
4467
4731
  if (eventBus && !didMount.current) {
@@ -4517,7 +4781,7 @@ function useLayoutState(path, defaultValue) {
4517
4781
  */
4518
4782
 
4519
4783
  function usePrevious(value) {
4520
- const ref = useRef();
4784
+ const ref = useRef$1();
4521
4785
  useEffect(() => {
4522
4786
  ref.current = value;
4523
4787
  });
@@ -4535,8 +4799,8 @@ function useShowEntryEvent(id) {
4535
4799
  const {
4536
4800
  onShow
4537
4801
  } = useContext(LayoutContext);
4538
- const ref = useRef();
4539
- const focus = useRef(false);
4802
+ const ref = useRef$1();
4803
+ const focus = useRef$1(false);
4540
4804
  const onShowEntry = useCallback(event => {
4541
4805
  if (event.id === id) {
4542
4806
  onShow();
@@ -4642,7 +4906,7 @@ function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky)
4642
4906
  * @returns {Function} static function reference
4643
4907
  */
4644
4908
  function useStaticCallback(callback) {
4645
- const callbackRef = useRef(callback);
4909
+ const callbackRef = useRef$1(callback);
4646
4910
  callbackRef.current = callback;
4647
4911
  return useCallback((...args) => callbackRef.current(...args), []);
4648
4912
  }
@@ -4654,7 +4918,7 @@ function Group(props) {
4654
4918
  label,
4655
4919
  shouldOpen = false
4656
4920
  } = props;
4657
- const groupRef = useRef(null);
4921
+ const groupRef = useRef$1(null);
4658
4922
  const [open, setOpen] = useLayoutState(['groups', id, 'open'], shouldOpen);
4659
4923
  const onShow = useCallback(() => setOpen(true), [setOpen]);
4660
4924
  const toggleOpen = () => setOpen(!open);
@@ -4971,7 +5235,7 @@ function createDescriptionContext(overrides = {}) {
4971
5235
  * @param {Array} deps
4972
5236
  */
4973
5237
  function useUpdateLayoutEffect(effect, deps) {
4974
- const isMounted = useRef(false);
5238
+ const isMounted = useRef$1(false);
4975
5239
  useLayoutEffect(() => {
4976
5240
  if (isMounted.current) {
4977
5241
  return effect();
@@ -5090,7 +5354,7 @@ function ListGroup(props) {
5090
5354
  shouldOpen = true,
5091
5355
  shouldSort = true
5092
5356
  } = props;
5093
- const groupRef = useRef(null);
5357
+ const groupRef = useRef$1(null);
5094
5358
  const [open, setOpen] = useLayoutState(['groups', id, 'open'], false);
5095
5359
  const [sticky, setSticky] = useState(false);
5096
5360
  const onShow = useCallback(() => setOpen(true), [setOpen]);
@@ -5419,7 +5683,7 @@ const CodeEditor$1 = forwardRef((props, ref) => {
5419
5683
  hostLanguage = null,
5420
5684
  singleLine = false
5421
5685
  } = props;
5422
- const inputRef = useRef();
5686
+ const inputRef = useRef$1();
5423
5687
  const [editor, setEditor] = useState();
5424
5688
  const [localValue, setLocalValue] = useState(value || '');
5425
5689
  useBufferedFocus$1(editor, ref);
@@ -5498,7 +5762,7 @@ const CodeEditor = forwardRef((props, ref) => {
5498
5762
  tooltipContainer,
5499
5763
  variables
5500
5764
  } = props;
5501
- const inputRef = useRef();
5765
+ const inputRef = useRef$1();
5502
5766
  const [editor, setEditor] = useState();
5503
5767
  const [localValue, setLocalValue] = useState(value || '');
5504
5768
  useBufferedFocus(editor, ref);
@@ -5727,85 +5991,244 @@ function isEdited$7(node) {
5727
5991
  function prefixId$6(id) {
5728
5992
  return `bio-properties-panel-${id}`;
5729
5993
  }
5730
- const noop$1 = () => {};
5731
- function FeelTextfield(props) {
5994
+ function NumberField(props) {
5732
5995
  const {
5733
5996
  debounce,
5997
+ disabled,
5998
+ displayLabel = true,
5734
5999
  id,
6000
+ inputRef,
5735
6001
  label,
6002
+ max,
6003
+ min,
5736
6004
  onInput,
5737
- onError,
5738
- feel,
6005
+ step,
5739
6006
  value = '',
5740
- disabled = false,
5741
- variables,
5742
- tooltipContainer,
5743
- OptionalComponent = OptionalFeelInput
6007
+ onFocus,
6008
+ onBlur
5744
6009
  } = props;
5745
- const [localValue, _setLocalValue] = useState(value);
5746
- const editorRef = useShowEntryEvent(id);
5747
- const containerRef = useRef();
5748
- const feelActive = isString(localValue) && localValue.startsWith('=') || feel === 'required';
5749
- const feelOnlyValue = isString(localValue) && localValue.startsWith('=') ? localValue.substring(1) : localValue;
5750
- const [focus, _setFocus] = useState(undefined);
5751
- const setFocus = (offset = 0) => {
5752
- const hasFocus = containerRef.current.contains(document.activeElement);
5753
-
5754
- // Keep caret position if it is already focused, otherwise focus at the end
5755
- const position = hasFocus ? document.activeElement.selectionStart : Infinity;
5756
- _setFocus(position + offset);
5757
- };
6010
+ const [localValue, setLocalValue] = useState(value);
5758
6011
  const handleInputCallback = useMemo(() => {
5759
- return debounce(newValue => {
5760
- onInput(newValue);
6012
+ return debounce(event => {
6013
+ const {
6014
+ validity,
6015
+ value
6016
+ } = event.target;
6017
+ if (validity.valid) {
6018
+ onInput(value ? parseFloat(value) : undefined);
6019
+ }
5761
6020
  });
5762
6021
  }, [onInput, debounce]);
5763
- const setLocalValue = newValue => {
5764
- _setLocalValue(newValue);
5765
- if (!newValue || newValue === '=') {
5766
- handleInputCallback(undefined);
5767
- } else {
5768
- handleInputCallback(newValue);
5769
- }
5770
- };
5771
- const handleFeelToggle = useStaticCallback(() => {
5772
- if (feel === 'required') {
5773
- return;
5774
- }
5775
- if (!feelActive) {
5776
- setLocalValue('=' + localValue);
5777
- } else {
5778
- setLocalValue(feelOnlyValue);
5779
- }
5780
- });
5781
- const handleLocalInput = newValue => {
5782
- if (feelActive) {
5783
- newValue = '=' + newValue;
5784
- }
5785
- if (newValue === localValue) {
5786
- return;
5787
- }
5788
- setLocalValue(newValue);
5789
- if (!feelActive && isString(newValue) && newValue.startsWith('=')) {
5790
- // focus is behind `=` sign that will be removed
5791
- setFocus(-1);
5792
- }
6022
+ const handleInput = e => {
6023
+ handleInputCallback(e);
6024
+ setLocalValue(e.target.value);
5793
6025
  };
5794
- const handleLint = useStaticCallback(lint => {
5795
- if (!(lint && lint.length)) {
5796
- onError(undefined);
5797
- return;
5798
- }
5799
- const error = lint[0];
5800
- const message = `${error.source}: ${error.message}`;
5801
- onError(message);
5802
- });
5803
- useEffect(() => {
5804
- if (typeof focus !== 'undefined') {
5805
- editorRef.current.focus(focus);
5806
- _setFocus(undefined);
5807
- }
5808
- }, [focus]);
6026
+ useEffect(() => {
6027
+ if (value === localValue) {
6028
+ return;
6029
+ }
6030
+ setLocalValue(value);
6031
+ }, [value]);
6032
+ return jsxs("div", {
6033
+ class: "bio-properties-panel-numberfield",
6034
+ children: [displayLabel && jsx("label", {
6035
+ for: prefixId$5(id),
6036
+ class: "bio-properties-panel-label",
6037
+ children: label
6038
+ }), jsx("input", {
6039
+ id: prefixId$5(id),
6040
+ ref: inputRef,
6041
+ type: "number",
6042
+ name: id,
6043
+ spellCheck: "false",
6044
+ autoComplete: "off",
6045
+ disabled: disabled,
6046
+ class: "bio-properties-panel-input",
6047
+ max: max,
6048
+ min: min,
6049
+ onInput: handleInput,
6050
+ onFocus: onFocus,
6051
+ onBlur: onBlur,
6052
+ step: step,
6053
+ value: localValue
6054
+ })]
6055
+ });
6056
+ }
6057
+
6058
+ /**
6059
+ * @param {Object} props
6060
+ * @param {Boolean} props.debounce
6061
+ * @param {String} props.description
6062
+ * @param {Boolean} props.disabled
6063
+ * @param {Object} props.element
6064
+ * @param {Function} props.getValue
6065
+ * @param {String} props.id
6066
+ * @param {String} props.label
6067
+ * @param {String} props.max
6068
+ * @param {String} props.min
6069
+ * @param {Function} props.setValue
6070
+ * @param {Function} props.onFocus
6071
+ * @param {Function} props.onBlur
6072
+ * @param {String} props.step
6073
+ * @param {Function} props.validate
6074
+ */
6075
+ function NumberFieldEntry(props) {
6076
+ const {
6077
+ debounce,
6078
+ description,
6079
+ disabled,
6080
+ element,
6081
+ getValue,
6082
+ id,
6083
+ label,
6084
+ max,
6085
+ min,
6086
+ setValue,
6087
+ step,
6088
+ onFocus,
6089
+ onBlur,
6090
+ validate
6091
+ } = props;
6092
+ const [cachedInvalidValue, setCachedInvalidValue] = useState(null);
6093
+ const globalError = useError(id);
6094
+ const [localError, setLocalError] = useState(null);
6095
+ let value = getValue(element);
6096
+ const previousValue = usePrevious(value);
6097
+ useEffect(() => {
6098
+ if (isFunction(validate)) {
6099
+ const newValidationError = validate(value) || null;
6100
+ setLocalError(newValidationError);
6101
+ }
6102
+ }, [value]);
6103
+ const onInput = newValue => {
6104
+ let newValidationError = null;
6105
+ if (isFunction(validate)) {
6106
+ newValidationError = validate(newValue) || null;
6107
+ }
6108
+ if (newValidationError) {
6109
+ setCachedInvalidValue(newValue);
6110
+ } else {
6111
+ setValue(newValue);
6112
+ }
6113
+ setLocalError(newValidationError);
6114
+ };
6115
+ if (previousValue === value && localError) {
6116
+ value = cachedInvalidValue;
6117
+ }
6118
+ const error = globalError || localError;
6119
+ return jsxs("div", {
6120
+ class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
6121
+ "data-entry-id": id,
6122
+ children: [jsx(NumberField, {
6123
+ debounce: debounce,
6124
+ disabled: disabled,
6125
+ id: id,
6126
+ label: label,
6127
+ onFocus: onFocus,
6128
+ onBlur: onBlur,
6129
+ onInput: onInput,
6130
+ max: max,
6131
+ min: min,
6132
+ step: step,
6133
+ value: value
6134
+ }, element), error && jsx("div", {
6135
+ class: "bio-properties-panel-error",
6136
+ children: error
6137
+ }), jsx(Description$1, {
6138
+ forId: id,
6139
+ element: element,
6140
+ value: description
6141
+ })]
6142
+ });
6143
+ }
6144
+ function isEdited$6(node) {
6145
+ return node && !!node.value;
6146
+ }
6147
+
6148
+ // helpers /////////////////
6149
+
6150
+ function prefixId$5(id) {
6151
+ return `bio-properties-panel-${id}`;
6152
+ }
6153
+ const noop$1 = () => {};
6154
+ function FeelTextfield(props) {
6155
+ const {
6156
+ debounce,
6157
+ id,
6158
+ label,
6159
+ onInput,
6160
+ onError,
6161
+ feel,
6162
+ value = '',
6163
+ disabled = false,
6164
+ variables,
6165
+ tooltipContainer,
6166
+ OptionalComponent = OptionalFeelInput
6167
+ } = props;
6168
+ const [localValue, _setLocalValue] = useState(value);
6169
+ const editorRef = useShowEntryEvent(id);
6170
+ const containerRef = useRef$1();
6171
+ const feelActive = isString(localValue) && localValue.startsWith('=') || feel === 'required';
6172
+ const feelOnlyValue = isString(localValue) && localValue.startsWith('=') ? localValue.substring(1) : localValue;
6173
+ const [focus, _setFocus] = useState(undefined);
6174
+ const setFocus = (offset = 0) => {
6175
+ const hasFocus = containerRef.current.contains(document.activeElement);
6176
+
6177
+ // Keep caret position if it is already focused, otherwise focus at the end
6178
+ const position = hasFocus ? document.activeElement.selectionStart : Infinity;
6179
+ _setFocus(position + offset);
6180
+ };
6181
+ const handleInputCallback = useMemo(() => {
6182
+ return debounce(newValue => {
6183
+ onInput(newValue);
6184
+ });
6185
+ }, [onInput, debounce]);
6186
+ const setLocalValue = newValue => {
6187
+ _setLocalValue(newValue);
6188
+ if (!newValue || newValue === '=') {
6189
+ handleInputCallback(undefined);
6190
+ } else {
6191
+ handleInputCallback(newValue);
6192
+ }
6193
+ };
6194
+ const handleFeelToggle = useStaticCallback(() => {
6195
+ if (feel === 'required') {
6196
+ return;
6197
+ }
6198
+ if (!feelActive) {
6199
+ setLocalValue('=' + localValue);
6200
+ } else {
6201
+ setLocalValue(feelOnlyValue);
6202
+ }
6203
+ });
6204
+ const handleLocalInput = newValue => {
6205
+ if (feelActive) {
6206
+ newValue = '=' + newValue;
6207
+ }
6208
+ if (newValue === localValue) {
6209
+ return;
6210
+ }
6211
+ setLocalValue(newValue);
6212
+ if (!feelActive && isString(newValue) && newValue.startsWith('=')) {
6213
+ // focus is behind `=` sign that will be removed
6214
+ setFocus(-1);
6215
+ }
6216
+ };
6217
+ const handleLint = useStaticCallback(lint => {
6218
+ if (!(lint && lint.length)) {
6219
+ onError(undefined);
6220
+ return;
6221
+ }
6222
+ const error = lint[0];
6223
+ const message = `${error.source}: ${error.message}`;
6224
+ onError(message);
6225
+ });
6226
+ useEffect(() => {
6227
+ if (typeof focus !== 'undefined') {
6228
+ editorRef.current.focus(focus);
6229
+ _setFocus(undefined);
6230
+ }
6231
+ }, [focus]);
5809
6232
  useEffect(() => {
5810
6233
  if (value === localValue) {
5811
6234
  return;
@@ -5853,7 +6276,7 @@ function FeelTextfield(props) {
5853
6276
  'feel-active': feelActive
5854
6277
  }),
5855
6278
  children: [jsxs("label", {
5856
- for: prefixId$5(id),
6279
+ for: prefixId$4(id),
5857
6280
  class: "bio-properties-panel-label",
5858
6281
  onClick: () => setFocus(),
5859
6282
  children: [label, jsx(FeelIcon, {
@@ -5870,7 +6293,7 @@ function FeelTextfield(props) {
5870
6293
  disabled: feel !== 'optional' || disabled,
5871
6294
  onClick: handleFeelToggle
5872
6295
  }), feelActive ? jsx(CodeEditor, {
5873
- id: prefixId$5(id),
6296
+ id: prefixId$4(id),
5874
6297
  name: id,
5875
6298
  onInput: handleLocalInput,
5876
6299
  disabled: disabled,
@@ -5887,7 +6310,7 @@ function FeelTextfield(props) {
5887
6310
  ...props,
5888
6311
  onInput: handleLocalInput,
5889
6312
  contentAttributes: {
5890
- 'id': prefixId$5(id),
6313
+ 'id': prefixId$4(id),
5891
6314
  'aria-label': label
5892
6315
  },
5893
6316
  value: localValue,
@@ -5905,7 +6328,7 @@ const OptionalFeelInput = forwardRef((props, ref) => {
5905
6328
  onFocus,
5906
6329
  onBlur
5907
6330
  } = props;
5908
- const inputRef = useRef();
6331
+ const inputRef = useRef$1();
5909
6332
 
5910
6333
  // To be consistent with the FEEL editor, set focus at start of input
5911
6334
  // this ensures clean editing experience when switching with the keyboard
@@ -5925,7 +6348,7 @@ const OptionalFeelInput = forwardRef((props, ref) => {
5925
6348
  }
5926
6349
  };
5927
6350
  return jsx("input", {
5928
- id: prefixId$5(id),
6351
+ id: prefixId$4(id),
5929
6352
  type: "text",
5930
6353
  ref: inputRef,
5931
6354
  name: id,
@@ -5939,6 +6362,53 @@ const OptionalFeelInput = forwardRef((props, ref) => {
5939
6362
  value: value || ''
5940
6363
  });
5941
6364
  });
6365
+ const OptionalFeelNumberField = forwardRef((props, ref) => {
6366
+ const {
6367
+ id,
6368
+ debounce,
6369
+ disabled,
6370
+ onInput,
6371
+ value,
6372
+ min,
6373
+ max,
6374
+ step,
6375
+ onFocus,
6376
+ onBlur
6377
+ } = props;
6378
+ const inputRef = useRef$1();
6379
+
6380
+ // To be consistent with the FEEL editor, set focus at start of input
6381
+ // this ensures clean editing experience when switching with the keyboard
6382
+ ref.current = {
6383
+ focus: position => {
6384
+ const input = inputRef.current;
6385
+ if (!input) {
6386
+ return;
6387
+ }
6388
+ input.focus();
6389
+ if (typeof position === 'number' && position !== Infinity) {
6390
+ if (position > value.length) {
6391
+ position = value.length;
6392
+ }
6393
+ input.setSelectionRange(position, position);
6394
+ }
6395
+ }
6396
+ };
6397
+ return jsx(NumberField, {
6398
+ id: id,
6399
+ debounce: debounce,
6400
+ disabled: disabled,
6401
+ displayLabel: false,
6402
+ inputRef: inputRef,
6403
+ max: max,
6404
+ min: min,
6405
+ onInput: onInput,
6406
+ step: step,
6407
+ value: value,
6408
+ onFocus: onFocus,
6409
+ onBlur: onBlur
6410
+ });
6411
+ });
5942
6412
  forwardRef((props, ref) => {
5943
6413
  const {
5944
6414
  id,
@@ -5948,7 +6418,7 @@ forwardRef((props, ref) => {
5948
6418
  onFocus,
5949
6419
  onBlur
5950
6420
  } = props;
5951
- const inputRef = useRef();
6421
+ const inputRef = useRef$1();
5952
6422
 
5953
6423
  // To be consistent with the FEEL editor, set focus at start of input
5954
6424
  // this ensures clean editing experience when switching with the keyboard
@@ -5963,7 +6433,7 @@ forwardRef((props, ref) => {
5963
6433
  }
5964
6434
  };
5965
6435
  return jsx("textarea", {
5966
- id: prefixId$5(id),
6436
+ id: prefixId$4(id),
5967
6437
  type: "text",
5968
6438
  ref: inputRef,
5969
6439
  name: id,
@@ -5987,7 +6457,7 @@ const OptionalFeelToggleSwitch = forwardRef((props, ref) => {
5987
6457
  onBlur,
5988
6458
  switcherLabel
5989
6459
  } = props;
5990
- const inputRef = useRef();
6460
+ const inputRef = useRef$1();
5991
6461
 
5992
6462
  // To be consistent with the FEEL editor, set focus at start of input
5993
6463
  // this ensures clean editing experience when switching with the keyboard
@@ -6019,7 +6489,7 @@ forwardRef((props, ref) => {
6019
6489
  onFocus,
6020
6490
  onBlur
6021
6491
  } = props;
6022
- const inputRef = useRef();
6492
+ const inputRef = useRef$1();
6023
6493
  const handleChange = ({
6024
6494
  target
6025
6495
  }) => {
@@ -6039,7 +6509,7 @@ forwardRef((props, ref) => {
6039
6509
  };
6040
6510
  return jsx("input", {
6041
6511
  ref: inputRef,
6042
- id: prefixId$5(id),
6512
+ id: prefixId$4(id),
6043
6513
  name: id,
6044
6514
  onFocus: onFocus,
6045
6515
  onBlur: onBlur,
@@ -6079,261 +6549,30 @@ function FeelEntry(props) {
6079
6549
  disabled,
6080
6550
  feel,
6081
6551
  label,
6082
- getValue,
6083
- setValue,
6084
- tooltipContainer,
6085
- hostLanguage,
6086
- singleLine,
6087
- validate,
6088
- show = noop$1,
6089
- example,
6090
- variables,
6091
- onFocus,
6092
- onBlur
6093
- } = props;
6094
- const [cachedInvalidValue, setCachedInvalidValue] = useState(null);
6095
- const [validationError, setValidationError] = useState(null);
6096
- const [localError, setLocalError] = useState(null);
6097
- let value = getValue(element);
6098
- const previousValue = usePrevious(value);
6099
- useEffect(() => {
6100
- if (isFunction(validate)) {
6101
- const newValidationError = validate(value) || null;
6102
- setValidationError(newValidationError);
6103
- }
6104
- }, [value]);
6105
- const onInput = useStaticCallback(newValue => {
6106
- let newValidationError = null;
6107
- if (isFunction(validate)) {
6108
- newValidationError = validate(newValue) || null;
6109
- }
6110
- if (newValidationError) {
6111
- setCachedInvalidValue(newValue);
6112
- } else {
6113
- // don't create multiple commandStack entries for the same value
6114
- if (newValue !== value) {
6115
- setValue(newValue);
6116
- }
6117
- }
6118
- setValidationError(newValidationError);
6119
- });
6120
- const onError = useCallback(err => {
6121
- setLocalError(err);
6122
- }, []);
6123
- if (previousValue === value && validationError) {
6124
- value = cachedInvalidValue;
6125
- }
6126
- const temporaryError = useError(id);
6127
- const error = localError || temporaryError || validationError;
6128
- return jsxs("div", {
6129
- class: classnames(props.class, 'bio-properties-panel-entry', error ? 'has-error' : ''),
6130
- "data-entry-id": id,
6131
- children: [jsx(FeelTextfield, {
6132
- debounce: debounce,
6133
- disabled: disabled,
6134
- feel: feel,
6135
- id: id,
6136
- label: label,
6137
- onInput: onInput,
6138
- onError: onError,
6139
- onFocus: onFocus,
6140
- onBlur: onBlur,
6141
- example: example,
6142
- hostLanguage: hostLanguage,
6143
- singleLine: singleLine,
6144
- show: show,
6145
- value: value,
6146
- variables: variables,
6147
- tooltipContainer: tooltipContainer,
6148
- OptionalComponent: props.OptionalComponent
6149
- }, element), error && jsx("div", {
6150
- class: "bio-properties-panel-error",
6151
- children: error
6152
- }), jsx(Description$1, {
6153
- forId: id,
6154
- element: element,
6155
- value: description
6156
- })]
6157
- });
6158
- }
6159
-
6160
- /**
6161
- * @param {Object} props
6162
- * @param {Object} props.element
6163
- * @param {String} props.id
6164
- * @param {String} props.description
6165
- * @param {Boolean} props.debounce
6166
- * @param {Boolean} props.disabled
6167
- * @param {Boolean} props.feel
6168
- * @param {String} props.label
6169
- * @param {Function} props.getValue
6170
- * @param {Function} props.setValue
6171
- * @param {Function} props.tooltipContainer
6172
- * @param {Function} props.validate
6173
- * @param {Function} props.show
6174
- * @param {Function} props.example
6175
- * @param {Function} props.variables
6176
- * @param {Function} props.onFocus
6177
- * @param {Function} props.onBlur
6178
- */
6179
- function FeelToggleSwitchEntry(props) {
6180
- return jsx(FeelEntry, {
6181
- class: "bio-properties-panel-feel-toggle-switch",
6182
- OptionalComponent: OptionalFeelToggleSwitch,
6183
- ...props
6184
- });
6185
- }
6186
-
6187
- /**
6188
- * @param {Object} props
6189
- * @param {Object} props.element
6190
- * @param {String} props.id
6191
- * @param {String} props.description
6192
- * @param {String} props.hostLanguage
6193
- * @param {Boolean} props.singleLine
6194
- * @param {Boolean} props.debounce
6195
- * @param {Boolean} props.disabled
6196
- * @param {Boolean} props.feel
6197
- * @param {String} props.label
6198
- * @param {Function} props.getValue
6199
- * @param {Function} props.setValue
6200
- * @param {Function} props.tooltipContainer
6201
- * @param {Function} props.validate
6202
- * @param {Function} props.show
6203
- * @param {Function} props.example
6204
- * @param {Function} props.variables
6205
- * @param {Function} props.onFocus
6206
- * @param {Function} props.onBlur
6207
- */
6208
- function FeelTemplatingEntry(props) {
6209
- return jsx(FeelEntry, {
6210
- class: "bio-properties-panel-feel-templating",
6211
- OptionalComponent: CodeEditor$1,
6212
- ...props
6213
- });
6214
- }
6215
- function isEdited$6(node) {
6216
- if (!node) {
6217
- return false;
6218
- }
6219
- if (node.type === 'checkbox') {
6220
- return !!node.checked || node.classList.contains('edited');
6221
- }
6222
- return !!node.value || node.classList.contains('edited');
6223
- }
6224
-
6225
- // helpers /////////////////
6226
-
6227
- function prefixId$5(id) {
6228
- return `bio-properties-panel-${id}`;
6229
- }
6230
- function NumberField(props) {
6231
- const {
6232
- debounce,
6233
- disabled,
6234
- id,
6235
- label,
6236
- max,
6237
- min,
6238
- onInput,
6239
- step,
6240
- value = '',
6241
- onFocus,
6242
- onBlur
6243
- } = props;
6244
- const [localValue, setLocalValue] = useState(value);
6245
- const handleInputCallback = useMemo(() => {
6246
- return debounce(event => {
6247
- const {
6248
- validity,
6249
- value
6250
- } = event.target;
6251
- if (validity.valid) {
6252
- onInput(value ? parseFloat(value) : undefined);
6253
- }
6254
- });
6255
- }, [onInput, debounce]);
6256
- const handleInput = e => {
6257
- handleInputCallback(e);
6258
- setLocalValue(e.target.value);
6259
- };
6260
- useEffect(() => {
6261
- if (value === localValue) {
6262
- return;
6263
- }
6264
- setLocalValue(value);
6265
- }, [value]);
6266
- return jsxs("div", {
6267
- class: "bio-properties-panel-numberfield",
6268
- children: [jsx("label", {
6269
- for: prefixId$4(id),
6270
- class: "bio-properties-panel-label",
6271
- children: label
6272
- }), jsx("input", {
6273
- id: prefixId$4(id),
6274
- type: "number",
6275
- name: id,
6276
- spellCheck: "false",
6277
- autoComplete: "off",
6278
- disabled: disabled,
6279
- class: "bio-properties-panel-input",
6280
- max: max,
6281
- min: min,
6282
- onInput: handleInput,
6283
- onFocus: onFocus,
6284
- onBlur: onBlur,
6285
- step: step,
6286
- value: localValue
6287
- })]
6288
- });
6289
- }
6290
-
6291
- /**
6292
- * @param {Object} props
6293
- * @param {Boolean} props.debounce
6294
- * @param {String} props.description
6295
- * @param {Boolean} props.disabled
6296
- * @param {Object} props.element
6297
- * @param {Function} props.getValue
6298
- * @param {String} props.id
6299
- * @param {String} props.label
6300
- * @param {String} props.max
6301
- * @param {String} props.min
6302
- * @param {Function} props.setValue
6303
- * @param {Function} props.onFocus
6304
- * @param {Function} props.onBlur
6305
- * @param {String} props.step
6306
- * @param {Function} props.validate
6307
- */
6308
- function NumberFieldEntry(props) {
6309
- const {
6310
- debounce,
6311
- description,
6312
- disabled,
6313
- element,
6314
- getValue,
6315
- id,
6316
- label,
6317
- max,
6318
- min,
6552
+ getValue,
6319
6553
  setValue,
6320
- step,
6554
+ tooltipContainer,
6555
+ hostLanguage,
6556
+ singleLine,
6557
+ validate,
6558
+ show = noop$1,
6559
+ example,
6560
+ variables,
6321
6561
  onFocus,
6322
- onBlur,
6323
- validate
6562
+ onBlur
6324
6563
  } = props;
6325
6564
  const [cachedInvalidValue, setCachedInvalidValue] = useState(null);
6326
- const globalError = useError(id);
6565
+ const [validationError, setValidationError] = useState(null);
6327
6566
  const [localError, setLocalError] = useState(null);
6328
6567
  let value = getValue(element);
6329
6568
  const previousValue = usePrevious(value);
6330
6569
  useEffect(() => {
6331
6570
  if (isFunction(validate)) {
6332
6571
  const newValidationError = validate(value) || null;
6333
- setLocalError(newValidationError);
6572
+ setValidationError(newValidationError);
6334
6573
  }
6335
6574
  }, [value]);
6336
- const onInput = newValue => {
6575
+ const onInput = useStaticCallback(newValue => {
6337
6576
  let newValidationError = null;
6338
6577
  if (isFunction(validate)) {
6339
6578
  newValidationError = validate(newValue) || null;
@@ -6341,30 +6580,45 @@ function NumberFieldEntry(props) {
6341
6580
  if (newValidationError) {
6342
6581
  setCachedInvalidValue(newValue);
6343
6582
  } else {
6344
- setValue(newValue);
6583
+ // don't create multiple commandStack entries for the same value
6584
+ if (newValue !== value) {
6585
+ setValue(newValue);
6586
+ }
6345
6587
  }
6346
- setLocalError(newValidationError);
6347
- };
6348
- if (previousValue === value && localError) {
6588
+ setValidationError(newValidationError);
6589
+ });
6590
+ const onError = useCallback(err => {
6591
+ setLocalError(err);
6592
+ }, []);
6593
+ if (previousValue === value && validationError) {
6349
6594
  value = cachedInvalidValue;
6350
6595
  }
6351
- const error = globalError || localError;
6596
+ const temporaryError = useError(id);
6597
+ const error = localError || temporaryError || validationError;
6352
6598
  return jsxs("div", {
6353
- class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
6599
+ class: classnames(props.class, 'bio-properties-panel-entry', error ? 'has-error' : ''),
6354
6600
  "data-entry-id": id,
6355
- children: [jsx(NumberField, {
6601
+ children: [createElement(FeelTextfield, {
6602
+ ...props,
6356
6603
  debounce: debounce,
6357
6604
  disabled: disabled,
6605
+ feel: feel,
6358
6606
  id: id,
6607
+ key: element,
6359
6608
  label: label,
6609
+ onInput: onInput,
6610
+ onError: onError,
6360
6611
  onFocus: onFocus,
6361
6612
  onBlur: onBlur,
6362
- onInput: onInput,
6363
- max: max,
6364
- min: min,
6365
- step: step,
6366
- value: value
6367
- }, element), error && jsx("div", {
6613
+ example: example,
6614
+ hostLanguage: hostLanguage,
6615
+ singleLine: singleLine,
6616
+ show: show,
6617
+ value: value,
6618
+ variables: variables,
6619
+ tooltipContainer: tooltipContainer,
6620
+ OptionalComponent: props.OptionalComponent
6621
+ }), error && jsx("div", {
6368
6622
  class: "bio-properties-panel-error",
6369
6623
  children: error
6370
6624
  }), jsx(Description$1, {
@@ -6374,8 +6628,100 @@ function NumberFieldEntry(props) {
6374
6628
  })]
6375
6629
  });
6376
6630
  }
6377
- function isEdited$4(node) {
6378
- return node && !!node.value;
6631
+
6632
+ /**
6633
+ * @param {Object} props
6634
+ * @param {Object} props.element
6635
+ * @param {String} props.id
6636
+ * @param {String} props.description
6637
+ * @param {Boolean} props.debounce
6638
+ * @param {Boolean} props.disabled
6639
+ * @param {String} props.max
6640
+ * @param {String} props.min
6641
+ * @param {String} props.step
6642
+ * @param {Boolean} props.feel
6643
+ * @param {String} props.label
6644
+ * @param {Function} props.getValue
6645
+ * @param {Function} props.setValue
6646
+ * @param {Function} props.tooltipContainer
6647
+ * @param {Function} props.validate
6648
+ * @param {Function} props.show
6649
+ * @param {Function} props.example
6650
+ * @param {Function} props.variables
6651
+ * @param {Function} props.onFocus
6652
+ * @param {Function} props.onBlur
6653
+ */
6654
+ function FeelNumberEntry(props) {
6655
+ return jsx(FeelEntry, {
6656
+ class: "bio-properties-panel-feel-number",
6657
+ OptionalComponent: OptionalFeelNumberField,
6658
+ ...props
6659
+ });
6660
+ }
6661
+
6662
+ /**
6663
+ * @param {Object} props
6664
+ * @param {Object} props.element
6665
+ * @param {String} props.id
6666
+ * @param {String} props.description
6667
+ * @param {Boolean} props.debounce
6668
+ * @param {Boolean} props.disabled
6669
+ * @param {Boolean} props.feel
6670
+ * @param {String} props.label
6671
+ * @param {Function} props.getValue
6672
+ * @param {Function} props.setValue
6673
+ * @param {Function} props.tooltipContainer
6674
+ * @param {Function} props.validate
6675
+ * @param {Function} props.show
6676
+ * @param {Function} props.example
6677
+ * @param {Function} props.variables
6678
+ * @param {Function} props.onFocus
6679
+ * @param {Function} props.onBlur
6680
+ */
6681
+ function FeelToggleSwitchEntry(props) {
6682
+ return jsx(FeelEntry, {
6683
+ class: "bio-properties-panel-feel-toggle-switch",
6684
+ OptionalComponent: OptionalFeelToggleSwitch,
6685
+ ...props
6686
+ });
6687
+ }
6688
+
6689
+ /**
6690
+ * @param {Object} props
6691
+ * @param {Object} props.element
6692
+ * @param {String} props.id
6693
+ * @param {String} props.description
6694
+ * @param {String} props.hostLanguage
6695
+ * @param {Boolean} props.singleLine
6696
+ * @param {Boolean} props.debounce
6697
+ * @param {Boolean} props.disabled
6698
+ * @param {Boolean} props.feel
6699
+ * @param {String} props.label
6700
+ * @param {Function} props.getValue
6701
+ * @param {Function} props.setValue
6702
+ * @param {Function} props.tooltipContainer
6703
+ * @param {Function} props.validate
6704
+ * @param {Function} props.show
6705
+ * @param {Function} props.example
6706
+ * @param {Function} props.variables
6707
+ * @param {Function} props.onFocus
6708
+ * @param {Function} props.onBlur
6709
+ */
6710
+ function FeelTemplatingEntry(props) {
6711
+ return jsx(FeelEntry, {
6712
+ class: "bio-properties-panel-feel-templating",
6713
+ OptionalComponent: CodeEditor$1,
6714
+ ...props
6715
+ });
6716
+ }
6717
+ function isEdited$5(node) {
6718
+ if (!node) {
6719
+ return false;
6720
+ }
6721
+ if (node.type === 'checkbox') {
6722
+ return !!node.checked || node.classList.contains('edited');
6723
+ }
6724
+ return !!node.value || node.classList.contains('edited');
6379
6725
  }
6380
6726
 
6381
6727
  // helpers /////////////////
@@ -7024,7 +7370,7 @@ function AltTextEntry(props) {
7024
7370
  component: AltText,
7025
7371
  editField: editField,
7026
7372
  field: field,
7027
- isEdited: isEdited$6
7373
+ isEdited: isEdited$5
7028
7374
  });
7029
7375
  }
7030
7376
  return entries;
@@ -7046,7 +7392,7 @@ function AltText(props) {
7046
7392
  const setValue = value => {
7047
7393
  return editField(field, path, value);
7048
7394
  };
7049
- return FeelEntry({
7395
+ return FeelTemplatingEntry({
7050
7396
  debounce,
7051
7397
  element: field,
7052
7398
  feel: 'optional',
@@ -7054,6 +7400,7 @@ function AltText(props) {
7054
7400
  id,
7055
7401
  label: 'Alternative text',
7056
7402
  setValue,
7403
+ singleLine: true,
7057
7404
  variables
7058
7405
  });
7059
7406
  }
@@ -7142,7 +7489,7 @@ function DescriptionEntry(props) {
7142
7489
  component: Description,
7143
7490
  editField: editField,
7144
7491
  field: field,
7145
- isEdited: isEdited
7492
+ isEdited: isEdited$5
7146
7493
  });
7147
7494
  }
7148
7495
  return entries;
@@ -7154,6 +7501,9 @@ function Description(props) {
7154
7501
  id
7155
7502
  } = props;
7156
7503
  const debounce = useService('debounce');
7504
+ const variables = useVariables().map(name => ({
7505
+ name
7506
+ }));
7157
7507
  const path = ['description'];
7158
7508
  const getValue = () => {
7159
7509
  return get(field, path, '');
@@ -7161,13 +7511,15 @@ function Description(props) {
7161
7511
  const setValue = value => {
7162
7512
  return editField(field, path, value);
7163
7513
  };
7164
- return TextfieldEntry({
7514
+ return FeelTemplatingEntry({
7165
7515
  debounce,
7166
7516
  element: field,
7167
7517
  getValue,
7168
7518
  id,
7169
7519
  label: 'Field description',
7170
- setValue
7520
+ singleLine: true,
7521
+ setValue,
7522
+ variables
7171
7523
  });
7172
7524
  }
7173
7525
 
@@ -7586,127 +7938,129 @@ function Key$1(props) {
7586
7938
  });
7587
7939
  }
7588
7940
 
7589
- function simpleStringEntryFactory(options) {
7590
- const {
7591
- id,
7592
- label,
7593
- path,
7594
- props
7595
- } = options;
7941
+ function LabelEntry(props) {
7596
7942
  const {
7597
- editField,
7598
- field
7599
- } = props;
7600
- return {
7601
- id,
7602
- label,
7603
- path,
7604
7943
  field,
7605
- editField,
7606
- component: SimpleStringComponent,
7607
- isEdited: isEdited
7608
- };
7944
+ editField
7945
+ } = props;
7946
+ const {
7947
+ type,
7948
+ subtype
7949
+ } = field;
7950
+ const entries = [];
7951
+ if (type === 'datetime') {
7952
+ if (subtype === DATETIME_SUBTYPES.DATE || subtype === DATETIME_SUBTYPES.DATETIME) {
7953
+ entries.push({
7954
+ id: 'date-label',
7955
+ component: DateLabel,
7956
+ editField,
7957
+ field,
7958
+ isEdited: isEdited$5
7959
+ });
7960
+ }
7961
+ if (subtype === DATETIME_SUBTYPES.TIME || subtype === DATETIME_SUBTYPES.DATETIME) {
7962
+ entries.push({
7963
+ id: 'time-label',
7964
+ component: TimeLabel,
7965
+ editField,
7966
+ field,
7967
+ isEdited: isEdited$5
7968
+ });
7969
+ }
7970
+ } else if (INPUTS.includes(type) || type === 'button') {
7971
+ entries.push({
7972
+ id: 'label',
7973
+ component: Label$1,
7974
+ editField,
7975
+ field,
7976
+ isEdited: isEdited$5
7977
+ });
7978
+ }
7979
+ return entries;
7609
7980
  }
7610
- const SimpleStringComponent = props => {
7981
+ function Label$1(props) {
7611
7982
  const {
7612
- id,
7613
- label,
7614
- path,
7983
+ editField,
7615
7984
  field,
7616
- editField
7985
+ id
7617
7986
  } = props;
7618
7987
  const debounce = useService('debounce');
7619
- const getValue = () => get(field, path, '');
7620
- const setValue = value => editField(field, path, value);
7621
- return TextfieldEntry({
7988
+ const variables = useVariables().map(name => ({
7989
+ name
7990
+ }));
7991
+ const path = ['label'];
7992
+ const getValue = () => {
7993
+ return get(field, path, '');
7994
+ };
7995
+ const setValue = value => {
7996
+ return editField(field, path, value);
7997
+ };
7998
+ return FeelTemplatingEntry({
7622
7999
  debounce,
7623
8000
  element: field,
7624
8001
  getValue,
7625
8002
  id,
7626
- label,
7627
- setValue
8003
+ label: 'Field label',
8004
+ singleLine: true,
8005
+ setValue,
8006
+ variables
7628
8007
  });
7629
- };
7630
-
7631
- function simpleBoolEntryFactory(options) {
7632
- const {
7633
- id,
7634
- label,
7635
- description,
7636
- path,
7637
- props
7638
- } = options;
8008
+ }
8009
+ function DateLabel(props) {
7639
8010
  const {
7640
8011
  editField,
7641
- field
7642
- } = props;
7643
- return {
7644
- id,
7645
- label,
7646
- path,
7647
8012
  field,
7648
- editField,
7649
- description,
7650
- component: SimpleBoolComponent,
7651
- isEdited: isEdited$8
8013
+ id
8014
+ } = props;
8015
+ const debounce = useService('debounce');
8016
+ const variables = useVariables().map(name => ({
8017
+ name
8018
+ }));
8019
+ const path = DATE_LABEL_PATH;
8020
+ const getValue = () => {
8021
+ return get(field, path, '');
7652
8022
  };
8023
+ const setValue = value => {
8024
+ return editField(field, path, value);
8025
+ };
8026
+ return FeelTemplatingEntry({
8027
+ debounce,
8028
+ element: field,
8029
+ getValue,
8030
+ id,
8031
+ label: 'Date label',
8032
+ singleLine: true,
8033
+ setValue,
8034
+ variables
8035
+ });
7653
8036
  }
7654
- const SimpleBoolComponent = props => {
8037
+ function TimeLabel(props) {
7655
8038
  const {
7656
- id,
7657
- label,
7658
- path,
7659
- field,
7660
8039
  editField,
7661
- description
8040
+ field,
8041
+ id
7662
8042
  } = props;
7663
- const getValue = () => get(field, path, '');
7664
- const setValue = value => editField(field, path, value);
7665
- return CheckboxEntry({
8043
+ const debounce = useService('debounce');
8044
+ const variables = useVariables().map(name => ({
8045
+ name
8046
+ }));
8047
+ const path = TIME_LABEL_PATH;
8048
+ const getValue = () => {
8049
+ return get(field, path, '');
8050
+ };
8051
+ const setValue = value => {
8052
+ return editField(field, path, value);
8053
+ };
8054
+ return FeelTemplatingEntry({
8055
+ debounce,
7666
8056
  element: field,
7667
8057
  getValue,
7668
8058
  id,
7669
- label,
8059
+ label: 'Time label',
8060
+ singleLine: true,
7670
8061
  setValue,
7671
- description
8062
+ variables
7672
8063
  });
7673
- };
7674
-
7675
- function LabelEntry(props) {
7676
- const {
7677
- field
7678
- } = props;
7679
- const {
7680
- type,
7681
- subtype
7682
- } = field;
7683
- const entries = [];
7684
- if (type === 'datetime') {
7685
- if (subtype === DATETIME_SUBTYPES.DATE || subtype === DATETIME_SUBTYPES.DATETIME) {
7686
- entries.push(simpleStringEntryFactory({
7687
- id: 'date-label',
7688
- path: DATE_LABEL_PATH,
7689
- label: 'Date label',
7690
- props
7691
- }));
7692
- }
7693
- if (subtype === DATETIME_SUBTYPES.TIME || subtype === DATETIME_SUBTYPES.DATETIME) {
7694
- entries.push(simpleStringEntryFactory({
7695
- id: 'time-label',
7696
- path: TIME_LABEL_PATH,
7697
- label: 'Time label',
7698
- props
7699
- }));
7700
- }
7701
- } else if (INPUTS.includes(type) || type === 'button') {
7702
- entries.push(simpleStringEntryFactory({
7703
- id: 'label',
7704
- path: ['label'],
7705
- label: 'Field label',
7706
- props
7707
- }));
7708
- }
7709
- return entries;
7710
8064
  }
7711
8065
 
7712
8066
  function SourceEntry(props) {
@@ -7724,7 +8078,7 @@ function SourceEntry(props) {
7724
8078
  component: Source,
7725
8079
  editField: editField,
7726
8080
  field: field,
7727
- isEdited: isEdited$6
8081
+ isEdited: isEdited$5
7728
8082
  });
7729
8083
  }
7730
8084
  return entries;
@@ -7746,7 +8100,7 @@ function Source(props) {
7746
8100
  const setValue = value => {
7747
8101
  return editField(field, path, value);
7748
8102
  };
7749
- return FeelEntry({
8103
+ return FeelTemplatingEntry({
7750
8104
  debounce,
7751
8105
  description: 'Expression or static value (link/data URI)',
7752
8106
  element: field,
@@ -7755,6 +8109,7 @@ function Source(props) {
7755
8109
  id,
7756
8110
  label: 'Image source',
7757
8111
  setValue,
8112
+ singleLine: true,
7758
8113
  variables
7759
8114
  });
7760
8115
  }
@@ -7779,7 +8134,7 @@ function TextEntry(props) {
7779
8134
  component: Text,
7780
8135
  editField: editField,
7781
8136
  field: field,
7782
- isEdited: isEdited$6
8137
+ isEdited: isEdited$5
7783
8138
  }];
7784
8139
 
7785
8140
  // todo: skipped to make the release without too much risk
@@ -7812,7 +8167,7 @@ function Text(props) {
7812
8167
  const setValue = value => {
7813
8168
  return editField(field, path, value);
7814
8169
  };
7815
- const description = useMemo(() => jsxs(Fragment, {
8170
+ const description = useMemo(() => jsxs(Fragment$1, {
7816
8171
  children: ["Supports markdown and templating. ", jsx("a", {
7817
8172
  href: "https://docs.camunda.io/docs/components/modeler/forms/form-element-library/forms-element-library-text/",
7818
8173
  target: "_blank",
@@ -7848,7 +8203,7 @@ function NumberEntries(props) {
7848
8203
  entries.push({
7849
8204
  id: id + '-decimalDigits',
7850
8205
  component: NumberDecimalDigits,
7851
- isEdited: isEdited$4,
8206
+ isEdited: isEdited$6,
7852
8207
  editField,
7853
8208
  field
7854
8209
  });
@@ -8215,6 +8570,50 @@ function TimeFormatSelect(props) {
8215
8570
  });
8216
8571
  }
8217
8572
 
8573
+ function simpleBoolEntryFactory(options) {
8574
+ const {
8575
+ id,
8576
+ label,
8577
+ description,
8578
+ path,
8579
+ props
8580
+ } = options;
8581
+ const {
8582
+ editField,
8583
+ field
8584
+ } = props;
8585
+ return {
8586
+ id,
8587
+ label,
8588
+ path,
8589
+ field,
8590
+ editField,
8591
+ description,
8592
+ component: SimpleBoolComponent,
8593
+ isEdited: isEdited$8
8594
+ };
8595
+ }
8596
+ const SimpleBoolComponent = props => {
8597
+ const {
8598
+ id,
8599
+ label,
8600
+ path,
8601
+ field,
8602
+ editField,
8603
+ description
8604
+ } = props;
8605
+ const getValue = () => get(field, path, '');
8606
+ const setValue = value => editField(field, path, value);
8607
+ return CheckboxEntry({
8608
+ element: field,
8609
+ getValue,
8610
+ id,
8611
+ label,
8612
+ setValue,
8613
+ description
8614
+ });
8615
+ };
8616
+
8218
8617
  function SelectEntries(props) {
8219
8618
  const {
8220
8619
  field
@@ -8435,6 +8834,34 @@ function updateKey(properties, oldKey, newKey) {
8435
8834
  }, {});
8436
8835
  }
8437
8836
 
8837
+ function AutoFocusSelectEntry(props) {
8838
+ const {
8839
+ autoFocusEntry,
8840
+ element,
8841
+ getValue
8842
+ } = props;
8843
+ const value = getValue(element);
8844
+ const prevValue = usePrevious(value);
8845
+ const eventBus = useService('eventBus');
8846
+
8847
+ // auto focus specifc other entry when selected value changed
8848
+ useEffect(() => {
8849
+ if (autoFocusEntry && prevValue && value !== prevValue) {
8850
+ // @Note(pinussilvestrus): There is an issue in the properties
8851
+ // panel so we have to wait a bit before showing the entry.
8852
+ // Cf. https://github.com/camunda/linting/blob/4f5328e2722f73ae60ae584c5f576eaec3999cb2/lib/modeler/Linting.js#L37
8853
+ setTimeout(() => {
8854
+ eventBus.fire('propertiesPanel.showEntry', {
8855
+ id: autoFocusEntry
8856
+ });
8857
+ });
8858
+ }
8859
+ }, [value, autoFocusEntry, prevValue, eventBus]);
8860
+ return jsx(SelectEntry, {
8861
+ ...props
8862
+ });
8863
+ }
8864
+
8438
8865
  function ValuesSourceSelectEntry(props) {
8439
8866
  const {
8440
8867
  editField,
@@ -8473,7 +8900,8 @@ function ValuesSourceSelect(props) {
8473
8900
  value: valueSource
8474
8901
  }));
8475
8902
  };
8476
- return SelectEntry({
8903
+ return AutoFocusSelectEntry({
8904
+ autoFocusEntry: getAutoFocusEntryId(field),
8477
8905
  label: 'Type',
8478
8906
  element: field,
8479
8907
  getOptions: getValuesSourceOptions,
@@ -8483,6 +8911,20 @@ function ValuesSourceSelect(props) {
8483
8911
  });
8484
8912
  }
8485
8913
 
8914
+ // helpers //////////
8915
+
8916
+ function getAutoFocusEntryId(field) {
8917
+ const valuesSource = getValuesSource(field);
8918
+ if (valuesSource === VALUES_SOURCES.EXPRESSION) {
8919
+ return `${field.id}-valuesExpression-expression`;
8920
+ } else if (valuesSource === VALUES_SOURCES.INPUT) {
8921
+ return `${field.id}-dynamicValues-key`;
8922
+ } else if (valuesSource === VALUES_SOURCES.STATIC) {
8923
+ return `${field.id}-staticValues-0-label`;
8924
+ }
8925
+ return null;
8926
+ }
8927
+
8486
8928
  function InputKeyValuesSourceEntry(props) {
8487
8929
  const {
8488
8930
  editField,
@@ -8639,7 +9081,7 @@ function AdornerEntry(props) {
8639
9081
  entries.push({
8640
9082
  id: 'prefix-adorner',
8641
9083
  component: PrefixAdorner,
8642
- isEdited: isEdited,
9084
+ isEdited: isEdited$5,
8643
9085
  editField,
8644
9086
  field,
8645
9087
  onChange,
@@ -8648,7 +9090,7 @@ function AdornerEntry(props) {
8648
9090
  entries.push({
8649
9091
  id: 'suffix-adorner',
8650
9092
  component: SuffixAdorner,
8651
- isEdited: isEdited,
9093
+ isEdited: isEdited$5,
8652
9094
  editField,
8653
9095
  field,
8654
9096
  onChange,
@@ -8665,13 +9107,19 @@ function PrefixAdorner(props) {
8665
9107
  getValue
8666
9108
  } = props;
8667
9109
  const debounce = useService('debounce');
8668
- return TextfieldEntry({
9110
+ const variables = useVariables().map(name => ({
9111
+ name
9112
+ }));
9113
+ return FeelTemplatingEntry({
8669
9114
  debounce,
8670
9115
  element: field,
9116
+ feel: 'optional',
8671
9117
  getValue: getValue('prefixAdorner'),
8672
9118
  id,
8673
9119
  label: 'Prefix',
8674
- setValue: onChange('prefixAdorner')
9120
+ setValue: onChange('prefixAdorner'),
9121
+ singleLine: true,
9122
+ variables
8675
9123
  });
8676
9124
  }
8677
9125
  function SuffixAdorner(props) {
@@ -8682,13 +9130,18 @@ function SuffixAdorner(props) {
8682
9130
  getValue
8683
9131
  } = props;
8684
9132
  const debounce = useService('debounce');
8685
- return TextfieldEntry({
9133
+ const variables = useVariables().map(name => ({
9134
+ name
9135
+ }));
9136
+ return FeelTemplatingEntry({
8686
9137
  debounce,
8687
9138
  element: field,
8688
9139
  getValue: getValue('suffixAdorner'),
8689
9140
  id,
8690
9141
  label: 'Suffix',
8691
- setValue: onChange('suffixAdorner')
9142
+ setValue: onChange('suffixAdorner'),
9143
+ singleLine: true,
9144
+ variables
8692
9145
  });
8693
9146
  }
8694
9147
 
@@ -8707,7 +9160,7 @@ function ReadonlyEntry(props) {
8707
9160
  component: Readonly,
8708
9161
  editField: editField,
8709
9162
  field: field,
8710
- isEdited: isEdited$6
9163
+ isEdited: isEdited$5
8711
9164
  });
8712
9165
  }
8713
9166
  return entries;
@@ -8751,7 +9204,7 @@ function ConditionEntry(props) {
8751
9204
  component: Condition,
8752
9205
  editField: editField,
8753
9206
  field: field,
8754
- isEdited: isEdited$6
9207
+ isEdited: isEdited$5
8755
9208
  }];
8756
9209
  }
8757
9210
  function Condition(props) {
@@ -8789,6 +9242,55 @@ function Condition(props) {
8789
9242
  });
8790
9243
  }
8791
9244
 
9245
+ function ValuesExpressionEntry(props) {
9246
+ const {
9247
+ editField,
9248
+ field,
9249
+ id
9250
+ } = props;
9251
+ return [{
9252
+ id: id + '-expression',
9253
+ component: ValuesExpression,
9254
+ label: 'Values expression',
9255
+ isEdited: isEdited$5,
9256
+ editField,
9257
+ field
9258
+ }];
9259
+ }
9260
+ function ValuesExpression(props) {
9261
+ const {
9262
+ editField,
9263
+ field,
9264
+ id
9265
+ } = props;
9266
+ const debounce = useService('debounce');
9267
+ const variables = useVariables().map(name => ({
9268
+ name
9269
+ }));
9270
+ const path = VALUES_SOURCES_PATHS[VALUES_SOURCES.EXPRESSION];
9271
+ const schema = '[\n {\n "label": "dollar",\n "value": "$"\n }\n]';
9272
+ const description = jsxs("div", {
9273
+ children: ["Define an expression to populate the options from.", jsx("br", {}), jsx("br", {}), "The expression may result in an array of simple values or alternatively follow this schema:", jsx("pre", {
9274
+ children: jsx("code", {
9275
+ children: schema
9276
+ })
9277
+ })]
9278
+ });
9279
+ const getValue = () => get(field, path, '');
9280
+ const setValue = value => editField(field, path, value || '');
9281
+ return FeelEntry({
9282
+ debounce,
9283
+ description,
9284
+ element: field,
9285
+ feel: 'required',
9286
+ getValue,
9287
+ id,
9288
+ label: 'Options expression',
9289
+ setValue,
9290
+ variables
9291
+ });
9292
+ }
9293
+
8792
9294
  function GeneralGroup(field, editField, getService) {
8793
9295
  const entries = [...IdEntry({
8794
9296
  field,
@@ -8933,14 +9435,14 @@ function ValidationGroup(field, editField) {
8933
9435
  component: MinLength,
8934
9436
  getValue,
8935
9437
  field,
8936
- isEdited: isEdited$4,
9438
+ isEdited: isEdited$5,
8937
9439
  onChange
8938
9440
  }, {
8939
9441
  id: 'maxLength',
8940
9442
  component: MaxLength,
8941
9443
  getValue,
8942
9444
  field,
8943
- isEdited: isEdited$4,
9445
+ isEdited: isEdited$5,
8944
9446
  onChange
8945
9447
  });
8946
9448
  }
@@ -8960,14 +9462,14 @@ function ValidationGroup(field, editField) {
8960
9462
  component: Min,
8961
9463
  getValue,
8962
9464
  field,
8963
- isEdited: isEdited$4,
9465
+ isEdited: isEdited$5,
8964
9466
  onChange
8965
9467
  }, {
8966
9468
  id: 'max',
8967
9469
  component: Max,
8968
9470
  getValue,
8969
9471
  field,
8970
- isEdited: isEdited$4,
9472
+ isEdited: isEdited$5,
8971
9473
  onChange
8972
9474
  });
8973
9475
  }
@@ -9000,14 +9502,19 @@ function MinLength(props) {
9000
9502
  onChange
9001
9503
  } = props;
9002
9504
  const debounce = useService('debounce');
9003
- return NumberFieldEntry({
9505
+ const variables = useVariables().map(name => ({
9506
+ name
9507
+ }));
9508
+ return FeelNumberEntry({
9004
9509
  debounce,
9005
9510
  element: field,
9511
+ feel: 'optional',
9006
9512
  getValue: getValue('minLength'),
9007
9513
  id,
9008
9514
  label: 'Minimum length',
9009
9515
  min: 0,
9010
- setValue: onChange('minLength')
9516
+ setValue: onChange('minLength'),
9517
+ variables
9011
9518
  });
9012
9519
  }
9013
9520
  function MaxLength(props) {
@@ -9018,14 +9525,19 @@ function MaxLength(props) {
9018
9525
  onChange
9019
9526
  } = props;
9020
9527
  const debounce = useService('debounce');
9021
- return NumberFieldEntry({
9528
+ const variables = useVariables().map(name => ({
9529
+ name
9530
+ }));
9531
+ return FeelNumberEntry({
9022
9532
  debounce,
9023
9533
  element: field,
9534
+ feel: 'optional',
9024
9535
  getValue: getValue('maxLength'),
9025
9536
  id,
9026
9537
  label: 'Maximum length',
9027
9538
  min: 0,
9028
- setValue: onChange('maxLength')
9539
+ setValue: onChange('maxLength'),
9540
+ variables
9029
9541
  });
9030
9542
  }
9031
9543
  function Pattern(props) {
@@ -9053,14 +9565,19 @@ function Min(props) {
9053
9565
  onChange
9054
9566
  } = props;
9055
9567
  const debounce = useService('debounce');
9056
- return NumberFieldEntry({
9568
+ const variables = useVariables().map(name => ({
9569
+ name
9570
+ }));
9571
+ return FeelNumberEntry({
9057
9572
  debounce,
9058
9573
  element: field,
9574
+ feel: 'optional',
9059
9575
  id,
9060
9576
  label: 'Minimum',
9061
9577
  step: 'any',
9062
9578
  getValue: getValue('min'),
9063
- setValue: onChange('min')
9579
+ setValue: onChange('min'),
9580
+ variables
9064
9581
  });
9065
9582
  }
9066
9583
  function Max(props) {
@@ -9071,14 +9588,19 @@ function Max(props) {
9071
9588
  onChange
9072
9589
  } = props;
9073
9590
  const debounce = useService('debounce');
9074
- return NumberFieldEntry({
9591
+ const variables = useVariables().map(name => ({
9592
+ name
9593
+ }));
9594
+ return FeelNumberEntry({
9075
9595
  debounce,
9076
9596
  element: field,
9597
+ feel: 'optional',
9077
9598
  id,
9078
9599
  label: 'Maximum',
9079
9600
  step: 'any',
9080
9601
  getValue: getValue('max'),
9081
- setValue: onChange('max')
9602
+ setValue: onChange('max'),
9603
+ variables
9082
9604
  });
9083
9605
  }
9084
9606
  function ValidationType(props) {
@@ -9152,6 +9674,17 @@ function ValuesGroups(field, editField) {
9152
9674
  id: staticValuesId
9153
9675
  })
9154
9676
  });
9677
+ } else if (valuesSource === VALUES_SOURCES.EXPRESSION) {
9678
+ const valuesExpressionId = `${fieldId}-valuesExpression`;
9679
+ groups.push({
9680
+ id: valuesExpressionId,
9681
+ label: 'Options expression',
9682
+ component: Group,
9683
+ entries: ValuesExpressionEntry({
9684
+ ...context,
9685
+ id: valuesExpressionId
9686
+ })
9687
+ });
9155
9688
  }
9156
9689
  return groups;
9157
9690
  }
@@ -9311,47 +9844,37 @@ function FormPropertiesPanel(props) {
9311
9844
  } = props;
9312
9845
  const formEditor = injector.get('formEditor');
9313
9846
  const modeling = injector.get('modeling');
9314
- const selection = injector.get('selection');
9315
- const {
9316
- schema
9317
- } = formEditor._getState();
9847
+ const selectionModule = injector.get('selection');
9318
9848
  const [state, setState] = useState({
9319
- selectedFormField: selection.get() || schema
9849
+ selectedFormField: selectionModule.get() || formEditor._getState().schema
9320
9850
  });
9321
- const _update = field => {
9851
+ const selectedFormField = state.selectedFormField;
9852
+ const refresh = useCallback(field => {
9853
+ // TODO(skaiir): rework state management, re-rendering the whole properties panel is not the way to go
9854
+ // https://github.com/bpmn-io/form-js/issues/686
9322
9855
  setState({
9323
- ...state,
9324
- selectedFormField: field
9856
+ selectedFormField: selectionModule.get() || formEditor._getState().schema
9325
9857
  });
9326
9858
 
9327
9859
  // notify interested parties on property panel updates
9328
9860
  eventBus.fire('propertiesPanel.updated', {
9329
9861
  formField: field
9330
9862
  });
9331
- };
9332
- useLayoutEffect(() => {
9333
- function onSelectionChange(event) {
9334
- _update(event.selection || schema);
9335
- }
9336
- eventBus.on('selection.changed', onSelectionChange);
9337
- return () => {
9338
- eventBus.off('selection.changed', onSelectionChange);
9339
- };
9340
- }, []);
9863
+ }, [eventBus, formEditor, selectionModule]);
9341
9864
  useLayoutEffect(() => {
9342
- const onFieldChanged = () => {
9343
- /**
9344
- * TODO(pinussilvestrus): update with actual updated element,
9345
- * once we have a proper updater/change support
9346
- */
9347
- _update(selection.get() || schema);
9348
- };
9349
- eventBus.on('changed', onFieldChanged);
9865
+ /**
9866
+ * TODO(pinussilvestrus): update with actual updated element,
9867
+ * once we have a proper updater/change support
9868
+ */
9869
+ eventBus.on('changed', refresh);
9870
+ eventBus.on('import.done', refresh);
9871
+ eventBus.on('selection.changed', refresh);
9350
9872
  return () => {
9351
- eventBus.off('changed', onFieldChanged);
9873
+ eventBus.off('changed', refresh);
9874
+ eventBus.off('import.done', refresh);
9875
+ eventBus.off('selection.changed', refresh);
9352
9876
  };
9353
- }, []);
9354
- const selectedFormField = state.selectedFormField;
9877
+ }, [eventBus, refresh]);
9355
9878
  const getService = (type, strict = true) => injector.get(type, strict);
9356
9879
  const propertiesPanelContext = {
9357
9880
  getService
@@ -9447,10 +9970,70 @@ var PropertiesPanelModule = {
9447
9970
  propertiesPanel: ['type', PropertiesPanelRenderer]
9448
9971
  };
9449
9972
 
9973
+ /**
9974
+ * Manages the rendering of visual plugins.
9975
+ * @constructor
9976
+ * @param {Object} eventBus - Event bus for the application.
9977
+ */
9978
+ class RenderInjector extends SectionModuleBase {
9979
+ constructor(eventBus) {
9980
+ super(eventBus, 'renderInjector');
9981
+ this._eventBus = eventBus;
9982
+ this.registeredRenderers = [];
9983
+ }
9984
+
9985
+ /**
9986
+ * Inject a new renderer into the injector.
9987
+ * @param {string} identifier - Identifier for the renderer.
9988
+ * @param {Function} Renderer - The renderer function.
9989
+ */
9990
+ attachRenderer(identifier, Renderer) {
9991
+ this.registeredRenderers = [...this.registeredRenderers, {
9992
+ identifier,
9993
+ Renderer
9994
+ }];
9995
+ }
9996
+
9997
+ /**
9998
+ * Detach a renderer from the by key injector.
9999
+ * @param {string} identifier - Identifier for the renderer.
10000
+ */
10001
+ detachRenderer(identifier) {
10002
+ this.registeredRenderers = this.registeredRenderers.filter(r => r.identifier !== identifier);
10003
+ }
10004
+
10005
+ /**
10006
+ * Returns the registered renderers.
10007
+ * @returns {Array} Array of registered renderers.
10008
+ */
10009
+ fetchRenderers() {
10010
+ return this.registeredRenderers;
10011
+ }
10012
+ }
10013
+ RenderInjector.$inject = ['eventBus'];
10014
+
10015
+ var RenderInjectionModule = {
10016
+ __init__: ['renderInjector'],
10017
+ renderInjector: ['type', RenderInjector]
10018
+ };
10019
+
10020
+ class EditorTemplating {
10021
+ // same rules as viewer templating
10022
+ isTemplate(value) {
10023
+ return isString(value) && (value.startsWith('=') || /{{/.test(value));
10024
+ }
10025
+
10026
+ // return the template raw, as we usually just want to display that
10027
+ evaluate(template) {
10028
+ return template;
10029
+ }
10030
+ }
10031
+ EditorTemplating.$inject = [];
10032
+
9450
10033
  var ExpressionLanguageModule = {
9451
10034
  __init__: ['expressionLanguage', 'templating'],
9452
10035
  expressionLanguage: ['type', FeelExpressionLanguage],
9453
- templating: ['type', FeelersTemplating]
10036
+ templating: ['type', EditorTemplating]
9454
10037
  };
9455
10038
 
9456
10039
  const ids = new Ids([32, 36, 1]);
@@ -9705,7 +10288,7 @@ class FormEditor {
9705
10288
  * @internal
9706
10289
  */
9707
10290
  _getModules() {
9708
- return [ModelingModule, EditorActionsModule, DraggingModule, KeyboardModule, SelectionModule, PaletteModule, ExpressionLanguageModule, MarkdownModule, PropertiesPanelModule];
10291
+ return [ModelingModule, EditorActionsModule, DraggingModule, KeyboardModule, SelectionModule, PaletteModule, ExpressionLanguageModule, MarkdownModule, PropertiesPanelModule, RenderInjectionModule];
9709
10292
  }
9710
10293
 
9711
10294
  /**