@bpmn-io/properties-panel 3.33.2 → 3.34.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.esm.js +316 -34
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +315 -33
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useContext, useState, useRef,
|
|
1
|
+
import { useContext, useState, useRef, useEffect, useCallback, useMemo, useLayoutEffect } from '../preact/hooks';
|
|
2
2
|
import { isFunction, isArray, get, assign, set, isString, isNumber, debounce } from 'min-dash';
|
|
3
3
|
import { createPortal, forwardRef } from '../preact/compat';
|
|
4
4
|
import { jsx, jsxs, Fragment } from '../preact/jsx-runtime';
|
|
@@ -153,6 +153,20 @@ OpenPopupIcon.defaultProps = {
|
|
|
153
153
|
viewBox: "0 0 16 16"
|
|
154
154
|
};
|
|
155
155
|
|
|
156
|
+
/**
|
|
157
|
+
* @typedef { {
|
|
158
|
+
* getElementLabel: (element: object) => string,
|
|
159
|
+
* getTypeLabel: (element: object) => string,
|
|
160
|
+
* getElementIcon: (element: object) => import('preact').Component,
|
|
161
|
+
* getDocumentationRef: (element: object) => string
|
|
162
|
+
* } } HeaderProvider
|
|
163
|
+
*/
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* @param {Object} props
|
|
167
|
+
* @param {Object} props.element,
|
|
168
|
+
* @param {HeaderProvider} props.headerProvider
|
|
169
|
+
*/
|
|
156
170
|
function Header(props) {
|
|
157
171
|
const {
|
|
158
172
|
element,
|
|
@@ -180,11 +194,9 @@ function Header(props) {
|
|
|
180
194
|
}), jsxs("div", {
|
|
181
195
|
class: "bio-properties-panel-header-labels",
|
|
182
196
|
children: [jsx("div", {
|
|
183
|
-
title: type,
|
|
184
197
|
class: "bio-properties-panel-header-type",
|
|
185
198
|
children: type
|
|
186
199
|
}), label ? jsx("div", {
|
|
187
|
-
title: label,
|
|
188
200
|
class: "bio-properties-panel-header-label",
|
|
189
201
|
children: label
|
|
190
202
|
}) : null]
|
|
@@ -277,6 +289,26 @@ function useTooltipContext(id, element) {
|
|
|
277
289
|
return getTooltipForId(id, element);
|
|
278
290
|
}
|
|
279
291
|
|
|
292
|
+
/**
|
|
293
|
+
* @typedef {Object} TooltipProps
|
|
294
|
+
* @property {Object} [parent] - Parent element ref for portal rendering
|
|
295
|
+
* @property {String} [direction='right'] - Tooltip direction ( 'right', 'top')
|
|
296
|
+
* @property {String} [position] - Custom CSS position override
|
|
297
|
+
* @property {Number} [showDelay=250] - Delay in ms before showing tooltip on hover
|
|
298
|
+
* @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
|
|
299
|
+
* @property {*} [children] - Child elements to render inside the tooltip wrapper
|
|
300
|
+
*/
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Tooltip wrapper that provides context-based tooltip content lookup.
|
|
304
|
+
* All props are forwarded to the underlying Tooltip component.
|
|
305
|
+
*
|
|
306
|
+
* @param {TooltipProps & {
|
|
307
|
+
* forId: String,
|
|
308
|
+
* value?: String|Object,
|
|
309
|
+
* element?: Object
|
|
310
|
+
* }} props - Shared tooltip props plus wrapper-specific ones
|
|
311
|
+
*/
|
|
280
312
|
function TooltipWrapper(props) {
|
|
281
313
|
const {
|
|
282
314
|
forId,
|
|
@@ -293,35 +325,77 @@ function TooltipWrapper(props) {
|
|
|
293
325
|
forId: `bio-properties-panel-${forId}`
|
|
294
326
|
});
|
|
295
327
|
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* @param {TooltipProps & {
|
|
331
|
+
* forId: String,
|
|
332
|
+
* value: String|Object
|
|
333
|
+
* }} props
|
|
334
|
+
*/
|
|
296
335
|
function Tooltip(props) {
|
|
297
336
|
const {
|
|
298
337
|
forId,
|
|
299
338
|
value,
|
|
300
339
|
parent,
|
|
301
340
|
direction = 'right',
|
|
302
|
-
position
|
|
341
|
+
position,
|
|
342
|
+
showDelay = 250,
|
|
343
|
+
hideDelay = 250
|
|
303
344
|
} = props;
|
|
304
345
|
const [visible, setVisible] = useState(false);
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const SHOW_DELAY = 200;
|
|
308
|
-
let timeout = null;
|
|
346
|
+
const showTimeoutRef = useRef(null);
|
|
347
|
+
const hideTimeoutRef = useRef(null);
|
|
309
348
|
const wrapperRef = useRef(null);
|
|
310
349
|
const tooltipRef = useRef(null);
|
|
311
350
|
const show = (_, delay) => {
|
|
351
|
+
clearTimeout(showTimeoutRef.current);
|
|
352
|
+
clearTimeout(hideTimeoutRef.current);
|
|
312
353
|
if (visible) return;
|
|
313
354
|
if (delay) {
|
|
314
|
-
|
|
355
|
+
showTimeoutRef.current = setTimeout(() => {
|
|
315
356
|
setVisible(true);
|
|
316
|
-
},
|
|
357
|
+
}, showDelay);
|
|
317
358
|
} else {
|
|
318
359
|
setVisible(true);
|
|
319
360
|
}
|
|
320
361
|
};
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
362
|
+
const handleWrapperMouseEnter = e => {
|
|
363
|
+
show(e, true);
|
|
364
|
+
};
|
|
365
|
+
const hide = (delay = false) => {
|
|
366
|
+
clearTimeout(showTimeoutRef.current);
|
|
367
|
+
clearTimeout(hideTimeoutRef.current);
|
|
368
|
+
if (delay) {
|
|
369
|
+
hideTimeoutRef.current = setTimeout(() => {
|
|
370
|
+
setVisible(false);
|
|
371
|
+
}, hideDelay);
|
|
372
|
+
} else {
|
|
373
|
+
setVisible(false);
|
|
374
|
+
}
|
|
324
375
|
};
|
|
376
|
+
|
|
377
|
+
// Cleanup timeouts on unmount
|
|
378
|
+
useEffect(() => {
|
|
379
|
+
return () => {
|
|
380
|
+
clearTimeout(showTimeoutRef.current);
|
|
381
|
+
clearTimeout(hideTimeoutRef.current);
|
|
382
|
+
};
|
|
383
|
+
}, []);
|
|
384
|
+
|
|
385
|
+
// Handle click outside to close tooltip for non-focusable elements
|
|
386
|
+
useEffect(() => {
|
|
387
|
+
if (!visible) return;
|
|
388
|
+
const handleClickOutside = e => {
|
|
389
|
+
// If clicking outside both the wrapper and tooltip, hide it
|
|
390
|
+
if (wrapperRef.current && !wrapperRef.current.contains(e.target) && tooltipRef.current && !tooltipRef.current.contains(e.target)) {
|
|
391
|
+
hide(false);
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
395
|
+
return () => {
|
|
396
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
397
|
+
};
|
|
398
|
+
}, [visible, hide]);
|
|
325
399
|
const handleMouseLeave = ({
|
|
326
400
|
relatedTarget
|
|
327
401
|
}) => {
|
|
@@ -329,23 +403,32 @@ function Tooltip(props) {
|
|
|
329
403
|
if (relatedTarget === wrapperRef.current || relatedTarget === tooltipRef.current || relatedTarget?.parentElement === tooltipRef.current) {
|
|
330
404
|
return;
|
|
331
405
|
}
|
|
332
|
-
|
|
406
|
+
const selection = window.getSelection();
|
|
407
|
+
if (selection && selection.toString().length > 0) {
|
|
408
|
+
// Check if selection is within tooltip content
|
|
409
|
+
const selectionRange = selection.getRangeAt(0);
|
|
410
|
+
if (tooltipRef.current?.contains(selectionRange.commonAncestorContainer) || tooltipRef.current?.contains(selection.anchorNode) || tooltipRef.current?.contains(selection.focusNode)) {
|
|
411
|
+
return; // Keep tooltip open during text selection
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
hide(true);
|
|
415
|
+
};
|
|
416
|
+
const handleTooltipMouseEnter = () => {
|
|
417
|
+
clearTimeout(hideTimeoutRef.current);
|
|
333
418
|
};
|
|
334
419
|
const handleFocusOut = e => {
|
|
335
420
|
const {
|
|
336
|
-
|
|
421
|
+
relatedTarget
|
|
337
422
|
} = e;
|
|
338
423
|
|
|
339
|
-
// Don't hide
|
|
340
|
-
|
|
341
|
-
if (target === wrapperRef.current && isHovered) {
|
|
342
|
-
e.stopPropagation();
|
|
424
|
+
// Don't hide if focus moved to the tooltip or another element within the wrapper
|
|
425
|
+
if (tooltipRef.current?.contains(relatedTarget) || wrapperRef.current?.contains(relatedTarget)) {
|
|
343
426
|
return;
|
|
344
427
|
}
|
|
345
|
-
hide();
|
|
428
|
+
hide(false);
|
|
346
429
|
};
|
|
347
430
|
const hideTooltipViaEscape = e => {
|
|
348
|
-
e.code === 'Escape' && hide();
|
|
431
|
+
e.code === 'Escape' && hide(false);
|
|
349
432
|
};
|
|
350
433
|
const renderTooltip = () => {
|
|
351
434
|
return jsxs("div", {
|
|
@@ -356,6 +439,7 @@ function Tooltip(props) {
|
|
|
356
439
|
style: position || getTooltipPosition(wrapperRef.current),
|
|
357
440
|
ref: tooltipRef,
|
|
358
441
|
onClick: e => e.stopPropagation(),
|
|
442
|
+
onMouseEnter: handleTooltipMouseEnter,
|
|
359
443
|
onMouseLeave: handleMouseLeave,
|
|
360
444
|
children: [jsx("div", {
|
|
361
445
|
class: "bio-properties-panel-tooltip-content",
|
|
@@ -369,7 +453,7 @@ function Tooltip(props) {
|
|
|
369
453
|
class: "bio-properties-panel-tooltip-wrapper",
|
|
370
454
|
tabIndex: "0",
|
|
371
455
|
ref: wrapperRef,
|
|
372
|
-
onMouseEnter:
|
|
456
|
+
onMouseEnter: handleWrapperMouseEnter,
|
|
373
457
|
onMouseLeave: handleMouseLeave,
|
|
374
458
|
onFocus: show,
|
|
375
459
|
onBlur: handleFocusOut,
|
|
@@ -685,6 +769,9 @@ function useElementVisible(element) {
|
|
|
685
769
|
return visible;
|
|
686
770
|
}
|
|
687
771
|
|
|
772
|
+
/**
|
|
773
|
+
* @param {import('../PropertiesPanel').GroupDefinition} props
|
|
774
|
+
*/
|
|
688
775
|
function Group(props) {
|
|
689
776
|
const {
|
|
690
777
|
element,
|
|
@@ -739,8 +826,6 @@ function Group(props) {
|
|
|
739
826
|
class: classnames('bio-properties-panel-group-header', edited ? '' : 'empty', open ? 'open' : '', sticky && open ? 'sticky' : ''),
|
|
740
827
|
onClick: toggleOpen,
|
|
741
828
|
children: [jsx("div", {
|
|
742
|
-
title: props.tooltip ? null : label,
|
|
743
|
-
"data-title": label,
|
|
744
829
|
class: "bio-properties-panel-group-header-title",
|
|
745
830
|
children: jsx(TooltipWrapper, {
|
|
746
831
|
value: props.tooltip,
|
|
@@ -1093,6 +1178,13 @@ function useUpdateLayoutEffect(effect, deps) {
|
|
|
1093
1178
|
}, deps);
|
|
1094
1179
|
}
|
|
1095
1180
|
|
|
1181
|
+
/**
|
|
1182
|
+
*
|
|
1183
|
+
* @param {object} props
|
|
1184
|
+
* @param {string} [props.class]
|
|
1185
|
+
* @param {import('preact').Component[]} [props.menuItems]
|
|
1186
|
+
* @returns
|
|
1187
|
+
*/
|
|
1096
1188
|
function DropdownButton(props) {
|
|
1097
1189
|
const {
|
|
1098
1190
|
class: className,
|
|
@@ -1250,7 +1342,6 @@ function CollapsibleEntry(props) {
|
|
|
1250
1342
|
class: "bio-properties-panel-collapsible-entry-header",
|
|
1251
1343
|
onClick: toggleOpen,
|
|
1252
1344
|
children: [jsx("div", {
|
|
1253
|
-
title: label || placeholderLabel,
|
|
1254
1345
|
class: classnames('bio-properties-panel-collapsible-entry-header-title', !label && 'empty'),
|
|
1255
1346
|
children: label || placeholderLabel
|
|
1256
1347
|
}), jsx("button", {
|
|
@@ -1287,6 +1378,9 @@ function CollapsibleEntry(props) {
|
|
|
1287
1378
|
});
|
|
1288
1379
|
}
|
|
1289
1380
|
|
|
1381
|
+
/**
|
|
1382
|
+
* @param {import('../PropertiesPanel').ListItemDefinition} props
|
|
1383
|
+
*/
|
|
1290
1384
|
function ListItem(props) {
|
|
1291
1385
|
const {
|
|
1292
1386
|
autoFocusEntry,
|
|
@@ -1389,8 +1483,6 @@ function ListGroup(props) {
|
|
|
1389
1483
|
class: classnames('bio-properties-panel-group-header', hasItems ? '' : 'empty', hasItems && open ? 'open' : '', sticky && open ? 'sticky' : ''),
|
|
1390
1484
|
onClick: hasItems ? toggleOpen : noop$6,
|
|
1391
1485
|
children: [jsx("div", {
|
|
1392
|
-
title: props.tooltip ? null : label,
|
|
1393
|
-
"data-title": label,
|
|
1394
1486
|
class: "bio-properties-panel-group-header-title",
|
|
1395
1487
|
children: jsx(TooltipWrapper, {
|
|
1396
1488
|
value: props.tooltip,
|
|
@@ -1460,6 +1552,12 @@ function getNewItemIds(newItems, oldItems) {
|
|
|
1460
1552
|
return newIds.filter(itemId => !oldIds.includes(itemId));
|
|
1461
1553
|
}
|
|
1462
1554
|
|
|
1555
|
+
/**
|
|
1556
|
+
* @param {Object} props
|
|
1557
|
+
* @param {Object} props.element
|
|
1558
|
+
* @param {String} props.forId - id of the entry the description is used for
|
|
1559
|
+
* @param {String} props.value
|
|
1560
|
+
*/
|
|
1463
1561
|
function Description(props) {
|
|
1464
1562
|
const {
|
|
1465
1563
|
element,
|
|
@@ -1591,6 +1689,15 @@ function prefixId$8(id) {
|
|
|
1591
1689
|
return `bio-properties-panel-${id}`;
|
|
1592
1690
|
}
|
|
1593
1691
|
|
|
1692
|
+
/**
|
|
1693
|
+
* Button to open popups.
|
|
1694
|
+
*
|
|
1695
|
+
* @param {Object} props
|
|
1696
|
+
* @param {Function} props.onClick - Callback to trigger when the button is clicked.
|
|
1697
|
+
* @param {string} [props.title] - Tooltip text for the button.
|
|
1698
|
+
* @param {boolean} [props.disabled] - Whether the button is disabled.
|
|
1699
|
+
* @param {string} [props.className] - Additional class names for the button.
|
|
1700
|
+
*/
|
|
1594
1701
|
function OpenPopupButton({
|
|
1595
1702
|
onClick,
|
|
1596
1703
|
title = 'Open pop-up editor'
|
|
@@ -1736,6 +1843,7 @@ const FeelEditor = forwardRef((props, ref) => {
|
|
|
1736
1843
|
enableGutters,
|
|
1737
1844
|
value,
|
|
1738
1845
|
onInput,
|
|
1846
|
+
onKeyDown: onKeyDownProp = noop$4,
|
|
1739
1847
|
onFeelToggle = noop$4,
|
|
1740
1848
|
onLint = noop$4,
|
|
1741
1849
|
onOpenPopup = noop$4,
|
|
@@ -1768,6 +1876,8 @@ const FeelEditor = forwardRef((props, ref) => {
|
|
|
1768
1876
|
* - AND the cursor is at the beginning of the input
|
|
1769
1877
|
*/
|
|
1770
1878
|
const onKeyDown = e => {
|
|
1879
|
+
// Call parent onKeyDown handler first
|
|
1880
|
+
onKeyDownProp(e);
|
|
1771
1881
|
if (e.key !== 'Backspace' || !editor) {
|
|
1772
1882
|
return;
|
|
1773
1883
|
}
|
|
@@ -1887,6 +1997,22 @@ function FeelIcon(props) {
|
|
|
1887
1997
|
});
|
|
1888
1998
|
}
|
|
1889
1999
|
|
|
2000
|
+
/**
|
|
2001
|
+
* @param {KeyboardEvent} event
|
|
2002
|
+
* @return {boolean}
|
|
2003
|
+
*/
|
|
2004
|
+
function isCmd(event) {
|
|
2005
|
+
// ensure we don't react to AltGr
|
|
2006
|
+
// (mapped to CTRL + ALT)
|
|
2007
|
+
if (event.altKey) {
|
|
2008
|
+
return false;
|
|
2009
|
+
}
|
|
2010
|
+
return event.ctrlKey || event.metaKey;
|
|
2011
|
+
}
|
|
2012
|
+
function isCmdWithChar(event) {
|
|
2013
|
+
return isCmd(event) && event.key.length === 1 && /^[a-zA-Z]$/.test(event.key);
|
|
2014
|
+
}
|
|
2015
|
+
|
|
1890
2016
|
function ToggleSwitch(props) {
|
|
1891
2017
|
const {
|
|
1892
2018
|
id,
|
|
@@ -2254,7 +2380,7 @@ function FeelTextfield(props) {
|
|
|
2254
2380
|
handleInput(feelOnlyValue);
|
|
2255
2381
|
}
|
|
2256
2382
|
});
|
|
2257
|
-
const handleLocalInput = newValue => {
|
|
2383
|
+
const handleLocalInput = (newValue, useDebounce = true) => {
|
|
2258
2384
|
if (feelActive) {
|
|
2259
2385
|
newValue = '=' + newValue;
|
|
2260
2386
|
}
|
|
@@ -2262,23 +2388,33 @@ function FeelTextfield(props) {
|
|
|
2262
2388
|
return;
|
|
2263
2389
|
}
|
|
2264
2390
|
setLocalValue(newValue);
|
|
2265
|
-
|
|
2391
|
+
if (useDebounce) {
|
|
2392
|
+
handleInput(newValue);
|
|
2393
|
+
} else {
|
|
2394
|
+
onInput(newValue);
|
|
2395
|
+
}
|
|
2266
2396
|
if (!feelActive && isString(newValue) && newValue.startsWith('=')) {
|
|
2267
2397
|
// focus is behind `=` sign that will be removed
|
|
2268
2398
|
setFocus(-1);
|
|
2269
2399
|
}
|
|
2270
2400
|
};
|
|
2271
2401
|
const handleOnBlur = e => {
|
|
2402
|
+
handleInput.cancel?.();
|
|
2272
2403
|
if (e.target.type === 'checkbox') {
|
|
2273
2404
|
onInput(e.target.checked);
|
|
2274
2405
|
} else {
|
|
2275
2406
|
const trimmedValue = e.target.value.trim();
|
|
2276
|
-
|
|
2407
|
+
handleLocalInput(trimmedValue, false);
|
|
2277
2408
|
}
|
|
2278
2409
|
if (onBlur) {
|
|
2279
2410
|
onBlur(e);
|
|
2280
2411
|
}
|
|
2281
2412
|
};
|
|
2413
|
+
const handleOnKeyDown = e => {
|
|
2414
|
+
if (isCmdWithChar(e)) {
|
|
2415
|
+
handleInput.flush();
|
|
2416
|
+
}
|
|
2417
|
+
};
|
|
2282
2418
|
const handleLint = useStaticCallback((lint = []) => {
|
|
2283
2419
|
const syntaxError = lint.some(report => report.type === 'Syntax Error');
|
|
2284
2420
|
if (syntaxError) {
|
|
@@ -2345,12 +2481,23 @@ function FeelTextfield(props) {
|
|
|
2345
2481
|
if (feelActive || isPopupOpen) {
|
|
2346
2482
|
return;
|
|
2347
2483
|
}
|
|
2348
|
-
const
|
|
2349
|
-
if (
|
|
2484
|
+
const feelData = event.clipboardData.getData('application/FEEL');
|
|
2485
|
+
if (feelData) {
|
|
2350
2486
|
setTimeout(() => {
|
|
2351
2487
|
handleFeelToggle();
|
|
2352
2488
|
setFocus();
|
|
2353
2489
|
});
|
|
2490
|
+
return;
|
|
2491
|
+
}
|
|
2492
|
+
const input = event.target;
|
|
2493
|
+
const isFieldEmpty = !input.value;
|
|
2494
|
+
const isAllSelected = input.selectionStart === 0 && input.selectionEnd === input.value.length;
|
|
2495
|
+
if (isFieldEmpty || isAllSelected) {
|
|
2496
|
+
const textData = event.clipboardData.getData('text');
|
|
2497
|
+
const trimmedValue = textData.trim();
|
|
2498
|
+
setLocalValue(trimmedValue);
|
|
2499
|
+
handleInput(trimmedValue);
|
|
2500
|
+
event.preventDefault();
|
|
2354
2501
|
}
|
|
2355
2502
|
};
|
|
2356
2503
|
containerRef.current.addEventListener('copy', copyHandler);
|
|
@@ -2391,6 +2538,7 @@ function FeelTextfield(props) {
|
|
|
2391
2538
|
}), feelActive ? jsx(FeelEditor, {
|
|
2392
2539
|
name: id,
|
|
2393
2540
|
onInput: handleLocalInput,
|
|
2541
|
+
onKeyDown: handleOnKeyDown,
|
|
2394
2542
|
contentAttributes: {
|
|
2395
2543
|
'id': prefixId$5(id),
|
|
2396
2544
|
'aria-label': label
|
|
@@ -2413,6 +2561,7 @@ function FeelTextfield(props) {
|
|
|
2413
2561
|
...props,
|
|
2414
2562
|
popupOpen: isPopupOpen,
|
|
2415
2563
|
onInput: handleLocalInput,
|
|
2564
|
+
onKeyDown: handleOnKeyDown,
|
|
2416
2565
|
onBlur: handleOnBlur,
|
|
2417
2566
|
contentAttributes: {
|
|
2418
2567
|
'id': prefixId$5(id),
|
|
@@ -2431,6 +2580,7 @@ const OptionalFeelInput = forwardRef((props, ref) => {
|
|
|
2431
2580
|
id,
|
|
2432
2581
|
disabled,
|
|
2433
2582
|
onInput,
|
|
2583
|
+
onKeyDown,
|
|
2434
2584
|
value,
|
|
2435
2585
|
onFocus,
|
|
2436
2586
|
onBlur,
|
|
@@ -2466,6 +2616,7 @@ const OptionalFeelInput = forwardRef((props, ref) => {
|
|
|
2466
2616
|
class: "bio-properties-panel-input",
|
|
2467
2617
|
onInput: e => onInput(e.target.value),
|
|
2468
2618
|
onFocus: onFocus,
|
|
2619
|
+
onKeyDown: onKeyDown,
|
|
2469
2620
|
onBlur: onBlur,
|
|
2470
2621
|
placeholder: placeholder,
|
|
2471
2622
|
value: value || ''
|
|
@@ -3060,6 +3211,22 @@ function prefixIdLabel(id) {
|
|
|
3060
3211
|
return `bio-properties-panel-feelers-${id}-label`;
|
|
3061
3212
|
}
|
|
3062
3213
|
|
|
3214
|
+
/**
|
|
3215
|
+
* Entry for handling lists represented as nested entries.
|
|
3216
|
+
*
|
|
3217
|
+
* @template Item
|
|
3218
|
+
* @param {object} props
|
|
3219
|
+
* @param {string} props.id
|
|
3220
|
+
* @param {*} props.element
|
|
3221
|
+
* @param {Function} props.onAdd
|
|
3222
|
+
* @param {import('preact').Component} props.component
|
|
3223
|
+
* @param {string} [props.label='<empty>']
|
|
3224
|
+
* @param {Function} [props.onRemove]
|
|
3225
|
+
* @param {Item[]} [props.items]
|
|
3226
|
+
* @param {boolean} [props.open]
|
|
3227
|
+
* @param {string|boolean} [props.autoFocusEntry] either a custom selector string or true to focus the first input
|
|
3228
|
+
* @returns
|
|
3229
|
+
*/
|
|
3063
3230
|
function List(props) {
|
|
3064
3231
|
const {
|
|
3065
3232
|
id,
|
|
@@ -3107,7 +3274,6 @@ function List(props) {
|
|
|
3107
3274
|
class: classnames('bio-properties-panel-list-entry-header', sticky && open ? 'sticky' : ''),
|
|
3108
3275
|
onClick: toggleOpen,
|
|
3109
3276
|
children: [jsx("div", {
|
|
3110
|
-
title: label,
|
|
3111
3277
|
class: classnames('bio-properties-panel-list-entry-header-title', open && 'open'),
|
|
3112
3278
|
children: label
|
|
3113
3279
|
}), jsxs("div", {
|
|
@@ -3212,6 +3378,24 @@ function useNewItems(items = [], shouldReset) {
|
|
|
3212
3378
|
return previousItems ? items.filter(item => !previousItems.includes(item)) : [];
|
|
3213
3379
|
}
|
|
3214
3380
|
|
|
3381
|
+
/**
|
|
3382
|
+
* @typedef { { value: string, label: string, disabled: boolean, children: { value: string, label: string, disabled: boolean } } } Option
|
|
3383
|
+
*/
|
|
3384
|
+
|
|
3385
|
+
/**
|
|
3386
|
+
* Provides basic select input.
|
|
3387
|
+
*
|
|
3388
|
+
* @param {object} props
|
|
3389
|
+
* @param {string} props.id
|
|
3390
|
+
* @param {string[]} props.path
|
|
3391
|
+
* @param {string} props.label
|
|
3392
|
+
* @param {Function} props.onChange
|
|
3393
|
+
* @param {Function} props.onFocus
|
|
3394
|
+
* @param {Function} props.onBlur
|
|
3395
|
+
* @param {Array<Option>} [props.options]
|
|
3396
|
+
* @param {string} props.value
|
|
3397
|
+
* @param {boolean} [props.disabled]
|
|
3398
|
+
*/
|
|
3215
3399
|
function Select(props) {
|
|
3216
3400
|
const {
|
|
3217
3401
|
id,
|
|
@@ -3366,6 +3550,17 @@ function prefixId$4(id) {
|
|
|
3366
3550
|
return `bio-properties-panel-${id}`;
|
|
3367
3551
|
}
|
|
3368
3552
|
|
|
3553
|
+
/**
|
|
3554
|
+
* @param {Object} props
|
|
3555
|
+
* @param {Function} props.debounce
|
|
3556
|
+
* @param {Boolean} [props.disabled]
|
|
3557
|
+
* @param {Object} props.element
|
|
3558
|
+
* @param {Function} props.getValue
|
|
3559
|
+
* @param {String} props.id
|
|
3560
|
+
* @param {Function} [props.onBlur]
|
|
3561
|
+
* @param {Function} [props.onFocus]
|
|
3562
|
+
* @param {Function} props.setValue
|
|
3563
|
+
*/
|
|
3369
3564
|
function Simple(props) {
|
|
3370
3565
|
const {
|
|
3371
3566
|
debounce,
|
|
@@ -3438,6 +3633,7 @@ function TextArea(props) {
|
|
|
3438
3633
|
monospace,
|
|
3439
3634
|
onFocus,
|
|
3440
3635
|
onBlur,
|
|
3636
|
+
onPaste,
|
|
3441
3637
|
autoResize = true,
|
|
3442
3638
|
placeholder,
|
|
3443
3639
|
rows = autoResize ? 1 : 2,
|
|
@@ -3467,11 +3663,40 @@ function TextArea(props) {
|
|
|
3467
3663
|
const trimmedValue = e.target.value.trim();
|
|
3468
3664
|
|
|
3469
3665
|
// trim and commit on blur
|
|
3666
|
+
handleInput.cancel?.();
|
|
3470
3667
|
onInput(trimmedValue);
|
|
3668
|
+
setLocalValue(trimmedValue);
|
|
3471
3669
|
if (onBlur) {
|
|
3472
3670
|
onBlur(e);
|
|
3473
3671
|
}
|
|
3474
3672
|
};
|
|
3673
|
+
const handleOnPaste = e => {
|
|
3674
|
+
const input = e.target;
|
|
3675
|
+
const isFieldEmpty = !input.value;
|
|
3676
|
+
const isAllSelected = input.selectionStart === 0 && input.selectionEnd === input.value.length;
|
|
3677
|
+
|
|
3678
|
+
// Trim and handle paste if field is empty or all content is selected
|
|
3679
|
+
if (isFieldEmpty || isAllSelected) {
|
|
3680
|
+
const trimmedValue = e.clipboardData.getData('text').trim();
|
|
3681
|
+
setLocalValue(trimmedValue);
|
|
3682
|
+
handleInput(trimmedValue);
|
|
3683
|
+
if (onPaste) {
|
|
3684
|
+
onPaste(e);
|
|
3685
|
+
}
|
|
3686
|
+
e.preventDefault();
|
|
3687
|
+
return;
|
|
3688
|
+
}
|
|
3689
|
+
|
|
3690
|
+
// Allow default paste behavior for normal text editing
|
|
3691
|
+
if (onPaste) {
|
|
3692
|
+
onPaste(e);
|
|
3693
|
+
}
|
|
3694
|
+
};
|
|
3695
|
+
const handleOnKeyDown = e => {
|
|
3696
|
+
if (isCmdWithChar(e)) {
|
|
3697
|
+
handleInput.flush();
|
|
3698
|
+
}
|
|
3699
|
+
};
|
|
3475
3700
|
useLayoutEffect(() => {
|
|
3476
3701
|
autoResize && resizeToContents(ref.current);
|
|
3477
3702
|
}, []);
|
|
@@ -3503,7 +3728,9 @@ function TextArea(props) {
|
|
|
3503
3728
|
class: classnames('bio-properties-panel-input', monospace ? 'bio-properties-panel-input-monospace' : '', autoResize ? 'auto-resize' : ''),
|
|
3504
3729
|
onInput: handleLocalInput,
|
|
3505
3730
|
onFocus: onFocus,
|
|
3731
|
+
onKeyDown: handleOnKeyDown,
|
|
3506
3732
|
onBlur: handleOnBlur,
|
|
3733
|
+
onPaste: handleOnPaste,
|
|
3507
3734
|
placeholder: placeholder,
|
|
3508
3735
|
rows: rows,
|
|
3509
3736
|
value: localValue,
|
|
@@ -3524,6 +3751,7 @@ function TextArea(props) {
|
|
|
3524
3751
|
* @param {Function} props.setValue
|
|
3525
3752
|
* @param {Function} props.onFocus
|
|
3526
3753
|
* @param {Function} props.onBlur
|
|
3754
|
+
* @param {Function} props.onPaste
|
|
3527
3755
|
* @param {number} props.rows
|
|
3528
3756
|
* @param {boolean} props.monospace
|
|
3529
3757
|
* @param {Function} [props.validate]
|
|
@@ -3544,6 +3772,7 @@ function TextAreaEntry(props) {
|
|
|
3544
3772
|
validate,
|
|
3545
3773
|
onFocus,
|
|
3546
3774
|
onBlur,
|
|
3775
|
+
onPaste,
|
|
3547
3776
|
placeholder,
|
|
3548
3777
|
autoResize,
|
|
3549
3778
|
tooltip
|
|
@@ -3579,6 +3808,7 @@ function TextAreaEntry(props) {
|
|
|
3579
3808
|
onInput: onInput,
|
|
3580
3809
|
onFocus: onFocus,
|
|
3581
3810
|
onBlur: onBlur,
|
|
3811
|
+
onPaste: onPaste,
|
|
3582
3812
|
rows: rows,
|
|
3583
3813
|
debounce: debounce,
|
|
3584
3814
|
monospace: monospace,
|
|
@@ -3616,6 +3846,7 @@ function Textfield(props) {
|
|
|
3616
3846
|
onInput: commitValue,
|
|
3617
3847
|
onFocus,
|
|
3618
3848
|
onBlur,
|
|
3849
|
+
onPaste,
|
|
3619
3850
|
placeholder,
|
|
3620
3851
|
value = '',
|
|
3621
3852
|
tooltip
|
|
@@ -3635,11 +3866,35 @@ function Textfield(props) {
|
|
|
3635
3866
|
const trimmedValue = e.target.value.trim();
|
|
3636
3867
|
|
|
3637
3868
|
// trim and commit on blur
|
|
3869
|
+
handleInput.cancel?.();
|
|
3638
3870
|
onInput(trimmedValue);
|
|
3871
|
+
setLocalValue(trimmedValue);
|
|
3639
3872
|
if (onBlur) {
|
|
3640
3873
|
onBlur(e);
|
|
3641
3874
|
}
|
|
3642
3875
|
};
|
|
3876
|
+
const handleOnPaste = e => {
|
|
3877
|
+
const input = e.target;
|
|
3878
|
+
const isFieldEmpty = !input.value;
|
|
3879
|
+
const isAllSelected = input.selectionStart === 0 && input.selectionEnd === input.value.length;
|
|
3880
|
+
|
|
3881
|
+
// Trim and handle paste if field is empty or all content is selected (overwrite)
|
|
3882
|
+
if (isFieldEmpty || isAllSelected) {
|
|
3883
|
+
const trimmedValue = e.clipboardData.getData('text').trim();
|
|
3884
|
+
setLocalValue(trimmedValue);
|
|
3885
|
+
handleInput(trimmedValue);
|
|
3886
|
+
if (onPaste) {
|
|
3887
|
+
onPaste(e);
|
|
3888
|
+
}
|
|
3889
|
+
e.preventDefault();
|
|
3890
|
+
return;
|
|
3891
|
+
}
|
|
3892
|
+
|
|
3893
|
+
// Allow default paste behavior for normal text editing
|
|
3894
|
+
if (onPaste) {
|
|
3895
|
+
onPaste(e);
|
|
3896
|
+
}
|
|
3897
|
+
};
|
|
3643
3898
|
const handleLocalInput = e => {
|
|
3644
3899
|
if (e.target.value === localValue) {
|
|
3645
3900
|
return;
|
|
@@ -3653,6 +3908,11 @@ function Textfield(props) {
|
|
|
3653
3908
|
}
|
|
3654
3909
|
setLocalValue(value);
|
|
3655
3910
|
}, [value]);
|
|
3911
|
+
const handleOnKeyDown = e => {
|
|
3912
|
+
if (isCmdWithChar(e)) {
|
|
3913
|
+
handleInput.flush();
|
|
3914
|
+
}
|
|
3915
|
+
};
|
|
3656
3916
|
return jsxs("div", {
|
|
3657
3917
|
class: "bio-properties-panel-textfield",
|
|
3658
3918
|
children: [jsx("label", {
|
|
@@ -3675,7 +3935,9 @@ function Textfield(props) {
|
|
|
3675
3935
|
class: "bio-properties-panel-input",
|
|
3676
3936
|
onInput: handleLocalInput,
|
|
3677
3937
|
onFocus: onFocus,
|
|
3938
|
+
onKeyDown: handleOnKeyDown,
|
|
3678
3939
|
onBlur: handleOnBlur,
|
|
3940
|
+
onPaste: handleOnPaste,
|
|
3679
3941
|
placeholder: placeholder,
|
|
3680
3942
|
value: localValue
|
|
3681
3943
|
})]
|
|
@@ -3710,6 +3972,7 @@ function TextfieldEntry(props) {
|
|
|
3710
3972
|
validate,
|
|
3711
3973
|
onFocus,
|
|
3712
3974
|
onBlur,
|
|
3975
|
+
onPaste,
|
|
3713
3976
|
placeholder,
|
|
3714
3977
|
tooltip
|
|
3715
3978
|
} = props;
|
|
@@ -3745,6 +4008,7 @@ function TextfieldEntry(props) {
|
|
|
3745
4008
|
onInput: onInput,
|
|
3746
4009
|
onFocus: onFocus,
|
|
3747
4010
|
onBlur: onBlur,
|
|
4011
|
+
onPaste: onPaste,
|
|
3748
4012
|
placeholder: placeholder,
|
|
3749
4013
|
value: value,
|
|
3750
4014
|
tooltip: tooltip,
|
|
@@ -3777,7 +4041,7 @@ const DEFAULT_DEBOUNCE_TIME = 600;
|
|
|
3777
4041
|
* - If `debounceDelay` is `false`, the function executes immediately without debouncing.
|
|
3778
4042
|
* - If a number is provided, the function execution is delayed by the given time in milliseconds.
|
|
3779
4043
|
*
|
|
3780
|
-
* @param { Boolean | Number } [debounceDelay=
|
|
4044
|
+
* @param { Boolean | Number } [debounceDelay=600]
|
|
3781
4045
|
*
|
|
3782
4046
|
* @example
|
|
3783
4047
|
* const debounce = debounceInput();
|
|
@@ -4097,6 +4361,24 @@ function cancel(event) {
|
|
|
4097
4361
|
event.stopPropagation();
|
|
4098
4362
|
}
|
|
4099
4363
|
|
|
4364
|
+
/**
|
|
4365
|
+
* @typedef {Object} FeelPopupProps
|
|
4366
|
+
* @property {string} entryId
|
|
4367
|
+
* @property {Function} onInput
|
|
4368
|
+
* @property {Function} onClose
|
|
4369
|
+
* @property {string} title
|
|
4370
|
+
* @property {'feel'|'feelers'} type
|
|
4371
|
+
* @property {string} value
|
|
4372
|
+
* @property {Array} [links]
|
|
4373
|
+
* @property {Array|Object} [variables]
|
|
4374
|
+
* @property {Object} [position]
|
|
4375
|
+
* @property {string} [hostLanguage]
|
|
4376
|
+
* @property {boolean} [singleLine]
|
|
4377
|
+
* @property {HTMLElement} [sourceElement]
|
|
4378
|
+
* @property {HTMLElement|string} [tooltipContainer]
|
|
4379
|
+
* @property {Object} [eventBus]
|
|
4380
|
+
*/
|
|
4381
|
+
|
|
4100
4382
|
const FEEL_POPUP_WIDTH = 700;
|
|
4101
4383
|
const FEEL_POPUP_HEIGHT = 250;
|
|
4102
4384
|
|