refactor(xydrag): use map for drag items

This commit is contained in:
moklick
2024-04-16 17:09:51 +02:00
parent b2b23a9cd4
commit 2102c17108
6 changed files with 103 additions and 72 deletions
+1
View File
@@ -5,6 +5,7 @@
## Patch changes
- use correct positions for intersection helpers
- fix minimap interaction for touch devices
## 12.0.0-next.14
+2 -2
View File
@@ -122,8 +122,8 @@ export type NodeDragItem = {
// distance from the mouse cursor to the node when start dragging
distance: XYPosition;
measured: {
width: number | null;
height: number | null;
width?: number;
height?: number;
};
internals: {
positionAbsolute: XYPosition;
+7 -3
View File
@@ -205,9 +205,13 @@ export function isCoordinateExtent(extent?: CoordinateExtent | 'parent'): extent
return extent !== undefined && extent !== 'parent';
}
export function getNodeDimensions<NodeType extends NodeBase = NodeBase>(
node: NodeType
): { width: number; height: number } {
export function getNodeDimensions(node: {
measured?: { width?: number; height?: number };
width?: number;
height?: number;
initialWidth?: number;
initialHeight?: number;
}): { width: number; height: number } {
return {
width: node.measured?.width ?? node.width ?? node.initialWidth ?? 0,
height: node.measured?.height ?? node.height ?? node.initialHeight ?? 0,
+17 -6
View File
@@ -10,6 +10,7 @@ import {
getViewportForBounds,
isCoordinateExtent,
getNodeDimensions,
getPositionWithOrigin,
} from './general';
import {
type Transform,
@@ -25,6 +26,7 @@ import {
OnBeforeDeleteBase,
NodeLookup,
InternalNodeBase,
NodeDragItem,
} from '../types';
import { errorMessages } from '../constants';
@@ -186,7 +188,7 @@ export const getNodesBounds = (
export type GetInternalNodesBoundsParams = {
nodeOrigin?: NodeOrigin;
useRelativePosition?: boolean;
filter?: (node: NodeBase) => boolean;
filter?: (node: NodeBase | NodeDragItem) => boolean;
};
/**
@@ -194,10 +196,9 @@ export type GetInternalNodesBoundsParams = {
* @internal
*/
export const getInternalNodesBounds = (
nodeLookup: NodeLookup,
nodeLookup: NodeLookup | Map<string, NodeDragItem>,
params: GetInternalNodesBoundsParams = {
nodeOrigin: [0, 0],
useRelativePosition: false,
}
): Rect => {
if (nodeLookup.size === 0) {
@@ -208,12 +209,22 @@ export const getInternalNodesBounds = (
nodeLookup.forEach((node) => {
if (params.filter == undefined || params.filter(node)) {
const nodePos = getNodePositionWithOrigin(node, node.origin || params.nodeOrigin);
const { width, height } = getNodeDimensions(node);
const { x, y } = getPositionWithOrigin({
x: node.internals.positionAbsolute.x,
y: node.internals.positionAbsolute.x,
width,
height,
origin: node.origin || params.nodeOrigin,
});
box = getBoundsOfBoxes(
box,
rectToBox({
...nodePos[params.useRelativePosition ? 'position' : 'positionAbsolute'],
...getNodeDimensions(node),
x,
y,
width,
height,
})
);
}
+27 -27
View File
@@ -7,7 +7,7 @@ import {
getPointerPosition,
calculateNodePosition,
snapPosition,
getNodesBounds,
getInternalNodesBounds,
rectToBox,
} from '../utils';
import { getDragItems, getEventHandlerParams, hasSelector, wrapSelectionDragFunc } from './utils';
@@ -29,7 +29,12 @@ import type {
InternalNodeBase,
} from '../types';
export type OnDrag = (event: MouseEvent, dragItems: NodeDragItem[], node: NodeBase, nodes: NodeBase[]) => void;
export type OnDrag = (
event: MouseEvent,
dragItems: Map<string, NodeDragItem>,
node: NodeBase,
nodes: NodeBase[]
) => void;
type StoreItems<OnNodeDrag> = {
nodes: NodeBase[];
@@ -89,7 +94,7 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
}: XYDragParams<OnNodeDrag>): XYDragInstance {
let lastPos: { x: number | null; y: number | null } = { x: null, y: null };
let autoPanId = 0;
let dragItems: NodeDragItem[] = [];
let dragItems = new Map<string, NodeDragItem>();
let autoPanStarted = false;
let mousePosition: XYPosition = { x: 0, y: 0 };
let containerBounds: DOMRect | null = null;
@@ -117,13 +122,13 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
let hasChange = false;
let nodesBox: Box = { x: 0, y: 0, x2: 0, y2: 0 };
if (dragItems.length > 1 && nodeExtent) {
const rect = getNodesBounds(dragItems as unknown as NodeBase[], { nodeOrigin });
if (dragItems.size > 1 && nodeExtent) {
const rect = getInternalNodesBounds(dragItems, { nodeOrigin });
nodesBox = rectToBox(rect);
}
dragItems = dragItems.map((n) => {
let nextPosition = { x: x - n.distance.x, y: y - n.distance.y };
for (const [id, dragItem] of dragItems) {
let nextPosition = { x: x - dragItem.distance.x, y: y - dragItem.distance.y };
if (snapToGrid) {
nextPosition = snapPosition(nextPosition, snapGrid);
@@ -136,13 +141,13 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
[nodeExtent[1][0], nodeExtent[1][1]],
];
if (dragItems.length > 1 && nodeExtent && !n.extent) {
const { positionAbsolute } = n.internals;
if (dragItems.size > 1 && nodeExtent && !dragItem.extent) {
const { positionAbsolute } = dragItem.internals;
const x1 = positionAbsolute.x - nodesBox.x + nodeExtent[0][0];
const x2 = positionAbsolute.x + (n.measured?.width ?? 0) - nodesBox.x2 + nodeExtent[1][0];
const x2 = positionAbsolute.x + (dragItem.measured?.width ?? 0) - nodesBox.x2 + nodeExtent[1][0];
const y1 = positionAbsolute.y - nodesBox.y + nodeExtent[0][1];
const y2 = positionAbsolute.y + (n.measured?.height ?? 0) - nodesBox.y2 + nodeExtent[1][1];
const y2 = positionAbsolute.y + (dragItem.measured?.height ?? 0) - nodesBox.y2 + nodeExtent[1][1];
adjustedNodeExtent = [
[x1, y1],
@@ -151,7 +156,7 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
}
const { position, positionAbsolute } = calculateNodePosition({
nodeId: n.id,
nodeId: id,
nextPosition,
nodeLookup,
nodeExtent: adjustedNodeExtent,
@@ -160,13 +165,11 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
});
// we want to make sure that we only fire a change event when there is a change
hasChange = hasChange || n.position.x !== position.x || n.position.y !== position.y;
hasChange = hasChange || dragItem.position.x !== position.x || dragItem.position.y !== position.y;
n.position = position;
n.internals.positionAbsolute = positionAbsolute;
return n;
});
dragItem.position = position;
dragItem.internals.positionAbsolute = positionAbsolute;
}
if (!hasChange) {
return;
@@ -174,7 +177,7 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
updateNodePositions(dragItems, true);
if (dragEvent && (onDrag || onNodeDrag || (!nodeId && onSelectionDrag))) {
if (dragEvent && (true || onDrag || onNodeDrag || (!nodeId && onSelectionDrag))) {
const [currentNode, currentNodes] = getEventHandlerParams({
nodeId,
dragItems,
@@ -185,8 +188,7 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
onNodeDrag?.(dragEvent, currentNode, currentNodes);
if (!nodeId) {
const _onSelectionDrag = wrapSelectionDragFunc(onSelectionDrag);
_onSelectionDrag(dragEvent, currentNode, currentNodes);
onSelectionDrag?.(dragEvent, currentNodes);
}
}
}
@@ -242,7 +244,7 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
lastPos = pointerPos;
dragItems = getDragItems(nodeLookup, nodesDraggable, pointerPos, nodeId);
if (dragItems.length > 0 && (onDragStart || onNodeDragStart || (!nodeId && onSelectionDragStart))) {
if (dragItems.size > 0 && (onDragStart || onNodeDragStart || (!nodeId && onSelectionDragStart))) {
const [currentNode, currentNodes] = getEventHandlerParams({
nodeId,
dragItems,
@@ -253,8 +255,7 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
onNodeDragStart?.(event.sourceEvent as MouseEvent, currentNode, currentNodes);
if (!nodeId) {
const _onSelectionDragStart = wrapSelectionDragFunc(onSelectionDragStart);
_onSelectionDragStart(event.sourceEvent as MouseEvent, currentNode, currentNodes);
onSelectionDragStart?.(event.sourceEvent as MouseEvent, currentNodes);
}
}
}
@@ -308,7 +309,7 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
dragStarted = false;
cancelAnimationFrame(autoPanId);
if (dragItems.length > 0) {
if (dragItems.size > 0) {
const { nodeLookup, updateNodePositions, onNodeDragStop, onSelectionDragStop } = getStoreItems();
updateNodePositions(dragItems, false);
@@ -324,8 +325,7 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
onNodeDragStop?.(event.sourceEvent as MouseEvent, currentNode, currentNodes);
if (!nodeId) {
const _onSelectionDragStop = wrapSelectionDragFunc(onSelectionDragStop);
_onSelectionDragStop(event.sourceEvent as MouseEvent, currentNode, currentNodes);
onSelectionDragStop?.(event.sourceEvent as MouseEvent, currentNodes);
}
}
}
+49 -34
View File
@@ -40,8 +40,8 @@ export function getDragItems<NodeType extends NodeBase>(
nodesDraggable: boolean,
mousePos: XYPosition,
nodeId?: string
): NodeDragItem[] {
const dragItems: NodeDragItem[] = [];
): Map<string, NodeDragItem> {
const dragItems = new Map<string, NodeDragItem>();
for (const [id, node] of nodeLookup) {
if (
@@ -49,29 +49,32 @@ export function getDragItems<NodeType extends NodeBase>(
(!node.parentId || !isParentSelected(node, nodeLookup)) &&
(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined'))
) {
const internalNode = nodeLookup.get(id)!;
const internalNode = nodeLookup.get(id);
dragItems.push({
id: internalNode.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,
},
});
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;
}
@@ -84,20 +87,32 @@ export function getEventHandlerParams<NodeType extends NodeBase>({
nodeLookup,
}: {
nodeId?: string;
dragItems: NodeDragItem[];
dragItems: Map<string, NodeDragItem>;
nodeLookup: Map<string, NodeType>;
}): [NodeType, NodeType[]] {
const nodesFromDragItems: NodeType[] = dragItems.map((n) => {
const node = nodeLookup.get(n.id)!;
const nodesFromDragItems: NodeType[] = [];
return {
for (const [id, dragItem] of dragItems) {
const node = nodeLookup.get(id);
if (node) {
nodesFromDragItems.push({
...node,
position: dragItem.position,
});
}
}
if (!nodeId) {
return [nodesFromDragItems[0], nodesFromDragItems];
}
const node = nodeLookup.get(nodeId)!;
return [
{
...node,
position: n.position,
measured: {
...n.measured,
},
};
});
return [nodeId ? nodesFromDragItems.find((n) => n.id === nodeId)! : nodesFromDragItems[0], nodesFromDragItems];
position: dragItems.get(nodeId)?.position || node.position,
},
nodesFromDragItems,
];
}