@bpmn-io/properties-panel 0.23.0 → 0.24.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.esm.js CHANGED
@@ -8,52 +8,50 @@ import { createContext, createElement } from '../preact';
8
8
  import FeelEditor from '@bpmn-io/feel-editor';
9
9
 
10
10
  var ArrowIcon = function ArrowIcon(props) {
11
- return jsx("svg", { ...props,
11
+ return jsx("svg", {
12
+ ...props,
12
13
  children: jsx("path", {
13
14
  fillRule: "evenodd",
14
15
  d: "m11.657 8-4.95 4.95a1 1 0 0 1-1.414-1.414L8.828 8 5.293 4.464A1 1 0 1 1 6.707 3.05L11.657 8Z"
15
16
  })
16
17
  });
17
18
  };
18
-
19
19
  ArrowIcon.defaultProps = {
20
20
  xmlns: "http://www.w3.org/2000/svg",
21
21
  width: "16",
22
22
  height: "16"
23
23
  };
24
-
25
24
  var CreateIcon = function CreateIcon(props) {
26
- return jsx("svg", { ...props,
25
+ return jsx("svg", {
26
+ ...props,
27
27
  children: jsx("path", {
28
28
  fillRule: "evenodd",
29
29
  d: "M9 13V9h4a1 1 0 0 0 0-2H9V3a1 1 0 1 0-2 0v4H3a1 1 0 1 0 0 2h4v4a1 1 0 0 0 2 0Z"
30
30
  })
31
31
  });
32
32
  };
33
-
34
33
  CreateIcon.defaultProps = {
35
34
  xmlns: "http://www.w3.org/2000/svg",
36
35
  width: "16",
37
36
  height: "16"
38
37
  };
39
-
40
38
  var DeleteIcon = function DeleteIcon(props) {
41
- return jsx("svg", { ...props,
39
+ return jsx("svg", {
40
+ ...props,
42
41
  children: jsx("path", {
43
42
  fillRule: "evenodd",
44
43
  d: "M12 6v7c0 1.1-.4 1.55-1.5 1.55h-5C4.4 14.55 4 14.1 4 13V6h8Zm-1.5 1.5h-5v4.3c0 .66.5 1.2 1.111 1.2H9.39c.611 0 1.111-.54 1.111-1.2V7.5ZM13 3h-2l-1-1H6L5 3H3v1.5h10V3Z"
45
44
  })
46
45
  });
47
46
  };
48
-
49
47
  DeleteIcon.defaultProps = {
50
48
  xmlns: "http://www.w3.org/2000/svg",
51
49
  width: "16",
52
50
  height: "16"
53
51
  };
54
-
55
52
  var ExternalLinkIcon = function ExternalLinkIcon(props) {
56
- return jsx("svg", { ...props,
53
+ return jsx("svg", {
54
+ ...props,
57
55
  children: jsx("path", {
58
56
  fillRule: "evenodd",
59
57
  clipRule: "evenodd",
@@ -62,16 +60,15 @@ var ExternalLinkIcon = function ExternalLinkIcon(props) {
62
60
  })
63
61
  });
64
62
  };
65
-
66
63
  ExternalLinkIcon.defaultProps = {
67
64
  width: "16",
68
65
  height: "16",
69
66
  fill: "none",
70
67
  xmlns: "http://www.w3.org/2000/svg"
71
68
  };
72
-
73
69
  var FeelRequiredIcon = function FeelRequiredIcon(props) {
74
- return jsxs("svg", { ...props,
70
+ return jsxs("svg", {
71
+ ...props,
75
72
  children: [jsx("path", {
76
73
  d: "M5.8 7.06V5.95h4.307v1.11H5.8Zm0 3.071v-1.11h4.307v1.11H5.8Z",
77
74
  fill: "currentColor"
@@ -86,15 +83,14 @@ var FeelRequiredIcon = function FeelRequiredIcon(props) {
86
83
  })]
87
84
  });
88
85
  };
89
-
90
86
  FeelRequiredIcon.defaultProps = {
91
87
  viewBox: "0 0 16 16",
92
88
  fill: "none",
93
89
  xmlns: "http://www.w3.org/2000/svg"
94
90
  };
95
-
96
91
  var FeelOptionalIcon = function FeelOptionalIcon(props) {
97
- return jsxs("svg", { ...props,
92
+ return jsxs("svg", {
93
+ ...props,
98
94
  children: [jsx("path", {
99
95
  d: "M5.845 7.04V5.93h4.307v1.11H5.845Zm0 3.07V9h4.307v1.11H5.845Z",
100
96
  fill: "currentColor"
@@ -106,7 +102,6 @@ var FeelOptionalIcon = function FeelOptionalIcon(props) {
106
102
  })]
107
103
  });
108
104
  };
109
-
110
105
  FeelOptionalIcon.defaultProps = {
111
106
  viewBox: "0 0 16 16",
112
107
  fill: "none",
@@ -211,7 +206,6 @@ const LayoutContext = createContext({
211
206
  *
212
207
  * @returns {string}
213
208
  */
214
-
215
209
  function useDescriptionContext(id, element) {
216
210
  const {
217
211
  getDescriptionForId
@@ -232,28 +226,25 @@ function useError(id) {
232
226
  * @param {string} event
233
227
  * @param {Function} callback
234
228
  */
235
-
236
229
  function useEvent(event, callback, eventBus) {
237
230
  const eventContext = useContext(EventContext);
238
-
239
231
  if (!eventBus) {
240
232
  ({
241
233
  eventBus
242
234
  } = eventContext);
243
235
  }
236
+ const didMount = useRef(false);
244
237
 
245
- const didMount = useRef(false); // (1) subscribe immediately
246
-
238
+ // (1) subscribe immediately
247
239
  if (eventBus && !didMount.current) {
248
240
  eventBus.on(event, callback);
249
- } // (2) update subscription after inputs changed
250
-
241
+ }
251
242
 
243
+ // (2) update subscription after inputs changed
252
244
  useEffect(() => {
253
245
  if (eventBus && didMount.current) {
254
246
  eventBus.on(event, callback);
255
247
  }
256
-
257
248
  didMount.current = true;
258
249
  return () => {
259
250
  if (eventBus) {
@@ -264,6 +255,7 @@ function useEvent(event, callback, eventBus) {
264
255
  }
265
256
 
266
257
  const KEY_LENGTH = 6;
258
+
267
259
  /**
268
260
  * Create a persistent key factory for plain objects without id.
269
261
  *
@@ -283,21 +275,16 @@ const KEY_LENGTH = 6;
283
275
  * @param {any[]} dependencies
284
276
  * @returns {(element: object) => string}
285
277
  */
286
-
287
278
  function useKeyFactory(dependencies = []) {
288
279
  const map = useMemo(() => new Map(), dependencies);
289
-
290
280
  const getKey = el => {
291
281
  let key = map.get(el);
292
-
293
282
  if (!key) {
294
283
  key = Math.random().toString().slice(-KEY_LENGTH);
295
284
  map.set(el, key);
296
285
  }
297
-
298
286
  return key;
299
287
  };
300
-
301
288
  return getKey;
302
289
  }
303
290
 
@@ -316,7 +303,6 @@ function useKeyFactory(dependencies = []) {
316
303
  *
317
304
  * @returns {[ any, Function ]}
318
305
  */
319
-
320
306
  function useLayoutState(path, defaultValue) {
321
307
  const {
322
308
  getLayoutForKey,
@@ -324,14 +310,13 @@ function useLayoutState(path, defaultValue) {
324
310
  } = useContext(LayoutContext);
325
311
  const layoutForKey = getLayoutForKey(path, defaultValue);
326
312
  const [value, set] = useState(layoutForKey);
327
-
328
313
  const setState = newValue => {
329
314
  // (1) set component state
330
- set(newValue); // (2) set context
315
+ set(newValue);
331
316
 
317
+ // (2) set context
332
318
  setLayoutForKey(path, newValue);
333
319
  };
334
-
335
320
  return [value, setState];
336
321
  }
337
322
 
@@ -357,7 +342,6 @@ function usePrevious(value) {
357
342
  *
358
343
  * @returns {import('preact').Ref}
359
344
  */
360
-
361
345
  function useShowEntryEvent(id) {
362
346
  const {
363
347
  onShow
@@ -367,7 +351,6 @@ function useShowEntryEvent(id) {
367
351
  const onShowEntry = useCallback(event => {
368
352
  if (event.id === id) {
369
353
  onShow();
370
-
371
354
  if (!focus.current) {
372
355
  focus.current = true;
373
356
  }
@@ -378,11 +361,9 @@ function useShowEntryEvent(id) {
378
361
  if (isFunction(ref.current.focus)) {
379
362
  ref.current.focus();
380
363
  }
381
-
382
364
  if (isFunction(ref.current.select)) {
383
365
  ref.current.select();
384
366
  }
385
-
386
367
  focus.current = false;
387
368
  }
388
369
  });
@@ -405,17 +386,15 @@ function useShowEntryEvent(id) {
405
386
  * @param {string} scrollContainerSelector
406
387
  * @param {setSticky} setSticky
407
388
  */
408
-
409
389
  function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky) {
410
390
  useEffect(() => {
411
- const Observer = IntersectionObserver; // return early if IntersectionObserver is not available
391
+ const Observer = IntersectionObserver;
412
392
 
393
+ // return early if IntersectionObserver is not available
413
394
  if (!Observer) {
414
395
  return;
415
396
  }
416
-
417
397
  let observer;
418
-
419
398
  if (ref.current) {
420
399
  const scrollContainer = query(scrollContainerSelector);
421
400
  observer = new Observer(entries => {
@@ -433,9 +412,9 @@ function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky)
433
412
  threshold: [1]
434
413
  });
435
414
  observer.observe(ref.current);
436
- } // Unobserve if unmounted
437
-
415
+ }
438
416
 
417
+ // Unobserve if unmounted
439
418
  return () => {
440
419
  if (ref.current && observer) {
441
420
  observer.unobserve(ref.current);
@@ -458,7 +437,6 @@ function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky)
458
437
  * @param {Function} callback function with changing reference
459
438
  * @returns {Function} static function reference
460
439
  */
461
-
462
440
  function useStaticCallback(callback) {
463
441
  const callbackRef = useRef(callback);
464
442
  callbackRef.current = callback;
@@ -476,12 +454,11 @@ function Group(props) {
476
454
  const groupRef = useRef(null);
477
455
  const [open, setOpen] = useLayoutState(['groups', id, 'open'], shouldOpen);
478
456
  const onShow = useCallback(() => setOpen(true), [setOpen]);
479
-
480
457
  const toggleOpen = () => setOpen(!open);
481
-
482
458
  const [edited, setEdited] = useState(false);
483
- const [sticky, setSticky] = useState(false); // set edited state depending on all entries
459
+ const [sticky, setSticky] = useState(false);
484
460
 
461
+ // set edited state depending on all entries
485
462
  useEffect(() => {
486
463
  const hasOneEditedEntry = entries.find(entry => {
487
464
  const {
@@ -489,19 +466,19 @@ function Group(props) {
489
466
  isEdited
490
467
  } = entry;
491
468
  const entryNode = query(`[data-entry-id="${id}"]`);
492
-
493
469
  if (!isFunction(isEdited) || !entryNode) {
494
470
  return false;
495
471
  }
496
-
497
472
  const inputNode = query('.bio-properties-panel-input', entryNode);
498
473
  return isEdited(inputNode);
499
474
  });
500
475
  setEdited(hasOneEditedEntry);
501
- }, [entries]); // set css class when group is sticky to top
476
+ }, [entries]);
502
477
 
478
+ // set css class when group is sticky to top
503
479
  useStickyIntersectionObserver(groupRef, 'div.bio-properties-panel-scroll-container', setSticky);
504
- const propertiesPanelContext = { ...useContext(LayoutContext),
480
+ const propertiesPanelContext = {
481
+ ...useContext(LayoutContext),
505
482
  onShow
506
483
  };
507
484
  return jsxs("div", {
@@ -534,7 +511,8 @@ function Group(props) {
534
511
  component: Component,
535
512
  id
536
513
  } = entry;
537
- return createElement(Component, { ...entry,
514
+ return createElement(Component, {
515
+ ...entry,
538
516
  element: element,
539
517
  key: id
540
518
  });
@@ -543,7 +521,6 @@ function Group(props) {
543
521
  })]
544
522
  });
545
523
  }
546
-
547
524
  function DataMarker() {
548
525
  return jsx("div", {
549
526
  title: "Section contains data",
@@ -582,6 +559,7 @@ const DEFAULT_LAYOUT = {
582
559
  open: true
583
560
  };
584
561
  const DEFAULT_DESCRIPTION = {};
562
+
585
563
  /**
586
564
  * @typedef { {
587
565
  * component: import('preact').Component,
@@ -649,7 +627,6 @@ const DEFAULT_DESCRIPTION = {};
649
627
  * @param {Function} [props.descriptionLoaded]
650
628
  * @param {Object} [props.eventBus]
651
629
  */
652
-
653
630
  function PropertiesPanel(props) {
654
631
  const {
655
632
  element,
@@ -661,52 +638,46 @@ function PropertiesPanel(props) {
661
638
  descriptionConfig = {},
662
639
  descriptionLoaded,
663
640
  eventBus
664
- } = props; // set-up layout context
641
+ } = props;
665
642
 
643
+ // set-up layout context
666
644
  const [layout, setLayout] = useState(createLayout(layoutConfig));
667
645
  useEffect(() => {
668
646
  if (typeof layoutChanged === 'function') {
669
647
  layoutChanged(layout);
670
648
  }
671
649
  }, [layout, layoutChanged]);
672
-
673
650
  const getLayoutForKey = (key, defaultValue) => {
674
651
  return get(layout, key, defaultValue);
675
652
  };
676
-
677
653
  const setLayoutForKey = (key, config) => {
678
654
  const newLayout = assign({}, layout);
679
655
  set(newLayout, key, config);
680
656
  setLayout(newLayout);
681
657
  };
682
-
683
658
  const layoutContext = {
684
659
  layout,
685
660
  setLayout,
686
661
  getLayoutForKey,
687
662
  setLayoutForKey
688
- }; // set-up description context
663
+ };
689
664
 
665
+ // set-up description context
690
666
  const description = createDescriptionContext(descriptionConfig);
691
-
692
667
  if (typeof descriptionLoaded === 'function') {
693
668
  descriptionLoaded(description);
694
669
  }
695
-
696
670
  const getDescriptionForId = (id, element) => {
697
671
  return description[id] && description[id](element);
698
672
  };
699
-
700
673
  const descriptionContext = {
701
674
  description,
702
675
  getDescriptionForId
703
676
  };
704
677
  const [errors, setErrors] = useState({});
705
-
706
678
  const onSetErrors = ({
707
679
  errors
708
680
  }) => setErrors(errors);
709
-
710
681
  useEvent('propertiesPanel.setErrors', onSetErrors, eventBus);
711
682
  const errorsContext = {
712
683
  errors
@@ -716,19 +687,21 @@ function PropertiesPanel(props) {
716
687
  };
717
688
  const propertiesPanelContext = {
718
689
  element
719
- }; // empty state
690
+ };
720
691
 
692
+ // empty state
721
693
  if (placeholderProvider && !element) {
722
- return jsx(Placeholder, { ...placeholderProvider.getEmpty()
694
+ return jsx(Placeholder, {
695
+ ...placeholderProvider.getEmpty()
723
696
  });
724
- } // multiple state
725
-
697
+ }
726
698
 
699
+ // multiple state
727
700
  if (placeholderProvider && isArray(element)) {
728
- return jsx(Placeholder, { ...placeholderProvider.getMultiple()
701
+ return jsx(Placeholder, {
702
+ ...placeholderProvider.getMultiple()
729
703
  });
730
704
  }
731
-
732
705
  return jsx(LayoutContext.Provider, {
733
706
  value: propertiesPanelContext,
734
707
  children: jsx(ErrorsContext.Provider, {
@@ -751,7 +724,8 @@ function PropertiesPanel(props) {
751
724
  component: Component = Group,
752
725
  id
753
726
  } = group;
754
- return createElement(Component, { ...group,
727
+ return createElement(Component, {
728
+ ...group,
755
729
  key: id,
756
730
  element: element
757
731
  });
@@ -763,16 +737,19 @@ function PropertiesPanel(props) {
763
737
  })
764
738
  })
765
739
  });
766
- } // helpers //////////////////
740
+ }
741
+
742
+ // helpers //////////////////
767
743
 
768
744
  function createLayout(overrides) {
769
- return { ...DEFAULT_LAYOUT,
745
+ return {
746
+ ...DEFAULT_LAYOUT,
770
747
  ...overrides
771
748
  };
772
749
  }
773
-
774
750
  function createDescriptionContext(overrides) {
775
- return { ...DEFAULT_DESCRIPTION,
751
+ return {
752
+ ...DEFAULT_DESCRIPTION,
776
753
  ...overrides
777
754
  };
778
755
  }
@@ -786,24 +763,19 @@ function DropdownButton(props) {
786
763
  const dropdownRef = useRef(null);
787
764
  const menuRef = useRef(null);
788
765
  const [open, setOpen] = useState(false);
789
-
790
766
  const close = () => setOpen(false);
791
-
792
767
  function onDropdownToggle(event) {
793
768
  if (menuRef.current && menuRef.current.contains(event.target)) {
794
769
  return;
795
770
  }
796
-
797
771
  event.stopPropagation();
798
772
  setOpen(open => !open);
799
773
  }
800
-
801
774
  function onActionClick(event, action) {
802
775
  event.stopPropagation();
803
776
  close();
804
777
  action();
805
778
  }
806
-
807
779
  useGlobalClick([dropdownRef.current], () => close());
808
780
  return jsxs("div", {
809
781
  class: classnames('bio-properties-panel-dropdown-button', {
@@ -821,7 +793,6 @@ function DropdownButton(props) {
821
793
  })]
822
794
  });
823
795
  }
824
-
825
796
  function MenuItem({
826
797
  item,
827
798
  onClick
@@ -831,7 +802,6 @@ function MenuItem({
831
802
  class: "bio-properties-panel-dropdown-button__menu-item bio-properties-panel-dropdown-button__menu-item--separator"
832
803
  });
833
804
  }
834
-
835
805
  if (item.action) {
836
806
  return jsx("button", {
837
807
  class: "bio-properties-panel-dropdown-button__menu-item bio-properties-panel-dropdown-button__menu-item--actionable",
@@ -839,19 +809,17 @@ function MenuItem({
839
809
  children: item.entry
840
810
  });
841
811
  }
842
-
843
812
  return jsx("div", {
844
813
  class: "bio-properties-panel-dropdown-button__menu-item",
845
814
  children: item.entry
846
815
  });
847
816
  }
817
+
848
818
  /**
849
819
  *
850
820
  * @param {Array<null | Element>} ignoredElements
851
821
  * @param {Function} callback
852
822
  */
853
-
854
-
855
823
  function useGlobalClick(ignoredElements, callback) {
856
824
  useEffect(() => {
857
825
  /**
@@ -861,10 +829,8 @@ function useGlobalClick(ignoredElements, callback) {
861
829
  if (ignoredElements.some(element => element && element.contains(event.target))) {
862
830
  return;
863
831
  }
864
-
865
832
  callback();
866
833
  }
867
-
868
834
  document.addEventListener('click', listener, {
869
835
  capture: true
870
836
  });
@@ -881,7 +847,8 @@ function HeaderButton(props) {
881
847
  onClick = () => {},
882
848
  ...otherProps
883
849
  } = props;
884
- return jsx("button", { ...otherProps,
850
+ return jsx("button", {
851
+ ...otherProps,
885
852
  onClick: onClick,
886
853
  class: classnames('bio-properties-panel-group-header-button', classname),
887
854
  children: children
@@ -898,22 +865,21 @@ function CollapsibleEntry(props) {
898
865
  remove
899
866
  } = props;
900
867
  const [open, setOpen] = useState(shouldOpen);
901
-
902
868
  const toggleOpen = () => setOpen(!open);
903
-
904
869
  const {
905
870
  onShow
906
871
  } = useContext(LayoutContext);
907
- const propertiesPanelContext = { ...useContext(LayoutContext),
872
+ const propertiesPanelContext = {
873
+ ...useContext(LayoutContext),
908
874
  onShow: useCallback(() => {
909
875
  setOpen(true);
910
-
911
876
  if (isFunction(onShow)) {
912
877
  onShow();
913
878
  }
914
879
  }, [onShow, setOpen])
915
- }; // todo(pinussilvestrus): translate once we have a translate mechanism for the core
880
+ };
916
881
 
882
+ // todo(pinussilvestrus): translate once we have a translate mechanism for the core
917
883
  const placeholderLabel = '<empty>';
918
884
  return jsxs("div", {
919
885
  "data-entry-id": id,
@@ -946,7 +912,8 @@ function CollapsibleEntry(props) {
946
912
  component: Component,
947
913
  id
948
914
  } = entry;
949
- return createElement(Component, { ...entry,
915
+ return createElement(Component, {
916
+ ...entry,
950
917
  element: element,
951
918
  key: id
952
919
  });
@@ -960,13 +927,13 @@ function ListItem(props) {
960
927
  const {
961
928
  autoFocusEntry,
962
929
  autoOpen
963
- } = props; // focus specified entry on auto open
930
+ } = props;
964
931
 
932
+ // focus specified entry on auto open
965
933
  useEffect(() => {
966
934
  if (autoOpen && autoFocusEntry) {
967
935
  const entry = query(`[data-entry-id="${autoFocusEntry}"]`);
968
936
  const focusableInput = query('.bio-properties-panel-input', entry);
969
-
970
937
  if (focusableInput) {
971
938
  if (isFunction(focusableInput.select)) {
972
939
  focusableInput.select();
@@ -978,18 +945,18 @@ function ListItem(props) {
978
945
  }, [autoOpen, autoFocusEntry]);
979
946
  return jsx("div", {
980
947
  class: "bio-properties-panel-list-item",
981
- children: jsx(CollapsibleEntry, { ...props,
948
+ children: jsx(CollapsibleEntry, {
949
+ ...props,
982
950
  open: autoOpen
983
951
  })
984
952
  });
985
953
  }
986
954
 
987
955
  const noop$2 = () => {};
956
+
988
957
  /**
989
958
  * @param {import('../PropertiesPanel').ListGroupDefinition} props
990
959
  */
991
-
992
-
993
960
  function ListGroup(props) {
994
961
  const {
995
962
  add,
@@ -1009,20 +976,23 @@ function ListGroup(props) {
1009
976
  const prevItems = usePrevious(items);
1010
977
  const prevElement = usePrevious(element);
1011
978
  const elementChanged = element !== prevElement;
1012
- const shouldHandleEffects = !elementChanged && (shouldSort || shouldOpen); // reset initial ordering when element changes (before first render)
979
+ const shouldHandleEffects = !elementChanged && (shouldSort || shouldOpen);
1013
980
 
981
+ // reset initial ordering when element changes (before first render)
1014
982
  if (elementChanged) {
1015
983
  setOrdering(createOrdering(shouldSort ? sortItems(items) : items));
1016
- } // keep ordering in sync to items - and open changes
1017
- // (0) set initial ordering from given items
984
+ }
1018
985
 
986
+ // keep ordering in sync to items - and open changes
1019
987
 
988
+ // (0) set initial ordering from given items
1020
989
  useEffect(() => {
1021
990
  if (!prevItems || !shouldSort) {
1022
991
  setOrdering(createOrdering(items));
1023
992
  }
1024
- }, [items, element]); // (1) items were added
993
+ }, [items, element]);
1025
994
 
995
+ // (1) items were added
1026
996
  useEffect(() => {
1027
997
  if (shouldHandleEffects && prevItems && items.length > prevItems.length) {
1028
998
  let add = [];
@@ -1031,38 +1001,40 @@ function ListGroup(props) {
1031
1001
  add.push(item.id);
1032
1002
  }
1033
1003
  });
1034
- let newOrdering = ordering; // open if not open and configured
1004
+ let newOrdering = ordering;
1035
1005
 
1006
+ // open if not open and configured
1036
1007
  if (!open && shouldOpen) {
1037
- toggleOpen(); // if I opened and I should sort, then sort items
1008
+ toggleOpen();
1038
1009
 
1010
+ // if I opened and I should sort, then sort items
1039
1011
  if (shouldSort) {
1040
1012
  newOrdering = createOrdering(sortItems(items));
1041
1013
  }
1042
- } // add new items on top or bottom depending on sorting behavior
1043
-
1014
+ }
1044
1015
 
1016
+ // add new items on top or bottom depending on sorting behavior
1045
1017
  newOrdering = newOrdering.filter(item => !add.includes(item));
1046
-
1047
1018
  if (shouldSort) {
1048
1019
  newOrdering.unshift(...add);
1049
1020
  } else {
1050
1021
  newOrdering.push(...add);
1051
1022
  }
1052
-
1053
1023
  setOrdering(newOrdering);
1054
1024
  setNewItemAdded(true);
1055
1025
  } else {
1056
1026
  setNewItemAdded(false);
1057
1027
  }
1058
- }, [items, open, shouldHandleEffects]); // (2) sort items on open if shouldSort is set
1028
+ }, [items, open, shouldHandleEffects]);
1059
1029
 
1030
+ // (2) sort items on open if shouldSort is set
1060
1031
  useEffect(() => {
1061
1032
  if (shouldSort && open && !newItemAdded) {
1062
1033
  setOrdering(createOrdering(sortItems(items)));
1063
1034
  }
1064
- }, [open, shouldSort]); // (3) items were deleted
1035
+ }, [open, shouldSort]);
1065
1036
 
1037
+ // (3) items were deleted
1066
1038
  useEffect(() => {
1067
1039
  if (shouldHandleEffects && prevItems && items.length < prevItems.length) {
1068
1040
  let keep = [];
@@ -1073,14 +1045,14 @@ function ListGroup(props) {
1073
1045
  });
1074
1046
  setOrdering(keep);
1075
1047
  }
1076
- }, [items, shouldHandleEffects]); // set css class when group is sticky to top
1048
+ }, [items, shouldHandleEffects]);
1077
1049
 
1050
+ // set css class when group is sticky to top
1078
1051
  useStickyIntersectionObserver(groupRef, 'div.bio-properties-panel-scroll-container', setSticky);
1079
-
1080
1052
  const toggleOpen = () => setOpen(!open);
1081
-
1082
1053
  const hasItems = !!items.length;
1083
- const propertiesPanelContext = { ...useContext(LayoutContext),
1054
+ const propertiesPanelContext = {
1055
+ ...useContext(LayoutContext),
1084
1056
  onShow
1085
1057
  };
1086
1058
  return jsxs("div", {
@@ -1122,17 +1094,17 @@ function ListGroup(props) {
1122
1094
  value: propertiesPanelContext,
1123
1095
  children: ordering.map((o, index) => {
1124
1096
  const item = getItem(items, o);
1125
-
1126
1097
  if (!item) {
1127
1098
  return;
1128
1099
  }
1129
-
1130
1100
  const {
1131
1101
  id
1132
- } = item; // if item was added, open first or last item based on ordering
1102
+ } = item;
1133
1103
 
1104
+ // if item was added, open first or last item based on ordering
1134
1105
  const autoOpen = newItemAdded && (shouldSort ? index === 0 : index === ordering.length - 1);
1135
- return createElement(ListItem, { ...item,
1106
+ return createElement(ListItem, {
1107
+ ...item,
1136
1108
  autoOpen: autoOpen,
1137
1109
  element: element,
1138
1110
  index: index,
@@ -1142,20 +1114,19 @@ function ListGroup(props) {
1142
1114
  })
1143
1115
  })]
1144
1116
  });
1145
- } // helpers ////////////////////
1117
+ }
1118
+
1119
+ // helpers ////////////////////
1146
1120
 
1147
1121
  /**
1148
1122
  * Sorts given items alphanumeric by label
1149
1123
  */
1150
-
1151
1124
  function sortItems(items) {
1152
1125
  return sortBy(items, i => i.label.toLowerCase());
1153
1126
  }
1154
-
1155
1127
  function getItem(items, id) {
1156
1128
  return find(items, i => i.id === id);
1157
1129
  }
1158
-
1159
1130
  function createOrdering(items) {
1160
1131
  return items.map(i => i.id);
1161
1132
  }
@@ -1168,7 +1139,6 @@ function Description(props) {
1168
1139
  } = props;
1169
1140
  const contextDescription = useDescriptionContext(forId, element);
1170
1141
  const description = value || contextDescription;
1171
-
1172
1142
  if (description) {
1173
1143
  return jsx("div", {
1174
1144
  class: "bio-properties-panel-description",
@@ -1183,26 +1153,24 @@ function Checkbox(props) {
1183
1153
  label,
1184
1154
  onChange,
1185
1155
  disabled,
1186
- value = false
1156
+ value = false,
1157
+ onFocus,
1158
+ onBlur
1187
1159
  } = props;
1188
1160
  const [localValue, setLocalValue] = useState(value);
1189
-
1190
1161
  const handleChangeCallback = ({
1191
1162
  target
1192
1163
  }) => {
1193
1164
  onChange(target.checked);
1194
1165
  };
1195
-
1196
1166
  const handleChange = e => {
1197
1167
  handleChangeCallback(e);
1198
1168
  setLocalValue(e.target.value);
1199
1169
  };
1200
-
1201
1170
  useEffect(() => {
1202
1171
  if (value === localValue) {
1203
1172
  return;
1204
1173
  }
1205
-
1206
1174
  setLocalValue(value);
1207
1175
  }, [value]);
1208
1176
  const ref = useShowEntryEvent(id);
@@ -1212,6 +1180,8 @@ function Checkbox(props) {
1212
1180
  ref: ref,
1213
1181
  id: prefixId$7(id),
1214
1182
  name: id,
1183
+ onFocus: onFocus,
1184
+ onBlur: onBlur,
1215
1185
  type: "checkbox",
1216
1186
  class: "bio-properties-panel-input",
1217
1187
  onChange: handleChange,
@@ -1224,6 +1194,7 @@ function Checkbox(props) {
1224
1194
  })]
1225
1195
  });
1226
1196
  }
1197
+
1227
1198
  /**
1228
1199
  * @param {Object} props
1229
1200
  * @param {Object} props.element
@@ -1232,10 +1203,10 @@ function Checkbox(props) {
1232
1203
  * @param {String} props.label
1233
1204
  * @param {Function} props.getValue
1234
1205
  * @param {Function} props.setValue
1206
+ * @param {Function} props.onFocus
1207
+ * @param {Function} props.onBlur
1235
1208
  * @param {boolean} [props.disabled]
1236
1209
  */
1237
-
1238
-
1239
1210
  function CheckboxEntry(props) {
1240
1211
  const {
1241
1212
  element,
@@ -1244,7 +1215,9 @@ function CheckboxEntry(props) {
1244
1215
  label,
1245
1216
  getValue,
1246
1217
  setValue,
1247
- disabled
1218
+ disabled,
1219
+ onFocus,
1220
+ onBlur
1248
1221
  } = props;
1249
1222
  const value = getValue(element);
1250
1223
  const error = useError(id);
@@ -1256,6 +1229,8 @@ function CheckboxEntry(props) {
1256
1229
  id: id,
1257
1230
  label: label,
1258
1231
  onChange: setValue,
1232
+ onFocus: onFocus,
1233
+ onBlur: onBlur,
1259
1234
  value: value
1260
1235
  }, element), error && jsx("div", {
1261
1236
  class: "bio-properties-panel-error",
@@ -1269,7 +1244,9 @@ function CheckboxEntry(props) {
1269
1244
  }
1270
1245
  function isEdited$7(node) {
1271
1246
  return node && !!node.checked;
1272
- } // helpers /////////////////
1247
+ }
1248
+
1249
+ // helpers /////////////////
1273
1250
 
1274
1251
  function prefixId$7(id) {
1275
1252
  return `bio-properties-panel-${id}`;
@@ -1278,11 +1255,14 @@ function prefixId$7(id) {
1278
1255
  const useBufferedFocus = function (editor, ref) {
1279
1256
  const [buffer, setBuffer] = useState(undefined);
1280
1257
  ref.current = useMemo(() => ({
1281
- focus: argument => {
1258
+ focus: offset => {
1282
1259
  if (editor) {
1283
- editor.focus(argument);
1260
+ editor.focus(offset);
1284
1261
  } else {
1285
- setBuffer(argument);
1262
+ if (typeof offset === 'undefined') {
1263
+ offset = Infinity;
1264
+ }
1265
+ setBuffer(offset);
1286
1266
  }
1287
1267
  }
1288
1268
  }), [editor]);
@@ -1293,7 +1273,6 @@ const useBufferedFocus = function (editor, ref) {
1293
1273
  }
1294
1274
  }, [editor, buffer]);
1295
1275
  };
1296
-
1297
1276
  const CodeEditor = forwardRef((props, ref) => {
1298
1277
  const {
1299
1278
  value,
@@ -1313,25 +1292,22 @@ const CodeEditor = forwardRef((props, ref) => {
1313
1292
  });
1314
1293
  useEffect(() => {
1315
1294
  let editor;
1295
+
1316
1296
  /* Trigger FEEL toggle when
1317
1297
  *
1318
1298
  * - `backspace` is pressed
1319
1299
  * - AND the cursor is at the beginning of the input
1320
1300
  */
1321
-
1322
1301
  const onKeyDown = e => {
1323
1302
  if (e.key !== 'Backspace' || !editor) {
1324
1303
  return;
1325
1304
  }
1326
-
1327
1305
  const selection = editor.getSelection();
1328
1306
  const range = selection.ranges[selection.mainIndex];
1329
-
1330
1307
  if (range.from === 0 && range.to === 0) {
1331
1308
  onFeelToggle();
1332
1309
  }
1333
1310
  };
1334
-
1335
1311
  editor = new FeelEditor({
1336
1312
  container: inputRef.current,
1337
1313
  onChange: handleInput,
@@ -1346,24 +1322,26 @@ const CodeEditor = forwardRef((props, ref) => {
1346
1322
  inputRef.current.innerHTML = '';
1347
1323
  setEditor(null);
1348
1324
  };
1349
- }, [variables]);
1325
+ }, []);
1350
1326
  useEffect(() => {
1351
1327
  if (!editor) {
1352
1328
  return;
1353
1329
  }
1354
-
1355
1330
  if (value === localValue) {
1356
1331
  return;
1357
1332
  }
1358
-
1359
1333
  editor.setValue(value);
1360
1334
  setLocalValue(value);
1361
1335
  }, [value]);
1362
-
1336
+ useEffect(() => {
1337
+ if (!editor) {
1338
+ return;
1339
+ }
1340
+ editor.setVariables(variables);
1341
+ }, [variables]);
1363
1342
  const handleClick = () => {
1364
1343
  ref.current.focus();
1365
1344
  };
1366
-
1367
1345
  return jsx("div", {
1368
1346
  class: classnames('bio-properties-panel-feel-editor-container', disabled ? 'disabled' : null),
1369
1347
  children: jsx("div", {
@@ -1379,11 +1357,9 @@ function FeelIndicator(props) {
1379
1357
  const {
1380
1358
  active
1381
1359
  } = props;
1382
-
1383
1360
  if (!active) {
1384
1361
  return null;
1385
1362
  }
1386
-
1387
1363
  return jsx("span", {
1388
1364
  class: "bio-properties-panel-feel-indicator",
1389
1365
  children: "="
@@ -1391,13 +1367,12 @@ function FeelIndicator(props) {
1391
1367
  }
1392
1368
 
1393
1369
  const noop$1 = () => {};
1370
+
1394
1371
  /**
1395
1372
  * @param {Object} props
1396
1373
  * @param {Object} props.label
1397
1374
  * @param {String} props.feel
1398
1375
  */
1399
-
1400
-
1401
1376
  function FeelIcon(props) {
1402
1377
  const {
1403
1378
  label,
@@ -1408,15 +1383,14 @@ function FeelIcon(props) {
1408
1383
  } = props;
1409
1384
  const feelRequiredLabel = ' must be a FEEL expression';
1410
1385
  const feelOptionalLabel = ' can optionally be a FEEL expression';
1411
-
1412
1386
  const handleClick = e => {
1413
- onClick(e); // when pointer event was created from keyboard, keep focus on button
1387
+ onClick(e);
1414
1388
 
1389
+ // when pointer event was created from keyboard, keep focus on button
1415
1390
  if (!e.pointerType) {
1416
1391
  e.stopPropagation();
1417
1392
  }
1418
1393
  };
1419
-
1420
1394
  return jsx("button", {
1421
1395
  class: classnames('bio-properties-panel-feel-icon', active ? 'active' : null, feel === 'required' ? 'required' : 'optional'),
1422
1396
  onClick: handleClick,
@@ -1427,7 +1401,6 @@ function FeelIcon(props) {
1427
1401
  }
1428
1402
 
1429
1403
  const noop = () => {};
1430
-
1431
1404
  function FeelTextfield(props) {
1432
1405
  const {
1433
1406
  debounce,
@@ -1438,6 +1411,7 @@ function FeelTextfield(props) {
1438
1411
  feel,
1439
1412
  value = '',
1440
1413
  disabled = false,
1414
+ variables,
1441
1415
  OptionalComponent = OptionalFeelInput
1442
1416
  } = props;
1443
1417
  const [localValue, _setLocalValue] = useState(value);
@@ -1446,66 +1420,54 @@ function FeelTextfield(props) {
1446
1420
  const feelActive = localValue.startsWith('=') || feel === 'required';
1447
1421
  const feelOnlyValue = localValue.startsWith('=') ? localValue.substring(1) : localValue;
1448
1422
  const [focus, _setFocus] = useState(undefined);
1449
-
1450
1423
  const setFocus = (offset = 0) => {
1451
- const hasFocus = containerRef.current.contains(document.activeElement); // Keep carret position if it is already focused, otherwise focus at the end
1424
+ const hasFocus = containerRef.current.contains(document.activeElement);
1452
1425
 
1426
+ // Keep caret position if it is already focused, otherwise focus at the end
1453
1427
  const position = hasFocus ? document.activeElement.selectionStart : Infinity;
1454
-
1455
1428
  _setFocus(position + offset);
1456
1429
  };
1457
-
1458
1430
  const handleInputCallback = useMemo(() => {
1459
1431
  return debounce(newValue => {
1460
1432
  onInput(newValue);
1461
1433
  });
1462
1434
  }, [onInput, debounce]);
1463
-
1464
1435
  const setLocalValue = newValue => {
1465
1436
  _setLocalValue(newValue);
1466
-
1467
1437
  if (!newValue || newValue === '=') {
1468
1438
  handleInputCallback(undefined);
1469
1439
  } else {
1470
1440
  handleInputCallback(newValue);
1471
1441
  }
1472
1442
  };
1473
-
1474
1443
  const handleFeelToggle = useStaticCallback(() => {
1475
1444
  if (feel === 'required') {
1476
1445
  return;
1477
1446
  }
1478
-
1479
1447
  if (!feelActive) {
1480
1448
  setLocalValue('=' + localValue);
1481
1449
  } else {
1482
1450
  setLocalValue(feelOnlyValue);
1483
1451
  }
1484
1452
  });
1485
-
1486
1453
  const handleLocalInput = newValue => {
1487
1454
  if (feelActive) {
1488
1455
  newValue = '=' + newValue;
1489
1456
  }
1490
-
1491
1457
  if (newValue === localValue) {
1492
1458
  return;
1493
1459
  }
1494
-
1495
1460
  setLocalValue(newValue);
1496
-
1497
1461
  if (!feelActive && newValue.startsWith('=')) {
1498
1462
  // focus is behind `=` sign that will be removed
1499
1463
  setFocus(-1);
1500
1464
  }
1501
1465
  };
1502
-
1503
1466
  const handleLint = useStaticCallback(lint => {
1504
1467
  if (!(lint && lint.length)) {
1505
1468
  onError(undefined);
1506
1469
  return;
1507
1470
  }
1508
-
1509
1471
  const error = lint[0];
1510
1472
  const message = `${error.source}: ${error.message}`;
1511
1473
  onError(message);
@@ -1513,40 +1475,35 @@ function FeelTextfield(props) {
1513
1475
  useEffect(() => {
1514
1476
  if (typeof focus !== 'undefined') {
1515
1477
  editorRef.current.focus(focus);
1516
-
1517
1478
  _setFocus(undefined);
1518
1479
  }
1519
1480
  }, [focus]);
1520
1481
  useEffect(() => {
1521
1482
  if (value === localValue) {
1522
1483
  return;
1523
- } // External value change removed content => keep FEEL configuration
1524
-
1484
+ }
1525
1485
 
1486
+ // External value change removed content => keep FEEL configuration
1526
1487
  if (!value) {
1527
1488
  setLocalValue(feelActive ? '=' : '');
1528
1489
  return;
1529
1490
  }
1530
-
1531
1491
  setLocalValue(value);
1532
- }, [value]); // copy-paste integration
1492
+ }, [value]);
1533
1493
 
1494
+ // copy-paste integration
1534
1495
  useEffect(() => {
1535
1496
  const copyHandler = event => {
1536
1497
  if (!feelActive) {
1537
1498
  return;
1538
1499
  }
1539
-
1540
1500
  event.clipboardData.setData('application/FEEL', event.clipboardData.getData('text'));
1541
1501
  };
1542
-
1543
1502
  const pasteHandler = event => {
1544
1503
  if (feelActive) {
1545
1504
  return;
1546
1505
  }
1547
-
1548
1506
  const data = event.clipboardData.getData('application/FEEL');
1549
-
1550
1507
  if (data) {
1551
1508
  setTimeout(() => {
1552
1509
  handleFeelToggle();
@@ -1554,7 +1511,6 @@ function FeelTextfield(props) {
1554
1511
  });
1555
1512
  }
1556
1513
  };
1557
-
1558
1514
  containerRef.current.addEventListener('copy', copyHandler);
1559
1515
  containerRef.current.addEventListener('cut', copyHandler);
1560
1516
  containerRef.current.addEventListener('paste', pasteHandler);
@@ -1594,9 +1550,10 @@ function FeelTextfield(props) {
1594
1550
  },
1595
1551
  onLint: handleLint,
1596
1552
  value: feelOnlyValue,
1597
- variables: props.variables,
1553
+ variables: variables,
1598
1554
  ref: editorRef
1599
- }) : jsx(OptionalComponent, { ...props,
1555
+ }) : jsx(OptionalComponent, {
1556
+ ...props,
1600
1557
  onInput: handleLocalInput,
1601
1558
  value: localValue,
1602
1559
  ref: editorRef
@@ -1604,32 +1561,30 @@ function FeelTextfield(props) {
1604
1561
  })]
1605
1562
  });
1606
1563
  }
1607
-
1608
1564
  const OptionalFeelInput = forwardRef((props, ref) => {
1609
1565
  const {
1610
1566
  id,
1611
1567
  disabled,
1612
1568
  onInput,
1613
- value
1569
+ value,
1570
+ onFocus,
1571
+ onBlur
1614
1572
  } = props;
1615
- const inputRef = useRef(); // To be consistent with the FEEL editor, set focus at start of input
1616
- // this ensures clean editing experience when switching with the keyboard
1573
+ const inputRef = useRef();
1617
1574
 
1575
+ // To be consistent with the FEEL editor, set focus at start of input
1576
+ // this ensures clean editing experience when switching with the keyboard
1618
1577
  ref.current = {
1619
1578
  focus: position => {
1620
1579
  const input = inputRef.current;
1621
-
1622
1580
  if (!input) {
1623
1581
  return;
1624
1582
  }
1625
-
1626
1583
  input.focus();
1627
-
1628
1584
  if (typeof position === 'number') {
1629
1585
  if (position > value.length) {
1630
1586
  position = value.length;
1631
1587
  }
1632
-
1633
1588
  input.setSelectionRange(position, position);
1634
1589
  }
1635
1590
  }
@@ -1644,8 +1599,8 @@ const OptionalFeelInput = forwardRef((props, ref) => {
1644
1599
  disabled: disabled,
1645
1600
  class: "bio-properties-panel-input",
1646
1601
  onInput: e => onInput(e.target.value),
1647
- onFocus: props.onFocus,
1648
- onBlur: props.onBlur,
1602
+ onFocus: onFocus,
1603
+ onBlur: onBlur,
1649
1604
  value: value || ''
1650
1605
  });
1651
1606
  });
@@ -1654,19 +1609,20 @@ const OptionalFeelTextArea = forwardRef((props, ref) => {
1654
1609
  id,
1655
1610
  disabled,
1656
1611
  onInput,
1657
- value
1612
+ value,
1613
+ onFocus,
1614
+ onBlur
1658
1615
  } = props;
1659
- const inputRef = useRef(); // To be consistent with the FEEL editor, set focus at start of input
1660
- // this ensures clean editing experience when switching with the keyboard
1616
+ const inputRef = useRef();
1661
1617
 
1618
+ // To be consistent with the FEEL editor, set focus at start of input
1619
+ // this ensures clean editing experience when switching with the keyboard
1662
1620
  ref.current = {
1663
1621
  focus: () => {
1664
1622
  const input = inputRef.current;
1665
-
1666
1623
  if (!input) {
1667
1624
  return;
1668
1625
  }
1669
-
1670
1626
  input.focus();
1671
1627
  input.setSelectionRange(0, 0);
1672
1628
  }
@@ -1681,11 +1637,12 @@ const OptionalFeelTextArea = forwardRef((props, ref) => {
1681
1637
  disabled: disabled,
1682
1638
  class: "bio-properties-panel-input",
1683
1639
  onInput: e => onInput(e.target.value),
1684
- onFocus: props.onFocus,
1685
- onBlur: props.onBlur,
1640
+ onFocus: onFocus,
1641
+ onBlur: onBlur,
1686
1642
  value: value || ''
1687
1643
  });
1688
1644
  });
1645
+
1689
1646
  /**
1690
1647
  * @param {Object} props
1691
1648
  * @param {Object} props.element
@@ -1698,7 +1655,6 @@ const OptionalFeelTextArea = forwardRef((props, ref) => {
1698
1655
  * @param {Function} props.setValue
1699
1656
  * @param {Function} props.validate
1700
1657
  */
1701
-
1702
1658
  function FeelEntry(props) {
1703
1659
  const {
1704
1660
  element,
@@ -1711,7 +1667,11 @@ function FeelEntry(props) {
1711
1667
  getValue,
1712
1668
  setValue,
1713
1669
  validate,
1714
- show = noop
1670
+ show = noop,
1671
+ example,
1672
+ variables,
1673
+ onFocus,
1674
+ onBlur
1715
1675
  } = props;
1716
1676
  const [cachedInvalidValue, setCachedInvalidValue] = useState(null);
1717
1677
  const [validationError, setValidationError] = useState(null);
@@ -1726,11 +1686,9 @@ function FeelEntry(props) {
1726
1686
  }, [value]);
1727
1687
  const onInput = useStaticCallback(newValue => {
1728
1688
  let newValidationError = null;
1729
-
1730
1689
  if (isFunction(validate)) {
1731
1690
  newValidationError = validate(newValue) || null;
1732
1691
  }
1733
-
1734
1692
  if (newValidationError) {
1735
1693
  setCachedInvalidValue(newValue);
1736
1694
  } else {
@@ -1739,19 +1697,16 @@ function FeelEntry(props) {
1739
1697
  setValue(newValue);
1740
1698
  }
1741
1699
  }
1742
-
1743
1700
  setValidationError(newValidationError);
1744
1701
  });
1745
1702
  const onError = useCallback(err => {
1746
1703
  setLocalError(err);
1747
1704
  }, []);
1748
-
1749
1705
  if (previousValue === value && validationError) {
1750
1706
  value = cachedInvalidValue;
1751
1707
  }
1752
-
1753
1708
  const temporaryError = useError(id);
1754
- const error = temporaryError || validationError || localError;
1709
+ const error = localError || temporaryError || validationError;
1755
1710
  return jsxs("div", {
1756
1711
  class: classnames(props.class, 'bio-properties-panel-entry', error ? 'has-error' : ''),
1757
1712
  "data-entry-id": id,
@@ -1763,10 +1718,12 @@ function FeelEntry(props) {
1763
1718
  label: label,
1764
1719
  onInput: onInput,
1765
1720
  onError: onError,
1766
- example: props.example,
1721
+ onFocus: onFocus,
1722
+ onBlur: onBlur,
1723
+ example: example,
1767
1724
  show: show,
1768
1725
  value: value,
1769
- variables: props.variables,
1726
+ variables: variables,
1770
1727
  OptionalComponent: props.OptionalComponent
1771
1728
  }, element), error && jsx("div", {
1772
1729
  class: "bio-properties-panel-error",
@@ -1778,6 +1735,7 @@ function FeelEntry(props) {
1778
1735
  })]
1779
1736
  });
1780
1737
  }
1738
+
1781
1739
  /**
1782
1740
  * @param {Object} props
1783
1741
  * @param {Object} props.element
@@ -1788,9 +1746,10 @@ function FeelEntry(props) {
1788
1746
  * @param {String} props.label
1789
1747
  * @param {Function} props.getValue
1790
1748
  * @param {Function} props.setValue
1749
+ * @param {Function} props.onFocus
1750
+ * @param {Function} props.onBlur
1791
1751
  * @param {Function} props.validate
1792
1752
  */
1793
-
1794
1753
  function FeelTextArea(props) {
1795
1754
  return jsx(FeelEntry, {
1796
1755
  class: "bio-properties-panel-feel-textarea",
@@ -1800,7 +1759,9 @@ function FeelTextArea(props) {
1800
1759
  }
1801
1760
  function isEdited$6(node) {
1802
1761
  return node && (!!node.value || node.classList.contains('edited'));
1803
- } // helpers /////////////////
1762
+ }
1763
+
1764
+ // helpers /////////////////
1804
1765
 
1805
1766
  function prefixId$6(id) {
1806
1767
  return `bio-properties-panel-${id}`;
@@ -1822,9 +1783,7 @@ function List(props) {
1822
1783
  } = props;
1823
1784
  const [open, setOpen] = useState(!!shouldOpen);
1824
1785
  const hasItems = !!items.length;
1825
-
1826
1786
  const toggleOpen = () => hasItems && setOpen(!open);
1827
-
1828
1787
  const opening = !usePrevious(open) && open;
1829
1788
  const elementChanged = usePrevious(element) !== element;
1830
1789
  const shouldReset = opening || elementChanged;
@@ -1835,19 +1794,17 @@ function List(props) {
1835
1794
  setOpen(false);
1836
1795
  }
1837
1796
  }, [open, hasItems]);
1797
+
1838
1798
  /**
1839
1799
  * @param {MouseEvent} event
1840
1800
  */
1841
-
1842
1801
  function addItem(event) {
1843
1802
  event.stopPropagation();
1844
1803
  onAdd();
1845
-
1846
1804
  if (!open) {
1847
1805
  setOpen(true);
1848
1806
  }
1849
1807
  }
1850
-
1851
1808
  return jsxs("div", {
1852
1809
  "data-entry-id": id,
1853
1810
  class: classnames('bio-properties-panel-entry', 'bio-properties-panel-list-entry', hasItems ? '' : 'empty', open ? 'open' : ''),
@@ -1880,7 +1837,8 @@ function List(props) {
1880
1837
  })
1881
1838
  })]
1882
1839
  })]
1883
- }), hasItems && jsx(ItemsList, { ...restProps,
1840
+ }), hasItems && jsx(ItemsList, {
1841
+ ...restProps,
1884
1842
  autoFocusEntry: autoFocusEntry,
1885
1843
  component: component,
1886
1844
  element: element,
@@ -1892,7 +1850,6 @@ function List(props) {
1892
1850
  })]
1893
1851
  });
1894
1852
  }
1895
-
1896
1853
  function ItemsList(props) {
1897
1854
  const {
1898
1855
  autoFocusEntry,
@@ -1910,11 +1867,13 @@ function ItemsList(props) {
1910
1867
  useEffect(() => {
1911
1868
  if (newItem && autoFocusEntry) {
1912
1869
  // (0) select the parent entry (containing all list items)
1913
- const entry = query(`[data-entry-id="${id}"]`); // (1) select the first input or a custom element to be focussed
1870
+ const entry = query(`[data-entry-id="${id}"]`);
1914
1871
 
1872
+ // (1) select the first input or a custom element to be focussed
1915
1873
  const selector = typeof autoFocusEntry === 'boolean' ? '.bio-properties-panel-input' : autoFocusEntry;
1916
- const focusableInput = query(selector, entry); // (2) set focus
1874
+ const focusableInput = query(selector, entry);
1917
1875
 
1876
+ // (2) set focus
1918
1877
  if (focusableInput) {
1919
1878
  if (isFunction(focusableInput.select)) {
1920
1879
  focusableInput.select();
@@ -1930,7 +1889,8 @@ function ItemsList(props) {
1930
1889
  const key = getKey(item);
1931
1890
  return jsxs("li", {
1932
1891
  class: "bio-properties-panel-list-entry-item",
1933
- children: [jsx(Component, { ...restProps,
1892
+ children: [jsx(Component, {
1893
+ ...restProps,
1934
1894
  element: element,
1935
1895
  id: id,
1936
1896
  index: index,
@@ -1947,6 +1907,7 @@ function ItemsList(props) {
1947
1907
  })
1948
1908
  });
1949
1909
  }
1910
+
1950
1911
  /**
1951
1912
  * Place new items in the beginning of the list and sort the rest with provided function.
1952
1913
  *
@@ -1956,41 +1917,36 @@ function ItemsList(props) {
1956
1917
  * @param {boolean} [shouldReset=false] set to `true` to reset state of the hook
1957
1918
  * @returns {Item[]}
1958
1919
  */
1959
-
1960
-
1961
1920
  function useSortedItems(currentItems, compareFn, shouldReset = false) {
1962
- const itemsRef = useRef(currentItems.slice()); // (1) Reset and optionally sort.
1921
+ const itemsRef = useRef(currentItems.slice());
1963
1922
 
1923
+ // (1) Reset and optionally sort.
1964
1924
  if (shouldReset) {
1965
1925
  itemsRef.current = currentItems.slice();
1966
-
1967
1926
  if (compareFn) {
1968
1927
  itemsRef.current.sort(compareFn);
1969
1928
  }
1970
1929
  } else {
1971
- const items = itemsRef.current; // (2) Add new item to the list.
1930
+ const items = itemsRef.current;
1972
1931
 
1932
+ // (2) Add new item to the list.
1973
1933
  for (const item of currentItems) {
1974
1934
  if (!items.includes(item)) {
1975
1935
  // Unshift or push depending on whether we have a compareFn
1976
1936
  compareFn ? items.unshift(item) : items.push(item);
1977
1937
  }
1978
- } // (3) Filter out removed items.
1979
-
1938
+ }
1980
1939
 
1940
+ // (3) Filter out removed items.
1981
1941
  itemsRef.current = items.filter(item => currentItems.includes(item));
1982
1942
  }
1983
-
1984
1943
  return itemsRef.current;
1985
1944
  }
1986
-
1987
1945
  function useNewItems(items = [], shouldReset) {
1988
1946
  const previousItems = usePrevious(items.slice()) || [];
1989
-
1990
1947
  if (shouldReset) {
1991
1948
  return [];
1992
1949
  }
1993
-
1994
1950
  return previousItems ? items.filter(item => !previousItems.includes(item)) : [];
1995
1951
  }
1996
1952
 
@@ -2004,7 +1960,9 @@ function NumberField(props) {
2004
1960
  min,
2005
1961
  onInput,
2006
1962
  step,
2007
- value = ''
1963
+ value = '',
1964
+ onFocus,
1965
+ onBlur
2008
1966
  } = props;
2009
1967
  const [localValue, setLocalValue] = useState(value);
2010
1968
  const handleInputCallback = useMemo(() => {
@@ -2013,23 +1971,19 @@ function NumberField(props) {
2013
1971
  validity,
2014
1972
  value
2015
1973
  } = event.target;
2016
-
2017
1974
  if (validity.valid) {
2018
1975
  onInput(value ? parseFloat(value) : undefined);
2019
1976
  }
2020
1977
  });
2021
1978
  }, [onInput, debounce]);
2022
-
2023
1979
  const handleInput = e => {
2024
1980
  handleInputCallback(e);
2025
1981
  setLocalValue(e.target.value);
2026
1982
  };
2027
-
2028
1983
  useEffect(() => {
2029
1984
  if (value === localValue) {
2030
1985
  return;
2031
1986
  }
2032
-
2033
1987
  setLocalValue(value);
2034
1988
  }, [value]);
2035
1989
  return jsxs("div", {
@@ -2049,11 +2003,14 @@ function NumberField(props) {
2049
2003
  max: max,
2050
2004
  min: min,
2051
2005
  onInput: handleInput,
2006
+ onFocus: onFocus,
2007
+ onBlur: onBlur,
2052
2008
  step: step,
2053
2009
  value: localValue
2054
2010
  })]
2055
2011
  });
2056
2012
  }
2013
+
2057
2014
  /**
2058
2015
  * @param {Object} props
2059
2016
  * @param {Boolean} props.debounce
@@ -2066,10 +2023,10 @@ function NumberField(props) {
2066
2023
  * @param {String} props.max
2067
2024
  * @param {String} props.min
2068
2025
  * @param {Function} props.setValue
2026
+ * @param {Function} props.onFocus
2027
+ * @param {Function} props.onBlur
2069
2028
  * @param {String} props.step
2070
2029
  */
2071
-
2072
-
2073
2030
  function NumberFieldEntry(props) {
2074
2031
  const {
2075
2032
  debounce,
@@ -2082,7 +2039,9 @@ function NumberFieldEntry(props) {
2082
2039
  max,
2083
2040
  min,
2084
2041
  setValue,
2085
- step
2042
+ step,
2043
+ onFocus,
2044
+ onBlur
2086
2045
  } = props;
2087
2046
  const value = getValue(element);
2088
2047
  return jsxs("div", {
@@ -2094,6 +2053,8 @@ function NumberFieldEntry(props) {
2094
2053
  id: id,
2095
2054
  label: label,
2096
2055
  onInput: setValue,
2056
+ onFocus: onFocus,
2057
+ onBlur: onBlur,
2097
2058
  max: max,
2098
2059
  min: min,
2099
2060
  step: step,
@@ -2107,7 +2068,9 @@ function NumberFieldEntry(props) {
2107
2068
  }
2108
2069
  function isEdited$5(node) {
2109
2070
  return node && !!node.value;
2110
- } // helpers /////////////////
2071
+ }
2072
+
2073
+ // helpers /////////////////
2111
2074
 
2112
2075
  function prefixId$5(id) {
2113
2076
  return `bio-properties-panel-${id}`;
@@ -2120,27 +2083,25 @@ function Select(props) {
2120
2083
  onChange,
2121
2084
  options = [],
2122
2085
  value,
2123
- disabled
2086
+ disabled,
2087
+ onFocus,
2088
+ onBlur
2124
2089
  } = props;
2125
2090
  const ref = useShowEntryEvent(id);
2126
2091
  const [localValue, setLocalValue] = useState(value);
2127
-
2128
2092
  const handleChangeCallback = ({
2129
2093
  target
2130
2094
  }) => {
2131
2095
  onChange(target.value);
2132
2096
  };
2133
-
2134
2097
  const handleChange = e => {
2135
2098
  handleChangeCallback(e);
2136
2099
  setLocalValue(e.target.value);
2137
2100
  };
2138
-
2139
2101
  useEffect(() => {
2140
2102
  if (value === localValue) {
2141
2103
  return;
2142
2104
  }
2143
-
2144
2105
  setLocalValue(value);
2145
2106
  }, [value]);
2146
2107
  return jsxs("div", {
@@ -2155,6 +2116,8 @@ function Select(props) {
2155
2116
  name: id,
2156
2117
  class: "bio-properties-panel-input",
2157
2118
  onInput: handleChange,
2119
+ onFocus: onFocus,
2120
+ onBlur: onBlur,
2158
2121
  value: localValue,
2159
2122
  disabled: disabled,
2160
2123
  children: options.map((option, idx) => {
@@ -2167,6 +2130,7 @@ function Select(props) {
2167
2130
  })]
2168
2131
  });
2169
2132
  }
2133
+
2170
2134
  /**
2171
2135
  * @param {object} props
2172
2136
  * @param {object} props.element
@@ -2175,11 +2139,11 @@ function Select(props) {
2175
2139
  * @param {string} props.label
2176
2140
  * @param {Function} props.getValue
2177
2141
  * @param {Function} props.setValue
2142
+ * @param {Function} props.onFocus
2143
+ * @param {Function} props.onBlur
2178
2144
  * @param {Function} props.getOptions
2179
2145
  * @param {boolean} [props.disabled]
2180
2146
  */
2181
-
2182
-
2183
2147
  function SelectEntry(props) {
2184
2148
  const {
2185
2149
  element,
@@ -2189,7 +2153,9 @@ function SelectEntry(props) {
2189
2153
  getValue,
2190
2154
  setValue,
2191
2155
  getOptions,
2192
- disabled
2156
+ disabled,
2157
+ onFocus,
2158
+ onBlur
2193
2159
  } = props;
2194
2160
  const value = getValue(element);
2195
2161
  const options = getOptions(element);
@@ -2202,6 +2168,8 @@ function SelectEntry(props) {
2202
2168
  label: label,
2203
2169
  value: value,
2204
2170
  onChange: setValue,
2171
+ onFocus: onFocus,
2172
+ onBlur: onBlur,
2205
2173
  options: options,
2206
2174
  disabled: disabled
2207
2175
  }, element), error && jsx("div", {
@@ -2216,7 +2184,9 @@ function SelectEntry(props) {
2216
2184
  }
2217
2185
  function isEdited$4(node) {
2218
2186
  return node && !!node.value;
2219
- } // helpers /////////////////
2187
+ }
2188
+
2189
+ // helpers /////////////////
2220
2190
 
2221
2191
  function prefixId$4(id) {
2222
2192
  return `bio-properties-panel-${id}`;
@@ -2240,17 +2210,14 @@ function Simple(props) {
2240
2210
  target
2241
2211
  }) => setValue(target.value.length ? target.value : undefined));
2242
2212
  }, [setValue, debounce]);
2243
-
2244
2213
  const handleInput = e => {
2245
2214
  handleInputCallback(e);
2246
2215
  setLocalValue(e.target.value);
2247
2216
  };
2248
-
2249
2217
  useEffect(() => {
2250
2218
  if (value === localValue) {
2251
2219
  return;
2252
2220
  }
2253
-
2254
2221
  setLocalValue(value);
2255
2222
  }, [value]);
2256
2223
  return jsx("div", {
@@ -2273,7 +2240,9 @@ function Simple(props) {
2273
2240
  }
2274
2241
  function isEdited$3(node) {
2275
2242
  return node && !!node.value;
2276
- } // helpers /////////////////
2243
+ }
2244
+
2245
+ // helpers /////////////////
2277
2246
 
2278
2247
  function prefixId$3(id) {
2279
2248
  return `bio-properties-panel-${id}`;
@@ -2288,7 +2257,9 @@ function TextArea(props) {
2288
2257
  onInput,
2289
2258
  value = '',
2290
2259
  disabled,
2291
- monospace
2260
+ monospace,
2261
+ onFocus,
2262
+ onBlur
2292
2263
  } = props;
2293
2264
  const [localValue, setLocalValue] = useState(value);
2294
2265
  const ref = useShowEntryEvent(id);
@@ -2297,17 +2268,14 @@ function TextArea(props) {
2297
2268
  target
2298
2269
  }) => onInput(target.value.length ? target.value : undefined));
2299
2270
  }, [onInput, debounce]);
2300
-
2301
2271
  const handleInput = e => {
2302
2272
  handleInputCallback(e);
2303
2273
  setLocalValue(e.target.value);
2304
2274
  };
2305
-
2306
2275
  useEffect(() => {
2307
2276
  if (value === localValue) {
2308
2277
  return;
2309
2278
  }
2310
-
2311
2279
  setLocalValue(value);
2312
2280
  }, [value]);
2313
2281
  return jsxs("div", {
@@ -2323,14 +2291,15 @@ function TextArea(props) {
2323
2291
  spellCheck: "false",
2324
2292
  class: classnames('bio-properties-panel-input', monospace ? 'bio-properties-panel-input-monospace' : ''),
2325
2293
  onInput: handleInput,
2326
- onFocus: props.onFocus,
2327
- onBlur: props.onBlur,
2294
+ onFocus: onFocus,
2295
+ onBlur: onBlur,
2328
2296
  rows: rows,
2329
2297
  value: localValue,
2330
2298
  disabled: disabled
2331
2299
  })]
2332
2300
  });
2333
2301
  }
2302
+
2334
2303
  /**
2335
2304
  * @param {object} props
2336
2305
  * @param {object} props.element
@@ -2340,12 +2309,12 @@ function TextArea(props) {
2340
2309
  * @param {string} props.label
2341
2310
  * @param {Function} props.getValue
2342
2311
  * @param {Function} props.setValue
2312
+ * @param {Function} props.onFocus
2313
+ * @param {Function} props.onBlur
2343
2314
  * @param {number} props.rows
2344
2315
  * @param {boolean} props.monospace
2345
2316
  * @param {boolean} [props.disabled]
2346
2317
  */
2347
-
2348
-
2349
2318
  function TextAreaEntry(props) {
2350
2319
  const {
2351
2320
  element,
@@ -2357,7 +2326,9 @@ function TextAreaEntry(props) {
2357
2326
  setValue,
2358
2327
  rows,
2359
2328
  monospace,
2360
- disabled
2329
+ disabled,
2330
+ onFocus,
2331
+ onBlur
2361
2332
  } = props;
2362
2333
  const value = getValue(element);
2363
2334
  const error = useError(id);
@@ -2369,6 +2340,8 @@ function TextAreaEntry(props) {
2369
2340
  label: label,
2370
2341
  value: value,
2371
2342
  onInput: setValue,
2343
+ onFocus: onFocus,
2344
+ onBlur: onBlur,
2372
2345
  rows: rows,
2373
2346
  debounce: debounce,
2374
2347
  monospace: monospace,
@@ -2385,7 +2358,9 @@ function TextAreaEntry(props) {
2385
2358
  }
2386
2359
  function isEdited$2(node) {
2387
2360
  return node && !!node.value;
2388
- } // helpers /////////////////
2361
+ }
2362
+
2363
+ // helpers /////////////////
2389
2364
 
2390
2365
  function prefixId$2(id) {
2391
2366
  return `bio-properties-panel-${id}`;
@@ -2398,6 +2373,8 @@ function Textfield(props) {
2398
2373
  id,
2399
2374
  label,
2400
2375
  onInput,
2376
+ onFocus,
2377
+ onBlur,
2401
2378
  value = ''
2402
2379
  } = props;
2403
2380
  const [localValue, setLocalValue] = useState(value || '');
@@ -2407,17 +2384,14 @@ function Textfield(props) {
2407
2384
  target
2408
2385
  }) => onInput(target.value.length ? target.value : undefined));
2409
2386
  }, [onInput, debounce]);
2410
-
2411
2387
  const handleInput = e => {
2412
2388
  handleInputCallback(e);
2413
2389
  setLocalValue(e.target.value);
2414
2390
  };
2415
-
2416
2391
  useEffect(() => {
2417
2392
  if (value === localValue) {
2418
2393
  return;
2419
2394
  }
2420
-
2421
2395
  setLocalValue(value);
2422
2396
  }, [value]);
2423
2397
  return jsxs("div", {
@@ -2436,12 +2410,13 @@ function Textfield(props) {
2436
2410
  disabled: disabled,
2437
2411
  class: "bio-properties-panel-input",
2438
2412
  onInput: handleInput,
2439
- onFocus: props.onFocus,
2440
- onBlur: props.onBlur,
2413
+ onFocus: onFocus,
2414
+ onBlur: onBlur,
2441
2415
  value: localValue
2442
2416
  })]
2443
2417
  });
2444
2418
  }
2419
+
2445
2420
  /**
2446
2421
  * @param {Object} props
2447
2422
  * @param {Object} props.element
@@ -2452,10 +2427,10 @@ function Textfield(props) {
2452
2427
  * @param {String} props.label
2453
2428
  * @param {Function} props.getValue
2454
2429
  * @param {Function} props.setValue
2430
+ * @param {Function} props.onFocus
2431
+ * @param {Function} props.onBlur
2455
2432
  * @param {Function} props.validate
2456
2433
  */
2457
-
2458
-
2459
2434
  function TextfieldEntry(props) {
2460
2435
  const {
2461
2436
  element,
@@ -2466,7 +2441,9 @@ function TextfieldEntry(props) {
2466
2441
  label,
2467
2442
  getValue,
2468
2443
  setValue,
2469
- validate
2444
+ validate,
2445
+ onFocus,
2446
+ onBlur
2470
2447
  } = props;
2471
2448
  const [cachedInvalidValue, setCachedInvalidValue] = useState(null);
2472
2449
  const globalError = useError(id);
@@ -2479,27 +2456,21 @@ function TextfieldEntry(props) {
2479
2456
  setLocalError(newValidationError);
2480
2457
  }
2481
2458
  }, [value]);
2482
-
2483
2459
  const onInput = newValue => {
2484
2460
  let newValidationError = null;
2485
-
2486
2461
  if (isFunction(validate)) {
2487
2462
  newValidationError = validate(newValue) || null;
2488
2463
  }
2489
-
2490
2464
  if (newValidationError) {
2491
2465
  setCachedInvalidValue(newValue);
2492
2466
  } else {
2493
2467
  setValue(newValue);
2494
2468
  }
2495
-
2496
2469
  setLocalError(newValidationError);
2497
2470
  };
2498
-
2499
2471
  if (previousValue === value && localError) {
2500
2472
  value = cachedInvalidValue;
2501
2473
  }
2502
-
2503
2474
  const error = globalError || localError;
2504
2475
  return jsxs("div", {
2505
2476
  class: classnames('bio-properties-panel-entry', error ? 'has-error' : ''),
@@ -2510,6 +2481,8 @@ function TextfieldEntry(props) {
2510
2481
  id: id,
2511
2482
  label: label,
2512
2483
  onInput: onInput,
2484
+ onFocus: onFocus,
2485
+ onBlur: onBlur,
2513
2486
  value: value
2514
2487
  }, element), error && jsx("div", {
2515
2488
  class: "bio-properties-panel-error",
@@ -2523,7 +2496,9 @@ function TextfieldEntry(props) {
2523
2496
  }
2524
2497
  function isEdited$1(node) {
2525
2498
  return node && !!node.value;
2526
- } // helpers /////////////////
2499
+ }
2500
+
2501
+ // helpers /////////////////
2527
2502
 
2528
2503
  function prefixId$1(id) {
2529
2504
  return `bio-properties-panel-${id}`;
@@ -2535,24 +2510,22 @@ function ToggleSwitch(props) {
2535
2510
  label,
2536
2511
  onInput,
2537
2512
  value,
2538
- switcherLabel
2513
+ switcherLabel,
2514
+ onFocus,
2515
+ onBlur
2539
2516
  } = props;
2540
2517
  const [localValue, setLocalValue] = useState(value);
2541
-
2542
2518
  const handleInputCallback = async () => {
2543
2519
  onInput(!value);
2544
2520
  };
2545
-
2546
2521
  const handleInput = e => {
2547
2522
  handleInputCallback();
2548
2523
  setLocalValue(e.target.value);
2549
2524
  };
2550
-
2551
2525
  useEffect(() => {
2552
2526
  if (value === localValue) {
2553
2527
  return;
2554
2528
  }
2555
-
2556
2529
  setLocalValue(value);
2557
2530
  }, [value]);
2558
2531
  return jsxs("div", {
@@ -2569,9 +2542,11 @@ function ToggleSwitch(props) {
2569
2542
  id: prefixId(id),
2570
2543
  class: "bio-properties-panel-input",
2571
2544
  type: "checkbox",
2545
+ onFocus: onFocus,
2546
+ onBlur: onBlur,
2572
2547
  name: id,
2573
2548
  onInput: handleInput,
2574
- checked: localValue
2549
+ checked: !!localValue
2575
2550
  }), jsx("span", {
2576
2551
  class: "bio-properties-panel-toggle-switch__slider"
2577
2552
  })]
@@ -2582,6 +2557,7 @@ function ToggleSwitch(props) {
2582
2557
  })]
2583
2558
  });
2584
2559
  }
2560
+
2585
2561
  /**
2586
2562
  * @param {Object} props
2587
2563
  * @param {Object} props.element
@@ -2591,9 +2567,9 @@ function ToggleSwitch(props) {
2591
2567
  * @param {String} props.switcherLabel
2592
2568
  * @param {Function} props.getValue
2593
2569
  * @param {Function} props.setValue
2570
+ * @param {Function} props.onFocus
2571
+ * @param {Function} props.onBlur
2594
2572
  */
2595
-
2596
-
2597
2573
  function ToggleSwitchEntry(props) {
2598
2574
  const {
2599
2575
  element,
@@ -2602,7 +2578,9 @@ function ToggleSwitchEntry(props) {
2602
2578
  label,
2603
2579
  switcherLabel,
2604
2580
  getValue,
2605
- setValue
2581
+ setValue,
2582
+ onFocus,
2583
+ onBlur
2606
2584
  } = props;
2607
2585
  const value = getValue(element);
2608
2586
  return jsxs("div", {
@@ -2613,6 +2591,8 @@ function ToggleSwitchEntry(props) {
2613
2591
  label: label,
2614
2592
  value: value,
2615
2593
  onInput: setValue,
2594
+ onFocus: onFocus,
2595
+ onBlur: onBlur,
2616
2596
  switcherLabel: switcherLabel
2617
2597
  }), jsx(Description, {
2618
2598
  forId: id,
@@ -2623,7 +2603,9 @@ function ToggleSwitchEntry(props) {
2623
2603
  }
2624
2604
  function isEdited(node) {
2625
2605
  return node && !!node.checked;
2626
- } // helpers /////////////////
2606
+ }
2607
+
2608
+ // helpers /////////////////
2627
2609
 
2628
2610
  function prefixId(id) {
2629
2611
  return `bio-properties-panel-${id}`;