From f8c547d92f3d6b6b0118d04a9b0436c9c4a495f4 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Mon, 27 Oct 2025 12:06:53 +0100 Subject: [PATCH 01/18] remove setPaneClickDistance from store and update it declaritively --- examples/react/src/examples/Figma/index.tsx | 1 + .../react/src/components/StoreUpdater/index.tsx | 4 ---- packages/react/src/container/FlowRenderer/index.tsx | 2 ++ packages/react/src/container/Pane/index.tsx | 1 + packages/react/src/container/ReactFlow/index.tsx | 1 - packages/react/src/container/ZoomPane/index.tsx | 13 ++++++------- packages/react/src/store/index.ts | 3 --- packages/react/src/types/store.ts | 1 - packages/svelte/src/lib/actions/zoom/index.ts | 3 +-- packages/svelte/src/lib/container/Pane/types.ts | 1 + .../src/lib/container/SvelteFlow/SvelteFlow.svelte | 2 ++ packages/svelte/src/lib/container/Zoom/Zoom.svelte | 2 ++ packages/svelte/src/lib/container/Zoom/types.ts | 1 + packages/svelte/src/lib/store/index.ts | 5 ----- packages/svelte/src/lib/store/types.ts | 1 - packages/system/src/types/panzoom.ts | 3 ++- packages/system/src/xypanzoom/XYPanZoom.ts | 12 +++++++----- 17 files changed, 26 insertions(+), 30 deletions(-) diff --git a/examples/react/src/examples/Figma/index.tsx b/examples/react/src/examples/Figma/index.tsx index 6780b6cb..f6daab75 100644 --- a/examples/react/src/examples/Figma/index.tsx +++ b/examples/react/src/examples/Figma/index.tsx @@ -34,6 +34,7 @@ const BasicFlow = () => { selectionMode={SelectionMode.Partial} panOnDrag={panOnDrag} panOnScroll + paneClickDistance={2} zoomActivationKeyCode="Meta" multiSelectionKeyCode={MULTI_SELECT_KEY} onPaneContextMenu={onPaneContextMenu} diff --git a/packages/react/src/components/StoreUpdater/index.tsx b/packages/react/src/components/StoreUpdater/index.tsx index 73b6000a..2eb926d8 100644 --- a/packages/react/src/components/StoreUpdater/index.tsx +++ b/packages/react/src/components/StoreUpdater/index.tsx @@ -69,7 +69,6 @@ const reactFlowFieldsToTrack = [ 'onBeforeDelete', 'debug', 'autoPanSpeed', - 'paneClickDistance', 'ariaLabelConfig', ] as const; @@ -93,7 +92,6 @@ const selector = (s: ReactFlowState) => ({ setNodeExtent: s.setNodeExtent, reset: s.reset, setDefaultNodesAndEdges: s.setDefaultNodesAndEdges, - setPaneClickDistance: s.setPaneClickDistance, }); const initPrevValues = { @@ -124,7 +122,6 @@ export function StoreUpdater(); @@ -155,7 +152,6 @@ export function StoreUpdater({ onViewportChange={onViewportChange} isControlledViewport={isControlledViewport} paneClickDistance={paneClickDistance} + selectionOnDrag={_selectionOnDrag} > ({ isSelecting={!!isSelecting} selectionMode={selectionMode} selectionKeyPressed={selectionKeyPressed} + paneClickDistance={paneClickDistance} selectionOnDrag={_selectionOnDrag} > {children} diff --git a/packages/react/src/container/Pane/index.tsx b/packages/react/src/container/Pane/index.tsx index ef86be3f..aadac2e0 100644 --- a/packages/react/src/container/Pane/index.tsx +++ b/packages/react/src/container/Pane/index.tsx @@ -39,6 +39,7 @@ type PaneProps = { | 'onPaneMouseMove' | 'onPaneMouseLeave' | 'selectionOnDrag' + | 'paneClickDistance' > >; diff --git a/packages/react/src/container/ReactFlow/index.tsx b/packages/react/src/container/ReactFlow/index.tsx index 33b5ee54..bd20242f 100644 --- a/packages/react/src/container/ReactFlow/index.tsx +++ b/packages/react/src/container/ReactFlow/index.tsx @@ -309,7 +309,6 @@ function ReactFlow( nodeDragThreshold={nodeDragThreshold} connectionDragThreshold={connectionDragThreshold} onBeforeDelete={onBeforeDelete} - paneClickDistance={paneClickDistance} debug={debug} ariaLabelConfig={ariaLabelConfig} /> diff --git a/packages/react/src/container/ZoomPane/index.tsx b/packages/react/src/container/ZoomPane/index.tsx index bce2b2b7..67babfa2 100644 --- a/packages/react/src/container/ZoomPane/index.tsx +++ b/packages/react/src/container/ZoomPane/index.tsx @@ -12,12 +12,7 @@ import type { ReactFlowState } from '../../types'; type ZoomPaneProps = Omit< FlowRendererProps, - | 'deleteKeyCode' - | 'selectionKeyCode' - | 'multiSelectionKeyCode' - | 'noDragClassName' - | 'disableKeyboardA11y' - | 'selectionOnDrag' + 'deleteKeyCode' | 'selectionKeyCode' | 'multiSelectionKeyCode' | 'noDragClassName' | 'disableKeyboardA11y' > & { isControlledViewport: boolean; }; @@ -49,6 +44,7 @@ export function ZoomPane({ onViewportChange, isControlledViewport, paneClickDistance, + selectionOnDrag, }: ZoomPaneProps) { const store = useStoreApi(); const zoomPane = useRef(null); @@ -77,7 +73,6 @@ export function ZoomPane({ maxZoom, translateExtent, viewport: defaultViewport, - paneClickDistance, onDraggingChange: (paneDragging: boolean) => store.setState({ paneDragging }), onPanZoomStart: (event, vp) => { const { onViewportChangeStart, onMoveStart } = store.getState(); @@ -128,6 +123,8 @@ export function ZoomPane({ lib, onTransformChange, connectionInProgress, + selectionOnDrag, + paneClickDistance, }); }, [ onPaneContextMenu, @@ -146,6 +143,8 @@ export function ZoomPane({ lib, onTransformChange, connectionInProgress, + selectionOnDrag, + paneClickDistance, ]); return ( diff --git a/packages/react/src/store/index.ts b/packages/react/src/store/index.ts index 085f8b1e..3254bdfa 100644 --- a/packages/react/src/store/index.ts +++ b/packages/react/src/store/index.ts @@ -312,9 +312,6 @@ const createStore = ({ set({ translateExtent }); }, - setPaneClickDistance: (clickDistance) => { - get().panZoom?.setClickDistance(clickDistance); - }, resetSelectedElements: () => { const { edges, nodes, triggerNodeChanges, triggerEdgeChanges, elementsSelectable } = get(); diff --git a/packages/react/src/types/store.ts b/packages/react/src/types/store.ts index 1fe0d308..59a05127 100644 --- a/packages/react/src/types/store.ts +++ b/packages/react/src/types/store.ts @@ -175,7 +175,6 @@ export type ReactFlowActions = { triggerEdgeChanges: (changes: EdgeChange[]) => void; panBy: PanBy; setCenter: SetCenter; - setPaneClickDistance: (distance: number) => void; }; export type ReactFlowState = ReactFlowStore< diff --git a/packages/svelte/src/lib/actions/zoom/index.ts b/packages/svelte/src/lib/actions/zoom/index.ts index 27d7328f..0fd7d72d 100644 --- a/packages/svelte/src/lib/actions/zoom/index.ts +++ b/packages/svelte/src/lib/actions/zoom/index.ts @@ -35,6 +35,7 @@ type ZoomParams = { userSelectionActive: boolean; lib: string; paneClickDistance: number; + selectionOnDrag?: boolean; onTransformChange: (transform: Transform) => void; onDraggingChange: (dragging: boolean) => void; connectionInProgress: boolean; @@ -49,7 +50,6 @@ export default function zoom(domNode: Element, params: ZoomParams) { onPanZoom, onPanZoomEnd, translateExtent, - paneClickDistance, setPanZoomInstance, onDraggingChange, onTransformChange @@ -61,7 +61,6 @@ export default function zoom(domNode: Element, params: ZoomParams) { maxZoom, translateExtent, viewport: initialViewport, - paneClickDistance, onPanZoom, onPanZoomStart, onPanZoomEnd, diff --git a/packages/svelte/src/lib/container/Pane/types.ts b/packages/svelte/src/lib/container/Pane/types.ts index 8442834e..f0cef4df 100644 --- a/packages/svelte/src/lib/container/Pane/types.ts +++ b/packages/svelte/src/lib/container/Pane/types.ts @@ -5,6 +5,7 @@ import type { SvelteFlowStore } from '$lib/store/types'; export type PaneProps = { store: SvelteFlowStore; panOnDrag?: boolean | number[]; + paneClickDistance: number; selectionOnDrag?: boolean; onselectionstart?: (event: PointerEvent) => void; onselectionend?: (event: PointerEvent) => void; diff --git a/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte b/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte index d16a3890..d92ff2d6 100644 --- a/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte +++ b/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte @@ -156,6 +156,7 @@ {panOnScrollSpeed} {panOnDrag} {paneClickDistance} + {selectionOnDrag} {onmovestart} {onmove} {onmoveend} @@ -168,6 +169,7 @@ {onselectionstart} {onselectionend} {panOnDrag} + {paneClickDistance} {selectionOnDrag} > diff --git a/packages/svelte/src/lib/container/Zoom/Zoom.svelte b/packages/svelte/src/lib/container/Zoom/Zoom.svelte index 70971e46..761cffef 100644 --- a/packages/svelte/src/lib/container/Zoom/Zoom.svelte +++ b/packages/svelte/src/lib/container/Zoom/Zoom.svelte @@ -16,6 +16,7 @@ panOnScroll, panOnScrollSpeed, paneClickDistance, + selectionOnDrag, onmovestart, onmove, onmoveend, @@ -69,6 +70,7 @@ translateExtent: store.translateExtent, lib: 'svelte', paneClickDistance, + selectionOnDrag, onTransformChange: (transform: Transform) => { store.viewport = { x: transform[0], y: transform[1], zoom: transform[2] }; }, diff --git a/packages/svelte/src/lib/container/Zoom/types.ts b/packages/svelte/src/lib/container/Zoom/types.ts index 8573fbcb..b9e5bf93 100644 --- a/packages/svelte/src/lib/container/Zoom/types.ts +++ b/packages/svelte/src/lib/container/Zoom/types.ts @@ -14,6 +14,7 @@ export type ZoomProps( elements: T[], elementsToDeselect: Set | null = null @@ -393,7 +389,6 @@ export function createStore void; setMaxZoom: (maxZoom: number) => void; setTranslateExtent: (extent: CoordinateExtent) => void; - setPaneClickDistance: (distance: number) => void; fitView: (options?: FitViewOptions) => Promise; setCenter: SetCenter; updateNodePositions: UpdateNodePositions; diff --git a/packages/system/src/types/panzoom.ts b/packages/system/src/types/panzoom.ts index 7fa54e97..11291868 100644 --- a/packages/system/src/types/panzoom.ts +++ b/packages/system/src/types/panzoom.ts @@ -9,7 +9,6 @@ export type PanZoomParams = { domNode: Element; minZoom: number; maxZoom: number; - paneClickDistance: number; viewport: Viewport; translateExtent: CoordinateExtent; onDraggingChange: OnDraggingChange; @@ -43,6 +42,8 @@ export type PanZoomUpdateOptions = { lib: string; onTransformChange: OnTransformChange; connectionInProgress: boolean; + paneClickDistance: number; + selectionOnDrag?: boolean; }; export type PanZoomInstance = { diff --git a/packages/system/src/xypanzoom/XYPanZoom.ts b/packages/system/src/xypanzoom/XYPanZoom.ts index 85b8ba1c..ee1a763e 100644 --- a/packages/system/src/xypanzoom/XYPanZoom.ts +++ b/packages/system/src/xypanzoom/XYPanZoom.ts @@ -37,7 +37,6 @@ export function XYPanZoom({ domNode, minZoom, maxZoom, - paneClickDistance, translateExtent, viewport, onPanZoom, @@ -55,10 +54,7 @@ export function XYPanZoom({ isPanScrolling: false, }; const bbox = domNode.getBoundingClientRect(); - const d3ZoomInstance = zoom() - .clickDistance(!isNumeric(paneClickDistance) || paneClickDistance < 0 ? 0 : paneClickDistance) - .scaleExtent([minZoom, maxZoom]) - .translateExtent(translateExtent); + const d3ZoomInstance = zoom().scaleExtent([minZoom, maxZoom]).translateExtent(translateExtent); const d3Selection = select(domNode).call(d3ZoomInstance); setViewportConstrained( @@ -109,6 +105,8 @@ export function XYPanZoom({ lib, onTransformChange, connectionInProgress, + paneClickDistance, + selectionOnDrag, }: PanZoomUpdateOptions) { if (userSelectionActive && !zoomPanValues.isZoomingOrPanning) { destroy(); @@ -116,6 +114,10 @@ export function XYPanZoom({ const isPanOnScroll = panOnScroll && !zoomActivationKeyPressed && !userSelectionActive; + d3ZoomInstance.clickDistance( + selectionOnDrag ? Infinity : !isNumeric(paneClickDistance) || paneClickDistance < 0 ? 0 : paneClickDistance + ); + const wheelHandler = isPanOnScroll ? createPanOnScrollHandler({ zoomPanValues, From fa7bf16ce38f51a275784f9d67d0853396b66d29 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Mon, 27 Oct 2025 15:35:03 +0100 Subject: [PATCH 02/18] allow paneClick events to happen more leniantly --- examples/react/src/examples/Figma/index.tsx | 3 +- .../src/routes/examples/figma/+page.svelte | 7 +- packages/react/src/container/Pane/index.tsx | 40 +++++---- .../svelte/src/lib/container/Pane/Pane.svelte | 88 +++++++++++-------- 4 files changed, 79 insertions(+), 59 deletions(-) diff --git a/examples/react/src/examples/Figma/index.tsx b/examples/react/src/examples/Figma/index.tsx index f6daab75..136d0629 100644 --- a/examples/react/src/examples/Figma/index.tsx +++ b/examples/react/src/examples/Figma/index.tsx @@ -34,7 +34,7 @@ const BasicFlow = () => { selectionMode={SelectionMode.Partial} panOnDrag={panOnDrag} panOnScroll - paneClickDistance={2} + paneClickDistance={100} zoomActivationKeyCode="Meta" multiSelectionKeyCode={MULTI_SELECT_KEY} onPaneContextMenu={onPaneContextMenu} @@ -44,6 +44,7 @@ const BasicFlow = () => { onMoveStart={onMoveStart} onMove={onMove} onMoveEnd={onMoveEnd} + onPaneClick={(e) => console.log('pane click', e)} > diff --git a/examples/svelte/src/routes/examples/figma/+page.svelte b/examples/svelte/src/routes/examples/figma/+page.svelte index 35a694ac..0b4c0b6a 100644 --- a/examples/svelte/src/routes/examples/figma/+page.svelte +++ b/examples/svelte/src/routes/examples/figma/+page.svelte @@ -9,11 +9,6 @@ import '@xyflow/svelte/dist/style.css'; - const onPaneContextMenu = (e: any) => { - e.preventDefault(); - console.log('context menu'); - }; - const panOnDrag = [1, 2]; const onmovestart = (e: any) => console.log('move start', e); @@ -46,10 +41,12 @@ selectionMode={SelectionMode.Partial} selectionOnDrag panOnScroll + paneClickDistance={100} {panOnDrag} {onmovestart} {onmove} {onmoveend} + onpaneclick={(e) => console.log('on pane click', e)} > diff --git a/packages/react/src/container/Pane/index.tsx b/packages/react/src/container/Pane/index.tsx index aadac2e0..f952434b 100644 --- a/packages/react/src/container/Pane/index.tsx +++ b/packages/react/src/container/Pane/index.tsx @@ -67,6 +67,7 @@ export function Pane({ selectionKeyPressed, selectionMode = SelectionMode.Full, panOnDrag, + paneClickDistance = 0, selectionOnDrag, onSelectionStart, onSelectionEnd, @@ -117,14 +118,9 @@ export function Pane({ const onWheel = onPaneScroll ? (event: React.WheelEvent) => onPaneScroll(event) : undefined; const onClickCapture = (event: ReactMouseEvent) => { - const isSelectionOnDragActive = - (selectionOnDrag && container.current === event.target) || !selectionOnDrag || selectionKeyPressed; - - if (!isSelectionOnDragActive) { - return; + if (selectionInProgress.current) { + event.stopPropagation(); } - - event.stopPropagation(); }; // We are using capture here in order to prevent other pointer events @@ -149,9 +145,6 @@ export function Pane({ return; } - event.stopPropagation(); - event.preventDefault(); - (event.target as Partial)?.setPointerCapture?.(event.pointerId); selectionStarted.current = true; @@ -159,8 +152,6 @@ export function Pane({ const { x, y } = getEventPosition(event.nativeEvent, containerBounds.current); - resetSelectedElements(); - store.setState({ userSelectionRect: { 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 => { @@ -191,11 +189,23 @@ export function Pane({ return; } - selectionInProgress.current = true; - const { x: mouseX, y: mouseY } = getEventPosition(event.nativeEvent, containerBounds.current); 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 = { startX, startY, diff --git a/packages/svelte/src/lib/container/Pane/Pane.svelte b/packages/svelte/src/lib/container/Pane/Pane.svelte index ef77b1de..fed9d005 100644 --- a/packages/svelte/src/lib/container/Pane/Pane.svelte +++ b/packages/svelte/src/lib/container/Pane/Pane.svelte @@ -47,6 +47,7 @@ let { store = $bindable(), panOnDrag = true, + paneClickDistance = 1, selectionOnDrag, onpaneclick, onpanecontextmenu, @@ -67,7 +68,7 @@ let panOnDragActive = $derived(store.panActivationKeyPressed || panOnDrag); let isSelecting = $derived( store.selectionKeyPressed || - store.selectionRect || + !!store.selectionRect || (selectionOnDrag && panOnDragActive !== true) ); let hasActiveSelection = $derived( @@ -76,19 +77,7 @@ // Used to prevent click events when the user lets go of the selectionKey during a selection let selectionInProgress = 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; - } + let selectionStarted = false; // We start the selection process when the user clicks down on the pane function onPointerDownCapture(event: PointerEvent) { @@ -114,14 +103,12 @@ return; } - event.stopPropagation(); - event.preventDefault(); - (event.target as Partial)?.setPointerCapture?.(event.pointerId); - const { x, y } = getEventPosition(event, containerBounds); + selectionStarted = true; + selectionInProgress = false; - store.unselectNodesAndEdges(); + const { x, y } = getEventPosition(event, containerBounds); store.selectionRect = { width: 0, @@ -132,7 +119,14 @@ y }; - onselectionstart?.(event); + if (event.target !== container || paneClickDistance === 0) { + event.stopPropagation(); + event.preventDefault(); + + store.unselectNodesAndEdges(); + + onselectionstart?.(event); + } } function onPointerMove(event: PointerEvent) { @@ -140,11 +134,23 @@ return; } - selectionInProgress = true; - const mousePos = getEventPosition(event, containerBounds); 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 = { ...store.selectionRect, x: mousePos.x < startX ? mousePos.x : startX, @@ -195,7 +201,7 @@ } function onPointerUp(event: PointerEvent) { - if (event.button !== 0) { + if (event.button !== 0 || !selectionStarted) { return; } @@ -203,22 +209,20 @@ // We only want to trigger click functions when in selection mode if // the user did not move the mouse. - if (!isSelecting && store.selectionRectMode === 'user' && event.target === container) { + + if (!selectionInProgress && event.target === container) { onClick?.(event); } + store.selectionRect = null; - if (selectedNodeIds.size > 0) { + if (selectionInProgress && selectedNodeIds.size > 0) { 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); + + selectionStarted = false; } const onContextMenu = (event: MouseEvent) => { @@ -231,17 +235,25 @@ }; const onClickCapture = (event: MouseEvent) => { - const isSelectionActive = - (selectionOnDrag && container === event.target) || - !selectionOnDrag || - store.selectionKeyPressed; + if (selectionInProgress) { + event.stopPropagation(); + selectionInProgress = false; + } + }; - 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; } - event.stopPropagation(); - }; + onpaneclick?.({ event }); + store.unselectNodesAndEdges(); + store.selectionRectMode = null; + store.selectionRect = null; + } From 5ec0cac7fad21109b74839969c0818f88ddc87d9 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Mon, 27 Oct 2025 15:39:07 +0100 Subject: [PATCH 03/18] chore(changeset) --- .changeset/sour-jokes-argue.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/sour-jokes-argue.md diff --git a/.changeset/sour-jokes-argue.md b/.changeset/sour-jokes-argue.md new file mode 100644 index 00000000..1eb9edea --- /dev/null +++ b/.changeset/sour-jokes-argue.md @@ -0,0 +1,7 @@ +--- +'@xyflow/react': patch +'@xyflow/svelte': patch +'@xyflow/system': patch +--- + +Fix onPaneClick events being surpressed when selectionOnDrag=true From 1f43ccc3ab42ac34be23dc8ce68d8d0df1876a08 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Mon, 27 Oct 2025 15:53:06 +0100 Subject: [PATCH 04/18] fix onSelectionEnd behavior --- examples/react/src/examples/Figma/index.tsx | 2 ++ .../svelte/src/routes/examples/figma/+page.svelte | 2 ++ packages/react/src/container/Pane/index.tsx | 11 ++++------- packages/svelte/src/lib/container/Pane/Pane.svelte | 5 ++++- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/examples/react/src/examples/Figma/index.tsx b/examples/react/src/examples/Figma/index.tsx index 136d0629..28e21179 100644 --- a/examples/react/src/examples/Figma/index.tsx +++ b/examples/react/src/examples/Figma/index.tsx @@ -45,6 +45,8 @@ const BasicFlow = () => { onMove={onMove} onMoveEnd={onMoveEnd} onPaneClick={(e) => console.log('pane click', e)} + onSelectionStart={(e) => console.log('on selection start', e)} + onSelectionEnd={(e) => console.log('on selection end', e)} > diff --git a/examples/svelte/src/routes/examples/figma/+page.svelte b/examples/svelte/src/routes/examples/figma/+page.svelte index 0b4c0b6a..9c448353 100644 --- a/examples/svelte/src/routes/examples/figma/+page.svelte +++ b/examples/svelte/src/routes/examples/figma/+page.svelte @@ -47,6 +47,8 @@ {onmove} {onmoveend} onpaneclick={(e) => console.log('on pane click', e)} + onselectionend={(e) => console.log('on selection end', e)} + onselectionstart={(e) => console.log('on selection start', e)} > diff --git a/packages/react/src/container/Pane/index.tsx b/packages/react/src/container/Pane/index.tsx index f952434b..32dcdb9a 100644 --- a/packages/react/src/container/Pane/index.tsx +++ b/packages/react/src/container/Pane/index.tsx @@ -120,6 +120,7 @@ export function Pane({ const onClickCapture = (event: ReactMouseEvent) => { if (selectionInProgress.current) { event.stopPropagation(); + selectionInProgress.current = false; } }; @@ -202,6 +203,7 @@ export function Pane({ if (distance <= paneClickDistance) { return; } + onSelectionStart?.(event); } selectionInProgress.current = true; @@ -277,14 +279,9 @@ export function Pane({ userSelectionRect: null, nodesSelectionActive: selectedNodeIds.current.size > 0, }); - onSelectionEnd?.(event); - /* - * 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 (selectionKeyPressed || selectionOnDrag) { - selectionInProgress.current = false; + if (selectionInProgress.current) { + onSelectionEnd?.(event); } selectionStarted.current = false; diff --git a/packages/svelte/src/lib/container/Pane/Pane.svelte b/packages/svelte/src/lib/container/Pane/Pane.svelte index fed9d005..46c48625 100644 --- a/packages/svelte/src/lib/container/Pane/Pane.svelte +++ b/packages/svelte/src/lib/container/Pane/Pane.svelte @@ -147,6 +147,7 @@ if (distance <= paneClickDistance) { return; } + onselectionstart?.(event); } selectionInProgress = true; @@ -220,7 +221,9 @@ store.selectionRectMode = 'nodes'; } - onselectionend?.(event); + if (selectionInProgress) { + onselectionend?.(event); + } selectionStarted = false; } From e743054b7e7324680b56080a216fce3546b20198 Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 28 Oct 2025 09:29:00 +0100 Subject: [PATCH 05/18] chore(pane): cleanup --- packages/react/src/container/Pane/index.tsx | 51 +++++++++------------ 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/packages/react/src/container/Pane/index.tsx b/packages/react/src/container/Pane/index.tsx index 32dcdb9a..3296098b 100644 --- a/packages/react/src/container/Pane/index.tsx +++ b/packages/react/src/container/Pane/index.tsx @@ -1,19 +1,15 @@ import { useRef, + type MouseEventHandler, + type MutableRefObject, type MouseEvent as ReactMouseEvent, type PointerEvent as ReactPointerEvent, + type WheelEvent as ReactWheelEvent, type ReactNode, } from 'react'; import { shallow } from 'zustand/shallow'; import cc from 'classcat'; -import { - getNodesInside, - getEventPosition, - SelectionMode, - areSetsEqual, - type NodeChange, - type EdgeChange, -} from '@xyflow/system'; +import { getNodesInside, getEventPosition, SelectionMode, areSetsEqual } from '@xyflow/system'; import { UserSelection } from '../../components/UserSelection'; import { containerStyle } from '../../styles/utils'; @@ -44,9 +40,9 @@ type PaneProps = { >; const wrapHandler = ( - handler: React.MouseEventHandler | undefined, - containerRef: React.MutableRefObject -): React.MouseEventHandler => { + handler: MouseEventHandler | undefined, + containerRef: MutableRefObject +): MouseEventHandler => { return (event: ReactMouseEvent) => { if (event.target !== containerRef.current) { return; @@ -85,7 +81,6 @@ export function Pane({ const container = useRef(null); const containerBounds = useRef(); - const selectedNodeIds = useRef>(new Set()); const selectedEdgeIds = useRef>(new Set()); @@ -115,7 +110,7 @@ export function Pane({ onPaneContextMenu?.(event); }; - const onWheel = onPaneScroll ? (event: React.WheelEvent) => onPaneScroll(event) : undefined; + const onWheel = onPaneScroll ? (event: ReactWheelEvent) => onPaneScroll(event) : undefined; const onClickCapture = (event: ReactMouseEvent) => { if (selectionInProgress.current) { @@ -130,19 +125,16 @@ export function Pane({ const { resetSelectedElements, domNode } = store.getState(); containerBounds.current = domNode?.getBoundingClientRect(); - const isNoKeyEvent = event.target !== container.current && !!(event.target as HTMLElement).closest('.nokey'); - const isSelectionActive = - (selectionOnDrag && container.current === event.target) || !selectionOnDrag || selectionKeyPressed; + if (!containerBounds.current) { + return; + } - if ( - !elementsSelectable || - !isSelecting || - event.button !== 0 || - !containerBounds.current || - isNoKeyEvent || - !isSelectionActive || - !event.isPrimary - ) { + const eventTargetIsContainer = event.target === container.current; + // if a child element has the 'nokey' class, we don't want to swallow the event and don't start a selection + const isNoKeyEvent = !eventTargetIsContainer && !!(event.target as HTMLElement).closest('.nokey'); + const isSelectionActive = (selectionOnDrag && eventTargetIsContainer) || !selectionOnDrag || selectionKeyPressed; + + if (isNoKeyEvent || !isSelecting || !isSelectionActive || event.button !== 0 || !event.isPrimary) { return; } @@ -164,7 +156,7 @@ export function Pane({ }, }); - if (event.target !== container.current || paneClickDistance === 0) { + if (!eventTargetIsContainer || paneClickDistance === 0) { event.stopPropagation(); event.preventDefault(); @@ -242,12 +234,12 @@ export function Pane({ } if (!areSetsEqual(prevSelectedNodeIds, selectedNodeIds.current)) { - const changes = getSelectionChanges(nodeLookup, selectedNodeIds.current, true) as NodeChange[]; + const changes = getSelectionChanges(nodeLookup, selectedNodeIds.current, true); triggerNodeChanges(changes); } if (!areSetsEqual(prevSelectedEdgeIds, selectedEdgeIds.current)) { - const changes = getSelectionChanges(edgeLookup, selectedEdgeIds.current) as EdgeChange[]; + const changes = getSelectionChanges(edgeLookup, selectedEdgeIds.current); triggerEdgeChanges(changes); } @@ -265,12 +257,11 @@ export function Pane({ (event.target as Partial)?.releasePointerCapture?.(event.pointerId); - const { userSelectionRect } = store.getState(); /* * We only want to trigger click functions when in selection mode if * the user did not move the mouse. */ - if (!userSelectionActive && userSelectionRect && event.target === container.current) { + if (!userSelectionActive && event.target === container.current && store.getState().userSelectionRect) { onClick?.(event); } From 0220907b8ba52d2b7bbe8be04260f73a57eb7663 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Tue, 28 Oct 2025 11:22:26 +0100 Subject: [PATCH 06/18] typo --- .changeset/sour-jokes-argue.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/sour-jokes-argue.md b/.changeset/sour-jokes-argue.md index 1eb9edea..dca49fd6 100644 --- a/.changeset/sour-jokes-argue.md +++ b/.changeset/sour-jokes-argue.md @@ -4,4 +4,4 @@ '@xyflow/system': patch --- -Fix onPaneClick events being surpressed when selectionOnDrag=true +Fix onPaneClick events being suppressed when selectionOnDrag=true From 2fb49335066c58e296282c1de9a4920f22672a76 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Tue, 28 Oct 2025 11:42:51 +0100 Subject: [PATCH 07/18] make sure onSelectionEnd is always fired --- packages/react/src/container/Pane/index.tsx | 1 + packages/svelte/src/lib/container/Pane/Pane.svelte | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/react/src/container/Pane/index.tsx b/packages/react/src/container/Pane/index.tsx index 3296098b..460c290d 100644 --- a/packages/react/src/container/Pane/index.tsx +++ b/packages/react/src/container/Pane/index.tsx @@ -163,6 +163,7 @@ export function Pane({ resetSelectedElements(); onSelectionStart?.(event); + selectionInProgress.current = true; } }; diff --git a/packages/svelte/src/lib/container/Pane/Pane.svelte b/packages/svelte/src/lib/container/Pane/Pane.svelte index 46c48625..45547737 100644 --- a/packages/svelte/src/lib/container/Pane/Pane.svelte +++ b/packages/svelte/src/lib/container/Pane/Pane.svelte @@ -126,6 +126,7 @@ store.unselectNodesAndEdges(); onselectionstart?.(event); + selectionInProgress = true; } } From 5d0887c9db861560ec03491b8ab17bff1ab2a977 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Tue, 28 Oct 2025 11:46:33 +0100 Subject: [PATCH 08/18] cleanup paneClickDistance remnants --- packages/react/src/components/StoreUpdater/index.tsx | 1 - packages/react/src/container/Pane/index.tsx | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/react/src/components/StoreUpdater/index.tsx b/packages/react/src/components/StoreUpdater/index.tsx index 2eb926d8..7f375d0a 100644 --- a/packages/react/src/components/StoreUpdater/index.tsx +++ b/packages/react/src/components/StoreUpdater/index.tsx @@ -107,7 +107,6 @@ const initPrevValues = { elementsSelectable: true, noPanClassName: 'nopan', rfId: '1', - paneClickDistance: 0, }; export function StoreUpdater( diff --git a/packages/react/src/container/Pane/index.tsx b/packages/react/src/container/Pane/index.tsx index 460c290d..8a6f032b 100644 --- a/packages/react/src/container/Pane/index.tsx +++ b/packages/react/src/container/Pane/index.tsx @@ -21,6 +21,7 @@ type PaneProps = { isSelecting: boolean; selectionKeyPressed: boolean; children: ReactNode; + paneClickDistance: number; } & Partial< Pick< ReactFlowProps, @@ -35,7 +36,6 @@ type PaneProps = { | 'onPaneMouseMove' | 'onPaneMouseLeave' | 'selectionOnDrag' - | 'paneClickDistance' > >; @@ -63,7 +63,7 @@ export function Pane({ selectionKeyPressed, selectionMode = SelectionMode.Full, panOnDrag, - paneClickDistance = 0, + paneClickDistance, selectionOnDrag, onSelectionStart, onSelectionEnd, From f29adb83538aca0b8c04baa7f532c9ad65a2a656 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Tue, 28 Oct 2025 12:20:47 +0100 Subject: [PATCH 09/18] only prevent pointer events when starting selection box on top of node --- examples/react/src/examples/Figma/index.tsx | 5 ++++- packages/react/src/container/Pane/index.tsx | 10 ++++------ .../svelte/src/lib/container/Pane/Pane.svelte | 18 ++++++++---------- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/examples/react/src/examples/Figma/index.tsx b/examples/react/src/examples/Figma/index.tsx index 28e21179..2ac0d6b6 100644 --- a/examples/react/src/examples/Figma/index.tsx +++ b/examples/react/src/examples/Figma/index.tsx @@ -30,7 +30,7 @@ const BasicFlow = () => { { onPaneClick={(e) => console.log('pane click', e)} onSelectionStart={(e) => console.log('on selection start', e)} onSelectionEnd={(e) => console.log('on selection end', e)} + onPointerDown={(e) => console.log('pointer down', e)} + onPointerUp={(e) => console.log('pointer up', e)} + onClick={(e) => console.log('click', e)} > diff --git a/packages/react/src/container/Pane/index.tsx b/packages/react/src/container/Pane/index.tsx index 8a6f032b..4c40afd6 100644 --- a/packages/react/src/container/Pane/index.tsx +++ b/packages/react/src/container/Pane/index.tsx @@ -86,7 +86,6 @@ export function Pane({ // Used to prevent click events when the user lets go of the selectionKey during a selection const selectionInProgress = useRef(false); - const selectionStarted = useRef(false); const onClick = (event: ReactMouseEvent) => { // We prevent click events when the user let go of the selectionKey during a selection @@ -140,7 +139,6 @@ export function Pane({ (event.target as Partial)?.setPointerCapture?.(event.pointerId); - selectionStarted.current = true; selectionInProgress.current = false; const { x, y } = getEventPosition(event.nativeEvent, containerBounds.current); @@ -156,10 +154,12 @@ export function Pane({ }, }); - if (!eventTargetIsContainer || paneClickDistance === 0) { + if (!eventTargetIsContainer) { event.stopPropagation(); event.preventDefault(); + } + if (!eventTargetIsContainer || paneClickDistance === 0 || !selectionOnDrag) { resetSelectedElements(); onSelectionStart?.(event); @@ -252,7 +252,7 @@ export function Pane({ }; const onPointerUp = (event: ReactPointerEvent) => { - if (event.button !== 0 || !selectionStarted.current) { + if (event.button !== 0) { return; } @@ -275,8 +275,6 @@ export function Pane({ if (selectionInProgress.current) { onSelectionEnd?.(event); } - - selectionStarted.current = false; }; const draggable = panOnDrag === true || (Array.isArray(panOnDrag) && panOnDrag.includes(0)); diff --git a/packages/svelte/src/lib/container/Pane/Pane.svelte b/packages/svelte/src/lib/container/Pane/Pane.svelte index 45547737..cb5595b6 100644 --- a/packages/svelte/src/lib/container/Pane/Pane.svelte +++ b/packages/svelte/src/lib/container/Pane/Pane.svelte @@ -77,19 +77,18 @@ // Used to prevent click events when the user lets go of the selectionKey during a selection let selectionInProgress = false; - let selectionStarted = false; // We start the selection process when the user clicks down on the pane function onPointerDownCapture(event: PointerEvent) { containerBounds = container?.getBoundingClientRect(); + const eventTargetIsContainer = event.target === container; + const isNoKeyEvent = - event.target !== container && !!(event.target as HTMLElement).closest('.nokey'); + !eventTargetIsContainer && !!(event.target as HTMLElement).closest('.nokey'); const isSelectionActive = - (selectionOnDrag && container === event.target) || - !selectionOnDrag || - store.selectionKeyPressed; + (selectionOnDrag && eventTargetIsContainer) || !selectionOnDrag || store.selectionKeyPressed; if ( !store.elementsSelectable || @@ -105,7 +104,6 @@ (event.target as Partial)?.setPointerCapture?.(event.pointerId); - selectionStarted = true; selectionInProgress = false; const { x, y } = getEventPosition(event, containerBounds); @@ -119,10 +117,12 @@ y }; - if (event.target !== container || paneClickDistance === 0) { + if (!eventTargetIsContainer) { event.stopPropagation(); event.preventDefault(); + } + if (paneClickDistance === 0 || !selectionOnDrag) { store.unselectNodesAndEdges(); onselectionstart?.(event); @@ -203,7 +203,7 @@ } function onPointerUp(event: PointerEvent) { - if (event.button !== 0 || !selectionStarted) { + if (event.button !== 0) { return; } @@ -225,8 +225,6 @@ if (selectionInProgress) { onselectionend?.(event); } - - selectionStarted = false; } const onContextMenu = (event: MouseEvent) => { From 7238977e7af52ab83a12ebeeb5977c62b57b577c Mon Sep 17 00:00:00 2001 From: peterkogo Date: Tue, 28 Oct 2025 12:35:41 +0100 Subject: [PATCH 10/18] make sure elements are deselected once the selection starts --- packages/react/src/container/Pane/index.tsx | 2 ++ packages/svelte/src/lib/container/Pane/Pane.svelte | 1 + 2 files changed, 3 insertions(+) diff --git a/packages/react/src/container/Pane/index.tsx b/packages/react/src/container/Pane/index.tsx index 4c40afd6..b57d8f3c 100644 --- a/packages/react/src/container/Pane/index.tsx +++ b/packages/react/src/container/Pane/index.tsx @@ -177,6 +177,7 @@ export function Pane({ triggerNodeChanges, triggerEdgeChanges, defaultEdgeOptions, + resetSelectedElements, } = store.getState(); if (!containerBounds.current || !userSelectionRect) { @@ -196,6 +197,7 @@ export function Pane({ if (distance <= paneClickDistance) { return; } + resetSelectedElements(); onSelectionStart?.(event); } diff --git a/packages/svelte/src/lib/container/Pane/Pane.svelte b/packages/svelte/src/lib/container/Pane/Pane.svelte index cb5595b6..569d0d5d 100644 --- a/packages/svelte/src/lib/container/Pane/Pane.svelte +++ b/packages/svelte/src/lib/container/Pane/Pane.svelte @@ -148,6 +148,7 @@ if (distance <= paneClickDistance) { return; } + store.unselectNodesAndEdges(); onselectionstart?.(event); } From eb7a27251a6a8df0861595b2735be753b0371768 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Tue, 28 Oct 2025 13:21:59 +0100 Subject: [PATCH 11/18] rename hasActiveSelection to isSelectionEnabled --- packages/react/src/container/Pane/index.tsx | 14 +++++++------- packages/svelte/src/lib/container/Pane/Pane.svelte | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/react/src/container/Pane/index.tsx b/packages/react/src/container/Pane/index.tsx index b57d8f3c..53b3d15e 100644 --- a/packages/react/src/container/Pane/index.tsx +++ b/packages/react/src/container/Pane/index.tsx @@ -77,7 +77,7 @@ export function Pane({ }: PaneProps) { const store = useStoreApi(); const { userSelectionActive, elementsSelectable, dragging, connectionInProgress } = useStore(selector, shallow); - const hasActiveSelection = elementsSelectable && (isSelecting || userSelectionActive); + const isSelectionEnabled = elementsSelectable && (isSelecting || userSelectionActive); const container = useRef(null); const containerBounds = useRef(); @@ -284,14 +284,14 @@ export function Pane({ return (
{@render children()}
From 2baa83913877eaa80b36f9c38d25c49c9699cc56 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Tue, 28 Oct 2025 14:00:05 +0100 Subject: [PATCH 12/18] simplify conditions --- packages/react/src/container/Pane/index.tsx | 9 +++------ .../svelte/src/lib/container/Pane/Pane.svelte | 15 +++++++-------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/packages/react/src/container/Pane/index.tsx b/packages/react/src/container/Pane/index.tsx index 53b3d15e..94f23c35 100644 --- a/packages/react/src/container/Pane/index.tsx +++ b/packages/react/src/container/Pane/index.tsx @@ -123,15 +123,12 @@ export function Pane({ const onPointerDownCapture = (event: ReactPointerEvent): void => { const { resetSelectedElements, domNode } = store.getState(); containerBounds.current = domNode?.getBoundingClientRect(); - - if (!containerBounds.current) { - return; - } + if (!containerBounds.current) return; const eventTargetIsContainer = event.target === container.current; // if a child element has the 'nokey' class, we don't want to swallow the event and don't start a selection const isNoKeyEvent = !eventTargetIsContainer && !!(event.target as HTMLElement).closest('.nokey'); - const isSelectionActive = (selectionOnDrag && eventTargetIsContainer) || !selectionOnDrag || selectionKeyPressed; + const isSelectionActive = (selectionOnDrag && eventTargetIsContainer) || selectionKeyPressed; if (isNoKeyEvent || !isSelecting || !isSelectionActive || event.button !== 0 || !event.isPrimary) { return; @@ -159,7 +156,7 @@ export function Pane({ event.preventDefault(); } - if (!eventTargetIsContainer || paneClickDistance === 0 || !selectionOnDrag) { + if (paneClickDistance === 0 || selectionKeyPressed) { resetSelectedElements(); onSelectionStart?.(event); diff --git a/packages/svelte/src/lib/container/Pane/Pane.svelte b/packages/svelte/src/lib/container/Pane/Pane.svelte index 5aaf0231..d2fdec9d 100644 --- a/packages/svelte/src/lib/container/Pane/Pane.svelte +++ b/packages/svelte/src/lib/container/Pane/Pane.svelte @@ -81,6 +81,7 @@ // We start the selection process when the user clicks down on the pane function onPointerDownCapture(event: PointerEvent) { containerBounds = container?.getBoundingClientRect(); + if (!containerBounds) return; const eventTargetIsContainer = event.target === container; @@ -88,15 +89,13 @@ !eventTargetIsContainer && !!(event.target as HTMLElement).closest('.nokey'); const isSelectionActive = - (selectionOnDrag && eventTargetIsContainer) || !selectionOnDrag || store.selectionKeyPressed; + (selectionOnDrag && eventTargetIsContainer) || store.selectionKeyPressed; if ( - !store.elementsSelectable || - !isSelecting || - event.button !== 0 || - !containerBounds || isNoKeyEvent || + !isSelecting || !isSelectionActive || + event.button !== 0 || !event.isPrimary ) { return; @@ -122,7 +121,7 @@ event.preventDefault(); } - if (paneClickDistance === 0 || !selectionOnDrag) { + if (paneClickDistance === 0 || store.selectionKeyPressed) { store.unselectNodesAndEdges(); onselectionstart?.(event); @@ -219,8 +218,8 @@ store.selectionRect = null; - if (selectionInProgress && selectedNodeIds.size > 0) { - store.selectionRectMode = 'nodes'; + if (selectionInProgress) { + store.selectionRectMode = selectedNodeIds.size > 0 ? 'nodes' : null; } if (selectionInProgress) { From 082801bb30d0234656ece94cee8d6d1efbed9896 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Tue, 28 Oct 2025 14:11:20 +0100 Subject: [PATCH 13/18] fix NodeSelection appearing when no nodes are selected --- .../src/lib/components/NodeSelection/NodeSelection.svelte | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/svelte/src/lib/components/NodeSelection/NodeSelection.svelte b/packages/svelte/src/lib/components/NodeSelection/NodeSelection.svelte index 54109028..e453f8b6 100644 --- a/packages/svelte/src/lib/components/NodeSelection/NodeSelection.svelte +++ b/packages/svelte/src/lib/components/NodeSelection/NodeSelection.svelte @@ -31,7 +31,12 @@ if (store.selectionRectMode === 'nodes') { // eslint-disable-next-line @typescript-eslint/no-unused-expressions store.nodes; - return getInternalNodesBounds(store.nodeLookup, { filter: (node) => !!node.selected }); + const nodeBounds = getInternalNodesBounds(store.nodeLookup, { + filter: (node) => !!node.selected + }); + if (nodeBounds.width > 0 && nodeBounds.height > 0) { + return nodeBounds; + } } return null; }); From 2a401ed675b85417da46fbca68832c58d0f9bf02 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Tue, 28 Oct 2025 14:12:12 +0100 Subject: [PATCH 14/18] chore(changeset) --- .changeset/eight-rice-laugh.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/eight-rice-laugh.md diff --git a/.changeset/eight-rice-laugh.md b/.changeset/eight-rice-laugh.md new file mode 100644 index 00000000..95c2db53 --- /dev/null +++ b/.changeset/eight-rice-laugh.md @@ -0,0 +1,5 @@ +--- +'@xyflow/svelte': patch +--- + +Fix showin selection box when no nodes are selected From 2a2857852d06f39598a83eb9e453b8317ea617a7 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Tue, 28 Oct 2025 14:25:47 +0100 Subject: [PATCH 15/18] typo --- .changeset/eight-rice-laugh.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/eight-rice-laugh.md b/.changeset/eight-rice-laugh.md index 95c2db53..dd528c10 100644 --- a/.changeset/eight-rice-laugh.md +++ b/.changeset/eight-rice-laugh.md @@ -2,4 +2,4 @@ '@xyflow/svelte': patch --- -Fix showin selection box when no nodes are selected +Fix showing selection box when no nodes are selected From 3c609e01222f22c4fa669485949bd7d870d5a8f7 Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 28 Oct 2025 15:44:39 +0100 Subject: [PATCH 16/18] chore(svelte/selection): add class name and styles --- .../src/lib/components/NodeSelection/NodeSelection.svelte | 7 ++++++- .../svelte/src/lib/components/Selection/Selection.svelte | 7 +++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/svelte/src/lib/components/NodeSelection/NodeSelection.svelte b/packages/svelte/src/lib/components/NodeSelection/NodeSelection.svelte index e453f8b6..5dd87b42 100644 --- a/packages/svelte/src/lib/components/NodeSelection/NodeSelection.svelte +++ b/packages/svelte/src/lib/components/NodeSelection/NodeSelection.svelte @@ -85,7 +85,7 @@ onkeydown={store.disableKeyboardA11y ? undefined : onkeydown} bind:this={ref} > - + {/if} @@ -97,4 +97,9 @@ z-index: 2000; pointer-events: all; } + + .svelte-flow__selection-wrapper:focus, + .svelte-flow__selection-wrapper:focus-visible { + outline: none; + } diff --git a/packages/svelte/src/lib/components/Selection/Selection.svelte b/packages/svelte/src/lib/components/Selection/Selection.svelte index 25a7de2e..1422b185 100644 --- a/packages/svelte/src/lib/components/Selection/Selection.svelte +++ b/packages/svelte/src/lib/components/Selection/Selection.svelte @@ -1,4 +1,5 @@ {#if isVisible}
Date: Tue, 28 Oct 2025 15:46:50 +0100 Subject: [PATCH 17/18] chore(svelte/selection): cleanup --- .../src/lib/components/NodeSelection/NodeSelection.svelte | 2 +- .../svelte/src/lib/components/Selection/Selection.svelte | 7 ++----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/svelte/src/lib/components/NodeSelection/NodeSelection.svelte b/packages/svelte/src/lib/components/NodeSelection/NodeSelection.svelte index 5dd87b42..65e12b6a 100644 --- a/packages/svelte/src/lib/components/NodeSelection/NodeSelection.svelte +++ b/packages/svelte/src/lib/components/NodeSelection/NodeSelection.svelte @@ -85,7 +85,7 @@ onkeydown={store.disableKeyboardA11y ? undefined : onkeydown} bind:this={ref} > - +
{/if} diff --git a/packages/svelte/src/lib/components/Selection/Selection.svelte b/packages/svelte/src/lib/components/Selection/Selection.svelte index 1422b185..25a7de2e 100644 --- a/packages/svelte/src/lib/components/Selection/Selection.svelte +++ b/packages/svelte/src/lib/components/Selection/Selection.svelte @@ -1,5 +1,4 @@ {#if isVisible}
Date: Tue, 28 Oct 2025 16:13:41 +0100 Subject: [PATCH 18/18] chore(changeset) --- .changeset/thirty-goats-invent.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/thirty-goats-invent.md diff --git a/.changeset/thirty-goats-invent.md b/.changeset/thirty-goats-invent.md new file mode 100644 index 00000000..c57c53f5 --- /dev/null +++ b/.changeset/thirty-goats-invent.md @@ -0,0 +1,5 @@ +--- +'@xyflow/svelte': patch +--- + +Remove focus outline from selection box