allow paneClick events to happen more leniantly

This commit is contained in:
peterkogo
2025-10-27 15:35:03 +01:00
parent f8c547d92f
commit fa7bf16ce3
4 changed files with 79 additions and 59 deletions
+2 -1
View File
@@ -34,7 +34,7 @@ const BasicFlow = () => {
selectionMode={SelectionMode.Partial} selectionMode={SelectionMode.Partial}
panOnDrag={panOnDrag} panOnDrag={panOnDrag}
panOnScroll panOnScroll
paneClickDistance={2} paneClickDistance={100}
zoomActivationKeyCode="Meta" zoomActivationKeyCode="Meta"
multiSelectionKeyCode={MULTI_SELECT_KEY} multiSelectionKeyCode={MULTI_SELECT_KEY}
onPaneContextMenu={onPaneContextMenu} onPaneContextMenu={onPaneContextMenu}
@@ -44,6 +44,7 @@ const BasicFlow = () => {
onMoveStart={onMoveStart} onMoveStart={onMoveStart}
onMove={onMove} onMove={onMove}
onMoveEnd={onMoveEnd} onMoveEnd={onMoveEnd}
onPaneClick={(e) => console.log('pane click', e)}
> >
<Background variant={BackgroundVariant.Cross} /> <Background variant={BackgroundVariant.Cross} />
<Controls /> <Controls />
@@ -9,11 +9,6 @@
import '@xyflow/svelte/dist/style.css'; import '@xyflow/svelte/dist/style.css';
const onPaneContextMenu = (e: any) => {
e.preventDefault();
console.log('context menu');
};
const panOnDrag = [1, 2]; const panOnDrag = [1, 2];
const onmovestart = (e: any) => console.log('move start', e); const onmovestart = (e: any) => console.log('move start', e);
@@ -46,10 +41,12 @@
selectionMode={SelectionMode.Partial} selectionMode={SelectionMode.Partial}
selectionOnDrag selectionOnDrag
panOnScroll panOnScroll
paneClickDistance={100}
{panOnDrag} {panOnDrag}
{onmovestart} {onmovestart}
{onmove} {onmove}
{onmoveend} {onmoveend}
onpaneclick={(e) => console.log('on pane click', e)}
> >
<Controls /> <Controls />
<Background variant={BackgroundVariant.Dots} /> <Background variant={BackgroundVariant.Dots} />
+25 -15
View File
@@ -67,6 +67,7 @@ export function Pane({
selectionKeyPressed, selectionKeyPressed,
selectionMode = SelectionMode.Full, selectionMode = SelectionMode.Full,
panOnDrag, panOnDrag,
paneClickDistance = 0,
selectionOnDrag, selectionOnDrag,
onSelectionStart, onSelectionStart,
onSelectionEnd, onSelectionEnd,
@@ -117,14 +118,9 @@ export function Pane({
const onWheel = onPaneScroll ? (event: React.WheelEvent) => onPaneScroll(event) : undefined; const onWheel = onPaneScroll ? (event: React.WheelEvent) => onPaneScroll(event) : undefined;
const onClickCapture = (event: ReactMouseEvent) => { const onClickCapture = (event: ReactMouseEvent) => {
const isSelectionOnDragActive = if (selectionInProgress.current) {
(selectionOnDrag && container.current === event.target) || !selectionOnDrag || selectionKeyPressed; event.stopPropagation();
if (!isSelectionOnDragActive) {
return;
} }
event.stopPropagation();
}; };
// We are using capture here in order to prevent other pointer events // We are using capture here in order to prevent other pointer events
@@ -149,9 +145,6 @@ export function Pane({
return; return;
} }
event.stopPropagation();
event.preventDefault();
(event.target as Partial<Element>)?.setPointerCapture?.(event.pointerId); (event.target as Partial<Element>)?.setPointerCapture?.(event.pointerId);
selectionStarted.current = true; selectionStarted.current = true;
@@ -159,8 +152,6 @@ export function Pane({
const { x, y } = getEventPosition(event.nativeEvent, containerBounds.current); const { x, y } = getEventPosition(event.nativeEvent, containerBounds.current);
resetSelectedElements();
store.setState({ store.setState({
userSelectionRect: { userSelectionRect: {
width: 0, width: 0,
@@ -172,7 +163,14 @@ export function Pane({
}, },
}); });
onSelectionStart?.(event); if (event.target !== container.current || paneClickDistance === 0) {
event.stopPropagation();
event.preventDefault();
resetSelectedElements();
onSelectionStart?.(event);
}
}; };
const onPointerMove = (event: ReactPointerEvent): void => { const onPointerMove = (event: ReactPointerEvent): void => {
@@ -191,11 +189,23 @@ export function Pane({
return; return;
} }
selectionInProgress.current = true;
const { x: mouseX, y: mouseY } = getEventPosition(event.nativeEvent, containerBounds.current); const { x: mouseX, y: mouseY } = getEventPosition(event.nativeEvent, containerBounds.current);
const { startX, startY } = userSelectionRect; const { startX, startY } = userSelectionRect;
if (
!selectionInProgress.current &&
event.target === container.current &&
!selectionKeyPressed &&
paneClickDistance > 0
) {
const distance = Math.hypot(mouseX - startX, mouseY - startY);
if (distance <= paneClickDistance) {
return;
}
}
selectionInProgress.current = true;
const nextUserSelectRect = { const nextUserSelectRect = {
startX, startX,
startY, startY,
@@ -47,6 +47,7 @@
let { let {
store = $bindable(), store = $bindable(),
panOnDrag = true, panOnDrag = true,
paneClickDistance = 1,
selectionOnDrag, selectionOnDrag,
onpaneclick, onpaneclick,
onpanecontextmenu, onpanecontextmenu,
@@ -67,7 +68,7 @@
let panOnDragActive = $derived(store.panActivationKeyPressed || panOnDrag); let panOnDragActive = $derived(store.panActivationKeyPressed || panOnDrag);
let isSelecting = $derived( let isSelecting = $derived(
store.selectionKeyPressed || store.selectionKeyPressed ||
store.selectionRect || !!store.selectionRect ||
(selectionOnDrag && panOnDragActive !== true) (selectionOnDrag && panOnDragActive !== true)
); );
let hasActiveSelection = $derived( let hasActiveSelection = $derived(
@@ -76,19 +77,7 @@
// Used to prevent click events when the user lets go of the selectionKey during a selection // Used to prevent click events when the user lets go of the selectionKey during a selection
let selectionInProgress = false; let selectionInProgress = false;
let selectionStarted = false;
function onClick(event: MouseEvent) {
// We prevent click events when the user let go of the selectionKey during a selection
// We also prevent click events when a connection is in progress
if (selectionInProgress || store.connection.inProgress) {
selectionInProgress = false;
return;
}
onpaneclick?.({ event });
store.unselectNodesAndEdges();
store.selectionRectMode = null;
}
// We start the selection process when the user clicks down on the pane // We start the selection process when the user clicks down on the pane
function onPointerDownCapture(event: PointerEvent) { function onPointerDownCapture(event: PointerEvent) {
@@ -114,14 +103,12 @@
return; return;
} }
event.stopPropagation();
event.preventDefault();
(event.target as Partial<Element>)?.setPointerCapture?.(event.pointerId); (event.target as Partial<Element>)?.setPointerCapture?.(event.pointerId);
const { x, y } = getEventPosition(event, containerBounds); selectionStarted = true;
selectionInProgress = false;
store.unselectNodesAndEdges(); const { x, y } = getEventPosition(event, containerBounds);
store.selectionRect = { store.selectionRect = {
width: 0, width: 0,
@@ -132,7 +119,14 @@
y y
}; };
onselectionstart?.(event); if (event.target !== container || paneClickDistance === 0) {
event.stopPropagation();
event.preventDefault();
store.unselectNodesAndEdges();
onselectionstart?.(event);
}
} }
function onPointerMove(event: PointerEvent) { function onPointerMove(event: PointerEvent) {
@@ -140,11 +134,23 @@
return; return;
} }
selectionInProgress = true;
const mousePos = getEventPosition(event, containerBounds); const mousePos = getEventPosition(event, containerBounds);
const { startX = 0, startY = 0 } = store.selectionRect; const { startX = 0, startY = 0 } = store.selectionRect;
if (
!selectionInProgress &&
event.target === container &&
!store.selectionKeyPressed &&
paneClickDistance > 0
) {
const distance = Math.hypot(mousePos.x - startX, mousePos.y - startY);
if (distance <= paneClickDistance) {
return;
}
}
selectionInProgress = true;
const nextUserSelectRect = { const nextUserSelectRect = {
...store.selectionRect, ...store.selectionRect,
x: mousePos.x < startX ? mousePos.x : startX, x: mousePos.x < startX ? mousePos.x : startX,
@@ -195,7 +201,7 @@
} }
function onPointerUp(event: PointerEvent) { function onPointerUp(event: PointerEvent) {
if (event.button !== 0) { if (event.button !== 0 || !selectionStarted) {
return; return;
} }
@@ -203,22 +209,20 @@
// We only want to trigger click functions when in selection mode if // We only want to trigger click functions when in selection mode if
// the user did not move the mouse. // the user did not move the mouse.
if (!isSelecting && store.selectionRectMode === 'user' && event.target === container) {
if (!selectionInProgress && event.target === container) {
onClick?.(event); onClick?.(event);
} }
store.selectionRect = null; store.selectionRect = null;
if (selectedNodeIds.size > 0) { if (selectionInProgress && selectedNodeIds.size > 0) {
store.selectionRectMode = 'nodes'; store.selectionRectMode = 'nodes';
} }
// If the user kept holding the selectionKey during the selection,
// we need to reset the selectionInProgress, so the next click event is not prevented
if (store.selectionKeyPressed) {
selectionInProgress = false;
}
onselectionend?.(event); onselectionend?.(event);
selectionStarted = false;
} }
const onContextMenu = (event: MouseEvent) => { const onContextMenu = (event: MouseEvent) => {
@@ -231,17 +235,25 @@
}; };
const onClickCapture = (event: MouseEvent) => { const onClickCapture = (event: MouseEvent) => {
const isSelectionActive = if (selectionInProgress) {
(selectionOnDrag && container === event.target) || event.stopPropagation();
!selectionOnDrag || selectionInProgress = false;
store.selectionKeyPressed; }
};
if (!isSelectionActive) { function onClick(event: MouseEvent) {
// We prevent click events when the user let go of the selectionKey during a selection
// We also prevent click events when a connection is in progress
if (selectionInProgress || store.connection.inProgress) {
selectionInProgress = false;
return; return;
} }
event.stopPropagation(); onpaneclick?.({ event });
}; store.unselectNodesAndEdges();
store.selectionRectMode = null;
store.selectionRect = null;
}
</script> </script>
<!-- svelte-ignore a11y_no_static_element_interactions --> <!-- svelte-ignore a11y_no_static_element_interactions -->