Skip to content

Commit

Permalink
Merge pull request #4880 from crimx/patch-2
Browse files Browse the repository at this point in the history
Fix type check for all event targets
  • Loading branch information
moklick authored Jan 10, 2025
2 parents 78f9d4e + e2d849d commit cdca1c5
Show file tree
Hide file tree
Showing 10 changed files with 27 additions and 18 deletions.
7 changes: 7 additions & 0 deletions .changeset/little-toys-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@xyflow/react': patch
'@xyflow/svelte': patch
'@xyflow/system': patch
---

Add type check for all event targets
2 changes: 1 addition & 1 deletion examples/react/src/examples/AddNodeOnEdgeDrop/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const AddNodeOnEdgeDrop = () => {
(event) => {
if (!connectingNodeId.current) return;

const targetIsPane = (event.target as HTMLDivElement)?.classList.contains('react-flow__pane');
const targetIsPane = (event.target as Partial<Element> | null)?.classList?.contains('react-flow__pane');

if (targetIsPane && 'clientX' in event && 'clientY' in event) {
// we need to remove the wrapper bounds, in order to get the correct position
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
if (!connectingNodeId) return;
// See of connection landed inside the flow pane
const targetIsPane = (event.target as HTMLDivElement)?.classList.contains('svelte-flow__pane');
const targetIsPane = (event.target as Partial<Element> | null)?.classList?.contains('svelte-flow__pane');
if (targetIsPane && 'clientX' in event && 'clientY' in event) {
const id = getId();
const position = {
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/components/Handle/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ function HandleComponent(
return;
}

const doc = getHostForElement(event.target as HTMLElement);
const doc = getHostForElement(event.target);
const isValidConnectionHandler = isValidConnection || isValidConnectionStore;
const { connection, isValid } = XYHandle.isValid(event.nativeEvent, {
handle: {
Expand Down
4 changes: 2 additions & 2 deletions packages/react/src/container/Pane/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export function Pane({
return;
}

(event.target as Element)?.setPointerCapture?.(event.pointerId);
(event.target as Partial<Element> | null)?.setPointerCapture?.(event.pointerId);

selectionStarted.current = true;
selectionInProgress.current = false;
Expand Down Expand Up @@ -230,7 +230,7 @@ export function Pane({
return;
}

(event.target as Element)?.releasePointerCapture?.(event.pointerId);
(event.target as Partial<Element>)?.releasePointerCapture?.(event.pointerId);
const { userSelectionRect } = store.getState();

// We only want to trigger click functions when in selection mode if
Expand Down
4 changes: 2 additions & 2 deletions packages/svelte/src/lib/container/Pane/Pane.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
return;
}
(event.target as Element)?.setPointerCapture?.(event.pointerId);
(event.target as Partial<Element> | null)?.setPointerCapture?.(event.pointerId);
const { x, y } = getEventPosition(event, containerBounds);
Expand Down Expand Up @@ -175,7 +175,7 @@
return;
}
(event.target as Element)?.releasePointerCapture?.(event.pointerId);
(event.target as Partial<Element> | null)?.releasePointerCapture?.(event.pointerId);
// We only want to trigger click functions when in selection mode if
// the user did not move the mouse.
Expand Down
12 changes: 7 additions & 5 deletions packages/system/src/utils/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,20 @@ export const getDimensions = (node: HTMLDivElement): Dimensions => ({
height: node.offsetHeight,
});

export const getHostForElement = (element: HTMLElement): Document | ShadowRoot =>
(element.getRootNode?.() as Document | ShadowRoot) || window?.document;
export const getHostForElement = (element: HTMLElement | EventTarget | null): Document | ShadowRoot =>
((element as Partial<HTMLElement> | null)?.getRootNode?.() as Document | ShadowRoot) || window?.document;

const inputTags = ['INPUT', 'SELECT', 'TEXTAREA'];

export function isInputDOMNode(event: KeyboardEvent): boolean {
// using composed path for handling shadow dom
const target = (event.composedPath?.()?.[0] || event.target) as HTMLElement;
const isInput = inputTags.includes(target?.nodeName) || target?.hasAttribute?.('contenteditable');
const target = (event.composedPath?.()?.[0] || event.target) as Element | null;
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');
return isInput || !!target.closest('.nokey');
}

export const isMouseEvent = (event: MouseEvent | TouchEvent): event is MouseEvent => 'clientX' in event;
Expand Down
2 changes: 1 addition & 1 deletion packages/system/src/xydrag/XYDrag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
}
})
.filter((event: MouseEvent) => {
const target = event.target as HTMLDivElement;
const target = event.target;
const isDraggable =
!event.button &&
(!noDragClassName || !hasSelector(target, `.${noDragClassName}`, domNode)) &&
Expand Down
8 changes: 4 additions & 4 deletions packages/system/src/xydrag/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,13 @@ export function isParentSelected<NodeType extends NodeBase>(node: NodeType, node
return isParentSelected(parentNode, nodeLookup);
}

export function hasSelector(target: Element, selector: string, domNode: Element): boolean {
let current = target;
export function hasSelector(target: Element | EventTarget | null, selector: string, domNode: Element): boolean {
let current = target as Partial<Element> | null | undefined;

do {
if (current?.matches(selector)) return true;
if (current?.matches?.(selector)) return true;
if (current === domNode) return false;
current = current.parentElement as Element;
current = current?.parentElement;
} while (current);

return false;
Expand Down
2 changes: 1 addition & 1 deletion packages/system/src/xyhandle/XYHandle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ function onPointerDown(
}: OnPointerDownParams
) {
// when xyflow is used inside a shadow root we can't use document
const doc = getHostForElement(event.target as HTMLElement);
const doc = getHostForElement(event.target);
let autoPanId = 0;
let closestHandle: Handle | null;

Expand Down

0 comments on commit cdca1c5

Please sign in to comment.