@bpmn-io/form-js-editor 1.3.3 → 1.4.1

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 (50) hide show
  1. package/LICENSE +22 -22
  2. package/README.md +152 -116
  3. package/dist/assets/form-js-editor-base.css +832 -797
  4. package/dist/assets/form-js-editor.css +74 -19
  5. package/dist/assets/properties-panel.css +23 -3
  6. package/dist/index.cjs +1097 -838
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.es.js +1130 -876
  9. package/dist/index.es.js.map +1 -1
  10. package/dist/types/FormEditor.d.ts +2 -0
  11. package/dist/types/features/modeling/behavior/ValuesSourceBehavior.d.ts +8 -0
  12. package/dist/types/features/modeling/behavior/index.d.ts +2 -0
  13. package/dist/types/features/modeling/index.d.ts +1 -0
  14. package/dist/types/features/palette/components/Palette.d.ts +35 -5
  15. package/dist/types/features/palette/components/PaletteEntry.d.ts +1 -0
  16. package/dist/types/features/properties-panel/PropertiesPanelHeaderProvider.d.ts +1 -1
  17. package/dist/types/features/properties-panel/PropertiesPanelRenderer.d.ts +17 -0
  18. package/dist/types/features/properties-panel/PropertiesProvider.d.ts +10 -0
  19. package/dist/types/features/properties-panel/Util.d.ts +2 -0
  20. package/dist/types/features/properties-panel/entries/ActionEntry.d.ts +1 -0
  21. package/dist/types/features/properties-panel/entries/AdornerEntry.d.ts +1 -0
  22. package/dist/types/features/properties-panel/entries/AltTextEntry.d.ts +1 -0
  23. package/dist/types/features/properties-panel/entries/DateTimeConstraintsEntry.d.ts +1 -0
  24. package/dist/types/features/properties-panel/entries/DateTimeEntry.d.ts +1 -0
  25. package/dist/types/features/properties-panel/entries/DateTimeSerializationEntry.d.ts +1 -0
  26. package/dist/types/features/properties-panel/entries/DefaultValueEntry.d.ts +11 -1
  27. package/dist/types/features/properties-panel/entries/DescriptionEntry.d.ts +1 -0
  28. package/dist/types/features/properties-panel/entries/DisabledEntry.d.ts +1 -0
  29. package/dist/types/features/properties-panel/entries/GroupEntries.d.ts +1 -0
  30. package/dist/types/features/properties-panel/entries/IdEntry.d.ts +1 -0
  31. package/dist/types/features/properties-panel/entries/ImageSourceEntry.d.ts +1 -0
  32. package/dist/types/features/properties-panel/entries/KeyEntry.d.ts +1 -0
  33. package/dist/types/features/properties-panel/entries/LabelEntry.d.ts +1 -0
  34. package/dist/types/features/properties-panel/entries/NumberEntries.d.ts +1 -0
  35. package/dist/types/features/properties-panel/entries/NumberSerializationEntry.d.ts +1 -0
  36. package/dist/types/features/properties-panel/entries/ReadonlyEntry.d.ts +1 -0
  37. package/dist/types/features/properties-panel/entries/SelectEntries.d.ts +1 -0
  38. package/dist/types/features/properties-panel/entries/SpacerEntry.d.ts +1 -0
  39. package/dist/types/features/properties-panel/entries/TextEntry.d.ts +1 -0
  40. package/dist/types/features/properties-panel/entries/factories/simpleBoolEntryFactory.d.ts +1 -0
  41. package/dist/types/features/properties-panel/groups/AppearanceGroup.d.ts +1 -0
  42. package/dist/types/features/properties-panel/groups/ConstraintsGroup.d.ts +1 -0
  43. package/dist/types/features/properties-panel/groups/GeneralGroup.d.ts +17 -1
  44. package/dist/types/features/properties-panel/groups/SerializationGroup.d.ts +1 -0
  45. package/dist/types/features/properties-panel/groups/ValidationGroup.d.ts +1 -0
  46. package/dist/types/features/properties-panel/groups/ValuesGroups.d.ts +1 -1
  47. package/dist/types/features/properties-panel/index.d.ts +2 -0
  48. package/dist/types/index.d.ts +2 -0
  49. package/dist/types/types.d.ts +28 -28
  50. package/package.json +4 -4
package/dist/index.es.js CHANGED
@@ -1,13 +1,13 @@
1
- import { FormFieldRegistry as FormFieldRegistry$1, iconsByType, Text as Text$1, FormFields, formFields, FormContext, FormRenderContext, FormComponent, Importer, PathRegistry, FormLayouter, FieldFactory, runRecursively, clone, 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';
1
+ import { FormFieldRegistry as FormFieldRegistry$1, iconsByType, Text as Text$1, FormFields, sanitizeImageSource, FormContext, FormRenderContext, FormComponent, Importer, PathRegistry, FormLayouter, FieldFactory, runRecursively, clone, VALUES_SOURCES, VALUES_SOURCES_PATHS, getSchemaVariables, DATETIME_SUBTYPES, DATE_LABEL_PATH, TIME_LABEL_PATH, DATETIME_SUBTYPE_PATH, DATETIME_SUBTYPES_LABELS, TIME_SERIALISING_FORMAT_PATH, TIME_SERIALISING_FORMATS, TIME_INTERVAL_PATH, TIME_USE24H_PATH, DATE_DISALLOW_PAST_PATH, TIME_SERIALISINGFORMAT_LABELS, getValuesSource, VALUES_SOURCES_DEFAULTS, VALUES_SOURCES_LABELS, FeelExpressionLanguage, createFormContainer, createInjector, MarkdownModule, schemaVersion } from '@bpmn-io/form-js-viewer';
2
2
  export { schemaVersion } from '@bpmn-io/form-js-viewer';
3
3
  import Ids from 'ids';
4
- import { isArray, isFunction, isNumber, bind, assign, debounce, forEach, get, isObject, uniqueBy, sortBy, find, set as set$1, isString, isUndefined, without, has } from 'min-dash';
4
+ import { isArray, isFunction, isNumber, bind, assign, debounce, forEach, get, isObject, uniqueBy, sortBy, find, isString, set as set$1, reduce, isUndefined, without, has } from 'min-dash';
5
5
  import classnames from 'classnames';
6
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';
7
+ import { useContext, useRef, useEffect, useMemo, useState, useCallback, useLayoutEffect } from 'preact/hooks';
8
8
  import { createContext, Fragment, render, createElement } from 'preact';
9
9
  import * as React from 'preact/compat';
10
- import { createPortal, useRef, useContext as useContext$1, useEffect as useEffect$1, forwardRef } from 'preact/compat';
10
+ import { createPortal, useRef as useRef$1, useContext as useContext$1, useEffect as useEffect$1, forwardRef } from 'preact/compat';
11
11
  import dragula from '@bpmn-io/draggle';
12
12
  import { classes, query, closest, event, matches, domify } from 'min-dom';
13
13
  import { mutate } from 'array-move';
@@ -18,7 +18,7 @@ import * as focusTrap from 'focus-trap';
18
18
  import Big from 'big.js';
19
19
 
20
20
  var FN_REF = '__fn';
21
- var DEFAULT_PRIORITY$2 = 1000;
21
+ var DEFAULT_PRIORITY$3 = 1000;
22
22
  var slice = Array.prototype.slice;
23
23
 
24
24
  /**
@@ -163,7 +163,7 @@ EventBus.prototype.on = function (events, priority, callback, that) {
163
163
  if (isFunction(priority)) {
164
164
  that = callback;
165
165
  callback = priority;
166
- priority = DEFAULT_PRIORITY$2;
166
+ priority = DEFAULT_PRIORITY$3;
167
167
  }
168
168
  if (!isNumber(priority)) {
169
169
  throw new Error('priority must be a number');
@@ -202,7 +202,7 @@ EventBus.prototype.once = function (events, priority, callback, that) {
202
202
  if (isFunction(priority)) {
203
203
  that = callback;
204
204
  callback = priority;
205
- priority = DEFAULT_PRIORITY$2;
205
+ priority = DEFAULT_PRIORITY$3;
206
206
  }
207
207
  if (!isNumber(priority)) {
208
208
  throw new Error('priority must be a number');
@@ -517,10 +517,10 @@ function invokeFunction(fn, args) {
517
517
  return fn.apply(null, args);
518
518
  }
519
519
 
520
- /**
521
- * A factory to create a configurable debouncer.
522
- *
523
- * @param {number|boolean} [config=true]
520
+ /**
521
+ * A factory to create a configurable debouncer.
522
+ *
523
+ * @param {number|boolean} [config=true]
524
524
  */
525
525
  function DebounceFactory(config = true) {
526
526
  const timeout = typeof config === 'number' ? config : config ? 300 : 0;
@@ -533,11 +533,11 @@ function DebounceFactory(config = true) {
533
533
  DebounceFactory.$inject = ['config.debounce'];
534
534
 
535
535
  class FormFieldRegistry extends FormFieldRegistry$1 {
536
- /**
537
- * Updates a form fields id.
538
- *
539
- * @param {Object} formField
540
- * @param {string} newId
536
+ /**
537
+ * Updates a form fields id.
538
+ *
539
+ * @param {Object} formField
540
+ * @param {string} newId
541
541
  */
542
542
  updateId(formField, newId) {
543
543
  this._validateId(newId);
@@ -558,13 +558,13 @@ class FormFieldRegistry extends FormFieldRegistry$1 {
558
558
  }
559
559
  }
560
560
 
561
- /**
562
- * Validate the suitability of the given id and signals a problem
563
- * with an exception.
564
- *
565
- * @param {string} id
566
- *
567
- * @throws {Error} if id is empty or already assigned
561
+ /**
562
+ * Validate the suitability of the given id and signals a problem
563
+ * with an exception.
564
+ *
565
+ * @param {string} id
566
+ *
567
+ * @throws {Error} if id is empty or already assigned
568
568
  */
569
569
  _validateId(id) {
570
570
  if (!id) {
@@ -581,11 +581,11 @@ const MAX_COLUMNS = 16;
581
581
  const MIN_COLUMNS = 2;
582
582
  const MAX_FIELDS_PER_ROW = 4;
583
583
  class FormLayoutValidator {
584
- /**
585
- * @constructor
586
- *
587
- * @param { import('./FormLayouter').default } formLayouter
588
- * @param { import('./FormFieldRegistry').default } formFieldRegistry
584
+ /**
585
+ * @constructor
586
+ *
587
+ * @param { import('./FormLayouter').default } formLayouter
588
+ * @param { import('./FormFieldRegistry').default } formFieldRegistry
589
589
  */
590
590
  constructor(formLayouter, formFieldRegistry) {
591
591
  this._formLayouter = formLayouter;
@@ -656,21 +656,21 @@ function editorFormFieldClasses(type, {
656
656
  });
657
657
  }
658
658
 
659
- /**
660
- * Add a dragger that calls back the passed function with
661
- * { event, delta } on drag.
662
- *
663
- * @example
664
- *
665
- * function dragMove(event, delta) {
666
- * // we are dragging (!!)
667
- * }
668
- *
669
- * domElement.addEventListener('dragstart', dragger(dragMove));
670
- *
671
- * @param {Function} fn
672
- *
673
- * @return {Function} drag start callback function
659
+ /**
660
+ * Add a dragger that calls back the passed function with
661
+ * { event, delta } on drag.
662
+ *
663
+ * @example
664
+ *
665
+ * function dragMove(event, delta) {
666
+ * // we are dragging (!!)
667
+ * }
668
+ *
669
+ * domElement.addEventListener('dragstart', dragger(dragMove));
670
+ *
671
+ * @param {Function} fn
672
+ *
673
+ * @return {Function} drag start callback function
674
674
  */
675
675
  function createDragger$1(fn) {
676
676
  let self;
@@ -711,12 +711,12 @@ function createDragger$1(fn) {
711
711
  return onDragStart;
712
712
  }
713
713
 
714
- /**
715
- * Throttle function call according UI update cycle.
716
- *
717
- * @param {Function} fn
718
- *
719
- * @return {Function} throttled fn
714
+ /**
715
+ * Throttle function call according UI update cycle.
716
+ *
717
+ * @param {Function} fn
718
+ *
719
+ * @return {Function} throttled fn
720
720
  */
721
721
  function throttle(fn) {
722
722
  let active = false;
@@ -750,11 +750,11 @@ const DragAndDropContext = createContext({
750
750
  });
751
751
  var DragAndDropContext$1 = DragAndDropContext;
752
752
 
753
- /**
754
- * @param {string} type
755
- * @param {boolean} [strict]
756
- *
757
- * @returns {any}
753
+ /**
754
+ * @param {string} type
755
+ * @param {boolean} [strict]
756
+ *
757
+ * @returns {any}
758
758
  */
759
759
  function getService$1(type, strict) {}
760
760
  const FormEditorContext = createContext({
@@ -769,6 +769,27 @@ function useService$1 (type, strict) {
769
769
  return getService(type, strict);
770
770
  }
771
771
 
772
+ function usePrevious$1(value) {
773
+ const ref = useRef();
774
+ useEffect(() => ref.current = value);
775
+ return ref.current;
776
+ }
777
+
778
+ function useDebounce(fn, dependencies = []) {
779
+ const debounce = useService$1('debounce');
780
+ const callback = useMemo(() => {
781
+ return debounce(fn);
782
+ }, dependencies);
783
+
784
+ // cleanup async side-effect if callback #flush is provided.
785
+ useEffect(() => {
786
+ return () => {
787
+ typeof callback.flush === 'function' && callback.flush();
788
+ };
789
+ }, [callback]);
790
+ return callback;
791
+ }
792
+
772
793
  var _path$4;
773
794
  function _extends$4() { _extends$4 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$4.apply(this, arguments); }
774
795
  var SvgClose = function SvgClose(props) {
@@ -1062,7 +1083,7 @@ const FillContext = createContext({
1062
1083
  var FillContext$1 = FillContext;
1063
1084
 
1064
1085
  var Fill = (props => {
1065
- const uid = useRef(Symbol('fill_uid'));
1086
+ const uid = useRef$1(Symbol('fill_uid'));
1066
1087
  const fillContext = useContext$1(FillContext$1);
1067
1088
  useEffect$1(() => {
1068
1089
  if (!fillContext) {
@@ -1104,23 +1125,23 @@ var Slot = (props => {
1104
1125
  return fillsAndSeparators;
1105
1126
  });
1106
1127
 
1107
- /**
1108
- * Creates a Fragment for a fill.
1109
- *
1110
- * @param {Object} fill Fill to be rendered
1111
- * @returns {Object} Preact Fragment containing fill's children
1128
+ /**
1129
+ * Creates a Fragment for a fill.
1130
+ *
1131
+ * @param {Object} fill Fill to be rendered
1132
+ * @returns {Object} Preact Fragment containing fill's children
1112
1133
  */
1113
1134
  const FillFragment = fill => jsx(Fragment, {
1114
1135
  children: fill.children
1115
1136
  }, fill.id);
1116
1137
 
1117
- /**
1118
- * Creates an array of fills, with separators inserted between groups.
1119
- *
1120
- * @param {Array} groups Groups of fills
1121
- * @param {Function} fillRenderer Function to create a fill
1122
- * @param {Function} separatorRenderer Function to create a separator
1123
- * @returns {Array} Array of fills and separators
1138
+ /**
1139
+ * Creates an array of fills, with separators inserted between groups.
1140
+ *
1141
+ * @param {Array} groups Groups of fills
1142
+ * @param {Function} fillRenderer Function to create a fill
1143
+ * @param {Function} separatorRenderer Function to create a separator
1144
+ * @returns {Array} Array of fills and separators
1124
1145
  */
1125
1146
  const buildFills = (groups, fillRenderer, separatorRenderer) => {
1126
1147
  const result = [];
@@ -1138,8 +1159,8 @@ const buildFills = (groups, fillRenderer, separatorRenderer) => {
1138
1159
  return result;
1139
1160
  };
1140
1161
 
1141
- /**
1142
- * Groups fills by group name property.
1162
+ /**
1163
+ * Groups fills by group name property.
1143
1164
  */
1144
1165
  const _groupByGroupName = fills => {
1145
1166
  const groups = [];
@@ -1159,8 +1180,8 @@ const _groupByGroupName = fills => {
1159
1180
  return Object.keys(groupsById).sort().map(id => groupsById[id]);
1160
1181
  };
1161
1182
 
1162
- /**
1163
- * Compares fills by priority.
1183
+ /**
1184
+ * Compares fills by priority.
1164
1185
  */
1165
1186
  const _comparePriority = (a, b) => {
1166
1187
  return (b.priority || 0) - (a.priority || 0);
@@ -1191,17 +1212,63 @@ var SlotFillRoot = (props => {
1191
1212
  });
1192
1213
  });
1193
1214
 
1194
- const PALETTE_ENTRIES = formFields.filter(({
1195
- config: fieldConfig
1196
- }) => fieldConfig.type !== 'default').map(({
1197
- config: fieldConfig
1198
- }) => {
1199
- return {
1200
- label: fieldConfig.label,
1201
- type: fieldConfig.type,
1202
- group: fieldConfig.group
1215
+ function PaletteEntry(props) {
1216
+ const {
1217
+ type,
1218
+ label,
1219
+ icon,
1220
+ iconUrl,
1221
+ getPaletteIcon
1222
+ } = props;
1223
+ const modeling = useService$1('modeling');
1224
+ const formEditor = useService$1('formEditor');
1225
+ const Icon = getPaletteIcon({
1226
+ icon,
1227
+ iconUrl,
1228
+ label,
1229
+ type
1230
+ });
1231
+ const onKeyDown = event => {
1232
+ if (event.code === 'Enter') {
1233
+ const {
1234
+ fieldType: type
1235
+ } = event.target.dataset;
1236
+ const {
1237
+ schema
1238
+ } = formEditor._getState();
1239
+
1240
+ // add new form field to last position
1241
+ modeling.addFormField({
1242
+ type
1243
+ }, schema, schema.components.length);
1244
+ }
1203
1245
  };
1204
- });
1246
+ return jsxs("button", {
1247
+ class: "fjs-palette-field fjs-drag-copy fjs-no-drop",
1248
+ "data-field-type": type,
1249
+ title: `Create ${getIndefiniteArticle(type)} ${label} element`,
1250
+ onKeyDown: onKeyDown,
1251
+ children: [Icon ? jsx(Icon, {
1252
+ class: "fjs-palette-field-icon",
1253
+ width: "36",
1254
+ height: "36",
1255
+ viewBox: "0 0 54 54"
1256
+ }) : null, jsx("span", {
1257
+ class: "fjs-palette-field-text",
1258
+ children: label
1259
+ })]
1260
+ });
1261
+ }
1262
+
1263
+ // helpers ///////////
1264
+
1265
+ function getIndefiniteArticle(type) {
1266
+ if (['image'].includes(type)) {
1267
+ return 'an';
1268
+ }
1269
+ return 'a';
1270
+ }
1271
+
1205
1272
  const PALETTE_GROUPS = [{
1206
1273
  label: 'Basic input',
1207
1274
  id: 'basic-input'
@@ -1216,10 +1283,12 @@ const PALETTE_GROUPS = [{
1216
1283
  id: 'action'
1217
1284
  }];
1218
1285
  function Palette(props) {
1219
- const [entries, setEntries] = useState(PALETTE_ENTRIES);
1286
+ const formFields = useService$1('formFields');
1287
+ const initialPaletteEntries = useRef(collectPaletteEntries(formFields));
1288
+ const [paletteEntries, setPaletteEntries] = useState(initialPaletteEntries.current);
1220
1289
  const [searchTerm, setSearchTerm] = useState('');
1221
- const inputRef = useRef$1();
1222
- const groups = groupEntries(entries);
1290
+ const inputRef = useRef();
1291
+ const groups = groupEntries(paletteEntries);
1223
1292
  const simplifyString = useCallback(str => {
1224
1293
  return str.toLowerCase().replace(/\s+/g, '');
1225
1294
  }, []);
@@ -1235,8 +1304,8 @@ function Palette(props) {
1235
1304
 
1236
1305
  // filter entries on search change
1237
1306
  useEffect(() => {
1238
- const entries = PALETTE_ENTRIES.filter(filter);
1239
- setEntries(entries);
1307
+ const entries = initialPaletteEntries.current.filter(filter);
1308
+ setPaletteEntries(entries);
1240
1309
  }, [filter, searchTerm]);
1241
1310
  const handleInput = useCallback(event => {
1242
1311
  setSearchTerm(() => event.target.value);
@@ -1283,24 +1352,10 @@ function Palette(props) {
1283
1352
  children: label
1284
1353
  }), jsx("div", {
1285
1354
  class: "fjs-palette-fields fjs-drag-container fjs-no-drop",
1286
- children: entries.map(({
1287
- label,
1288
- type
1289
- }) => {
1290
- const Icon = iconsByType(type);
1291
- return jsxs("div", {
1292
- class: "fjs-palette-field fjs-drag-copy fjs-no-drop",
1293
- "data-field-type": type,
1294
- title: `Create ${getIndefiniteArticle(type)} ${label} element`,
1295
- children: [Icon ? jsx(Icon, {
1296
- class: "fjs-palette-field-icon",
1297
- width: "36",
1298
- height: "36",
1299
- viewBox: "0 0 54 54"
1300
- }) : null, jsx("span", {
1301
- class: "fjs-palette-field-text",
1302
- children: label
1303
- })]
1355
+ children: entries.map(entry => {
1356
+ return jsx(PaletteEntry, {
1357
+ getPaletteIcon: getPaletteIcon,
1358
+ ...entry
1304
1359
  });
1305
1360
  })
1306
1361
  })]
@@ -1340,11 +1395,59 @@ function groupEntries(entries) {
1340
1395
  });
1341
1396
  return groups.filter(g => g.entries.length);
1342
1397
  }
1343
- function getIndefiniteArticle(type) {
1344
- if (['image'].includes(type)) {
1345
- return 'an';
1398
+
1399
+ /**
1400
+ * Returns a list of palette entries.
1401
+ *
1402
+ * @param {FormFields} formFields
1403
+ * @returns {Array<PaletteEntry>}
1404
+ */
1405
+ function collectPaletteEntries(formFields) {
1406
+ return Object.entries(formFields._formFields).map(([type, formField]) => {
1407
+ const {
1408
+ config: fieldConfig
1409
+ } = formField;
1410
+ return {
1411
+ label: fieldConfig.label,
1412
+ type: type,
1413
+ group: fieldConfig.group,
1414
+ icon: fieldConfig.icon,
1415
+ iconUrl: fieldConfig.iconUrl
1416
+ };
1417
+ }).filter(({
1418
+ type
1419
+ }) => type !== 'default');
1420
+ }
1421
+
1422
+ /**
1423
+ * There are various options to specify an icon for a palette entry.
1424
+ *
1425
+ * a) via `iconUrl` property in a form field config
1426
+ * b) via `icon` property in a form field config
1427
+ * c) via statically defined iconsByType (fallback)
1428
+ */
1429
+ function getPaletteIcon(entry) {
1430
+ const {
1431
+ icon,
1432
+ iconUrl,
1433
+ type,
1434
+ label
1435
+ } = entry;
1436
+ let Icon;
1437
+ if (iconUrl) {
1438
+ Icon = () => jsx("img", {
1439
+ class: "fjs-field-icon-image",
1440
+ width: 36,
1441
+ style: {
1442
+ margin: 'auto'
1443
+ },
1444
+ alt: label,
1445
+ src: sanitizeImageSource(iconUrl)
1446
+ });
1447
+ } else {
1448
+ Icon = icon || iconsByType(type);
1346
1449
  }
1347
- return 'a';
1450
+ return Icon;
1348
1451
  }
1349
1452
 
1350
1453
  var InjectedRendersRoot = (() => {
@@ -1388,20 +1491,20 @@ const DRAG_NO_DROP_CLS = 'fjs-no-drop';
1388
1491
  const DRAG_NO_MOVE_CLS = 'fjs-no-move';
1389
1492
  const ERROR_DROP_CLS = 'fjs-error-drop';
1390
1493
 
1391
- /**
1392
- * @typedef { { id: String, components: Array<any> } } FormRow
1494
+ /**
1495
+ * @typedef { { id: String, components: Array<any> } } FormRow
1393
1496
  */
1394
1497
 
1395
1498
  class Dragging {
1396
- /**
1397
- * @constructor
1398
- *
1399
- * @param { import('../../core/FormFieldRegistry').default } formFieldRegistry
1400
- * @param { import('../../core/FormLayouter').default } formLayouter
1401
- * @param { import('../../core/FormLayoutValidator').default } formLayoutValidator
1402
- * @param { import('../../core/EventBus').default } eventBus
1403
- * @param { import('../modeling/Modeling').default } modeling
1404
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
1499
+ /**
1500
+ * @constructor
1501
+ *
1502
+ * @param { import('../../core/FormFieldRegistry').default } formFieldRegistry
1503
+ * @param { import('../../core/FormLayouter').default } formLayouter
1504
+ * @param { import('../../core/FormLayoutValidator').default } formLayoutValidator
1505
+ * @param { import('../../core/EventBus').default } eventBus
1506
+ * @param { import('../modeling/Modeling').default } modeling
1507
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
1405
1508
  */
1406
1509
  constructor(formFieldRegistry, formLayouter, formLayoutValidator, eventBus, modeling, pathRegistry) {
1407
1510
  this._formFieldRegistry = formFieldRegistry;
@@ -1412,13 +1515,13 @@ class Dragging {
1412
1515
  this._pathRegistry = pathRegistry;
1413
1516
  }
1414
1517
 
1415
- /**
1416
- * Calculcates position in form schema given the dropped place.
1417
- *
1418
- * @param { FormRow } targetRow
1419
- * @param { any } targetFormField
1420
- * @param { HTMLElement } sibling
1421
- * @returns { number }
1518
+ /**
1519
+ * Calculcates position in form schema given the dropped place.
1520
+ *
1521
+ * @param { FormRow } targetRow
1522
+ * @param { any } targetFormField
1523
+ * @param { HTMLElement } sibling
1524
+ * @returns { number }
1422
1525
  */
1423
1526
  getTargetIndex(targetRow, targetFormField, sibling) {
1424
1527
  /** @type HTMLElement */
@@ -1559,8 +1662,8 @@ class Dragging {
1559
1662
  }
1560
1663
  }
1561
1664
 
1562
- /**
1563
- * @param { { container: Array<string>, direction: string, mirrorContainer: string } } options
1665
+ /**
1666
+ * @param { { container: Array<string>, direction: string, mirrorContainer: string } } options
1564
1667
  */
1565
1668
  createDragulaInstance(options) {
1566
1669
  const {
@@ -1731,13 +1834,13 @@ function FieldResizer(props) {
1731
1834
  field,
1732
1835
  position
1733
1836
  } = props;
1734
- const ref = useRef$1(null);
1837
+ const ref = useRef(null);
1735
1838
  const formLayoutValidator = useService$1('formLayoutValidator');
1736
1839
  const modeling = useService$1('modeling');
1737
1840
 
1738
1841
  // we can't use state as we need to
1739
1842
  // manipulate this inside dragging events
1740
- const context = useRef$1({
1843
+ const context = useRef({
1741
1844
  startColumns: 0,
1742
1845
  newColumns: 0
1743
1846
  });
@@ -1867,6 +1970,7 @@ function Element$1(props) {
1867
1970
  const eventBus = useService$1('eventBus'),
1868
1971
  formEditor = useService$1('formEditor'),
1869
1972
  formFieldRegistry = useService$1('formFieldRegistry'),
1973
+ formFields = useService$1('formFields'),
1870
1974
  modeling = useService$1('modeling'),
1871
1975
  selection = useService$1('selection');
1872
1976
  const {
@@ -1881,7 +1985,7 @@ function Element$1(props) {
1881
1985
  type,
1882
1986
  showOutline
1883
1987
  } = field;
1884
- const ref = useRef$1();
1988
+ const ref = useRef();
1885
1989
  function scrollIntoView({
1886
1990
  selection
1887
1991
  }) {
@@ -1952,6 +2056,7 @@ function Element$1(props) {
1952
2056
  field: field
1953
2057
  }), jsx(ContextPad, {
1954
2058
  children: selection.isSelected(field) && field.type !== 'default' ? jsx("button", {
2059
+ title: getRemoveButtonTitle(field, formFields),
1955
2060
  class: "fjs-context-pad-item",
1956
2061
  onClick: onRemove,
1957
2062
  children: jsx(DeleteIcon$1, {})
@@ -1974,7 +2079,7 @@ function DebugColumns(props) {
1974
2079
  return null;
1975
2080
  }
1976
2081
  return jsx("div", {
1977
- style: "width: fit-content;\r padding: 2px 6px;\r height: 16px;\r background: var(--color-blue-205-100-95);\r display: flex;\r justify-content: center;\r align-items: center;\r position: absolute;\r bottom: -2px;\r z-index: 2;\r font-size: 10px;\r right: 3px;",
2082
+ style: "width: fit-content; padding: 2px 6px; height: 16px; background: var(--color-blue-205-100-95); display: flex; justify-content: center; align-items: center; position: absolute; bottom: -2px; z-index: 2; font-size: 10px; right: 3px;",
1978
2083
  class: "fjs-debug-columns",
1979
2084
  children: (field.layout || {}).columns || 'auto'
1980
2085
  });
@@ -2051,8 +2156,8 @@ function FormEditor$1(props) {
2051
2156
  const {
2052
2157
  ariaLabel
2053
2158
  } = properties;
2054
- const formContainerRef = useRef$1(null);
2055
- const propertiesPanelRef = useRef$1(null);
2159
+ const formContainerRef = useRef(null);
2160
+ const propertiesPanelRef = useRef(null);
2056
2161
  const [, setSelection] = useState(schema);
2057
2162
  useEffect(() => {
2058
2163
  function handleSelectionChanged(event) {
@@ -2114,6 +2219,9 @@ function FormEditor$1(props) {
2114
2219
 
2115
2220
  // fire event after render to notify interested parties
2116
2221
  useEffect(() => {
2222
+ eventBus.fire('rendered');
2223
+
2224
+ // keep deprecated event to ensure backward compatibility
2117
2225
  eventBus.fire('formEditor.rendered');
2118
2226
  }, []);
2119
2227
  const [hoveredId, setHoveredId] = useState(null);
@@ -2209,15 +2317,20 @@ function CreatePreview(props) {
2209
2317
  const {
2210
2318
  drake
2211
2319
  } = useContext(DragAndDropContext$1);
2320
+ const formFields = useService$1('formFields');
2212
2321
  function handleCloned(clone, original, type) {
2213
2322
  const fieldType = clone.dataset.fieldType;
2214
2323
 
2215
2324
  // (1) field preview
2216
2325
  if (fieldType) {
2326
+ const paletteEntry = findPaletteEntry(fieldType, formFields);
2327
+ if (!paletteEntry) {
2328
+ return;
2329
+ }
2217
2330
  const {
2218
2331
  label
2219
- } = findPaletteEntry(fieldType);
2220
- const Icon = iconsByType(fieldType);
2332
+ } = paletteEntry;
2333
+ const Icon = getPaletteIcon(paletteEntry);
2221
2334
  clone.innerHTML = '';
2222
2335
  clone.class = 'gu-mirror';
2223
2336
  clone.classList.add('fjs-field-preview-container');
@@ -2258,12 +2371,19 @@ function CreatePreview(props) {
2258
2371
 
2259
2372
  // helper //////
2260
2373
 
2261
- function findPaletteEntry(type) {
2262
- return PALETTE_ENTRIES.find(entry => entry.type === type);
2374
+ function findPaletteEntry(type, formFields) {
2375
+ return collectPaletteEntries(formFields).find(entry => entry.type === type);
2263
2376
  }
2264
2377
  function defaultPropertiesPanel(propertiesPanelConfig) {
2265
2378
  return !(propertiesPanelConfig && propertiesPanelConfig.parent);
2266
2379
  }
2380
+ function getRemoveButtonTitle(formField, formFields) {
2381
+ const entry = findPaletteEntry(formField.type, formFields);
2382
+ if (!entry) {
2383
+ return 'Remove form field';
2384
+ }
2385
+ return `Remove ${entry.label}`;
2386
+ }
2267
2387
 
2268
2388
  class Renderer {
2269
2389
  constructor(renderConfig, eventBus, formEditor, injector) {
@@ -2674,7 +2794,7 @@ function isRedo(event) {
2674
2794
  var KEYDOWN_EVENT = 'keyboard.keydown',
2675
2795
  KEYUP_EVENT = 'keyboard.keyup';
2676
2796
  var HANDLE_MODIFIER_ATTRIBUTE = 'input-handle-modified-keys';
2677
- var DEFAULT_PRIORITY$1 = 1000;
2797
+ var DEFAULT_PRIORITY$2 = 1000;
2678
2798
 
2679
2799
  /**
2680
2800
  * A keyboard abstraction that may be activated and
@@ -2807,7 +2927,7 @@ Keyboard.prototype.addListener = function (priority, listener, type) {
2807
2927
  if (isFunction(priority)) {
2808
2928
  type = listener;
2809
2929
  listener = priority;
2810
- priority = DEFAULT_PRIORITY$1;
2930
+ priority = DEFAULT_PRIORITY$2;
2811
2931
  }
2812
2932
  this._eventBus.on(type || KEYDOWN_EVENT, priority, listener);
2813
2933
  };
@@ -3045,10 +3165,10 @@ function updateRow(formField, rowId) {
3045
3165
  }
3046
3166
 
3047
3167
  class AddFormFieldHandler {
3048
- /**
3049
- * @constructor
3050
- * @param { import('../../../FormEditor').default } formEditor
3051
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3168
+ /**
3169
+ * @constructor
3170
+ * @param { import('../../../FormEditor').default } formEditor
3171
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3052
3172
  */
3053
3173
  constructor(formEditor, formFieldRegistry) {
3054
3174
  this._formEditor = formEditor;
@@ -3109,10 +3229,10 @@ class AddFormFieldHandler {
3109
3229
  AddFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3110
3230
 
3111
3231
  class EditFormFieldHandler {
3112
- /**
3113
- * @constructor
3114
- * @param { import('../../../FormEditor').default } formEditor
3115
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3232
+ /**
3233
+ * @constructor
3234
+ * @param { import('../../../FormEditor').default } formEditor
3235
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3116
3236
  */
3117
3237
  constructor(formEditor, formFieldRegistry) {
3118
3238
  this._formEditor = formEditor;
@@ -3175,11 +3295,11 @@ class EditFormFieldHandler {
3175
3295
  EditFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3176
3296
 
3177
3297
  class MoveFormFieldHandler {
3178
- /**
3179
- * @constructor
3180
- * @param { import('../../../FormEditor').default } formEditor
3181
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3182
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3298
+ /**
3299
+ * @constructor
3300
+ * @param { import('../../../FormEditor').default } formEditor
3301
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3302
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3183
3303
  */
3184
3304
  constructor(formEditor, formFieldRegistry, pathRegistry) {
3185
3305
  this._formEditor = formEditor;
@@ -3284,10 +3404,10 @@ class MoveFormFieldHandler {
3284
3404
  MoveFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry', 'pathRegistry'];
3285
3405
 
3286
3406
  class RemoveFormFieldHandler {
3287
- /**
3288
- * @constructor
3289
- * @param { import('../../../FormEditor').default } formEditor
3290
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3407
+ /**
3408
+ * @constructor
3409
+ * @param { import('../../../FormEditor').default } formEditor
3410
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3291
3411
  */
3292
3412
  constructor(formEditor, formFieldRegistry) {
3293
3413
  this._formEditor = formEditor;
@@ -3347,9 +3467,9 @@ class RemoveFormFieldHandler {
3347
3467
  RemoveFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3348
3468
 
3349
3469
  class UpdateIdClaimHandler {
3350
- /**
3351
- * @constructor
3352
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3470
+ /**
3471
+ * @constructor
3472
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3353
3473
  */
3354
3474
  constructor(formFieldRegistry) {
3355
3475
  this._formFieldRegistry = formFieldRegistry;
@@ -3382,9 +3502,9 @@ class UpdateIdClaimHandler {
3382
3502
  UpdateIdClaimHandler.$inject = ['formFieldRegistry'];
3383
3503
 
3384
3504
  class UpdateKeyClaimHandler {
3385
- /**
3386
- * @constructor
3387
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3505
+ /**
3506
+ * @constructor
3507
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3388
3508
  */
3389
3509
  constructor(pathRegistry) {
3390
3510
  this._pathRegistry = pathRegistry;
@@ -3425,9 +3545,9 @@ class UpdateKeyClaimHandler {
3425
3545
  UpdateKeyClaimHandler.$inject = ['pathRegistry'];
3426
3546
 
3427
3547
  class UpdatePathClaimHandler {
3428
- /**
3429
- * @constructor
3430
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3548
+ /**
3549
+ * @constructor
3550
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3431
3551
  */
3432
3552
  constructor(pathRegistry) {
3433
3553
  this._pathRegistry = pathRegistry;
@@ -3625,7 +3745,7 @@ Modeling.$inject = ['commandStack', 'eventBus', 'formEditor', 'formFieldRegistry
3625
3745
  * @typedef { (context: CommandContext) => void } ComposeHandlerFunction
3626
3746
  */
3627
3747
 
3628
- var DEFAULT_PRIORITY = 1000;
3748
+ var DEFAULT_PRIORITY$1 = 1000;
3629
3749
 
3630
3750
  /**
3631
3751
  * A utility that can be used to plug into the command execution for
@@ -3686,7 +3806,7 @@ CommandInterceptor.prototype.on = function (events, hook, priority, handlerFn, u
3686
3806
  that = unwrap;
3687
3807
  unwrap = handlerFn;
3688
3808
  handlerFn = priority;
3689
- priority = DEFAULT_PRIORITY;
3809
+ priority = DEFAULT_PRIORITY$1;
3690
3810
  }
3691
3811
  if (isObject(unwrap)) {
3692
3812
  that = unwrap;
@@ -3989,8 +4109,8 @@ class ValidateBehavior extends CommandInterceptor {
3989
4109
  constructor(eventBus) {
3990
4110
  super(eventBus);
3991
4111
 
3992
- /**
3993
- * Remove custom validation if <validationType> is about to be added.
4112
+ /**
4113
+ * Remove custom validation if <validationType> is about to be added.
3994
4114
  */
3995
4115
  this.preExecute('formField.edit', function (context) {
3996
4116
  const {
@@ -4013,12 +4133,61 @@ class ValidateBehavior extends CommandInterceptor {
4013
4133
  }
4014
4134
  ValidateBehavior.$inject = ['eventBus'];
4015
4135
 
4136
+ class ValuesSourceBehavior extends CommandInterceptor {
4137
+ constructor(eventBus) {
4138
+ super(eventBus);
4139
+
4140
+ /**
4141
+ * Cleanup properties on changing the values source.
4142
+ *
4143
+ * 1) Remove other sources, e.g. set `values` => remove `valuesKey` and `valuesExpression`
4144
+ * 2) Remove default values for all other values sources
4145
+ */
4146
+ this.preExecute('formField.edit', function (context) {
4147
+ const {
4148
+ properties
4149
+ } = context;
4150
+ const newProperties = {};
4151
+ if (!isValuesSourceUpdate(properties)) {
4152
+ return;
4153
+ }
4154
+
4155
+ // clean up value sources that are not to going to be set
4156
+ Object.values(VALUES_SOURCES).forEach(source => {
4157
+ const path = VALUES_SOURCES_PATHS[source];
4158
+ if (get(properties, path) == undefined) {
4159
+ newProperties[VALUES_SOURCES_PATHS[source]] = undefined;
4160
+ }
4161
+ });
4162
+
4163
+ // clean up default value
4164
+ if (get(properties, VALUES_SOURCES_PATHS[VALUES_SOURCES.EXPRESSION]) !== undefined || get(properties, VALUES_SOURCES_PATHS[VALUES_SOURCES.INPUT]) !== undefined) {
4165
+ newProperties['defaultValue'] = undefined;
4166
+ }
4167
+ context.properties = {
4168
+ ...properties,
4169
+ ...newProperties
4170
+ };
4171
+ }, true);
4172
+ }
4173
+ }
4174
+ ValuesSourceBehavior.$inject = ['eventBus'];
4175
+
4176
+ // helper ///////////////////
4177
+
4178
+ function isValuesSourceUpdate(properties) {
4179
+ return Object.values(VALUES_SOURCES_PATHS).some(path => {
4180
+ return get(properties, path) !== undefined;
4181
+ });
4182
+ }
4183
+
4016
4184
  var behaviorModule = {
4017
- __init__: ['idBehavior', 'keyBehavior', 'pathBehavior', 'validateBehavior'],
4185
+ __init__: ['idBehavior', 'keyBehavior', 'pathBehavior', 'validateBehavior', 'valuesSourceBehavior'],
4018
4186
  idBehavior: ['type', IdBehavior],
4019
4187
  keyBehavior: ['type', KeyBehavior],
4020
4188
  pathBehavior: ['type', PathBehavior],
4021
- validateBehavior: ['type', ValidateBehavior]
4189
+ validateBehavior: ['type', ValidateBehavior],
4190
+ valuesSourceBehavior: ['type', ValuesSourceBehavior]
4022
4191
  };
4023
4192
 
4024
4193
  /**
@@ -4570,22 +4739,22 @@ var SelectionModule = {
4570
4739
  selectionBehavior: ['type', SelectionBehavior]
4571
4740
  };
4572
4741
 
4573
- /**
4574
- * Base class for sectionable UI modules.
4575
- *
4576
- * @property {EventBus} _eventBus - EventBus instance used for event handling.
4577
- * @property {string} managerType - Type of the render manager. Used to form event names.
4578
- *
4579
- * @class SectionModuleBase
4742
+ /**
4743
+ * Base class for sectionable UI modules.
4744
+ *
4745
+ * @property {EventBus} _eventBus - EventBus instance used for event handling.
4746
+ * @property {string} managerType - Type of the render manager. Used to form event names.
4747
+ *
4748
+ * @class SectionModuleBase
4580
4749
  */
4581
4750
  class SectionModuleBase {
4582
- /**
4583
- * Create a SectionModuleBase instance.
4584
- *
4585
- * @param {any} eventBus - The EventBus instance used for event handling.
4586
- * @param {string} sectionKey - The type of render manager. Used to form event names.
4587
- *
4588
- * @constructor
4751
+ /**
4752
+ * Create a SectionModuleBase instance.
4753
+ *
4754
+ * @param {any} eventBus - The EventBus instance used for event handling.
4755
+ * @param {string} sectionKey - The type of render manager. Used to form event names.
4756
+ *
4757
+ * @constructor
4589
4758
  */
4590
4759
  constructor(eventBus, sectionKey) {
4591
4760
  this._eventBus = eventBus;
@@ -4598,10 +4767,10 @@ class SectionModuleBase {
4598
4767
  });
4599
4768
  }
4600
4769
 
4601
- /**
4602
- * Attach the managed section to a parent node.
4603
- *
4604
- * @param {HTMLElement} container - The parent node to attach to.
4770
+ /**
4771
+ * Attach the managed section to a parent node.
4772
+ *
4773
+ * @param {HTMLElement} container - The parent node to attach to.
4605
4774
  */
4606
4775
  attachTo(container) {
4607
4776
  this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.attach`, {
@@ -4609,22 +4778,22 @@ class SectionModuleBase {
4609
4778
  }));
4610
4779
  }
4611
4780
 
4612
- /**
4613
- * Detach the managed section from its parent node.
4781
+ /**
4782
+ * Detach the managed section from its parent node.
4614
4783
  */
4615
4784
  detach() {
4616
4785
  this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.detach`));
4617
4786
  }
4618
4787
 
4619
- /**
4620
- * Reset the managed section to its initial state.
4788
+ /**
4789
+ * Reset the managed section to its initial state.
4621
4790
  */
4622
4791
  reset() {
4623
4792
  this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.reset`));
4624
4793
  }
4625
4794
 
4626
- /**
4627
- * Circumvents timing issues.
4795
+ /**
4796
+ * Circumvents timing issues.
4628
4797
  */
4629
4798
  _onceSectionRendered(callback) {
4630
4799
  if (this.isSectionRendered) {
@@ -4747,6 +4916,29 @@ FeelIcon$1.defaultProps = {
4747
4916
  fill: "none",
4748
4917
  xmlns: "http://www.w3.org/2000/svg"
4749
4918
  };
4919
+ var HelpIcon = function HelpIcon(props) {
4920
+ return jsxs("svg", {
4921
+ ...props,
4922
+ children: [jsx("path", {
4923
+ d: "M16 2a14 14 0 1 0 14 14A14 14 0 0 0 16 2Zm0 26a12 12 0 1 1 12-12 12 12 0 0 1-12 12Z"
4924
+ }), jsx("circle", {
4925
+ cx: "16",
4926
+ cy: "23.5",
4927
+ r: "1.5"
4928
+ }), jsx("path", {
4929
+ d: "M17 8h-1.5a4.49 4.49 0 0 0-4.5 4.5v.5h2v-.5a2.5 2.5 0 0 1 2.5-2.5H17a2.5 2.5 0 0 1 0 5h-2v4.5h2V17a4.5 4.5 0 0 0 0-9Z"
4930
+ }), jsx("path", {
4931
+ style: {
4932
+ fill: "none"
4933
+ },
4934
+ d: "M0 0h32v32H0z"
4935
+ })]
4936
+ });
4937
+ };
4938
+ HelpIcon.defaultProps = {
4939
+ xmlns: "http://www.w3.org/2000/svg",
4940
+ viewBox: "0 0 32 32"
4941
+ };
4750
4942
  function Header(props) {
4751
4943
  const {
4752
4944
  element,
@@ -4877,8 +5069,8 @@ function Tooltip(props) {
4877
5069
  const [visible, setShow] = useState(false);
4878
5070
  const [focusedViaKeyboard, setFocusedViaKeyboard] = useState(false);
4879
5071
  let timeout = null;
4880
- const wrapperRef = useRef$1(null);
4881
- const tooltipRef = useRef$1(null);
5072
+ const wrapperRef = useRef(null);
5073
+ const tooltipRef = useRef(null);
4882
5074
  const showTooltip = async event => {
4883
5075
  const show = () => setShow(true);
4884
5076
  if (!visible && !timeout) {
@@ -5041,7 +5233,7 @@ function useEvent(event, callback, eventBus) {
5041
5233
  eventBus
5042
5234
  } = eventContext);
5043
5235
  }
5044
- const didMount = useRef$1(false);
5236
+ const didMount = useRef(false);
5045
5237
 
5046
5238
  // (1) subscribe immediately
5047
5239
  if (eventBus && !didMount.current) {
@@ -5097,7 +5289,7 @@ function useLayoutState(path, defaultValue) {
5097
5289
  */
5098
5290
 
5099
5291
  function usePrevious(value) {
5100
- const ref = useRef$1();
5292
+ const ref = useRef();
5101
5293
  useEffect(() => {
5102
5294
  ref.current = value;
5103
5295
  });
@@ -5115,8 +5307,8 @@ function useShowEntryEvent(id) {
5115
5307
  const {
5116
5308
  onShow
5117
5309
  } = useContext(LayoutContext);
5118
- const ref = useRef$1();
5119
- const focus = useRef$1(false);
5310
+ const ref = useRef();
5311
+ const focus = useRef(false);
5120
5312
  const onShowEntry = useCallback(event => {
5121
5313
  if (event.id === id) {
5122
5314
  onShow();
@@ -5222,7 +5414,7 @@ function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky)
5222
5414
  * @returns {Function} static function reference
5223
5415
  */
5224
5416
  function useStaticCallback(callback) {
5225
- const callbackRef = useRef$1(callback);
5417
+ const callbackRef = useRef(callback);
5226
5418
  callbackRef.current = callback;
5227
5419
  return useCallback((...args) => callbackRef.current(...args), []);
5228
5420
  }
@@ -5234,7 +5426,7 @@ function Group(props) {
5234
5426
  label,
5235
5427
  shouldOpen = false
5236
5428
  } = props;
5237
- const groupRef = useRef$1(null);
5429
+ const groupRef = useRef(null);
5238
5430
  const [open, setOpen] = useLayoutState(['groups', id, 'open'], shouldOpen);
5239
5431
  const onShow = useCallback(() => setOpen(true), [setOpen]);
5240
5432
  const toggleOpen = () => setOpen(!open);
@@ -5296,6 +5488,7 @@ function Group(props) {
5296
5488
  edited: edited,
5297
5489
  hasErrors: hasErrors
5298
5490
  }), jsx("button", {
5491
+ type: "button",
5299
5492
  title: "Toggle section",
5300
5493
  class: "bio-properties-panel-group-header-button bio-properties-panel-arrow",
5301
5494
  children: jsx(ArrowIcon, {
@@ -5424,7 +5617,7 @@ const CodeEditor$1 = forwardRef((props, ref) => {
5424
5617
  hostLanguage = null,
5425
5618
  singleLine = false
5426
5619
  } = props;
5427
- const inputRef = useRef$1();
5620
+ const inputRef = useRef();
5428
5621
  const [editor, setEditor] = useState();
5429
5622
  const [localValue, setLocalValue] = useState(value || '');
5430
5623
  useBufferedFocus$1(editor, ref);
@@ -5476,6 +5669,7 @@ const CodeEditor$1 = forwardRef((props, ref) => {
5476
5669
  ref: inputRef,
5477
5670
  onClick: handleClick
5478
5671
  }), jsx("button", {
5672
+ type: "button",
5479
5673
  title: "Open pop-up editor",
5480
5674
  class: "bio-properties-panel-open-feel-popup",
5481
5675
  onClick: () => onPopupOpen('feelers'),
@@ -5523,7 +5717,7 @@ const CodeEditor = forwardRef((props, ref) => {
5523
5717
  tooltipContainer,
5524
5718
  variables
5525
5719
  } = props;
5526
- const inputRef = useRef$1();
5720
+ const inputRef = useRef();
5527
5721
  const [editor, setEditor] = useState();
5528
5722
  const [localValue, setLocalValue] = useState(value || '');
5529
5723
  useBufferedFocus(editor, ref);
@@ -5596,6 +5790,7 @@ const CodeEditor = forwardRef((props, ref) => {
5596
5790
  ref: inputRef,
5597
5791
  onClick: handleClick
5598
5792
  }), jsx("button", {
5793
+ type: "button",
5599
5794
  title: "Open pop-up editor",
5600
5795
  class: "bio-properties-panel-open-feel-popup",
5601
5796
  onClick: () => onPopupOpen(),
@@ -5640,6 +5835,7 @@ function FeelIcon(props) {
5640
5835
  }
5641
5836
  };
5642
5837
  return jsx("button", {
5838
+ type: "button",
5643
5839
  class: classnames('bio-properties-panel-feel-icon', active ? 'active' : null, feel === 'required' ? 'required' : 'optional'),
5644
5840
  onClick: handleClick,
5645
5841
  disabled: feel === 'required' || disabled,
@@ -5755,9 +5951,10 @@ function PopupComponent(props, globalRef) {
5755
5951
  closeOnEscape = true,
5756
5952
  title
5757
5953
  } = props;
5758
- const focusTrapRef = useRef$1(null);
5759
- const localRef = useRef$1(null);
5954
+ const focusTrapRef = useRef(null);
5955
+ const localRef = useRef(null);
5760
5956
  const popupRef = globalRef || localRef;
5957
+ const containerNode = useMemo(() => getContainerNode(container), [container]);
5761
5958
  const handleKeydown = event => {
5762
5959
  // do not allow keyboard events to bubble
5763
5960
  event.stopPropagation();
@@ -5817,7 +6014,7 @@ function PopupComponent(props, globalRef) {
5817
6014
  class: classnames('bio-properties-panel-popup', className),
5818
6015
  style: style,
5819
6016
  children: props.children
5820
- }), container || document.body);
6017
+ }), containerNode || document.body);
5821
6018
  }
5822
6019
  const Popup = forwardRef(PopupComponent);
5823
6020
  Popup.Title = Title;
@@ -5835,12 +6032,12 @@ function Title(props) {
5835
6032
 
5836
6033
  // we can't use state as we need to
5837
6034
  // manipulate this inside dragging events
5838
- const context = useRef$1({
6035
+ const context = useRef({
5839
6036
  startPosition: null,
5840
6037
  newPosition: null
5841
6038
  });
5842
- const dragPreviewRef = useRef$1();
5843
- const titleRef = useRef$1();
6039
+ const dragPreviewRef = useRef();
6040
+ const titleRef = useRef();
5844
6041
  const onMove = (event, delta) => {
5845
6042
  cancel(event);
5846
6043
  const {
@@ -5937,6 +6134,12 @@ function cancel(event) {
5937
6134
  event.preventDefault();
5938
6135
  event.stopPropagation();
5939
6136
  }
6137
+ function getContainerNode(node) {
6138
+ if (typeof node === 'string') {
6139
+ return query(node);
6140
+ }
6141
+ return node;
6142
+ }
5940
6143
  const FEEL_POPUP_WIDTH = 700;
5941
6144
  const FEEL_POPUP_HEIGHT = 250;
5942
6145
 
@@ -6047,9 +6250,9 @@ function FeelPopupComponent(props) {
6047
6250
  variables,
6048
6251
  emit
6049
6252
  } = props;
6050
- const editorRef = useRef$1();
6051
- const popupRef = useRef$1();
6052
- const isAutoCompletionOpen = useRef$1(false);
6253
+ const editorRef = useRef();
6254
+ const popupRef = useRef();
6255
+ const isAutoCompletionOpen = useRef(false);
6053
6256
  const handleSetReturnFocus = () => {
6054
6257
  sourceElement && sourceElement.focus();
6055
6258
  };
@@ -6079,6 +6282,12 @@ function FeelPopupComponent(props) {
6079
6282
  domNode: popupRef.current
6080
6283
  });
6081
6284
  }, []);
6285
+ useEffect(() => {
6286
+ // Set focus on editor when popup is opened
6287
+ if (editorRef.current) {
6288
+ editorRef.current.focus();
6289
+ }
6290
+ }, [editorRef]);
6082
6291
  return jsxs(Popup, {
6083
6292
  container: container,
6084
6293
  className: "bio-properties-panel-feel-popup",
@@ -6097,10 +6306,21 @@ function FeelPopupComponent(props) {
6097
6306
  height: FEEL_POPUP_HEIGHT,
6098
6307
  width: FEEL_POPUP_WIDTH,
6099
6308
  ref: popupRef,
6100
- children: [jsx(Popup.Title, {
6309
+ children: [jsxs(Popup.Title, {
6101
6310
  title: title,
6102
6311
  emit: emit,
6103
- draggable: true
6312
+ draggable: true,
6313
+ children: [type === 'feel' && jsxs("a", {
6314
+ href: "https://docs.camunda.io/docs/components/modeler/feel/what-is-feel/",
6315
+ target: "_blank",
6316
+ class: "bio-properties-panel-feel-popup__title-link",
6317
+ children: ["Learn FEEL expressions", jsx(HelpIcon, {})]
6318
+ }), type === 'feelers' && jsxs("a", {
6319
+ href: "https://docs.camunda.io/docs/components/modeler/forms/configuration/forms-config-templating-syntax/",
6320
+ target: "_blank",
6321
+ class: "bio-properties-panel-feel-popup__title-link",
6322
+ children: ["Learn templating", jsx(HelpIcon, {})]
6323
+ })]
6104
6324
  }), jsx(Popup.Body, {
6105
6325
  children: jsxs("div", {
6106
6326
  onKeyDownCapture: onKeyDownCapture,
@@ -6132,6 +6352,7 @@ function FeelPopupComponent(props) {
6132
6352
  })
6133
6353
  }), jsx(Popup.Footer, {
6134
6354
  children: jsx("button", {
6355
+ type: "button",
6135
6356
  onClick: onClose,
6136
6357
  title: "Close pop-up editor",
6137
6358
  class: "bio-properties-panel-feel-popup__close-btn",
@@ -6157,7 +6378,7 @@ function autoCompletionOpen(element) {
6157
6378
  * @param {Array} deps
6158
6379
  */
6159
6380
  function useUpdateEffect(effect, deps) {
6160
- const isMounted = useRef$1(false);
6381
+ const isMounted = useRef(false);
6161
6382
  useEffect(() => {
6162
6383
  if (isMounted.current) {
6163
6384
  return effect();
@@ -6461,7 +6682,7 @@ function FeelTextfield(props) {
6461
6682
  } = props;
6462
6683
  const [localValue, _setLocalValue] = useState(value);
6463
6684
  const editorRef = useShowEntryEvent(id);
6464
- const containerRef = useRef$1();
6685
+ const containerRef = useRef();
6465
6686
  const feelActive = isString(localValue) && localValue.startsWith('=') || feel === 'required';
6466
6687
  const feelOnlyValue = isString(localValue) && localValue.startsWith('=') ? localValue.substring(1) : localValue;
6467
6688
  const [focus, _setFocus] = useState(undefined);
@@ -6484,7 +6705,7 @@ function FeelTextfield(props) {
6484
6705
  }, [onInput, debounce]);
6485
6706
  const setLocalValue = newValue => {
6486
6707
  _setLocalValue(newValue);
6487
- if (!newValue || newValue === '=') {
6708
+ if (typeof newValue === 'undefined' || newValue === '' || newValue === '=') {
6488
6709
  handleInputCallback(undefined);
6489
6710
  } else {
6490
6711
  handleInputCallback(newValue);
@@ -6652,7 +6873,7 @@ const OptionalFeelInput = forwardRef((props, ref) => {
6652
6873
  onFocus,
6653
6874
  onBlur
6654
6875
  } = props;
6655
- const inputRef = useRef$1();
6876
+ const inputRef = useRef();
6656
6877
 
6657
6878
  // To be consistent with the FEEL editor, set focus at start of input
6658
6879
  // this ensures clean editing experience when switching with the keyboard
@@ -6699,7 +6920,7 @@ const OptionalFeelNumberField = forwardRef((props, ref) => {
6699
6920
  onFocus,
6700
6921
  onBlur
6701
6922
  } = props;
6702
- const inputRef = useRef$1();
6923
+ const inputRef = useRef();
6703
6924
 
6704
6925
  // To be consistent with the FEEL editor, set focus at start of input
6705
6926
  // this ensures clean editing experience when switching with the keyboard
@@ -6742,7 +6963,7 @@ forwardRef((props, ref) => {
6742
6963
  onFocus,
6743
6964
  onBlur
6744
6965
  } = props;
6745
- const inputRef = useRef$1();
6966
+ const inputRef = useRef();
6746
6967
 
6747
6968
  // To be consistent with the FEEL editor, set focus at start of input
6748
6969
  // this ensures clean editing experience when switching with the keyboard
@@ -6781,7 +7002,7 @@ const OptionalFeelToggleSwitch = forwardRef((props, ref) => {
6781
7002
  onBlur,
6782
7003
  switcherLabel
6783
7004
  } = props;
6784
- const inputRef = useRef$1();
7005
+ const inputRef = useRef();
6785
7006
 
6786
7007
  // To be consistent with the FEEL editor, set focus at start of input
6787
7008
  // this ensures clean editing experience when switching with the keyboard
@@ -6813,7 +7034,7 @@ forwardRef((props, ref) => {
6813
7034
  onFocus,
6814
7035
  onBlur
6815
7036
  } = props;
6816
- const inputRef = useRef$1();
7037
+ const inputRef = useRef();
6817
7038
  const handleChange = ({
6818
7039
  target
6819
7040
  }) => {
@@ -7327,7 +7548,7 @@ function createTooltipContext(overrides = {}) {
7327
7548
  * @param {Array} deps
7328
7549
  */
7329
7550
  function useUpdateLayoutEffect(effect, deps) {
7330
- const isMounted = useRef$1(false);
7551
+ const isMounted = useRef(false);
7331
7552
  useLayoutEffect(() => {
7332
7553
  if (isMounted.current) {
7333
7554
  return effect();
@@ -7373,12 +7594,14 @@ function CollapsibleEntry(props) {
7373
7594
  class: classnames('bio-properties-panel-collapsible-entry-header-title', !label && 'empty'),
7374
7595
  children: label || placeholderLabel
7375
7596
  }), jsx("button", {
7597
+ type: "button",
7376
7598
  title: "Toggle list item",
7377
7599
  class: "bio-properties-panel-arrow bio-properties-panel-collapsible-entry-arrow",
7378
7600
  children: jsx(ArrowIcon, {
7379
7601
  class: open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right'
7380
7602
  })
7381
7603
  }), remove ? jsx("button", {
7604
+ type: "button",
7382
7605
  title: "Delete item",
7383
7606
  class: "bio-properties-panel-remove-entry",
7384
7607
  onClick: remove,
@@ -7446,7 +7669,7 @@ function ListGroup(props) {
7446
7669
  shouldOpen = true,
7447
7670
  shouldSort = true
7448
7671
  } = props;
7449
- const groupRef = useRef$1(null);
7672
+ const groupRef = useRef(null);
7450
7673
  const [open, setOpen] = useLayoutState(['groups', id, 'open'], false);
7451
7674
  const [sticky, setSticky] = useState(false);
7452
7675
  const onShow = useCallback(() => setOpen(true), [setOpen]);
@@ -7580,6 +7803,7 @@ function ListGroup(props) {
7580
7803
  }), jsxs("div", {
7581
7804
  class: "bio-properties-panel-group-header-buttons",
7582
7805
  children: [add ? jsxs("button", {
7806
+ type: "button",
7583
7807
  title: "Create new list item",
7584
7808
  class: "bio-properties-panel-group-header-button bio-properties-panel-add-entry",
7585
7809
  onClick: handleAddClick,
@@ -7592,6 +7816,7 @@ function ListGroup(props) {
7592
7816
  class: classnames('bio-properties-panel-list-badge', hasError ? 'bio-properties-panel-list-badge--error' : ''),
7593
7817
  children: items.length
7594
7818
  }) : null, hasItems ? jsx("button", {
7819
+ type: "button",
7595
7820
  title: "Toggle section",
7596
7821
  class: "bio-properties-panel-group-header-button bio-properties-panel-arrow",
7597
7822
  children: jsx(ArrowIcon, {
@@ -8246,11 +8471,11 @@ var index = {
8246
8471
  feelPopup: ['type', FeelPopupModule]
8247
8472
  };
8248
8473
 
8249
- /**
8250
- * @param {string} type
8251
- * @param {boolean} [strict]
8252
- *
8253
- * @returns {any}
8474
+ /**
8475
+ * @param {string} type
8476
+ * @param {boolean} [strict]
8477
+ *
8478
+ * @returns {any}
8254
8479
  */
8255
8480
  function getService(type, strict) {}
8256
8481
  const PropertiesPanelContext = createContext({
@@ -8288,25 +8513,43 @@ function isValidDotPath(path) {
8288
8513
  }
8289
8514
  const INPUTS = ['checkbox', 'checklist', 'datetime', 'number', 'radio', 'select', 'taglist', 'textfield', 'textarea'];
8290
8515
  const VALUES_INPUTS = ['checklist', 'radio', 'select', 'taglist'];
8516
+ function hasEntryConfigured(formFieldDefinition, entryId) {
8517
+ const {
8518
+ propertiesPanelEntries = []
8519
+ } = formFieldDefinition;
8520
+ if (!propertiesPanelEntries.length) {
8521
+ return false;
8522
+ }
8523
+ return propertiesPanelEntries.some(id => id === entryId);
8524
+ }
8525
+ function hasValuesGroupsConfigured(formFieldDefinition) {
8526
+ const {
8527
+ propertiesPanelEntries = []
8528
+ } = formFieldDefinition;
8529
+ if (!propertiesPanelEntries.length) {
8530
+ return false;
8531
+ }
8532
+ return propertiesPanelEntries.some(id => id === 'values');
8533
+ }
8534
+
8535
+ function useService (type, strict) {
8536
+ const {
8537
+ getService
8538
+ } = useContext(FormPropertiesPanelContext);
8539
+ return getService(type, strict);
8540
+ }
8541
+
8542
+ /**
8543
+ * Retrieve list of variables from the form schema.
8544
+ *
8545
+ * @returns { string[] } list of variables used in form schema
8546
+ */
8547
+ function useVariables() {
8548
+ const form = useService('formEditor');
8549
+ const schema = form.getSchema();
8550
+ return getSchemaVariables(schema);
8551
+ }
8291
8552
 
8292
- const labelsByType = {
8293
- button: 'BUTTON',
8294
- checkbox: 'CHECKBOX',
8295
- checklist: 'CHECKLIST',
8296
- columns: 'COLUMNS',
8297
- default: 'FORM',
8298
- datetime: 'DATETIME',
8299
- group: 'GROUP',
8300
- image: 'IMAGE VIEW',
8301
- number: 'NUMBER',
8302
- radio: 'RADIO',
8303
- select: 'SELECT',
8304
- spacer: 'SPACER',
8305
- taglist: 'TAGLIST',
8306
- text: 'TEXT VIEW',
8307
- textfield: 'TEXT FIELD',
8308
- textarea: 'TEXT AREA'
8309
- };
8310
8553
  const PropertiesPanelHeaderProvider = {
8311
8554
  getElementLabel: field => {
8312
8555
  const {
@@ -8330,25 +8573,43 @@ const PropertiesPanelHeaderProvider = {
8330
8573
  const {
8331
8574
  type
8332
8575
  } = field;
8333
- const Icon = iconsByType(type);
8576
+
8577
+ // @Note: We know that we are inside the properties panel context,
8578
+ // so we can savely use the hook here.
8579
+ // eslint-disable-next-line react-hooks/rules-of-hooks
8580
+ const fieldDefinition = useService('formFields').get(type).config;
8581
+ const Icon = fieldDefinition.icon || iconsByType(type);
8334
8582
  if (Icon) {
8335
8583
  return () => jsx(Icon, {
8336
8584
  width: "36",
8337
8585
  height: "36",
8338
8586
  viewBox: "0 0 54 54"
8339
8587
  });
8588
+ } else if (fieldDefinition.iconUrl) {
8589
+ return getPaletteIcon({
8590
+ iconUrl: fieldDefinition.iconUrl,
8591
+ label: fieldDefinition.label
8592
+ });
8340
8593
  }
8341
8594
  },
8342
8595
  getTypeLabel: field => {
8343
8596
  const {
8344
8597
  type
8345
8598
  } = field;
8346
- return labelsByType[type];
8599
+ if (type === 'default') {
8600
+ return 'Form';
8601
+ }
8602
+
8603
+ // @Note: We know that we are inside the properties panel context,
8604
+ // so we can savely use the hook here.
8605
+ // eslint-disable-next-line react-hooks/rules-of-hooks
8606
+ const fieldDefinition = useService('formFields').get(type).config;
8607
+ return fieldDefinition.label || type;
8347
8608
  }
8348
8609
  };
8349
8610
 
8350
- /**
8351
- * Provide placeholders for empty and multiple state.
8611
+ /**
8612
+ * Provide placeholders for empty and multiple state.
8352
8613
  */
8353
8614
  const PropertiesPanelPlaceholderProvider = {
8354
8615
  getEmpty: () => {
@@ -8363,24 +8624,210 @@ const PropertiesPanelPlaceholderProvider = {
8363
8624
  }
8364
8625
  };
8365
8626
 
8366
- function ActionEntry(props) {
8627
+ function FormPropertiesPanel(props) {
8367
8628
  const {
8368
- editField,
8369
- field
8629
+ eventBus,
8630
+ getProviders,
8631
+ injector
8370
8632
  } = props;
8633
+ const formEditor = injector.get('formEditor');
8634
+ const modeling = injector.get('modeling');
8635
+ const selectionModule = injector.get('selection');
8636
+ const propertiesPanelConfig = injector.get('config.propertiesPanel') || {};
8371
8637
  const {
8372
- type
8373
- } = field;
8374
- const entries = [];
8375
- if (type === 'button') {
8376
- entries.push({
8377
- id: 'action',
8378
- component: Action,
8379
- editField: editField,
8380
- field: field,
8381
- isEdited: isEdited$3
8638
+ feelPopupContainer
8639
+ } = propertiesPanelConfig;
8640
+ const [state, setState] = useState({
8641
+ selectedFormField: selectionModule.get() || formEditor._getState().schema
8642
+ });
8643
+ const selectedFormField = state.selectedFormField;
8644
+ const refresh = useCallback(field => {
8645
+ // TODO(skaiir): rework state management, re-rendering the whole properties panel is not the way to go
8646
+ // https://github.com/bpmn-io/form-js/issues/686
8647
+ setState({
8648
+ selectedFormField: selectionModule.get() || formEditor._getState().schema
8649
+ });
8650
+
8651
+ // notify interested parties on property panel updates
8652
+ eventBus.fire('propertiesPanel.updated', {
8653
+ formField: field
8654
+ });
8655
+ }, [eventBus, formEditor, selectionModule]);
8656
+ useLayoutEffect(() => {
8657
+ /**
8658
+ * TODO(pinussilvestrus): update with actual updated element,
8659
+ * once we have a proper updater/change support
8660
+ */
8661
+ eventBus.on('changed', refresh);
8662
+ eventBus.on('import.done', refresh);
8663
+ eventBus.on('selection.changed', refresh);
8664
+ return () => {
8665
+ eventBus.off('changed', refresh);
8666
+ eventBus.off('import.done', refresh);
8667
+ eventBus.off('selection.changed', refresh);
8668
+ };
8669
+ }, [eventBus, refresh]);
8670
+ const getService = (type, strict = true) => injector.get(type, strict);
8671
+ const propertiesPanelContext = {
8672
+ getService
8673
+ };
8674
+ const onFocus = () => eventBus.fire('propertiesPanel.focusin');
8675
+ const onBlur = () => eventBus.fire('propertiesPanel.focusout');
8676
+ const editField = useCallback((formField, key, value) => modeling.editFormField(formField, key, value), [modeling]);
8677
+
8678
+ // retrieve groups for selected form field
8679
+ const providers = getProviders(selectedFormField);
8680
+ const groups = useMemo(() => {
8681
+ return reduce(providers, function (groups, provider) {
8682
+ // do not collect groups for multi element state
8683
+ if (isArray(selectedFormField)) {
8684
+ return [];
8685
+ }
8686
+ const updater = provider.getGroups(selectedFormField, editField);
8687
+ return updater(groups);
8688
+ }, []);
8689
+ }, [providers, selectedFormField, editField]);
8690
+ return jsx("div", {
8691
+ class: "fjs-properties-panel",
8692
+ "data-field": selectedFormField && selectedFormField.id,
8693
+ onFocusCapture: onFocus,
8694
+ onBlurCapture: onBlur,
8695
+ children: jsx(FormPropertiesPanelContext.Provider, {
8696
+ value: propertiesPanelContext,
8697
+ children: jsx(PropertiesPanel, {
8698
+ element: selectedFormField,
8699
+ eventBus: eventBus,
8700
+ groups: groups,
8701
+ headerProvider: PropertiesPanelHeaderProvider,
8702
+ placeholderProvider: PropertiesPanelPlaceholderProvider,
8703
+ feelPopupContainer: feelPopupContainer
8704
+ })
8705
+ })
8706
+ });
8707
+ }
8708
+
8709
+ const DEFAULT_PRIORITY = 1000;
8710
+
8711
+ /**
8712
+ * @typedef { { parent: Element } } PropertiesPanelConfig
8713
+ * @typedef { import('../../core/EventBus').default } EventBus
8714
+ * @typedef { import('../../types').Injector } Injector
8715
+ * @typedef { { getGroups: ({ formField, editFormField }) => ({ groups}) => Array } } PropertiesProvider
8716
+ */
8717
+
8718
+ /**
8719
+ * @param {PropertiesPanelConfig} propertiesPanelConfig
8720
+ * @param {Injector} injector
8721
+ * @param {EventBus} eventBus
8722
+ */
8723
+ class PropertiesPanelRenderer {
8724
+ constructor(propertiesPanelConfig, injector, eventBus) {
8725
+ const {
8726
+ parent
8727
+ } = propertiesPanelConfig || {};
8728
+ this._eventBus = eventBus;
8729
+ this._injector = injector;
8730
+ this._container = domify('<div class="fjs-properties-container" input-handle-modified-keys="y,z"></div>');
8731
+ if (parent) {
8732
+ this.attachTo(parent);
8733
+ }
8734
+ this._eventBus.once('formEditor.rendered', 500, () => {
8735
+ this._render();
8736
+ });
8737
+ }
8738
+
8739
+ /**
8740
+ * Attach the properties panel to a parent node.
8741
+ *
8742
+ * @param {HTMLElement} container
8743
+ */
8744
+ attachTo(container) {
8745
+ if (!container) {
8746
+ throw new Error('container required');
8747
+ }
8748
+ if (typeof container === 'string') {
8749
+ container = query(container);
8750
+ }
8751
+
8752
+ // (1) detach from old parent
8753
+ this.detach();
8754
+
8755
+ // (2) append to parent container
8756
+ container.appendChild(this._container);
8757
+
8758
+ // (3) notify interested parties
8759
+ this._eventBus.fire('propertiesPanel.attach');
8760
+ }
8761
+
8762
+ /**
8763
+ * Detach the properties panel from its parent node.
8764
+ */
8765
+ detach() {
8766
+ const parentNode = this._container.parentNode;
8767
+ if (parentNode) {
8768
+ parentNode.removeChild(this._container);
8769
+ this._eventBus.fire('propertiesPanel.detach');
8770
+ }
8771
+ }
8772
+ _render() {
8773
+ render(jsx(FormPropertiesPanel, {
8774
+ getProviders: this._getProviders.bind(this),
8775
+ eventBus: this._eventBus,
8776
+ injector: this._injector
8777
+ }), this._container);
8778
+ this._eventBus.fire('propertiesPanel.rendered');
8779
+ }
8780
+ _destroy() {
8781
+ if (this._container) {
8782
+ render(null, this._container);
8783
+ this._eventBus.fire('propertiesPanel.destroyed');
8784
+ }
8785
+ }
8786
+
8787
+ /**
8788
+ * Register a new properties provider to the properties panel.
8789
+ *
8790
+ * @param {PropertiesProvider} provider
8791
+ * @param {Number} [priority]
8792
+ */
8793
+ registerProvider(provider, priority) {
8794
+ if (!priority) {
8795
+ priority = DEFAULT_PRIORITY;
8796
+ }
8797
+ if (typeof provider.getGroups !== 'function') {
8798
+ console.error('Properties provider does not implement #getGroups(element) API');
8799
+ return;
8800
+ }
8801
+ this._eventBus.on('propertiesPanel.getProviders', priority, function (event) {
8802
+ event.providers.push(provider);
8803
+ });
8804
+ this._eventBus.fire('propertiesPanel.providersChanged');
8805
+ }
8806
+ _getProviders() {
8807
+ const event = this._eventBus.createEvent({
8808
+ type: 'propertiesPanel.getProviders',
8809
+ providers: []
8382
8810
  });
8811
+ this._eventBus.fire(event);
8812
+ return event.providers;
8383
8813
  }
8814
+ }
8815
+ PropertiesPanelRenderer.$inject = ['config.propertiesPanel', 'injector', 'eventBus'];
8816
+
8817
+ function ActionEntry(props) {
8818
+ const {
8819
+ editField,
8820
+ field
8821
+ } = props;
8822
+ const entries = [];
8823
+ entries.push({
8824
+ id: 'action',
8825
+ component: Action,
8826
+ editField: editField,
8827
+ field: field,
8828
+ isEdited: isEdited$3,
8829
+ isDefaultVisible: field => field.type === 'button'
8830
+ });
8384
8831
  return entries;
8385
8832
  }
8386
8833
  function Action(props) {
@@ -8413,42 +8860,20 @@ function Action(props) {
8413
8860
  });
8414
8861
  }
8415
8862
 
8416
- function useService (type, strict) {
8417
- const {
8418
- getService
8419
- } = useContext(FormPropertiesPanelContext);
8420
- return getService(type, strict);
8421
- }
8422
-
8423
- /**
8424
- * Retrieve list of variables from the form schema.
8425
- *
8426
- * @returns { string[] } list of variables used in form schema
8427
- */
8428
- function useVariables() {
8429
- const form = useService('formEditor');
8430
- const schema = form.getSchema();
8431
- return getSchemaVariables(schema);
8432
- }
8433
-
8434
8863
  function AltTextEntry(props) {
8435
8864
  const {
8436
8865
  editField,
8437
8866
  field
8438
8867
  } = props;
8439
- const {
8440
- type
8441
- } = field;
8442
8868
  const entries = [];
8443
- if (type === 'image') {
8444
- entries.push({
8445
- id: 'alt',
8446
- component: AltText,
8447
- editField: editField,
8448
- field: field,
8449
- isEdited: isEdited$6
8450
- });
8451
- }
8869
+ entries.push({
8870
+ id: 'alt',
8871
+ component: AltText,
8872
+ editField: editField,
8873
+ field: field,
8874
+ isEdited: isEdited$6,
8875
+ isDefaultVisible: field => ['image'].includes(field.type)
8876
+ });
8452
8877
  return entries;
8453
8878
  }
8454
8879
  function AltText(props) {
@@ -8559,19 +8984,15 @@ function DescriptionEntry(props) {
8559
8984
  editField,
8560
8985
  field
8561
8986
  } = props;
8562
- const {
8563
- type
8564
- } = field;
8565
8987
  const entries = [];
8566
- if (INPUTS.includes(type)) {
8567
- entries.push({
8568
- id: 'description',
8569
- component: Description,
8570
- editField: editField,
8571
- field: field,
8572
- isEdited: isEdited$6
8573
- });
8574
- }
8988
+ entries.push({
8989
+ id: 'description',
8990
+ component: Description,
8991
+ editField: editField,
8992
+ field: field,
8993
+ isEdited: isEdited$6,
8994
+ isDefaultVisible: field => INPUTS.includes(field.type)
8995
+ });
8575
8996
  return entries;
8576
8997
  }
8577
8998
  function Description(props) {
@@ -8613,10 +9034,14 @@ function DefaultOptionEntry(props) {
8613
9034
  type
8614
9035
  } = field;
8615
9036
  const entries = [];
8616
-
8617
- // Only make default values available when they are statically defined
8618
- if (!INPUTS.includes(type) || VALUES_INPUTS.includes(type) && !field.values) {
8619
- return entries;
9037
+ function isDefaultVisible(matchers) {
9038
+ return field => {
9039
+ // Only make default values available when they are statically defined
9040
+ if (!INPUTS.includes(type) || VALUES_INPUTS.includes(type) && !field.values) {
9041
+ return false;
9042
+ }
9043
+ return matchers(field);
9044
+ };
8620
9045
  }
8621
9046
  const defaultOptions = {
8622
9047
  editField,
@@ -8624,44 +9049,39 @@ function DefaultOptionEntry(props) {
8624
9049
  id: 'defaultValue',
8625
9050
  label: 'Default value'
8626
9051
  };
8627
- if (type === 'checkbox') {
8628
- entries.push({
8629
- ...defaultOptions,
8630
- component: DefaultValueCheckbox,
8631
- isEdited: isEdited$3
8632
- });
8633
- }
8634
- if (type === 'number') {
8635
- entries.push({
8636
- ...defaultOptions,
8637
- component: DefaultValueNumber,
8638
- isEdited: isEdited
8639
- });
8640
- }
8641
- if (type === 'radio' || type === 'select') {
8642
- entries.push({
8643
- ...defaultOptions,
8644
- component: DefaultValueSingleSelect,
8645
- isEdited: isEdited$3
8646
- });
8647
- }
9052
+ entries.push({
9053
+ ...defaultOptions,
9054
+ component: DefaultValueCheckbox,
9055
+ isEdited: isEdited$3,
9056
+ isDefaultVisible: isDefaultVisible(field => field.type === 'checkbox')
9057
+ });
9058
+ entries.push({
9059
+ ...defaultOptions,
9060
+ component: DefaultValueNumber,
9061
+ isEdited: isEdited,
9062
+ isDefaultVisible: isDefaultVisible(field => field.type === 'number')
9063
+ });
9064
+ entries.push({
9065
+ ...defaultOptions,
9066
+ component: DefaultValueSingleSelect,
9067
+ isEdited: isEdited$3,
9068
+ isDefaultVisible: isDefaultVisible(field => field.type === 'radio' || field.type === 'select')
9069
+ });
8648
9070
 
8649
9071
  // todo(Skaiir): implement a multiselect equivalent (cf. https://github.com/bpmn-io/form-js/issues/265)
8650
9072
 
8651
- if (type === 'textfield') {
8652
- entries.push({
8653
- ...defaultOptions,
8654
- component: DefaultValueTextfield,
8655
- isEdited: isEdited
8656
- });
8657
- }
8658
- if (type === 'textarea') {
8659
- entries.push({
8660
- ...defaultOptions,
8661
- component: DefaultValueTextarea,
8662
- isEdited: isEdited$1
8663
- });
8664
- }
9073
+ entries.push({
9074
+ ...defaultOptions,
9075
+ component: DefaultValueTextfield,
9076
+ isEdited: isEdited,
9077
+ isDefaultVisible: isDefaultVisible(field => field.type === 'textfield')
9078
+ });
9079
+ entries.push({
9080
+ ...defaultOptions,
9081
+ component: DefaultValueTextarea,
9082
+ isEdited: isEdited$1,
9083
+ isDefaultVisible: isDefaultVisible(field => field.type === 'textarea')
9084
+ });
8665
9085
  return entries;
8666
9086
  }
8667
9087
  function DefaultValueCheckbox(props) {
@@ -8846,19 +9266,15 @@ function DisabledEntry(props) {
8846
9266
  editField,
8847
9267
  field
8848
9268
  } = props;
8849
- const {
8850
- type
8851
- } = field;
8852
9269
  const entries = [];
8853
- if (INPUTS.includes(type)) {
8854
- entries.push({
8855
- id: 'disabled',
8856
- component: Disabled,
8857
- editField: editField,
8858
- field: field,
8859
- isEdited: isEdited$8
8860
- });
8861
- }
9270
+ entries.push({
9271
+ id: 'disabled',
9272
+ component: Disabled,
9273
+ editField: editField,
9274
+ field: field,
9275
+ isEdited: isEdited$8,
9276
+ isDefaultVisible: field => INPUTS.includes(field.type)
9277
+ });
8862
9278
  return entries;
8863
9279
  }
8864
9280
  function Disabled(props) {
@@ -8891,15 +9307,14 @@ function IdEntry(props) {
8891
9307
  field
8892
9308
  } = props;
8893
9309
  const entries = [];
8894
- if (field.type === 'default') {
8895
- entries.push({
8896
- id: 'id',
8897
- component: Id,
8898
- editField: editField,
8899
- field: field,
8900
- isEdited: isEdited
8901
- });
8902
- }
9310
+ entries.push({
9311
+ id: 'id',
9312
+ component: Id,
9313
+ editField: editField,
9314
+ field: field,
9315
+ isEdited: isEdited,
9316
+ isDefaultVisible: field => field.type === 'default'
9317
+ });
8903
9318
  return entries;
8904
9319
  }
8905
9320
  function Id(props) {
@@ -8970,19 +9385,15 @@ function KeyEntry(props) {
8970
9385
  editField,
8971
9386
  field
8972
9387
  } = props;
8973
- const {
8974
- type
8975
- } = field;
8976
9388
  const entries = [];
8977
- if (INPUTS.includes(type)) {
8978
- entries.push({
8979
- id: 'key',
8980
- component: Key$1,
8981
- editField: editField,
8982
- field: field,
8983
- isEdited: isEdited
8984
- });
8985
- }
9389
+ entries.push({
9390
+ id: 'key',
9391
+ component: Key$1,
9392
+ editField: editField,
9393
+ field: field,
9394
+ isEdited: isEdited,
9395
+ isDefaultVisible: field => INPUTS.includes(field.type)
9396
+ });
8986
9397
  return entries;
8987
9398
  }
8988
9399
  function Key$1(props) {
@@ -9128,7 +9539,8 @@ function simpleBoolEntryFactory(options) {
9128
9539
  label,
9129
9540
  description,
9130
9541
  path,
9131
- props
9542
+ props,
9543
+ isDefaultVisible
9132
9544
  } = options;
9133
9545
  const {
9134
9546
  editField,
@@ -9142,7 +9554,8 @@ function simpleBoolEntryFactory(options) {
9142
9554
  editField,
9143
9555
  description,
9144
9556
  component: SimpleBoolComponent,
9145
- isEdited: isEdited$5
9557
+ isEdited: isEdited$5,
9558
+ isDefaultVisible
9146
9559
  };
9147
9560
  }
9148
9561
  const SimpleBoolComponent = props => {
@@ -9190,39 +9603,35 @@ function LabelEntry(props) {
9190
9603
  field,
9191
9604
  editField
9192
9605
  } = props;
9193
- const {
9194
- type,
9195
- subtype
9196
- } = field;
9197
9606
  const entries = [];
9198
- if (type === 'datetime') {
9199
- if (subtype === DATETIME_SUBTYPES.DATE || subtype === DATETIME_SUBTYPES.DATETIME) {
9200
- entries.push({
9201
- id: 'date-label',
9202
- component: DateLabel,
9203
- editField,
9204
- field,
9205
- isEdited: isEdited$6
9206
- });
9607
+ entries.push({
9608
+ id: 'date-label',
9609
+ component: DateLabel,
9610
+ editField,
9611
+ field,
9612
+ isEdited: isEdited$6,
9613
+ isDefaultVisible: function (field) {
9614
+ return field.type === 'datetime' && (field.subtype === DATETIME_SUBTYPES.DATE || field.subtype === DATETIME_SUBTYPES.DATETIME);
9207
9615
  }
9208
- if (subtype === DATETIME_SUBTYPES.TIME || subtype === DATETIME_SUBTYPES.DATETIME) {
9209
- entries.push({
9210
- id: 'time-label',
9211
- component: TimeLabel,
9212
- editField,
9213
- field,
9214
- isEdited: isEdited$6
9215
- });
9616
+ });
9617
+ entries.push({
9618
+ id: 'time-label',
9619
+ component: TimeLabel,
9620
+ editField,
9621
+ field,
9622
+ isEdited: isEdited$6,
9623
+ isDefaultVisible: function (field) {
9624
+ return field.type === 'datetime' && (field.subtype === DATETIME_SUBTYPES.TIME || field.subtype === DATETIME_SUBTYPES.DATETIME);
9216
9625
  }
9217
- } else if (INPUTS.includes(type) || type === 'button' || type === 'group') {
9218
- entries.push({
9219
- id: 'label',
9220
- component: Label$1,
9221
- editField,
9222
- field,
9223
- isEdited: isEdited$6
9224
- });
9225
- }
9626
+ });
9627
+ entries.push({
9628
+ id: 'label',
9629
+ component: Label$1,
9630
+ editField,
9631
+ field,
9632
+ isEdited: isEdited$6,
9633
+ isDefaultVisible: field => INPUTS.includes(field.type) || field.type === 'button' || field.type === 'group'
9634
+ });
9226
9635
  return entries;
9227
9636
  }
9228
9637
  function Label$1(props) {
@@ -9316,19 +9725,15 @@ function SourceEntry(props) {
9316
9725
  editField,
9317
9726
  field
9318
9727
  } = props;
9319
- const {
9320
- type
9321
- } = field;
9322
9728
  const entries = [];
9323
- if (type === 'image') {
9324
- entries.push({
9325
- id: 'source',
9326
- component: Source,
9327
- editField: editField,
9328
- field: field,
9329
- isEdited: isEdited$6
9330
- });
9331
- }
9729
+ entries.push({
9730
+ id: 'source',
9731
+ component: Source,
9732
+ editField: editField,
9733
+ field: field,
9734
+ isEdited: isEdited$6,
9735
+ isDefaultVisible: field => field.type === 'image'
9736
+ });
9332
9737
  return entries;
9333
9738
  }
9334
9739
  function Source(props) {
@@ -9366,24 +9771,15 @@ function Source(props) {
9366
9771
  function TextEntry(props) {
9367
9772
  const {
9368
9773
  editField,
9369
- /* getService, */
9370
9774
  field
9371
9775
  } = props;
9372
- const {
9373
- type
9374
- } = field;
9375
-
9376
- // const templating = getService('templating');
9377
-
9378
- if (type !== 'text') {
9379
- return [];
9380
- }
9381
9776
  const entries = [{
9382
9777
  id: 'text',
9383
9778
  component: Text,
9384
9779
  editField: editField,
9385
9780
  field: field,
9386
- isEdited: isEdited$6
9781
+ isEdited: isEdited$6,
9782
+ isDefaultVisible: field => field.type === 'text'
9387
9783
  }];
9388
9784
 
9389
9785
  // todo: skipped to make the release without too much risk
@@ -9442,19 +9838,14 @@ function SpacerEntry(props) {
9442
9838
  field,
9443
9839
  id
9444
9840
  } = props;
9445
- const {
9446
- type
9447
- } = field;
9448
- if (type !== 'spacer') {
9449
- return [];
9450
- }
9451
9841
  const entries = [];
9452
9842
  entries.push({
9453
9843
  id: id + '-height',
9454
9844
  component: SpacerHeight,
9455
9845
  isEdited: isEdited$7,
9456
9846
  editField,
9457
- field
9847
+ field,
9848
+ isDefaultVisible: field => field.type === 'spacer'
9458
9849
  });
9459
9850
  return entries;
9460
9851
  }
@@ -9493,26 +9884,22 @@ function NumberEntries(props) {
9493
9884
  field,
9494
9885
  id
9495
9886
  } = props;
9496
- const {
9497
- type
9498
- } = field;
9499
- if (type !== 'number') {
9500
- return [];
9501
- }
9502
9887
  const entries = [];
9503
9888
  entries.push({
9504
9889
  id: id + '-decimalDigits',
9505
9890
  component: NumberDecimalDigits,
9506
9891
  isEdited: isEdited$7,
9507
9892
  editField,
9508
- field
9893
+ field,
9894
+ isDefaultVisible: field => field.type === 'number'
9509
9895
  });
9510
9896
  entries.push({
9511
9897
  id: id + '-step',
9512
9898
  component: NumberArrowStep,
9513
9899
  isEdited: isEdited,
9514
9900
  editField,
9515
- field
9901
+ field,
9902
+ isDefaultVisible: field => field.type === 'number'
9516
9903
  });
9517
9904
  return entries;
9518
9905
  }
@@ -9597,19 +9984,14 @@ function NumberSerializationEntry(props) {
9597
9984
  editField,
9598
9985
  field
9599
9986
  } = props;
9600
- const {
9601
- type
9602
- } = field;
9603
- if (type !== 'number') {
9604
- return [];
9605
- }
9606
9987
  const entries = [];
9607
9988
  entries.push({
9608
9989
  id: 'serialize-to-string',
9609
9990
  component: SerializeToString,
9610
9991
  isEdited: isEdited$5,
9611
9992
  editField,
9612
- field
9993
+ field,
9994
+ isDefaultVisible: field => field.type === 'number'
9613
9995
  });
9614
9996
  return entries;
9615
9997
  }
@@ -9648,29 +10030,22 @@ function DateTimeEntry(props) {
9648
10030
  editField,
9649
10031
  field
9650
10032
  } = props;
9651
- const {
9652
- type,
9653
- subtype
9654
- } = field;
9655
- if (type !== 'datetime') {
9656
- return [];
9657
- }
9658
10033
  const entries = [{
9659
10034
  id: 'subtype',
9660
10035
  component: DateTimeSubtypeSelect,
9661
10036
  isEdited: isEdited$3,
9662
10037
  editField,
9663
- field
10038
+ field,
10039
+ isDefaultVisible: field => field.type === 'datetime'
9664
10040
  }];
9665
- if (subtype === DATETIME_SUBTYPES.TIME || subtype === DATETIME_SUBTYPES.DATETIME) {
9666
- entries.push({
9667
- id: 'use24h',
9668
- component: Use24h,
9669
- isEdited: isEdited$5,
9670
- editField,
9671
- field
9672
- });
9673
- }
10041
+ entries.push({
10042
+ id: 'use24h',
10043
+ component: Use24h,
10044
+ isEdited: isEdited$5,
10045
+ editField,
10046
+ field,
10047
+ isDefaultVisible: field => field.type === 'datetime' && (field.subtype === DATETIME_SUBTYPES.TIME || field.subtype === DATETIME_SUBTYPES.DATETIME)
10048
+ });
9674
10049
  return entries;
9675
10050
  }
9676
10051
  function DateTimeSubtypeSelect(props) {
@@ -9758,32 +10133,31 @@ function DateTimeConstraintsEntry(props) {
9758
10133
  field,
9759
10134
  id
9760
10135
  } = props;
9761
- const {
9762
- type,
9763
- subtype
9764
- } = field;
9765
- if (type !== 'datetime') {
9766
- return [];
10136
+ function isDefaultVisible(subtypes) {
10137
+ return field => {
10138
+ if (field.type !== 'datetime') {
10139
+ return false;
10140
+ }
10141
+ return subtypes.includes(field.subtype);
10142
+ };
9767
10143
  }
9768
10144
  const entries = [];
9769
- if (subtype === DATETIME_SUBTYPES.TIME || subtype === DATETIME_SUBTYPES.DATETIME) {
9770
- entries.push({
9771
- id: id + '-timeInterval',
9772
- component: TimeIntervalSelect,
9773
- isEdited: isEdited$3,
9774
- editField,
9775
- field
9776
- });
9777
- }
9778
- if (subtype === DATETIME_SUBTYPES.DATE || subtype === DATETIME_SUBTYPES.DATETIME) {
9779
- entries.push({
9780
- id: id + '-disallowPassedDates',
9781
- component: DisallowPassedDates,
9782
- isEdited: isEdited$5,
9783
- editField,
9784
- field
9785
- });
9786
- }
10145
+ entries.push({
10146
+ id: id + '-timeInterval',
10147
+ component: TimeIntervalSelect,
10148
+ isEdited: isEdited$3,
10149
+ editField,
10150
+ field,
10151
+ isDefaultVisible: isDefaultVisible([DATETIME_SUBTYPES.TIME, DATETIME_SUBTYPES.DATETIME])
10152
+ });
10153
+ entries.push({
10154
+ id: id + '-disallowPassedDates',
10155
+ component: DisallowPassedDates,
10156
+ isEdited: isEdited$5,
10157
+ editField,
10158
+ field,
10159
+ isDefaultVisible: isDefaultVisible([DATETIME_SUBTYPES.DATE, DATETIME_SUBTYPES.DATETIME])
10160
+ });
9787
10161
  return entries;
9788
10162
  }
9789
10163
  function DisallowPassedDates(props) {
@@ -9837,23 +10211,15 @@ function DateTimeFormatEntry(props) {
9837
10211
  editField,
9838
10212
  field
9839
10213
  } = props;
9840
- const {
9841
- type,
9842
- subtype
9843
- } = field;
9844
- if (type !== 'datetime') {
9845
- return [];
9846
- }
9847
10214
  const entries = [];
9848
- if (subtype === DATETIME_SUBTYPES.TIME || subtype === DATETIME_SUBTYPES.DATETIME) {
9849
- entries.push({
9850
- id: 'time-format',
9851
- component: TimeFormatSelect,
9852
- isEdited: isEdited$3,
9853
- editField,
9854
- field
9855
- });
9856
- }
10215
+ entries.push({
10216
+ id: 'time-format',
10217
+ component: TimeFormatSelect,
10218
+ isEdited: isEdited$3,
10219
+ editField,
10220
+ field,
10221
+ isDefaultVisible: field => field.type === 'datetime' && (field.subtype === DATETIME_SUBTYPES.TIME || field.subtype === DATETIME_SUBTYPES.DATETIME)
10222
+ });
9857
10223
  return entries;
9858
10224
  }
9859
10225
  function TimeFormatSelect(props) {
@@ -9881,20 +10247,12 @@ function TimeFormatSelect(props) {
9881
10247
  }
9882
10248
 
9883
10249
  function SelectEntries(props) {
9884
- const {
9885
- field
9886
- } = props;
9887
- const {
9888
- type
9889
- } = field;
9890
- if (type !== 'select') {
9891
- return [];
9892
- }
9893
10250
  const entries = [simpleBoolEntryFactory({
9894
10251
  id: 'searchable',
9895
10252
  path: ['searchable'],
9896
10253
  label: 'Searchable',
9897
- props
10254
+ props,
10255
+ isDefaultVisible: field => field.type === 'select'
9898
10256
  })];
9899
10257
  return entries;
9900
10258
  }
@@ -10074,14 +10432,14 @@ function Value(props) {
10074
10432
 
10075
10433
  // helpers //////////
10076
10434
 
10077
- /**
10078
- * Returns copy of object with updated value.
10079
- *
10080
- * @param {Object} properties
10081
- * @param {string} key
10082
- * @param {string} value
10083
- *
10084
- * @returns {Object}
10435
+ /**
10436
+ * Returns copy of object with updated value.
10437
+ *
10438
+ * @param {Object} properties
10439
+ * @param {string} key
10440
+ * @param {string} value
10441
+ *
10442
+ * @returns {Object}
10085
10443
  */
10086
10444
  function updateValue(properties, key, value) {
10087
10445
  return {
@@ -10090,14 +10448,14 @@ function updateValue(properties, key, value) {
10090
10448
  };
10091
10449
  }
10092
10450
 
10093
- /**
10094
- * Returns copy of object with updated key.
10095
- *
10096
- * @param {Object} properties
10097
- * @param {string} oldKey
10098
- * @param {string} newKey
10099
- *
10100
- * @returns {Object}
10451
+ /**
10452
+ * Returns copy of object with updated key.
10453
+ *
10454
+ * @param {Object} properties
10455
+ * @param {string} oldKey
10456
+ * @param {string} newKey
10457
+ *
10458
+ * @returns {Object}
10101
10459
  */
10102
10460
  function updateKey(properties, oldKey, newKey) {
10103
10461
  return Object.entries(properties).reduce((newProperties, entry) => {
@@ -10161,11 +10519,7 @@ function ValuesSourceSelect(props) {
10161
10519
  const setValue = value => {
10162
10520
  let newField = field;
10163
10521
  const newProperties = {};
10164
- Object.values(VALUES_SOURCES).forEach(source => {
10165
- // Clear all values source definitions and default the newly selected one
10166
- const newValue = value === source ? VALUES_SOURCES_DEFAULTS[source] : undefined;
10167
- newProperties[VALUES_SOURCES_PATHS[source]] = newValue;
10168
- });
10522
+ newProperties[VALUES_SOURCES_PATHS[value]] = VALUES_SOURCES_DEFAULTS[value];
10169
10523
  newField = editField(field, newProperties);
10170
10524
  return newField;
10171
10525
  };
@@ -10339,9 +10693,6 @@ function AdornerEntry(props) {
10339
10693
  editField,
10340
10694
  field
10341
10695
  } = props;
10342
- const {
10343
- type
10344
- } = field;
10345
10696
  const entries = [];
10346
10697
  const onChange = key => {
10347
10698
  return value => {
@@ -10354,26 +10705,26 @@ function AdornerEntry(props) {
10354
10705
  return get(field, ['appearance', key]);
10355
10706
  };
10356
10707
  };
10357
- if (['number', 'textfield'].includes(type)) {
10358
- entries.push({
10359
- id: 'prefix-adorner',
10360
- component: PrefixAdorner,
10361
- isEdited: isEdited$6,
10362
- editField,
10363
- field,
10364
- onChange,
10365
- getValue
10366
- });
10367
- entries.push({
10368
- id: 'suffix-adorner',
10369
- component: SuffixAdorner,
10370
- isEdited: isEdited$6,
10371
- editField,
10372
- field,
10373
- onChange,
10374
- getValue
10375
- });
10376
- }
10708
+ entries.push({
10709
+ id: 'prefix-adorner',
10710
+ component: PrefixAdorner,
10711
+ isEdited: isEdited$6,
10712
+ editField,
10713
+ field,
10714
+ onChange,
10715
+ getValue,
10716
+ isDefaultVisible: field => ['number', 'textfield'].includes(field.type)
10717
+ });
10718
+ entries.push({
10719
+ id: 'suffix-adorner',
10720
+ component: SuffixAdorner,
10721
+ isEdited: isEdited$6,
10722
+ editField,
10723
+ field,
10724
+ onChange,
10725
+ getValue,
10726
+ isDefaultVisible: field => ['number', 'textfield'].includes(field.type)
10727
+ });
10377
10728
  return entries;
10378
10729
  }
10379
10730
  function PrefixAdorner(props) {
@@ -10427,19 +10778,15 @@ function ReadonlyEntry(props) {
10427
10778
  editField,
10428
10779
  field
10429
10780
  } = props;
10430
- const {
10431
- type
10432
- } = field;
10433
10781
  const entries = [];
10434
- if (INPUTS.includes(type)) {
10435
- entries.push({
10436
- id: 'readonly',
10437
- component: Readonly,
10438
- editField: editField,
10439
- field: field,
10440
- isEdited: isEdited$6
10441
- });
10442
- }
10782
+ entries.push({
10783
+ id: 'readonly',
10784
+ component: Readonly,
10785
+ editField: editField,
10786
+ field: field,
10787
+ isEdited: isEdited$6,
10788
+ isDefaultVisible: field => INPUTS.includes(field.type)
10789
+ });
10443
10790
  return entries;
10444
10791
  }
10445
10792
  function Readonly(props) {
@@ -10623,6 +10970,9 @@ function GeneralGroup(field, editField, getService) {
10623
10970
  field,
10624
10971
  editField
10625
10972
  })];
10973
+ if (entries.length === 0) {
10974
+ return null;
10975
+ }
10626
10976
  return {
10627
10977
  id: 'general',
10628
10978
  label: 'General',
@@ -10683,9 +11033,6 @@ function ValidationGroup(field, editField) {
10683
11033
  } = field;
10684
11034
  const validate = get(field, ['validate'], {});
10685
11035
  const isCustomValidation = [undefined, VALIDATION_TYPE_OPTIONS.custom.value].includes(validate.validationType);
10686
- if (!INPUTS.includes(type)) {
10687
- return null;
10688
- }
10689
11036
  const onChange = key => {
10690
11037
  return value => {
10691
11038
  const validate = get(field, ['validate'], {});
@@ -10703,63 +11050,62 @@ function ValidationGroup(field, editField) {
10703
11050
  getValue,
10704
11051
  field,
10705
11052
  isEdited: isEdited$5,
10706
- onChange
11053
+ onChange,
11054
+ isDefaultVisible: field => INPUTS.includes(field.type)
10707
11055
  }];
10708
- if (type === 'textfield') {
10709
- entries.push({
10710
- id: 'validationType',
10711
- component: ValidationType,
10712
- getValue,
10713
- field,
10714
- editField,
10715
- isEdited: isEdited,
10716
- onChange
10717
- });
10718
- }
10719
- if (type === 'textarea' || type === 'textfield' && isCustomValidation) {
10720
- entries.push({
10721
- id: 'minLength',
10722
- component: MinLength,
10723
- getValue,
10724
- field,
10725
- isEdited: isEdited$6,
10726
- onChange
10727
- }, {
10728
- id: 'maxLength',
10729
- component: MaxLength,
10730
- getValue,
10731
- field,
10732
- isEdited: isEdited$6,
10733
- onChange
10734
- });
10735
- }
10736
- if (type === 'textfield' && isCustomValidation) {
10737
- entries.push({
10738
- id: 'pattern',
10739
- component: Pattern,
10740
- getValue,
10741
- field,
10742
- isEdited: isEdited,
10743
- onChange
10744
- });
10745
- }
10746
- if (type === 'number') {
10747
- entries.push({
10748
- id: 'min',
10749
- component: Min,
10750
- getValue,
10751
- field,
10752
- isEdited: isEdited$6,
10753
- onChange
10754
- }, {
10755
- id: 'max',
10756
- component: Max,
10757
- getValue,
10758
- field,
10759
- isEdited: isEdited$6,
10760
- onChange
10761
- });
10762
- }
11056
+ entries.push({
11057
+ id: 'validationType',
11058
+ component: ValidationType,
11059
+ getValue,
11060
+ field,
11061
+ editField,
11062
+ isEdited: isEdited,
11063
+ onChange,
11064
+ isDefaultVisible: field => field.type === 'textfield'
11065
+ });
11066
+ entries.push({
11067
+ id: 'minLength',
11068
+ component: MinLength,
11069
+ getValue,
11070
+ field,
11071
+ isEdited: isEdited$6,
11072
+ onChange,
11073
+ isDefaultVisible: field => INPUTS.includes(field.type) && (type === 'textarea' || type === 'textfield' && isCustomValidation)
11074
+ }, {
11075
+ id: 'maxLength',
11076
+ component: MaxLength,
11077
+ getValue,
11078
+ field,
11079
+ isEdited: isEdited$6,
11080
+ onChange,
11081
+ isDefaultVisible: field => INPUTS.includes(field.type) && (type === 'textarea' || type === 'textfield' && isCustomValidation)
11082
+ });
11083
+ entries.push({
11084
+ id: 'pattern',
11085
+ component: Pattern,
11086
+ getValue,
11087
+ field,
11088
+ isEdited: isEdited,
11089
+ onChange,
11090
+ isDefaultVisible: field => INPUTS.includes(field.type) && type === 'textfield' && isCustomValidation
11091
+ });
11092
+ entries.push({
11093
+ id: 'min',
11094
+ component: Min,
11095
+ getValue,
11096
+ field,
11097
+ isEdited: isEdited$6,
11098
+ onChange,
11099
+ isDefaultVisible: field => field.type === 'number'
11100
+ }, {
11101
+ id: 'max',
11102
+ component: Max,
11103
+ getValue,
11104
+ field,
11105
+ isEdited: isEdited$6,
11106
+ onChange,
11107
+ isDefaultVisible: field => field.type === 'number'
11108
+ });
10763
11109
  return {
10764
11110
  id: 'validation',
10765
11111
  label: 'Validation',
@@ -10913,12 +11259,14 @@ function ValidationType(props) {
10913
11259
  });
10914
11260
  }
10915
11261
 
10916
- function ValuesGroups(field, editField) {
11262
+ function ValuesGroups(field, editField, getService) {
10917
11263
  const {
10918
11264
  type,
10919
11265
  id: fieldId
10920
11266
  } = field;
10921
- if (!VALUES_INPUTS.includes(type)) {
11267
+ const formFields = getService('formFields');
11268
+ const fieldDefinition = formFields.get(type).config;
11269
+ if (!VALUES_INPUTS.includes(type) && !hasValuesGroupsConfigured(fieldDefinition)) {
10922
11270
  return [];
10923
11271
  }
10924
11272
  const context = {
@@ -10927,8 +11275,8 @@ function ValuesGroups(field, editField) {
10927
11275
  };
10928
11276
  const valuesSourceId = `${fieldId}-valuesSource`;
10929
11277
 
10930
- /**
10931
- * @type {Array<Group|ListGroup>}
11278
+ /**
11279
+ * @type {Array<Group|ListGroup>}
10932
11280
  */
10933
11281
  const groups = [{
10934
11282
  id: valuesSourceId,
@@ -11049,13 +11397,13 @@ function CustomPropertiesGroup(field, editField) {
11049
11397
 
11050
11398
  // helpers //////////
11051
11399
 
11052
- /**
11053
- * Returns copy of object without key.
11054
- *
11055
- * @param {Object} properties
11056
- * @param {string} oldKey
11057
- *
11058
- * @returns {Object}
11400
+ /**
11401
+ * Returns copy of object without key.
11402
+ *
11403
+ * @param {Object} properties
11404
+ * @param {string} oldKey
11405
+ *
11406
+ * @returns {Object}
11059
11407
  */
11060
11408
  function removeKey(properties, oldKey) {
11061
11409
  return Object.entries(properties).reduce((newProperties, entry) => {
@@ -11124,158 +11472,64 @@ function ConditionGroup(field, editField) {
11124
11472
  };
11125
11473
  }
11126
11474
 
11127
- function getGroups(field, editField, getService) {
11128
- if (!field) {
11129
- return [];
11130
- }
11131
- const groups = [GeneralGroup(field, editField, getService), ConditionGroup(field, editField), LayoutGroup(field, editField), AppearanceGroup(field, editField), SerializationGroup(field, editField), ...ValuesGroups(field, editField), ConstraintsGroup(field, editField), ValidationGroup(field, editField), CustomPropertiesGroup(field, editField)];
11132
-
11133
- // contract: if a group returns null, it should not be displayed at all
11134
- return groups.filter(group => group !== null);
11135
- }
11136
- function FormPropertiesPanel(props) {
11137
- const {
11138
- eventBus,
11139
- injector
11140
- } = props;
11141
- const formEditor = injector.get('formEditor');
11142
- const modeling = injector.get('modeling');
11143
- const selectionModule = injector.get('selection');
11144
- const propertiesPanelConfig = injector.get('config.propertiesPanel') || {};
11145
- const {
11146
- feelPopupContainer
11147
- } = propertiesPanelConfig;
11148
- const [state, setState] = useState({
11149
- selectedFormField: selectionModule.get() || formEditor._getState().schema
11150
- });
11151
- const selectedFormField = state.selectedFormField;
11152
- const refresh = useCallback(field => {
11153
- // TODO(skaiir): rework state management, re-rendering the whole properties panel is not the way to go
11154
- // https://github.com/bpmn-io/form-js/issues/686
11155
- setState({
11156
- selectedFormField: selectionModule.get() || formEditor._getState().schema
11157
- });
11158
-
11159
- // notify interested parties on property panel updates
11160
- eventBus.fire('propertiesPanel.updated', {
11161
- formField: field
11162
- });
11163
- }, [eventBus, formEditor, selectionModule]);
11164
- useLayoutEffect(() => {
11165
- /**
11166
- * TODO(pinussilvestrus): update with actual updated element,
11167
- * once we have a proper updater/change support
11168
- */
11169
- eventBus.on('changed', refresh);
11170
- eventBus.on('import.done', refresh);
11171
- eventBus.on('selection.changed', refresh);
11172
- return () => {
11173
- eventBus.off('changed', refresh);
11174
- eventBus.off('import.done', refresh);
11175
- eventBus.off('selection.changed', refresh);
11176
- };
11177
- }, [eventBus, refresh]);
11178
- const getService = (type, strict = true) => injector.get(type, strict);
11179
- const propertiesPanelContext = {
11180
- getService
11181
- };
11182
- const onFocus = () => eventBus.fire('propertiesPanel.focusin');
11183
- const onBlur = () => eventBus.fire('propertiesPanel.focusout');
11184
- const editField = useCallback((formField, key, value) => modeling.editFormField(formField, key, value), [modeling]);
11185
- return jsx("div", {
11186
- class: "fjs-properties-panel",
11187
- "data-field": selectedFormField && selectedFormField.id,
11188
- onFocusCapture: onFocus,
11189
- onBlurCapture: onBlur,
11190
- children: jsx(FormPropertiesPanelContext.Provider, {
11191
- value: propertiesPanelContext,
11192
- children: jsx(PropertiesPanel, {
11193
- element: selectedFormField,
11194
- eventBus: eventBus,
11195
- groups: getGroups(selectedFormField, editField, getService),
11196
- headerProvider: PropertiesPanelHeaderProvider,
11197
- placeholderProvider: PropertiesPanelPlaceholderProvider,
11198
- feelPopupContainer: feelPopupContainer
11199
- })
11200
- })
11201
- });
11202
- }
11203
-
11204
- class PropertiesPanelRenderer {
11205
- constructor(propertiesPanelConfig, injector, eventBus) {
11206
- const {
11207
- parent
11208
- } = propertiesPanelConfig || {};
11209
- this._eventBus = eventBus;
11475
+ class PropertiesProvider {
11476
+ constructor(propertiesPanel, injector) {
11210
11477
  this._injector = injector;
11211
- this._container = domify('<div class="fjs-properties-container" input-handle-modified-keys="y,z"></div>');
11212
- if (parent) {
11213
- this.attachTo(parent);
11214
- }
11215
- this._eventBus.once('formEditor.rendered', 500, () => {
11216
- this._render();
11217
- });
11478
+ propertiesPanel.registerProvider(this);
11218
11479
  }
11219
-
11220
- /**
11221
- * Attach the properties panel to a parent node.
11222
- *
11223
- * @param {HTMLElement} container
11224
- */
11225
- attachTo(container) {
11226
- if (!container) {
11227
- throw new Error('container required');
11228
- }
11229
- if (typeof container === 'string') {
11230
- container = query(container);
11231
- }
11232
-
11233
- // (1) detach from old parent
11234
- this.detach();
11235
-
11236
- // (2) append to parent container
11237
- container.appendChild(this._container);
11238
-
11239
- // (3) notify interested parties
11240
- this._eventBus.fire('propertiesPanel.attach');
11480
+ _filterVisibleEntries(groups, field, getService) {
11481
+ return groups.forEach(group => {
11482
+ const {
11483
+ entries
11484
+ } = group;
11485
+ const {
11486
+ type
11487
+ } = field;
11488
+ const formFields = getService('formFields');
11489
+ const fieldDefinition = formFields.get(type).config;
11490
+ if (!entries) {
11491
+ return;
11492
+ }
11493
+ group.entries = entries.filter(entry => {
11494
+ const {
11495
+ isDefaultVisible
11496
+ } = entry;
11497
+ if (!isDefaultVisible) {
11498
+ return true;
11499
+ }
11500
+ return isDefaultVisible(field) || hasEntryConfigured(fieldDefinition, entry.id);
11501
+ });
11502
+ });
11241
11503
  }
11504
+ getGroups(field, editField) {
11505
+ return groups => {
11506
+ if (!field) {
11507
+ return groups;
11508
+ }
11509
+ const getService = (type, strict = true) => this._injector.get(type, strict);
11510
+ groups = [...groups, GeneralGroup(field, editField, getService), ConditionGroup(field, editField), LayoutGroup(field, editField), AppearanceGroup(field, editField), SerializationGroup(field, editField), ...ValuesGroups(field, editField, getService), ConstraintsGroup(field, editField), ValidationGroup(field, editField), CustomPropertiesGroup(field, editField)].filter(group => group != null);
11511
+ this._filterVisibleEntries(groups, field, getService);
11242
11512
 
11243
- /**
11244
- * Detach the properties panel from its parent node.
11245
- */
11246
- detach() {
11247
- const parentNode = this._container.parentNode;
11248
- if (parentNode) {
11249
- parentNode.removeChild(this._container);
11250
- this._eventBus.fire('propertiesPanel.detach');
11251
- }
11252
- }
11253
- _render() {
11254
- render(jsx(FormPropertiesPanel, {
11255
- eventBus: this._eventBus,
11256
- injector: this._injector
11257
- }), this._container);
11258
- this._eventBus.fire('propertiesPanel.rendered');
11259
- }
11260
- _destroy() {
11261
- if (this._container) {
11262
- render(null, this._container);
11263
- this._eventBus.fire('propertiesPanel.destroyed');
11264
- }
11513
+ // contract: if a group has no entries or items, it should not be displayed at all
11514
+ return groups.filter(group => {
11515
+ return group.items || group.entries && group.entries.length;
11516
+ });
11517
+ };
11265
11518
  }
11266
11519
  }
11267
- PropertiesPanelRenderer.$inject = ['config.propertiesPanel', 'injector', 'eventBus'];
11520
+ PropertiesProvider.$inject = ['propertiesPanel', 'injector'];
11268
11521
 
11269
11522
  var PropertiesPanelModule = {
11270
11523
  __depends__: [index],
11271
- __init__: ['propertiesPanel'],
11272
- propertiesPanel: ['type', PropertiesPanelRenderer]
11524
+ __init__: ['propertiesPanel', 'propertiesProvider'],
11525
+ propertiesPanel: ['type', PropertiesPanelRenderer],
11526
+ propertiesProvider: ['type', PropertiesProvider]
11273
11527
  };
11274
11528
 
11275
- /**
11276
- * Manages the rendering of visual plugins.
11277
- * @constructor
11278
- * @param {Object} eventBus - Event bus for the application.
11529
+ /**
11530
+ * Manages the rendering of visual plugins.
11531
+ * @constructor
11532
+ * @param {Object} eventBus - Event bus for the application.
11279
11533
  */
11280
11534
  class RenderInjector extends SectionModuleBase {
11281
11535
  constructor(eventBus) {
@@ -11284,10 +11538,10 @@ class RenderInjector extends SectionModuleBase {
11284
11538
  this.registeredRenderers = [];
11285
11539
  }
11286
11540
 
11287
- /**
11288
- * Inject a new renderer into the injector.
11289
- * @param {string} identifier - Identifier for the renderer.
11290
- * @param {Function} Renderer - The renderer function.
11541
+ /**
11542
+ * Inject a new renderer into the injector.
11543
+ * @param {string} identifier - Identifier for the renderer.
11544
+ * @param {Function} Renderer - The renderer function.
11291
11545
  */
11292
11546
  attachRenderer(identifier, Renderer) {
11293
11547
  this.registeredRenderers = [...this.registeredRenderers, {
@@ -11296,17 +11550,17 @@ class RenderInjector extends SectionModuleBase {
11296
11550
  }];
11297
11551
  }
11298
11552
 
11299
- /**
11300
- * Detach a renderer from the by key injector.
11301
- * @param {string} identifier - Identifier for the renderer.
11553
+ /**
11554
+ * Detach a renderer from the by key injector.
11555
+ * @param {string} identifier - Identifier for the renderer.
11302
11556
  */
11303
11557
  detachRenderer(identifier) {
11304
11558
  this.registeredRenderers = this.registeredRenderers.filter(r => r.identifier !== identifier);
11305
11559
  }
11306
11560
 
11307
- /**
11308
- * Returns the registered renderers.
11309
- * @returns {Array} Array of registered renderers.
11561
+ /**
11562
+ * Returns the registered renderers.
11563
+ * @returns {Array} Array of registered renderers.
11310
11564
  */
11311
11565
  fetchRenderers() {
11312
11566
  return this.registeredRenderers;
@@ -11340,48 +11594,48 @@ var ExpressionLanguageModule = {
11340
11594
 
11341
11595
  const ids = new Ids([32, 36, 1]);
11342
11596
 
11343
- /**
11344
- * @typedef { import('./types').Injector } Injector
11345
- * @typedef { import('./types').Module } Module
11346
- * @typedef { import('./types').Schema } Schema
11347
- *
11348
- * @typedef { import('./types').FormEditorOptions } FormEditorOptions
11349
- * @typedef { import('./types').FormEditorProperties } FormEditorProperties
11350
- *
11351
- * @typedef { {
11352
- * properties: FormEditorProperties,
11353
- * schema: Schema
11354
- * } } State
11355
- *
11356
- * @typedef { (type:string, priority:number, handler:Function) => void } OnEventWithPriority
11357
- * @typedef { (type:string, handler:Function) => void } OnEventWithOutPriority
11358
- * @typedef { OnEventWithPriority & OnEventWithOutPriority } OnEventType
11597
+ /**
11598
+ * @typedef { import('./types').Injector } Injector
11599
+ * @typedef { import('./types').Module } Module
11600
+ * @typedef { import('./types').Schema } Schema
11601
+ *
11602
+ * @typedef { import('./types').FormEditorOptions } FormEditorOptions
11603
+ * @typedef { import('./types').FormEditorProperties } FormEditorProperties
11604
+ *
11605
+ * @typedef { {
11606
+ * properties: FormEditorProperties,
11607
+ * schema: Schema
11608
+ * } } State
11609
+ *
11610
+ * @typedef { (type:string, priority:number, handler:Function) => void } OnEventWithPriority
11611
+ * @typedef { (type:string, handler:Function) => void } OnEventWithOutPriority
11612
+ * @typedef { OnEventWithPriority & OnEventWithOutPriority } OnEventType
11359
11613
  */
11360
11614
 
11361
- /**
11362
- * The form editor.
11615
+ /**
11616
+ * The form editor.
11363
11617
  */
11364
11618
  class FormEditor {
11365
- /**
11366
- * @constructor
11367
- * @param {FormEditorOptions} options
11619
+ /**
11620
+ * @constructor
11621
+ * @param {FormEditorOptions} options
11368
11622
  */
11369
11623
  constructor(options = {}) {
11370
- /**
11371
- * @public
11372
- * @type {OnEventType}
11624
+ /**
11625
+ * @public
11626
+ * @type {OnEventType}
11373
11627
  */
11374
11628
  this.on = this._onEvent;
11375
11629
 
11376
- /**
11377
- * @public
11378
- * @type {String}
11630
+ /**
11631
+ * @public
11632
+ * @type {String}
11379
11633
  */
11380
11634
  this._id = ids.next();
11381
11635
 
11382
- /**
11383
- * @private
11384
- * @type {Element}
11636
+ /**
11637
+ * @private
11638
+ * @type {Element}
11385
11639
  */
11386
11640
  this._container = createFormContainer();
11387
11641
  this._container.setAttribute('input-handle-modified-keys', 'z,y');
@@ -11392,15 +11646,15 @@ class FormEditor {
11392
11646
  properties = {}
11393
11647
  } = options;
11394
11648
 
11395
- /**
11396
- * @private
11397
- * @type {any}
11649
+ /**
11650
+ * @private
11651
+ * @type {any}
11398
11652
  */
11399
11653
  this.exporter = exporter;
11400
11654
 
11401
- /**
11402
- * @private
11403
- * @type {State}
11655
+ /**
11656
+ * @private
11657
+ * @type {State}
11404
11658
  */
11405
11659
  this._state = {
11406
11660
  properties,
@@ -11429,10 +11683,10 @@ class FormEditor {
11429
11683
  this._detach(false);
11430
11684
  }
11431
11685
 
11432
- /**
11433
- * @param {Schema} schema
11434
- *
11435
- * @return {Promise<{ warnings: Array<any> }>}
11686
+ /**
11687
+ * @param {Schema} schema
11688
+ *
11689
+ * @return {Promise<{ warnings: Array<any> }>}
11436
11690
  */
11437
11691
  importSchema(schema) {
11438
11692
  return new Promise((resolve, reject) => {
@@ -11461,15 +11715,15 @@ class FormEditor {
11461
11715
  });
11462
11716
  }
11463
11717
 
11464
- /**
11465
- * @returns {Schema}
11718
+ /**
11719
+ * @returns {Schema}
11466
11720
  */
11467
11721
  saveSchema() {
11468
11722
  return this.getSchema();
11469
11723
  }
11470
11724
 
11471
- /**
11472
- * @returns {Schema}
11725
+ /**
11726
+ * @returns {Schema}
11473
11727
  */
11474
11728
  getSchema() {
11475
11729
  const {
@@ -11478,8 +11732,8 @@ class FormEditor {
11478
11732
  return exportSchema(schema, this.exporter, schemaVersion);
11479
11733
  }
11480
11734
 
11481
- /**
11482
- * @param {Element|string} parentNode
11735
+ /**
11736
+ * @param {Element|string} parentNode
11483
11737
  */
11484
11738
  attachTo(parentNode) {
11485
11739
  if (!parentNode) {
@@ -11497,10 +11751,10 @@ class FormEditor {
11497
11751
  this._detach();
11498
11752
  }
11499
11753
 
11500
- /**
11501
- * @internal
11502
- *
11503
- * @param {boolean} [emit]
11754
+ /**
11755
+ * @internal
11756
+ *
11757
+ * @param {boolean} [emit]
11504
11758
  */
11505
11759
  _detach(emit = true) {
11506
11760
  const container = this._container,
@@ -11514,9 +11768,9 @@ class FormEditor {
11514
11768
  parentNode.removeChild(container);
11515
11769
  }
11516
11770
 
11517
- /**
11518
- * @param {any} property
11519
- * @param {any} value
11771
+ /**
11772
+ * @param {any} property
11773
+ * @param {any} value
11520
11774
  */
11521
11775
  setProperty(property, value) {
11522
11776
  const properties = set$1(this._getState().properties, [property], value);
@@ -11525,21 +11779,21 @@ class FormEditor {
11525
11779
  });
11526
11780
  }
11527
11781
 
11528
- /**
11529
- * @param {string} type
11530
- * @param {Function} handler
11782
+ /**
11783
+ * @param {string} type
11784
+ * @param {Function} handler
11531
11785
  */
11532
11786
  off(type, handler) {
11533
11787
  this.get('eventBus').off(type, handler);
11534
11788
  }
11535
11789
 
11536
- /**
11537
- * @internal
11538
- *
11539
- * @param {FormEditorOptions} options
11540
- * @param {Element} container
11541
- *
11542
- * @returns {Injector}
11790
+ /**
11791
+ * @internal
11792
+ *
11793
+ * @param {FormEditorOptions} options
11794
+ * @param {Element} container
11795
+ *
11796
+ * @returns {Injector}
11543
11797
  */
11544
11798
  _createInjector(options, container) {
11545
11799
  const {
@@ -11561,22 +11815,22 @@ class FormEditor {
11561
11815
  }, core, ...modules, ...additionalModules]);
11562
11816
  }
11563
11817
 
11564
- /**
11565
- * @internal
11818
+ /**
11819
+ * @internal
11566
11820
  */
11567
11821
  _emit(type, data) {
11568
11822
  this.get('eventBus').fire(type, data);
11569
11823
  }
11570
11824
 
11571
- /**
11572
- * @internal
11825
+ /**
11826
+ * @internal
11573
11827
  */
11574
11828
  _getState() {
11575
11829
  return this._state;
11576
11830
  }
11577
11831
 
11578
- /**
11579
- * @internal
11832
+ /**
11833
+ * @internal
11580
11834
  */
11581
11835
  _setState(state) {
11582
11836
  this._state = {
@@ -11586,15 +11840,15 @@ class FormEditor {
11586
11840
  this._emit('changed', this._getState());
11587
11841
  }
11588
11842
 
11589
- /**
11590
- * @internal
11843
+ /**
11844
+ * @internal
11591
11845
  */
11592
11846
  _getModules() {
11593
11847
  return [ModelingModule, EditorActionsModule, DraggingModule, KeyboardModule, SelectionModule, PaletteModule, ExpressionLanguageModule, MarkdownModule, PropertiesPanelModule, RenderInjectionModule];
11594
11848
  }
11595
11849
 
11596
- /**
11597
- * @internal
11850
+ /**
11851
+ * @internal
11598
11852
  */
11599
11853
  _onEvent(type, priority, handler) {
11600
11854
  this.get('eventBus').on(type, priority, handler);
@@ -11642,5 +11896,5 @@ function createFormEditor(options) {
11642
11896
  });
11643
11897
  }
11644
11898
 
11645
- export { FormEditor, createFormEditor };
11899
+ export { FormEditor, createFormEditor, useDebounce, usePrevious$1 as usePrevious, useService as usePropertiesPanelService, useService$1 as useService, useVariables };
11646
11900
  //# sourceMappingURL=index.es.js.map