From 75aff8ef269281fc93e0841b809929826ef89bfc Mon Sep 17 00:00:00 2001 From: moklick Date: Wed, 15 Oct 2025 15:15:07 +0200 Subject: [PATCH] refactor(selection): make it possible to start a selection above a node #5342 --- .../examples/CustomNode/ColorSelectorNode.tsx | 2 +- packages/react/src/container/Pane/index.tsx | 43 +++++++++++++++---- packages/system/src/utils/dom.ts | 1 - 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/examples/react/src/examples/CustomNode/ColorSelectorNode.tsx b/examples/react/src/examples/CustomNode/ColorSelectorNode.tsx index 1265d408..77bce1c9 100644 --- a/examples/react/src/examples/CustomNode/ColorSelectorNode.tsx +++ b/examples/react/src/examples/CustomNode/ColorSelectorNode.tsx @@ -30,7 +30,7 @@ function ColorSelectorNode({ data, isConnectable }: NodeProps
Custom Color Picker Node: {data.color}
- + (false); const selectionStarted = useRef(false); + const isNoKeyEvent = useRef(null); + const onClick = (event: ReactMouseEvent) => { // 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 @@ -119,20 +121,40 @@ export function Pane({ const onWheel = onPaneScroll ? (event: React.WheelEvent) => onPaneScroll(event) : undefined; + const onMouseDownCapture = (event: ReactMouseEvent) => { + isNoKeyEvent.current = + isNoKeyEvent.current === null ? !!(event.target as HTMLElement).closest('.nokey') : isNoKeyEvent.current; + + if (isNoKeyEvent.current) { + return; + } + + event.stopPropagation(); + }; + + const onClickCapture = (event: ReactMouseEvent) => { + if (isNoKeyEvent.current) { + isNoKeyEvent.current = null; + return; + } + + isNoKeyEvent.current = null; + + event.stopPropagation(); + }; + const onPointerDown = (event: ReactPointerEvent): void => { const { resetSelectedElements, domNode } = store.getState(); containerBounds.current = domNode?.getBoundingClientRect(); + isNoKeyEvent.current = + isNoKeyEvent.current === null ? !!(event.target as HTMLElement).closest('.nokey') : isNoKeyEvent.current; - if ( - !elementsSelectable || - !isSelecting || - event.button !== 0 || - event.target !== container.current || - !containerBounds.current - ) { + if (!elementsSelectable || !isSelecting || event.button !== 0 || !containerBounds.current || isNoKeyEvent.current) { return; } + event.stopPropagation(); + (event.target as Partial | null)?.setPointerCapture?.(event.pointerId); selectionStarted.current = true; @@ -234,7 +256,7 @@ export function Pane({ (event.target as Partial)?.releasePointerCapture?.(event.pointerId); const { userSelectionRect } = store.getState(); - + isNoKeyEvent.current = null; /* * We only want to trigger click functions when in selection mode if * the user did not move the mouse. @@ -270,9 +292,12 @@ export function Pane({ onContextMenu={wrapHandler(onContextMenu, container)} onWheel={wrapHandler(onWheel, container)} onPointerEnter={hasActiveSelection ? undefined : onPaneMouseEnter} - onPointerDown={hasActiveSelection ? onPointerDown : onPaneMouseMove} + onPointerDown={hasActiveSelection ? undefined : onPaneMouseMove} onPointerMove={hasActiveSelection ? onPointerMove : onPaneMouseMove} onPointerUp={hasActiveSelection ? onPointerUp : undefined} + onPointerDownCapture={hasActiveSelection ? onPointerDown : undefined} + onMouseDownCapture={hasActiveSelection ? onMouseDownCapture : undefined} + onClickCapture={hasActiveSelection ? onClickCapture : undefined} onPointerLeave={onPaneMouseLeave} ref={container} style={containerStyle} diff --git a/packages/system/src/utils/dom.ts b/packages/system/src/utils/dom.ts index 2311153f..5f92c136 100644 --- a/packages/system/src/utils/dom.ts +++ b/packages/system/src/utils/dom.ts @@ -43,7 +43,6 @@ export function isInputDOMNode(event: KeyboardEvent): boolean { if (target?.nodeType !== 1 /* Node.ELEMENT_NODE */) return false; const isInput = inputTags.includes(target.nodeName) || target.hasAttribute('contenteditable'); - // when an input field is focused we don't want to trigger deletion or movement of nodes return isInput || !!target.closest('.nokey'); }