@bpmn-io/form-js-editor 1.18.0 → 1.20.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/assets/form-js-editor.css +7 -1
- package/dist/assets/properties-panel.css +7 -1
- package/dist/index.cjs +522 -77
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +522 -77
- package/dist/index.es.js.map +1 -1
- package/package.json +7 -7
package/dist/index.es.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import Ids from 'ids';
|
|
1
|
+
import { Ids } from 'ids';
|
|
2
2
|
import { FormFieldRegistry as FormFieldRegistry$1, iconsByType, Label as Label$3, IFrame, Text as Text$1, Html, Table, ExpressionField, DocumentPreview, FormFields, sanitizeImageSource, getAncestryList, FormContext, FormRenderContext, FormComponent, getScrollContainer, FieldFactory, FormLayouter, PathRegistry, Importer, FeelExpressionLanguage, OPTIONS_SOURCES, OPTIONS_SOURCES_PATHS, clone, runRecursively, getSchemaVariables, DATETIME_SUBTYPES, DATE_LABEL_PATH, TIME_LABEL_PATH, TEXT_VIEW_DEFAULT_TEXT, TIME_USE24H_PATH, DATETIME_SUBTYPE_PATH, DATETIME_SUBTYPES_LABELS, TIME_INTERVAL_PATH, TIME_SERIALISING_FORMAT_PATH, DATE_DISALLOW_PAST_PATH, TIME_SERIALISING_FORMATS, TIME_SERIALISINGFORMAT_LABELS, getOptionsSource, OPTIONS_SOURCES_DEFAULTS, OPTIONS_SOURCES_LABELS, SECURITY_ATTRIBUTES_DEFINITIONS, createFormContainer, createInjector, MarkdownRendererModule, schemaVersion } from '@bpmn-io/form-js-viewer';
|
|
3
3
|
export { schemaVersion } from '@bpmn-io/form-js-viewer';
|
|
4
4
|
import { isArray, isFunction, isNumber, bind, assign, debounce, forEach, isString, uniqueBy, isObject, get, isDefined, set as set$1, reduce, isNil, without, has } from 'min-dash';
|
|
@@ -13,7 +13,7 @@ import { classes, query, event, domify } from 'min-dom';
|
|
|
13
13
|
import { arrayMoveMutable } from 'array-move';
|
|
14
14
|
import { FeelersEditor } from 'feelers';
|
|
15
15
|
import Editor from '@bpmn-io/feel-editor';
|
|
16
|
-
import {
|
|
16
|
+
import { EditorView, lineNumbers } from '@codemirror/view';
|
|
17
17
|
import * as focusTrap from 'focus-trap';
|
|
18
18
|
import Big from 'big.js';
|
|
19
19
|
|
|
@@ -2870,11 +2870,35 @@ EditorActions.prototype._registerDefaultActions = function (injector) {
|
|
|
2870
2870
|
}
|
|
2871
2871
|
});
|
|
2872
2872
|
}
|
|
2873
|
+
if (copyPaste && selection) {
|
|
2874
|
+
this.register('duplicate', function () {
|
|
2875
|
+
var selectedElements = selection.get();
|
|
2876
|
+
if (selectedElements.length) {
|
|
2877
|
+
return copyPaste.duplicate(selectedElements);
|
|
2878
|
+
}
|
|
2879
|
+
});
|
|
2880
|
+
}
|
|
2873
2881
|
if (copyPaste) {
|
|
2874
2882
|
this.register('paste', function () {
|
|
2875
2883
|
copyPaste.paste();
|
|
2876
2884
|
});
|
|
2877
2885
|
}
|
|
2886
|
+
if (copyPaste && selection && rules) {
|
|
2887
|
+
this.register('cut', function () {
|
|
2888
|
+
var selectedElements = selection.get();
|
|
2889
|
+
if (!selectedElements.length) {
|
|
2890
|
+
return;
|
|
2891
|
+
}
|
|
2892
|
+
var allowed = rules.allowed('elements.delete', {
|
|
2893
|
+
elements: selectedElements
|
|
2894
|
+
});
|
|
2895
|
+
if (allowed === false) {
|
|
2896
|
+
return;
|
|
2897
|
+
}
|
|
2898
|
+
var cuttableElements = isArray(allowed) ? allowed : selectedElements;
|
|
2899
|
+
return copyPaste.cut(cuttableElements.slice());
|
|
2900
|
+
});
|
|
2901
|
+
}
|
|
2878
2902
|
if (zoomScroll) {
|
|
2879
2903
|
this.register('stepZoom', function (opts) {
|
|
2880
2904
|
zoomScroll.stepZoom(opts.value);
|
|
@@ -3093,6 +3117,8 @@ const EditorExpressionLanguageModule = {
|
|
|
3093
3117
|
|
|
3094
3118
|
var KEYS_COPY = ['c', 'C'];
|
|
3095
3119
|
var KEYS_PASTE = ['v', 'V'];
|
|
3120
|
+
var KEYS_DUPLICATE = ['d', 'D'];
|
|
3121
|
+
var KEYS_CUT = ['x', 'X'];
|
|
3096
3122
|
var KEYS_REDO = ['y', 'Y'];
|
|
3097
3123
|
var KEYS_UNDO = ['z', 'Z'];
|
|
3098
3124
|
|
|
@@ -3108,7 +3134,7 @@ function hasModifier(event) {
|
|
|
3108
3134
|
* @param {KeyboardEvent} event
|
|
3109
3135
|
* @return {boolean}
|
|
3110
3136
|
*/
|
|
3111
|
-
function isCmd(event) {
|
|
3137
|
+
function isCmd$1(event) {
|
|
3112
3138
|
// ensure we don't react to AltGr
|
|
3113
3139
|
// (mapped to CTRL + ALT)
|
|
3114
3140
|
if (event.altKey) {
|
|
@@ -3140,28 +3166,42 @@ function isShift(event) {
|
|
|
3140
3166
|
* @param {KeyboardEvent} event
|
|
3141
3167
|
*/
|
|
3142
3168
|
function isCopy(event) {
|
|
3143
|
-
return isCmd(event) && isKey(KEYS_COPY, event);
|
|
3169
|
+
return isCmd$1(event) && isKey(KEYS_COPY, event);
|
|
3144
3170
|
}
|
|
3145
3171
|
|
|
3146
3172
|
/**
|
|
3147
3173
|
* @param {KeyboardEvent} event
|
|
3148
3174
|
*/
|
|
3149
3175
|
function isPaste(event) {
|
|
3150
|
-
return isCmd(event) && isKey(KEYS_PASTE, event);
|
|
3176
|
+
return isCmd$1(event) && isKey(KEYS_PASTE, event);
|
|
3177
|
+
}
|
|
3178
|
+
|
|
3179
|
+
/**
|
|
3180
|
+
* @param {KeyboardEvent} event
|
|
3181
|
+
*/
|
|
3182
|
+
function isDuplicate(event) {
|
|
3183
|
+
return isCmd$1(event) && isKey(KEYS_DUPLICATE, event);
|
|
3184
|
+
}
|
|
3185
|
+
|
|
3186
|
+
/**
|
|
3187
|
+
* @param {KeyboardEvent} event
|
|
3188
|
+
*/
|
|
3189
|
+
function isCut(event) {
|
|
3190
|
+
return isCmd$1(event) && isKey(KEYS_CUT, event);
|
|
3151
3191
|
}
|
|
3152
3192
|
|
|
3153
3193
|
/**
|
|
3154
3194
|
* @param {KeyboardEvent} event
|
|
3155
3195
|
*/
|
|
3156
3196
|
function isUndo(event) {
|
|
3157
|
-
return isCmd(event) && !isShift(event) && isKey(KEYS_UNDO, event);
|
|
3197
|
+
return isCmd$1(event) && !isShift(event) && isKey(KEYS_UNDO, event);
|
|
3158
3198
|
}
|
|
3159
3199
|
|
|
3160
3200
|
/**
|
|
3161
3201
|
* @param {KeyboardEvent} event
|
|
3162
3202
|
*/
|
|
3163
3203
|
function isRedo(event) {
|
|
3164
|
-
return isCmd(event) && (isKey(KEYS_REDO, event) || isKey(KEYS_UNDO, event) && isShift(event));
|
|
3204
|
+
return isCmd$1(event) && (isKey(KEYS_REDO, event) || isKey(KEYS_UNDO, event) && isShift(event));
|
|
3165
3205
|
}
|
|
3166
3206
|
|
|
3167
3207
|
/**
|
|
@@ -3330,7 +3370,7 @@ Keyboard.prototype.removeListener = function (listener, type) {
|
|
|
3330
3370
|
this._eventBus.off(type || KEYDOWN_EVENT, listener);
|
|
3331
3371
|
};
|
|
3332
3372
|
Keyboard.prototype.hasModifier = hasModifier;
|
|
3333
|
-
Keyboard.prototype.isCmd = isCmd;
|
|
3373
|
+
Keyboard.prototype.isCmd = isCmd$1;
|
|
3334
3374
|
Keyboard.prototype.isShift = isShift;
|
|
3335
3375
|
Keyboard.prototype.isKey = isKey;
|
|
3336
3376
|
|
|
@@ -3415,6 +3455,26 @@ KeyboardBindings.prototype.registerBindings = function (keyboard, editorActions)
|
|
|
3415
3455
|
}
|
|
3416
3456
|
});
|
|
3417
3457
|
|
|
3458
|
+
// duplicate
|
|
3459
|
+
// CTRL/CMD + D
|
|
3460
|
+
addListener('duplicate', function (context) {
|
|
3461
|
+
var event = context.keyEvent;
|
|
3462
|
+
if (isDuplicate(event)) {
|
|
3463
|
+
editorActions.trigger('duplicate');
|
|
3464
|
+
return true;
|
|
3465
|
+
}
|
|
3466
|
+
});
|
|
3467
|
+
|
|
3468
|
+
// cut
|
|
3469
|
+
// CTRL/CMD + X
|
|
3470
|
+
addListener('cut', function (context) {
|
|
3471
|
+
var event = context.keyEvent;
|
|
3472
|
+
if (isCut(event)) {
|
|
3473
|
+
editorActions.trigger('cut');
|
|
3474
|
+
return true;
|
|
3475
|
+
}
|
|
3476
|
+
});
|
|
3477
|
+
|
|
3418
3478
|
// zoom in one step
|
|
3419
3479
|
// CTRL/CMD + +
|
|
3420
3480
|
addListener('stepZoom', function (context) {
|
|
@@ -3422,7 +3482,7 @@ KeyboardBindings.prototype.registerBindings = function (keyboard, editorActions)
|
|
|
3422
3482
|
|
|
3423
3483
|
// quirk: it has to be triggered by `=` as well to work on international keyboard layout
|
|
3424
3484
|
// cf: https://github.com/bpmn-io/bpmn-js/issues/1362#issuecomment-722989754
|
|
3425
|
-
if (isKey(['+', 'Add', '='], event) && isCmd(event)) {
|
|
3485
|
+
if (isKey(['+', 'Add', '='], event) && isCmd$1(event)) {
|
|
3426
3486
|
editorActions.trigger('stepZoom', {
|
|
3427
3487
|
value: 1
|
|
3428
3488
|
});
|
|
@@ -3434,7 +3494,7 @@ KeyboardBindings.prototype.registerBindings = function (keyboard, editorActions)
|
|
|
3434
3494
|
// CTRL + -
|
|
3435
3495
|
addListener('stepZoom', function (context) {
|
|
3436
3496
|
var event = context.keyEvent;
|
|
3437
|
-
if (isKey(['-', 'Subtract'], event) && isCmd(event)) {
|
|
3497
|
+
if (isKey(['-', 'Subtract'], event) && isCmd$1(event)) {
|
|
3438
3498
|
editorActions.trigger('stepZoom', {
|
|
3439
3499
|
value: -1
|
|
3440
3500
|
});
|
|
@@ -3446,7 +3506,7 @@ KeyboardBindings.prototype.registerBindings = function (keyboard, editorActions)
|
|
|
3446
3506
|
// CTRL + 0
|
|
3447
3507
|
addListener('zoom', function (context) {
|
|
3448
3508
|
var event = context.keyEvent;
|
|
3449
|
-
if (isKey('0', event) && isCmd(event)) {
|
|
3509
|
+
if (isKey('0', event) && isCmd$1(event)) {
|
|
3450
3510
|
editorActions.trigger('zoom', {
|
|
3451
3511
|
value: 1
|
|
3452
3512
|
});
|
|
@@ -5453,6 +5513,21 @@ OpenPopupIcon.defaultProps = {
|
|
|
5453
5513
|
xmlns: "http://www.w3.org/2000/svg",
|
|
5454
5514
|
viewBox: "0 0 16 16"
|
|
5455
5515
|
};
|
|
5516
|
+
|
|
5517
|
+
/**
|
|
5518
|
+
* @typedef { {
|
|
5519
|
+
* getElementLabel: (element: object) => string,
|
|
5520
|
+
* getTypeLabel: (element: object) => string,
|
|
5521
|
+
* getElementIcon: (element: object) => import('preact').Component,
|
|
5522
|
+
* getDocumentationRef: (element: object) => string
|
|
5523
|
+
* } } HeaderProvider
|
|
5524
|
+
*/
|
|
5525
|
+
|
|
5526
|
+
/**
|
|
5527
|
+
* @param {Object} props
|
|
5528
|
+
* @param {Object} props.element,
|
|
5529
|
+
* @param {HeaderProvider} props.headerProvider
|
|
5530
|
+
*/
|
|
5456
5531
|
function Header(props) {
|
|
5457
5532
|
const {
|
|
5458
5533
|
element,
|
|
@@ -5480,11 +5555,9 @@ function Header(props) {
|
|
|
5480
5555
|
}), jsxs("div", {
|
|
5481
5556
|
class: "bio-properties-panel-header-labels",
|
|
5482
5557
|
children: [jsx("div", {
|
|
5483
|
-
title: type,
|
|
5484
5558
|
class: "bio-properties-panel-header-type",
|
|
5485
5559
|
children: type
|
|
5486
5560
|
}), label ? jsx("div", {
|
|
5487
|
-
title: label,
|
|
5488
5561
|
class: "bio-properties-panel-header-label",
|
|
5489
5562
|
children: label
|
|
5490
5563
|
}) : null]
|
|
@@ -5572,6 +5645,27 @@ function useTooltipContext(id, element) {
|
|
|
5572
5645
|
} = useContext(TooltipContext);
|
|
5573
5646
|
return getTooltipForId(id, element);
|
|
5574
5647
|
}
|
|
5648
|
+
|
|
5649
|
+
/**
|
|
5650
|
+
* @typedef {Object} TooltipProps
|
|
5651
|
+
* @property {Object} [parent] - Parent element ref for portal rendering
|
|
5652
|
+
* @property {String} [direction='right'] - Tooltip direction ( 'right', 'top')
|
|
5653
|
+
* @property {String} [position] - Custom CSS position override
|
|
5654
|
+
* @property {Number} [showDelay=250] - Delay in ms before showing tooltip on hover
|
|
5655
|
+
* @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
|
|
5656
|
+
* @property {*} [children] - Child elements to render inside the tooltip wrapper
|
|
5657
|
+
*/
|
|
5658
|
+
|
|
5659
|
+
/**
|
|
5660
|
+
* Tooltip wrapper that provides context-based tooltip content lookup.
|
|
5661
|
+
* All props are forwarded to the underlying Tooltip component.
|
|
5662
|
+
*
|
|
5663
|
+
* @param {TooltipProps & {
|
|
5664
|
+
* forId: String,
|
|
5665
|
+
* value?: String|Object,
|
|
5666
|
+
* element?: Object
|
|
5667
|
+
* }} props - Shared tooltip props plus wrapper-specific ones
|
|
5668
|
+
*/
|
|
5575
5669
|
function TooltipWrapper(props) {
|
|
5576
5670
|
const {
|
|
5577
5671
|
forId,
|
|
@@ -5588,35 +5682,93 @@ function TooltipWrapper(props) {
|
|
|
5588
5682
|
forId: `bio-properties-panel-${forId}`
|
|
5589
5683
|
});
|
|
5590
5684
|
}
|
|
5685
|
+
|
|
5686
|
+
/**
|
|
5687
|
+
* @param {TooltipProps & {
|
|
5688
|
+
* forId: String,
|
|
5689
|
+
* value: String|Object
|
|
5690
|
+
* }} props
|
|
5691
|
+
*/
|
|
5591
5692
|
function Tooltip(props) {
|
|
5592
5693
|
const {
|
|
5593
5694
|
forId,
|
|
5594
5695
|
value,
|
|
5595
5696
|
parent,
|
|
5596
5697
|
direction = 'right',
|
|
5597
|
-
position
|
|
5698
|
+
position,
|
|
5699
|
+
showDelay = 250,
|
|
5700
|
+
hideDelay = 250
|
|
5598
5701
|
} = props;
|
|
5599
5702
|
const [visible, setVisible] = useState(false);
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
const
|
|
5603
|
-
|
|
5703
|
+
const [tooltipPosition, setTooltipPosition] = useState(null);
|
|
5704
|
+
const [arrowOffset, setArrowOffset] = useState(null);
|
|
5705
|
+
const showTimeoutRef = useRef(null);
|
|
5706
|
+
const hideTimeoutRef = useRef(null);
|
|
5604
5707
|
const wrapperRef = useRef(null);
|
|
5605
5708
|
const tooltipRef = useRef(null);
|
|
5606
5709
|
const show = (_, delay) => {
|
|
5710
|
+
clearTimeout(showTimeoutRef.current);
|
|
5711
|
+
clearTimeout(hideTimeoutRef.current);
|
|
5607
5712
|
if (visible) return;
|
|
5608
5713
|
if (delay) {
|
|
5609
|
-
|
|
5714
|
+
showTimeoutRef.current = setTimeout(() => {
|
|
5610
5715
|
setVisible(true);
|
|
5611
|
-
},
|
|
5716
|
+
}, showDelay);
|
|
5612
5717
|
} else {
|
|
5613
5718
|
setVisible(true);
|
|
5614
5719
|
}
|
|
5615
5720
|
};
|
|
5616
|
-
const
|
|
5617
|
-
|
|
5618
|
-
|
|
5721
|
+
const handleWrapperMouseEnter = e => {
|
|
5722
|
+
show(e, true);
|
|
5723
|
+
};
|
|
5724
|
+
const hide = (delay = false) => {
|
|
5725
|
+
clearTimeout(showTimeoutRef.current);
|
|
5726
|
+
clearTimeout(hideTimeoutRef.current);
|
|
5727
|
+
if (delay) {
|
|
5728
|
+
hideTimeoutRef.current = setTimeout(() => {
|
|
5729
|
+
setVisible(false);
|
|
5730
|
+
}, hideDelay);
|
|
5731
|
+
} else {
|
|
5732
|
+
setVisible(false);
|
|
5733
|
+
}
|
|
5619
5734
|
};
|
|
5735
|
+
|
|
5736
|
+
// Cleanup timeouts on unmount
|
|
5737
|
+
useEffect(() => {
|
|
5738
|
+
return () => {
|
|
5739
|
+
clearTimeout(showTimeoutRef.current);
|
|
5740
|
+
clearTimeout(hideTimeoutRef.current);
|
|
5741
|
+
};
|
|
5742
|
+
}, []);
|
|
5743
|
+
|
|
5744
|
+
// Handle click outside to close tooltip for non-focusable elements
|
|
5745
|
+
useEffect(() => {
|
|
5746
|
+
if (!visible) return;
|
|
5747
|
+
const handleClickOutside = e => {
|
|
5748
|
+
// If clicking outside both the wrapper and tooltip, hide it
|
|
5749
|
+
if (wrapperRef.current && !wrapperRef.current.contains(e.target) && tooltipRef.current && !tooltipRef.current.contains(e.target)) {
|
|
5750
|
+
hide(false);
|
|
5751
|
+
}
|
|
5752
|
+
};
|
|
5753
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
5754
|
+
return () => {
|
|
5755
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
5756
|
+
};
|
|
5757
|
+
}, [visible, hide]);
|
|
5758
|
+
useLayoutEffect(() => {
|
|
5759
|
+
if (!visible || position) {
|
|
5760
|
+
setTooltipPosition(null);
|
|
5761
|
+
setArrowOffset(null);
|
|
5762
|
+
return;
|
|
5763
|
+
}
|
|
5764
|
+
if (!wrapperRef.current || !tooltipRef.current) return;
|
|
5765
|
+
const {
|
|
5766
|
+
tooltipPosition: newPosition,
|
|
5767
|
+
arrowOffset: newArrowOffset
|
|
5768
|
+
} = getTooltipPosition(wrapperRef.current, tooltipRef.current, direction);
|
|
5769
|
+
setTooltipPosition(newPosition);
|
|
5770
|
+
setArrowOffset(newArrowOffset);
|
|
5771
|
+
}, [visible, position]);
|
|
5620
5772
|
const handleMouseLeave = ({
|
|
5621
5773
|
relatedTarget
|
|
5622
5774
|
}) => {
|
|
@@ -5624,39 +5776,52 @@ function Tooltip(props) {
|
|
|
5624
5776
|
if (relatedTarget === wrapperRef.current || relatedTarget === tooltipRef.current || relatedTarget?.parentElement === tooltipRef.current) {
|
|
5625
5777
|
return;
|
|
5626
5778
|
}
|
|
5627
|
-
|
|
5779
|
+
const selection = window.getSelection();
|
|
5780
|
+
if (selection && selection.toString().length > 0) {
|
|
5781
|
+
// Check if selection is within tooltip content
|
|
5782
|
+
const selectionRange = selection.getRangeAt(0);
|
|
5783
|
+
if (tooltipRef.current?.contains(selectionRange.commonAncestorContainer) || tooltipRef.current?.contains(selection.anchorNode) || tooltipRef.current?.contains(selection.focusNode)) {
|
|
5784
|
+
return; // Keep tooltip open during text selection
|
|
5785
|
+
}
|
|
5786
|
+
}
|
|
5787
|
+
hide(true);
|
|
5788
|
+
};
|
|
5789
|
+
const handleTooltipMouseEnter = () => {
|
|
5790
|
+
clearTimeout(hideTimeoutRef.current);
|
|
5628
5791
|
};
|
|
5629
5792
|
const handleFocusOut = e => {
|
|
5630
5793
|
const {
|
|
5631
|
-
|
|
5794
|
+
relatedTarget
|
|
5632
5795
|
} = e;
|
|
5633
5796
|
|
|
5634
|
-
// Don't hide
|
|
5635
|
-
|
|
5636
|
-
if (target === wrapperRef.current && isHovered) {
|
|
5637
|
-
e.stopPropagation();
|
|
5797
|
+
// Don't hide if focus moved to the tooltip or another element within the wrapper
|
|
5798
|
+
if (tooltipRef.current?.contains(relatedTarget) || wrapperRef.current?.contains(relatedTarget)) {
|
|
5638
5799
|
return;
|
|
5639
5800
|
}
|
|
5640
|
-
hide();
|
|
5801
|
+
hide(false);
|
|
5641
5802
|
};
|
|
5642
5803
|
const hideTooltipViaEscape = e => {
|
|
5643
|
-
e.code === 'Escape' && hide();
|
|
5804
|
+
e.code === 'Escape' && hide(false);
|
|
5644
5805
|
};
|
|
5645
5806
|
const renderTooltip = () => {
|
|
5807
|
+
const tooltipStyle = position || (tooltipPosition ? `right: ${tooltipPosition.right}; top: ${tooltipPosition.top}px;` : undefined);
|
|
5808
|
+
const arrowStyle = arrowOffset != null ? `margin-top: ${arrowOffset}px;` : undefined;
|
|
5646
5809
|
return jsxs("div", {
|
|
5647
5810
|
class: `bio-properties-panel-tooltip ${direction}`,
|
|
5648
5811
|
role: "tooltip",
|
|
5649
5812
|
id: "bio-properties-panel-tooltip",
|
|
5650
5813
|
"aria-labelledby": forId,
|
|
5651
|
-
style:
|
|
5814
|
+
style: tooltipStyle,
|
|
5652
5815
|
ref: tooltipRef,
|
|
5653
5816
|
onClick: e => e.stopPropagation(),
|
|
5817
|
+
onMouseEnter: handleTooltipMouseEnter,
|
|
5654
5818
|
onMouseLeave: handleMouseLeave,
|
|
5655
5819
|
children: [jsx("div", {
|
|
5656
5820
|
class: "bio-properties-panel-tooltip-content",
|
|
5657
5821
|
children: value
|
|
5658
5822
|
}), jsx("div", {
|
|
5659
|
-
class: "bio-properties-panel-tooltip-arrow"
|
|
5823
|
+
class: "bio-properties-panel-tooltip-arrow",
|
|
5824
|
+
style: arrowStyle
|
|
5660
5825
|
})]
|
|
5661
5826
|
});
|
|
5662
5827
|
};
|
|
@@ -5664,7 +5829,7 @@ function Tooltip(props) {
|
|
|
5664
5829
|
class: "bio-properties-panel-tooltip-wrapper",
|
|
5665
5830
|
tabIndex: "0",
|
|
5666
5831
|
ref: wrapperRef,
|
|
5667
|
-
onMouseEnter:
|
|
5832
|
+
onMouseEnter: handleWrapperMouseEnter,
|
|
5668
5833
|
onMouseLeave: handleMouseLeave,
|
|
5669
5834
|
onFocus: show,
|
|
5670
5835
|
onBlur: handleFocusOut,
|
|
@@ -5675,11 +5840,47 @@ function Tooltip(props) {
|
|
|
5675
5840
|
|
|
5676
5841
|
// helper
|
|
5677
5842
|
|
|
5678
|
-
function getTooltipPosition(refElement) {
|
|
5843
|
+
function getTooltipPosition(refElement, tooltipElement, direction) {
|
|
5844
|
+
if (!refElement) {
|
|
5845
|
+
return {
|
|
5846
|
+
tooltipPosition: null,
|
|
5847
|
+
arrowOffset: null
|
|
5848
|
+
};
|
|
5849
|
+
}
|
|
5679
5850
|
const refPosition = refElement.getBoundingClientRect();
|
|
5680
5851
|
const right = `calc(100% - ${refPosition.x}px)`;
|
|
5681
|
-
|
|
5682
|
-
|
|
5852
|
+
let top = refPosition.top - 10;
|
|
5853
|
+
let arrowOffset = null;
|
|
5854
|
+
|
|
5855
|
+
// Ensure that the tooltip is within the viewport, adjust the top position if needed.
|
|
5856
|
+
// This is only relevant for the 'right' direction for now
|
|
5857
|
+
if (tooltipElement && direction === 'right') {
|
|
5858
|
+
const tooltipRect = tooltipElement.getBoundingClientRect();
|
|
5859
|
+
const viewportHeight = window.innerHeight;
|
|
5860
|
+
const minTop = 0;
|
|
5861
|
+
const maxTop = viewportHeight - tooltipRect.height;
|
|
5862
|
+
const originalTop = top;
|
|
5863
|
+
if (top > maxTop) {
|
|
5864
|
+
top = maxTop;
|
|
5865
|
+
}
|
|
5866
|
+
if (top < minTop) {
|
|
5867
|
+
top = minTop;
|
|
5868
|
+
}
|
|
5869
|
+
|
|
5870
|
+
// Adjust the arrow position if the tooltip had to be moved to stay within viewport
|
|
5871
|
+
if (top !== originalTop) {
|
|
5872
|
+
const defaultMarginTop = 16;
|
|
5873
|
+
const topDiff = top - originalTop;
|
|
5874
|
+
arrowOffset = defaultMarginTop - topDiff;
|
|
5875
|
+
}
|
|
5876
|
+
}
|
|
5877
|
+
return {
|
|
5878
|
+
tooltipPosition: {
|
|
5879
|
+
right,
|
|
5880
|
+
top
|
|
5881
|
+
},
|
|
5882
|
+
arrowOffset
|
|
5883
|
+
};
|
|
5683
5884
|
}
|
|
5684
5885
|
|
|
5685
5886
|
/**
|
|
@@ -5917,12 +6118,17 @@ function useStickyIntersectionObserver(ref, scrollContainerSelector, setSticky)
|
|
|
5917
6118
|
* The `callback` reference is static and can be safely used in external
|
|
5918
6119
|
* libraries or as a prop that does not cause rerendering of children.
|
|
5919
6120
|
*
|
|
6121
|
+
* The ref update is deferred to useLayoutEffect to prevent stale-closure
|
|
6122
|
+
* bugs when Chrome fires blur on elements removed during re-render.
|
|
6123
|
+
*
|
|
5920
6124
|
* @param {Function} callback function with changing reference
|
|
5921
6125
|
* @returns {Function} static function reference
|
|
5922
6126
|
*/
|
|
5923
6127
|
function useStaticCallback(callback) {
|
|
5924
6128
|
const callbackRef = useRef(callback);
|
|
5925
|
-
|
|
6129
|
+
useLayoutEffect(() => {
|
|
6130
|
+
callbackRef.current = callback;
|
|
6131
|
+
});
|
|
5926
6132
|
return useCallback((...args) => callbackRef.current(...args), []);
|
|
5927
6133
|
}
|
|
5928
6134
|
function useElementVisible(element) {
|
|
@@ -5942,6 +6148,10 @@ function useElementVisible(element) {
|
|
|
5942
6148
|
}, [element, visible]);
|
|
5943
6149
|
return visible;
|
|
5944
6150
|
}
|
|
6151
|
+
|
|
6152
|
+
/**
|
|
6153
|
+
* @param {import('../PropertiesPanel').GroupDefinition} props
|
|
6154
|
+
*/
|
|
5945
6155
|
function Group(props) {
|
|
5946
6156
|
const {
|
|
5947
6157
|
element,
|
|
@@ -5996,8 +6206,6 @@ function Group(props) {
|
|
|
5996
6206
|
class: classnames('bio-properties-panel-group-header', edited ? '' : 'empty', open ? 'open' : '', sticky && open ? 'sticky' : ''),
|
|
5997
6207
|
onClick: toggleOpen,
|
|
5998
6208
|
children: [jsx("div", {
|
|
5999
|
-
title: props.tooltip ? null : label,
|
|
6000
|
-
"data-title": label,
|
|
6001
6209
|
class: "bio-properties-panel-group-header-title",
|
|
6002
6210
|
children: jsx(TooltipWrapper, {
|
|
6003
6211
|
value: props.tooltip,
|
|
@@ -6201,9 +6409,11 @@ function PropertiesPanel$1(props) {
|
|
|
6201
6409
|
return get(layout, key, defaultValue);
|
|
6202
6410
|
};
|
|
6203
6411
|
const setLayoutForKey = (key, config) => {
|
|
6204
|
-
|
|
6205
|
-
|
|
6206
|
-
|
|
6412
|
+
setLayout(prevLayout => {
|
|
6413
|
+
const newLayout = assign({}, prevLayout);
|
|
6414
|
+
set$1(newLayout, key, config);
|
|
6415
|
+
return newLayout;
|
|
6416
|
+
});
|
|
6207
6417
|
};
|
|
6208
6418
|
const layoutContext = {
|
|
6209
6419
|
layout,
|
|
@@ -6402,7 +6612,6 @@ function CollapsibleEntry(props) {
|
|
|
6402
6612
|
class: "bio-properties-panel-collapsible-entry-header",
|
|
6403
6613
|
onClick: toggleOpen,
|
|
6404
6614
|
children: [jsx("div", {
|
|
6405
|
-
title: label || placeholderLabel,
|
|
6406
6615
|
class: classnames('bio-properties-panel-collapsible-entry-header-title', !label && 'empty'),
|
|
6407
6616
|
children: label || placeholderLabel
|
|
6408
6617
|
}), jsx("button", {
|
|
@@ -6438,6 +6647,10 @@ function CollapsibleEntry(props) {
|
|
|
6438
6647
|
})]
|
|
6439
6648
|
});
|
|
6440
6649
|
}
|
|
6650
|
+
|
|
6651
|
+
/**
|
|
6652
|
+
* @param {import('../PropertiesPanel').ListItemDefinition} props
|
|
6653
|
+
*/
|
|
6441
6654
|
function ListItem(props) {
|
|
6442
6655
|
const {
|
|
6443
6656
|
autoFocusEntry,
|
|
@@ -6539,8 +6752,6 @@ function ListGroup(props) {
|
|
|
6539
6752
|
class: classnames('bio-properties-panel-group-header', hasItems ? '' : 'empty', hasItems && open ? 'open' : '', sticky && open ? 'sticky' : ''),
|
|
6540
6753
|
onClick: hasItems ? toggleOpen : noop$6,
|
|
6541
6754
|
children: [jsx("div", {
|
|
6542
|
-
title: props.tooltip ? null : label,
|
|
6543
|
-
"data-title": label,
|
|
6544
6755
|
class: "bio-properties-panel-group-header-title",
|
|
6545
6756
|
children: jsx(TooltipWrapper, {
|
|
6546
6757
|
value: props.tooltip,
|
|
@@ -6609,6 +6820,13 @@ function getNewItemIds(newItems, oldItems) {
|
|
|
6609
6820
|
const oldIds = oldItems.map(item => item.id);
|
|
6610
6821
|
return newIds.filter(itemId => !oldIds.includes(itemId));
|
|
6611
6822
|
}
|
|
6823
|
+
|
|
6824
|
+
/**
|
|
6825
|
+
* @param {Object} props
|
|
6826
|
+
* @param {Object} props.element
|
|
6827
|
+
* @param {String} props.forId - id of the entry the description is used for
|
|
6828
|
+
* @param {String} props.value
|
|
6829
|
+
*/
|
|
6612
6830
|
function Description$1(props) {
|
|
6613
6831
|
const {
|
|
6614
6832
|
element,
|
|
@@ -6738,6 +6956,16 @@ function isEdited$8(node) {
|
|
|
6738
6956
|
function prefixId$8(id) {
|
|
6739
6957
|
return `bio-properties-panel-${id}`;
|
|
6740
6958
|
}
|
|
6959
|
+
|
|
6960
|
+
/**
|
|
6961
|
+
* Button to open popups.
|
|
6962
|
+
*
|
|
6963
|
+
* @param {Object} props
|
|
6964
|
+
* @param {Function} props.onClick - Callback to trigger when the button is clicked.
|
|
6965
|
+
* @param {string} [props.title] - Tooltip text for the button.
|
|
6966
|
+
* @param {boolean} [props.disabled] - Whether the button is disabled.
|
|
6967
|
+
* @param {string} [props.className] - Additional class names for the button.
|
|
6968
|
+
*/
|
|
6741
6969
|
function OpenPopupButton({
|
|
6742
6970
|
onClick,
|
|
6743
6971
|
title = 'Open pop-up editor'
|
|
@@ -6881,6 +7109,7 @@ const FeelEditor = forwardRef((props, ref) => {
|
|
|
6881
7109
|
enableGutters,
|
|
6882
7110
|
value,
|
|
6883
7111
|
onInput,
|
|
7112
|
+
onKeyDown: onKeyDownProp = noop$4,
|
|
6884
7113
|
onFeelToggle = noop$4,
|
|
6885
7114
|
onLint = noop$4,
|
|
6886
7115
|
onOpenPopup = noop$4,
|
|
@@ -6913,6 +7142,8 @@ const FeelEditor = forwardRef((props, ref) => {
|
|
|
6913
7142
|
* - AND the cursor is at the beginning of the input
|
|
6914
7143
|
*/
|
|
6915
7144
|
const onKeyDown = e => {
|
|
7145
|
+
// Call parent onKeyDown handler first
|
|
7146
|
+
onKeyDownProp(e);
|
|
6916
7147
|
if (e.key !== 'Backspace' || !editor) {
|
|
6917
7148
|
return;
|
|
6918
7149
|
}
|
|
@@ -7029,6 +7260,22 @@ function FeelIcon(props) {
|
|
|
7029
7260
|
children: jsx(FeelIcon$1, {})
|
|
7030
7261
|
});
|
|
7031
7262
|
}
|
|
7263
|
+
|
|
7264
|
+
/**
|
|
7265
|
+
* @param {KeyboardEvent} event
|
|
7266
|
+
* @return {boolean}
|
|
7267
|
+
*/
|
|
7268
|
+
function isCmd(event) {
|
|
7269
|
+
// ensure we don't react to AltGr
|
|
7270
|
+
// (mapped to CTRL + ALT)
|
|
7271
|
+
if (event.altKey) {
|
|
7272
|
+
return false;
|
|
7273
|
+
}
|
|
7274
|
+
return event.ctrlKey || event.metaKey;
|
|
7275
|
+
}
|
|
7276
|
+
function isCmdWithChar(event) {
|
|
7277
|
+
return isCmd(event) && event.key.length === 1 && /^[a-zA-Z]$/.test(event.key);
|
|
7278
|
+
}
|
|
7032
7279
|
function ToggleSwitch(props) {
|
|
7033
7280
|
const {
|
|
7034
7281
|
id,
|
|
@@ -7137,7 +7384,7 @@ function ToggleSwitchEntry(props) {
|
|
|
7137
7384
|
inline: inline,
|
|
7138
7385
|
tooltip: tooltip,
|
|
7139
7386
|
element: element
|
|
7140
|
-
}), jsx(Description$1, {
|
|
7387
|
+
}, element), jsx(Description$1, {
|
|
7141
7388
|
forId: id,
|
|
7142
7389
|
element: element,
|
|
7143
7390
|
value: description
|
|
@@ -7310,7 +7557,7 @@ function prefixId$6(id) {
|
|
|
7310
7557
|
const noop$2 = () => {};
|
|
7311
7558
|
|
|
7312
7559
|
/**
|
|
7313
|
-
* @typedef {'required'|'optional'|'static'} FeelType
|
|
7560
|
+
* @typedef {'required'|'optional'|'optional-default-enabled'|'static'} FeelType
|
|
7314
7561
|
*/
|
|
7315
7562
|
|
|
7316
7563
|
/**
|
|
@@ -7340,7 +7587,7 @@ function FeelTextfield(props) {
|
|
|
7340
7587
|
element,
|
|
7341
7588
|
label,
|
|
7342
7589
|
hostLanguage,
|
|
7343
|
-
onInput,
|
|
7590
|
+
onInput: commitValue,
|
|
7344
7591
|
onBlur,
|
|
7345
7592
|
onError,
|
|
7346
7593
|
placeholder,
|
|
@@ -7353,11 +7600,17 @@ function FeelTextfield(props) {
|
|
|
7353
7600
|
OptionalComponent = OptionalFeelInput,
|
|
7354
7601
|
tooltip
|
|
7355
7602
|
} = props;
|
|
7356
|
-
const [localValue, setLocalValue] = useState(value);
|
|
7603
|
+
const [localValue, setLocalValue] = useState(getInitialFeelLocalValue(feel, value));
|
|
7357
7604
|
const editorRef = useShowEntryEvent(id);
|
|
7358
7605
|
const containerRef = useRef();
|
|
7359
|
-
const
|
|
7360
|
-
|
|
7606
|
+
const onInput = useCallback(newValue => {
|
|
7607
|
+
// we don't commit empty FEEL expressions,
|
|
7608
|
+
// but instead serialize them as <undefined>
|
|
7609
|
+
const newModelValue = newValue === '' || newValue === '=' ? undefined : newValue;
|
|
7610
|
+
commitValue(newModelValue);
|
|
7611
|
+
}, [commitValue]);
|
|
7612
|
+
const feelActive = isFeelActive(feel, localValue);
|
|
7613
|
+
const feelOnlyValue = getFeelValue(localValue);
|
|
7361
7614
|
const feelLanguageContext = useContext(FeelLanguageContext);
|
|
7362
7615
|
const [focus, _setFocus] = useState(undefined);
|
|
7363
7616
|
const {
|
|
@@ -7375,13 +7628,7 @@ function FeelTextfield(props) {
|
|
|
7375
7628
|
/**
|
|
7376
7629
|
* @type { import('min-dash').DebouncedFunction }
|
|
7377
7630
|
*/
|
|
7378
|
-
const
|
|
7379
|
-
const handleInput = newValue => {
|
|
7380
|
-
// we don't commit empty FEEL expressions,
|
|
7381
|
-
// but instead serialize them as <undefined>
|
|
7382
|
-
const newModelValue = newValue === '' || newValue === '=' ? undefined : newValue;
|
|
7383
|
-
handleInputCallback(newModelValue);
|
|
7384
|
-
};
|
|
7631
|
+
const handleInput = useDebounce(onInput, debounce);
|
|
7385
7632
|
const handleFeelToggle = useStaticCallback(() => {
|
|
7386
7633
|
if (feel === 'required') {
|
|
7387
7634
|
return;
|
|
@@ -7394,7 +7641,7 @@ function FeelTextfield(props) {
|
|
|
7394
7641
|
handleInput(feelOnlyValue);
|
|
7395
7642
|
}
|
|
7396
7643
|
});
|
|
7397
|
-
const handleLocalInput = newValue => {
|
|
7644
|
+
const handleLocalInput = (newValue, useDebounce = true) => {
|
|
7398
7645
|
if (feelActive) {
|
|
7399
7646
|
newValue = '=' + newValue;
|
|
7400
7647
|
}
|
|
@@ -7402,23 +7649,33 @@ function FeelTextfield(props) {
|
|
|
7402
7649
|
return;
|
|
7403
7650
|
}
|
|
7404
7651
|
setLocalValue(newValue);
|
|
7405
|
-
|
|
7652
|
+
if (useDebounce) {
|
|
7653
|
+
handleInput(newValue);
|
|
7654
|
+
} else {
|
|
7655
|
+
onInput(newValue);
|
|
7656
|
+
}
|
|
7406
7657
|
if (!feelActive && isString(newValue) && newValue.startsWith('=')) {
|
|
7407
7658
|
// focus is behind `=` sign that will be removed
|
|
7408
7659
|
setFocus(-1);
|
|
7409
7660
|
}
|
|
7410
7661
|
};
|
|
7411
7662
|
const handleOnBlur = e => {
|
|
7663
|
+
handleInput.cancel?.();
|
|
7412
7664
|
if (e.target.type === 'checkbox') {
|
|
7413
7665
|
onInput(e.target.checked);
|
|
7414
7666
|
} else {
|
|
7415
7667
|
const trimmedValue = e.target.value.trim();
|
|
7416
|
-
|
|
7668
|
+
handleLocalInput(trimmedValue, false);
|
|
7417
7669
|
}
|
|
7418
7670
|
if (onBlur) {
|
|
7419
7671
|
onBlur(e);
|
|
7420
7672
|
}
|
|
7421
7673
|
};
|
|
7674
|
+
const handleOnKeyDown = e => {
|
|
7675
|
+
if (isCmdWithChar(e)) {
|
|
7676
|
+
handleInput.flush?.();
|
|
7677
|
+
}
|
|
7678
|
+
};
|
|
7422
7679
|
const handleLint = useStaticCallback((lint = []) => {
|
|
7423
7680
|
const syntaxError = lint.some(report => report.type === 'Syntax Error');
|
|
7424
7681
|
if (syntaxError) {
|
|
@@ -7485,12 +7742,26 @@ function FeelTextfield(props) {
|
|
|
7485
7742
|
if (feelActive || isPopupOpen) {
|
|
7486
7743
|
return;
|
|
7487
7744
|
}
|
|
7488
|
-
const
|
|
7489
|
-
if (
|
|
7745
|
+
const feelData = event.clipboardData.getData('application/FEEL');
|
|
7746
|
+
if (feelData) {
|
|
7490
7747
|
setTimeout(() => {
|
|
7491
7748
|
handleFeelToggle();
|
|
7492
7749
|
setFocus();
|
|
7493
7750
|
});
|
|
7751
|
+
return;
|
|
7752
|
+
}
|
|
7753
|
+
const input = event.target;
|
|
7754
|
+
const isFieldEmpty = !input.value;
|
|
7755
|
+
const isAllSelected = input.selectionStart === 0 && input.selectionEnd === input.value.length;
|
|
7756
|
+
if (isFieldEmpty || isAllSelected) {
|
|
7757
|
+
const textData = event.clipboardData.getData('text');
|
|
7758
|
+
const trimmedValue = textData.trim();
|
|
7759
|
+
setLocalValue(trimmedValue);
|
|
7760
|
+
handleInput(trimmedValue);
|
|
7761
|
+
if (!feelActive && isString(trimmedValue) && trimmedValue.startsWith('=')) {
|
|
7762
|
+
setFocus(trimmedValue.length - 1);
|
|
7763
|
+
}
|
|
7764
|
+
event.preventDefault();
|
|
7494
7765
|
}
|
|
7495
7766
|
};
|
|
7496
7767
|
containerRef.current.addEventListener('copy', copyHandler);
|
|
@@ -7526,11 +7797,12 @@ function FeelTextfield(props) {
|
|
|
7526
7797
|
ref: containerRef,
|
|
7527
7798
|
children: [jsx(FeelIndicator, {
|
|
7528
7799
|
active: feelActive,
|
|
7529
|
-
disabled: feel
|
|
7800
|
+
disabled: !isFeelOptional(feel) || disabled,
|
|
7530
7801
|
onClick: handleFeelToggle
|
|
7531
7802
|
}), feelActive ? jsx(FeelEditor, {
|
|
7532
7803
|
name: id,
|
|
7533
7804
|
onInput: handleLocalInput,
|
|
7805
|
+
onKeyDown: handleOnKeyDown,
|
|
7534
7806
|
contentAttributes: {
|
|
7535
7807
|
'id': prefixId$5(id),
|
|
7536
7808
|
'aria-label': label
|
|
@@ -7553,6 +7825,7 @@ function FeelTextfield(props) {
|
|
|
7553
7825
|
...props,
|
|
7554
7826
|
popupOpen: isPopupOpen,
|
|
7555
7827
|
onInput: handleLocalInput,
|
|
7828
|
+
onKeyDown: handleOnKeyDown,
|
|
7556
7829
|
onBlur: handleOnBlur,
|
|
7557
7830
|
contentAttributes: {
|
|
7558
7831
|
'id': prefixId$5(id),
|
|
@@ -7571,6 +7844,7 @@ const OptionalFeelInput = forwardRef((props, ref) => {
|
|
|
7571
7844
|
id,
|
|
7572
7845
|
disabled,
|
|
7573
7846
|
onInput,
|
|
7847
|
+
onKeyDown,
|
|
7574
7848
|
value,
|
|
7575
7849
|
onFocus,
|
|
7576
7850
|
onBlur,
|
|
@@ -7606,6 +7880,7 @@ const OptionalFeelInput = forwardRef((props, ref) => {
|
|
|
7606
7880
|
class: "bio-properties-panel-input",
|
|
7607
7881
|
onInput: e => onInput(e.target.value),
|
|
7608
7882
|
onFocus: onFocus,
|
|
7883
|
+
onKeyDown: onKeyDown,
|
|
7609
7884
|
onBlur: onBlur,
|
|
7610
7885
|
placeholder: placeholder,
|
|
7611
7886
|
value: value || ''
|
|
@@ -7979,6 +8254,87 @@ function isEdited$5(node) {
|
|
|
7979
8254
|
function prefixId$5(id) {
|
|
7980
8255
|
return `bio-properties-panel-${id}`;
|
|
7981
8256
|
}
|
|
8257
|
+
|
|
8258
|
+
/**
|
|
8259
|
+
* Determine if FEEL is optional for the configured {@link FeelType}.
|
|
8260
|
+
*
|
|
8261
|
+
* @param {FeelType} feelType
|
|
8262
|
+
*
|
|
8263
|
+
* @return {boolean}
|
|
8264
|
+
*/
|
|
8265
|
+
function isFeelOptional(feelType) {
|
|
8266
|
+
return feelType === 'optional' || feelType === 'optional-default-enabled';
|
|
8267
|
+
}
|
|
8268
|
+
|
|
8269
|
+
/**
|
|
8270
|
+
* Determine if FEEL editing is currently active.
|
|
8271
|
+
*
|
|
8272
|
+
* @param {FeelType} feelType
|
|
8273
|
+
* @param {string} localValue
|
|
8274
|
+
*
|
|
8275
|
+
* @return {boolean}
|
|
8276
|
+
*/
|
|
8277
|
+
function isFeelActive(feelType, localValue) {
|
|
8278
|
+
if (feelType === 'required') {
|
|
8279
|
+
return true;
|
|
8280
|
+
}
|
|
8281
|
+
if (isString(localValue)) {
|
|
8282
|
+
if (localValue.startsWith('=')) {
|
|
8283
|
+
return true;
|
|
8284
|
+
}
|
|
8285
|
+
}
|
|
8286
|
+
return false;
|
|
8287
|
+
}
|
|
8288
|
+
|
|
8289
|
+
/**
|
|
8290
|
+
* @template T
|
|
8291
|
+
* @param {T} value
|
|
8292
|
+
*
|
|
8293
|
+
* @return {string|T}
|
|
8294
|
+
*/
|
|
8295
|
+
function getFeelValue(value) {
|
|
8296
|
+
if (isString(value) && value.startsWith('=')) {
|
|
8297
|
+
return value.substring(1);
|
|
8298
|
+
}
|
|
8299
|
+
return value;
|
|
8300
|
+
}
|
|
8301
|
+
|
|
8302
|
+
/**
|
|
8303
|
+
* Initialize local FEEL value.
|
|
8304
|
+
*
|
|
8305
|
+
* `optional-default-enabled` starts in FEEL mode if no value or empty string is provided.
|
|
8306
|
+
*
|
|
8307
|
+
* @template T
|
|
8308
|
+
* @param {FeelType} feelType
|
|
8309
|
+
* @param {T} value
|
|
8310
|
+
*
|
|
8311
|
+
* @return {string|T}
|
|
8312
|
+
*/
|
|
8313
|
+
function getInitialFeelLocalValue(feelType, value) {
|
|
8314
|
+
if (feelType === 'optional-default-enabled' && (value === undefined || value === '')) {
|
|
8315
|
+
return '=';
|
|
8316
|
+
}
|
|
8317
|
+
return value;
|
|
8318
|
+
}
|
|
8319
|
+
|
|
8320
|
+
/**
|
|
8321
|
+
* @typedef { { value: string, label: string, disabled: boolean, children: { value: string, label: string, disabled: boolean } } } Option
|
|
8322
|
+
*/
|
|
8323
|
+
|
|
8324
|
+
/**
|
|
8325
|
+
* Provides basic select input.
|
|
8326
|
+
*
|
|
8327
|
+
* @param {object} props
|
|
8328
|
+
* @param {string} props.id
|
|
8329
|
+
* @param {string[]} props.path
|
|
8330
|
+
* @param {string} props.label
|
|
8331
|
+
* @param {Function} props.onChange
|
|
8332
|
+
* @param {Function} props.onFocus
|
|
8333
|
+
* @param {Function} props.onBlur
|
|
8334
|
+
* @param {Array<Option>} [props.options]
|
|
8335
|
+
* @param {string} props.value
|
|
8336
|
+
* @param {boolean} [props.disabled]
|
|
8337
|
+
*/
|
|
7982
8338
|
function Select(props) {
|
|
7983
8339
|
const {
|
|
7984
8340
|
id,
|
|
@@ -8144,12 +8500,13 @@ function TextArea(props) {
|
|
|
8144
8500
|
id,
|
|
8145
8501
|
label,
|
|
8146
8502
|
debounce,
|
|
8147
|
-
onInput,
|
|
8503
|
+
onInput: commitValue,
|
|
8148
8504
|
value = '',
|
|
8149
8505
|
disabled,
|
|
8150
8506
|
monospace,
|
|
8151
8507
|
onFocus,
|
|
8152
8508
|
onBlur,
|
|
8509
|
+
onPaste,
|
|
8153
8510
|
autoResize = true,
|
|
8154
8511
|
placeholder,
|
|
8155
8512
|
rows = autoResize ? 1 : 2,
|
|
@@ -8157,16 +8514,16 @@ function TextArea(props) {
|
|
|
8157
8514
|
} = props;
|
|
8158
8515
|
const [localValue, setLocalValue] = useState(value);
|
|
8159
8516
|
const ref = useShowEntryEvent(id);
|
|
8517
|
+
const onInput = useCallback(newValue => {
|
|
8518
|
+
const newModelValue = newValue === '' ? undefined : newValue;
|
|
8519
|
+
commitValue(newModelValue);
|
|
8520
|
+
}, [commitValue]);
|
|
8160
8521
|
const visible = useElementVisible(ref.current);
|
|
8161
8522
|
|
|
8162
8523
|
/**
|
|
8163
8524
|
* @type { import('min-dash').DebouncedFunction }
|
|
8164
8525
|
*/
|
|
8165
|
-
const
|
|
8166
|
-
const handleInput = newValue => {
|
|
8167
|
-
const newModelValue = newValue === '' ? undefined : newValue;
|
|
8168
|
-
handleInputCallback(newModelValue);
|
|
8169
|
-
};
|
|
8526
|
+
const handleInput = useDebounce(onInput, debounce);
|
|
8170
8527
|
const handleLocalInput = e => {
|
|
8171
8528
|
autoResize && resizeToContents(e.target);
|
|
8172
8529
|
if (e.target.value === localValue) {
|
|
@@ -8179,11 +8536,40 @@ function TextArea(props) {
|
|
|
8179
8536
|
const trimmedValue = e.target.value.trim();
|
|
8180
8537
|
|
|
8181
8538
|
// trim and commit on blur
|
|
8539
|
+
handleInput.cancel?.();
|
|
8182
8540
|
onInput(trimmedValue);
|
|
8541
|
+
setLocalValue(trimmedValue);
|
|
8183
8542
|
if (onBlur) {
|
|
8184
8543
|
onBlur(e);
|
|
8185
8544
|
}
|
|
8186
8545
|
};
|
|
8546
|
+
const handleOnPaste = e => {
|
|
8547
|
+
const input = e.target;
|
|
8548
|
+
const isFieldEmpty = !input.value;
|
|
8549
|
+
const isAllSelected = input.selectionStart === 0 && input.selectionEnd === input.value.length;
|
|
8550
|
+
|
|
8551
|
+
// Trim and handle paste if field is empty or all content is selected
|
|
8552
|
+
if (isFieldEmpty || isAllSelected) {
|
|
8553
|
+
const trimmedValue = e.clipboardData.getData('text').trim();
|
|
8554
|
+
setLocalValue(trimmedValue);
|
|
8555
|
+
handleInput(trimmedValue);
|
|
8556
|
+
if (onPaste) {
|
|
8557
|
+
onPaste(e);
|
|
8558
|
+
}
|
|
8559
|
+
e.preventDefault();
|
|
8560
|
+
return;
|
|
8561
|
+
}
|
|
8562
|
+
|
|
8563
|
+
// Allow default paste behavior for normal text editing
|
|
8564
|
+
if (onPaste) {
|
|
8565
|
+
onPaste(e);
|
|
8566
|
+
}
|
|
8567
|
+
};
|
|
8568
|
+
const handleOnKeyDown = e => {
|
|
8569
|
+
if (isCmdWithChar(e)) {
|
|
8570
|
+
handleInput.flush?.();
|
|
8571
|
+
}
|
|
8572
|
+
};
|
|
8187
8573
|
useLayoutEffect(() => {
|
|
8188
8574
|
autoResize && resizeToContents(ref.current);
|
|
8189
8575
|
}, []);
|
|
@@ -8215,7 +8601,9 @@ function TextArea(props) {
|
|
|
8215
8601
|
class: classnames('bio-properties-panel-input', monospace ? 'bio-properties-panel-input-monospace' : '', autoResize ? 'auto-resize' : ''),
|
|
8216
8602
|
onInput: handleLocalInput,
|
|
8217
8603
|
onFocus: onFocus,
|
|
8604
|
+
onKeyDown: handleOnKeyDown,
|
|
8218
8605
|
onBlur: handleOnBlur,
|
|
8606
|
+
onPaste: handleOnPaste,
|
|
8219
8607
|
placeholder: placeholder,
|
|
8220
8608
|
rows: rows,
|
|
8221
8609
|
value: localValue,
|
|
@@ -8236,6 +8624,7 @@ function TextArea(props) {
|
|
|
8236
8624
|
* @param {Function} props.setValue
|
|
8237
8625
|
* @param {Function} props.onFocus
|
|
8238
8626
|
* @param {Function} props.onBlur
|
|
8627
|
+
* @param {Function} props.onPaste
|
|
8239
8628
|
* @param {number} props.rows
|
|
8240
8629
|
* @param {boolean} props.monospace
|
|
8241
8630
|
* @param {Function} [props.validate]
|
|
@@ -8256,6 +8645,7 @@ function TextAreaEntry(props) {
|
|
|
8256
8645
|
validate,
|
|
8257
8646
|
onFocus,
|
|
8258
8647
|
onBlur,
|
|
8648
|
+
onPaste,
|
|
8259
8649
|
placeholder,
|
|
8260
8650
|
autoResize,
|
|
8261
8651
|
tooltip
|
|
@@ -8291,6 +8681,7 @@ function TextAreaEntry(props) {
|
|
|
8291
8681
|
onInput: onInput,
|
|
8292
8682
|
onFocus: onFocus,
|
|
8293
8683
|
onBlur: onBlur,
|
|
8684
|
+
onPaste: onPaste,
|
|
8294
8685
|
rows: rows,
|
|
8295
8686
|
debounce: debounce,
|
|
8296
8687
|
monospace: monospace,
|
|
@@ -8324,32 +8715,57 @@ function Textfield(props) {
|
|
|
8324
8715
|
disabled = false,
|
|
8325
8716
|
id,
|
|
8326
8717
|
label,
|
|
8327
|
-
onInput,
|
|
8718
|
+
onInput: commitValue,
|
|
8328
8719
|
onFocus,
|
|
8329
8720
|
onBlur,
|
|
8721
|
+
onPaste,
|
|
8330
8722
|
placeholder,
|
|
8331
8723
|
value = '',
|
|
8332
8724
|
tooltip
|
|
8333
8725
|
} = props;
|
|
8334
8726
|
const [localValue, setLocalValue] = useState(value || '');
|
|
8335
8727
|
const ref = useShowEntryEvent(id);
|
|
8728
|
+
const onInput = useCallback(newValue => {
|
|
8729
|
+
const newModelValue = newValue === '' ? undefined : newValue;
|
|
8730
|
+
commitValue(newModelValue);
|
|
8731
|
+
}, [commitValue]);
|
|
8336
8732
|
|
|
8337
8733
|
/**
|
|
8338
8734
|
* @type { import('min-dash').DebouncedFunction }
|
|
8339
8735
|
*/
|
|
8340
|
-
const
|
|
8736
|
+
const handleInput = useDebounce(onInput, debounce);
|
|
8341
8737
|
const handleOnBlur = e => {
|
|
8342
8738
|
const trimmedValue = e.target.value.trim();
|
|
8343
8739
|
|
|
8344
8740
|
// trim and commit on blur
|
|
8741
|
+
handleInput.cancel?.();
|
|
8345
8742
|
onInput(trimmedValue);
|
|
8743
|
+
setLocalValue(trimmedValue);
|
|
8346
8744
|
if (onBlur) {
|
|
8347
8745
|
onBlur(e);
|
|
8348
8746
|
}
|
|
8349
8747
|
};
|
|
8350
|
-
const
|
|
8351
|
-
const
|
|
8352
|
-
|
|
8748
|
+
const handleOnPaste = e => {
|
|
8749
|
+
const input = e.target;
|
|
8750
|
+
const isFieldEmpty = !input.value;
|
|
8751
|
+
const isAllSelected = input.selectionStart === 0 && input.selectionEnd === input.value.length;
|
|
8752
|
+
|
|
8753
|
+
// Trim and handle paste if field is empty or all content is selected (overwrite)
|
|
8754
|
+
if (isFieldEmpty || isAllSelected) {
|
|
8755
|
+
const trimmedValue = e.clipboardData.getData('text').trim();
|
|
8756
|
+
setLocalValue(trimmedValue);
|
|
8757
|
+
handleInput(trimmedValue);
|
|
8758
|
+
if (onPaste) {
|
|
8759
|
+
onPaste(e);
|
|
8760
|
+
}
|
|
8761
|
+
e.preventDefault();
|
|
8762
|
+
return;
|
|
8763
|
+
}
|
|
8764
|
+
|
|
8765
|
+
// Allow default paste behavior for normal text editing
|
|
8766
|
+
if (onPaste) {
|
|
8767
|
+
onPaste(e);
|
|
8768
|
+
}
|
|
8353
8769
|
};
|
|
8354
8770
|
const handleLocalInput = e => {
|
|
8355
8771
|
if (e.target.value === localValue) {
|
|
@@ -8364,6 +8780,11 @@ function Textfield(props) {
|
|
|
8364
8780
|
}
|
|
8365
8781
|
setLocalValue(value);
|
|
8366
8782
|
}, [value]);
|
|
8783
|
+
const handleOnKeyDown = e => {
|
|
8784
|
+
if (isCmdWithChar(e)) {
|
|
8785
|
+
handleInput.flush?.();
|
|
8786
|
+
}
|
|
8787
|
+
};
|
|
8367
8788
|
return jsxs("div", {
|
|
8368
8789
|
class: "bio-properties-panel-textfield",
|
|
8369
8790
|
children: [jsx("label", {
|
|
@@ -8386,7 +8807,9 @@ function Textfield(props) {
|
|
|
8386
8807
|
class: "bio-properties-panel-input",
|
|
8387
8808
|
onInput: handleLocalInput,
|
|
8388
8809
|
onFocus: onFocus,
|
|
8810
|
+
onKeyDown: handleOnKeyDown,
|
|
8389
8811
|
onBlur: handleOnBlur,
|
|
8812
|
+
onPaste: handleOnPaste,
|
|
8390
8813
|
placeholder: placeholder,
|
|
8391
8814
|
value: localValue
|
|
8392
8815
|
})]
|
|
@@ -8421,6 +8844,7 @@ function TextfieldEntry(props) {
|
|
|
8421
8844
|
validate,
|
|
8422
8845
|
onFocus,
|
|
8423
8846
|
onBlur,
|
|
8847
|
+
onPaste,
|
|
8424
8848
|
placeholder,
|
|
8425
8849
|
tooltip
|
|
8426
8850
|
} = props;
|
|
@@ -8456,6 +8880,7 @@ function TextfieldEntry(props) {
|
|
|
8456
8880
|
onInput: onInput,
|
|
8457
8881
|
onFocus: onFocus,
|
|
8458
8882
|
onBlur: onBlur,
|
|
8883
|
+
onPaste: onPaste,
|
|
8459
8884
|
placeholder: placeholder,
|
|
8460
8885
|
value: value,
|
|
8461
8886
|
tooltip: tooltip,
|
|
@@ -8725,6 +9150,7 @@ function Title(props) {
|
|
|
8725
9150
|
class: "bio-properties-panel-popup__title",
|
|
8726
9151
|
children: title
|
|
8727
9152
|
}), children, showCloseButton && jsx("button", {
|
|
9153
|
+
type: "button",
|
|
8728
9154
|
title: closeButtonTooltip,
|
|
8729
9155
|
class: "bio-properties-panel-popup__close",
|
|
8730
9156
|
onClick: onClose,
|
|
@@ -8766,6 +9192,25 @@ function cancel(event) {
|
|
|
8766
9192
|
event.preventDefault();
|
|
8767
9193
|
event.stopPropagation();
|
|
8768
9194
|
}
|
|
9195
|
+
|
|
9196
|
+
/**
|
|
9197
|
+
* @typedef {Object} FeelPopupProps
|
|
9198
|
+
* @property {string} entryId
|
|
9199
|
+
* @property {Function} onInput
|
|
9200
|
+
* @property {Function} onClose
|
|
9201
|
+
* @property {string} title
|
|
9202
|
+
* @property {'feel'|'feelers'} type
|
|
9203
|
+
* @property {string} value
|
|
9204
|
+
* @property {Array} [links]
|
|
9205
|
+
* @property {Array|Object} [variables]
|
|
9206
|
+
* @property {Object} [position]
|
|
9207
|
+
* @property {string} [hostLanguage]
|
|
9208
|
+
* @property {boolean} [singleLine]
|
|
9209
|
+
* @property {HTMLElement} [sourceElement]
|
|
9210
|
+
* @property {HTMLElement|string} [tooltipContainer]
|
|
9211
|
+
* @property {Object} [eventBus]
|
|
9212
|
+
*/
|
|
9213
|
+
|
|
8769
9214
|
const FEEL_POPUP_WIDTH = 700;
|
|
8770
9215
|
const FEEL_POPUP_HEIGHT = 250;
|
|
8771
9216
|
|
|
@@ -13478,7 +13923,7 @@ function CustomPropertiesGroup(field, editField) {
|
|
|
13478
13923
|
event.stopPropagation();
|
|
13479
13924
|
return editField(field, ['properties'], removeKey(properties, key));
|
|
13480
13925
|
};
|
|
13481
|
-
const id = `property-${
|
|
13926
|
+
const id = `property-${index}`;
|
|
13482
13927
|
return {
|
|
13483
13928
|
autoFocusEntry: id + '-key',
|
|
13484
13929
|
entries: CustomValueEntry({
|