@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.cjs CHANGED
@@ -38,7 +38,7 @@ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
38
38
  var focusTrap__namespace = /*#__PURE__*/_interopNamespaceDefault(focusTrap);
39
39
 
40
40
  var FN_REF = '__fn';
41
- var DEFAULT_PRIORITY$2 = 1000;
41
+ var DEFAULT_PRIORITY$3 = 1000;
42
42
  var slice = Array.prototype.slice;
43
43
 
44
44
  /**
@@ -183,7 +183,7 @@ EventBus.prototype.on = function (events, priority, callback, that) {
183
183
  if (minDash.isFunction(priority)) {
184
184
  that = callback;
185
185
  callback = priority;
186
- priority = DEFAULT_PRIORITY$2;
186
+ priority = DEFAULT_PRIORITY$3;
187
187
  }
188
188
  if (!minDash.isNumber(priority)) {
189
189
  throw new Error('priority must be a number');
@@ -222,7 +222,7 @@ EventBus.prototype.once = function (events, priority, callback, that) {
222
222
  if (minDash.isFunction(priority)) {
223
223
  that = callback;
224
224
  callback = priority;
225
- priority = DEFAULT_PRIORITY$2;
225
+ priority = DEFAULT_PRIORITY$3;
226
226
  }
227
227
  if (!minDash.isNumber(priority)) {
228
228
  throw new Error('priority must be a number');
@@ -537,10 +537,10 @@ function invokeFunction(fn, args) {
537
537
  return fn.apply(null, args);
538
538
  }
539
539
 
540
- /**
541
- * A factory to create a configurable debouncer.
542
- *
543
- * @param {number|boolean} [config=true]
540
+ /**
541
+ * A factory to create a configurable debouncer.
542
+ *
543
+ * @param {number|boolean} [config=true]
544
544
  */
545
545
  function DebounceFactory(config = true) {
546
546
  const timeout = typeof config === 'number' ? config : config ? 300 : 0;
@@ -553,11 +553,11 @@ function DebounceFactory(config = true) {
553
553
  DebounceFactory.$inject = ['config.debounce'];
554
554
 
555
555
  class FormFieldRegistry extends formJsViewer.FormFieldRegistry {
556
- /**
557
- * Updates a form fields id.
558
- *
559
- * @param {Object} formField
560
- * @param {string} newId
556
+ /**
557
+ * Updates a form fields id.
558
+ *
559
+ * @param {Object} formField
560
+ * @param {string} newId
561
561
  */
562
562
  updateId(formField, newId) {
563
563
  this._validateId(newId);
@@ -578,13 +578,13 @@ class FormFieldRegistry extends formJsViewer.FormFieldRegistry {
578
578
  }
579
579
  }
580
580
 
581
- /**
582
- * Validate the suitability of the given id and signals a problem
583
- * with an exception.
584
- *
585
- * @param {string} id
586
- *
587
- * @throws {Error} if id is empty or already assigned
581
+ /**
582
+ * Validate the suitability of the given id and signals a problem
583
+ * with an exception.
584
+ *
585
+ * @param {string} id
586
+ *
587
+ * @throws {Error} if id is empty or already assigned
588
588
  */
589
589
  _validateId(id) {
590
590
  if (!id) {
@@ -601,11 +601,11 @@ const MAX_COLUMNS = 16;
601
601
  const MIN_COLUMNS = 2;
602
602
  const MAX_FIELDS_PER_ROW = 4;
603
603
  class FormLayoutValidator {
604
- /**
605
- * @constructor
606
- *
607
- * @param { import('./FormLayouter').default } formLayouter
608
- * @param { import('./FormFieldRegistry').default } formFieldRegistry
604
+ /**
605
+ * @constructor
606
+ *
607
+ * @param { import('./FormLayouter').default } formLayouter
608
+ * @param { import('./FormFieldRegistry').default } formFieldRegistry
609
609
  */
610
610
  constructor(formLayouter, formFieldRegistry) {
611
611
  this._formLayouter = formLayouter;
@@ -676,21 +676,21 @@ function editorFormFieldClasses(type, {
676
676
  });
677
677
  }
678
678
 
679
- /**
680
- * Add a dragger that calls back the passed function with
681
- * { event, delta } on drag.
682
- *
683
- * @example
684
- *
685
- * function dragMove(event, delta) {
686
- * // we are dragging (!!)
687
- * }
688
- *
689
- * domElement.addEventListener('dragstart', dragger(dragMove));
690
- *
691
- * @param {Function} fn
692
- *
693
- * @return {Function} drag start callback function
679
+ /**
680
+ * Add a dragger that calls back the passed function with
681
+ * { event, delta } on drag.
682
+ *
683
+ * @example
684
+ *
685
+ * function dragMove(event, delta) {
686
+ * // we are dragging (!!)
687
+ * }
688
+ *
689
+ * domElement.addEventListener('dragstart', dragger(dragMove));
690
+ *
691
+ * @param {Function} fn
692
+ *
693
+ * @return {Function} drag start callback function
694
694
  */
695
695
  function createDragger$1(fn) {
696
696
  let self;
@@ -731,12 +731,12 @@ function createDragger$1(fn) {
731
731
  return onDragStart;
732
732
  }
733
733
 
734
- /**
735
- * Throttle function call according UI update cycle.
736
- *
737
- * @param {Function} fn
738
- *
739
- * @return {Function} throttled fn
734
+ /**
735
+ * Throttle function call according UI update cycle.
736
+ *
737
+ * @param {Function} fn
738
+ *
739
+ * @return {Function} throttled fn
740
740
  */
741
741
  function throttle(fn) {
742
742
  let active = false;
@@ -770,11 +770,11 @@ const DragAndDropContext = preact.createContext({
770
770
  });
771
771
  var DragAndDropContext$1 = DragAndDropContext;
772
772
 
773
- /**
774
- * @param {string} type
775
- * @param {boolean} [strict]
776
- *
777
- * @returns {any}
773
+ /**
774
+ * @param {string} type
775
+ * @param {boolean} [strict]
776
+ *
777
+ * @returns {any}
778
778
  */
779
779
  function getService$1(type, strict) {}
780
780
  const FormEditorContext = preact.createContext({
@@ -789,6 +789,27 @@ function useService$1 (type, strict) {
789
789
  return getService(type, strict);
790
790
  }
791
791
 
792
+ function usePrevious$1(value) {
793
+ const ref = hooks.useRef();
794
+ hooks.useEffect(() => ref.current = value);
795
+ return ref.current;
796
+ }
797
+
798
+ function useDebounce(fn, dependencies = []) {
799
+ const debounce = useService$1('debounce');
800
+ const callback = hooks.useMemo(() => {
801
+ return debounce(fn);
802
+ }, dependencies);
803
+
804
+ // cleanup async side-effect if callback #flush is provided.
805
+ hooks.useEffect(() => {
806
+ return () => {
807
+ typeof callback.flush === 'function' && callback.flush();
808
+ };
809
+ }, [callback]);
810
+ return callback;
811
+ }
812
+
792
813
  var _path$4;
793
814
  function _extends$4() { _extends$4 = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends$4.apply(this, arguments); }
794
815
  var SvgClose = function SvgClose(props) {
@@ -1124,23 +1145,23 @@ var Slot = (props => {
1124
1145
  return fillsAndSeparators;
1125
1146
  });
1126
1147
 
1127
- /**
1128
- * Creates a Fragment for a fill.
1129
- *
1130
- * @param {Object} fill Fill to be rendered
1131
- * @returns {Object} Preact Fragment containing fill's children
1148
+ /**
1149
+ * Creates a Fragment for a fill.
1150
+ *
1151
+ * @param {Object} fill Fill to be rendered
1152
+ * @returns {Object} Preact Fragment containing fill's children
1132
1153
  */
1133
1154
  const FillFragment = fill => jsxRuntime.jsx(preact.Fragment, {
1134
1155
  children: fill.children
1135
1156
  }, fill.id);
1136
1157
 
1137
- /**
1138
- * Creates an array of fills, with separators inserted between groups.
1139
- *
1140
- * @param {Array} groups Groups of fills
1141
- * @param {Function} fillRenderer Function to create a fill
1142
- * @param {Function} separatorRenderer Function to create a separator
1143
- * @returns {Array} Array of fills and separators
1158
+ /**
1159
+ * Creates an array of fills, with separators inserted between groups.
1160
+ *
1161
+ * @param {Array} groups Groups of fills
1162
+ * @param {Function} fillRenderer Function to create a fill
1163
+ * @param {Function} separatorRenderer Function to create a separator
1164
+ * @returns {Array} Array of fills and separators
1144
1165
  */
1145
1166
  const buildFills = (groups, fillRenderer, separatorRenderer) => {
1146
1167
  const result = [];
@@ -1158,8 +1179,8 @@ const buildFills = (groups, fillRenderer, separatorRenderer) => {
1158
1179
  return result;
1159
1180
  };
1160
1181
 
1161
- /**
1162
- * Groups fills by group name property.
1182
+ /**
1183
+ * Groups fills by group name property.
1163
1184
  */
1164
1185
  const _groupByGroupName = fills => {
1165
1186
  const groups = [];
@@ -1179,8 +1200,8 @@ const _groupByGroupName = fills => {
1179
1200
  return Object.keys(groupsById).sort().map(id => groupsById[id]);
1180
1201
  };
1181
1202
 
1182
- /**
1183
- * Compares fills by priority.
1203
+ /**
1204
+ * Compares fills by priority.
1184
1205
  */
1185
1206
  const _comparePriority = (a, b) => {
1186
1207
  return (b.priority || 0) - (a.priority || 0);
@@ -1211,17 +1232,63 @@ var SlotFillRoot = (props => {
1211
1232
  });
1212
1233
  });
1213
1234
 
1214
- const PALETTE_ENTRIES = formJsViewer.formFields.filter(({
1215
- config: fieldConfig
1216
- }) => fieldConfig.type !== 'default').map(({
1217
- config: fieldConfig
1218
- }) => {
1219
- return {
1220
- label: fieldConfig.label,
1221
- type: fieldConfig.type,
1222
- group: fieldConfig.group
1235
+ function PaletteEntry(props) {
1236
+ const {
1237
+ type,
1238
+ label,
1239
+ icon,
1240
+ iconUrl,
1241
+ getPaletteIcon
1242
+ } = props;
1243
+ const modeling = useService$1('modeling');
1244
+ const formEditor = useService$1('formEditor');
1245
+ const Icon = getPaletteIcon({
1246
+ icon,
1247
+ iconUrl,
1248
+ label,
1249
+ type
1250
+ });
1251
+ const onKeyDown = event => {
1252
+ if (event.code === 'Enter') {
1253
+ const {
1254
+ fieldType: type
1255
+ } = event.target.dataset;
1256
+ const {
1257
+ schema
1258
+ } = formEditor._getState();
1259
+
1260
+ // add new form field to last position
1261
+ modeling.addFormField({
1262
+ type
1263
+ }, schema, schema.components.length);
1264
+ }
1223
1265
  };
1224
- });
1266
+ return jsxRuntime.jsxs("button", {
1267
+ class: "fjs-palette-field fjs-drag-copy fjs-no-drop",
1268
+ "data-field-type": type,
1269
+ title: `Create ${getIndefiniteArticle(type)} ${label} element`,
1270
+ onKeyDown: onKeyDown,
1271
+ children: [Icon ? jsxRuntime.jsx(Icon, {
1272
+ class: "fjs-palette-field-icon",
1273
+ width: "36",
1274
+ height: "36",
1275
+ viewBox: "0 0 54 54"
1276
+ }) : null, jsxRuntime.jsx("span", {
1277
+ class: "fjs-palette-field-text",
1278
+ children: label
1279
+ })]
1280
+ });
1281
+ }
1282
+
1283
+ // helpers ///////////
1284
+
1285
+ function getIndefiniteArticle(type) {
1286
+ if (['image'].includes(type)) {
1287
+ return 'an';
1288
+ }
1289
+ return 'a';
1290
+ }
1291
+
1225
1292
  const PALETTE_GROUPS = [{
1226
1293
  label: 'Basic input',
1227
1294
  id: 'basic-input'
@@ -1236,10 +1303,12 @@ const PALETTE_GROUPS = [{
1236
1303
  id: 'action'
1237
1304
  }];
1238
1305
  function Palette(props) {
1239
- const [entries, setEntries] = hooks.useState(PALETTE_ENTRIES);
1306
+ const formFields = useService$1('formFields');
1307
+ const initialPaletteEntries = hooks.useRef(collectPaletteEntries(formFields));
1308
+ const [paletteEntries, setPaletteEntries] = hooks.useState(initialPaletteEntries.current);
1240
1309
  const [searchTerm, setSearchTerm] = hooks.useState('');
1241
1310
  const inputRef = hooks.useRef();
1242
- const groups = groupEntries(entries);
1311
+ const groups = groupEntries(paletteEntries);
1243
1312
  const simplifyString = hooks.useCallback(str => {
1244
1313
  return str.toLowerCase().replace(/\s+/g, '');
1245
1314
  }, []);
@@ -1255,8 +1324,8 @@ function Palette(props) {
1255
1324
 
1256
1325
  // filter entries on search change
1257
1326
  hooks.useEffect(() => {
1258
- const entries = PALETTE_ENTRIES.filter(filter);
1259
- setEntries(entries);
1327
+ const entries = initialPaletteEntries.current.filter(filter);
1328
+ setPaletteEntries(entries);
1260
1329
  }, [filter, searchTerm]);
1261
1330
  const handleInput = hooks.useCallback(event => {
1262
1331
  setSearchTerm(() => event.target.value);
@@ -1303,24 +1372,10 @@ function Palette(props) {
1303
1372
  children: label
1304
1373
  }), jsxRuntime.jsx("div", {
1305
1374
  class: "fjs-palette-fields fjs-drag-container fjs-no-drop",
1306
- children: entries.map(({
1307
- label,
1308
- type
1309
- }) => {
1310
- const Icon = formJsViewer.iconsByType(type);
1311
- return jsxRuntime.jsxs("div", {
1312
- class: "fjs-palette-field fjs-drag-copy fjs-no-drop",
1313
- "data-field-type": type,
1314
- title: `Create ${getIndefiniteArticle(type)} ${label} element`,
1315
- children: [Icon ? jsxRuntime.jsx(Icon, {
1316
- class: "fjs-palette-field-icon",
1317
- width: "36",
1318
- height: "36",
1319
- viewBox: "0 0 54 54"
1320
- }) : null, jsxRuntime.jsx("span", {
1321
- class: "fjs-palette-field-text",
1322
- children: label
1323
- })]
1375
+ children: entries.map(entry => {
1376
+ return jsxRuntime.jsx(PaletteEntry, {
1377
+ getPaletteIcon: getPaletteIcon,
1378
+ ...entry
1324
1379
  });
1325
1380
  })
1326
1381
  })]
@@ -1360,11 +1415,59 @@ function groupEntries(entries) {
1360
1415
  });
1361
1416
  return groups.filter(g => g.entries.length);
1362
1417
  }
1363
- function getIndefiniteArticle(type) {
1364
- if (['image'].includes(type)) {
1365
- return 'an';
1418
+
1419
+ /**
1420
+ * Returns a list of palette entries.
1421
+ *
1422
+ * @param {FormFields} formFields
1423
+ * @returns {Array<PaletteEntry>}
1424
+ */
1425
+ function collectPaletteEntries(formFields) {
1426
+ return Object.entries(formFields._formFields).map(([type, formField]) => {
1427
+ const {
1428
+ config: fieldConfig
1429
+ } = formField;
1430
+ return {
1431
+ label: fieldConfig.label,
1432
+ type: type,
1433
+ group: fieldConfig.group,
1434
+ icon: fieldConfig.icon,
1435
+ iconUrl: fieldConfig.iconUrl
1436
+ };
1437
+ }).filter(({
1438
+ type
1439
+ }) => type !== 'default');
1440
+ }
1441
+
1442
+ /**
1443
+ * There are various options to specify an icon for a palette entry.
1444
+ *
1445
+ * a) via `iconUrl` property in a form field config
1446
+ * b) via `icon` property in a form field config
1447
+ * c) via statically defined iconsByType (fallback)
1448
+ */
1449
+ function getPaletteIcon(entry) {
1450
+ const {
1451
+ icon,
1452
+ iconUrl,
1453
+ type,
1454
+ label
1455
+ } = entry;
1456
+ let Icon;
1457
+ if (iconUrl) {
1458
+ Icon = () => jsxRuntime.jsx("img", {
1459
+ class: "fjs-field-icon-image",
1460
+ width: 36,
1461
+ style: {
1462
+ margin: 'auto'
1463
+ },
1464
+ alt: label,
1465
+ src: formJsViewer.sanitizeImageSource(iconUrl)
1466
+ });
1467
+ } else {
1468
+ Icon = icon || formJsViewer.iconsByType(type);
1366
1469
  }
1367
- return 'a';
1470
+ return Icon;
1368
1471
  }
1369
1472
 
1370
1473
  var InjectedRendersRoot = (() => {
@@ -1408,20 +1511,20 @@ const DRAG_NO_DROP_CLS = 'fjs-no-drop';
1408
1511
  const DRAG_NO_MOVE_CLS = 'fjs-no-move';
1409
1512
  const ERROR_DROP_CLS = 'fjs-error-drop';
1410
1513
 
1411
- /**
1412
- * @typedef { { id: String, components: Array<any> } } FormRow
1514
+ /**
1515
+ * @typedef { { id: String, components: Array<any> } } FormRow
1413
1516
  */
1414
1517
 
1415
1518
  class Dragging {
1416
- /**
1417
- * @constructor
1418
- *
1419
- * @param { import('../../core/FormFieldRegistry').default } formFieldRegistry
1420
- * @param { import('../../core/FormLayouter').default } formLayouter
1421
- * @param { import('../../core/FormLayoutValidator').default } formLayoutValidator
1422
- * @param { import('../../core/EventBus').default } eventBus
1423
- * @param { import('../modeling/Modeling').default } modeling
1424
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
1519
+ /**
1520
+ * @constructor
1521
+ *
1522
+ * @param { import('../../core/FormFieldRegistry').default } formFieldRegistry
1523
+ * @param { import('../../core/FormLayouter').default } formLayouter
1524
+ * @param { import('../../core/FormLayoutValidator').default } formLayoutValidator
1525
+ * @param { import('../../core/EventBus').default } eventBus
1526
+ * @param { import('../modeling/Modeling').default } modeling
1527
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
1425
1528
  */
1426
1529
  constructor(formFieldRegistry, formLayouter, formLayoutValidator, eventBus, modeling, pathRegistry) {
1427
1530
  this._formFieldRegistry = formFieldRegistry;
@@ -1432,13 +1535,13 @@ class Dragging {
1432
1535
  this._pathRegistry = pathRegistry;
1433
1536
  }
1434
1537
 
1435
- /**
1436
- * Calculcates position in form schema given the dropped place.
1437
- *
1438
- * @param { FormRow } targetRow
1439
- * @param { any } targetFormField
1440
- * @param { HTMLElement } sibling
1441
- * @returns { number }
1538
+ /**
1539
+ * Calculcates position in form schema given the dropped place.
1540
+ *
1541
+ * @param { FormRow } targetRow
1542
+ * @param { any } targetFormField
1543
+ * @param { HTMLElement } sibling
1544
+ * @returns { number }
1442
1545
  */
1443
1546
  getTargetIndex(targetRow, targetFormField, sibling) {
1444
1547
  /** @type HTMLElement */
@@ -1579,8 +1682,8 @@ class Dragging {
1579
1682
  }
1580
1683
  }
1581
1684
 
1582
- /**
1583
- * @param { { container: Array<string>, direction: string, mirrorContainer: string } } options
1685
+ /**
1686
+ * @param { { container: Array<string>, direction: string, mirrorContainer: string } } options
1584
1687
  */
1585
1688
  createDragulaInstance(options) {
1586
1689
  const {
@@ -1887,6 +1990,7 @@ function Element$1(props) {
1887
1990
  const eventBus = useService$1('eventBus'),
1888
1991
  formEditor = useService$1('formEditor'),
1889
1992
  formFieldRegistry = useService$1('formFieldRegistry'),
1993
+ formFields = useService$1('formFields'),
1890
1994
  modeling = useService$1('modeling'),
1891
1995
  selection = useService$1('selection');
1892
1996
  const {
@@ -1972,6 +2076,7 @@ function Element$1(props) {
1972
2076
  field: field
1973
2077
  }), jsxRuntime.jsx(ContextPad, {
1974
2078
  children: selection.isSelected(field) && field.type !== 'default' ? jsxRuntime.jsx("button", {
2079
+ title: getRemoveButtonTitle(field, formFields),
1975
2080
  class: "fjs-context-pad-item",
1976
2081
  onClick: onRemove,
1977
2082
  children: jsxRuntime.jsx(DeleteIcon$1, {})
@@ -1994,7 +2099,7 @@ function DebugColumns(props) {
1994
2099
  return null;
1995
2100
  }
1996
2101
  return jsxRuntime.jsx("div", {
1997
- 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;",
2102
+ 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;",
1998
2103
  class: "fjs-debug-columns",
1999
2104
  children: (field.layout || {}).columns || 'auto'
2000
2105
  });
@@ -2134,6 +2239,9 @@ function FormEditor$1(props) {
2134
2239
 
2135
2240
  // fire event after render to notify interested parties
2136
2241
  hooks.useEffect(() => {
2242
+ eventBus.fire('rendered');
2243
+
2244
+ // keep deprecated event to ensure backward compatibility
2137
2245
  eventBus.fire('formEditor.rendered');
2138
2246
  }, []);
2139
2247
  const [hoveredId, setHoveredId] = hooks.useState(null);
@@ -2229,15 +2337,20 @@ function CreatePreview(props) {
2229
2337
  const {
2230
2338
  drake
2231
2339
  } = hooks.useContext(DragAndDropContext$1);
2340
+ const formFields = useService$1('formFields');
2232
2341
  function handleCloned(clone, original, type) {
2233
2342
  const fieldType = clone.dataset.fieldType;
2234
2343
 
2235
2344
  // (1) field preview
2236
2345
  if (fieldType) {
2346
+ const paletteEntry = findPaletteEntry(fieldType, formFields);
2347
+ if (!paletteEntry) {
2348
+ return;
2349
+ }
2237
2350
  const {
2238
2351
  label
2239
- } = findPaletteEntry(fieldType);
2240
- const Icon = formJsViewer.iconsByType(fieldType);
2352
+ } = paletteEntry;
2353
+ const Icon = getPaletteIcon(paletteEntry);
2241
2354
  clone.innerHTML = '';
2242
2355
  clone.class = 'gu-mirror';
2243
2356
  clone.classList.add('fjs-field-preview-container');
@@ -2278,12 +2391,19 @@ function CreatePreview(props) {
2278
2391
 
2279
2392
  // helper //////
2280
2393
 
2281
- function findPaletteEntry(type) {
2282
- return PALETTE_ENTRIES.find(entry => entry.type === type);
2394
+ function findPaletteEntry(type, formFields) {
2395
+ return collectPaletteEntries(formFields).find(entry => entry.type === type);
2283
2396
  }
2284
2397
  function defaultPropertiesPanel(propertiesPanelConfig) {
2285
2398
  return !(propertiesPanelConfig && propertiesPanelConfig.parent);
2286
2399
  }
2400
+ function getRemoveButtonTitle(formField, formFields) {
2401
+ const entry = findPaletteEntry(formField.type, formFields);
2402
+ if (!entry) {
2403
+ return 'Remove form field';
2404
+ }
2405
+ return `Remove ${entry.label}`;
2406
+ }
2287
2407
 
2288
2408
  class Renderer {
2289
2409
  constructor(renderConfig, eventBus, formEditor, injector) {
@@ -2694,7 +2814,7 @@ function isRedo(event) {
2694
2814
  var KEYDOWN_EVENT = 'keyboard.keydown',
2695
2815
  KEYUP_EVENT = 'keyboard.keyup';
2696
2816
  var HANDLE_MODIFIER_ATTRIBUTE = 'input-handle-modified-keys';
2697
- var DEFAULT_PRIORITY$1 = 1000;
2817
+ var DEFAULT_PRIORITY$2 = 1000;
2698
2818
 
2699
2819
  /**
2700
2820
  * A keyboard abstraction that may be activated and
@@ -2827,7 +2947,7 @@ Keyboard.prototype.addListener = function (priority, listener, type) {
2827
2947
  if (minDash.isFunction(priority)) {
2828
2948
  type = listener;
2829
2949
  listener = priority;
2830
- priority = DEFAULT_PRIORITY$1;
2950
+ priority = DEFAULT_PRIORITY$2;
2831
2951
  }
2832
2952
  this._eventBus.on(type || KEYDOWN_EVENT, priority, listener);
2833
2953
  };
@@ -3065,10 +3185,10 @@ function updateRow(formField, rowId) {
3065
3185
  }
3066
3186
 
3067
3187
  class AddFormFieldHandler {
3068
- /**
3069
- * @constructor
3070
- * @param { import('../../../FormEditor').default } formEditor
3071
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3188
+ /**
3189
+ * @constructor
3190
+ * @param { import('../../../FormEditor').default } formEditor
3191
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3072
3192
  */
3073
3193
  constructor(formEditor, formFieldRegistry) {
3074
3194
  this._formEditor = formEditor;
@@ -3129,10 +3249,10 @@ class AddFormFieldHandler {
3129
3249
  AddFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3130
3250
 
3131
3251
  class EditFormFieldHandler {
3132
- /**
3133
- * @constructor
3134
- * @param { import('../../../FormEditor').default } formEditor
3135
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3252
+ /**
3253
+ * @constructor
3254
+ * @param { import('../../../FormEditor').default } formEditor
3255
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3136
3256
  */
3137
3257
  constructor(formEditor, formFieldRegistry) {
3138
3258
  this._formEditor = formEditor;
@@ -3195,11 +3315,11 @@ class EditFormFieldHandler {
3195
3315
  EditFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3196
3316
 
3197
3317
  class MoveFormFieldHandler {
3198
- /**
3199
- * @constructor
3200
- * @param { import('../../../FormEditor').default } formEditor
3201
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3202
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3318
+ /**
3319
+ * @constructor
3320
+ * @param { import('../../../FormEditor').default } formEditor
3321
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3322
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3203
3323
  */
3204
3324
  constructor(formEditor, formFieldRegistry, pathRegistry) {
3205
3325
  this._formEditor = formEditor;
@@ -3304,10 +3424,10 @@ class MoveFormFieldHandler {
3304
3424
  MoveFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry', 'pathRegistry'];
3305
3425
 
3306
3426
  class RemoveFormFieldHandler {
3307
- /**
3308
- * @constructor
3309
- * @param { import('../../../FormEditor').default } formEditor
3310
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3427
+ /**
3428
+ * @constructor
3429
+ * @param { import('../../../FormEditor').default } formEditor
3430
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3311
3431
  */
3312
3432
  constructor(formEditor, formFieldRegistry) {
3313
3433
  this._formEditor = formEditor;
@@ -3367,9 +3487,9 @@ class RemoveFormFieldHandler {
3367
3487
  RemoveFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3368
3488
 
3369
3489
  class UpdateIdClaimHandler {
3370
- /**
3371
- * @constructor
3372
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3490
+ /**
3491
+ * @constructor
3492
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3373
3493
  */
3374
3494
  constructor(formFieldRegistry) {
3375
3495
  this._formFieldRegistry = formFieldRegistry;
@@ -3402,9 +3522,9 @@ class UpdateIdClaimHandler {
3402
3522
  UpdateIdClaimHandler.$inject = ['formFieldRegistry'];
3403
3523
 
3404
3524
  class UpdateKeyClaimHandler {
3405
- /**
3406
- * @constructor
3407
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3525
+ /**
3526
+ * @constructor
3527
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3408
3528
  */
3409
3529
  constructor(pathRegistry) {
3410
3530
  this._pathRegistry = pathRegistry;
@@ -3445,9 +3565,9 @@ class UpdateKeyClaimHandler {
3445
3565
  UpdateKeyClaimHandler.$inject = ['pathRegistry'];
3446
3566
 
3447
3567
  class UpdatePathClaimHandler {
3448
- /**
3449
- * @constructor
3450
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3568
+ /**
3569
+ * @constructor
3570
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3451
3571
  */
3452
3572
  constructor(pathRegistry) {
3453
3573
  this._pathRegistry = pathRegistry;
@@ -3645,7 +3765,7 @@ Modeling.$inject = ['commandStack', 'eventBus', 'formEditor', 'formFieldRegistry
3645
3765
  * @typedef { (context: CommandContext) => void } ComposeHandlerFunction
3646
3766
  */
3647
3767
 
3648
- var DEFAULT_PRIORITY = 1000;
3768
+ var DEFAULT_PRIORITY$1 = 1000;
3649
3769
 
3650
3770
  /**
3651
3771
  * A utility that can be used to plug into the command execution for
@@ -3706,7 +3826,7 @@ CommandInterceptor.prototype.on = function (events, hook, priority, handlerFn, u
3706
3826
  that = unwrap;
3707
3827
  unwrap = handlerFn;
3708
3828
  handlerFn = priority;
3709
- priority = DEFAULT_PRIORITY;
3829
+ priority = DEFAULT_PRIORITY$1;
3710
3830
  }
3711
3831
  if (minDash.isObject(unwrap)) {
3712
3832
  that = unwrap;
@@ -4009,8 +4129,8 @@ class ValidateBehavior extends CommandInterceptor {
4009
4129
  constructor(eventBus) {
4010
4130
  super(eventBus);
4011
4131
 
4012
- /**
4013
- * Remove custom validation if <validationType> is about to be added.
4132
+ /**
4133
+ * Remove custom validation if <validationType> is about to be added.
4014
4134
  */
4015
4135
  this.preExecute('formField.edit', function (context) {
4016
4136
  const {
@@ -4033,12 +4153,61 @@ class ValidateBehavior extends CommandInterceptor {
4033
4153
  }
4034
4154
  ValidateBehavior.$inject = ['eventBus'];
4035
4155
 
4156
+ class ValuesSourceBehavior extends CommandInterceptor {
4157
+ constructor(eventBus) {
4158
+ super(eventBus);
4159
+
4160
+ /**
4161
+ * Cleanup properties on changing the values source.
4162
+ *
4163
+ * 1) Remove other sources, e.g. set `values` => remove `valuesKey` and `valuesExpression`
4164
+ * 2) Remove default values for all other values sources
4165
+ */
4166
+ this.preExecute('formField.edit', function (context) {
4167
+ const {
4168
+ properties
4169
+ } = context;
4170
+ const newProperties = {};
4171
+ if (!isValuesSourceUpdate(properties)) {
4172
+ return;
4173
+ }
4174
+
4175
+ // clean up value sources that are not to going to be set
4176
+ Object.values(formJsViewer.VALUES_SOURCES).forEach(source => {
4177
+ const path = formJsViewer.VALUES_SOURCES_PATHS[source];
4178
+ if (minDash.get(properties, path) == undefined) {
4179
+ newProperties[formJsViewer.VALUES_SOURCES_PATHS[source]] = undefined;
4180
+ }
4181
+ });
4182
+
4183
+ // clean up default value
4184
+ if (minDash.get(properties, formJsViewer.VALUES_SOURCES_PATHS[formJsViewer.VALUES_SOURCES.EXPRESSION]) !== undefined || minDash.get(properties, formJsViewer.VALUES_SOURCES_PATHS[formJsViewer.VALUES_SOURCES.INPUT]) !== undefined) {
4185
+ newProperties['defaultValue'] = undefined;
4186
+ }
4187
+ context.properties = {
4188
+ ...properties,
4189
+ ...newProperties
4190
+ };
4191
+ }, true);
4192
+ }
4193
+ }
4194
+ ValuesSourceBehavior.$inject = ['eventBus'];
4195
+
4196
+ // helper ///////////////////
4197
+
4198
+ function isValuesSourceUpdate(properties) {
4199
+ return Object.values(formJsViewer.VALUES_SOURCES_PATHS).some(path => {
4200
+ return minDash.get(properties, path) !== undefined;
4201
+ });
4202
+ }
4203
+
4036
4204
  var behaviorModule = {
4037
- __init__: ['idBehavior', 'keyBehavior', 'pathBehavior', 'validateBehavior'],
4205
+ __init__: ['idBehavior', 'keyBehavior', 'pathBehavior', 'validateBehavior', 'valuesSourceBehavior'],
4038
4206
  idBehavior: ['type', IdBehavior],
4039
4207
  keyBehavior: ['type', KeyBehavior],
4040
4208
  pathBehavior: ['type', PathBehavior],
4041
- validateBehavior: ['type', ValidateBehavior]
4209
+ validateBehavior: ['type', ValidateBehavior],
4210
+ valuesSourceBehavior: ['type', ValuesSourceBehavior]
4042
4211
  };
4043
4212
 
4044
4213
  /**
@@ -4590,22 +4759,22 @@ var SelectionModule = {
4590
4759
  selectionBehavior: ['type', SelectionBehavior]
4591
4760
  };
4592
4761
 
4593
- /**
4594
- * Base class for sectionable UI modules.
4595
- *
4596
- * @property {EventBus} _eventBus - EventBus instance used for event handling.
4597
- * @property {string} managerType - Type of the render manager. Used to form event names.
4598
- *
4599
- * @class SectionModuleBase
4762
+ /**
4763
+ * Base class for sectionable UI modules.
4764
+ *
4765
+ * @property {EventBus} _eventBus - EventBus instance used for event handling.
4766
+ * @property {string} managerType - Type of the render manager. Used to form event names.
4767
+ *
4768
+ * @class SectionModuleBase
4600
4769
  */
4601
4770
  class SectionModuleBase {
4602
- /**
4603
- * Create a SectionModuleBase instance.
4604
- *
4605
- * @param {any} eventBus - The EventBus instance used for event handling.
4606
- * @param {string} sectionKey - The type of render manager. Used to form event names.
4607
- *
4608
- * @constructor
4771
+ /**
4772
+ * Create a SectionModuleBase instance.
4773
+ *
4774
+ * @param {any} eventBus - The EventBus instance used for event handling.
4775
+ * @param {string} sectionKey - The type of render manager. Used to form event names.
4776
+ *
4777
+ * @constructor
4609
4778
  */
4610
4779
  constructor(eventBus, sectionKey) {
4611
4780
  this._eventBus = eventBus;
@@ -4618,10 +4787,10 @@ class SectionModuleBase {
4618
4787
  });
4619
4788
  }
4620
4789
 
4621
- /**
4622
- * Attach the managed section to a parent node.
4623
- *
4624
- * @param {HTMLElement} container - The parent node to attach to.
4790
+ /**
4791
+ * Attach the managed section to a parent node.
4792
+ *
4793
+ * @param {HTMLElement} container - The parent node to attach to.
4625
4794
  */
4626
4795
  attachTo(container) {
4627
4796
  this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.attach`, {
@@ -4629,22 +4798,22 @@ class SectionModuleBase {
4629
4798
  }));
4630
4799
  }
4631
4800
 
4632
- /**
4633
- * Detach the managed section from its parent node.
4801
+ /**
4802
+ * Detach the managed section from its parent node.
4634
4803
  */
4635
4804
  detach() {
4636
4805
  this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.detach`));
4637
4806
  }
4638
4807
 
4639
- /**
4640
- * Reset the managed section to its initial state.
4808
+ /**
4809
+ * Reset the managed section to its initial state.
4641
4810
  */
4642
4811
  reset() {
4643
4812
  this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.reset`));
4644
4813
  }
4645
4814
 
4646
- /**
4647
- * Circumvents timing issues.
4815
+ /**
4816
+ * Circumvents timing issues.
4648
4817
  */
4649
4818
  _onceSectionRendered(callback) {
4650
4819
  if (this.isSectionRendered) {
@@ -4767,6 +4936,29 @@ FeelIcon$1.defaultProps = {
4767
4936
  fill: "none",
4768
4937
  xmlns: "http://www.w3.org/2000/svg"
4769
4938
  };
4939
+ var HelpIcon = function HelpIcon(props) {
4940
+ return jsxRuntime.jsxs("svg", {
4941
+ ...props,
4942
+ children: [jsxRuntime.jsx("path", {
4943
+ 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"
4944
+ }), jsxRuntime.jsx("circle", {
4945
+ cx: "16",
4946
+ cy: "23.5",
4947
+ r: "1.5"
4948
+ }), jsxRuntime.jsx("path", {
4949
+ 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"
4950
+ }), jsxRuntime.jsx("path", {
4951
+ style: {
4952
+ fill: "none"
4953
+ },
4954
+ d: "M0 0h32v32H0z"
4955
+ })]
4956
+ });
4957
+ };
4958
+ HelpIcon.defaultProps = {
4959
+ xmlns: "http://www.w3.org/2000/svg",
4960
+ viewBox: "0 0 32 32"
4961
+ };
4770
4962
  function Header(props) {
4771
4963
  const {
4772
4964
  element,
@@ -5316,6 +5508,7 @@ function Group(props) {
5316
5508
  edited: edited,
5317
5509
  hasErrors: hasErrors
5318
5510
  }), jsxRuntime.jsx("button", {
5511
+ type: "button",
5319
5512
  title: "Toggle section",
5320
5513
  class: "bio-properties-panel-group-header-button bio-properties-panel-arrow",
5321
5514
  children: jsxRuntime.jsx(ArrowIcon, {
@@ -5496,6 +5689,7 @@ const CodeEditor$1 = React.forwardRef((props, ref) => {
5496
5689
  ref: inputRef,
5497
5690
  onClick: handleClick
5498
5691
  }), jsxRuntime.jsx("button", {
5692
+ type: "button",
5499
5693
  title: "Open pop-up editor",
5500
5694
  class: "bio-properties-panel-open-feel-popup",
5501
5695
  onClick: () => onPopupOpen('feelers'),
@@ -5616,6 +5810,7 @@ const CodeEditor = React.forwardRef((props, ref) => {
5616
5810
  ref: inputRef,
5617
5811
  onClick: handleClick
5618
5812
  }), jsxRuntime.jsx("button", {
5813
+ type: "button",
5619
5814
  title: "Open pop-up editor",
5620
5815
  class: "bio-properties-panel-open-feel-popup",
5621
5816
  onClick: () => onPopupOpen(),
@@ -5660,6 +5855,7 @@ function FeelIcon(props) {
5660
5855
  }
5661
5856
  };
5662
5857
  return jsxRuntime.jsx("button", {
5858
+ type: "button",
5663
5859
  class: classnames('bio-properties-panel-feel-icon', active ? 'active' : null, feel === 'required' ? 'required' : 'optional'),
5664
5860
  onClick: handleClick,
5665
5861
  disabled: feel === 'required' || disabled,
@@ -5778,6 +5974,7 @@ function PopupComponent(props, globalRef) {
5778
5974
  const focusTrapRef = hooks.useRef(null);
5779
5975
  const localRef = hooks.useRef(null);
5780
5976
  const popupRef = globalRef || localRef;
5977
+ const containerNode = hooks.useMemo(() => getContainerNode(container), [container]);
5781
5978
  const handleKeydown = event => {
5782
5979
  // do not allow keyboard events to bubble
5783
5980
  event.stopPropagation();
@@ -5837,7 +6034,7 @@ function PopupComponent(props, globalRef) {
5837
6034
  class: classnames('bio-properties-panel-popup', className),
5838
6035
  style: style,
5839
6036
  children: props.children
5840
- }), container || document.body);
6037
+ }), containerNode || document.body);
5841
6038
  }
5842
6039
  const Popup = React.forwardRef(PopupComponent);
5843
6040
  Popup.Title = Title;
@@ -5957,6 +6154,12 @@ function cancel(event) {
5957
6154
  event.preventDefault();
5958
6155
  event.stopPropagation();
5959
6156
  }
6157
+ function getContainerNode(node) {
6158
+ if (typeof node === 'string') {
6159
+ return minDom.query(node);
6160
+ }
6161
+ return node;
6162
+ }
5960
6163
  const FEEL_POPUP_WIDTH = 700;
5961
6164
  const FEEL_POPUP_HEIGHT = 250;
5962
6165
 
@@ -6099,6 +6302,12 @@ function FeelPopupComponent(props) {
6099
6302
  domNode: popupRef.current
6100
6303
  });
6101
6304
  }, []);
6305
+ hooks.useEffect(() => {
6306
+ // Set focus on editor when popup is opened
6307
+ if (editorRef.current) {
6308
+ editorRef.current.focus();
6309
+ }
6310
+ }, [editorRef]);
6102
6311
  return jsxRuntime.jsxs(Popup, {
6103
6312
  container: container,
6104
6313
  className: "bio-properties-panel-feel-popup",
@@ -6117,10 +6326,21 @@ function FeelPopupComponent(props) {
6117
6326
  height: FEEL_POPUP_HEIGHT,
6118
6327
  width: FEEL_POPUP_WIDTH,
6119
6328
  ref: popupRef,
6120
- children: [jsxRuntime.jsx(Popup.Title, {
6329
+ children: [jsxRuntime.jsxs(Popup.Title, {
6121
6330
  title: title,
6122
6331
  emit: emit,
6123
- draggable: true
6332
+ draggable: true,
6333
+ children: [type === 'feel' && jsxRuntime.jsxs("a", {
6334
+ href: "https://docs.camunda.io/docs/components/modeler/feel/what-is-feel/",
6335
+ target: "_blank",
6336
+ class: "bio-properties-panel-feel-popup__title-link",
6337
+ children: ["Learn FEEL expressions", jsxRuntime.jsx(HelpIcon, {})]
6338
+ }), type === 'feelers' && jsxRuntime.jsxs("a", {
6339
+ href: "https://docs.camunda.io/docs/components/modeler/forms/configuration/forms-config-templating-syntax/",
6340
+ target: "_blank",
6341
+ class: "bio-properties-panel-feel-popup__title-link",
6342
+ children: ["Learn templating", jsxRuntime.jsx(HelpIcon, {})]
6343
+ })]
6124
6344
  }), jsxRuntime.jsx(Popup.Body, {
6125
6345
  children: jsxRuntime.jsxs("div", {
6126
6346
  onKeyDownCapture: onKeyDownCapture,
@@ -6152,6 +6372,7 @@ function FeelPopupComponent(props) {
6152
6372
  })
6153
6373
  }), jsxRuntime.jsx(Popup.Footer, {
6154
6374
  children: jsxRuntime.jsx("button", {
6375
+ type: "button",
6155
6376
  onClick: onClose,
6156
6377
  title: "Close pop-up editor",
6157
6378
  class: "bio-properties-panel-feel-popup__close-btn",
@@ -6504,7 +6725,7 @@ function FeelTextfield(props) {
6504
6725
  }, [onInput, debounce]);
6505
6726
  const setLocalValue = newValue => {
6506
6727
  _setLocalValue(newValue);
6507
- if (!newValue || newValue === '=') {
6728
+ if (typeof newValue === 'undefined' || newValue === '' || newValue === '=') {
6508
6729
  handleInputCallback(undefined);
6509
6730
  } else {
6510
6731
  handleInputCallback(newValue);
@@ -7393,12 +7614,14 @@ function CollapsibleEntry(props) {
7393
7614
  class: classnames('bio-properties-panel-collapsible-entry-header-title', !label && 'empty'),
7394
7615
  children: label || placeholderLabel
7395
7616
  }), jsxRuntime.jsx("button", {
7617
+ type: "button",
7396
7618
  title: "Toggle list item",
7397
7619
  class: "bio-properties-panel-arrow bio-properties-panel-collapsible-entry-arrow",
7398
7620
  children: jsxRuntime.jsx(ArrowIcon, {
7399
7621
  class: open ? 'bio-properties-panel-arrow-down' : 'bio-properties-panel-arrow-right'
7400
7622
  })
7401
7623
  }), remove ? jsxRuntime.jsx("button", {
7624
+ type: "button",
7402
7625
  title: "Delete item",
7403
7626
  class: "bio-properties-panel-remove-entry",
7404
7627
  onClick: remove,
@@ -7600,6 +7823,7 @@ function ListGroup(props) {
7600
7823
  }), jsxRuntime.jsxs("div", {
7601
7824
  class: "bio-properties-panel-group-header-buttons",
7602
7825
  children: [add ? jsxRuntime.jsxs("button", {
7826
+ type: "button",
7603
7827
  title: "Create new list item",
7604
7828
  class: "bio-properties-panel-group-header-button bio-properties-panel-add-entry",
7605
7829
  onClick: handleAddClick,
@@ -7612,6 +7836,7 @@ function ListGroup(props) {
7612
7836
  class: classnames('bio-properties-panel-list-badge', hasError ? 'bio-properties-panel-list-badge--error' : ''),
7613
7837
  children: items.length
7614
7838
  }) : null, hasItems ? jsxRuntime.jsx("button", {
7839
+ type: "button",
7615
7840
  title: "Toggle section",
7616
7841
  class: "bio-properties-panel-group-header-button bio-properties-panel-arrow",
7617
7842
  children: jsxRuntime.jsx(ArrowIcon, {
@@ -8266,11 +8491,11 @@ var index = {
8266
8491
  feelPopup: ['type', FeelPopupModule]
8267
8492
  };
8268
8493
 
8269
- /**
8270
- * @param {string} type
8271
- * @param {boolean} [strict]
8272
- *
8273
- * @returns {any}
8494
+ /**
8495
+ * @param {string} type
8496
+ * @param {boolean} [strict]
8497
+ *
8498
+ * @returns {any}
8274
8499
  */
8275
8500
  function getService(type, strict) {}
8276
8501
  const PropertiesPanelContext = preact.createContext({
@@ -8308,25 +8533,43 @@ function isValidDotPath(path) {
8308
8533
  }
8309
8534
  const INPUTS = ['checkbox', 'checklist', 'datetime', 'number', 'radio', 'select', 'taglist', 'textfield', 'textarea'];
8310
8535
  const VALUES_INPUTS = ['checklist', 'radio', 'select', 'taglist'];
8536
+ function hasEntryConfigured(formFieldDefinition, entryId) {
8537
+ const {
8538
+ propertiesPanelEntries = []
8539
+ } = formFieldDefinition;
8540
+ if (!propertiesPanelEntries.length) {
8541
+ return false;
8542
+ }
8543
+ return propertiesPanelEntries.some(id => id === entryId);
8544
+ }
8545
+ function hasValuesGroupsConfigured(formFieldDefinition) {
8546
+ const {
8547
+ propertiesPanelEntries = []
8548
+ } = formFieldDefinition;
8549
+ if (!propertiesPanelEntries.length) {
8550
+ return false;
8551
+ }
8552
+ return propertiesPanelEntries.some(id => id === 'values');
8553
+ }
8554
+
8555
+ function useService (type, strict) {
8556
+ const {
8557
+ getService
8558
+ } = hooks.useContext(FormPropertiesPanelContext);
8559
+ return getService(type, strict);
8560
+ }
8561
+
8562
+ /**
8563
+ * Retrieve list of variables from the form schema.
8564
+ *
8565
+ * @returns { string[] } list of variables used in form schema
8566
+ */
8567
+ function useVariables() {
8568
+ const form = useService('formEditor');
8569
+ const schema = form.getSchema();
8570
+ return formJsViewer.getSchemaVariables(schema);
8571
+ }
8311
8572
 
8312
- const labelsByType = {
8313
- button: 'BUTTON',
8314
- checkbox: 'CHECKBOX',
8315
- checklist: 'CHECKLIST',
8316
- columns: 'COLUMNS',
8317
- default: 'FORM',
8318
- datetime: 'DATETIME',
8319
- group: 'GROUP',
8320
- image: 'IMAGE VIEW',
8321
- number: 'NUMBER',
8322
- radio: 'RADIO',
8323
- select: 'SELECT',
8324
- spacer: 'SPACER',
8325
- taglist: 'TAGLIST',
8326
- text: 'TEXT VIEW',
8327
- textfield: 'TEXT FIELD',
8328
- textarea: 'TEXT AREA'
8329
- };
8330
8573
  const PropertiesPanelHeaderProvider = {
8331
8574
  getElementLabel: field => {
8332
8575
  const {
@@ -8350,25 +8593,43 @@ const PropertiesPanelHeaderProvider = {
8350
8593
  const {
8351
8594
  type
8352
8595
  } = field;
8353
- const Icon = formJsViewer.iconsByType(type);
8596
+
8597
+ // @Note: We know that we are inside the properties panel context,
8598
+ // so we can savely use the hook here.
8599
+ // eslint-disable-next-line react-hooks/rules-of-hooks
8600
+ const fieldDefinition = useService('formFields').get(type).config;
8601
+ const Icon = fieldDefinition.icon || formJsViewer.iconsByType(type);
8354
8602
  if (Icon) {
8355
8603
  return () => jsxRuntime.jsx(Icon, {
8356
8604
  width: "36",
8357
8605
  height: "36",
8358
8606
  viewBox: "0 0 54 54"
8359
8607
  });
8608
+ } else if (fieldDefinition.iconUrl) {
8609
+ return getPaletteIcon({
8610
+ iconUrl: fieldDefinition.iconUrl,
8611
+ label: fieldDefinition.label
8612
+ });
8360
8613
  }
8361
8614
  },
8362
8615
  getTypeLabel: field => {
8363
8616
  const {
8364
8617
  type
8365
8618
  } = field;
8366
- return labelsByType[type];
8619
+ if (type === 'default') {
8620
+ return 'Form';
8621
+ }
8622
+
8623
+ // @Note: We know that we are inside the properties panel context,
8624
+ // so we can savely use the hook here.
8625
+ // eslint-disable-next-line react-hooks/rules-of-hooks
8626
+ const fieldDefinition = useService('formFields').get(type).config;
8627
+ return fieldDefinition.label || type;
8367
8628
  }
8368
8629
  };
8369
8630
 
8370
- /**
8371
- * Provide placeholders for empty and multiple state.
8631
+ /**
8632
+ * Provide placeholders for empty and multiple state.
8372
8633
  */
8373
8634
  const PropertiesPanelPlaceholderProvider = {
8374
8635
  getEmpty: () => {
@@ -8383,24 +8644,210 @@ const PropertiesPanelPlaceholderProvider = {
8383
8644
  }
8384
8645
  };
8385
8646
 
8386
- function ActionEntry(props) {
8647
+ function FormPropertiesPanel(props) {
8387
8648
  const {
8388
- editField,
8389
- field
8649
+ eventBus,
8650
+ getProviders,
8651
+ injector
8390
8652
  } = props;
8391
- const {
8392
- type
8393
- } = field;
8394
- const entries = [];
8395
- if (type === 'button') {
8396
- entries.push({
8397
- id: 'action',
8398
- component: Action,
8399
- editField: editField,
8400
- field: field,
8401
- isEdited: isEdited$3
8653
+ const formEditor = injector.get('formEditor');
8654
+ const modeling = injector.get('modeling');
8655
+ const selectionModule = injector.get('selection');
8656
+ const propertiesPanelConfig = injector.get('config.propertiesPanel') || {};
8657
+ const {
8658
+ feelPopupContainer
8659
+ } = propertiesPanelConfig;
8660
+ const [state, setState] = hooks.useState({
8661
+ selectedFormField: selectionModule.get() || formEditor._getState().schema
8662
+ });
8663
+ const selectedFormField = state.selectedFormField;
8664
+ const refresh = hooks.useCallback(field => {
8665
+ // TODO(skaiir): rework state management, re-rendering the whole properties panel is not the way to go
8666
+ // https://github.com/bpmn-io/form-js/issues/686
8667
+ setState({
8668
+ selectedFormField: selectionModule.get() || formEditor._getState().schema
8669
+ });
8670
+
8671
+ // notify interested parties on property panel updates
8672
+ eventBus.fire('propertiesPanel.updated', {
8673
+ formField: field
8674
+ });
8675
+ }, [eventBus, formEditor, selectionModule]);
8676
+ hooks.useLayoutEffect(() => {
8677
+ /**
8678
+ * TODO(pinussilvestrus): update with actual updated element,
8679
+ * once we have a proper updater/change support
8680
+ */
8681
+ eventBus.on('changed', refresh);
8682
+ eventBus.on('import.done', refresh);
8683
+ eventBus.on('selection.changed', refresh);
8684
+ return () => {
8685
+ eventBus.off('changed', refresh);
8686
+ eventBus.off('import.done', refresh);
8687
+ eventBus.off('selection.changed', refresh);
8688
+ };
8689
+ }, [eventBus, refresh]);
8690
+ const getService = (type, strict = true) => injector.get(type, strict);
8691
+ const propertiesPanelContext = {
8692
+ getService
8693
+ };
8694
+ const onFocus = () => eventBus.fire('propertiesPanel.focusin');
8695
+ const onBlur = () => eventBus.fire('propertiesPanel.focusout');
8696
+ const editField = hooks.useCallback((formField, key, value) => modeling.editFormField(formField, key, value), [modeling]);
8697
+
8698
+ // retrieve groups for selected form field
8699
+ const providers = getProviders(selectedFormField);
8700
+ const groups = hooks.useMemo(() => {
8701
+ return minDash.reduce(providers, function (groups, provider) {
8702
+ // do not collect groups for multi element state
8703
+ if (minDash.isArray(selectedFormField)) {
8704
+ return [];
8705
+ }
8706
+ const updater = provider.getGroups(selectedFormField, editField);
8707
+ return updater(groups);
8708
+ }, []);
8709
+ }, [providers, selectedFormField, editField]);
8710
+ return jsxRuntime.jsx("div", {
8711
+ class: "fjs-properties-panel",
8712
+ "data-field": selectedFormField && selectedFormField.id,
8713
+ onFocusCapture: onFocus,
8714
+ onBlurCapture: onBlur,
8715
+ children: jsxRuntime.jsx(FormPropertiesPanelContext.Provider, {
8716
+ value: propertiesPanelContext,
8717
+ children: jsxRuntime.jsx(PropertiesPanel, {
8718
+ element: selectedFormField,
8719
+ eventBus: eventBus,
8720
+ groups: groups,
8721
+ headerProvider: PropertiesPanelHeaderProvider,
8722
+ placeholderProvider: PropertiesPanelPlaceholderProvider,
8723
+ feelPopupContainer: feelPopupContainer
8724
+ })
8725
+ })
8726
+ });
8727
+ }
8728
+
8729
+ const DEFAULT_PRIORITY = 1000;
8730
+
8731
+ /**
8732
+ * @typedef { { parent: Element } } PropertiesPanelConfig
8733
+ * @typedef { import('../../core/EventBus').default } EventBus
8734
+ * @typedef { import('../../types').Injector } Injector
8735
+ * @typedef { { getGroups: ({ formField, editFormField }) => ({ groups}) => Array } } PropertiesProvider
8736
+ */
8737
+
8738
+ /**
8739
+ * @param {PropertiesPanelConfig} propertiesPanelConfig
8740
+ * @param {Injector} injector
8741
+ * @param {EventBus} eventBus
8742
+ */
8743
+ class PropertiesPanelRenderer {
8744
+ constructor(propertiesPanelConfig, injector, eventBus) {
8745
+ const {
8746
+ parent
8747
+ } = propertiesPanelConfig || {};
8748
+ this._eventBus = eventBus;
8749
+ this._injector = injector;
8750
+ this._container = minDom.domify('<div class="fjs-properties-container" input-handle-modified-keys="y,z"></div>');
8751
+ if (parent) {
8752
+ this.attachTo(parent);
8753
+ }
8754
+ this._eventBus.once('formEditor.rendered', 500, () => {
8755
+ this._render();
8402
8756
  });
8403
8757
  }
8758
+
8759
+ /**
8760
+ * Attach the properties panel to a parent node.
8761
+ *
8762
+ * @param {HTMLElement} container
8763
+ */
8764
+ attachTo(container) {
8765
+ if (!container) {
8766
+ throw new Error('container required');
8767
+ }
8768
+ if (typeof container === 'string') {
8769
+ container = minDom.query(container);
8770
+ }
8771
+
8772
+ // (1) detach from old parent
8773
+ this.detach();
8774
+
8775
+ // (2) append to parent container
8776
+ container.appendChild(this._container);
8777
+
8778
+ // (3) notify interested parties
8779
+ this._eventBus.fire('propertiesPanel.attach');
8780
+ }
8781
+
8782
+ /**
8783
+ * Detach the properties panel from its parent node.
8784
+ */
8785
+ detach() {
8786
+ const parentNode = this._container.parentNode;
8787
+ if (parentNode) {
8788
+ parentNode.removeChild(this._container);
8789
+ this._eventBus.fire('propertiesPanel.detach');
8790
+ }
8791
+ }
8792
+ _render() {
8793
+ preact.render(jsxRuntime.jsx(FormPropertiesPanel, {
8794
+ getProviders: this._getProviders.bind(this),
8795
+ eventBus: this._eventBus,
8796
+ injector: this._injector
8797
+ }), this._container);
8798
+ this._eventBus.fire('propertiesPanel.rendered');
8799
+ }
8800
+ _destroy() {
8801
+ if (this._container) {
8802
+ preact.render(null, this._container);
8803
+ this._eventBus.fire('propertiesPanel.destroyed');
8804
+ }
8805
+ }
8806
+
8807
+ /**
8808
+ * Register a new properties provider to the properties panel.
8809
+ *
8810
+ * @param {PropertiesProvider} provider
8811
+ * @param {Number} [priority]
8812
+ */
8813
+ registerProvider(provider, priority) {
8814
+ if (!priority) {
8815
+ priority = DEFAULT_PRIORITY;
8816
+ }
8817
+ if (typeof provider.getGroups !== 'function') {
8818
+ console.error('Properties provider does not implement #getGroups(element) API');
8819
+ return;
8820
+ }
8821
+ this._eventBus.on('propertiesPanel.getProviders', priority, function (event) {
8822
+ event.providers.push(provider);
8823
+ });
8824
+ this._eventBus.fire('propertiesPanel.providersChanged');
8825
+ }
8826
+ _getProviders() {
8827
+ const event = this._eventBus.createEvent({
8828
+ type: 'propertiesPanel.getProviders',
8829
+ providers: []
8830
+ });
8831
+ this._eventBus.fire(event);
8832
+ return event.providers;
8833
+ }
8834
+ }
8835
+ PropertiesPanelRenderer.$inject = ['config.propertiesPanel', 'injector', 'eventBus'];
8836
+
8837
+ function ActionEntry(props) {
8838
+ const {
8839
+ editField,
8840
+ field
8841
+ } = props;
8842
+ const entries = [];
8843
+ entries.push({
8844
+ id: 'action',
8845
+ component: Action,
8846
+ editField: editField,
8847
+ field: field,
8848
+ isEdited: isEdited$3,
8849
+ isDefaultVisible: field => field.type === 'button'
8850
+ });
8404
8851
  return entries;
8405
8852
  }
8406
8853
  function Action(props) {
@@ -8433,42 +8880,20 @@ function Action(props) {
8433
8880
  });
8434
8881
  }
8435
8882
 
8436
- function useService (type, strict) {
8437
- const {
8438
- getService
8439
- } = hooks.useContext(FormPropertiesPanelContext);
8440
- return getService(type, strict);
8441
- }
8442
-
8443
- /**
8444
- * Retrieve list of variables from the form schema.
8445
- *
8446
- * @returns { string[] } list of variables used in form schema
8447
- */
8448
- function useVariables() {
8449
- const form = useService('formEditor');
8450
- const schema = form.getSchema();
8451
- return formJsViewer.getSchemaVariables(schema);
8452
- }
8453
-
8454
8883
  function AltTextEntry(props) {
8455
8884
  const {
8456
8885
  editField,
8457
8886
  field
8458
8887
  } = props;
8459
- const {
8460
- type
8461
- } = field;
8462
8888
  const entries = [];
8463
- if (type === 'image') {
8464
- entries.push({
8465
- id: 'alt',
8466
- component: AltText,
8467
- editField: editField,
8468
- field: field,
8469
- isEdited: isEdited$6
8470
- });
8471
- }
8889
+ entries.push({
8890
+ id: 'alt',
8891
+ component: AltText,
8892
+ editField: editField,
8893
+ field: field,
8894
+ isEdited: isEdited$6,
8895
+ isDefaultVisible: field => ['image'].includes(field.type)
8896
+ });
8472
8897
  return entries;
8473
8898
  }
8474
8899
  function AltText(props) {
@@ -8579,19 +9004,15 @@ function DescriptionEntry(props) {
8579
9004
  editField,
8580
9005
  field
8581
9006
  } = props;
8582
- const {
8583
- type
8584
- } = field;
8585
9007
  const entries = [];
8586
- if (INPUTS.includes(type)) {
8587
- entries.push({
8588
- id: 'description',
8589
- component: Description,
8590
- editField: editField,
8591
- field: field,
8592
- isEdited: isEdited$6
8593
- });
8594
- }
9008
+ entries.push({
9009
+ id: 'description',
9010
+ component: Description,
9011
+ editField: editField,
9012
+ field: field,
9013
+ isEdited: isEdited$6,
9014
+ isDefaultVisible: field => INPUTS.includes(field.type)
9015
+ });
8595
9016
  return entries;
8596
9017
  }
8597
9018
  function Description(props) {
@@ -8633,10 +9054,14 @@ function DefaultOptionEntry(props) {
8633
9054
  type
8634
9055
  } = field;
8635
9056
  const entries = [];
8636
-
8637
- // Only make default values available when they are statically defined
8638
- if (!INPUTS.includes(type) || VALUES_INPUTS.includes(type) && !field.values) {
8639
- return entries;
9057
+ function isDefaultVisible(matchers) {
9058
+ return field => {
9059
+ // Only make default values available when they are statically defined
9060
+ if (!INPUTS.includes(type) || VALUES_INPUTS.includes(type) && !field.values) {
9061
+ return false;
9062
+ }
9063
+ return matchers(field);
9064
+ };
8640
9065
  }
8641
9066
  const defaultOptions = {
8642
9067
  editField,
@@ -8644,44 +9069,39 @@ function DefaultOptionEntry(props) {
8644
9069
  id: 'defaultValue',
8645
9070
  label: 'Default value'
8646
9071
  };
8647
- if (type === 'checkbox') {
8648
- entries.push({
8649
- ...defaultOptions,
8650
- component: DefaultValueCheckbox,
8651
- isEdited: isEdited$3
8652
- });
8653
- }
8654
- if (type === 'number') {
8655
- entries.push({
8656
- ...defaultOptions,
8657
- component: DefaultValueNumber,
8658
- isEdited: isEdited
8659
- });
8660
- }
8661
- if (type === 'radio' || type === 'select') {
8662
- entries.push({
8663
- ...defaultOptions,
8664
- component: DefaultValueSingleSelect,
8665
- isEdited: isEdited$3
8666
- });
8667
- }
9072
+ entries.push({
9073
+ ...defaultOptions,
9074
+ component: DefaultValueCheckbox,
9075
+ isEdited: isEdited$3,
9076
+ isDefaultVisible: isDefaultVisible(field => field.type === 'checkbox')
9077
+ });
9078
+ entries.push({
9079
+ ...defaultOptions,
9080
+ component: DefaultValueNumber,
9081
+ isEdited: isEdited,
9082
+ isDefaultVisible: isDefaultVisible(field => field.type === 'number')
9083
+ });
9084
+ entries.push({
9085
+ ...defaultOptions,
9086
+ component: DefaultValueSingleSelect,
9087
+ isEdited: isEdited$3,
9088
+ isDefaultVisible: isDefaultVisible(field => field.type === 'radio' || field.type === 'select')
9089
+ });
8668
9090
 
8669
9091
  // todo(Skaiir): implement a multiselect equivalent (cf. https://github.com/bpmn-io/form-js/issues/265)
8670
9092
 
8671
- if (type === 'textfield') {
8672
- entries.push({
8673
- ...defaultOptions,
8674
- component: DefaultValueTextfield,
8675
- isEdited: isEdited
8676
- });
8677
- }
8678
- if (type === 'textarea') {
8679
- entries.push({
8680
- ...defaultOptions,
8681
- component: DefaultValueTextarea,
8682
- isEdited: isEdited$1
8683
- });
8684
- }
9093
+ entries.push({
9094
+ ...defaultOptions,
9095
+ component: DefaultValueTextfield,
9096
+ isEdited: isEdited,
9097
+ isDefaultVisible: isDefaultVisible(field => field.type === 'textfield')
9098
+ });
9099
+ entries.push({
9100
+ ...defaultOptions,
9101
+ component: DefaultValueTextarea,
9102
+ isEdited: isEdited$1,
9103
+ isDefaultVisible: isDefaultVisible(field => field.type === 'textarea')
9104
+ });
8685
9105
  return entries;
8686
9106
  }
8687
9107
  function DefaultValueCheckbox(props) {
@@ -8866,19 +9286,15 @@ function DisabledEntry(props) {
8866
9286
  editField,
8867
9287
  field
8868
9288
  } = props;
8869
- const {
8870
- type
8871
- } = field;
8872
9289
  const entries = [];
8873
- if (INPUTS.includes(type)) {
8874
- entries.push({
8875
- id: 'disabled',
8876
- component: Disabled,
8877
- editField: editField,
8878
- field: field,
8879
- isEdited: isEdited$8
8880
- });
8881
- }
9290
+ entries.push({
9291
+ id: 'disabled',
9292
+ component: Disabled,
9293
+ editField: editField,
9294
+ field: field,
9295
+ isEdited: isEdited$8,
9296
+ isDefaultVisible: field => INPUTS.includes(field.type)
9297
+ });
8882
9298
  return entries;
8883
9299
  }
8884
9300
  function Disabled(props) {
@@ -8911,15 +9327,14 @@ function IdEntry(props) {
8911
9327
  field
8912
9328
  } = props;
8913
9329
  const entries = [];
8914
- if (field.type === 'default') {
8915
- entries.push({
8916
- id: 'id',
8917
- component: Id,
8918
- editField: editField,
8919
- field: field,
8920
- isEdited: isEdited
8921
- });
8922
- }
9330
+ entries.push({
9331
+ id: 'id',
9332
+ component: Id,
9333
+ editField: editField,
9334
+ field: field,
9335
+ isEdited: isEdited,
9336
+ isDefaultVisible: field => field.type === 'default'
9337
+ });
8923
9338
  return entries;
8924
9339
  }
8925
9340
  function Id(props) {
@@ -8990,19 +9405,15 @@ function KeyEntry(props) {
8990
9405
  editField,
8991
9406
  field
8992
9407
  } = props;
8993
- const {
8994
- type
8995
- } = field;
8996
9408
  const entries = [];
8997
- if (INPUTS.includes(type)) {
8998
- entries.push({
8999
- id: 'key',
9000
- component: Key$1,
9001
- editField: editField,
9002
- field: field,
9003
- isEdited: isEdited
9004
- });
9005
- }
9409
+ entries.push({
9410
+ id: 'key',
9411
+ component: Key$1,
9412
+ editField: editField,
9413
+ field: field,
9414
+ isEdited: isEdited,
9415
+ isDefaultVisible: field => INPUTS.includes(field.type)
9416
+ });
9006
9417
  return entries;
9007
9418
  }
9008
9419
  function Key$1(props) {
@@ -9148,7 +9559,8 @@ function simpleBoolEntryFactory(options) {
9148
9559
  label,
9149
9560
  description,
9150
9561
  path,
9151
- props
9562
+ props,
9563
+ isDefaultVisible
9152
9564
  } = options;
9153
9565
  const {
9154
9566
  editField,
@@ -9162,7 +9574,8 @@ function simpleBoolEntryFactory(options) {
9162
9574
  editField,
9163
9575
  description,
9164
9576
  component: SimpleBoolComponent,
9165
- isEdited: isEdited$5
9577
+ isEdited: isEdited$5,
9578
+ isDefaultVisible
9166
9579
  };
9167
9580
  }
9168
9581
  const SimpleBoolComponent = props => {
@@ -9210,39 +9623,35 @@ function LabelEntry(props) {
9210
9623
  field,
9211
9624
  editField
9212
9625
  } = props;
9213
- const {
9214
- type,
9215
- subtype
9216
- } = field;
9217
9626
  const entries = [];
9218
- if (type === 'datetime') {
9219
- if (subtype === formJsViewer.DATETIME_SUBTYPES.DATE || subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME) {
9220
- entries.push({
9221
- id: 'date-label',
9222
- component: DateLabel,
9223
- editField,
9224
- field,
9225
- isEdited: isEdited$6
9226
- });
9627
+ entries.push({
9628
+ id: 'date-label',
9629
+ component: DateLabel,
9630
+ editField,
9631
+ field,
9632
+ isEdited: isEdited$6,
9633
+ isDefaultVisible: function (field) {
9634
+ return field.type === 'datetime' && (field.subtype === formJsViewer.DATETIME_SUBTYPES.DATE || field.subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME);
9227
9635
  }
9228
- if (subtype === formJsViewer.DATETIME_SUBTYPES.TIME || subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME) {
9229
- entries.push({
9230
- id: 'time-label',
9231
- component: TimeLabel,
9232
- editField,
9233
- field,
9234
- isEdited: isEdited$6
9235
- });
9636
+ });
9637
+ entries.push({
9638
+ id: 'time-label',
9639
+ component: TimeLabel,
9640
+ editField,
9641
+ field,
9642
+ isEdited: isEdited$6,
9643
+ isDefaultVisible: function (field) {
9644
+ return field.type === 'datetime' && (field.subtype === formJsViewer.DATETIME_SUBTYPES.TIME || field.subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME);
9236
9645
  }
9237
- } else if (INPUTS.includes(type) || type === 'button' || type === 'group') {
9238
- entries.push({
9239
- id: 'label',
9240
- component: Label$1,
9241
- editField,
9242
- field,
9243
- isEdited: isEdited$6
9244
- });
9245
- }
9646
+ });
9647
+ entries.push({
9648
+ id: 'label',
9649
+ component: Label$1,
9650
+ editField,
9651
+ field,
9652
+ isEdited: isEdited$6,
9653
+ isDefaultVisible: field => INPUTS.includes(field.type) || field.type === 'button' || field.type === 'group'
9654
+ });
9246
9655
  return entries;
9247
9656
  }
9248
9657
  function Label$1(props) {
@@ -9336,19 +9745,15 @@ function SourceEntry(props) {
9336
9745
  editField,
9337
9746
  field
9338
9747
  } = props;
9339
- const {
9340
- type
9341
- } = field;
9342
9748
  const entries = [];
9343
- if (type === 'image') {
9344
- entries.push({
9345
- id: 'source',
9346
- component: Source,
9347
- editField: editField,
9348
- field: field,
9349
- isEdited: isEdited$6
9350
- });
9351
- }
9749
+ entries.push({
9750
+ id: 'source',
9751
+ component: Source,
9752
+ editField: editField,
9753
+ field: field,
9754
+ isEdited: isEdited$6,
9755
+ isDefaultVisible: field => field.type === 'image'
9756
+ });
9352
9757
  return entries;
9353
9758
  }
9354
9759
  function Source(props) {
@@ -9386,24 +9791,15 @@ function Source(props) {
9386
9791
  function TextEntry(props) {
9387
9792
  const {
9388
9793
  editField,
9389
- /* getService, */
9390
9794
  field
9391
9795
  } = props;
9392
- const {
9393
- type
9394
- } = field;
9395
-
9396
- // const templating = getService('templating');
9397
-
9398
- if (type !== 'text') {
9399
- return [];
9400
- }
9401
9796
  const entries = [{
9402
9797
  id: 'text',
9403
9798
  component: Text,
9404
9799
  editField: editField,
9405
9800
  field: field,
9406
- isEdited: isEdited$6
9801
+ isEdited: isEdited$6,
9802
+ isDefaultVisible: field => field.type === 'text'
9407
9803
  }];
9408
9804
 
9409
9805
  // todo: skipped to make the release without too much risk
@@ -9462,19 +9858,14 @@ function SpacerEntry(props) {
9462
9858
  field,
9463
9859
  id
9464
9860
  } = props;
9465
- const {
9466
- type
9467
- } = field;
9468
- if (type !== 'spacer') {
9469
- return [];
9470
- }
9471
9861
  const entries = [];
9472
9862
  entries.push({
9473
9863
  id: id + '-height',
9474
9864
  component: SpacerHeight,
9475
9865
  isEdited: isEdited$7,
9476
9866
  editField,
9477
- field
9867
+ field,
9868
+ isDefaultVisible: field => field.type === 'spacer'
9478
9869
  });
9479
9870
  return entries;
9480
9871
  }
@@ -9513,26 +9904,22 @@ function NumberEntries(props) {
9513
9904
  field,
9514
9905
  id
9515
9906
  } = props;
9516
- const {
9517
- type
9518
- } = field;
9519
- if (type !== 'number') {
9520
- return [];
9521
- }
9522
9907
  const entries = [];
9523
9908
  entries.push({
9524
9909
  id: id + '-decimalDigits',
9525
9910
  component: NumberDecimalDigits,
9526
9911
  isEdited: isEdited$7,
9527
9912
  editField,
9528
- field
9913
+ field,
9914
+ isDefaultVisible: field => field.type === 'number'
9529
9915
  });
9530
9916
  entries.push({
9531
9917
  id: id + '-step',
9532
9918
  component: NumberArrowStep,
9533
9919
  isEdited: isEdited,
9534
9920
  editField,
9535
- field
9921
+ field,
9922
+ isDefaultVisible: field => field.type === 'number'
9536
9923
  });
9537
9924
  return entries;
9538
9925
  }
@@ -9617,19 +10004,14 @@ function NumberSerializationEntry(props) {
9617
10004
  editField,
9618
10005
  field
9619
10006
  } = props;
9620
- const {
9621
- type
9622
- } = field;
9623
- if (type !== 'number') {
9624
- return [];
9625
- }
9626
10007
  const entries = [];
9627
10008
  entries.push({
9628
10009
  id: 'serialize-to-string',
9629
10010
  component: SerializeToString,
9630
10011
  isEdited: isEdited$5,
9631
10012
  editField,
9632
- field
10013
+ field,
10014
+ isDefaultVisible: field => field.type === 'number'
9633
10015
  });
9634
10016
  return entries;
9635
10017
  }
@@ -9668,29 +10050,22 @@ function DateTimeEntry(props) {
9668
10050
  editField,
9669
10051
  field
9670
10052
  } = props;
9671
- const {
9672
- type,
9673
- subtype
9674
- } = field;
9675
- if (type !== 'datetime') {
9676
- return [];
9677
- }
9678
10053
  const entries = [{
9679
10054
  id: 'subtype',
9680
10055
  component: DateTimeSubtypeSelect,
9681
10056
  isEdited: isEdited$3,
9682
10057
  editField,
9683
- field
10058
+ field,
10059
+ isDefaultVisible: field => field.type === 'datetime'
9684
10060
  }];
9685
- if (subtype === formJsViewer.DATETIME_SUBTYPES.TIME || subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME) {
9686
- entries.push({
9687
- id: 'use24h',
9688
- component: Use24h,
9689
- isEdited: isEdited$5,
9690
- editField,
9691
- field
9692
- });
9693
- }
10061
+ entries.push({
10062
+ id: 'use24h',
10063
+ component: Use24h,
10064
+ isEdited: isEdited$5,
10065
+ editField,
10066
+ field,
10067
+ isDefaultVisible: field => field.type === 'datetime' && (field.subtype === formJsViewer.DATETIME_SUBTYPES.TIME || field.subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME)
10068
+ });
9694
10069
  return entries;
9695
10070
  }
9696
10071
  function DateTimeSubtypeSelect(props) {
@@ -9778,32 +10153,31 @@ function DateTimeConstraintsEntry(props) {
9778
10153
  field,
9779
10154
  id
9780
10155
  } = props;
9781
- const {
9782
- type,
9783
- subtype
9784
- } = field;
9785
- if (type !== 'datetime') {
9786
- return [];
10156
+ function isDefaultVisible(subtypes) {
10157
+ return field => {
10158
+ if (field.type !== 'datetime') {
10159
+ return false;
10160
+ }
10161
+ return subtypes.includes(field.subtype);
10162
+ };
9787
10163
  }
9788
10164
  const entries = [];
9789
- if (subtype === formJsViewer.DATETIME_SUBTYPES.TIME || subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME) {
9790
- entries.push({
9791
- id: id + '-timeInterval',
9792
- component: TimeIntervalSelect,
9793
- isEdited: isEdited$3,
9794
- editField,
9795
- field
9796
- });
9797
- }
9798
- if (subtype === formJsViewer.DATETIME_SUBTYPES.DATE || subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME) {
9799
- entries.push({
9800
- id: id + '-disallowPassedDates',
9801
- component: DisallowPassedDates,
9802
- isEdited: isEdited$5,
9803
- editField,
9804
- field
9805
- });
9806
- }
10165
+ entries.push({
10166
+ id: id + '-timeInterval',
10167
+ component: TimeIntervalSelect,
10168
+ isEdited: isEdited$3,
10169
+ editField,
10170
+ field,
10171
+ isDefaultVisible: isDefaultVisible([formJsViewer.DATETIME_SUBTYPES.TIME, formJsViewer.DATETIME_SUBTYPES.DATETIME])
10172
+ });
10173
+ entries.push({
10174
+ id: id + '-disallowPassedDates',
10175
+ component: DisallowPassedDates,
10176
+ isEdited: isEdited$5,
10177
+ editField,
10178
+ field,
10179
+ isDefaultVisible: isDefaultVisible([formJsViewer.DATETIME_SUBTYPES.DATE, formJsViewer.DATETIME_SUBTYPES.DATETIME])
10180
+ });
9807
10181
  return entries;
9808
10182
  }
9809
10183
  function DisallowPassedDates(props) {
@@ -9857,23 +10231,15 @@ function DateTimeFormatEntry(props) {
9857
10231
  editField,
9858
10232
  field
9859
10233
  } = props;
9860
- const {
9861
- type,
9862
- subtype
9863
- } = field;
9864
- if (type !== 'datetime') {
9865
- return [];
9866
- }
9867
10234
  const entries = [];
9868
- if (subtype === formJsViewer.DATETIME_SUBTYPES.TIME || subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME) {
9869
- entries.push({
9870
- id: 'time-format',
9871
- component: TimeFormatSelect,
9872
- isEdited: isEdited$3,
9873
- editField,
9874
- field
9875
- });
9876
- }
10235
+ entries.push({
10236
+ id: 'time-format',
10237
+ component: TimeFormatSelect,
10238
+ isEdited: isEdited$3,
10239
+ editField,
10240
+ field,
10241
+ isDefaultVisible: field => field.type === 'datetime' && (field.subtype === formJsViewer.DATETIME_SUBTYPES.TIME || field.subtype === formJsViewer.DATETIME_SUBTYPES.DATETIME)
10242
+ });
9877
10243
  return entries;
9878
10244
  }
9879
10245
  function TimeFormatSelect(props) {
@@ -9901,20 +10267,12 @@ function TimeFormatSelect(props) {
9901
10267
  }
9902
10268
 
9903
10269
  function SelectEntries(props) {
9904
- const {
9905
- field
9906
- } = props;
9907
- const {
9908
- type
9909
- } = field;
9910
- if (type !== 'select') {
9911
- return [];
9912
- }
9913
10270
  const entries = [simpleBoolEntryFactory({
9914
10271
  id: 'searchable',
9915
10272
  path: ['searchable'],
9916
10273
  label: 'Searchable',
9917
- props
10274
+ props,
10275
+ isDefaultVisible: field => field.type === 'select'
9918
10276
  })];
9919
10277
  return entries;
9920
10278
  }
@@ -10094,14 +10452,14 @@ function Value(props) {
10094
10452
 
10095
10453
  // helpers //////////
10096
10454
 
10097
- /**
10098
- * Returns copy of object with updated value.
10099
- *
10100
- * @param {Object} properties
10101
- * @param {string} key
10102
- * @param {string} value
10103
- *
10104
- * @returns {Object}
10455
+ /**
10456
+ * Returns copy of object with updated value.
10457
+ *
10458
+ * @param {Object} properties
10459
+ * @param {string} key
10460
+ * @param {string} value
10461
+ *
10462
+ * @returns {Object}
10105
10463
  */
10106
10464
  function updateValue(properties, key, value) {
10107
10465
  return {
@@ -10110,14 +10468,14 @@ function updateValue(properties, key, value) {
10110
10468
  };
10111
10469
  }
10112
10470
 
10113
- /**
10114
- * Returns copy of object with updated key.
10115
- *
10116
- * @param {Object} properties
10117
- * @param {string} oldKey
10118
- * @param {string} newKey
10119
- *
10120
- * @returns {Object}
10471
+ /**
10472
+ * Returns copy of object with updated key.
10473
+ *
10474
+ * @param {Object} properties
10475
+ * @param {string} oldKey
10476
+ * @param {string} newKey
10477
+ *
10478
+ * @returns {Object}
10121
10479
  */
10122
10480
  function updateKey(properties, oldKey, newKey) {
10123
10481
  return Object.entries(properties).reduce((newProperties, entry) => {
@@ -10181,11 +10539,7 @@ function ValuesSourceSelect(props) {
10181
10539
  const setValue = value => {
10182
10540
  let newField = field;
10183
10541
  const newProperties = {};
10184
- Object.values(formJsViewer.VALUES_SOURCES).forEach(source => {
10185
- // Clear all values source definitions and default the newly selected one
10186
- const newValue = value === source ? formJsViewer.VALUES_SOURCES_DEFAULTS[source] : undefined;
10187
- newProperties[formJsViewer.VALUES_SOURCES_PATHS[source]] = newValue;
10188
- });
10542
+ newProperties[formJsViewer.VALUES_SOURCES_PATHS[value]] = formJsViewer.VALUES_SOURCES_DEFAULTS[value];
10189
10543
  newField = editField(field, newProperties);
10190
10544
  return newField;
10191
10545
  };
@@ -10359,9 +10713,6 @@ function AdornerEntry(props) {
10359
10713
  editField,
10360
10714
  field
10361
10715
  } = props;
10362
- const {
10363
- type
10364
- } = field;
10365
10716
  const entries = [];
10366
10717
  const onChange = key => {
10367
10718
  return value => {
@@ -10374,26 +10725,26 @@ function AdornerEntry(props) {
10374
10725
  return minDash.get(field, ['appearance', key]);
10375
10726
  };
10376
10727
  };
10377
- if (['number', 'textfield'].includes(type)) {
10378
- entries.push({
10379
- id: 'prefix-adorner',
10380
- component: PrefixAdorner,
10381
- isEdited: isEdited$6,
10382
- editField,
10383
- field,
10384
- onChange,
10385
- getValue
10386
- });
10387
- entries.push({
10388
- id: 'suffix-adorner',
10389
- component: SuffixAdorner,
10390
- isEdited: isEdited$6,
10391
- editField,
10392
- field,
10393
- onChange,
10394
- getValue
10395
- });
10396
- }
10728
+ entries.push({
10729
+ id: 'prefix-adorner',
10730
+ component: PrefixAdorner,
10731
+ isEdited: isEdited$6,
10732
+ editField,
10733
+ field,
10734
+ onChange,
10735
+ getValue,
10736
+ isDefaultVisible: field => ['number', 'textfield'].includes(field.type)
10737
+ });
10738
+ entries.push({
10739
+ id: 'suffix-adorner',
10740
+ component: SuffixAdorner,
10741
+ isEdited: isEdited$6,
10742
+ editField,
10743
+ field,
10744
+ onChange,
10745
+ getValue,
10746
+ isDefaultVisible: field => ['number', 'textfield'].includes(field.type)
10747
+ });
10397
10748
  return entries;
10398
10749
  }
10399
10750
  function PrefixAdorner(props) {
@@ -10447,19 +10798,15 @@ function ReadonlyEntry(props) {
10447
10798
  editField,
10448
10799
  field
10449
10800
  } = props;
10450
- const {
10451
- type
10452
- } = field;
10453
10801
  const entries = [];
10454
- if (INPUTS.includes(type)) {
10455
- entries.push({
10456
- id: 'readonly',
10457
- component: Readonly,
10458
- editField: editField,
10459
- field: field,
10460
- isEdited: isEdited$6
10461
- });
10462
- }
10802
+ entries.push({
10803
+ id: 'readonly',
10804
+ component: Readonly,
10805
+ editField: editField,
10806
+ field: field,
10807
+ isEdited: isEdited$6,
10808
+ isDefaultVisible: field => INPUTS.includes(field.type)
10809
+ });
10463
10810
  return entries;
10464
10811
  }
10465
10812
  function Readonly(props) {
@@ -10643,6 +10990,9 @@ function GeneralGroup(field, editField, getService) {
10643
10990
  field,
10644
10991
  editField
10645
10992
  })];
10993
+ if (entries.length === 0) {
10994
+ return null;
10995
+ }
10646
10996
  return {
10647
10997
  id: 'general',
10648
10998
  label: 'General',
@@ -10703,9 +11053,6 @@ function ValidationGroup(field, editField) {
10703
11053
  } = field;
10704
11054
  const validate = minDash.get(field, ['validate'], {});
10705
11055
  const isCustomValidation = [undefined, VALIDATION_TYPE_OPTIONS.custom.value].includes(validate.validationType);
10706
- if (!INPUTS.includes(type)) {
10707
- return null;
10708
- }
10709
11056
  const onChange = key => {
10710
11057
  return value => {
10711
11058
  const validate = minDash.get(field, ['validate'], {});
@@ -10723,63 +11070,62 @@ function ValidationGroup(field, editField) {
10723
11070
  getValue,
10724
11071
  field,
10725
11072
  isEdited: isEdited$5,
10726
- onChange
11073
+ onChange,
11074
+ isDefaultVisible: field => INPUTS.includes(field.type)
10727
11075
  }];
10728
- if (type === 'textfield') {
10729
- entries.push({
10730
- id: 'validationType',
10731
- component: ValidationType,
10732
- getValue,
10733
- field,
10734
- editField,
10735
- isEdited: isEdited,
10736
- onChange
10737
- });
10738
- }
10739
- if (type === 'textarea' || type === 'textfield' && isCustomValidation) {
10740
- entries.push({
10741
- id: 'minLength',
10742
- component: MinLength,
10743
- getValue,
10744
- field,
10745
- isEdited: isEdited$6,
10746
- onChange
10747
- }, {
10748
- id: 'maxLength',
10749
- component: MaxLength,
10750
- getValue,
10751
- field,
10752
- isEdited: isEdited$6,
10753
- onChange
10754
- });
10755
- }
10756
- if (type === 'textfield' && isCustomValidation) {
10757
- entries.push({
10758
- id: 'pattern',
10759
- component: Pattern,
10760
- getValue,
10761
- field,
10762
- isEdited: isEdited,
10763
- onChange
10764
- });
10765
- }
10766
- if (type === 'number') {
10767
- entries.push({
10768
- id: 'min',
10769
- component: Min,
10770
- getValue,
10771
- field,
10772
- isEdited: isEdited$6,
10773
- onChange
10774
- }, {
10775
- id: 'max',
10776
- component: Max,
10777
- getValue,
10778
- field,
10779
- isEdited: isEdited$6,
10780
- onChange
10781
- });
10782
- }
11076
+ entries.push({
11077
+ id: 'validationType',
11078
+ component: ValidationType,
11079
+ getValue,
11080
+ field,
11081
+ editField,
11082
+ isEdited: isEdited,
11083
+ onChange,
11084
+ isDefaultVisible: field => field.type === 'textfield'
11085
+ });
11086
+ entries.push({
11087
+ id: 'minLength',
11088
+ component: MinLength,
11089
+ getValue,
11090
+ field,
11091
+ isEdited: isEdited$6,
11092
+ onChange,
11093
+ isDefaultVisible: field => INPUTS.includes(field.type) && (type === 'textarea' || type === 'textfield' && isCustomValidation)
11094
+ }, {
11095
+ id: 'maxLength',
11096
+ component: MaxLength,
11097
+ getValue,
11098
+ field,
11099
+ isEdited: isEdited$6,
11100
+ onChange,
11101
+ isDefaultVisible: field => INPUTS.includes(field.type) && (type === 'textarea' || type === 'textfield' && isCustomValidation)
11102
+ });
11103
+ entries.push({
11104
+ id: 'pattern',
11105
+ component: Pattern,
11106
+ getValue,
11107
+ field,
11108
+ isEdited: isEdited,
11109
+ onChange,
11110
+ isDefaultVisible: field => INPUTS.includes(field.type) && type === 'textfield' && isCustomValidation
11111
+ });
11112
+ entries.push({
11113
+ id: 'min',
11114
+ component: Min,
11115
+ getValue,
11116
+ field,
11117
+ isEdited: isEdited$6,
11118
+ onChange,
11119
+ isDefaultVisible: field => field.type === 'number'
11120
+ }, {
11121
+ id: 'max',
11122
+ component: Max,
11123
+ getValue,
11124
+ field,
11125
+ isEdited: isEdited$6,
11126
+ onChange,
11127
+ isDefaultVisible: field => field.type === 'number'
11128
+ });
10783
11129
  return {
10784
11130
  id: 'validation',
10785
11131
  label: 'Validation',
@@ -10933,12 +11279,14 @@ function ValidationType(props) {
10933
11279
  });
10934
11280
  }
10935
11281
 
10936
- function ValuesGroups(field, editField) {
11282
+ function ValuesGroups(field, editField, getService) {
10937
11283
  const {
10938
11284
  type,
10939
11285
  id: fieldId
10940
11286
  } = field;
10941
- if (!VALUES_INPUTS.includes(type)) {
11287
+ const formFields = getService('formFields');
11288
+ const fieldDefinition = formFields.get(type).config;
11289
+ if (!VALUES_INPUTS.includes(type) && !hasValuesGroupsConfigured(fieldDefinition)) {
10942
11290
  return [];
10943
11291
  }
10944
11292
  const context = {
@@ -10947,8 +11295,8 @@ function ValuesGroups(field, editField) {
10947
11295
  };
10948
11296
  const valuesSourceId = `${fieldId}-valuesSource`;
10949
11297
 
10950
- /**
10951
- * @type {Array<Group|ListGroup>}
11298
+ /**
11299
+ * @type {Array<Group|ListGroup>}
10952
11300
  */
10953
11301
  const groups = [{
10954
11302
  id: valuesSourceId,
@@ -11069,13 +11417,13 @@ function CustomPropertiesGroup(field, editField) {
11069
11417
 
11070
11418
  // helpers //////////
11071
11419
 
11072
- /**
11073
- * Returns copy of object without key.
11074
- *
11075
- * @param {Object} properties
11076
- * @param {string} oldKey
11077
- *
11078
- * @returns {Object}
11420
+ /**
11421
+ * Returns copy of object without key.
11422
+ *
11423
+ * @param {Object} properties
11424
+ * @param {string} oldKey
11425
+ *
11426
+ * @returns {Object}
11079
11427
  */
11080
11428
  function removeKey(properties, oldKey) {
11081
11429
  return Object.entries(properties).reduce((newProperties, entry) => {
@@ -11144,158 +11492,64 @@ function ConditionGroup(field, editField) {
11144
11492
  };
11145
11493
  }
11146
11494
 
11147
- function getGroups(field, editField, getService) {
11148
- if (!field) {
11149
- return [];
11150
- }
11151
- 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)];
11152
-
11153
- // contract: if a group returns null, it should not be displayed at all
11154
- return groups.filter(group => group !== null);
11155
- }
11156
- function FormPropertiesPanel(props) {
11157
- const {
11158
- eventBus,
11159
- injector
11160
- } = props;
11161
- const formEditor = injector.get('formEditor');
11162
- const modeling = injector.get('modeling');
11163
- const selectionModule = injector.get('selection');
11164
- const propertiesPanelConfig = injector.get('config.propertiesPanel') || {};
11165
- const {
11166
- feelPopupContainer
11167
- } = propertiesPanelConfig;
11168
- const [state, setState] = hooks.useState({
11169
- selectedFormField: selectionModule.get() || formEditor._getState().schema
11170
- });
11171
- const selectedFormField = state.selectedFormField;
11172
- const refresh = hooks.useCallback(field => {
11173
- // TODO(skaiir): rework state management, re-rendering the whole properties panel is not the way to go
11174
- // https://github.com/bpmn-io/form-js/issues/686
11175
- setState({
11176
- selectedFormField: selectionModule.get() || formEditor._getState().schema
11177
- });
11178
-
11179
- // notify interested parties on property panel updates
11180
- eventBus.fire('propertiesPanel.updated', {
11181
- formField: field
11182
- });
11183
- }, [eventBus, formEditor, selectionModule]);
11184
- hooks.useLayoutEffect(() => {
11185
- /**
11186
- * TODO(pinussilvestrus): update with actual updated element,
11187
- * once we have a proper updater/change support
11188
- */
11189
- eventBus.on('changed', refresh);
11190
- eventBus.on('import.done', refresh);
11191
- eventBus.on('selection.changed', refresh);
11192
- return () => {
11193
- eventBus.off('changed', refresh);
11194
- eventBus.off('import.done', refresh);
11195
- eventBus.off('selection.changed', refresh);
11196
- };
11197
- }, [eventBus, refresh]);
11198
- const getService = (type, strict = true) => injector.get(type, strict);
11199
- const propertiesPanelContext = {
11200
- getService
11201
- };
11202
- const onFocus = () => eventBus.fire('propertiesPanel.focusin');
11203
- const onBlur = () => eventBus.fire('propertiesPanel.focusout');
11204
- const editField = hooks.useCallback((formField, key, value) => modeling.editFormField(formField, key, value), [modeling]);
11205
- return jsxRuntime.jsx("div", {
11206
- class: "fjs-properties-panel",
11207
- "data-field": selectedFormField && selectedFormField.id,
11208
- onFocusCapture: onFocus,
11209
- onBlurCapture: onBlur,
11210
- children: jsxRuntime.jsx(FormPropertiesPanelContext.Provider, {
11211
- value: propertiesPanelContext,
11212
- children: jsxRuntime.jsx(PropertiesPanel, {
11213
- element: selectedFormField,
11214
- eventBus: eventBus,
11215
- groups: getGroups(selectedFormField, editField, getService),
11216
- headerProvider: PropertiesPanelHeaderProvider,
11217
- placeholderProvider: PropertiesPanelPlaceholderProvider,
11218
- feelPopupContainer: feelPopupContainer
11219
- })
11220
- })
11221
- });
11222
- }
11223
-
11224
- class PropertiesPanelRenderer {
11225
- constructor(propertiesPanelConfig, injector, eventBus) {
11226
- const {
11227
- parent
11228
- } = propertiesPanelConfig || {};
11229
- this._eventBus = eventBus;
11495
+ class PropertiesProvider {
11496
+ constructor(propertiesPanel, injector) {
11230
11497
  this._injector = injector;
11231
- this._container = minDom.domify('<div class="fjs-properties-container" input-handle-modified-keys="y,z"></div>');
11232
- if (parent) {
11233
- this.attachTo(parent);
11234
- }
11235
- this._eventBus.once('formEditor.rendered', 500, () => {
11236
- this._render();
11237
- });
11498
+ propertiesPanel.registerProvider(this);
11238
11499
  }
11239
-
11240
- /**
11241
- * Attach the properties panel to a parent node.
11242
- *
11243
- * @param {HTMLElement} container
11244
- */
11245
- attachTo(container) {
11246
- if (!container) {
11247
- throw new Error('container required');
11248
- }
11249
- if (typeof container === 'string') {
11250
- container = minDom.query(container);
11251
- }
11252
-
11253
- // (1) detach from old parent
11254
- this.detach();
11255
-
11256
- // (2) append to parent container
11257
- container.appendChild(this._container);
11258
-
11259
- // (3) notify interested parties
11260
- this._eventBus.fire('propertiesPanel.attach');
11500
+ _filterVisibleEntries(groups, field, getService) {
11501
+ return groups.forEach(group => {
11502
+ const {
11503
+ entries
11504
+ } = group;
11505
+ const {
11506
+ type
11507
+ } = field;
11508
+ const formFields = getService('formFields');
11509
+ const fieldDefinition = formFields.get(type).config;
11510
+ if (!entries) {
11511
+ return;
11512
+ }
11513
+ group.entries = entries.filter(entry => {
11514
+ const {
11515
+ isDefaultVisible
11516
+ } = entry;
11517
+ if (!isDefaultVisible) {
11518
+ return true;
11519
+ }
11520
+ return isDefaultVisible(field) || hasEntryConfigured(fieldDefinition, entry.id);
11521
+ });
11522
+ });
11261
11523
  }
11524
+ getGroups(field, editField) {
11525
+ return groups => {
11526
+ if (!field) {
11527
+ return groups;
11528
+ }
11529
+ const getService = (type, strict = true) => this._injector.get(type, strict);
11530
+ 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);
11531
+ this._filterVisibleEntries(groups, field, getService);
11262
11532
 
11263
- /**
11264
- * Detach the properties panel from its parent node.
11265
- */
11266
- detach() {
11267
- const parentNode = this._container.parentNode;
11268
- if (parentNode) {
11269
- parentNode.removeChild(this._container);
11270
- this._eventBus.fire('propertiesPanel.detach');
11271
- }
11272
- }
11273
- _render() {
11274
- preact.render(jsxRuntime.jsx(FormPropertiesPanel, {
11275
- eventBus: this._eventBus,
11276
- injector: this._injector
11277
- }), this._container);
11278
- this._eventBus.fire('propertiesPanel.rendered');
11279
- }
11280
- _destroy() {
11281
- if (this._container) {
11282
- preact.render(null, this._container);
11283
- this._eventBus.fire('propertiesPanel.destroyed');
11284
- }
11533
+ // contract: if a group has no entries or items, it should not be displayed at all
11534
+ return groups.filter(group => {
11535
+ return group.items || group.entries && group.entries.length;
11536
+ });
11537
+ };
11285
11538
  }
11286
11539
  }
11287
- PropertiesPanelRenderer.$inject = ['config.propertiesPanel', 'injector', 'eventBus'];
11540
+ PropertiesProvider.$inject = ['propertiesPanel', 'injector'];
11288
11541
 
11289
11542
  var PropertiesPanelModule = {
11290
11543
  __depends__: [index],
11291
- __init__: ['propertiesPanel'],
11292
- propertiesPanel: ['type', PropertiesPanelRenderer]
11544
+ __init__: ['propertiesPanel', 'propertiesProvider'],
11545
+ propertiesPanel: ['type', PropertiesPanelRenderer],
11546
+ propertiesProvider: ['type', PropertiesProvider]
11293
11547
  };
11294
11548
 
11295
- /**
11296
- * Manages the rendering of visual plugins.
11297
- * @constructor
11298
- * @param {Object} eventBus - Event bus for the application.
11549
+ /**
11550
+ * Manages the rendering of visual plugins.
11551
+ * @constructor
11552
+ * @param {Object} eventBus - Event bus for the application.
11299
11553
  */
11300
11554
  class RenderInjector extends SectionModuleBase {
11301
11555
  constructor(eventBus) {
@@ -11304,10 +11558,10 @@ class RenderInjector extends SectionModuleBase {
11304
11558
  this.registeredRenderers = [];
11305
11559
  }
11306
11560
 
11307
- /**
11308
- * Inject a new renderer into the injector.
11309
- * @param {string} identifier - Identifier for the renderer.
11310
- * @param {Function} Renderer - The renderer function.
11561
+ /**
11562
+ * Inject a new renderer into the injector.
11563
+ * @param {string} identifier - Identifier for the renderer.
11564
+ * @param {Function} Renderer - The renderer function.
11311
11565
  */
11312
11566
  attachRenderer(identifier, Renderer) {
11313
11567
  this.registeredRenderers = [...this.registeredRenderers, {
@@ -11316,17 +11570,17 @@ class RenderInjector extends SectionModuleBase {
11316
11570
  }];
11317
11571
  }
11318
11572
 
11319
- /**
11320
- * Detach a renderer from the by key injector.
11321
- * @param {string} identifier - Identifier for the renderer.
11573
+ /**
11574
+ * Detach a renderer from the by key injector.
11575
+ * @param {string} identifier - Identifier for the renderer.
11322
11576
  */
11323
11577
  detachRenderer(identifier) {
11324
11578
  this.registeredRenderers = this.registeredRenderers.filter(r => r.identifier !== identifier);
11325
11579
  }
11326
11580
 
11327
- /**
11328
- * Returns the registered renderers.
11329
- * @returns {Array} Array of registered renderers.
11581
+ /**
11582
+ * Returns the registered renderers.
11583
+ * @returns {Array} Array of registered renderers.
11330
11584
  */
11331
11585
  fetchRenderers() {
11332
11586
  return this.registeredRenderers;
@@ -11360,48 +11614,48 @@ var ExpressionLanguageModule = {
11360
11614
 
11361
11615
  const ids = new Ids([32, 36, 1]);
11362
11616
 
11363
- /**
11364
- * @typedef { import('./types').Injector } Injector
11365
- * @typedef { import('./types').Module } Module
11366
- * @typedef { import('./types').Schema } Schema
11367
- *
11368
- * @typedef { import('./types').FormEditorOptions } FormEditorOptions
11369
- * @typedef { import('./types').FormEditorProperties } FormEditorProperties
11370
- *
11371
- * @typedef { {
11372
- * properties: FormEditorProperties,
11373
- * schema: Schema
11374
- * } } State
11375
- *
11376
- * @typedef { (type:string, priority:number, handler:Function) => void } OnEventWithPriority
11377
- * @typedef { (type:string, handler:Function) => void } OnEventWithOutPriority
11378
- * @typedef { OnEventWithPriority & OnEventWithOutPriority } OnEventType
11617
+ /**
11618
+ * @typedef { import('./types').Injector } Injector
11619
+ * @typedef { import('./types').Module } Module
11620
+ * @typedef { import('./types').Schema } Schema
11621
+ *
11622
+ * @typedef { import('./types').FormEditorOptions } FormEditorOptions
11623
+ * @typedef { import('./types').FormEditorProperties } FormEditorProperties
11624
+ *
11625
+ * @typedef { {
11626
+ * properties: FormEditorProperties,
11627
+ * schema: Schema
11628
+ * } } State
11629
+ *
11630
+ * @typedef { (type:string, priority:number, handler:Function) => void } OnEventWithPriority
11631
+ * @typedef { (type:string, handler:Function) => void } OnEventWithOutPriority
11632
+ * @typedef { OnEventWithPriority & OnEventWithOutPriority } OnEventType
11379
11633
  */
11380
11634
 
11381
- /**
11382
- * The form editor.
11635
+ /**
11636
+ * The form editor.
11383
11637
  */
11384
11638
  class FormEditor {
11385
- /**
11386
- * @constructor
11387
- * @param {FormEditorOptions} options
11639
+ /**
11640
+ * @constructor
11641
+ * @param {FormEditorOptions} options
11388
11642
  */
11389
11643
  constructor(options = {}) {
11390
- /**
11391
- * @public
11392
- * @type {OnEventType}
11644
+ /**
11645
+ * @public
11646
+ * @type {OnEventType}
11393
11647
  */
11394
11648
  this.on = this._onEvent;
11395
11649
 
11396
- /**
11397
- * @public
11398
- * @type {String}
11650
+ /**
11651
+ * @public
11652
+ * @type {String}
11399
11653
  */
11400
11654
  this._id = ids.next();
11401
11655
 
11402
- /**
11403
- * @private
11404
- * @type {Element}
11656
+ /**
11657
+ * @private
11658
+ * @type {Element}
11405
11659
  */
11406
11660
  this._container = formJsViewer.createFormContainer();
11407
11661
  this._container.setAttribute('input-handle-modified-keys', 'z,y');
@@ -11412,15 +11666,15 @@ class FormEditor {
11412
11666
  properties = {}
11413
11667
  } = options;
11414
11668
 
11415
- /**
11416
- * @private
11417
- * @type {any}
11669
+ /**
11670
+ * @private
11671
+ * @type {any}
11418
11672
  */
11419
11673
  this.exporter = exporter;
11420
11674
 
11421
- /**
11422
- * @private
11423
- * @type {State}
11675
+ /**
11676
+ * @private
11677
+ * @type {State}
11424
11678
  */
11425
11679
  this._state = {
11426
11680
  properties,
@@ -11449,10 +11703,10 @@ class FormEditor {
11449
11703
  this._detach(false);
11450
11704
  }
11451
11705
 
11452
- /**
11453
- * @param {Schema} schema
11454
- *
11455
- * @return {Promise<{ warnings: Array<any> }>}
11706
+ /**
11707
+ * @param {Schema} schema
11708
+ *
11709
+ * @return {Promise<{ warnings: Array<any> }>}
11456
11710
  */
11457
11711
  importSchema(schema) {
11458
11712
  return new Promise((resolve, reject) => {
@@ -11481,15 +11735,15 @@ class FormEditor {
11481
11735
  });
11482
11736
  }
11483
11737
 
11484
- /**
11485
- * @returns {Schema}
11738
+ /**
11739
+ * @returns {Schema}
11486
11740
  */
11487
11741
  saveSchema() {
11488
11742
  return this.getSchema();
11489
11743
  }
11490
11744
 
11491
- /**
11492
- * @returns {Schema}
11745
+ /**
11746
+ * @returns {Schema}
11493
11747
  */
11494
11748
  getSchema() {
11495
11749
  const {
@@ -11498,8 +11752,8 @@ class FormEditor {
11498
11752
  return exportSchema(schema, this.exporter, formJsViewer.schemaVersion);
11499
11753
  }
11500
11754
 
11501
- /**
11502
- * @param {Element|string} parentNode
11755
+ /**
11756
+ * @param {Element|string} parentNode
11503
11757
  */
11504
11758
  attachTo(parentNode) {
11505
11759
  if (!parentNode) {
@@ -11517,10 +11771,10 @@ class FormEditor {
11517
11771
  this._detach();
11518
11772
  }
11519
11773
 
11520
- /**
11521
- * @internal
11522
- *
11523
- * @param {boolean} [emit]
11774
+ /**
11775
+ * @internal
11776
+ *
11777
+ * @param {boolean} [emit]
11524
11778
  */
11525
11779
  _detach(emit = true) {
11526
11780
  const container = this._container,
@@ -11534,9 +11788,9 @@ class FormEditor {
11534
11788
  parentNode.removeChild(container);
11535
11789
  }
11536
11790
 
11537
- /**
11538
- * @param {any} property
11539
- * @param {any} value
11791
+ /**
11792
+ * @param {any} property
11793
+ * @param {any} value
11540
11794
  */
11541
11795
  setProperty(property, value) {
11542
11796
  const properties = minDash.set(this._getState().properties, [property], value);
@@ -11545,21 +11799,21 @@ class FormEditor {
11545
11799
  });
11546
11800
  }
11547
11801
 
11548
- /**
11549
- * @param {string} type
11550
- * @param {Function} handler
11802
+ /**
11803
+ * @param {string} type
11804
+ * @param {Function} handler
11551
11805
  */
11552
11806
  off(type, handler) {
11553
11807
  this.get('eventBus').off(type, handler);
11554
11808
  }
11555
11809
 
11556
- /**
11557
- * @internal
11558
- *
11559
- * @param {FormEditorOptions} options
11560
- * @param {Element} container
11561
- *
11562
- * @returns {Injector}
11810
+ /**
11811
+ * @internal
11812
+ *
11813
+ * @param {FormEditorOptions} options
11814
+ * @param {Element} container
11815
+ *
11816
+ * @returns {Injector}
11563
11817
  */
11564
11818
  _createInjector(options, container) {
11565
11819
  const {
@@ -11581,22 +11835,22 @@ class FormEditor {
11581
11835
  }, core, ...modules, ...additionalModules]);
11582
11836
  }
11583
11837
 
11584
- /**
11585
- * @internal
11838
+ /**
11839
+ * @internal
11586
11840
  */
11587
11841
  _emit(type, data) {
11588
11842
  this.get('eventBus').fire(type, data);
11589
11843
  }
11590
11844
 
11591
- /**
11592
- * @internal
11845
+ /**
11846
+ * @internal
11593
11847
  */
11594
11848
  _getState() {
11595
11849
  return this._state;
11596
11850
  }
11597
11851
 
11598
- /**
11599
- * @internal
11852
+ /**
11853
+ * @internal
11600
11854
  */
11601
11855
  _setState(state) {
11602
11856
  this._state = {
@@ -11606,15 +11860,15 @@ class FormEditor {
11606
11860
  this._emit('changed', this._getState());
11607
11861
  }
11608
11862
 
11609
- /**
11610
- * @internal
11863
+ /**
11864
+ * @internal
11611
11865
  */
11612
11866
  _getModules() {
11613
11867
  return [ModelingModule, EditorActionsModule, DraggingModule, KeyboardModule, SelectionModule, PaletteModule, ExpressionLanguageModule, formJsViewer.MarkdownModule, PropertiesPanelModule, RenderInjectionModule];
11614
11868
  }
11615
11869
 
11616
- /**
11617
- * @internal
11870
+ /**
11871
+ * @internal
11618
11872
  */
11619
11873
  _onEvent(type, priority, handler) {
11620
11874
  this.get('eventBus').on(type, priority, handler);
@@ -11668,4 +11922,9 @@ Object.defineProperty(exports, 'schemaVersion', {
11668
11922
  });
11669
11923
  exports.FormEditor = FormEditor;
11670
11924
  exports.createFormEditor = createFormEditor;
11925
+ exports.useDebounce = useDebounce;
11926
+ exports.usePrevious = usePrevious$1;
11927
+ exports.usePropertiesPanelService = useService;
11928
+ exports.useService = useService$1;
11929
+ exports.useVariables = useVariables;
11671
11930
  //# sourceMappingURL=index.cjs.map