@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.es.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { FormFieldRegistry as FormFieldRegistry$1, iconsByType, Text as Text$1, FormFields, formFields, FormContext, FormRenderContext, FormComponent, Importer, PathRegistry, FormLayouter, FieldFactory, runRecursively, clone, getSchemaVariables, DATETIME_SUBTYPES, DATE_LABEL_PATH, TIME_LABEL_PATH, DATETIME_SUBTYPE_PATH, DATETIME_SUBTYPES_LABELS, TIME_SERIALISING_FORMAT_PATH, TIME_SERIALISING_FORMATS, TIME_INTERVAL_PATH, TIME_USE24H_PATH, DATE_DISALLOW_PAST_PATH, TIME_SERIALISINGFORMAT_LABELS, getValuesSource, VALUES_SOURCES, VALUES_SOURCES_DEFAULTS, VALUES_SOURCES_PATHS, VALUES_SOURCES_LABELS, FeelExpressionLanguage, createFormContainer, createInjector, MarkdownModule, schemaVersion } from '@bpmn-io/form-js-viewer';
2
2
  export { schemaVersion } from '@bpmn-io/form-js-viewer';
3
3
  import Ids from 'ids';
4
- import { isArray, isFunction, isNumber, bind, assign, debounce, forEach, get, isObject, uniqueBy, sortBy, find, throttle as throttle$1, set as set$1, isString, isUndefined, without, has } from 'min-dash';
4
+ import { isArray, isFunction, isNumber, bind, assign, debounce, forEach, get, isObject, uniqueBy, sortBy, find, set as set$1, isString, isUndefined, without, has } from 'min-dash';
5
5
  import classnames from 'classnames';
6
6
  import { jsx, jsxs, Fragment as Fragment$1 } from 'preact/jsx-runtime';
7
7
  import { useContext, useState, useMemo, useEffect, useCallback, useRef as useRef$1, useLayoutEffect } from 'preact/hooks';
@@ -517,10 +517,10 @@ function invokeFunction(fn, args) {
517
517
  return fn.apply(null, args);
518
518
  }
519
519
 
520
- /**
521
- * A factory to create a configurable debouncer.
522
- *
523
- * @param {number|boolean} [config=true]
520
+ /**
521
+ * A factory to create a configurable debouncer.
522
+ *
523
+ * @param {number|boolean} [config=true]
524
524
  */
525
525
  function DebounceFactory(config = true) {
526
526
  const timeout = typeof config === 'number' ? config : config ? 300 : 0;
@@ -533,11 +533,11 @@ function DebounceFactory(config = true) {
533
533
  DebounceFactory.$inject = ['config.debounce'];
534
534
 
535
535
  class FormFieldRegistry extends FormFieldRegistry$1 {
536
- /**
537
- * Updates a form fields id.
538
- *
539
- * @param {Object} formField
540
- * @param {string} newId
536
+ /**
537
+ * Updates a form fields id.
538
+ *
539
+ * @param {Object} formField
540
+ * @param {string} newId
541
541
  */
542
542
  updateId(formField, newId) {
543
543
  this._validateId(newId);
@@ -558,13 +558,13 @@ class FormFieldRegistry extends FormFieldRegistry$1 {
558
558
  }
559
559
  }
560
560
 
561
- /**
562
- * Validate the suitability of the given id and signals a problem
563
- * with an exception.
564
- *
565
- * @param {string} id
566
- *
567
- * @throws {Error} if id is empty or already assigned
561
+ /**
562
+ * Validate the suitability of the given id and signals a problem
563
+ * with an exception.
564
+ *
565
+ * @param {string} id
566
+ *
567
+ * @throws {Error} if id is empty or already assigned
568
568
  */
569
569
  _validateId(id) {
570
570
  if (!id) {
@@ -581,11 +581,11 @@ const MAX_COLUMNS = 16;
581
581
  const MIN_COLUMNS = 2;
582
582
  const MAX_FIELDS_PER_ROW = 4;
583
583
  class FormLayoutValidator {
584
- /**
585
- * @constructor
586
- *
587
- * @param { import('./FormLayouter').default } formLayouter
588
- * @param { import('./FormFieldRegistry').default } formFieldRegistry
584
+ /**
585
+ * @constructor
586
+ *
587
+ * @param { import('./FormLayouter').default } formLayouter
588
+ * @param { import('./FormFieldRegistry').default } formFieldRegistry
589
589
  */
590
590
  constructor(formLayouter, formFieldRegistry) {
591
591
  this._formLayouter = formLayouter;
@@ -656,21 +656,21 @@ function editorFormFieldClasses(type, {
656
656
  });
657
657
  }
658
658
 
659
- /**
660
- * Add a dragger that calls back the passed function with
661
- * { event, delta } on drag.
662
- *
663
- * @example
664
- *
665
- * function dragMove(event, delta) {
666
- * // we are dragging (!!)
667
- * }
668
- *
669
- * domElement.addEventListener('dragstart', dragger(dragMove));
670
- *
671
- * @param {Function} fn
672
- *
673
- * @return {Function} drag start callback function
659
+ /**
660
+ * Add a dragger that calls back the passed function with
661
+ * { event, delta } on drag.
662
+ *
663
+ * @example
664
+ *
665
+ * function dragMove(event, delta) {
666
+ * // we are dragging (!!)
667
+ * }
668
+ *
669
+ * domElement.addEventListener('dragstart', dragger(dragMove));
670
+ *
671
+ * @param {Function} fn
672
+ *
673
+ * @return {Function} drag start callback function
674
674
  */
675
675
  function createDragger$1(fn) {
676
676
  let self;
@@ -711,12 +711,12 @@ function createDragger$1(fn) {
711
711
  return onDragStart;
712
712
  }
713
713
 
714
- /**
715
- * Throttle function call according UI update cycle.
716
- *
717
- * @param {Function} fn
718
- *
719
- * @return {Function} throttled fn
714
+ /**
715
+ * Throttle function call according UI update cycle.
716
+ *
717
+ * @param {Function} fn
718
+ *
719
+ * @return {Function} throttled fn
720
720
  */
721
721
  function throttle(fn) {
722
722
  let active = false;
@@ -750,11 +750,11 @@ const DragAndDropContext = createContext({
750
750
  });
751
751
  var DragAndDropContext$1 = DragAndDropContext;
752
752
 
753
- /**
754
- * @param {string} type
755
- * @param {boolean} [strict]
756
- *
757
- * @returns {any}
753
+ /**
754
+ * @param {string} type
755
+ * @param {boolean} [strict]
756
+ *
757
+ * @returns {any}
758
758
  */
759
759
  function getService$1(type, strict) {}
760
760
  const FormEditorContext = createContext({
@@ -1104,23 +1104,23 @@ var Slot = (props => {
1104
1104
  return fillsAndSeparators;
1105
1105
  });
1106
1106
 
1107
- /**
1108
- * Creates a Fragment for a fill.
1109
- *
1110
- * @param {Object} fill Fill to be rendered
1111
- * @returns {Object} Preact Fragment containing fill's children
1107
+ /**
1108
+ * Creates a Fragment for a fill.
1109
+ *
1110
+ * @param {Object} fill Fill to be rendered
1111
+ * @returns {Object} Preact Fragment containing fill's children
1112
1112
  */
1113
1113
  const FillFragment = fill => jsx(Fragment, {
1114
1114
  children: fill.children
1115
1115
  }, fill.id);
1116
1116
 
1117
- /**
1118
- * Creates an array of fills, with separators inserted between groups.
1119
- *
1120
- * @param {Array} groups Groups of fills
1121
- * @param {Function} fillRenderer Function to create a fill
1122
- * @param {Function} separatorRenderer Function to create a separator
1123
- * @returns {Array} Array of fills and separators
1117
+ /**
1118
+ * Creates an array of fills, with separators inserted between groups.
1119
+ *
1120
+ * @param {Array} groups Groups of fills
1121
+ * @param {Function} fillRenderer Function to create a fill
1122
+ * @param {Function} separatorRenderer Function to create a separator
1123
+ * @returns {Array} Array of fills and separators
1124
1124
  */
1125
1125
  const buildFills = (groups, fillRenderer, separatorRenderer) => {
1126
1126
  const result = [];
@@ -1138,8 +1138,8 @@ const buildFills = (groups, fillRenderer, separatorRenderer) => {
1138
1138
  return result;
1139
1139
  };
1140
1140
 
1141
- /**
1142
- * Groups fills by group name property.
1141
+ /**
1142
+ * Groups fills by group name property.
1143
1143
  */
1144
1144
  const _groupByGroupName = fills => {
1145
1145
  const groups = [];
@@ -1159,8 +1159,8 @@ const _groupByGroupName = fills => {
1159
1159
  return Object.keys(groupsById).sort().map(id => groupsById[id]);
1160
1160
  };
1161
1161
 
1162
- /**
1163
- * Compares fills by priority.
1162
+ /**
1163
+ * Compares fills by priority.
1164
1164
  */
1165
1165
  const _comparePriority = (a, b) => {
1166
1166
  return (b.priority || 0) - (a.priority || 0);
@@ -1388,20 +1388,20 @@ const DRAG_NO_DROP_CLS = 'fjs-no-drop';
1388
1388
  const DRAG_NO_MOVE_CLS = 'fjs-no-move';
1389
1389
  const ERROR_DROP_CLS = 'fjs-error-drop';
1390
1390
 
1391
- /**
1392
- * @typedef { { id: String, components: Array<any> } } FormRow
1391
+ /**
1392
+ * @typedef { { id: String, components: Array<any> } } FormRow
1393
1393
  */
1394
1394
 
1395
1395
  class Dragging {
1396
- /**
1397
- * @constructor
1398
- *
1399
- * @param { import('../../core/FormFieldRegistry').default } formFieldRegistry
1400
- * @param { import('../../core/FormLayouter').default } formLayouter
1401
- * @param { import('../../core/FormLayoutValidator').default } formLayoutValidator
1402
- * @param { import('../../core/EventBus').default } eventBus
1403
- * @param { import('../modeling/Modeling').default } modeling
1404
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
1396
+ /**
1397
+ * @constructor
1398
+ *
1399
+ * @param { import('../../core/FormFieldRegistry').default } formFieldRegistry
1400
+ * @param { import('../../core/FormLayouter').default } formLayouter
1401
+ * @param { import('../../core/FormLayoutValidator').default } formLayoutValidator
1402
+ * @param { import('../../core/EventBus').default } eventBus
1403
+ * @param { import('../modeling/Modeling').default } modeling
1404
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
1405
1405
  */
1406
1406
  constructor(formFieldRegistry, formLayouter, formLayoutValidator, eventBus, modeling, pathRegistry) {
1407
1407
  this._formFieldRegistry = formFieldRegistry;
@@ -1412,13 +1412,13 @@ class Dragging {
1412
1412
  this._pathRegistry = pathRegistry;
1413
1413
  }
1414
1414
 
1415
- /**
1416
- * Calculcates position in form schema given the dropped place.
1417
- *
1418
- * @param { FormRow } targetRow
1419
- * @param { any } targetFormField
1420
- * @param { HTMLElement } sibling
1421
- * @returns { number }
1415
+ /**
1416
+ * Calculcates position in form schema given the dropped place.
1417
+ *
1418
+ * @param { FormRow } targetRow
1419
+ * @param { any } targetFormField
1420
+ * @param { HTMLElement } sibling
1421
+ * @returns { number }
1422
1422
  */
1423
1423
  getTargetIndex(targetRow, targetFormField, sibling) {
1424
1424
  /** @type HTMLElement */
@@ -1559,8 +1559,8 @@ class Dragging {
1559
1559
  }
1560
1560
  }
1561
1561
 
1562
- /**
1563
- * @param { { container: Array<string>, direction: string, mirrorContainer: string } } options
1562
+ /**
1563
+ * @param { { container: Array<string>, direction: string, mirrorContainer: string } } options
1564
1564
  */
1565
1565
  createDragulaInstance(options) {
1566
1566
  const {
@@ -1974,7 +1974,7 @@ function DebugColumns(props) {
1974
1974
  return null;
1975
1975
  }
1976
1976
  return jsx("div", {
1977
- style: "width: fit-content;\r padding: 2px 6px;\r height: 16px;\r background: var(--color-blue-205-100-95);\r display: flex;\r justify-content: center;\r align-items: center;\r position: absolute;\r bottom: -2px;\r z-index: 2;\r font-size: 10px;\r right: 3px;",
1977
+ style: "width: fit-content; padding: 2px 6px; height: 16px; background: var(--color-blue-205-100-95); display: flex; justify-content: center; align-items: center; position: absolute; bottom: -2px; z-index: 2; font-size: 10px; right: 3px;",
1978
1978
  class: "fjs-debug-columns",
1979
1979
  children: (field.layout || {}).columns || 'auto'
1980
1980
  });
@@ -3045,10 +3045,10 @@ function updateRow(formField, rowId) {
3045
3045
  }
3046
3046
 
3047
3047
  class AddFormFieldHandler {
3048
- /**
3049
- * @constructor
3050
- * @param { import('../../../FormEditor').default } formEditor
3051
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3048
+ /**
3049
+ * @constructor
3050
+ * @param { import('../../../FormEditor').default } formEditor
3051
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3052
3052
  */
3053
3053
  constructor(formEditor, formFieldRegistry) {
3054
3054
  this._formEditor = formEditor;
@@ -3109,10 +3109,10 @@ class AddFormFieldHandler {
3109
3109
  AddFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3110
3110
 
3111
3111
  class EditFormFieldHandler {
3112
- /**
3113
- * @constructor
3114
- * @param { import('../../../FormEditor').default } formEditor
3115
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3112
+ /**
3113
+ * @constructor
3114
+ * @param { import('../../../FormEditor').default } formEditor
3115
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3116
3116
  */
3117
3117
  constructor(formEditor, formFieldRegistry) {
3118
3118
  this._formEditor = formEditor;
@@ -3175,11 +3175,11 @@ class EditFormFieldHandler {
3175
3175
  EditFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3176
3176
 
3177
3177
  class MoveFormFieldHandler {
3178
- /**
3179
- * @constructor
3180
- * @param { import('../../../FormEditor').default } formEditor
3181
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3182
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3178
+ /**
3179
+ * @constructor
3180
+ * @param { import('../../../FormEditor').default } formEditor
3181
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3182
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3183
3183
  */
3184
3184
  constructor(formEditor, formFieldRegistry, pathRegistry) {
3185
3185
  this._formEditor = formEditor;
@@ -3284,10 +3284,10 @@ class MoveFormFieldHandler {
3284
3284
  MoveFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry', 'pathRegistry'];
3285
3285
 
3286
3286
  class RemoveFormFieldHandler {
3287
- /**
3288
- * @constructor
3289
- * @param { import('../../../FormEditor').default } formEditor
3290
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3287
+ /**
3288
+ * @constructor
3289
+ * @param { import('../../../FormEditor').default } formEditor
3290
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3291
3291
  */
3292
3292
  constructor(formEditor, formFieldRegistry) {
3293
3293
  this._formEditor = formEditor;
@@ -3347,9 +3347,9 @@ class RemoveFormFieldHandler {
3347
3347
  RemoveFormFieldHandler.$inject = ['formEditor', 'formFieldRegistry'];
3348
3348
 
3349
3349
  class UpdateIdClaimHandler {
3350
- /**
3351
- * @constructor
3352
- * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3350
+ /**
3351
+ * @constructor
3352
+ * @param { import('../../../core/FormFieldRegistry').default } formFieldRegistry
3353
3353
  */
3354
3354
  constructor(formFieldRegistry) {
3355
3355
  this._formFieldRegistry = formFieldRegistry;
@@ -3382,9 +3382,9 @@ class UpdateIdClaimHandler {
3382
3382
  UpdateIdClaimHandler.$inject = ['formFieldRegistry'];
3383
3383
 
3384
3384
  class UpdateKeyClaimHandler {
3385
- /**
3386
- * @constructor
3387
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3385
+ /**
3386
+ * @constructor
3387
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3388
3388
  */
3389
3389
  constructor(pathRegistry) {
3390
3390
  this._pathRegistry = pathRegistry;
@@ -3425,9 +3425,9 @@ class UpdateKeyClaimHandler {
3425
3425
  UpdateKeyClaimHandler.$inject = ['pathRegistry'];
3426
3426
 
3427
3427
  class UpdatePathClaimHandler {
3428
- /**
3429
- * @constructor
3430
- * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3428
+ /**
3429
+ * @constructor
3430
+ * @param { import('@bpmn-io/form-js-viewer').PathRegistry } pathRegistry
3431
3431
  */
3432
3432
  constructor(pathRegistry) {
3433
3433
  this._pathRegistry = pathRegistry;
@@ -3989,8 +3989,8 @@ class ValidateBehavior extends CommandInterceptor {
3989
3989
  constructor(eventBus) {
3990
3990
  super(eventBus);
3991
3991
 
3992
- /**
3993
- * Remove custom validation if <validationType> is about to be added.
3992
+ /**
3993
+ * Remove custom validation if <validationType> is about to be added.
3994
3994
  */
3995
3995
  this.preExecute('formField.edit', function (context) {
3996
3996
  const {
@@ -4570,22 +4570,22 @@ var SelectionModule = {
4570
4570
  selectionBehavior: ['type', SelectionBehavior]
4571
4571
  };
4572
4572
 
4573
- /**
4574
- * Base class for sectionable UI modules.
4575
- *
4576
- * @property {EventBus} _eventBus - EventBus instance used for event handling.
4577
- * @property {string} managerType - Type of the render manager. Used to form event names.
4578
- *
4579
- * @class SectionModuleBase
4573
+ /**
4574
+ * Base class for sectionable UI modules.
4575
+ *
4576
+ * @property {EventBus} _eventBus - EventBus instance used for event handling.
4577
+ * @property {string} managerType - Type of the render manager. Used to form event names.
4578
+ *
4579
+ * @class SectionModuleBase
4580
4580
  */
4581
4581
  class SectionModuleBase {
4582
- /**
4583
- * Create a SectionModuleBase instance.
4584
- *
4585
- * @param {any} eventBus - The EventBus instance used for event handling.
4586
- * @param {string} sectionKey - The type of render manager. Used to form event names.
4587
- *
4588
- * @constructor
4582
+ /**
4583
+ * Create a SectionModuleBase instance.
4584
+ *
4585
+ * @param {any} eventBus - The EventBus instance used for event handling.
4586
+ * @param {string} sectionKey - The type of render manager. Used to form event names.
4587
+ *
4588
+ * @constructor
4589
4589
  */
4590
4590
  constructor(eventBus, sectionKey) {
4591
4591
  this._eventBus = eventBus;
@@ -4598,10 +4598,10 @@ class SectionModuleBase {
4598
4598
  });
4599
4599
  }
4600
4600
 
4601
- /**
4602
- * Attach the managed section to a parent node.
4603
- *
4604
- * @param {HTMLElement} container - The parent node to attach to.
4601
+ /**
4602
+ * Attach the managed section to a parent node.
4603
+ *
4604
+ * @param {HTMLElement} container - The parent node to attach to.
4605
4605
  */
4606
4606
  attachTo(container) {
4607
4607
  this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.attach`, {
@@ -4609,22 +4609,22 @@ class SectionModuleBase {
4609
4609
  }));
4610
4610
  }
4611
4611
 
4612
- /**
4613
- * Detach the managed section from its parent node.
4612
+ /**
4613
+ * Detach the managed section from its parent node.
4614
4614
  */
4615
4615
  detach() {
4616
4616
  this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.detach`));
4617
4617
  }
4618
4618
 
4619
- /**
4620
- * Reset the managed section to its initial state.
4619
+ /**
4620
+ * Reset the managed section to its initial state.
4621
4621
  */
4622
4622
  reset() {
4623
4623
  this._onceSectionRendered(() => this._eventBus.fire(`${this._sectionKey}.reset`));
4624
4624
  }
4625
4625
 
4626
- /**
4627
- * Circumvents timing issues.
4626
+ /**
4627
+ * Circumvents timing issues.
4628
4628
  */
4629
4629
  _onceSectionRendered(callback) {
4630
4630
  if (this.isSectionRendered) {
@@ -4875,18 +4875,25 @@ function Tooltip(props) {
4875
4875
  parent
4876
4876
  } = props;
4877
4877
  const [visible, setShow] = useState(false);
4878
+ const [focusedViaKeyboard, setFocusedViaKeyboard] = useState(false);
4878
4879
  let timeout = null;
4879
4880
  const wrapperRef = useRef$1(null);
4880
4881
  const tooltipRef = useRef$1(null);
4881
4882
  const showTooltip = async event => {
4882
- const show = () => !visible && setShow(true);
4883
- if (event instanceof MouseEvent) {
4884
- timeout = setTimeout(show, 200);
4885
- } else {
4886
- show();
4883
+ const show = () => setShow(true);
4884
+ if (!visible && !timeout) {
4885
+ if (event instanceof MouseEvent) {
4886
+ timeout = setTimeout(show, 200);
4887
+ } else {
4888
+ show();
4889
+ setFocusedViaKeyboard(true);
4890
+ }
4887
4891
  }
4888
4892
  };
4889
- const hideTooltip = () => setShow(false);
4893
+ const hideTooltip = () => {
4894
+ setShow(false);
4895
+ setFocusedViaKeyboard(false);
4896
+ };
4890
4897
  const hideTooltipViaEscape = e => {
4891
4898
  e.code === 'Escape' && hideTooltip();
4892
4899
  };
@@ -4910,7 +4917,7 @@ function Tooltip(props) {
4910
4917
  if (visible && !isTooltipHovered({
4911
4918
  x: e.x,
4912
4919
  y: e.y
4913
- }) && !isFocused) {
4920
+ }) && !(isFocused && focusedViaKeyboard)) {
4914
4921
  hideTooltip();
4915
4922
  }
4916
4923
  };
@@ -4918,8 +4925,8 @@ function Tooltip(props) {
4918
4925
  const {
4919
4926
  relatedTarget
4920
4927
  } = e;
4921
- const isTooltipChild = el => el && !!el.closest('.bio-properties-panel-tooltip');
4922
- if (visible && !isHovered(wrapperRef.current) && !isTooltipChild(relatedTarget)) {
4928
+ const isTooltipChild = el => !!el.closest('.bio-properties-panel-tooltip');
4929
+ if (visible && !isHovered(wrapperRef.current) && relatedTarget && !isTooltipChild(relatedTarget)) {
4923
4930
  hideTooltip();
4924
4931
  }
4925
4932
  };
@@ -4931,7 +4938,7 @@ function Tooltip(props) {
4931
4938
  document.removeEventListener('mousemove', hideHoveredTooltip);
4932
4939
  document.removeEventListener('focusout', hideFocusedTooltip);
4933
4940
  };
4934
- }, [wrapperRef.current, visible]);
4941
+ }, [wrapperRef.current, visible, focusedViaKeyboard]);
4935
4942
  const renderTooltip = () => {
4936
4943
  return jsxs("div", {
4937
4944
  class: "bio-properties-panel-tooltip",
@@ -4954,12 +4961,12 @@ function Tooltip(props) {
4954
4961
  tabIndex: "0",
4955
4962
  ref: wrapperRef,
4956
4963
  onMouseEnter: showTooltip,
4957
- onMouseLeave: () => clearTimeout(timeout),
4964
+ onMouseLeave: () => {
4965
+ clearTimeout(timeout);
4966
+ timeout = null;
4967
+ },
4958
4968
  onFocus: showTooltip,
4959
4969
  onKeyDown: hideTooltipViaEscape,
4960
- onMouseDown: e => {
4961
- e.preventDefault();
4962
- },
4963
4970
  children: [props.children, visible ? parent ? createPortal(renderTooltip(), parent.current) : renderTooltip() : null]
4964
4971
  });
4965
4972
  }
@@ -5236,20 +5243,24 @@ function Group(props) {
5236
5243
 
5237
5244
  // set edited state depending on all entries
5238
5245
  useEffect(() => {
5239
- const hasOneEditedEntry = entries.find(entry => {
5240
- const {
5241
- id,
5242
- isEdited
5243
- } = entry;
5244
- const entryNode = query(`[data-entry-id="${id}"]`);
5245
- if (!isFunction(isEdited) || !entryNode) {
5246
- return false;
5247
- }
5248
- const inputNode = query('.bio-properties-panel-input', entryNode);
5249
- return isEdited(inputNode);
5246
+ // 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
5247
+ const scheduled = requestAnimationFrame(() => {
5248
+ const hasOneEditedEntry = entries.find(entry => {
5249
+ const {
5250
+ id,
5251
+ isEdited
5252
+ } = entry;
5253
+ const entryNode = query(`[data-entry-id="${id}"]`);
5254
+ if (!isFunction(isEdited) || !entryNode) {
5255
+ return false;
5256
+ }
5257
+ const inputNode = query('.bio-properties-panel-input', entryNode);
5258
+ return isEdited(inputNode);
5259
+ });
5260
+ setEdited(hasOneEditedEntry);
5250
5261
  });
5251
- setEdited(hasOneEditedEntry);
5252
- }, [entries]);
5262
+ return () => cancelAnimationFrame(scheduled);
5263
+ }, [entries, setEdited]);
5253
5264
 
5254
5265
  // set error state depending on all entries
5255
5266
  const allErrors = useErrors();
@@ -5269,7 +5280,8 @@ function Group(props) {
5269
5280
  class: classnames('bio-properties-panel-group-header', edited ? '' : 'empty', open ? 'open' : '', sticky && open ? 'sticky' : ''),
5270
5281
  onClick: toggleOpen,
5271
5282
  children: [jsx("div", {
5272
- title: label,
5283
+ title: props.tooltip ? null : label,
5284
+ "data-title": label,
5273
5285
  class: "bio-properties-panel-group-header-title",
5274
5286
  children: jsx(TooltipWrapper, {
5275
5287
  value: props.tooltip,
@@ -5676,7 +5688,11 @@ function createDragger(fn, dragPreview) {
5676
5688
  // (2) setup drag listeners
5677
5689
 
5678
5690
  // attach drag + cleanup event
5679
- document.addEventListener('dragover', onDrag);
5691
+ // we need to do this to make sure we track cursor
5692
+ // movements before we reach other drag event handlers,
5693
+ // e.g. in child containers.
5694
+ document.addEventListener('dragover', onDrag, true);
5695
+ document.addEventListener('dragenter', preventDefault, true);
5680
5696
  document.addEventListener('dragend', onEnd);
5681
5697
  document.addEventListener('drop', preventDefault);
5682
5698
  }
@@ -5690,7 +5706,8 @@ function createDragger(fn, dragPreview) {
5690
5706
  return fn.call(self, event, delta);
5691
5707
  }
5692
5708
  function onEnd() {
5693
- document.removeEventListener('dragover', onDrag);
5709
+ document.removeEventListener('dragover', onDrag, true);
5710
+ document.removeEventListener('dragenter', preventDefault, true);
5694
5711
  document.removeEventListener('dragend', onEnd);
5695
5712
  document.removeEventListener('drop', preventDefault);
5696
5713
  }
@@ -5711,6 +5728,7 @@ const noop$3 = () => {};
5711
5728
  * @param {Object} props
5712
5729
  * @param {HTMLElement} [props.container]
5713
5730
  * @param {string} [props.className]
5731
+ * @param {boolean} [props.delayInitialFocus]
5714
5732
  * @param {{x: number, y: number}} [props.position]
5715
5733
  * @param {number} [props.width]
5716
5734
  * @param {number} [props.height]
@@ -5718,12 +5736,15 @@ const noop$3 = () => {};
5718
5736
  * @param {Function} [props.onPostActivate]
5719
5737
  * @param {Function} [props.onPostDeactivate]
5720
5738
  * @param {boolean} [props.returnFocus]
5739
+ * @param {boolean} [props.closeOnEscape]
5721
5740
  * @param {string} props.title
5741
+ * @param {Ref} [ref]
5722
5742
  */
5723
- function Popup(props) {
5743
+ function PopupComponent(props, globalRef) {
5724
5744
  const {
5725
5745
  container,
5726
5746
  className,
5747
+ delayInitialFocus,
5727
5748
  position,
5728
5749
  width,
5729
5750
  height,
@@ -5731,12 +5752,16 @@ function Popup(props) {
5731
5752
  onPostActivate = noop$3,
5732
5753
  onPostDeactivate = noop$3,
5733
5754
  returnFocus = true,
5755
+ closeOnEscape = true,
5734
5756
  title
5735
5757
  } = props;
5736
5758
  const focusTrapRef = useRef$1(null);
5737
- const popupRef = useRef$1(null);
5738
- const handleKeyPress = event => {
5739
- if (event.key === 'Escape') {
5759
+ const localRef = useRef$1(null);
5760
+ const popupRef = globalRef || localRef;
5761
+ const handleKeydown = event => {
5762
+ // do not allow keyboard events to bubble
5763
+ event.stopPropagation();
5764
+ if (closeOnEscape && event.key === 'Escape') {
5740
5765
  onClose();
5741
5766
  }
5742
5767
  };
@@ -5761,14 +5786,6 @@ function Popup(props) {
5761
5786
  if (height) {
5762
5787
  style.height = height + 'px';
5763
5788
  }
5764
- useEffect(() => {
5765
- if (popupRef.current) {
5766
- popupRef.current.addEventListener('keydown', handleKeyPress);
5767
- }
5768
- return () => {
5769
- popupRef.current.removeEventListener('keydown', handleKeyPress);
5770
- };
5771
- }, [popupRef]);
5772
5789
  useEffect(() => {
5773
5790
  if (popupRef.current) {
5774
5791
  popupRef.current.addEventListener('focusin', handleFocus);
@@ -5781,6 +5798,7 @@ function Popup(props) {
5781
5798
  if (popupRef.current) {
5782
5799
  focusTrapRef.current = focusTrap.createFocusTrap(popupRef.current, {
5783
5800
  clickOutsideDeactivates: true,
5801
+ delayInitialFocus,
5784
5802
  fallbackFocus: popupRef.current,
5785
5803
  onPostActivate,
5786
5804
  onPostDeactivate,
@@ -5794,12 +5812,14 @@ function Popup(props) {
5794
5812
  "aria-label": title,
5795
5813
  tabIndex: -1,
5796
5814
  ref: popupRef,
5815
+ onKeyDown: handleKeydown,
5797
5816
  role: "dialog",
5798
5817
  class: classnames('bio-properties-panel-popup', className),
5799
5818
  style: style,
5800
5819
  children: props.children
5801
5820
  }), container || document.body);
5802
5821
  }
5822
+ const Popup = forwardRef(PopupComponent);
5803
5823
  Popup.Title = Title;
5804
5824
  Popup.Body = Body;
5805
5825
  Popup.Footer = Footer;
@@ -5808,6 +5828,7 @@ function Title(props) {
5808
5828
  children,
5809
5829
  className,
5810
5830
  draggable,
5831
+ emit = () => {},
5811
5832
  title,
5812
5833
  ...rest
5813
5834
  } = props;
@@ -5820,7 +5841,8 @@ function Title(props) {
5820
5841
  });
5821
5842
  const dragPreviewRef = useRef$1();
5822
5843
  const titleRef = useRef$1();
5823
- const onMove = throttle$1((_, delta) => {
5844
+ const onMove = (event, delta) => {
5845
+ cancel(event);
5824
5846
  const {
5825
5847
  x: dx,
5826
5848
  y: dy
@@ -5832,20 +5854,33 @@ function Title(props) {
5832
5854
  const popupParent = getPopupParent(titleRef.current);
5833
5855
  popupParent.style.top = newPosition.y + 'px';
5834
5856
  popupParent.style.left = newPosition.x + 'px';
5835
- });
5857
+
5858
+ // notify interested parties
5859
+ emit('dragover', {
5860
+ newPosition,
5861
+ delta
5862
+ });
5863
+ };
5836
5864
  const onMoveStart = event => {
5837
5865
  // initialize drag handler
5838
5866
  const onDragStart = createDragger(onMove, dragPreviewRef.current);
5839
5867
  onDragStart(event);
5868
+ event.stopPropagation();
5840
5869
  const popupParent = getPopupParent(titleRef.current);
5841
5870
  const bounds = popupParent.getBoundingClientRect();
5842
5871
  context.current.startPosition = {
5843
5872
  x: bounds.left,
5844
5873
  y: bounds.top
5845
5874
  };
5875
+
5876
+ // notify interested parties
5877
+ emit('dragstart');
5846
5878
  };
5847
5879
  const onMoveEnd = () => {
5848
5880
  context.current.newPosition = null;
5881
+
5882
+ // notify interested parties
5883
+ emit('dragend');
5849
5884
  };
5850
5885
  return jsxs("div", {
5851
5886
  class: classnames('bio-properties-panel-popup__header', draggable && 'draggable', className),
@@ -5898,26 +5933,52 @@ function Footer(props) {
5898
5933
  function getPopupParent(node) {
5899
5934
  return node.closest('.bio-properties-panel-popup');
5900
5935
  }
5936
+ function cancel(event) {
5937
+ event.preventDefault();
5938
+ event.stopPropagation();
5939
+ }
5901
5940
  const FEEL_POPUP_WIDTH = 700;
5902
5941
  const FEEL_POPUP_HEIGHT = 250;
5903
5942
 
5904
5943
  /**
5905
- * FEEL popup component, built as a singleton.
5944
+ * FEEL popup component, built as a singleton. Emits lifecycle events as follows:
5945
+ * - `feelPopup.open` - fired before the popup is mounted
5946
+ * - `feelPopup.opened` - fired after the popup is mounted. Event context contains the DOM node of the popup
5947
+ * - `feelPopup.close` - fired before the popup is unmounted. Event context contains the DOM node of the popup
5948
+ * - `feelPopup.closed` - fired after the popup is unmounted
5906
5949
  */
5907
5950
  function FEELPopupRoot(props) {
5908
5951
  const {
5909
- element
5952
+ element,
5953
+ eventBus = {
5954
+ fire() {},
5955
+ on() {},
5956
+ off() {}
5957
+ },
5958
+ popupContainer
5910
5959
  } = props;
5911
5960
  const prevElement = usePrevious(element);
5912
5961
  const [popupConfig, setPopupConfig] = useState({});
5913
5962
  const [open, setOpen] = useState(false);
5914
5963
  const [source, setSource] = useState(null);
5915
5964
  const [sourceElement, setSourceElement] = useState(null);
5916
- const handleOpen = (key, config, _sourceElement) => {
5917
- setSource(key);
5965
+ const emit = (type, context) => {
5966
+ eventBus.fire('feelPopup.' + type, context);
5967
+ };
5968
+ const isOpen = useCallback(() => {
5969
+ return !!open;
5970
+ }, [open]);
5971
+ useUpdateEffect(() => {
5972
+ if (!open) {
5973
+ emit('closed');
5974
+ }
5975
+ }, [open]);
5976
+ const handleOpen = (entryId, config, _sourceElement) => {
5977
+ setSource(entryId);
5918
5978
  setPopupConfig(config);
5919
5979
  setOpen(true);
5920
5980
  setSourceElement(_sourceElement);
5981
+ emit('open');
5921
5982
  };
5922
5983
  const handleClose = () => {
5923
5984
  setOpen(false);
@@ -5931,21 +5992,47 @@ function FEELPopupRoot(props) {
5931
5992
 
5932
5993
  // close popup on element change, cf. https://github.com/bpmn-io/properties-panel/issues/270
5933
5994
  useEffect(() => {
5934
- if (element && element !== prevElement) {
5995
+ if (element && prevElement && element !== prevElement) {
5935
5996
  handleClose();
5936
5997
  }
5937
5998
  }, [element]);
5999
+
6000
+ // allow close and open via events
6001
+ useEffect(() => {
6002
+ const handlePopupOpen = context => {
6003
+ const {
6004
+ entryId,
6005
+ popupConfig,
6006
+ sourceElement
6007
+ } = context;
6008
+ handleOpen(entryId, popupConfig, sourceElement);
6009
+ };
6010
+ const handleIsOpen = () => {
6011
+ return isOpen();
6012
+ };
6013
+ eventBus.on('feelPopup._close', handleClose);
6014
+ eventBus.on('feelPopup._open', handlePopupOpen);
6015
+ eventBus.on('feelPopup._isOpen', handleIsOpen);
6016
+ return () => {
6017
+ eventBus.off('feelPopup._close', handleClose);
6018
+ eventBus.off('feelPopup._open', handleOpen);
6019
+ eventBus.off('feelPopup._isOpen', handleIsOpen);
6020
+ };
6021
+ }, [eventBus, isOpen]);
5938
6022
  return jsxs(FeelPopupContext.Provider, {
5939
6023
  value: feelPopupContext,
5940
6024
  children: [open && jsx(FeelPopupComponent, {
5941
6025
  onClose: handleClose,
6026
+ container: popupContainer,
5942
6027
  sourceElement: sourceElement,
6028
+ emit: emit,
5943
6029
  ...popupConfig
5944
6030
  }), props.children]
5945
6031
  });
5946
6032
  }
5947
6033
  function FeelPopupComponent(props) {
5948
6034
  const {
6035
+ container,
5949
6036
  id,
5950
6037
  hostLanguage,
5951
6038
  onInput,
@@ -5957,20 +6044,45 @@ function FeelPopupComponent(props) {
5957
6044
  tooltipContainer,
5958
6045
  type,
5959
6046
  value,
5960
- variables
6047
+ variables,
6048
+ emit
5961
6049
  } = props;
5962
6050
  const editorRef = useRef$1();
6051
+ const popupRef = useRef$1();
6052
+ const isAutoCompletionOpen = useRef$1(false);
5963
6053
  const handleSetReturnFocus = () => {
5964
6054
  sourceElement && sourceElement.focus();
5965
6055
  };
5966
- useEffect(() => {
5967
- const editor = editorRef.current;
5968
- if (editor) {
5969
- editor.focus();
6056
+ const onKeyDownCapture = event => {
6057
+ // we use capture here to make sure we handle the event before the editor does
6058
+ if (event.key === 'Escape') {
6059
+ isAutoCompletionOpen.current = autoCompletionOpen(event.target);
6060
+ }
6061
+ };
6062
+ const onKeyDown = event => {
6063
+ if (event.key === 'Escape') {
6064
+ // close popup only if auto completion is not open
6065
+ // we need to do check this because the editor is not
6066
+ // stop propagating the keydown event
6067
+ // cf. https://discuss.codemirror.net/t/how-can-i-replace-the-default-autocompletion-keymap-v6/3322/5
6068
+ if (!isAutoCompletionOpen.current) {
6069
+ onClose();
6070
+ isAutoCompletionOpen.current = false;
6071
+ }
5970
6072
  }
5971
- }, [editorRef, id]);
6073
+ };
6074
+ useEffect(() => {
6075
+ emit('opened', {
6076
+ domNode: popupRef.current
6077
+ });
6078
+ return () => emit('close', {
6079
+ domNode: popupRef.current
6080
+ });
6081
+ }, []);
5972
6082
  return jsxs(Popup, {
6083
+ container: container,
5973
6084
  className: "bio-properties-panel-feel-popup",
6085
+ emit: emit,
5974
6086
  position: position,
5975
6087
  title: title,
5976
6088
  onClose: onClose
@@ -5979,14 +6091,20 @@ function FeelPopupComponent(props) {
5979
6091
  ,
5980
6092
 
5981
6093
  returnFocus: false,
6094
+ closeOnEscape: false,
6095
+ delayInitialFocus: false,
5982
6096
  onPostDeactivate: handleSetReturnFocus,
5983
6097
  height: FEEL_POPUP_HEIGHT,
5984
6098
  width: FEEL_POPUP_WIDTH,
6099
+ ref: popupRef,
5985
6100
  children: [jsx(Popup.Title, {
5986
6101
  title: title,
6102
+ emit: emit,
5987
6103
  draggable: true
5988
6104
  }), jsx(Popup.Body, {
5989
6105
  children: jsxs("div", {
6106
+ onKeyDownCapture: onKeyDownCapture,
6107
+ onKeyDown: onKeyDown,
5990
6108
  class: "bio-properties-panel-feel-popup__body",
5991
6109
  children: [type === 'feel' && jsx(CodeEditor, {
5992
6110
  enableGutters: true,
@@ -6028,6 +6146,26 @@ function FeelPopupComponent(props) {
6028
6146
  function prefixId$8(id) {
6029
6147
  return `bio-properties-panel-${id}`;
6030
6148
  }
6149
+ function autoCompletionOpen(element) {
6150
+ return element.closest('.cm-editor').querySelector('.cm-tooltip-autocomplete');
6151
+ }
6152
+
6153
+ /**
6154
+ * This hook behaves like useEffect, but does not trigger on the first render.
6155
+ *
6156
+ * @param {Function} effect
6157
+ * @param {Array} deps
6158
+ */
6159
+ function useUpdateEffect(effect, deps) {
6160
+ const isMounted = useRef$1(false);
6161
+ useEffect(() => {
6162
+ if (isMounted.current) {
6163
+ return effect();
6164
+ } else {
6165
+ isMounted.current = true;
6166
+ }
6167
+ }, deps);
6168
+ }
6031
6169
  function ToggleSwitch(props) {
6032
6170
  const {
6033
6171
  id,
@@ -6774,7 +6912,7 @@ function FeelEntry(props) {
6774
6912
  setLocalError(err);
6775
6913
  }, []);
6776
6914
  const temporaryError = useError(id);
6777
- const error = localError || temporaryError || validationError;
6915
+ const error = temporaryError || localError || validationError;
6778
6916
  return jsxs("div", {
6779
6917
  class: classnames(props.class, 'bio-properties-panel-entry', error ? 'has-error' : ''),
6780
6918
  "data-entry-id": id,
@@ -6923,7 +7061,7 @@ function calculatePopupPosition(element) {
6923
7061
 
6924
7062
  // todo(pinussilvestrus): make this configurable in the future
6925
7063
  function getPopupTitle(element, label) {
6926
- let popupTitle;
7064
+ let popupTitle = '';
6927
7065
  if (element && element.type) {
6928
7066
  popupTitle = `${element.type} / `;
6929
7067
  }
@@ -7010,6 +7148,7 @@ const DEFAULT_TOOLTIP = {};
7010
7148
  * @param {Function} [props.descriptionLoaded]
7011
7149
  * @param {TooltipConfig} [props.tooltipConfig]
7012
7150
  * @param {Function} [props.tooltipLoaded]
7151
+ * @param {HTMLElement} [props.feelPopupContainer]
7013
7152
  * @param {Object} [props.eventBus]
7014
7153
  */
7015
7154
  function PropertiesPanel(props) {
@@ -7024,6 +7163,7 @@ function PropertiesPanel(props) {
7024
7163
  descriptionLoaded,
7025
7164
  tooltipConfig,
7026
7165
  tooltipLoaded,
7166
+ feelPopupContainer,
7027
7167
  eventBus
7028
7168
  } = props;
7029
7169
 
@@ -7126,6 +7266,8 @@ function PropertiesPanel(props) {
7126
7266
  value: eventContext,
7127
7267
  children: jsx(FEELPopupRoot, {
7128
7268
  element: element,
7269
+ eventBus: eventBus,
7270
+ popupContainer: feelPopupContainer,
7129
7271
  children: jsxs("div", {
7130
7272
  class: "bio-properties-panel",
7131
7273
  children: [jsx(Header, {
@@ -7425,7 +7567,8 @@ function ListGroup(props) {
7425
7567
  class: classnames('bio-properties-panel-group-header', hasItems ? '' : 'empty', hasItems && open ? 'open' : '', sticky && open ? 'sticky' : ''),
7426
7568
  onClick: hasItems ? toggleOpen : noop$1,
7427
7569
  children: [jsx("div", {
7428
- title: label,
7570
+ title: props.tooltip ? null : label,
7571
+ "data-title": label,
7429
7572
  class: "bio-properties-panel-group-header-title",
7430
7573
  children: jsx(TooltipWrapper, {
7431
7574
  value: props.tooltip,
@@ -8063,12 +8206,51 @@ function isEdited(node) {
8063
8206
  function prefixId(id) {
8064
8207
  return `bio-properties-panel-${id}`;
8065
8208
  }
8209
+ class FeelPopupModule {
8210
+ constructor(eventBus) {
8211
+ this._eventBus = eventBus;
8212
+ }
8066
8213
 
8067
- /**
8068
- * @param {string} type
8069
- * @param {boolean} [strict]
8070
- *
8071
- * @returns {any}
8214
+ /**
8215
+ * Check if the FEEL popup is open.
8216
+ * @return {Boolean}
8217
+ */
8218
+ isOpen() {
8219
+ return this._eventBus.fire('feelPopup._isOpen');
8220
+ }
8221
+
8222
+ /**
8223
+ * Open the FEEL popup.
8224
+ *
8225
+ * @param {String} entryId
8226
+ * @param {Object} popupConfig
8227
+ * @param {HTMLElement} sourceElement
8228
+ */
8229
+ open(entryId, popupConfig, sourceElement) {
8230
+ return this._eventBus.fire('feelPopup._open', {
8231
+ entryId,
8232
+ popupConfig,
8233
+ sourceElement
8234
+ });
8235
+ }
8236
+
8237
+ /**
8238
+ * Close the FEEL popup.
8239
+ */
8240
+ close() {
8241
+ return this._eventBus.fire('feelPopup._close');
8242
+ }
8243
+ }
8244
+ FeelPopupModule.$inject = ['eventBus'];
8245
+ var index = {
8246
+ feelPopup: ['type', FeelPopupModule]
8247
+ };
8248
+
8249
+ /**
8250
+ * @param {string} type
8251
+ * @param {boolean} [strict]
8252
+ *
8253
+ * @returns {any}
8072
8254
  */
8073
8255
  function getService(type, strict) {}
8074
8256
  const PropertiesPanelContext = createContext({
@@ -8165,8 +8347,8 @@ const PropertiesPanelHeaderProvider = {
8165
8347
  }
8166
8348
  };
8167
8349
 
8168
- /**
8169
- * Provide placeholders for empty and multiple state.
8350
+ /**
8351
+ * Provide placeholders for empty and multiple state.
8170
8352
  */
8171
8353
  const PropertiesPanelPlaceholderProvider = {
8172
8354
  getEmpty: () => {
@@ -8238,10 +8420,10 @@ function useService (type, strict) {
8238
8420
  return getService(type, strict);
8239
8421
  }
8240
8422
 
8241
- /**
8242
- * Retrieve list of variables from the form schema.
8243
- *
8244
- * @returns { string[] } list of variables used in form schema
8423
+ /**
8424
+ * Retrieve list of variables from the form schema.
8425
+ *
8426
+ * @returns { string[] } list of variables used in form schema
8245
8427
  */
8246
8428
  function useVariables() {
8247
8429
  const form = useService('formEditor');
@@ -9892,14 +10074,14 @@ function Value(props) {
9892
10074
 
9893
10075
  // helpers //////////
9894
10076
 
9895
- /**
9896
- * Returns copy of object with updated value.
9897
- *
9898
- * @param {Object} properties
9899
- * @param {string} key
9900
- * @param {string} value
9901
- *
9902
- * @returns {Object}
10077
+ /**
10078
+ * Returns copy of object with updated value.
10079
+ *
10080
+ * @param {Object} properties
10081
+ * @param {string} key
10082
+ * @param {string} value
10083
+ *
10084
+ * @returns {Object}
9903
10085
  */
9904
10086
  function updateValue(properties, key, value) {
9905
10087
  return {
@@ -9908,14 +10090,14 @@ function updateValue(properties, key, value) {
9908
10090
  };
9909
10091
  }
9910
10092
 
9911
- /**
9912
- * Returns copy of object with updated key.
9913
- *
9914
- * @param {Object} properties
9915
- * @param {string} oldKey
9916
- * @param {string} newKey
9917
- *
9918
- * @returns {Object}
10093
+ /**
10094
+ * Returns copy of object with updated key.
10095
+ *
10096
+ * @param {Object} properties
10097
+ * @param {string} oldKey
10098
+ * @param {string} newKey
10099
+ *
10100
+ * @returns {Object}
9919
10101
  */
9920
10102
  function updateKey(properties, oldKey, newKey) {
9921
10103
  return Object.entries(properties).reduce((newProperties, entry) => {
@@ -10729,7 +10911,8 @@ function ValidationType(props) {
10729
10911
  id,
10730
10912
  label: 'Validation pattern',
10731
10913
  setValue,
10732
- getOptions: () => Object.values(VALIDATION_TYPE_OPTIONS)
10914
+ getOptions: () => Object.values(VALIDATION_TYPE_OPTIONS),
10915
+ 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
10733
10916
  });
10734
10917
  }
10735
10918
 
@@ -10747,13 +10930,13 @@ function ValuesGroups(field, editField) {
10747
10930
  };
10748
10931
  const valuesSourceId = `${fieldId}-valuesSource`;
10749
10932
 
10750
- /**
10751
- * @type {Array<Group|ListGroup>}
10933
+ /**
10934
+ * @type {Array<Group|ListGroup>}
10752
10935
  */
10753
10936
  const groups = [{
10754
10937
  id: valuesSourceId,
10755
10938
  label: 'Options source',
10756
- 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.',
10939
+ tooltip: getValuesTooltip(),
10757
10940
  component: Group,
10758
10941
  entries: ValuesSourceSelectEntry({
10759
10942
  ...context,
@@ -10798,6 +10981,12 @@ function ValuesGroups(field, editField) {
10798
10981
  return groups;
10799
10982
  }
10800
10983
 
10984
+ // helpers //////////
10985
+
10986
+ function getValuesTooltip() {
10987
+ 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.';
10988
+ }
10989
+
10801
10990
  function CustomPropertiesGroup(field, editField) {
10802
10991
  const {
10803
10992
  properties = {},
@@ -10863,13 +11052,13 @@ function CustomPropertiesGroup(field, editField) {
10863
11052
 
10864
11053
  // helpers //////////
10865
11054
 
10866
- /**
10867
- * Returns copy of object without key.
10868
- *
10869
- * @param {Object} properties
10870
- * @param {string} oldKey
10871
- *
10872
- * @returns {Object}
11055
+ /**
11056
+ * Returns copy of object without key.
11057
+ *
11058
+ * @param {Object} properties
11059
+ * @param {string} oldKey
11060
+ *
11061
+ * @returns {Object}
10873
11062
  */
10874
11063
  function removeKey(properties, oldKey) {
10875
11064
  return Object.entries(properties).reduce((newProperties, entry) => {
@@ -10955,6 +11144,10 @@ function FormPropertiesPanel(props) {
10955
11144
  const formEditor = injector.get('formEditor');
10956
11145
  const modeling = injector.get('modeling');
10957
11146
  const selectionModule = injector.get('selection');
11147
+ const propertiesPanelConfig = injector.get('config.propertiesPanel') || {};
11148
+ const {
11149
+ feelPopupContainer
11150
+ } = propertiesPanelConfig;
10958
11151
  const [state, setState] = useState({
10959
11152
  selectedFormField: selectionModule.get() || formEditor._getState().schema
10960
11153
  });
@@ -10972,9 +11165,9 @@ function FormPropertiesPanel(props) {
10972
11165
  });
10973
11166
  }, [eventBus, formEditor, selectionModule]);
10974
11167
  useLayoutEffect(() => {
10975
- /**
10976
- * TODO(pinussilvestrus): update with actual updated element,
10977
- * once we have a proper updater/change support
11168
+ /**
11169
+ * TODO(pinussilvestrus): update with actual updated element,
11170
+ * once we have a proper updater/change support
10978
11171
  */
10979
11172
  eventBus.on('changed', refresh);
10980
11173
  eventBus.on('import.done', refresh);
@@ -11004,7 +11197,8 @@ function FormPropertiesPanel(props) {
11004
11197
  eventBus: eventBus,
11005
11198
  groups: getGroups(selectedFormField, editField, getService),
11006
11199
  headerProvider: PropertiesPanelHeaderProvider,
11007
- placeholderProvider: PropertiesPanelPlaceholderProvider
11200
+ placeholderProvider: PropertiesPanelPlaceholderProvider,
11201
+ feelPopupContainer: feelPopupContainer
11008
11202
  })
11009
11203
  })
11010
11204
  });
@@ -11026,10 +11220,10 @@ class PropertiesPanelRenderer {
11026
11220
  });
11027
11221
  }
11028
11222
 
11029
- /**
11030
- * Attach the properties panel to a parent node.
11031
- *
11032
- * @param {HTMLElement} container
11223
+ /**
11224
+ * Attach the properties panel to a parent node.
11225
+ *
11226
+ * @param {HTMLElement} container
11033
11227
  */
11034
11228
  attachTo(container) {
11035
11229
  if (!container) {
@@ -11049,8 +11243,8 @@ class PropertiesPanelRenderer {
11049
11243
  this._eventBus.fire('propertiesPanel.attach');
11050
11244
  }
11051
11245
 
11052
- /**
11053
- * Detach the properties panel from its parent node.
11246
+ /**
11247
+ * Detach the properties panel from its parent node.
11054
11248
  */
11055
11249
  detach() {
11056
11250
  const parentNode = this._container.parentNode;
@@ -11076,14 +11270,15 @@ class PropertiesPanelRenderer {
11076
11270
  PropertiesPanelRenderer.$inject = ['config.propertiesPanel', 'injector', 'eventBus'];
11077
11271
 
11078
11272
  var PropertiesPanelModule = {
11273
+ __depends__: [index],
11079
11274
  __init__: ['propertiesPanel'],
11080
11275
  propertiesPanel: ['type', PropertiesPanelRenderer]
11081
11276
  };
11082
11277
 
11083
- /**
11084
- * Manages the rendering of visual plugins.
11085
- * @constructor
11086
- * @param {Object} eventBus - Event bus for the application.
11278
+ /**
11279
+ * Manages the rendering of visual plugins.
11280
+ * @constructor
11281
+ * @param {Object} eventBus - Event bus for the application.
11087
11282
  */
11088
11283
  class RenderInjector extends SectionModuleBase {
11089
11284
  constructor(eventBus) {
@@ -11092,10 +11287,10 @@ class RenderInjector extends SectionModuleBase {
11092
11287
  this.registeredRenderers = [];
11093
11288
  }
11094
11289
 
11095
- /**
11096
- * Inject a new renderer into the injector.
11097
- * @param {string} identifier - Identifier for the renderer.
11098
- * @param {Function} Renderer - The renderer function.
11290
+ /**
11291
+ * Inject a new renderer into the injector.
11292
+ * @param {string} identifier - Identifier for the renderer.
11293
+ * @param {Function} Renderer - The renderer function.
11099
11294
  */
11100
11295
  attachRenderer(identifier, Renderer) {
11101
11296
  this.registeredRenderers = [...this.registeredRenderers, {
@@ -11104,17 +11299,17 @@ class RenderInjector extends SectionModuleBase {
11104
11299
  }];
11105
11300
  }
11106
11301
 
11107
- /**
11108
- * Detach a renderer from the by key injector.
11109
- * @param {string} identifier - Identifier for the renderer.
11302
+ /**
11303
+ * Detach a renderer from the by key injector.
11304
+ * @param {string} identifier - Identifier for the renderer.
11110
11305
  */
11111
11306
  detachRenderer(identifier) {
11112
11307
  this.registeredRenderers = this.registeredRenderers.filter(r => r.identifier !== identifier);
11113
11308
  }
11114
11309
 
11115
- /**
11116
- * Returns the registered renderers.
11117
- * @returns {Array} Array of registered renderers.
11310
+ /**
11311
+ * Returns the registered renderers.
11312
+ * @returns {Array} Array of registered renderers.
11118
11313
  */
11119
11314
  fetchRenderers() {
11120
11315
  return this.registeredRenderers;
@@ -11148,48 +11343,48 @@ var ExpressionLanguageModule = {
11148
11343
 
11149
11344
  const ids = new Ids([32, 36, 1]);
11150
11345
 
11151
- /**
11152
- * @typedef { import('./types').Injector } Injector
11153
- * @typedef { import('./types').Module } Module
11154
- * @typedef { import('./types').Schema } Schema
11155
- *
11156
- * @typedef { import('./types').FormEditorOptions } FormEditorOptions
11157
- * @typedef { import('./types').FormEditorProperties } FormEditorProperties
11158
- *
11159
- * @typedef { {
11160
- * properties: FormEditorProperties,
11161
- * schema: Schema
11162
- * } } State
11163
- *
11164
- * @typedef { (type:string, priority:number, handler:Function) => void } OnEventWithPriority
11165
- * @typedef { (type:string, handler:Function) => void } OnEventWithOutPriority
11166
- * @typedef { OnEventWithPriority & OnEventWithOutPriority } OnEventType
11346
+ /**
11347
+ * @typedef { import('./types').Injector } Injector
11348
+ * @typedef { import('./types').Module } Module
11349
+ * @typedef { import('./types').Schema } Schema
11350
+ *
11351
+ * @typedef { import('./types').FormEditorOptions } FormEditorOptions
11352
+ * @typedef { import('./types').FormEditorProperties } FormEditorProperties
11353
+ *
11354
+ * @typedef { {
11355
+ * properties: FormEditorProperties,
11356
+ * schema: Schema
11357
+ * } } State
11358
+ *
11359
+ * @typedef { (type:string, priority:number, handler:Function) => void } OnEventWithPriority
11360
+ * @typedef { (type:string, handler:Function) => void } OnEventWithOutPriority
11361
+ * @typedef { OnEventWithPriority & OnEventWithOutPriority } OnEventType
11167
11362
  */
11168
11363
 
11169
- /**
11170
- * The form editor.
11364
+ /**
11365
+ * The form editor.
11171
11366
  */
11172
11367
  class FormEditor {
11173
- /**
11174
- * @constructor
11175
- * @param {FormEditorOptions} options
11368
+ /**
11369
+ * @constructor
11370
+ * @param {FormEditorOptions} options
11176
11371
  */
11177
11372
  constructor(options = {}) {
11178
- /**
11179
- * @public
11180
- * @type {OnEventType}
11373
+ /**
11374
+ * @public
11375
+ * @type {OnEventType}
11181
11376
  */
11182
11377
  this.on = this._onEvent;
11183
11378
 
11184
- /**
11185
- * @public
11186
- * @type {String}
11379
+ /**
11380
+ * @public
11381
+ * @type {String}
11187
11382
  */
11188
11383
  this._id = ids.next();
11189
11384
 
11190
- /**
11191
- * @private
11192
- * @type {Element}
11385
+ /**
11386
+ * @private
11387
+ * @type {Element}
11193
11388
  */
11194
11389
  this._container = createFormContainer();
11195
11390
  this._container.setAttribute('input-handle-modified-keys', 'z,y');
@@ -11200,15 +11395,15 @@ class FormEditor {
11200
11395
  properties = {}
11201
11396
  } = options;
11202
11397
 
11203
- /**
11204
- * @private
11205
- * @type {any}
11398
+ /**
11399
+ * @private
11400
+ * @type {any}
11206
11401
  */
11207
11402
  this.exporter = exporter;
11208
11403
 
11209
- /**
11210
- * @private
11211
- * @type {State}
11404
+ /**
11405
+ * @private
11406
+ * @type {State}
11212
11407
  */
11213
11408
  this._state = {
11214
11409
  properties,
@@ -11237,10 +11432,10 @@ class FormEditor {
11237
11432
  this._detach(false);
11238
11433
  }
11239
11434
 
11240
- /**
11241
- * @param {Schema} schema
11242
- *
11243
- * @return {Promise<{ warnings: Array<any> }>}
11435
+ /**
11436
+ * @param {Schema} schema
11437
+ *
11438
+ * @return {Promise<{ warnings: Array<any> }>}
11244
11439
  */
11245
11440
  importSchema(schema) {
11246
11441
  return new Promise((resolve, reject) => {
@@ -11269,15 +11464,15 @@ class FormEditor {
11269
11464
  });
11270
11465
  }
11271
11466
 
11272
- /**
11273
- * @returns {Schema}
11467
+ /**
11468
+ * @returns {Schema}
11274
11469
  */
11275
11470
  saveSchema() {
11276
11471
  return this.getSchema();
11277
11472
  }
11278
11473
 
11279
- /**
11280
- * @returns {Schema}
11474
+ /**
11475
+ * @returns {Schema}
11281
11476
  */
11282
11477
  getSchema() {
11283
11478
  const {
@@ -11286,8 +11481,8 @@ class FormEditor {
11286
11481
  return exportSchema(schema, this.exporter, schemaVersion);
11287
11482
  }
11288
11483
 
11289
- /**
11290
- * @param {Element|string} parentNode
11484
+ /**
11485
+ * @param {Element|string} parentNode
11291
11486
  */
11292
11487
  attachTo(parentNode) {
11293
11488
  if (!parentNode) {
@@ -11305,10 +11500,10 @@ class FormEditor {
11305
11500
  this._detach();
11306
11501
  }
11307
11502
 
11308
- /**
11309
- * @internal
11310
- *
11311
- * @param {boolean} [emit]
11503
+ /**
11504
+ * @internal
11505
+ *
11506
+ * @param {boolean} [emit]
11312
11507
  */
11313
11508
  _detach(emit = true) {
11314
11509
  const container = this._container,
@@ -11322,9 +11517,9 @@ class FormEditor {
11322
11517
  parentNode.removeChild(container);
11323
11518
  }
11324
11519
 
11325
- /**
11326
- * @param {any} property
11327
- * @param {any} value
11520
+ /**
11521
+ * @param {any} property
11522
+ * @param {any} value
11328
11523
  */
11329
11524
  setProperty(property, value) {
11330
11525
  const properties = set$1(this._getState().properties, [property], value);
@@ -11333,21 +11528,21 @@ class FormEditor {
11333
11528
  });
11334
11529
  }
11335
11530
 
11336
- /**
11337
- * @param {string} type
11338
- * @param {Function} handler
11531
+ /**
11532
+ * @param {string} type
11533
+ * @param {Function} handler
11339
11534
  */
11340
11535
  off(type, handler) {
11341
11536
  this.get('eventBus').off(type, handler);
11342
11537
  }
11343
11538
 
11344
- /**
11345
- * @internal
11346
- *
11347
- * @param {FormEditorOptions} options
11348
- * @param {Element} container
11349
- *
11350
- * @returns {Injector}
11539
+ /**
11540
+ * @internal
11541
+ *
11542
+ * @param {FormEditorOptions} options
11543
+ * @param {Element} container
11544
+ *
11545
+ * @returns {Injector}
11351
11546
  */
11352
11547
  _createInjector(options, container) {
11353
11548
  const {
@@ -11369,22 +11564,22 @@ class FormEditor {
11369
11564
  }, core, ...modules, ...additionalModules]);
11370
11565
  }
11371
11566
 
11372
- /**
11373
- * @internal
11567
+ /**
11568
+ * @internal
11374
11569
  */
11375
11570
  _emit(type, data) {
11376
11571
  this.get('eventBus').fire(type, data);
11377
11572
  }
11378
11573
 
11379
- /**
11380
- * @internal
11574
+ /**
11575
+ * @internal
11381
11576
  */
11382
11577
  _getState() {
11383
11578
  return this._state;
11384
11579
  }
11385
11580
 
11386
- /**
11387
- * @internal
11581
+ /**
11582
+ * @internal
11388
11583
  */
11389
11584
  _setState(state) {
11390
11585
  this._state = {
@@ -11394,15 +11589,15 @@ class FormEditor {
11394
11589
  this._emit('changed', this._getState());
11395
11590
  }
11396
11591
 
11397
- /**
11398
- * @internal
11592
+ /**
11593
+ * @internal
11399
11594
  */
11400
11595
  _getModules() {
11401
11596
  return [ModelingModule, EditorActionsModule, DraggingModule, KeyboardModule, SelectionModule, PaletteModule, ExpressionLanguageModule, MarkdownModule, PropertiesPanelModule, RenderInjectionModule];
11402
11597
  }
11403
11598
 
11404
- /**
11405
- * @internal
11599
+ /**
11600
+ * @internal
11406
11601
  */
11407
11602
  _onEvent(type, priority, handler) {
11408
11603
  this.get('eventBus').on(type, priority, handler);