@bpmn-io/form-js-editor 1.3.0-alpha.0 → 1.3.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.
package/dist/index.cjs CHANGED
@@ -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({
@@ -1124,23 +1124,23 @@ var Slot = (props => {
1124
1124
  return fillsAndSeparators;
1125
1125
  });
1126
1126
 
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
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
1132
1132
  */
1133
1133
  const FillFragment = fill => jsxRuntime.jsx(preact.Fragment, {
1134
1134
  children: fill.children
1135
1135
  }, fill.id);
1136
1136
 
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
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
1144
1144
  */
1145
1145
  const buildFills = (groups, fillRenderer, separatorRenderer) => {
1146
1146
  const result = [];
@@ -1158,8 +1158,8 @@ const buildFills = (groups, fillRenderer, separatorRenderer) => {
1158
1158
  return result;
1159
1159
  };
1160
1160
 
1161
- /**
1162
- * Groups fills by group name property.
1161
+ /**
1162
+ * Groups fills by group name property.
1163
1163
  */
1164
1164
  const _groupByGroupName = fills => {
1165
1165
  const groups = [];
@@ -1179,8 +1179,8 @@ const _groupByGroupName = fills => {
1179
1179
  return Object.keys(groupsById).sort().map(id => groupsById[id]);
1180
1180
  };
1181
1181
 
1182
- /**
1183
- * Compares fills by priority.
1182
+ /**
1183
+ * Compares fills by priority.
1184
1184
  */
1185
1185
  const _comparePriority = (a, b) => {
1186
1186
  return (b.priority || 0) - (a.priority || 0);
@@ -1408,20 +1408,20 @@ const DRAG_NO_DROP_CLS = 'fjs-no-drop';
1408
1408
  const DRAG_NO_MOVE_CLS = 'fjs-no-move';
1409
1409
  const ERROR_DROP_CLS = 'fjs-error-drop';
1410
1410
 
1411
- /**
1412
- * @typedef { { id: String, components: Array<any> } } FormRow
1411
+ /**
1412
+ * @typedef { { id: String, components: Array<any> } } FormRow
1413
1413
  */
1414
1414
 
1415
1415
  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
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
1425
1425
  */
1426
1426
  constructor(formFieldRegistry, formLayouter, formLayoutValidator, eventBus, modeling, pathRegistry) {
1427
1427
  this._formFieldRegistry = formFieldRegistry;
@@ -1432,13 +1432,13 @@ class Dragging {
1432
1432
  this._pathRegistry = pathRegistry;
1433
1433
  }
1434
1434
 
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 }
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 }
1442
1442
  */
1443
1443
  getTargetIndex(targetRow, targetFormField, sibling) {
1444
1444
  /** @type HTMLElement */
@@ -1579,8 +1579,8 @@ class Dragging {
1579
1579
  }
1580
1580
  }
1581
1581
 
1582
- /**
1583
- * @param { { container: Array<string>, direction: string, mirrorContainer: string } } options
1582
+ /**
1583
+ * @param { { container: Array<string>, direction: string, mirrorContainer: string } } options
1584
1584
  */
1585
1585
  createDragulaInstance(options) {
1586
1586
  const {
@@ -1994,7 +1994,7 @@ function DebugColumns(props) {
1994
1994
  return null;
1995
1995
  }
1996
1996
  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;",
1997
+ 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
1998
  class: "fjs-debug-columns",
1999
1999
  children: (field.layout || {}).columns || 'auto'
2000
2000
  });
@@ -3065,10 +3065,10 @@ function updateRow(formField, rowId) {
3065
3065
  }
3066
3066
 
3067
3067
  class AddFormFieldHandler {
3068
- /**
3069
- * @constructor
3070
- * @param { import('../../../FormEditor').default } formEditor
3071
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3068
+ /**
3069
+ * @constructor
3070
+ * @param { import('../../../FormEditor').default } formEditor
3071
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3072
3072
  */
3073
3073
  constructor(formEditor, formFieldRegistry) {
3074
3074
  this._formEditor = formEditor;
@@ -3129,10 +3129,10 @@ class AddFormFieldHandler {
3129
3129
  AddFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3130
3130
 
3131
3131
  class EditFormFieldHandler {
3132
- /**
3133
- * @constructor
3134
- * @param { import('../../../FormEditor').default } formEditor
3135
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3132
+ /**
3133
+ * @constructor
3134
+ * @param { import('../../../FormEditor').default } formEditor
3135
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3136
3136
  */
3137
3137
  constructor(formEditor, formFieldRegistry) {
3138
3138
  this._formEditor = formEditor;
@@ -3195,11 +3195,11 @@ class EditFormFieldHandler {
3195
3195
  EditFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3196
3196
 
3197
3197
  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
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
3203
3203
  */
3204
3204
  constructor(formEditor, formFieldRegistry, pathRegistry) {
3205
3205
  this._formEditor = formEditor;
@@ -3304,10 +3304,10 @@ class MoveFormFieldHandler {
3304
3304
  MoveFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry', 'pathRegistry'];
3305
3305
 
3306
3306
  class RemoveFormFieldHandler {
3307
- /**
3308
- * @constructor
3309
- * @param { import('../../../FormEditor').default } formEditor
3310
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3307
+ /**
3308
+ * @constructor
3309
+ * @param { import('../../../FormEditor').default } formEditor
3310
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3311
3311
  */
3312
3312
  constructor(formEditor, formFieldRegistry) {
3313
3313
  this._formEditor = formEditor;
@@ -3367,9 +3367,9 @@ class RemoveFormFieldHandler {
3367
3367
  RemoveFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3368
3368
 
3369
3369
  class UpdateIdClaimHandler {
3370
- /**
3371
- * @constructor
3372
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3370
+ /**
3371
+ * @constructor
3372
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3373
3373
  */
3374
3374
  constructor(formFieldRegistry) {
3375
3375
  this._formFieldRegistry = formFieldRegistry;
@@ -3402,9 +3402,9 @@ class UpdateIdClaimHandler {
3402
3402
  UpdateIdClaimHandler.$inject = ['formFieldRegistry'];
3403
3403
 
3404
3404
  class UpdateKeyClaimHandler {
3405
- /**
3406
- * @constructor
3407
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3405
+ /**
3406
+ * @constructor
3407
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3408
3408
  */
3409
3409
  constructor(pathRegistry) {
3410
3410
  this._pathRegistry = pathRegistry;
@@ -3445,9 +3445,9 @@ class UpdateKeyClaimHandler {
3445
3445
  UpdateKeyClaimHandler.$inject = ['pathRegistry'];
3446
3446
 
3447
3447
  class UpdatePathClaimHandler {
3448
- /**
3449
- * @constructor
3450
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3448
+ /**
3449
+ * @constructor
3450
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3451
3451
  */
3452
3452
  constructor(pathRegistry) {
3453
3453
  this._pathRegistry = pathRegistry;
@@ -4009,8 +4009,8 @@ class ValidateBehavior extends CommandInterceptor {
4009
4009
  constructor(eventBus) {
4010
4010
  super(eventBus);
4011
4011
 
4012
- /**
4013
- * Remove custom validation if <validationType> is about to be added.
4012
+ /**
4013
+ * Remove custom validation if <validationType> is about to be added.
4014
4014
  */
4015
4015
  this.preExecute('formField.edit', function (context) {
4016
4016
  const {
@@ -4590,22 +4590,22 @@ var SelectionModule = {
4590
4590
  selectionBehavior: ['type', SelectionBehavior]
4591
4591
  };
4592
4592
 
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
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
4600
4600
  */
4601
4601
  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
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
4609
4609
  */
4610
4610
  constructor(eventBus, sectionKey) {
4611
4611
  this._eventBus = eventBus;
@@ -4618,10 +4618,10 @@ class SectionModuleBase {
4618
4618
  });
4619
4619
  }
4620
4620
 
4621
- /**
4622
- * Attach the managed section to a parent node.
4623
- *
4624
- * @param {HTMLElement} container - The parent node to attach to.
4621
+ /**
4622
+ * Attach the managed section to a parent node.
4623
+ *
4624
+ * @param {HTMLElement} container - The parent node to attach to.
4625
4625
  */
4626
4626
  attachTo(container) {
4627
4627
  this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.attach`, {
@@ -4629,22 +4629,22 @@ class SectionModuleBase {
4629
4629
  }));
4630
4630
  }
4631
4631
 
4632
- /**
4633
- * Detach the managed section from its parent node.
4632
+ /**
4633
+ * Detach the managed section from its parent node.
4634
4634
  */
4635
4635
  detach() {
4636
4636
  this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.detach`));
4637
4637
  }
4638
4638
 
4639
- /**
4640
- * Reset the managed section to its initial state.
4639
+ /**
4640
+ * Reset the managed section to its initial state.
4641
4641
  */
4642
4642
  reset() {
4643
4643
  this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.reset`));
4644
4644
  }
4645
4645
 
4646
- /**
4647
- * Circumvents timing issues.
4646
+ /**
4647
+ * Circumvents timing issues.
4648
4648
  */
4649
4649
  _onceSectionRendered(callback) {
4650
4650
  if (this.isSectionRendered) {
@@ -4895,18 +4895,25 @@ function Tooltip(props) {
4895
4895
  parent
4896
4896
  } = props;
4897
4897
  const [visible, setShow] = hooks.useState(false);
4898
+ const [focusedViaKeyboard, setFocusedViaKeyboard] = hooks.useState(false);
4898
4899
  let timeout = null;
4899
4900
  const wrapperRef = hooks.useRef(null);
4900
4901
  const tooltipRef = hooks.useRef(null);
4901
4902
  const showTooltip = async event => {
4902
- const show = () => !visible && setShow(true);
4903
- if (event instanceof MouseEvent) {
4904
- timeout = setTimeout(show, 200);
4905
- } else {
4906
- show();
4903
+ const show = () => setShow(true);
4904
+ if (!visible && !timeout) {
4905
+ if (event instanceof MouseEvent) {
4906
+ timeout = setTimeout(show, 200);
4907
+ } else {
4908
+ show();
4909
+ setFocusedViaKeyboard(true);
4910
+ }
4907
4911
  }
4908
4912
  };
4909
- const hideTooltip = () => setShow(false);
4913
+ const hideTooltip = () => {
4914
+ setShow(false);
4915
+ setFocusedViaKeyboard(false);
4916
+ };
4910
4917
  const hideTooltipViaEscape = e => {
4911
4918
  e.code === 'Escape' && hideTooltip();
4912
4919
  };
@@ -4930,7 +4937,7 @@ function Tooltip(props) {
4930
4937
  if (visible && !isTooltipHovered({
4931
4938
  x: e.x,
4932
4939
  y: e.y
4933
- }) && !isFocused) {
4940
+ }) && !(isFocused && focusedViaKeyboard)) {
4934
4941
  hideTooltip();
4935
4942
  }
4936
4943
  };
@@ -4938,8 +4945,8 @@ function Tooltip(props) {
4938
4945
  const {
4939
4946
  relatedTarget
4940
4947
  } = e;
4941
- const isTooltipChild = el => el && !!el.closest('.bio-properties-panel-tooltip');
4942
- if (visible && !isHovered(wrapperRef.current) && !isTooltipChild(relatedTarget)) {
4948
+ const isTooltipChild = el => !!el.closest('.bio-properties-panel-tooltip');
4949
+ if (visible && !isHovered(wrapperRef.current) && relatedTarget && !isTooltipChild(relatedTarget)) {
4943
4950
  hideTooltip();
4944
4951
  }
4945
4952
  };
@@ -4951,7 +4958,7 @@ function Tooltip(props) {
4951
4958
  document.removeEventListener('mousemove', hideHoveredTooltip);
4952
4959
  document.removeEventListener('focusout', hideFocusedTooltip);
4953
4960
  };
4954
- }, [wrapperRef.current, visible]);
4961
+ }, [wrapperRef.current, visible, focusedViaKeyboard]);
4955
4962
  const renderTooltip = () => {
4956
4963
  return jsxRuntime.jsxs("div", {
4957
4964
  class: "bio-properties-panel-tooltip",
@@ -4974,12 +4981,12 @@ function Tooltip(props) {
4974
4981
  tabIndex: "0",
4975
4982
  ref: wrapperRef,
4976
4983
  onMouseEnter: showTooltip,
4977
- onMouseLeave: () => clearTimeout(timeout),
4984
+ onMouseLeave: () => {
4985
+ clearTimeout(timeout);
4986
+ timeout = null;
4987
+ },
4978
4988
  onFocus: showTooltip,
4979
4989
  onKeyDown: hideTooltipViaEscape,
4980
- onMouseDown: e => {
4981
- e.preventDefault();
4982
- },
4983
4990
  children: [props.children, visible ? parent ? React.createPortal(renderTooltip(), parent.current) : renderTooltip() : null]
4984
4991
  });
4985
4992
  }
@@ -5256,20 +5263,24 @@ function Group(props) {
5256
5263
 
5257
5264
  // set edited state depending on all entries
5258
5265
  hooks.useEffect(() => {
5259
- const hasOneEditedEntry = entries.find(entry => {
5260
- const {
5261
- id,
5262
- isEdited
5263
- } = entry;
5264
- const entryNode = minDom.query(`[data-entry-id="${id}"]`);
5265
- if (!minDash.isFunction(isEdited) || !entryNode) {
5266
- return false;
5267
- }
5268
- const inputNode = minDom.query('.bio-properties-panel-input', entryNode);
5269
- return isEdited(inputNode);
5266
+ // TODO(@barmac): replace with CSS when `:has()` is supported in all major browsers, or rewrite as in https://github.com/camunda/camunda-modeler/issues/3815#issuecomment-1733038161
5267
+ const scheduled = requestAnimationFrame(() => {
5268
+ const hasOneEditedEntry = entries.find(entry => {
5269
+ const {
5270
+ id,
5271
+ isEdited
5272
+ } = entry;
5273
+ const entryNode = minDom.query(`[data-entry-id="${id}"]`);
5274
+ if (!minDash.isFunction(isEdited) || !entryNode) {
5275
+ return false;
5276
+ }
5277
+ const inputNode = minDom.query('.bio-properties-panel-input', entryNode);
5278
+ return isEdited(inputNode);
5279
+ });
5280
+ setEdited(hasOneEditedEntry);
5270
5281
  });
5271
- setEdited(hasOneEditedEntry);
5272
- }, [entries]);
5282
+ return () => cancelAnimationFrame(scheduled);
5283
+ }, [entries, setEdited]);
5273
5284
 
5274
5285
  // set error state depending on all entries
5275
5286
  const allErrors = useErrors();
@@ -5289,7 +5300,8 @@ function Group(props) {
5289
5300
  class: classnames('bio-properties-panel-group-header', edited ? '' : 'empty', open ? 'open' : '', sticky && open ? 'sticky' : ''),
5290
5301
  onClick: toggleOpen,
5291
5302
  children: [jsxRuntime.jsx("div", {
5292
- title: label,
5303
+ title: props.tooltip ? null : label,
5304
+ "data-title": label,
5293
5305
  class: "bio-properties-panel-group-header-title",
5294
5306
  children: jsxRuntime.jsx(TooltipWrapper, {
5295
5307
  value: props.tooltip,
@@ -5696,7 +5708,11 @@ function createDragger(fn, dragPreview) {
5696
5708
  // (2) setup drag listeners
5697
5709
 
5698
5710
  // attach drag + cleanup event
5699
- document.addEventListener('dragover', onDrag);
5711
+ // we need to do this to make sure we track cursor
5712
+ // movements before we reach other drag event handlers,
5713
+ // e.g. in child containers.
5714
+ document.addEventListener('dragover', onDrag, true);
5715
+ document.addEventListener('dragenter', preventDefault, true);
5700
5716
  document.addEventListener('dragend', onEnd);
5701
5717
  document.addEventListener('drop', preventDefault);
5702
5718
  }
@@ -5710,7 +5726,8 @@ function createDragger(fn, dragPreview) {
5710
5726
  return fn.call(self, event, delta);
5711
5727
  }
5712
5728
  function onEnd() {
5713
- document.removeEventListener('dragover', onDrag);
5729
+ document.removeEventListener('dragover', onDrag, true);
5730
+ document.removeEventListener('dragenter', preventDefault, true);
5714
5731
  document.removeEventListener('dragend', onEnd);
5715
5732
  document.removeEventListener('drop', preventDefault);
5716
5733
  }
@@ -5731,6 +5748,7 @@ const noop$3 = () => {};
5731
5748
  * @param {Object} props
5732
5749
  * @param {HTMLElement} [props.container]
5733
5750
  * @param {string} [props.className]
5751
+ * @param {boolean} [props.delayInitialFocus]
5734
5752
  * @param {{x: number, y: number}} [props.position]
5735
5753
  * @param {number} [props.width]
5736
5754
  * @param {number} [props.height]
@@ -5738,12 +5756,15 @@ const noop$3 = () => {};
5738
5756
  * @param {Function} [props.onPostActivate]
5739
5757
  * @param {Function} [props.onPostDeactivate]
5740
5758
  * @param {boolean} [props.returnFocus]
5759
+ * @param {boolean} [props.closeOnEscape]
5741
5760
  * @param {string} props.title
5761
+ * @param {Ref} [ref]
5742
5762
  */
5743
- function Popup(props) {
5763
+ function PopupComponent(props, globalRef) {
5744
5764
  const {
5745
5765
  container,
5746
5766
  className,
5767
+ delayInitialFocus,
5747
5768
  position,
5748
5769
  width,
5749
5770
  height,
@@ -5751,12 +5772,16 @@ function Popup(props) {
5751
5772
  onPostActivate = noop$3,
5752
5773
  onPostDeactivate = noop$3,
5753
5774
  returnFocus = true,
5775
+ closeOnEscape = true,
5754
5776
  title
5755
5777
  } = props;
5756
5778
  const focusTrapRef = hooks.useRef(null);
5757
- const popupRef = hooks.useRef(null);
5758
- const handleKeyPress = event => {
5759
- if (event.key === 'Escape') {
5779
+ const localRef = hooks.useRef(null);
5780
+ const popupRef = globalRef || localRef;
5781
+ const handleKeydown = event => {
5782
+ // do not allow keyboard events to bubble
5783
+ event.stopPropagation();
5784
+ if (closeOnEscape && event.key === 'Escape') {
5760
5785
  onClose();
5761
5786
  }
5762
5787
  };
@@ -5781,14 +5806,6 @@ function Popup(props) {
5781
5806
  if (height) {
5782
5807
  style.height = height + 'px';
5783
5808
  }
5784
- hooks.useEffect(() => {
5785
- if (popupRef.current) {
5786
- popupRef.current.addEventListener('keydown', handleKeyPress);
5787
- }
5788
- return () => {
5789
- popupRef.current.removeEventListener('keydown', handleKeyPress);
5790
- };
5791
- }, [popupRef]);
5792
5809
  hooks.useEffect(() => {
5793
5810
  if (popupRef.current) {
5794
5811
  popupRef.current.addEventListener('focusin', handleFocus);
@@ -5801,6 +5818,7 @@ function Popup(props) {
5801
5818
  if (popupRef.current) {
5802
5819
  focusTrapRef.current = focusTrap__namespace.createFocusTrap(popupRef.current, {
5803
5820
  clickOutsideDeactivates: true,
5821
+ delayInitialFocus,
5804
5822
  fallbackFocus: popupRef.current,
5805
5823
  onPostActivate,
5806
5824
  onPostDeactivate,
@@ -5814,12 +5832,14 @@ function Popup(props) {
5814
5832
  "aria-label": title,
5815
5833
  tabIndex: -1,
5816
5834
  ref: popupRef,
5835
+ onKeyDown: handleKeydown,
5817
5836
  role: "dialog",
5818
5837
  class: classnames('bio-properties-panel-popup', className),
5819
5838
  style: style,
5820
5839
  children: props.children
5821
5840
  }), container || document.body);
5822
5841
  }
5842
+ const Popup = React.forwardRef(PopupComponent);
5823
5843
  Popup.Title = Title;
5824
5844
  Popup.Body = Body;
5825
5845
  Popup.Footer = Footer;
@@ -5828,6 +5848,7 @@ function Title(props) {
5828
5848
  children,
5829
5849
  className,
5830
5850
  draggable,
5851
+ emit = () => {},
5831
5852
  title,
5832
5853
  ...rest
5833
5854
  } = props;
@@ -5840,7 +5861,8 @@ function Title(props) {
5840
5861
  });
5841
5862
  const dragPreviewRef = hooks.useRef();
5842
5863
  const titleRef = hooks.useRef();
5843
- const onMove = minDash.throttle((_, delta) => {
5864
+ const onMove = (event, delta) => {
5865
+ cancel(event);
5844
5866
  const {
5845
5867
  x: dx,
5846
5868
  y: dy
@@ -5852,20 +5874,33 @@ function Title(props) {
5852
5874
  const popupParent = getPopupParent(titleRef.current);
5853
5875
  popupParent.style.top = newPosition.y + 'px';
5854
5876
  popupParent.style.left = newPosition.x + 'px';
5855
- });
5877
+
5878
+ // notify interested parties
5879
+ emit('dragover', {
5880
+ newPosition,
5881
+ delta
5882
+ });
5883
+ };
5856
5884
  const onMoveStart = event => {
5857
5885
  // initialize drag handler
5858
5886
  const onDragStart = createDragger(onMove, dragPreviewRef.current);
5859
5887
  onDragStart(event);
5888
+ event.stopPropagation();
5860
5889
  const popupParent = getPopupParent(titleRef.current);
5861
5890
  const bounds = popupParent.getBoundingClientRect();
5862
5891
  context.current.startPosition = {
5863
5892
  x: bounds.left,
5864
5893
  y: bounds.top
5865
5894
  };
5895
+
5896
+ // notify interested parties
5897
+ emit('dragstart');
5866
5898
  };
5867
5899
  const onMoveEnd = () => {
5868
5900
  context.current.newPosition = null;
5901
+
5902
+ // notify interested parties
5903
+ emit('dragend');
5869
5904
  };
5870
5905
  return jsxRuntime.jsxs("div", {
5871
5906
  class: classnames('bio-properties-panel-popup__header', draggable && 'draggable', className),
@@ -5918,26 +5953,52 @@ function Footer(props) {
5918
5953
  function getPopupParent(node) {
5919
5954
  return node.closest('.bio-properties-panel-popup');
5920
5955
  }
5956
+ function cancel(event) {
5957
+ event.preventDefault();
5958
+ event.stopPropagation();
5959
+ }
5921
5960
  const FEEL_POPUP_WIDTH = 700;
5922
5961
  const FEEL_POPUP_HEIGHT = 250;
5923
5962
 
5924
5963
  /**
5925
- * FEEL popup component, built as a singleton.
5964
+ * FEEL popup component, built as a singleton. Emits lifecycle events as follows:
5965
+ * - `feelPopup.open` - fired before the popup is mounted
5966
+ * - `feelPopup.opened` - fired after the popup is mounted. Event context contains the DOM node of the popup
5967
+ * - `feelPopup.close` - fired before the popup is unmounted. Event context contains the DOM node of the popup
5968
+ * - `feelPopup.closed` - fired after the popup is unmounted
5926
5969
  */
5927
5970
  function FEELPopupRoot(props) {
5928
5971
  const {
5929
- element
5972
+ element,
5973
+ eventBus = {
5974
+ fire() {},
5975
+ on() {},
5976
+ off() {}
5977
+ },
5978
+ popupContainer
5930
5979
  } = props;
5931
5980
  const prevElement = usePrevious(element);
5932
5981
  const [popupConfig, setPopupConfig] = hooks.useState({});
5933
5982
  const [open, setOpen] = hooks.useState(false);
5934
5983
  const [source, setSource] = hooks.useState(null);
5935
5984
  const [sourceElement, setSourceElement] = hooks.useState(null);
5936
- const handleOpen = (key, config, _sourceElement) => {
5937
- setSource(key);
5985
+ const emit = (type, context) => {
5986
+ eventBus.fire('feelPopup.' + type, context);
5987
+ };
5988
+ const isOpen = hooks.useCallback(() => {
5989
+ return !!open;
5990
+ }, [open]);
5991
+ useUpdateEffect(() => {
5992
+ if (!open) {
5993
+ emit('closed');
5994
+ }
5995
+ }, [open]);
5996
+ const handleOpen = (entryId, config, _sourceElement) => {
5997
+ setSource(entryId);
5938
5998
  setPopupConfig(config);
5939
5999
  setOpen(true);
5940
6000
  setSourceElement(_sourceElement);
6001
+ emit('open');
5941
6002
  };
5942
6003
  const handleClose = () => {
5943
6004
  setOpen(false);
@@ -5951,21 +6012,47 @@ function FEELPopupRoot(props) {
5951
6012
 
5952
6013
  // close popup on element change, cf. https://github.com/bpmn-io/properties-panel/issues/270
5953
6014
  hooks.useEffect(() => {
5954
- if (element && element !== prevElement) {
6015
+ if (element && prevElement && element !== prevElement) {
5955
6016
  handleClose();
5956
6017
  }
5957
6018
  }, [element]);
6019
+
6020
+ // allow close and open via events
6021
+ hooks.useEffect(() => {
6022
+ const handlePopupOpen = context => {
6023
+ const {
6024
+ entryId,
6025
+ popupConfig,
6026
+ sourceElement
6027
+ } = context;
6028
+ handleOpen(entryId, popupConfig, sourceElement);
6029
+ };
6030
+ const handleIsOpen = () => {
6031
+ return isOpen();
6032
+ };
6033
+ eventBus.on('feelPopup._close', handleClose);
6034
+ eventBus.on('feelPopup._open', handlePopupOpen);
6035
+ eventBus.on('feelPopup._isOpen', handleIsOpen);
6036
+ return () => {
6037
+ eventBus.off('feelPopup._close', handleClose);
6038
+ eventBus.off('feelPopup._open', handleOpen);
6039
+ eventBus.off('feelPopup._isOpen', handleIsOpen);
6040
+ };
6041
+ }, [eventBus, isOpen]);
5958
6042
  return jsxRuntime.jsxs(FeelPopupContext.Provider, {
5959
6043
  value: feelPopupContext,
5960
6044
  children: [open && jsxRuntime.jsx(FeelPopupComponent, {
5961
6045
  onClose: handleClose,
6046
+ container: popupContainer,
5962
6047
  sourceElement: sourceElement,
6048
+ emit: emit,
5963
6049
  ...popupConfig
5964
6050
  }), props.children]
5965
6051
  });
5966
6052
  }
5967
6053
  function FeelPopupComponent(props) {
5968
6054
  const {
6055
+ container,
5969
6056
  id,
5970
6057
  hostLanguage,
5971
6058
  onInput,
@@ -5977,20 +6064,45 @@ function FeelPopupComponent(props) {
5977
6064
  tooltipContainer,
5978
6065
  type,
5979
6066
  value,
5980
- variables
6067
+ variables,
6068
+ emit
5981
6069
  } = props;
5982
6070
  const editorRef = hooks.useRef();
6071
+ const popupRef = hooks.useRef();
6072
+ const isAutoCompletionOpen = hooks.useRef(false);
5983
6073
  const handleSetReturnFocus = () => {
5984
6074
  sourceElement && sourceElement.focus();
5985
6075
  };
5986
- hooks.useEffect(() => {
5987
- const editor = editorRef.current;
5988
- if (editor) {
5989
- editor.focus();
6076
+ const onKeyDownCapture = event => {
6077
+ // we use capture here to make sure we handle the event before the editor does
6078
+ if (event.key === 'Escape') {
6079
+ isAutoCompletionOpen.current = autoCompletionOpen(event.target);
6080
+ }
6081
+ };
6082
+ const onKeyDown = event => {
6083
+ if (event.key === 'Escape') {
6084
+ // close popup only if auto completion is not open
6085
+ // we need to do check this because the editor is not
6086
+ // stop propagating the keydown event
6087
+ // cf. https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322/5
6088
+ if (!isAutoCompletionOpen.current) {
6089
+ onClose();
6090
+ isAutoCompletionOpen.current = false;
6091
+ }
5990
6092
  }
5991
- }, [editorRef, id]);
6093
+ };
6094
+ hooks.useEffect(() => {
6095
+ emit('opened', {
6096
+ domNode: popupRef.current
6097
+ });
6098
+ return () => emit('close', {
6099
+ domNode: popupRef.current
6100
+ });
6101
+ }, []);
5992
6102
  return jsxRuntime.jsxs(Popup, {
6103
+ container: container,
5993
6104
  className: "bio-properties-panel-feel-popup",
6105
+ emit: emit,
5994
6106
  position: position,
5995
6107
  title: title,
5996
6108
  onClose: onClose
@@ -5999,14 +6111,20 @@ function FeelPopupComponent(props) {
5999
6111
  ,
6000
6112
 
6001
6113
  returnFocus: false,
6114
+ closeOnEscape: false,
6115
+ delayInitialFocus: false,
6002
6116
  onPostDeactivate: handleSetReturnFocus,
6003
6117
  height: FEEL_POPUP_HEIGHT,
6004
6118
  width: FEEL_POPUP_WIDTH,
6119
+ ref: popupRef,
6005
6120
  children: [jsxRuntime.jsx(Popup.Title, {
6006
6121
  title: title,
6122
+ emit: emit,
6007
6123
  draggable: true
6008
6124
  }), jsxRuntime.jsx(Popup.Body, {
6009
6125
  children: jsxRuntime.jsxs("div", {
6126
+ onKeyDownCapture: onKeyDownCapture,
6127
+ onKeyDown: onKeyDown,
6010
6128
  class: "bio-properties-panel-feel-popup__body",
6011
6129
  children: [type === 'feel' && jsxRuntime.jsx(CodeEditor, {
6012
6130
  enableGutters: true,
@@ -6048,6 +6166,26 @@ function FeelPopupComponent(props) {
6048
6166
  function prefixId$8(id) {
6049
6167
  return `bio-properties-panel-${id}`;
6050
6168
  }
6169
+ function autoCompletionOpen(element) {
6170
+ return element.closest('.cm-editor').querySelector('.cm-tooltip-autocomplete');
6171
+ }
6172
+
6173
+ /**
6174
+ * This hook behaves like useEffect, but does not trigger on the first render.
6175
+ *
6176
+ * @param {Function} effect
6177
+ * @param {Array} deps
6178
+ */
6179
+ function useUpdateEffect(effect, deps) {
6180
+ const isMounted = hooks.useRef(false);
6181
+ hooks.useEffect(() => {
6182
+ if (isMounted.current) {
6183
+ return effect();
6184
+ } else {
6185
+ isMounted.current = true;
6186
+ }
6187
+ }, deps);
6188
+ }
6051
6189
  function ToggleSwitch(props) {
6052
6190
  const {
6053
6191
  id,
@@ -6794,7 +6932,7 @@ function FeelEntry(props) {
6794
6932
  setLocalError(err);
6795
6933
  }, []);
6796
6934
  const temporaryError = useError(id);
6797
- const error = localError || temporaryError || validationError;
6935
+ const error = temporaryError || localError || validationError;
6798
6936
  return jsxRuntime.jsxs("div", {
6799
6937
  class: classnames(props.class, 'bio-properties-panel-entry', error ? 'has-error' : ''),
6800
6938
  "data-entry-id": id,
@@ -6943,7 +7081,7 @@ function calculatePopupPosition(element) {
6943
7081
 
6944
7082
  // todo(pinussilvestrus): make this configurable in the future
6945
7083
  function getPopupTitle(element, label) {
6946
- let popupTitle;
7084
+ let popupTitle = '';
6947
7085
  if (element && element.type) {
6948
7086
  popupTitle = `${element.type} / `;
6949
7087
  }
@@ -7030,6 +7168,7 @@ const DEFAULT_TOOLTIP = {};
7030
7168
  * @param {Function} [props.descriptionLoaded]
7031
7169
  * @param {TooltipConfig} [props.tooltipConfig]
7032
7170
  * @param {Function} [props.tooltipLoaded]
7171
+ * @param {HTMLElement} [props.feelPopupContainer]
7033
7172
  * @param {Object} [props.eventBus]
7034
7173
  */
7035
7174
  function PropertiesPanel(props) {
@@ -7044,6 +7183,7 @@ function PropertiesPanel(props) {
7044
7183
  descriptionLoaded,
7045
7184
  tooltipConfig,
7046
7185
  tooltipLoaded,
7186
+ feelPopupContainer,
7047
7187
  eventBus
7048
7188
  } = props;
7049
7189
 
@@ -7146,6 +7286,8 @@ function PropertiesPanel(props) {
7146
7286
  value: eventContext,
7147
7287
  children: jsxRuntime.jsx(FEELPopupRoot, {
7148
7288
  element: element,
7289
+ eventBus: eventBus,
7290
+ popupContainer: feelPopupContainer,
7149
7291
  children: jsxRuntime.jsxs("div", {
7150
7292
  class: "bio-properties-panel",
7151
7293
  children: [jsxRuntime.jsx(Header, {
@@ -7445,7 +7587,8 @@ function ListGroup(props) {
7445
7587
  class: classnames('bio-properties-panel-group-header', hasItems ? '' : 'empty', hasItems && open ? 'open' : '', sticky && open ? 'sticky' : ''),
7446
7588
  onClick: hasItems ? toggleOpen : noop$1,
7447
7589
  children: [jsxRuntime.jsx("div", {
7448
- title: label,
7590
+ title: props.tooltip ? null : label,
7591
+ "data-title": label,
7449
7592
  class: "bio-properties-panel-group-header-title",
7450
7593
  children: jsxRuntime.jsx(TooltipWrapper, {
7451
7594
  value: props.tooltip,
@@ -8083,12 +8226,51 @@ function isEdited(node) {
8083
8226
  function prefixId(id) {
8084
8227
  return `bio-properties-panel-${id}`;
8085
8228
  }
8229
+ class FeelPopupModule {
8230
+ constructor(eventBus) {
8231
+ this._eventBus = eventBus;
8232
+ }
8086
8233
 
8087
- /**
8088
- * @param {string} type
8089
- * @param {boolean} [strict]
8090
- *
8091
- * @returns {any}
8234
+ /**
8235
+ * Check if the FEEL popup is open.
8236
+ * @return {Boolean}
8237
+ */
8238
+ isOpen() {
8239
+ return this._eventBus.fire('feelPopup._isOpen');
8240
+ }
8241
+
8242
+ /**
8243
+ * Open the FEEL popup.
8244
+ *
8245
+ * @param {String} entryId
8246
+ * @param {Object} popupConfig
8247
+ * @param {HTMLElement} sourceElement
8248
+ */
8249
+ open(entryId, popupConfig, sourceElement) {
8250
+ return this._eventBus.fire('feelPopup._open', {
8251
+ entryId,
8252
+ popupConfig,
8253
+ sourceElement
8254
+ });
8255
+ }
8256
+
8257
+ /**
8258
+ * Close the FEEL popup.
8259
+ */
8260
+ close() {
8261
+ return this._eventBus.fire('feelPopup._close');
8262
+ }
8263
+ }
8264
+ FeelPopupModule.$inject = ['eventBus'];
8265
+ var index = {
8266
+ feelPopup: ['type', FeelPopupModule]
8267
+ };
8268
+
8269
+ /**
8270
+ * @param {string} type
8271
+ * @param {boolean} [strict]
8272
+ *
8273
+ * @returns {any}
8092
8274
  */
8093
8275
  function getService(type, strict) {}
8094
8276
  const PropertiesPanelContext = preact.createContext({
@@ -8185,8 +8367,8 @@ const PropertiesPanelHeaderProvider = {
8185
8367
  }
8186
8368
  };
8187
8369
 
8188
- /**
8189
- * Provide placeholders for empty and multiple state.
8370
+ /**
8371
+ * Provide placeholders for empty and multiple state.
8190
8372
  */
8191
8373
  const PropertiesPanelPlaceholderProvider = {
8192
8374
  getEmpty: () => {
@@ -8258,10 +8440,10 @@ function useService (type, strict) {
8258
8440
  return getService(type, strict);
8259
8441
  }
8260
8442
 
8261
- /**
8262
- * Retrieve list of variables from the form schema.
8263
- *
8264
- * @returns { string[] } list of variables used in form schema
8443
+ /**
8444
+ * Retrieve list of variables from the form schema.
8445
+ *
8446
+ * @returns { string[] } list of variables used in form schema
8265
8447
  */
8266
8448
  function useVariables() {
8267
8449
  const form = useService('formEditor');
@@ -9912,14 +10094,14 @@ function Value(props) {
9912
10094
 
9913
10095
  // helpers //////////
9914
10096
 
9915
- /**
9916
- * Returns copy of object with updated value.
9917
- *
9918
- * @param {Object} properties
9919
- * @param {string} key
9920
- * @param {string} value
9921
- *
9922
- * @returns {Object}
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}
9923
10105
  */
9924
10106
  function updateValue(properties, key, value) {
9925
10107
  return {
@@ -9928,14 +10110,14 @@ function updateValue(properties, key, value) {
9928
10110
  };
9929
10111
  }
9930
10112
 
9931
- /**
9932
- * Returns copy of object with updated key.
9933
- *
9934
- * @param {Object} properties
9935
- * @param {string} oldKey
9936
- * @param {string} newKey
9937
- *
9938
- * @returns {Object}
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}
9939
10121
  */
9940
10122
  function updateKey(properties, oldKey, newKey) {
9941
10123
  return Object.entries(properties).reduce((newProperties, entry) => {
@@ -10749,7 +10931,8 @@ function ValidationType(props) {
10749
10931
  id,
10750
10932
  label: 'Validation pattern',
10751
10933
  setValue,
10752
- getOptions: () => Object.values(VALIDATION_TYPE_OPTIONS)
10934
+ getOptions: () => Object.values(VALIDATION_TYPE_OPTIONS),
10935
+ tooltip: getValue('validationType')() === VALIDATION_TYPE_OPTIONS.phone.value ? 'The built-in phone validation pattern is based on the E.164 standard with no spaces. Ex: +491234567890' : undefined
10753
10936
  });
10754
10937
  }
10755
10938
 
@@ -10767,13 +10950,13 @@ function ValuesGroups(field, editField) {
10767
10950
  };
10768
10951
  const valuesSourceId = `${fieldId}-valuesSource`;
10769
10952
 
10770
- /**
10771
- * @type {Array<Group|ListGroup>}
10953
+ /**
10954
+ * @type {Array<Group|ListGroup>}
10772
10955
  */
10773
10956
  const groups = [{
10774
10957
  id: valuesSourceId,
10775
10958
  label: 'Options source',
10776
- tooltip: '"Static" defines a constant, predefined set of form options.\n"Dynamic" defines options that are populated dynamically, adjusting based on variable data for flexible responses to different conditions or inputs.',
10959
+ tooltip: getValuesTooltip(),
10777
10960
  component: Group,
10778
10961
  entries: ValuesSourceSelectEntry({
10779
10962
  ...context,
@@ -10818,6 +11001,12 @@ function ValuesGroups(field, editField) {
10818
11001
  return groups;
10819
11002
  }
10820
11003
 
11004
+ // helpers //////////
11005
+
11006
+ function getValuesTooltip() {
11007
+ return '"Static" defines a constant, predefined set of form options.\n\n' + '"Input data" defines options that are populated dynamically, adjusting based on variable data for flexible responses to different conditions or inputs.\n\n' + '"Expression" defines options that are populated from a FEEL expression.';
11008
+ }
11009
+
10821
11010
  function CustomPropertiesGroup(field, editField) {
10822
11011
  const {
10823
11012
  properties = {},
@@ -10883,13 +11072,13 @@ function CustomPropertiesGroup(field, editField) {
10883
11072
 
10884
11073
  // helpers //////////
10885
11074
 
10886
- /**
10887
- * Returns copy of object without key.
10888
- *
10889
- * @param {Object} properties
10890
- * @param {string} oldKey
10891
- *
10892
- * @returns {Object}
11075
+ /**
11076
+ * Returns copy of object without key.
11077
+ *
11078
+ * @param {Object} properties
11079
+ * @param {string} oldKey
11080
+ *
11081
+ * @returns {Object}
10893
11082
  */
10894
11083
  function removeKey(properties, oldKey) {
10895
11084
  return Object.entries(properties).reduce((newProperties, entry) => {
@@ -10975,6 +11164,10 @@ function FormPropertiesPanel(props) {
10975
11164
  const formEditor = injector.get('formEditor');
10976
11165
  const modeling = injector.get('modeling');
10977
11166
  const selectionModule = injector.get('selection');
11167
+ const propertiesPanelConfig = injector.get('config.propertiesPanel') || {};
11168
+ const {
11169
+ feelPopupContainer
11170
+ } = propertiesPanelConfig;
10978
11171
  const [state, setState] = hooks.useState({
10979
11172
  selectedFormField: selectionModule.get() || formEditor._getState().schema
10980
11173
  });
@@ -10992,9 +11185,9 @@ function FormPropertiesPanel(props) {
10992
11185
  });
10993
11186
  }, [eventBus, formEditor, selectionModule]);
10994
11187
  hooks.useLayoutEffect(() => {
10995
- /**
10996
- * TODO(pinussilvestrus): update with actual updated element,
10997
- * once we have a proper updater/change support
11188
+ /**
11189
+ * TODO(pinussilvestrus): update with actual updated element,
11190
+ * once we have a proper updater/change support
10998
11191
  */
10999
11192
  eventBus.on('changed', refresh);
11000
11193
  eventBus.on('import.done', refresh);
@@ -11024,7 +11217,8 @@ function FormPropertiesPanel(props) {
11024
11217
  eventBus: eventBus,
11025
11218
  groups: getGroups(selectedFormField, editField, getService),
11026
11219
  headerProvider: PropertiesPanelHeaderProvider,
11027
- placeholderProvider: PropertiesPanelPlaceholderProvider
11220
+ placeholderProvider: PropertiesPanelPlaceholderProvider,
11221
+ feelPopupContainer: feelPopupContainer
11028
11222
  })
11029
11223
  })
11030
11224
  });
@@ -11046,10 +11240,10 @@ class PropertiesPanelRenderer {
11046
11240
  });
11047
11241
  }
11048
11242
 
11049
- /**
11050
- * Attach the properties panel to a parent node.
11051
- *
11052
- * @param {HTMLElement} container
11243
+ /**
11244
+ * Attach the properties panel to a parent node.
11245
+ *
11246
+ * @param {HTMLElement} container
11053
11247
  */
11054
11248
  attachTo(container) {
11055
11249
  if (!container) {
@@ -11069,8 +11263,8 @@ class PropertiesPanelRenderer {
11069
11263
  this._eventBus.fire('propertiesPanel.attach');
11070
11264
  }
11071
11265
 
11072
- /**
11073
- * Detach the properties panel from its parent node.
11266
+ /**
11267
+ * Detach the properties panel from its parent node.
11074
11268
  */
11075
11269
  detach() {
11076
11270
  const parentNode = this._container.parentNode;
@@ -11096,14 +11290,15 @@ class PropertiesPanelRenderer {
11096
11290
  PropertiesPanelRenderer.$inject = ['config.propertiesPanel', 'injector', 'eventBus'];
11097
11291
 
11098
11292
  var PropertiesPanelModule = {
11293
+ __depends__: [index],
11099
11294
  __init__: ['propertiesPanel'],
11100
11295
  propertiesPanel: ['type', PropertiesPanelRenderer]
11101
11296
  };
11102
11297
 
11103
- /**
11104
- * Manages the rendering of visual plugins.
11105
- * @constructor
11106
- * @param {Object} eventBus - Event bus for the application.
11298
+ /**
11299
+ * Manages the rendering of visual plugins.
11300
+ * @constructor
11301
+ * @param {Object} eventBus - Event bus for the application.
11107
11302
  */
11108
11303
  class RenderInjector extends SectionModuleBase {
11109
11304
  constructor(eventBus) {
@@ -11112,10 +11307,10 @@ class RenderInjector extends SectionModuleBase {
11112
11307
  this.registeredRenderers = [];
11113
11308
  }
11114
11309
 
11115
- /**
11116
- * Inject a new renderer into the injector.
11117
- * @param {string} identifier - Identifier for the renderer.
11118
- * @param {Function} Renderer - The renderer function.
11310
+ /**
11311
+ * Inject a new renderer into the injector.
11312
+ * @param {string} identifier - Identifier for the renderer.
11313
+ * @param {Function} Renderer - The renderer function.
11119
11314
  */
11120
11315
  attachRenderer(identifier, Renderer) {
11121
11316
  this.registeredRenderers = [...this.registeredRenderers, {
@@ -11124,17 +11319,17 @@ class RenderInjector extends SectionModuleBase {
11124
11319
  }];
11125
11320
  }
11126
11321
 
11127
- /**
11128
- * Detach a renderer from the by key injector.
11129
- * @param {string} identifier - Identifier for the renderer.
11322
+ /**
11323
+ * Detach a renderer from the by key injector.
11324
+ * @param {string} identifier - Identifier for the renderer.
11130
11325
  */
11131
11326
  detachRenderer(identifier) {
11132
11327
  this.registeredRenderers = this.registeredRenderers.filter(r => r.identifier !== identifier);
11133
11328
  }
11134
11329
 
11135
- /**
11136
- * Returns the registered renderers.
11137
- * @returns {Array} Array of registered renderers.
11330
+ /**
11331
+ * Returns the registered renderers.
11332
+ * @returns {Array} Array of registered renderers.
11138
11333
  */
11139
11334
  fetchRenderers() {
11140
11335
  return this.registeredRenderers;
@@ -11168,48 +11363,48 @@ var ExpressionLanguageModule = {
11168
11363
 
11169
11364
  const ids = new Ids([32, 36, 1]);
11170
11365
 
11171
- /**
11172
- * @typedef { import('./types').Injector } Injector
11173
- * @typedef { import('./types').Module } Module
11174
- * @typedef { import('./types').Schema } Schema
11175
- *
11176
- * @typedef { import('./types').FormEditorOptions } FormEditorOptions
11177
- * @typedef { import('./types').FormEditorProperties } FormEditorProperties
11178
- *
11179
- * @typedef { {
11180
- * properties: FormEditorProperties,
11181
- * schema: Schema
11182
- * } } State
11183
- *
11184
- * @typedef { (type:string, priority:number, handler:Function) => void } OnEventWithPriority
11185
- * @typedef { (type:string, handler:Function) => void } OnEventWithOutPriority
11186
- * @typedef { OnEventWithPriority & OnEventWithOutPriority } OnEventType
11366
+ /**
11367
+ * @typedef { import('./types').Injector } Injector
11368
+ * @typedef { import('./types').Module } Module
11369
+ * @typedef { import('./types').Schema } Schema
11370
+ *
11371
+ * @typedef { import('./types').FormEditorOptions } FormEditorOptions
11372
+ * @typedef { import('./types').FormEditorProperties } FormEditorProperties
11373
+ *
11374
+ * @typedef { {
11375
+ * properties: FormEditorProperties,
11376
+ * schema: Schema
11377
+ * } } State
11378
+ *
11379
+ * @typedef { (type:string, priority:number, handler:Function) => void } OnEventWithPriority
11380
+ * @typedef { (type:string, handler:Function) => void } OnEventWithOutPriority
11381
+ * @typedef { OnEventWithPriority & OnEventWithOutPriority } OnEventType
11187
11382
  */
11188
11383
 
11189
- /**
11190
- * The form editor.
11384
+ /**
11385
+ * The form editor.
11191
11386
  */
11192
11387
  class FormEditor {
11193
- /**
11194
- * @constructor
11195
- * @param {FormEditorOptions} options
11388
+ /**
11389
+ * @constructor
11390
+ * @param {FormEditorOptions} options
11196
11391
  */
11197
11392
  constructor(options = {}) {
11198
- /**
11199
- * @public
11200
- * @type {OnEventType}
11393
+ /**
11394
+ * @public
11395
+ * @type {OnEventType}
11201
11396
  */
11202
11397
  this.on = this._onEvent;
11203
11398
 
11204
- /**
11205
- * @public
11206
- * @type {String}
11399
+ /**
11400
+ * @public
11401
+ * @type {String}
11207
11402
  */
11208
11403
  this._id = ids.next();
11209
11404
 
11210
- /**
11211
- * @private
11212
- * @type {Element}
11405
+ /**
11406
+ * @private
11407
+ * @type {Element}
11213
11408
  */
11214
11409
  this._container = formJsViewer.createFormContainer();
11215
11410
  this._container.setAttribute('input-handle-modified-keys', 'z,y');
@@ -11220,15 +11415,15 @@ class FormEditor {
11220
11415
  properties = {}
11221
11416
  } = options;
11222
11417
 
11223
- /**
11224
- * @private
11225
- * @type {any}
11418
+ /**
11419
+ * @private
11420
+ * @type {any}
11226
11421
  */
11227
11422
  this.exporter = exporter;
11228
11423
 
11229
- /**
11230
- * @private
11231
- * @type {State}
11424
+ /**
11425
+ * @private
11426
+ * @type {State}
11232
11427
  */
11233
11428
  this._state = {
11234
11429
  properties,
@@ -11257,10 +11452,10 @@ class FormEditor {
11257
11452
  this._detach(false);
11258
11453
  }
11259
11454
 
11260
- /**
11261
- * @param {Schema} schema
11262
- *
11263
- * @return {Promise<{ warnings: Array<any> }>}
11455
+ /**
11456
+ * @param {Schema} schema
11457
+ *
11458
+ * @return {Promise<{ warnings: Array<any> }>}
11264
11459
  */
11265
11460
  importSchema(schema) {
11266
11461
  return new Promise((resolve, reject) => {
@@ -11289,15 +11484,15 @@ class FormEditor {
11289
11484
  });
11290
11485
  }
11291
11486
 
11292
- /**
11293
- * @returns {Schema}
11487
+ /**
11488
+ * @returns {Schema}
11294
11489
  */
11295
11490
  saveSchema() {
11296
11491
  return this.getSchema();
11297
11492
  }
11298
11493
 
11299
- /**
11300
- * @returns {Schema}
11494
+ /**
11495
+ * @returns {Schema}
11301
11496
  */
11302
11497
  getSchema() {
11303
11498
  const {
@@ -11306,8 +11501,8 @@ class FormEditor {
11306
11501
  return exportSchema(schema, this.exporter, formJsViewer.schemaVersion);
11307
11502
  }
11308
11503
 
11309
- /**
11310
- * @param {Element|string} parentNode
11504
+ /**
11505
+ * @param {Element|string} parentNode
11311
11506
  */
11312
11507
  attachTo(parentNode) {
11313
11508
  if (!parentNode) {
@@ -11325,10 +11520,10 @@ class FormEditor {
11325
11520
  this._detach();
11326
11521
  }
11327
11522
 
11328
- /**
11329
- * @internal
11330
- *
11331
- * @param {boolean} [emit]
11523
+ /**
11524
+ * @internal
11525
+ *
11526
+ * @param {boolean} [emit]
11332
11527
  */
11333
11528
  _detach(emit = true) {
11334
11529
  const container = this._container,
@@ -11342,9 +11537,9 @@ class FormEditor {
11342
11537
  parentNode.removeChild(container);
11343
11538
  }
11344
11539
 
11345
- /**
11346
- * @param {any} property
11347
- * @param {any} value
11540
+ /**
11541
+ * @param {any} property
11542
+ * @param {any} value
11348
11543
  */
11349
11544
  setProperty(property, value) {
11350
11545
  const properties = minDash.set(this._getState().properties, [property], value);
@@ -11353,21 +11548,21 @@ class FormEditor {
11353
11548
  });
11354
11549
  }
11355
11550
 
11356
- /**
11357
- * @param {string} type
11358
- * @param {Function} handler
11551
+ /**
11552
+ * @param {string} type
11553
+ * @param {Function} handler
11359
11554
  */
11360
11555
  off(type, handler) {
11361
11556
  this.get('eventBus').off(type, handler);
11362
11557
  }
11363
11558
 
11364
- /**
11365
- * @internal
11366
- *
11367
- * @param {FormEditorOptions} options
11368
- * @param {Element} container
11369
- *
11370
- * @returns {Injector}
11559
+ /**
11560
+ * @internal
11561
+ *
11562
+ * @param {FormEditorOptions} options
11563
+ * @param {Element} container
11564
+ *
11565
+ * @returns {Injector}
11371
11566
  */
11372
11567
  _createInjector(options, container) {
11373
11568
  const {
@@ -11389,22 +11584,22 @@ class FormEditor {
11389
11584
  }, core, ...modules, ...additionalModules]);
11390
11585
  }
11391
11586
 
11392
- /**
11393
- * @internal
11587
+ /**
11588
+ * @internal
11394
11589
  */
11395
11590
  _emit(type, data) {
11396
11591
  this.get('eventBus').fire(type, data);
11397
11592
  }
11398
11593
 
11399
- /**
11400
- * @internal
11594
+ /**
11595
+ * @internal
11401
11596
  */
11402
11597
  _getState() {
11403
11598
  return this._state;
11404
11599
  }
11405
11600
 
11406
- /**
11407
- * @internal
11601
+ /**
11602
+ * @internal
11408
11603
  */
11409
11604
  _setState(state) {
11410
11605
  this._state = {
@@ -11414,15 +11609,15 @@ class FormEditor {
11414
11609
  this._emit('changed', this._getState());
11415
11610
  }
11416
11611
 
11417
- /**
11418
- * @internal
11612
+ /**
11613
+ * @internal
11419
11614
  */
11420
11615
  _getModules() {
11421
11616
  return [ModelingModule, EditorActionsModule, DraggingModule, KeyboardModule, SelectionModule, PaletteModule, ExpressionLanguageModule, formJsViewer.MarkdownModule, PropertiesPanelModule, RenderInjectionModule];
11422
11617
  }
11423
11618
 
11424
- /**
11425
- * @internal
11619
+ /**
11620
+ * @internal
11426
11621
  */
11427
11622
  _onEvent(type, priority, handler) {
11428
11623
  this.get('eventBus').on(type, priority, handler);