clamp position on adoptUserNode
This commit is contained in:
@@ -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}
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
Reference in New Issue
Block a user