Files
xyflow/packages/system/src/xydrag/utils.ts
2025-02-11 15:18:41 +01:00

124 lines
3.3 KiB
TypeScript

import { type NodeDragItem, type XYPosition, InternalNodeBase, NodeBase, NodeLookup } from '../types';
export function isParentSelected<NodeType extends NodeBase>(node: NodeType, nodeLookup: NodeLookup): boolean {
if (!node.parentId) {
return false;
}
const parentNode = nodeLookup.get(node.parentId);
if (!parentNode) {
return false;
}
if (parentNode.selected) {
return true;
}
return isParentSelected(parentNode, nodeLookup);
}
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 === domNode) return false;
current = current?.parentElement;
} while (current);
return false;
}
// looks for all selected nodes and created a NodeDragItem for each of them
export function getDragItems<NodeType extends NodeBase>(
nodeLookup: Map<string, InternalNodeBase<NodeType>>,
nodesDraggable: boolean,
mousePos: XYPosition,
nodeId?: string
): Map<string, NodeDragItem> {
const dragItems = new Map<string, NodeDragItem>();
for (const [id, node] of nodeLookup) {
if (
(node.selected || node.id === nodeId) &&
(!node.parentId || !isParentSelected(node, nodeLookup)) &&
(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined'))
) {
const internalNode = nodeLookup.get(id);
if (internalNode) {
dragItems.set(id, {
id,
position: internalNode.position || { x: 0, y: 0 },
distance: {
x: mousePos.x - internalNode.internals.positionAbsolute.x,
y: mousePos.y - internalNode.internals.positionAbsolute.y,
},
extent: internalNode.extent,
parentId: internalNode.parentId,
origin: internalNode.origin,
expandParent: internalNode.expandParent,
internals: {
positionAbsolute: internalNode.internals.positionAbsolute || { x: 0, y: 0 },
},
measured: {
width: internalNode.measured.width ?? 0,
height: internalNode.measured.height ?? 0,
},
});
}
}
}
return dragItems;
}
/*
* returns two params:
* 1. the dragged node (or the first of the list, if we are dragging a node selection)
* 2. array of selected nodes (for multi selections)
*/
export function getEventHandlerParams<NodeType extends InternalNodeBase>({
nodeId,
dragItems,
nodeLookup,
dragging = true,
}: {
nodeId?: string;
dragItems: Map<string, NodeDragItem>;
nodeLookup: Map<string, NodeType>;
dragging?: boolean;
}): [NodeBase, NodeBase[]] {
const nodesFromDragItems: NodeBase[] = [];
for (const [id, dragItem] of dragItems) {
const node = nodeLookup.get(id)?.internals.userNode;
if (node) {
nodesFromDragItems.push({
...node,
position: dragItem.position,
dragging,
});
}
}
if (!nodeId) {
return [nodesFromDragItems[0], nodesFromDragItems];
}
const node = nodeLookup.get(nodeId)?.internals.userNode;
return [
!node
? nodesFromDragItems[0]
: {
...node,
position: dragItems.get(nodeId)?.position || node.position,
dragging,
},
nodesFromDragItems,
];
}