@bpmn-io/form-js-editor 1.17.0 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var Ids = require('ids');
3
+ var ids$1 = require('ids');
4
4
  var formJsViewer = require('@bpmn-io/form-js-viewer');
5
5
  var minDash = require('min-dash');
6
6
  var classnames = require('classnames');
@@ -2890,11 +2890,35 @@ EditorActions.prototype._registerDefaultActions = function (injector) {
2890
2890
  }
2891
2891
  });
2892
2892
  }
2893
+ if (copyPaste && selection) {
2894
+ this.register('duplicate', function () {
2895
+ var selectedElements = selection.get();
2896
+ if (selectedElements.length) {
2897
+ return copyPaste.duplicate(selectedElements);
2898
+ }
2899
+ });
2900
+ }
2893
2901
  if (copyPaste) {
2894
2902
  this.register('paste', function () {
2895
2903
  copyPaste.paste();
2896
2904
  });
2897
2905
  }
2906
+ if (copyPaste && selection && rules) {
2907
+ this.register('cut', function () {
2908
+ var selectedElements = selection.get();
2909
+ if (!selectedElements.length) {
2910
+ return;
2911
+ }
2912
+ var allowed = rules.allowed('elements.delete', {
2913
+ elements: selectedElements
2914
+ });
2915
+ if (allowed === false) {
2916
+ return;
2917
+ }
2918
+ var cuttableElements = minDash.isArray(allowed) ? allowed : selectedElements;
2919
+ return copyPaste.cut(cuttableElements.slice());
2920
+ });
2921
+ }
2898
2922
  if (zoomScroll) {
2899
2923
  this.register('stepZoom', function (opts) {
2900
2924
  zoomScroll.stepZoom(opts.value);
@@ -3113,6 +3137,8 @@ const EditorExpressionLanguageModule = {
3113
3137
 
3114
3138
  var KEYS_COPY = ['c', 'C'];
3115
3139
  var KEYS_PASTE = ['v', 'V'];
3140
+ var KEYS_DUPLICATE = ['d', 'D'];
3141
+ var KEYS_CUT = ['x', 'X'];
3116
3142
  var KEYS_REDO = ['y', 'Y'];
3117
3143
  var KEYS_UNDO = ['z', 'Z'];
3118
3144
 
@@ -3128,7 +3154,7 @@ function hasModifier(event) {
3128
3154
  * @param {KeyboardEvent} event
3129
3155
  * @return {boolean}
3130
3156
  */
3131
- function isCmd(event) {
3157
+ function isCmd$1(event) {
3132
3158
  // ensure we don't react to AltGr
3133
3159
  // (mapped to CTRL + ALT)
3134
3160
  if (event.altKey) {
@@ -3160,28 +3186,42 @@ function isShift(event) {
3160
3186
  * @param {KeyboardEvent} event
3161
3187
  */
3162
3188
  function isCopy(event) {
3163
- return isCmd(event) && isKey(KEYS_COPY, event);
3189
+ return isCmd$1(event) && isKey(KEYS_COPY, event);
3164
3190
  }
3165
3191
 
3166
3192
  /**
3167
3193
  * @param {KeyboardEvent} event
3168
3194
  */
3169
3195
  function isPaste(event) {
3170
- return isCmd(event) && isKey(KEYS_PASTE, event);
3196
+ return isCmd$1(event) && isKey(KEYS_PASTE, event);
3197
+ }
3198
+
3199
+ /**
3200
+ * @param {KeyboardEvent} event
3201
+ */
3202
+ function isDuplicate(event) {
3203
+ return isCmd$1(event) && isKey(KEYS_DUPLICATE, event);
3204
+ }
3205
+
3206
+ /**
3207
+ * @param {KeyboardEvent} event
3208
+ */
3209
+ function isCut(event) {
3210
+ return isCmd$1(event) && isKey(KEYS_CUT, event);
3171
3211
  }
3172
3212
 
3173
3213
  /**
3174
3214
  * @param {KeyboardEvent} event
3175
3215
  */
3176
3216
  function isUndo(event) {
3177
- return isCmd(event) && !isShift(event) && isKey(KEYS_UNDO, event);
3217
+ return isCmd$1(event) && !isShift(event) && isKey(KEYS_UNDO, event);
3178
3218
  }
3179
3219
 
3180
3220
  /**
3181
3221
  * @param {KeyboardEvent} event
3182
3222
  */
3183
3223
  function isRedo(event) {
3184
- return isCmd(event) && (isKey(KEYS_REDO, event) || isKey(KEYS_UNDO, event) && isShift(event));
3224
+ return isCmd$1(event) && (isKey(KEYS_REDO, event) || isKey(KEYS_UNDO, event) && isShift(event));
3185
3225
  }
3186
3226
 
3187
3227
  /**
@@ -3350,7 +3390,7 @@ Keyboard.prototype.removeListener = function (listener, type) {
3350
3390
  this._eventBus.off(type || KEYDOWN_EVENT, listener);
3351
3391
  };
3352
3392
  Keyboard.prototype.hasModifier = hasModifier;
3353
- Keyboard.prototype.isCmd = isCmd;
3393
+ Keyboard.prototype.isCmd = isCmd$1;
3354
3394
  Keyboard.prototype.isShift = isShift;
3355
3395
  Keyboard.prototype.isKey = isKey;
3356
3396
 
@@ -3435,6 +3475,26 @@ KeyboardBindings.prototype.registerBindings = function (keyboard, editorActions)
3435
3475
  }
3436
3476
  });
3437
3477
 
3478
+ // duplicate
3479
+ // CTRL/CMD + D
3480
+ addListener('duplicate', function (context) {
3481
+ var event = context.keyEvent;
3482
+ if (isDuplicate(event)) {
3483
+ editorActions.trigger('duplicate');
3484
+ return true;
3485
+ }
3486
+ });
3487
+
3488
+ // cut
3489
+ // CTRL/CMD + X
3490
+ addListener('cut', function (context) {
3491
+ var event = context.keyEvent;
3492
+ if (isCut(event)) {
3493
+ editorActions.trigger('cut');
3494
+ return true;
3495
+ }
3496
+ });
3497
+
3438
3498
  // zoom in one step
3439
3499
  // CTRL/CMD + +
3440
3500
  addListener('stepZoom', function (context) {
@@ -3442,7 +3502,7 @@ KeyboardBindings.prototype.registerBindings = function (keyboard, editorActions)
3442
3502
 
3443
3503
  // quirk: it has to be triggered by `=` as well to work on international keyboard layout
3444
3504
  // cf: https://github.com/bpmn-io/bpmn-js/issues/1362#issuecomment-722989754
3445
- if (isKey(['+', 'Add', '='], event) && isCmd(event)) {
3505
+ if (isKey(['+', 'Add', '='], event) && isCmd$1(event)) {
3446
3506
  editorActions.trigger('stepZoom', {
3447
3507
  value: 1
3448
3508
  });
@@ -3454,7 +3514,7 @@ KeyboardBindings.prototype.registerBindings = function (keyboard, editorActions)
3454
3514
  // CTRL + -
3455
3515
  addListener('stepZoom', function (context) {
3456
3516
  var event = context.keyEvent;
3457
- if (isKey(['-', 'Subtract'], event) && isCmd(event)) {
3517
+ if (isKey(['-', 'Subtract'], event) && isCmd$1(event)) {
3458
3518
  editorActions.trigger('stepZoom', {
3459
3519
  value: -1
3460
3520
  });
@@ -3466,7 +3526,7 @@ KeyboardBindings.prototype.registerBindings = function (keyboard, editorActions)
3466
3526
  // CTRL + 0
3467
3527
  addListener('zoom', function (context) {
3468
3528
  var event = context.keyEvent;
3469
- if (isKey('0', event) && isCmd(event)) {
3529
+ if (isKey('0', event) && isCmd$1(event)) {
3470
3530
  editorActions.trigger('zoom', {
3471
3531
  value: 1
3472
3532
  });
@@ -5462,23 +5522,32 @@ LaunchIcon.defaultProps = {
5462
5522
  viewBox: "0 0 32 32"
5463
5523
  };
5464
5524
  var OpenPopupIcon = function OpenPopupIcon(props) {
5465
- return jsxRuntime.jsxs("svg", {
5525
+ return jsxRuntime.jsx("svg", {
5466
5526
  ...props,
5467
- children: [jsxRuntime.jsx("path", {
5468
- fill: "currentColor",
5469
- d: "M28 4H10a2.006 2.006 0 0 0-2 2v14a2.006 2.006 0 0 0 2 2h18a2.006 2.006 0 0 0 2-2V6a2.006 2.006 0 0 0-2-2Zm0 16H10V6h18Z"
5470
- }), jsxRuntime.jsx("path", {
5471
- fill: "currentColor",
5472
- d: "M18 26H4V16h2v-2H4a2.006 2.006 0 0 0-2 2v10a2.006 2.006 0 0 0 2 2h14a2.006 2.006 0 0 0 2-2v-2h-2Z"
5473
- })]
5527
+ children: jsxRuntime.jsx("path", {
5528
+ d: "M6 15v-1H2.7L7 9.7 6.3 9 2 13.3V10H1v5zm4-14v1h3.3L9 6.3l.7.7L14 2.7V6h1V1z"
5529
+ })
5474
5530
  });
5475
5531
  };
5476
5532
  OpenPopupIcon.defaultProps = {
5477
5533
  xmlns: "http://www.w3.org/2000/svg",
5478
- width: "16",
5479
- height: "16",
5480
- viewBox: "0 0 32 32"
5534
+ viewBox: "0 0 16 16"
5481
5535
  };
5536
+
5537
+ /**
5538
+ * @typedef { {
5539
+ * getElementLabel: (element: object) => string,
5540
+ * getTypeLabel: (element: object) => string,
5541
+ * getElementIcon: (element: object) => import('preact').Component,
5542
+ * getDocumentationRef: (element: object) => string
5543
+ * } } HeaderProvider
5544
+ */
5545
+
5546
+ /**
5547
+ * @param {Object} props
5548
+ * @param {Object} props.element,
5549
+ * @param {HeaderProvider} props.headerProvider
5550
+ */
5482
5551
  function Header(props) {
5483
5552
  const {
5484
5553
  element,
@@ -5506,11 +5575,9 @@ function Header(props) {
5506
5575
  }), jsxRuntime.jsxs("div", {
5507
5576
  class: "bio-properties-panel-header-labels",
5508
5577
  children: [jsxRuntime.jsx("div", {
5509
- title: type,
5510
5578
  class: "bio-properties-panel-header-type",
5511
5579
  children: type
5512
5580
  }), label ? jsxRuntime.jsx("div", {
5513
- title: label,
5514
5581
  class: "bio-properties-panel-header-label",
5515
5582
  children: label
5516
5583
  }) : null]
@@ -5598,6 +5665,27 @@ function useTooltipContext(id, element) {
5598
5665
  } = hooks.useContext(TooltipContext);
5599
5666
  return getTooltipForId(id, element);
5600
5667
  }
5668
+
5669
+ /**
5670
+ * @typedef {Object} TooltipProps
5671
+ * @property {Object} [parent] - Parent element ref for portal rendering
5672
+ * @property {String} [direction='right'] - Tooltip direction ( 'right', 'top')
5673
+ * @property {String} [position] - Custom CSS position override
5674
+ * @property {Number} [showDelay=250] - Delay in ms before showing tooltip on hover
5675
+ * @property {Number} [hideDelay=250] - Delay in ms before hiding tooltip when mouse leaves, to avoid multiple tooltips from being opened, this should be the same as showDelay
5676
+ * @property {*} [children] - Child elements to render inside the tooltip wrapper
5677
+ */
5678
+
5679
+ /**
5680
+ * Tooltip wrapper that provides context-based tooltip content lookup.
5681
+ * All props are forwarded to the underlying Tooltip component.
5682
+ *
5683
+ * @param {TooltipProps & {
5684
+ * forId: String,
5685
+ * value?: String|Object,
5686
+ * element?: Object
5687
+ * }} props - Shared tooltip props plus wrapper-specific ones
5688
+ */
5601
5689
  function TooltipWrapper(props) {
5602
5690
  const {
5603
5691
  forId,
@@ -5614,35 +5702,77 @@ function TooltipWrapper(props) {
5614
5702
  forId: `bio-properties-panel-${forId}`
5615
5703
  });
5616
5704
  }
5705
+
5706
+ /**
5707
+ * @param {TooltipProps & {
5708
+ * forId: String,
5709
+ * value: String|Object
5710
+ * }} props
5711
+ */
5617
5712
  function Tooltip(props) {
5618
5713
  const {
5619
5714
  forId,
5620
5715
  value,
5621
5716
  parent,
5622
5717
  direction = 'right',
5623
- position
5718
+ position,
5719
+ showDelay = 250,
5720
+ hideDelay = 250
5624
5721
  } = props;
5625
5722
  const [visible, setVisible] = hooks.useState(false);
5626
-
5627
- // Tooltip will be shown after SHOW_DELAY ms from hovering over the source element.
5628
- const SHOW_DELAY = 200;
5629
- let timeout = null;
5723
+ const showTimeoutRef = hooks.useRef(null);
5724
+ const hideTimeoutRef = hooks.useRef(null);
5630
5725
  const wrapperRef = hooks.useRef(null);
5631
5726
  const tooltipRef = hooks.useRef(null);
5632
5727
  const show = (_, delay) => {
5728
+ clearTimeout(showTimeoutRef.current);
5729
+ clearTimeout(hideTimeoutRef.current);
5633
5730
  if (visible) return;
5634
5731
  if (delay) {
5635
- timeout = setTimeout(() => {
5732
+ showTimeoutRef.current = setTimeout(() => {
5636
5733
  setVisible(true);
5637
- }, SHOW_DELAY);
5734
+ }, showDelay);
5638
5735
  } else {
5639
5736
  setVisible(true);
5640
5737
  }
5641
5738
  };
5642
- const hide = () => {
5643
- clearTimeout(timeout);
5644
- setVisible(false);
5739
+ const handleWrapperMouseEnter = e => {
5740
+ show(e, true);
5741
+ };
5742
+ const hide = (delay = false) => {
5743
+ clearTimeout(showTimeoutRef.current);
5744
+ clearTimeout(hideTimeoutRef.current);
5745
+ if (delay) {
5746
+ hideTimeoutRef.current = setTimeout(() => {
5747
+ setVisible(false);
5748
+ }, hideDelay);
5749
+ } else {
5750
+ setVisible(false);
5751
+ }
5645
5752
  };
5753
+
5754
+ // Cleanup timeouts on unmount
5755
+ hooks.useEffect(() => {
5756
+ return () => {
5757
+ clearTimeout(showTimeoutRef.current);
5758
+ clearTimeout(hideTimeoutRef.current);
5759
+ };
5760
+ }, []);
5761
+
5762
+ // Handle click outside to close tooltip for non-focusable elements
5763
+ hooks.useEffect(() => {
5764
+ if (!visible) return;
5765
+ const handleClickOutside = e => {
5766
+ // If clicking outside both the wrapper and tooltip, hide it
5767
+ if (wrapperRef.current && !wrapperRef.current.contains(e.target) && tooltipRef.current && !tooltipRef.current.contains(e.target)) {
5768
+ hide(false);
5769
+ }
5770
+ };
5771
+ document.addEventListener('mousedown', handleClickOutside);
5772
+ return () => {
5773
+ document.removeEventListener('mousedown', handleClickOutside);
5774
+ };
5775
+ }, [visible, hide]);
5646
5776
  const handleMouseLeave = ({
5647
5777
  relatedTarget
5648
5778
  }) => {
@@ -5650,23 +5780,32 @@ function Tooltip(props) {
5650
5780
  if (relatedTarget === wrapperRef.current || relatedTarget === tooltipRef.current || relatedTarget?.parentElement === tooltipRef.current) {
5651
5781
  return;
5652
5782
  }
5653
- hide();
5783
+ const selection = window.getSelection();
5784
+ if (selection && selection.toString().length > 0) {
5785
+ // Check if selection is within tooltip content
5786
+ const selectionRange = selection.getRangeAt(0);
5787
+ if (tooltipRef.current?.contains(selectionRange.commonAncestorContainer) || tooltipRef.current?.contains(selection.anchorNode) || tooltipRef.current?.contains(selection.focusNode)) {
5788
+ return; // Keep tooltip open during text selection
5789
+ }
5790
+ }
5791
+ hide(true);
5792
+ };
5793
+ const handleTooltipMouseEnter = () => {
5794
+ clearTimeout(hideTimeoutRef.current);
5654
5795
  };
5655
5796
  const handleFocusOut = e => {
5656
5797
  const {
5657
- target
5798
+ relatedTarget
5658
5799
  } = e;
5659
5800
 
5660
- // Don't hide the tooltip if the wrapper or the tooltip itself is clicked.
5661
- const isHovered = target.matches(':hover') || tooltipRef.current?.matches(':hover');
5662
- if (target === wrapperRef.current && isHovered) {
5663
- e.stopPropagation();
5801
+ // Don't hide if focus moved to the tooltip or another element within the wrapper
5802
+ if (tooltipRef.current?.contains(relatedTarget) || wrapperRef.current?.contains(relatedTarget)) {
5664
5803
  return;
5665
5804
  }
5666
- hide();
5805
+ hide(false);
5667
5806
  };
5668
5807
  const hideTooltipViaEscape = e => {
5669
- e.code === 'Escape' && hide();
5808
+ e.code === 'Escape' && hide(false);
5670
5809
  };
5671
5810
  const renderTooltip = () => {
5672
5811
  return jsxRuntime.jsxs("div", {
@@ -5677,6 +5816,7 @@ function Tooltip(props) {
5677
5816
  style: position || getTooltipPosition(wrapperRef.current),
5678
5817
  ref: tooltipRef,
5679
5818
  onClick: e => e.stopPropagation(),
5819
+ onMouseEnter: handleTooltipMouseEnter,
5680
5820
  onMouseLeave: handleMouseLeave,
5681
5821
  children: [jsxRuntime.jsx("div", {
5682
5822
  class: "bio-properties-panel-tooltip-content",
@@ -5690,7 +5830,7 @@ function Tooltip(props) {
5690
5830
  class: "bio-properties-panel-tooltip-wrapper",
5691
5831
  tabIndex: "0",
5692
5832
  ref: wrapperRef,
5693
- onMouseEnter: e => show(e, true),
5833
+ onMouseEnter: handleWrapperMouseEnter,
5694
5834
  onMouseLeave: handleMouseLeave,
5695
5835
  onFocus: show,
5696
5836
  onBlur: handleFocusOut,
@@ -5968,6 +6108,10 @@ function useElementVisible(element) {
5968
6108
  }, [element, visible]);
5969
6109
  return visible;
5970
6110
  }
6111
+
6112
+ /**
6113
+ * @param {import('../PropertiesPanel').GroupDefinition} props
6114
+ */
5971
6115
  function Group(props) {
5972
6116
  const {
5973
6117
  element,
@@ -6022,8 +6166,6 @@ function Group(props) {
6022
6166
  class: classnames('bio-properties-panel-group-header', edited ? '' : 'empty', open ? 'open' : '', sticky && open ? 'sticky' : ''),
6023
6167
  onClick: toggleOpen,
6024
6168
  children: [jsxRuntime.jsx("div", {
6025
- title: props.tooltip ? null : label,
6026
- "data-title": label,
6027
6169
  class: "bio-properties-panel-group-header-title",
6028
6170
  children: jsxRuntime.jsx(TooltipWrapper, {
6029
6171
  value: props.tooltip,
@@ -6227,9 +6369,11 @@ function PropertiesPanel$1(props) {
6227
6369
  return minDash.get(layout, key, defaultValue);
6228
6370
  };
6229
6371
  const setLayoutForKey = (key, config) => {
6230
- const newLayout = minDash.assign({}, layout);
6231
- minDash.set(newLayout, key, config);
6232
- setLayout(newLayout);
6372
+ setLayout(prevLayout => {
6373
+ const newLayout = minDash.assign({}, prevLayout);
6374
+ minDash.set(newLayout, key, config);
6375
+ return newLayout;
6376
+ });
6233
6377
  };
6234
6378
  const layoutContext = {
6235
6379
  layout,
@@ -6428,7 +6572,6 @@ function CollapsibleEntry(props) {
6428
6572
  class: "bio-properties-panel-collapsible-entry-header",
6429
6573
  onClick: toggleOpen,
6430
6574
  children: [jsxRuntime.jsx("div", {
6431
- title: label || placeholderLabel,
6432
6575
  class: classnames('bio-properties-panel-collapsible-entry-header-title', !label && 'empty'),
6433
6576
  children: label || placeholderLabel
6434
6577
  }), jsxRuntime.jsx("button", {
@@ -6464,6 +6607,10 @@ function CollapsibleEntry(props) {
6464
6607
  })]
6465
6608
  });
6466
6609
  }
6610
+
6611
+ /**
6612
+ * @param {import('../PropertiesPanel').ListItemDefinition} props
6613
+ */
6467
6614
  function ListItem(props) {
6468
6615
  const {
6469
6616
  autoFocusEntry,
@@ -6565,8 +6712,6 @@ function ListGroup(props) {
6565
6712
  class: classnames('bio-properties-panel-group-header', hasItems ? '' : 'empty', hasItems && open ? 'open' : '', sticky && open ? 'sticky' : ''),
6566
6713
  onClick: hasItems ? toggleOpen : noop$6,
6567
6714
  children: [jsxRuntime.jsx("div", {
6568
- title: props.tooltip ? null : label,
6569
- "data-title": label,
6570
6715
  class: "bio-properties-panel-group-header-title",
6571
6716
  children: jsxRuntime.jsx(TooltipWrapper, {
6572
6717
  value: props.tooltip,
@@ -6635,6 +6780,13 @@ function getNewItemIds(newItems, oldItems) {
6635
6780
  const oldIds = oldItems.map(item => item.id);
6636
6781
  return newIds.filter(itemId => !oldIds.includes(itemId));
6637
6782
  }
6783
+
6784
+ /**
6785
+ * @param {Object} props
6786
+ * @param {Object} props.element
6787
+ * @param {String} props.forId - id of the entry the description is used for
6788
+ * @param {String} props.value
6789
+ */
6638
6790
  function Description$1(props) {
6639
6791
  const {
6640
6792
  element,
@@ -6764,6 +6916,16 @@ function isEdited$8(node) {
6764
6916
  function prefixId$8(id) {
6765
6917
  return `bio-properties-panel-${id}`;
6766
6918
  }
6919
+
6920
+ /**
6921
+ * Button to open popups.
6922
+ *
6923
+ * @param {Object} props
6924
+ * @param {Function} props.onClick - Callback to trigger when the button is clicked.
6925
+ * @param {string} [props.title] - Tooltip text for the button.
6926
+ * @param {boolean} [props.disabled] - Whether the button is disabled.
6927
+ * @param {string} [props.className] - Additional class names for the button.
6928
+ */
6767
6929
  function OpenPopupButton({
6768
6930
  onClick,
6769
6931
  title = 'Open pop-up editor'
@@ -6907,6 +7069,7 @@ const FeelEditor = React.forwardRef((props, ref) => {
6907
7069
  enableGutters,
6908
7070
  value,
6909
7071
  onInput,
7072
+ onKeyDown: onKeyDownProp = noop$4,
6910
7073
  onFeelToggle = noop$4,
6911
7074
  onLint = noop$4,
6912
7075
  onOpenPopup = noop$4,
@@ -6939,6 +7102,8 @@ const FeelEditor = React.forwardRef((props, ref) => {
6939
7102
  * - AND the cursor is at the beginning of the input
6940
7103
  */
6941
7104
  const onKeyDown = e => {
7105
+ // Call parent onKeyDown handler first
7106
+ onKeyDownProp(e);
6942
7107
  if (e.key !== 'Backspace' || !editor) {
6943
7108
  return;
6944
7109
  }
@@ -7055,6 +7220,22 @@ function FeelIcon(props) {
7055
7220
  children: jsxRuntime.jsx(FeelIcon$1, {})
7056
7221
  });
7057
7222
  }
7223
+
7224
+ /**
7225
+ * @param {KeyboardEvent} event
7226
+ * @return {boolean}
7227
+ */
7228
+ function isCmd(event) {
7229
+ // ensure we don't react to AltGr
7230
+ // (mapped to CTRL + ALT)
7231
+ if (event.altKey) {
7232
+ return false;
7233
+ }
7234
+ return event.ctrlKey || event.metaKey;
7235
+ }
7236
+ function isCmdWithChar(event) {
7237
+ return isCmd(event) && event.key.length === 1 && /^[a-zA-Z]$/.test(event.key);
7238
+ }
7058
7239
  function ToggleSwitch(props) {
7059
7240
  const {
7060
7241
  id,
@@ -7366,7 +7547,7 @@ function FeelTextfield(props) {
7366
7547
  element,
7367
7548
  label,
7368
7549
  hostLanguage,
7369
- onInput,
7550
+ onInput: commitValue,
7370
7551
  onBlur,
7371
7552
  onError,
7372
7553
  placeholder,
@@ -7382,6 +7563,12 @@ function FeelTextfield(props) {
7382
7563
  const [localValue, setLocalValue] = hooks.useState(value);
7383
7564
  const editorRef = useShowEntryEvent(id);
7384
7565
  const containerRef = hooks.useRef();
7566
+ const onInput = hooks.useCallback(newValue => {
7567
+ // we don't commit empty FEEL expressions,
7568
+ // but instead serialize them as <undefined>
7569
+ const newModelValue = newValue === '' || newValue === '=' ? undefined : newValue;
7570
+ commitValue(newModelValue);
7571
+ }, [commitValue]);
7385
7572
  const feelActive = minDash.isString(localValue) && localValue.startsWith('=') || feel === 'required';
7386
7573
  const feelOnlyValue = minDash.isString(localValue) && localValue.startsWith('=') ? localValue.substring(1) : localValue;
7387
7574
  const feelLanguageContext = hooks.useContext(FeelLanguageContext);
@@ -7401,13 +7588,7 @@ function FeelTextfield(props) {
7401
7588
  /**
7402
7589
  * @type { import('min-dash').DebouncedFunction }
7403
7590
  */
7404
- const handleInputCallback = useDebounce(onInput, debounce);
7405
- const handleInput = newValue => {
7406
- // we don't commit empty FEEL expressions,
7407
- // but instead serialize them as <undefined>
7408
- const newModelValue = newValue === '' || newValue === '=' ? undefined : newValue;
7409
- handleInputCallback(newModelValue);
7410
- };
7591
+ const handleInput = useDebounce(onInput, debounce);
7411
7592
  const handleFeelToggle = useStaticCallback(() => {
7412
7593
  if (feel === 'required') {
7413
7594
  return;
@@ -7420,7 +7601,7 @@ function FeelTextfield(props) {
7420
7601
  handleInput(feelOnlyValue);
7421
7602
  }
7422
7603
  });
7423
- const handleLocalInput = newValue => {
7604
+ const handleLocalInput = (newValue, useDebounce = true) => {
7424
7605
  if (feelActive) {
7425
7606
  newValue = '=' + newValue;
7426
7607
  }
@@ -7428,23 +7609,33 @@ function FeelTextfield(props) {
7428
7609
  return;
7429
7610
  }
7430
7611
  setLocalValue(newValue);
7431
- handleInput(newValue);
7612
+ if (useDebounce) {
7613
+ handleInput(newValue);
7614
+ } else {
7615
+ onInput(newValue);
7616
+ }
7432
7617
  if (!feelActive && minDash.isString(newValue) && newValue.startsWith('=')) {
7433
7618
  // focus is behind `=` sign that will be removed
7434
7619
  setFocus(-1);
7435
7620
  }
7436
7621
  };
7437
7622
  const handleOnBlur = e => {
7623
+ handleInput.cancel?.();
7438
7624
  if (e.target.type === 'checkbox') {
7439
7625
  onInput(e.target.checked);
7440
7626
  } else {
7441
7627
  const trimmedValue = e.target.value.trim();
7442
- onInput(trimmedValue);
7628
+ handleLocalInput(trimmedValue, false);
7443
7629
  }
7444
7630
  if (onBlur) {
7445
7631
  onBlur(e);
7446
7632
  }
7447
7633
  };
7634
+ const handleOnKeyDown = e => {
7635
+ if (isCmdWithChar(e)) {
7636
+ handleInput.flush();
7637
+ }
7638
+ };
7448
7639
  const handleLint = useStaticCallback((lint = []) => {
7449
7640
  const syntaxError = lint.some(report => report.type === 'Syntax Error');
7450
7641
  if (syntaxError) {
@@ -7511,12 +7702,26 @@ function FeelTextfield(props) {
7511
7702
  if (feelActive || isPopupOpen) {
7512
7703
  return;
7513
7704
  }
7514
- const data = event.clipboardData.getData('application/FEEL');
7515
- if (data) {
7705
+ const feelData = event.clipboardData.getData('application/FEEL');
7706
+ if (feelData) {
7516
7707
  setTimeout(() => {
7517
7708
  handleFeelToggle();
7518
7709
  setFocus();
7519
7710
  });
7711
+ return;
7712
+ }
7713
+ const input = event.target;
7714
+ const isFieldEmpty = !input.value;
7715
+ const isAllSelected = input.selectionStart === 0 && input.selectionEnd === input.value.length;
7716
+ if (isFieldEmpty || isAllSelected) {
7717
+ const textData = event.clipboardData.getData('text');
7718
+ const trimmedValue = textData.trim();
7719
+ setLocalValue(trimmedValue);
7720
+ handleInput(trimmedValue);
7721
+ if (!feelActive && minDash.isString(trimmedValue) && trimmedValue.startsWith('=')) {
7722
+ setFocus(trimmedValue.length - 1);
7723
+ }
7724
+ event.preventDefault();
7520
7725
  }
7521
7726
  };
7522
7727
  containerRef.current.addEventListener('copy', copyHandler);
@@ -7557,6 +7762,7 @@ function FeelTextfield(props) {
7557
7762
  }), feelActive ? jsxRuntime.jsx(FeelEditor, {
7558
7763
  name: id,
7559
7764
  onInput: handleLocalInput,
7765
+ onKeyDown: handleOnKeyDown,
7560
7766
  contentAttributes: {
7561
7767
  'id': prefixId$5(id),
7562
7768
  'aria-label': label
@@ -7579,6 +7785,7 @@ function FeelTextfield(props) {
7579
7785
  ...props,
7580
7786
  popupOpen: isPopupOpen,
7581
7787
  onInput: handleLocalInput,
7788
+ onKeyDown: handleOnKeyDown,
7582
7789
  onBlur: handleOnBlur,
7583
7790
  contentAttributes: {
7584
7791
  'id': prefixId$5(id),
@@ -7597,6 +7804,7 @@ const OptionalFeelInput = React.forwardRef((props, ref) => {
7597
7804
  id,
7598
7805
  disabled,
7599
7806
  onInput,
7807
+ onKeyDown,
7600
7808
  value,
7601
7809
  onFocus,
7602
7810
  onBlur,
@@ -7632,6 +7840,7 @@ const OptionalFeelInput = React.forwardRef((props, ref) => {
7632
7840
  class: "bio-properties-panel-input",
7633
7841
  onInput: e => onInput(e.target.value),
7634
7842
  onFocus: onFocus,
7843
+ onKeyDown: onKeyDown,
7635
7844
  onBlur: onBlur,
7636
7845
  placeholder: placeholder,
7637
7846
  value: value || ''
@@ -8005,6 +8214,25 @@ function isEdited$5(node) {
8005
8214
  function prefixId$5(id) {
8006
8215
  return `bio-properties-panel-${id}`;
8007
8216
  }
8217
+
8218
+ /**
8219
+ * @typedef { { value: string, label: string, disabled: boolean, children: { value: string, label: string, disabled: boolean } } } Option
8220
+ */
8221
+
8222
+ /**
8223
+ * Provides basic select input.
8224
+ *
8225
+ * @param {object} props
8226
+ * @param {string} props.id
8227
+ * @param {string[]} props.path
8228
+ * @param {string} props.label
8229
+ * @param {Function} props.onChange
8230
+ * @param {Function} props.onFocus
8231
+ * @param {Function} props.onBlur
8232
+ * @param {Array<Option>} [props.options]
8233
+ * @param {string} props.value
8234
+ * @param {boolean} [props.disabled]
8235
+ */
8008
8236
  function Select(props) {
8009
8237
  const {
8010
8238
  id,
@@ -8170,12 +8398,13 @@ function TextArea(props) {
8170
8398
  id,
8171
8399
  label,
8172
8400
  debounce,
8173
- onInput,
8401
+ onInput: commitValue,
8174
8402
  value = '',
8175
8403
  disabled,
8176
8404
  monospace,
8177
8405
  onFocus,
8178
8406
  onBlur,
8407
+ onPaste,
8179
8408
  autoResize = true,
8180
8409
  placeholder,
8181
8410
  rows = autoResize ? 1 : 2,
@@ -8183,16 +8412,16 @@ function TextArea(props) {
8183
8412
  } = props;
8184
8413
  const [localValue, setLocalValue] = hooks.useState(value);
8185
8414
  const ref = useShowEntryEvent(id);
8415
+ const onInput = hooks.useCallback(newValue => {
8416
+ const newModelValue = newValue === '' ? undefined : newValue;
8417
+ commitValue(newModelValue);
8418
+ }, [commitValue]);
8186
8419
  const visible = useElementVisible(ref.current);
8187
8420
 
8188
8421
  /**
8189
8422
  * @type { import('min-dash').DebouncedFunction }
8190
8423
  */
8191
- const handleInputCallback = useDebounce(onInput, debounce);
8192
- const handleInput = newValue => {
8193
- const newModelValue = newValue === '' ? undefined : newValue;
8194
- handleInputCallback(newModelValue);
8195
- };
8424
+ const handleInput = useDebounce(onInput, debounce);
8196
8425
  const handleLocalInput = e => {
8197
8426
  autoResize && resizeToContents(e.target);
8198
8427
  if (e.target.value === localValue) {
@@ -8205,11 +8434,40 @@ function TextArea(props) {
8205
8434
  const trimmedValue = e.target.value.trim();
8206
8435
 
8207
8436
  // trim and commit on blur
8437
+ handleInput.cancel?.();
8208
8438
  onInput(trimmedValue);
8439
+ setLocalValue(trimmedValue);
8209
8440
  if (onBlur) {
8210
8441
  onBlur(e);
8211
8442
  }
8212
8443
  };
8444
+ const handleOnPaste = e => {
8445
+ const input = e.target;
8446
+ const isFieldEmpty = !input.value;
8447
+ const isAllSelected = input.selectionStart === 0 && input.selectionEnd === input.value.length;
8448
+
8449
+ // Trim and handle paste if field is empty or all content is selected
8450
+ if (isFieldEmpty || isAllSelected) {
8451
+ const trimmedValue = e.clipboardData.getData('text').trim();
8452
+ setLocalValue(trimmedValue);
8453
+ handleInput(trimmedValue);
8454
+ if (onPaste) {
8455
+ onPaste(e);
8456
+ }
8457
+ e.preventDefault();
8458
+ return;
8459
+ }
8460
+
8461
+ // Allow default paste behavior for normal text editing
8462
+ if (onPaste) {
8463
+ onPaste(e);
8464
+ }
8465
+ };
8466
+ const handleOnKeyDown = e => {
8467
+ if (isCmdWithChar(e)) {
8468
+ handleInput.flush();
8469
+ }
8470
+ };
8213
8471
  hooks.useLayoutEffect(() => {
8214
8472
  autoResize && resizeToContents(ref.current);
8215
8473
  }, []);
@@ -8241,7 +8499,9 @@ function TextArea(props) {
8241
8499
  class: classnames('bio-properties-panel-input', monospace ? 'bio-properties-panel-input-monospace' : '', autoResize ? 'auto-resize' : ''),
8242
8500
  onInput: handleLocalInput,
8243
8501
  onFocus: onFocus,
8502
+ onKeyDown: handleOnKeyDown,
8244
8503
  onBlur: handleOnBlur,
8504
+ onPaste: handleOnPaste,
8245
8505
  placeholder: placeholder,
8246
8506
  rows: rows,
8247
8507
  value: localValue,
@@ -8262,6 +8522,7 @@ function TextArea(props) {
8262
8522
  * @param {Function} props.setValue
8263
8523
  * @param {Function} props.onFocus
8264
8524
  * @param {Function} props.onBlur
8525
+ * @param {Function} props.onPaste
8265
8526
  * @param {number} props.rows
8266
8527
  * @param {boolean} props.monospace
8267
8528
  * @param {Function} [props.validate]
@@ -8282,6 +8543,7 @@ function TextAreaEntry(props) {
8282
8543
  validate,
8283
8544
  onFocus,
8284
8545
  onBlur,
8546
+ onPaste,
8285
8547
  placeholder,
8286
8548
  autoResize,
8287
8549
  tooltip
@@ -8317,6 +8579,7 @@ function TextAreaEntry(props) {
8317
8579
  onInput: onInput,
8318
8580
  onFocus: onFocus,
8319
8581
  onBlur: onBlur,
8582
+ onPaste: onPaste,
8320
8583
  rows: rows,
8321
8584
  debounce: debounce,
8322
8585
  monospace: monospace,
@@ -8350,32 +8613,57 @@ function Textfield(props) {
8350
8613
  disabled = false,
8351
8614
  id,
8352
8615
  label,
8353
- onInput,
8616
+ onInput: commitValue,
8354
8617
  onFocus,
8355
8618
  onBlur,
8619
+ onPaste,
8356
8620
  placeholder,
8357
8621
  value = '',
8358
8622
  tooltip
8359
8623
  } = props;
8360
8624
  const [localValue, setLocalValue] = hooks.useState(value || '');
8361
8625
  const ref = useShowEntryEvent(id);
8626
+ const onInput = hooks.useCallback(newValue => {
8627
+ const newModelValue = newValue === '' ? undefined : newValue;
8628
+ commitValue(newModelValue);
8629
+ }, [commitValue]);
8362
8630
 
8363
8631
  /**
8364
8632
  * @type { import('min-dash').DebouncedFunction }
8365
8633
  */
8366
- const handleInputCallback = useDebounce(onInput, debounce);
8634
+ const handleInput = useDebounce(onInput, debounce);
8367
8635
  const handleOnBlur = e => {
8368
8636
  const trimmedValue = e.target.value.trim();
8369
8637
 
8370
8638
  // trim and commit on blur
8639
+ handleInput.cancel?.();
8371
8640
  onInput(trimmedValue);
8641
+ setLocalValue(trimmedValue);
8372
8642
  if (onBlur) {
8373
8643
  onBlur(e);
8374
8644
  }
8375
8645
  };
8376
- const handleInput = newValue => {
8377
- const newModelValue = newValue === '' ? undefined : newValue;
8378
- handleInputCallback(newModelValue);
8646
+ const handleOnPaste = e => {
8647
+ const input = e.target;
8648
+ const isFieldEmpty = !input.value;
8649
+ const isAllSelected = input.selectionStart === 0 && input.selectionEnd === input.value.length;
8650
+
8651
+ // Trim and handle paste if field is empty or all content is selected (overwrite)
8652
+ if (isFieldEmpty || isAllSelected) {
8653
+ const trimmedValue = e.clipboardData.getData('text').trim();
8654
+ setLocalValue(trimmedValue);
8655
+ handleInput(trimmedValue);
8656
+ if (onPaste) {
8657
+ onPaste(e);
8658
+ }
8659
+ e.preventDefault();
8660
+ return;
8661
+ }
8662
+
8663
+ // Allow default paste behavior for normal text editing
8664
+ if (onPaste) {
8665
+ onPaste(e);
8666
+ }
8379
8667
  };
8380
8668
  const handleLocalInput = e => {
8381
8669
  if (e.target.value === localValue) {
@@ -8390,6 +8678,11 @@ function Textfield(props) {
8390
8678
  }
8391
8679
  setLocalValue(value);
8392
8680
  }, [value]);
8681
+ const handleOnKeyDown = e => {
8682
+ if (isCmdWithChar(e)) {
8683
+ handleInput.flush();
8684
+ }
8685
+ };
8393
8686
  return jsxRuntime.jsxs("div", {
8394
8687
  class: "bio-properties-panel-textfield",
8395
8688
  children: [jsxRuntime.jsx("label", {
@@ -8412,7 +8705,9 @@ function Textfield(props) {
8412
8705
  class: "bio-properties-panel-input",
8413
8706
  onInput: handleLocalInput,
8414
8707
  onFocus: onFocus,
8708
+ onKeyDown: handleOnKeyDown,
8415
8709
  onBlur: handleOnBlur,
8710
+ onPaste: handleOnPaste,
8416
8711
  placeholder: placeholder,
8417
8712
  value: localValue
8418
8713
  })]
@@ -8447,6 +8742,7 @@ function TextfieldEntry(props) {
8447
8742
  validate,
8448
8743
  onFocus,
8449
8744
  onBlur,
8745
+ onPaste,
8450
8746
  placeholder,
8451
8747
  tooltip
8452
8748
  } = props;
@@ -8482,6 +8778,7 @@ function TextfieldEntry(props) {
8482
8778
  onInput: onInput,
8483
8779
  onFocus: onFocus,
8484
8780
  onBlur: onBlur,
8781
+ onPaste: onPaste,
8485
8782
  placeholder: placeholder,
8486
8783
  value: value,
8487
8784
  tooltip: tooltip,
@@ -8792,6 +9089,25 @@ function cancel(event) {
8792
9089
  event.preventDefault();
8793
9090
  event.stopPropagation();
8794
9091
  }
9092
+
9093
+ /**
9094
+ * @typedef {Object} FeelPopupProps
9095
+ * @property {string} entryId
9096
+ * @property {Function} onInput
9097
+ * @property {Function} onClose
9098
+ * @property {string} title
9099
+ * @property {'feel'|'feelers'} type
9100
+ * @property {string} value
9101
+ * @property {Array} [links]
9102
+ * @property {Array|Object} [variables]
9103
+ * @property {Object} [position]
9104
+ * @property {string} [hostLanguage]
9105
+ * @property {boolean} [singleLine]
9106
+ * @property {HTMLElement} [sourceElement]
9107
+ * @property {HTMLElement|string} [tooltipContainer]
9108
+ * @property {Object} [eventBus]
9109
+ */
9110
+
8795
9111
  const FEEL_POPUP_WIDTH = 700;
8796
9112
  const FEEL_POPUP_HEIGHT = 250;
8797
9113
 
@@ -11475,7 +11791,14 @@ function Value$1(props) {
11475
11791
  if (error) {
11476
11792
  return;
11477
11793
  }
11794
+ const {
11795
+ defaultValue
11796
+ } = field;
11478
11797
  const values = minDash.get(field, ['values']);
11798
+ const previousValue = minDash.get(field, ['values', index, 'value']);
11799
+ if (!minDash.isNil(defaultValue) && defaultValue === previousValue) {
11800
+ minDash.set(field, ['defaultValue'], value);
11801
+ }
11479
11802
  return editField(field, 'values', minDash.set(values, [index, 'value'], value));
11480
11803
  };
11481
11804
  const getValue = () => {
@@ -12885,7 +13208,7 @@ function DocumentsDataSource(props) {
12885
13208
  children: "When using Camunda Tasklist UI, additional document reference attributes are automatically handled. Modifying the document reference may affect the document preview functionality."
12886
13209
  }), jsxRuntime.jsxs("p", {
12887
13210
  children: ["Learn more in our", ' ', jsxRuntime.jsx("a", {
12888
- href: "https://docs.camunda.io/docs/8.7/components/modeler/forms/form-element-library/forms-element-library-document-preview/",
13211
+ href: "https://docs.camunda.io/docs/components/modeler/forms/form-element-library/forms-element-library-document-preview/",
12889
13212
  target: "_blank",
12890
13213
  rel: "noopener noreferrer",
12891
13214
  children: "documentation"
@@ -13497,7 +13820,7 @@ function CustomPropertiesGroup(field, editField) {
13497
13820
  event.stopPropagation();
13498
13821
  return editField(field, ['properties'], removeKey(properties, key));
13499
13822
  };
13500
- const id = `property-${index}`;
13823
+ const id = `property-${field.id}-${index}`;
13501
13824
  return {
13502
13825
  autoFocusEntry: id + '-key',
13503
13826
  entries: CustomValueEntry({
@@ -13879,7 +14202,7 @@ const RepeatRenderModule = {
13879
14202
  repeatRenderManager: ['type', EditorRepeatRenderManager]
13880
14203
  };
13881
14204
 
13882
- const ids = new Ids([32, 36, 1]);
14205
+ const ids = new ids$1.Ids([32, 36, 1]);
13883
14206
 
13884
14207
  /**
13885
14208
  * @typedef { import('./types').Injector } Injector