clamp position on adoptUserNode

This commit is contained in:
peterkogo
2024-08-22 11:00:16 +02:00
parent 04774e5011
commit ea8b9e0050
5 changed files with 32 additions and 15 deletions
@@ -2,7 +2,6 @@ import { type MouseEvent, type KeyboardEvent } from 'react';
import cc from 'classcat'; import cc from 'classcat';
import { shallow } from 'zustand/shallow'; import { shallow } from 'zustand/shallow';
import { import {
clampPosition,
elementSelectionKeys, elementSelectionKeys,
errorMessages, errorMessages,
getNodeDimensions, getNodeDimensions,
@@ -88,10 +87,6 @@ export function NodeWrapper<NodeType extends Node>({
const nodeDimensions = getNodeDimensions(node); const nodeDimensions = getNodeDimensions(node);
const inlineDimensions = getNodeInlineStyleDimensions(node); const inlineDimensions = getNodeInlineStyleDimensions(node);
// TODO: clamping should happen earlier
const clampedPosition = nodeExtent
? clampPosition(internals.positionAbsolute, nodeExtent, nodeDimensions)
: internals.positionAbsolute;
const hasPointerEvents = isSelectable || isDraggable || onClick || onMouseEnter || onMouseMove || onMouseLeave; const hasPointerEvents = isSelectable || isDraggable || onClick || onMouseEnter || onMouseMove || onMouseLeave;
@@ -147,7 +142,7 @@ export function NodeWrapper<NodeType extends Node>({
store.setState({ store.setState({
ariaLiveMessage: `Moved selected node ${event.key ariaLiveMessage: `Moved selected node ${event.key
.replace('Arrow', '') .replace('Arrow', '')
.toLowerCase()}. New position, x: ${~~clampedPosition.x}, y: ${~~clampedPosition.y}`, .toLowerCase()}. New position, x: ${~~internals.positionAbsolute.x}, y: ${~~internals.positionAbsolute.y}`,
}); });
moveSelectedNodes({ moveSelectedNodes({
@@ -178,7 +173,7 @@ export function NodeWrapper<NodeType extends Node>({
ref={nodeRef} ref={nodeRef}
style={{ style={{
zIndex: internals.z, zIndex: internals.z,
transform: `translate(${clampedPosition.x}px,${clampedPosition.y}px)`, transform: `translate(${internals.positionAbsolute.x}px,${internals.positionAbsolute.y}px)`,
pointerEvents: hasPointerEvents ? 'all' : 'none', pointerEvents: hasPointerEvents ? 'all' : 'none',
visibility: hasDimensions ? 'visible' : 'hidden', visibility: hasDimensions ? 'visible' : 'hidden',
...node.style, ...node.style,
@@ -203,8 +198,8 @@ export function NodeWrapper<NodeType extends Node>({
id={id} id={id}
data={node.data} data={node.data}
type={nodeType} type={nodeType}
positionAbsoluteX={clampedPosition.x} positionAbsoluteX={internals.positionAbsolute.x}
positionAbsoluteY={clampedPosition.y} positionAbsoluteY={internals.positionAbsolute.y}
selected={node.selected} selected={node.selected}
selectable={isSelectable} selectable={isSelectable}
draggable={isDraggable} draggable={isDraggable}
+6 -1
View File
@@ -54,7 +54,12 @@ const createStore = ({
// //
// When this happens, we take the note objects passed by the user and extend them with fields // When this happens, we take the note objects passed by the user and extend them with fields
// relevant for internal React Flow operations. // relevant for internal React Flow operations.
adoptUserNodes(nodes, nodeLookup, parentLookup, { nodeOrigin, elevateNodesOnSelect, checkEquality: true }); adoptUserNodes(nodes, nodeLookup, parentLookup, {
nodeOrigin,
nodeExtent,
elevateNodesOnSelect,
checkEquality: true,
});
set({ nodes }); set({ nodes });
}, },
@@ -114,7 +114,7 @@ export const getInitialStore = ({
return { return {
flowId: writable<string | null>(null), flowId: writable<string | null>(null),
nodes: createNodesStore(nodes, nodeLookup, parentLookup, storeNodeOrigin), nodes: createNodesStore(nodes, nodeLookup, parentLookup, storeNodeOrigin, storeNodeExtent),
nodeLookup: readable<NodeLookup<InternalNode>>(nodeLookup), nodeLookup: readable<NodeLookup<InternalNode>>(nodeLookup),
parentLookup: readable<ParentLookup<InternalNode>>(parentLookup), parentLookup: readable<ParentLookup<InternalNode>>(parentLookup),
edgeLookup: readable<EdgeLookup<Edge>>(edgeLookup), edgeLookup: readable<EdgeLookup<Edge>>(edgeLookup),
+6 -2
View File
@@ -15,7 +15,9 @@ import {
type EdgeLookup, type EdgeLookup,
type NodeLookup, type NodeLookup,
type ParentLookup, type ParentLookup,
type NodeOrigin type NodeOrigin,
infiniteExtent,
type CoordinateExtent
} from '@xyflow/system'; } from '@xyflow/system';
import type { DefaultEdgeOptions, DefaultNodeOptions, Edge, InternalNode, Node } from '$lib/types'; import type { DefaultEdgeOptions, DefaultNodeOptions, Edge, InternalNode, Node } from '$lib/types';
@@ -131,7 +133,8 @@ export const createNodesStore = (
nodes: Node[], nodes: Node[],
nodeLookup: NodeLookup<InternalNode>, nodeLookup: NodeLookup<InternalNode>,
parentLookup: ParentLookup<InternalNode>, parentLookup: ParentLookup<InternalNode>,
nodeOrigin: NodeOrigin = [0, 0] nodeOrigin: NodeOrigin = [0, 0],
nodeExtent: CoordinateExtent = infiniteExtent
): { ): {
subscribe: (this: void, run: Subscriber<Node[]>) => Unsubscriber; subscribe: (this: void, run: Subscriber<Node[]>) => Unsubscriber;
update: (this: void, updater: Updater<Node[]>) => void; update: (this: void, updater: Updater<Node[]>) => void;
@@ -148,6 +151,7 @@ export const createNodesStore = (
adoptUserNodes(nds, nodeLookup, parentLookup, { adoptUserNodes(nds, nodeLookup, parentLookup, {
elevateNodesOnSelect, elevateNodesOnSelect,
nodeOrigin, nodeOrigin,
nodeExtent,
defaults, defaults,
checkEquality: false checkEquality: false
}); });
+15 -2
View File
@@ -18,7 +18,14 @@ import {
ParentLookup, ParentLookup,
} from '../types'; } from '../types';
import { getDimensions, getHandleBounds } from './dom'; import { getDimensions, getHandleBounds } from './dom';
import { getBoundsOfRects, getNodeDimensions, isNumeric, nodeToRect } from './general'; import {
clampPosition,
getBoundsOfRects,
getNodeDimensions,
isCoordinateExtent,
isNumeric,
nodeToRect,
} from './general';
import { getNodePositionWithOrigin } from './graph'; import { getNodePositionWithOrigin } from './graph';
import { ParentExpandChild } from './types'; import { ParentExpandChild } from './types';
@@ -28,6 +35,7 @@ const defaultOptions = {
elevateNodesOnSelect: true, elevateNodesOnSelect: true,
defaults: {}, defaults: {},
}; };
const adoptUserNodesDefaultOptions = { const adoptUserNodesDefaultOptions = {
...defaultOptions, ...defaultOptions,
checkEquality: true, checkEquality: true,
@@ -63,6 +71,7 @@ export function adoptUserNodes<NodeType extends NodeBase>(
options?: UpdateNodesOptions<NodeType> options?: UpdateNodesOptions<NodeType>
) { ) {
const _options = { ...adoptUserNodesDefaultOptions, ...options }; const _options = { ...adoptUserNodesDefaultOptions, ...options };
const tmpLookup = new Map(nodeLookup); const tmpLookup = new Map(nodeLookup);
nodeLookup.clear(); nodeLookup.clear();
parentLookup.clear(); parentLookup.clear();
@@ -74,6 +83,10 @@ export function adoptUserNodes<NodeType extends NodeBase>(
if (_options.checkEquality && userNode === internalNode?.internals.userNode) { if (_options.checkEquality && userNode === internalNode?.internals.userNode) {
nodeLookup.set(userNode.id, internalNode); nodeLookup.set(userNode.id, internalNode);
} else { } else {
const positionOrigin = getNodePositionWithOrigin(userNode, _options.nodeOrigin);
const extent = isCoordinateExtent(userNode.extent) ? userNode.extent : _options.nodeExtent;
const positionAbsolute = clampPosition(positionOrigin, extent, getNodeDimensions(userNode));
internalNode = { internalNode = {
..._options.defaults, ..._options.defaults,
...userNode, ...userNode,
@@ -82,7 +95,7 @@ export function adoptUserNodes<NodeType extends NodeBase>(
height: userNode.measured?.height, height: userNode.measured?.height,
}, },
internals: { internals: {
positionAbsolute: getNodePositionWithOrigin(userNode, _options.nodeOrigin), positionAbsolute,
// if user re-initializes the node or removes `measured` for whatever reason, we reset the handleBounds so that the node gets re-measured // if user re-initializes the node or removes `measured` for whatever reason, we reset the handleBounds so that the node gets re-measured
handleBounds: !userNode.measured ? undefined : internalNode?.internals.handleBounds, handleBounds: !userNode.measured ? undefined : internalNode?.internals.handleBounds,
z: calculateZ(userNode, selectedNodeZ), z: calculateZ(userNode, selectedNodeZ),