Merge pull request #3648 from xyflow/refactor/computed-attributes
Nodes: add computed attr for width/height and absolute position
This commit is contained in:
@@ -27,7 +27,7 @@ const initialNodes: Node[] = [
|
||||
type: 'input',
|
||||
data: { label: 'Node 1' },
|
||||
position: { x: 250, y: 5 },
|
||||
size: nodeSize,
|
||||
...nodeSize,
|
||||
handles: [
|
||||
{
|
||||
type: 'source',
|
||||
@@ -41,23 +41,19 @@ const initialNodes: Node[] = [
|
||||
id: '2',
|
||||
data: { label: 'Node 2' },
|
||||
position: { x: 100, y: 100 },
|
||||
size: nodeSize,
|
||||
...nodeSize,
|
||||
handles: [
|
||||
{
|
||||
type: 'source',
|
||||
position: Position.Bottom,
|
||||
x: nodeSize.width * 0.5,
|
||||
y: nodeSize.height,
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
{
|
||||
type: 'target',
|
||||
position: Position.Top,
|
||||
x: nodeSize.width * 0.5,
|
||||
y: 0,
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -65,23 +61,19 @@ const initialNodes: Node[] = [
|
||||
id: '3',
|
||||
data: { label: 'Node 3' },
|
||||
position: { x: 400, y: 100 },
|
||||
size: nodeSize,
|
||||
...nodeSize,
|
||||
handles: [
|
||||
{
|
||||
type: 'source',
|
||||
position: Position.Bottom,
|
||||
x: nodeSize.width * 0.5,
|
||||
y: nodeSize.height,
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
{
|
||||
type: 'target',
|
||||
position: Position.Top,
|
||||
x: nodeSize.width * 0.5,
|
||||
y: 0,
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -11,10 +11,8 @@
|
||||
data: { label: 'Node 0' },
|
||||
sourcePosition: Position.Right,
|
||||
targetPosition: Position.Left,
|
||||
size: {
|
||||
width: 100,
|
||||
height: 40,
|
||||
},
|
||||
width: 100,
|
||||
height: 40,
|
||||
handles: [
|
||||
{ type: 'source', x: 100, y: 20, position: Position.Right },
|
||||
{ type: 'target', x: 0, y: 20, position: Position.Left },
|
||||
@@ -26,10 +24,8 @@
|
||||
data: { label: 'A' },
|
||||
sourcePosition: Position.Right,
|
||||
targetPosition: Position.Left,
|
||||
size: {
|
||||
width: 100,
|
||||
height: 40,
|
||||
},
|
||||
width: 100,
|
||||
height: 40,
|
||||
handles: [
|
||||
{ type: 'source', x: 100, y: 20, position: Position.Right },
|
||||
{ type: 'target', x: 0, y: 20, position: Position.Left },
|
||||
@@ -41,10 +37,8 @@
|
||||
data: { label: 'B' },
|
||||
sourcePosition: Position.Right,
|
||||
targetPosition: Position.Left,
|
||||
size: {
|
||||
width: 100,
|
||||
height: 40,
|
||||
},
|
||||
width: 100,
|
||||
height: 40,
|
||||
handles: [
|
||||
{ type: 'source', x: 100, y: 20, position: Position.Right },
|
||||
{ type: 'target', x: 0, y: 20, position: Position.Left },
|
||||
@@ -56,10 +50,8 @@
|
||||
data: { label: 'C' },
|
||||
sourcePosition: Position.Right,
|
||||
targetPosition: Position.Left,
|
||||
size: {
|
||||
width: 100,
|
||||
height: 40,
|
||||
},
|
||||
width: 100,
|
||||
height: 40,
|
||||
handles: [
|
||||
{ type: 'source', x: 100, y: 20, position: Position.Right },
|
||||
{ type: 'target', x: 0, y: 20, position: Position.Left },
|
||||
|
||||
@@ -4,12 +4,9 @@ import { Node, Position, MarkerType, XYPosition } from '@xyflow/react';
|
||||
// of the line between the center of the intersectionNode and the target node
|
||||
function getNodeIntersection(intersectionNode: Node, targetNode: Node) {
|
||||
// https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a
|
||||
const {
|
||||
width: intersectionNodeWidth,
|
||||
height: intersectionNodeHeight,
|
||||
positionAbsolute: intersectionNodePosition,
|
||||
} = intersectionNode;
|
||||
const targetPosition = targetNode.positionAbsolute!;
|
||||
const { width: intersectionNodeWidth, height: intersectionNodeHeight } = intersectionNode;
|
||||
const intersectionNodePosition = intersectionNode.computed?.positionAbsolute!;
|
||||
const targetPosition = targetNode.computed?.positionAbsolute!;
|
||||
|
||||
const w = intersectionNodeWidth! / 2;
|
||||
const h = intersectionNodeHeight! / 2;
|
||||
@@ -32,7 +29,7 @@ function getNodeIntersection(intersectionNode: Node, targetNode: Node) {
|
||||
|
||||
// returns the position (top,right,bottom or right) passed node compared to the intersection point
|
||||
function getEdgePosition(node: Node, intersectionPoint: XYPosition) {
|
||||
const n = { ...node.positionAbsolute, ...node };
|
||||
const n = { ...node.computed?.positionAbsolute, ...node };
|
||||
const nx = Math.round(n.x!);
|
||||
const ny = Math.round(n.y!);
|
||||
const px = Math.round(intersectionPoint.x);
|
||||
@@ -41,13 +38,13 @@ function getEdgePosition(node: Node, intersectionPoint: XYPosition) {
|
||||
if (px <= nx + 1) {
|
||||
return Position.Left;
|
||||
}
|
||||
if (px >= nx + n.width! - 1) {
|
||||
if (px >= nx + n.computed?.width! - 1) {
|
||||
return Position.Right;
|
||||
}
|
||||
if (py <= ny + 1) {
|
||||
return Position.Top;
|
||||
}
|
||||
if (py >= n.y! + n.height! - 1) {
|
||||
if (py >= n.y! + n.computed?.height! - 1) {
|
||||
return Position.Bottom;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,13 +42,13 @@ function getEdgePosition(node: Node, intersectionPoint: XYPosition) {
|
||||
if (px <= nx + 1) {
|
||||
return Position.Left;
|
||||
}
|
||||
if (px >= nx + (n.width ?? 0) - 1) {
|
||||
if (px >= nx + (n.computed?.width ?? 0) - 1) {
|
||||
return Position.Right;
|
||||
}
|
||||
if (py <= ny + 1) {
|
||||
return Position.Top;
|
||||
}
|
||||
if (py >= n.y + (n.height ?? 0) - 1) {
|
||||
if (py >= n.y + (n.computed?.height ?? 0) - 1) {
|
||||
return Position.Bottom;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,10 @@ import type { MiniMapNodes, GetMiniMapNodeAttribute } from './types';
|
||||
declare const window: any;
|
||||
|
||||
const selector = (s: ReactFlowState) => s.nodeOrigin;
|
||||
const selectorNodes = (s: ReactFlowState) => s.nodes.filter((node) => !node.hidden && node.width && node.height);
|
||||
const selectorNodes = (s: ReactFlowState) =>
|
||||
s.nodes.filter(
|
||||
(node) => !node.hidden && (node.computed?.width || node.width) && (node.computed?.height || node.height)
|
||||
);
|
||||
const getAttrFunction = (func: any): GetMiniMapNodeAttribute => (func instanceof Function ? func : () => func);
|
||||
|
||||
function MiniMapNodes({
|
||||
@@ -44,8 +47,8 @@ function MiniMapNodes({
|
||||
key={node.id}
|
||||
x={x}
|
||||
y={y}
|
||||
width={node.width!}
|
||||
height={node.height!}
|
||||
width={node.computed?.width ?? node.width ?? 0}
|
||||
height={node.computed?.height ?? node.height ?? 0}
|
||||
style={node.style}
|
||||
selected={!!node.selected}
|
||||
className={nodeClassNameFunc(node)}
|
||||
|
||||
@@ -70,8 +70,8 @@ function ResizeControl({
|
||||
const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
|
||||
|
||||
prevValues.current = {
|
||||
width: node?.width ?? 0,
|
||||
height: node?.height ?? 0,
|
||||
width: node?.computed?.width ?? 0,
|
||||
height: node?.computed?.height ?? 0,
|
||||
x: node?.position.x ?? 0,
|
||||
y: node?.position.y ?? 0,
|
||||
};
|
||||
|
||||
@@ -10,8 +10,8 @@ import NodeToolbarPortal from './NodeToolbarPortal';
|
||||
import { NodeToolbarProps } from './types';
|
||||
|
||||
const nodeEqualityFn = (a: Node | undefined, b: Node | undefined) =>
|
||||
a?.positionAbsolute?.x === b?.positionAbsolute?.x &&
|
||||
a?.positionAbsolute?.y === b?.positionAbsolute?.y &&
|
||||
a?.computed?.positionAbsolute?.x === b?.computed?.positionAbsolute?.x &&
|
||||
a?.computed?.positionAbsolute?.y === b?.computed?.positionAbsolute?.y &&
|
||||
a?.width === b?.width &&
|
||||
a?.height === b?.height &&
|
||||
a?.selected === b?.selected &&
|
||||
|
||||
@@ -65,10 +65,10 @@ const ConnectionLine = ({
|
||||
}
|
||||
|
||||
const fromHandle = handleId ? handleBounds.find((d) => d.id === handleId) : handleBounds[0];
|
||||
const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.width ?? 0) / 2;
|
||||
const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.height ?? 0;
|
||||
const fromX = (fromNode.positionAbsolute?.x ?? 0) + fromHandleX;
|
||||
const fromY = (fromNode.positionAbsolute?.y ?? 0) + fromHandleY;
|
||||
const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.computed?.width ?? 0) / 2;
|
||||
const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.computed?.height ?? 0;
|
||||
const fromX = (fromNode.computed?.positionAbsolute?.x ?? 0) + fromHandleX;
|
||||
const fromY = (fromNode.computed?.positionAbsolute?.y ?? 0) + fromHandleY;
|
||||
const fromPosition = fromHandle?.position;
|
||||
const toPosition = fromPosition ? oppositePosition[fromPosition] : null;
|
||||
|
||||
|
||||
@@ -52,8 +52,8 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||
disableKeyboardA11y,
|
||||
ariaLabel,
|
||||
rfId,
|
||||
sizeWidth,
|
||||
sizeHeight,
|
||||
width,
|
||||
height,
|
||||
}: WrapNodeProps) => {
|
||||
const store = useStoreApi();
|
||||
const nodeRef = useRef<HTMLDivElement>(null);
|
||||
@@ -186,8 +186,8 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
||||
transform: `translate(${xPosOrigin}px,${yPosOrigin}px)`,
|
||||
pointerEvents: hasPointerEvents ? 'all' : 'none',
|
||||
visibility: initialized ? 'visible' : 'hidden',
|
||||
width: sizeWidth,
|
||||
height: sizeHeight,
|
||||
width,
|
||||
height,
|
||||
...style,
|
||||
}}
|
||||
data-id={id}
|
||||
|
||||
@@ -91,19 +91,19 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
||||
const isFocusable = !!(node.focusable || (nodesFocusable && typeof node.focusable === 'undefined'));
|
||||
|
||||
const clampedPosition = props.nodeExtent
|
||||
? clampPosition(node.positionAbsolute, props.nodeExtent)
|
||||
: node.positionAbsolute;
|
||||
? clampPosition(node.computed?.positionAbsolute, props.nodeExtent)
|
||||
: node.computed?.positionAbsolute;
|
||||
|
||||
const posX = clampedPosition?.x ?? 0;
|
||||
const posY = clampedPosition?.y ?? 0;
|
||||
const posOrigin = getPositionWithOrigin({
|
||||
x: posX,
|
||||
y: posY,
|
||||
width: node.width ?? 0,
|
||||
height: node.height ?? 0,
|
||||
width: node.computed?.width ?? node.width ?? 0,
|
||||
height: node.computed?.height ?? node.height ?? 0,
|
||||
origin: node.origin || props.nodeOrigin,
|
||||
});
|
||||
const initialized = (!!node.width && !!node.height) || (!!node.size?.width && !!node.size?.height);
|
||||
const initialized = (!!node.computed?.width && !!node.computed?.height) || (!!node.width && !!node.height);
|
||||
|
||||
return (
|
||||
<NodeComponent
|
||||
@@ -111,8 +111,8 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
||||
id={node.id}
|
||||
className={node.className}
|
||||
style={node.style}
|
||||
sizeWidth={node.size?.width}
|
||||
sizeHeight={node.size?.height}
|
||||
width={node.width ?? undefined}
|
||||
height={node.height ?? undefined}
|
||||
type={nodeType}
|
||||
data={node.data}
|
||||
sourcePosition={node.sourcePosition || Position.Bottom}
|
||||
|
||||
@@ -206,7 +206,7 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
||||
}
|
||||
|
||||
return (nodes || store.getState().nodes).filter((n) => {
|
||||
if (!isRect && (n.id === node!.id || !n.positionAbsolute)) {
|
||||
if (!isRect && (n.id === node!.id || !n.computed?.positionAbsolute)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,11 @@ function useUpdateNodePositions() {
|
||||
const yDiff = params.y * yVelo * factor;
|
||||
|
||||
const nodeUpdates = selectedNodes.map((node) => {
|
||||
if (node.positionAbsolute) {
|
||||
let nextPosition = { x: node.positionAbsolute.x + xDiff, y: node.positionAbsolute.y + yDiff };
|
||||
if (node.computed?.positionAbsolute) {
|
||||
let nextPosition = {
|
||||
x: node.computed?.positionAbsolute.x + xDiff,
|
||||
y: node.computed?.positionAbsolute.y + yDiff,
|
||||
};
|
||||
|
||||
if (snapToGrid) {
|
||||
nextPosition = snapPosition(nextPosition, snapGrid);
|
||||
@@ -40,7 +43,10 @@ function useUpdateNodePositions() {
|
||||
);
|
||||
|
||||
node.position = position;
|
||||
node.positionAbsolute = positionAbsolute;
|
||||
if (!node.computed) {
|
||||
node.computed = {};
|
||||
}
|
||||
node.computed.positionAbsolute = positionAbsolute;
|
||||
}
|
||||
|
||||
return node;
|
||||
|
||||
@@ -147,7 +147,7 @@ const createRFStore = ({
|
||||
};
|
||||
|
||||
if (positionChanged) {
|
||||
change.positionAbsolute = node.positionAbsolute;
|
||||
change.positionAbsolute = node.computed?.positionAbsolute;
|
||||
change.position = node.position;
|
||||
}
|
||||
|
||||
@@ -276,7 +276,10 @@ const createRFStore = ({
|
||||
|
||||
return {
|
||||
...node,
|
||||
positionAbsolute,
|
||||
computed: {
|
||||
...node.computed,
|
||||
positionAbsolute,
|
||||
},
|
||||
};
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -28,11 +28,7 @@ const getInitialState = ({
|
||||
let transform: Transform = [0, 0, 1];
|
||||
|
||||
if (fitView && width && height) {
|
||||
const nodesWithDimensions = nextNodes.map((node) => ({
|
||||
...node,
|
||||
width: node.size?.width,
|
||||
height: node.size?.height,
|
||||
}));
|
||||
const nodesWithDimensions = nextNodes.filter((node) => node.width && node.height);
|
||||
const bounds = getNodesBounds(nodesWithDimensions, [0, 0]);
|
||||
const { x, y, zoom } = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1);
|
||||
transform = [x, y, zoom];
|
||||
|
||||
@@ -40,6 +40,6 @@ export type WrapNodeProps<NodeData = any> = Pick<
|
||||
noPanClassName: string;
|
||||
rfId: string;
|
||||
disableKeyboardA11y: boolean;
|
||||
sizeWidth?: number;
|
||||
sizeHeight?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
};
|
||||
|
||||
@@ -5,14 +5,17 @@ export function handleParentExpand(res: any[], updateItem: any) {
|
||||
const parent = res.find((e) => e.id === updateItem.parentNode);
|
||||
|
||||
if (parent) {
|
||||
const extendWidth = updateItem.position.x + updateItem.width - parent.width;
|
||||
const extendHeight = updateItem.position.y + updateItem.height - parent.height;
|
||||
if (!parent.computed) {
|
||||
parent.computed = {};
|
||||
}
|
||||
const extendWidth = updateItem.position.x + updateItem.computed.width - parent.computed.width;
|
||||
const extendHeight = updateItem.position.y + updateItem.computed.height - parent.computed.height;
|
||||
|
||||
if (extendWidth > 0 || extendHeight > 0 || updateItem.position.x < 0 || updateItem.position.y < 0) {
|
||||
parent.style = { ...parent.style } || {};
|
||||
|
||||
parent.style.width = parent.style.width ?? parent.width;
|
||||
parent.style.height = parent.style.height ?? parent.height;
|
||||
parent.style.width = parent.style.width ?? parent.computed.width;
|
||||
parent.style.height = parent.style.height ?? parent.computed.height;
|
||||
|
||||
if (extendWidth > 0) {
|
||||
parent.style.width += extendWidth;
|
||||
@@ -36,8 +39,8 @@ export function handleParentExpand(res: any[], updateItem: any) {
|
||||
updateItem.position.y = 0;
|
||||
}
|
||||
|
||||
parent.width = parent.style.width;
|
||||
parent.height = parent.style.height;
|
||||
parent.computed.width = parent.style.width;
|
||||
parent.computed.height = parent.style.height;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,7 +90,10 @@ function applyChanges(changes: any[], elements: any[]): any[] {
|
||||
}
|
||||
|
||||
if (typeof currentChange.positionAbsolute !== 'undefined') {
|
||||
updateItem.positionAbsolute = currentChange.positionAbsolute;
|
||||
if (!updateItem.computed) {
|
||||
updateItem.computed = {};
|
||||
}
|
||||
updateItem.computed.positionAbsolute = currentChange.positionAbsolute;
|
||||
}
|
||||
|
||||
if (typeof currentChange.dragging !== 'undefined') {
|
||||
@@ -101,8 +107,11 @@ function applyChanges(changes: any[], elements: any[]): any[] {
|
||||
}
|
||||
case 'dimensions': {
|
||||
if (typeof currentChange.dimensions !== 'undefined') {
|
||||
updateItem.width = currentChange.dimensions.width;
|
||||
updateItem.height = currentChange.dimensions.height;
|
||||
if (!updateItem.computed) {
|
||||
updateItem.computed = {};
|
||||
}
|
||||
updateItem.computed.width = currentChange.dimensions.width;
|
||||
updateItem.computed.height = currentChange.dimensions.height;
|
||||
}
|
||||
|
||||
if (typeof currentChange.updateStyle !== 'undefined') {
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
export let sourcePosition: NodeWrapperProps['sourcePosition'] = undefined;
|
||||
export let targetPosition: NodeWrapperProps['targetPosition'] = undefined;
|
||||
export let zIndex: NodeWrapperProps['zIndex'];
|
||||
export let width: NodeWrapperProps['width'] = undefined;
|
||||
export let height: NodeWrapperProps['height'] = undefined;
|
||||
export let dragHandle: NodeWrapperProps['dragHandle'] = undefined;
|
||||
export let initialized: NodeWrapperProps['initialized'] = false;
|
||||
let className: string = '';
|
||||
@@ -168,9 +170,9 @@
|
||||
style:z-index={zIndex}
|
||||
style:transform="translate({positionOriginX}px, {positionOriginY}px)"
|
||||
style:visibility={initialized ? 'visible' : 'hidden'}
|
||||
style="{style} {node.size?.width ? `;width=${node.size?.width}px` : ''} {node.size?.height
|
||||
? `;height=${node.size?.height}px;`
|
||||
: ''}"
|
||||
style="{style ?? ''}; {!width ? '' : `width:${width}px;`} {!height
|
||||
? ''
|
||||
: `height:${height}px;`}"
|
||||
on:click={onSelectNodeHandler}
|
||||
on:mouseenter={(event) => dispatch('nodemouseenter', { node, event })}
|
||||
on:mouseleave={(event) => dispatch('nodemouseleave', { node, event })}
|
||||
|
||||
@@ -11,14 +11,15 @@ export type NodeWrapperProps = Pick<
|
||||
| 'selected'
|
||||
| 'selectable'
|
||||
| 'style'
|
||||
| 'type'
|
||||
| 'width'
|
||||
| 'height'
|
||||
| 'type'
|
||||
| 'sourcePosition'
|
||||
| 'targetPosition'
|
||||
| 'dragHandle'
|
||||
| 'hidden'
|
||||
> & {
|
||||
type: string;
|
||||
positionX: number;
|
||||
positionY: number;
|
||||
positionOriginX: number;
|
||||
|
||||
@@ -40,10 +40,10 @@
|
||||
<div class="svelte-flow__nodes">
|
||||
{#each $visibleNodes as node (node.id)}
|
||||
{@const posOrigin = getPositionWithOrigin({
|
||||
x: node.positionAbsolute?.x ?? 0,
|
||||
y: node.positionAbsolute?.y ?? 0,
|
||||
width: (node.size?.width || node.width) ?? 0,
|
||||
height: (node.size?.height || node.height) ?? 0,
|
||||
x: node.computed?.positionAbsolute?.x ?? 0,
|
||||
y: node.computed?.positionAbsolute?.y ?? 0,
|
||||
width: node.computed?.width ?? node.width ?? 0,
|
||||
height: node.computed?.height ?? node.height ?? 0,
|
||||
origin: node.origin
|
||||
})}
|
||||
<NodeWrapper
|
||||
@@ -61,20 +61,23 @@
|
||||
node.connectable ||
|
||||
($nodesConnectable && typeof node.connectable === 'undefined')
|
||||
)}
|
||||
positionX={node.positionAbsolute?.x ?? 0}
|
||||
positionY={node.positionAbsolute?.y ?? 0}
|
||||
positionX={node.computed?.positionAbsolute?.x ?? 0}
|
||||
positionY={node.computed?.positionAbsolute?.y ?? 0}
|
||||
positionOriginX={posOrigin.x ?? 0}
|
||||
positionOriginY={posOrigin.y ?? 0}
|
||||
isParent={!!node[internalsSymbol]?.isParent}
|
||||
style={node.style}
|
||||
class={node.class}
|
||||
type={node.type}
|
||||
type={node.type || 'default'}
|
||||
sourcePosition={node.sourcePosition}
|
||||
targetPosition={node.targetPosition}
|
||||
dragging={node.dragging}
|
||||
zIndex={node[internalsSymbol]?.z ?? 0}
|
||||
dragHandle={node.dragHandle}
|
||||
initialized={(!!node.width && !!node.height) || (!!node.size?.width && !!node.size?.height)}
|
||||
width={node.width}
|
||||
height={node.height}
|
||||
initialized={(!!node.computed?.width && !!node.computed?.height) ||
|
||||
(!!node.width && !!node.height)}
|
||||
{resizeObserver}
|
||||
on:nodeclick
|
||||
on:nodemouseenter
|
||||
|
||||
@@ -141,7 +141,7 @@ export function useSvelteFlow(): {
|
||||
}
|
||||
|
||||
return (nodesToIntersect || get(nodes)).filter((n) => {
|
||||
if (!isRect && (n.id === node.id || !n.positionAbsolute)) {
|
||||
if (!isRect && (n.id === node.id || !n.computed?.positionAbsolute)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { get } from 'svelte/store';
|
||||
import type { UpdateNodeInternals, NodeDimensionUpdate } from '@xyflow/system';
|
||||
import type { UpdateNodeInternals } from '@xyflow/system';
|
||||
|
||||
import { useStore } from '$lib/store';
|
||||
|
||||
|
||||
@@ -114,13 +114,13 @@
|
||||
{#if ariaLabel}<title id={labelledBy}>{ariaLabel}</title>{/if}
|
||||
|
||||
{#each $nodes as node (node.id)}
|
||||
{#if node.width && node.height}
|
||||
{#if (node.computed?.width || node?.width) && (node.computed?.height || node.height)}
|
||||
{@const pos = getNodePositionWithOrigin(node).positionAbsolute}
|
||||
<MinimapNode
|
||||
x={pos.x}
|
||||
y={pos.y}
|
||||
width={node.width}
|
||||
height={node.height}
|
||||
width={node.computed?.width ?? node.width ?? 0}
|
||||
height={node.computed?.height ?? node.height ?? 0}
|
||||
selected={node.selected}
|
||||
color={nodeColorFunc(node)}
|
||||
borderRadius={nodeBorderRadius}
|
||||
|
||||
@@ -51,10 +51,11 @@
|
||||
let nodeRect: Rect | undefined = undefined;
|
||||
|
||||
if (toolbarNodes.length === 1) {
|
||||
const toolbarNode = toolbarNodes[0];
|
||||
nodeRect = {
|
||||
...toolbarNodes[0].position,
|
||||
width: toolbarNodes[0].width ?? 0,
|
||||
height: toolbarNodes[0].height ?? 0
|
||||
...toolbarNode.position,
|
||||
width: toolbarNode.computed?.width ?? toolbarNode.width ?? 0,
|
||||
height: toolbarNode.computed?.height ?? toolbarNode.height ?? 0
|
||||
};
|
||||
} else if (toolbarNodes.length > 1) {
|
||||
nodeRect = getNodesBounds(toolbarNodes, $nodeOrigin);
|
||||
|
||||
@@ -80,10 +80,12 @@ export function getDerivedConnectionProps(
|
||||
: handleBounds[0];
|
||||
const fromHandleX = fromHandle
|
||||
? fromHandle.x + fromHandle.width / 2
|
||||
: (fromNode?.width ?? 0) / 2;
|
||||
const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode?.height ?? 0;
|
||||
const fromX = (fromNode?.positionAbsolute?.x ?? 0) + fromHandleX;
|
||||
const fromY = (fromNode?.positionAbsolute?.y ?? 0) + fromHandleY;
|
||||
: (fromNode?.computed?.width ?? 0) / 2;
|
||||
const fromHandleY = fromHandle
|
||||
? fromHandle.y + fromHandle.height / 2
|
||||
: fromNode?.computed?.height ?? 0;
|
||||
const fromX = (fromNode?.computed?.positionAbsolute?.x ?? 0) + fromHandleX;
|
||||
const fromY = (fromNode?.computed?.positionAbsolute?.y ?? 0) + fromHandleY;
|
||||
const fromPosition = fromHandle?.position;
|
||||
const toPosition = fromPosition ? oppositePosition[fromPosition] : undefined;
|
||||
|
||||
|
||||
@@ -66,22 +66,25 @@ export function createStore({
|
||||
|
||||
const updateNodePositions: UpdateNodePositions = (nodeDragItems, dragging = false) => {
|
||||
store.nodes.update((nds) => {
|
||||
return nds.map((n) => {
|
||||
return nds.map((node) => {
|
||||
const nodeDragItem = (nodeDragItems as Array<NodeBase | NodeDragItem>).find(
|
||||
(ndi) => ndi.id === n.id
|
||||
(ndi) => ndi.id === node.id
|
||||
);
|
||||
|
||||
if (nodeDragItem) {
|
||||
return {
|
||||
...n,
|
||||
[internalsSymbol]: n[internalsSymbol],
|
||||
...node,
|
||||
dragging,
|
||||
positionAbsolute: nodeDragItem.positionAbsolute,
|
||||
position: nodeDragItem.position
|
||||
position: nodeDragItem.position,
|
||||
computed: {
|
||||
...node.computed,
|
||||
positionAbsolute: nodeDragItem.computed?.positionAbsolute
|
||||
},
|
||||
[internalsSymbol]: node[internalsSymbol]
|
||||
};
|
||||
}
|
||||
|
||||
return n;
|
||||
return node;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -76,11 +76,7 @@ export const getInitialStore = ({
|
||||
let viewport: Viewport = { x: 0, y: 0, zoom: 1 };
|
||||
|
||||
if (fitView && width && height) {
|
||||
const nodesWithDimensions = nextNodes.map((node) => ({
|
||||
...node,
|
||||
width: node.size?.width,
|
||||
height: node.size?.height
|
||||
}));
|
||||
const nodesWithDimensions = nextNodes.filter((node) => node.width && node.height);
|
||||
const bounds = getNodesBounds(nodesWithDimensions, [0, 0]);
|
||||
viewport = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1);
|
||||
}
|
||||
|
||||
@@ -25,14 +25,14 @@ export type NodeBase<T = any, U extends string | undefined = string | undefined>
|
||||
zIndex?: number;
|
||||
extent?: 'parent' | CoordinateExtent;
|
||||
expandParent?: boolean;
|
||||
positionAbsolute?: XYPosition;
|
||||
ariaLabel?: string;
|
||||
focusable?: boolean;
|
||||
origin?: NodeOrigin;
|
||||
handles?: NodeHandle[];
|
||||
size?: {
|
||||
computed?: {
|
||||
width?: number;
|
||||
height?: number;
|
||||
positionAbsolute?: XYPosition;
|
||||
};
|
||||
|
||||
// only used internally
|
||||
@@ -78,11 +78,13 @@ export type NodeBounds = XYPosition & {
|
||||
export type NodeDragItem = {
|
||||
id: string;
|
||||
position: XYPosition;
|
||||
positionAbsolute: XYPosition;
|
||||
// distance from the mouse cursor to the node when start dragging
|
||||
distance: XYPosition;
|
||||
width?: number | null;
|
||||
height?: number | null;
|
||||
computed: {
|
||||
width: number | null;
|
||||
height: number | null;
|
||||
positionAbsolute: XYPosition;
|
||||
};
|
||||
extent?: 'parent' | CoordinateExtent;
|
||||
parentNode?: string;
|
||||
dragging?: boolean;
|
||||
|
||||
@@ -73,6 +73,8 @@ export const getHandleBounds = (
|
||||
}
|
||||
|
||||
const handlesArray = Array.from(handles) as HTMLDivElement[];
|
||||
|
||||
// @todo can't we use the node dimensions here?
|
||||
const nodeBounds = nodeElement.getBoundingClientRect();
|
||||
const nodeOffset = {
|
||||
x: nodeBounds.width * nodeOrigin[0],
|
||||
|
||||
@@ -88,20 +88,20 @@ function toHandleBounds(handles?: NodeHandle[]) {
|
||||
|
||||
function getHandleDataByNode(node?: NodeBase): [Rect, NodeHandleBounds | null, boolean] {
|
||||
const handleBounds = node?.[internalsSymbol]?.handleBounds || toHandleBounds(node?.handles) || null;
|
||||
const nodeWidth = node?.width || node?.size?.width;
|
||||
const nodeHeight = node?.height || node?.size?.height;
|
||||
const nodeWidth = node?.computed?.width || node?.width;
|
||||
const nodeHeight = node?.computed?.height || node?.height;
|
||||
|
||||
const isValid =
|
||||
handleBounds &&
|
||||
nodeWidth &&
|
||||
nodeHeight &&
|
||||
typeof node?.positionAbsolute?.x !== 'undefined' &&
|
||||
typeof node?.positionAbsolute?.y !== 'undefined';
|
||||
typeof node?.computed?.positionAbsolute?.x !== 'undefined' &&
|
||||
typeof node?.computed?.positionAbsolute?.y !== 'undefined';
|
||||
|
||||
return [
|
||||
{
|
||||
x: node?.positionAbsolute?.x || 0,
|
||||
y: node?.positionAbsolute?.y || 0,
|
||||
x: node?.computed?.positionAbsolute?.x || 0,
|
||||
y: node?.computed?.positionAbsolute?.y || 0,
|
||||
width: nodeWidth || 0,
|
||||
height: nodeHeight || 0,
|
||||
},
|
||||
|
||||
@@ -64,8 +64,8 @@ export const nodeToRect = (node: NodeBase, nodeOrigin: NodeOrigin = [0, 0]): Rec
|
||||
|
||||
return {
|
||||
...positionAbsolute,
|
||||
width: node.width || 0,
|
||||
height: node.height || 0,
|
||||
width: node.computed?.width ?? node.width ?? 0,
|
||||
height: node.computed?.height ?? node.height ?? 0,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -74,8 +74,8 @@ export const nodeToBox = (node: NodeBase, nodeOrigin: NodeOrigin = [0, 0]): Box
|
||||
|
||||
return {
|
||||
...positionAbsolute,
|
||||
x2: positionAbsolute.x + (node.width || 0),
|
||||
y2: positionAbsolute.y + (node.height || 0),
|
||||
x2: positionAbsolute.x + (node.computed?.width ?? node.width ?? 0),
|
||||
y2: positionAbsolute.y + (node.computed?.height ?? node.height ?? 0),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -86,8 +86,8 @@ export const getNodePositionWithOrigin = (
|
||||
};
|
||||
}
|
||||
|
||||
const offsetX = (node.width ?? 0) * nodeOrigin[0];
|
||||
const offsetY = (node.height ?? 0) * nodeOrigin[1];
|
||||
const offsetX = (node.computed?.width ?? node.width ?? 0) * nodeOrigin[0];
|
||||
const offsetY = (node.computed?.height ?? node.height ?? 0) * nodeOrigin[1];
|
||||
|
||||
const position: XYPosition = {
|
||||
x: node.position.x - offsetX,
|
||||
@@ -96,10 +96,10 @@ export const getNodePositionWithOrigin = (
|
||||
|
||||
return {
|
||||
...position,
|
||||
positionAbsolute: node.positionAbsolute
|
||||
positionAbsolute: node.computed?.positionAbsolute
|
||||
? {
|
||||
x: node.positionAbsolute.x - offsetX,
|
||||
y: node.positionAbsolute.y - offsetY,
|
||||
x: node.computed.positionAbsolute.x - offsetX,
|
||||
y: node.computed.positionAbsolute.y - offsetY,
|
||||
}
|
||||
: position,
|
||||
};
|
||||
@@ -118,8 +118,8 @@ export const getNodesBounds = (nodes: NodeBase[], nodeOrigin: NodeOrigin = [0, 0
|
||||
rectToBox({
|
||||
x,
|
||||
y,
|
||||
width: node.width || 0,
|
||||
height: node.height || 0,
|
||||
width: node.computed?.width ?? node.width ?? 0,
|
||||
height: node.computed?.height ?? node.height ?? 0,
|
||||
})
|
||||
);
|
||||
},
|
||||
@@ -145,17 +145,19 @@ export const getNodesInside = <NodeType extends NodeBase>(
|
||||
};
|
||||
|
||||
const visibleNodes = nodes.reduce<NodeType[]>((res, node) => {
|
||||
const { width, height, selectable = true, hidden = false } = node;
|
||||
const { computed, selectable = true, hidden = false } = node;
|
||||
const width = computed?.width ?? node.width ?? null;
|
||||
const height = computed?.height ?? node.height ?? null;
|
||||
|
||||
if ((excludeNonSelectableNodes && !selectable) || hidden) {
|
||||
return res;
|
||||
}
|
||||
|
||||
const overlappingArea = getOverlappingArea(paneRect, nodeToRect(node, nodeOrigin));
|
||||
const notInitialized = width === undefined || height === undefined || width === null || height === null;
|
||||
const notInitialized = width === null || height === null;
|
||||
|
||||
const partiallyVisible = partially && overlappingArea > 0;
|
||||
const area = (width || 0) * (height || 0);
|
||||
const area = (width ?? 0) * (height ?? 0);
|
||||
const isVisible = notInitialized || partiallyVisible || overlappingArea >= area;
|
||||
|
||||
if (isVisible || node.dragging) {
|
||||
@@ -185,7 +187,7 @@ export function fitView<Params extends FitViewParamsBase<NodeBase>, Options exte
|
||||
options?: Options
|
||||
) {
|
||||
const filteredNodes = nodes.filter((n) => {
|
||||
const isVisible = n.width && n.height && (options?.includeHiddenNodes || !n.hidden);
|
||||
const isVisible = n.computed?.width && n.computed?.height && (options?.includeHiddenNodes || !n.hidden);
|
||||
|
||||
if (options?.nodes?.length) {
|
||||
return isVisible && options?.nodes.some((optionNode) => optionNode.id === n.id);
|
||||
@@ -218,7 +220,7 @@ function clampNodeExtent(node: NodeDragItem | NodeBase, extent?: CoordinateExten
|
||||
if (!extent || extent === 'parent') {
|
||||
return extent;
|
||||
}
|
||||
return [extent[0], [extent[1][0] - (node.width || 0), extent[1][1] - (node.height || 0)]];
|
||||
return [extent[0], [extent[1][0] - (node.computed?.width ?? 0), extent[1][1] - (node.computed?.height ?? 0)]];
|
||||
}
|
||||
|
||||
export function calcNextPosition<NodeType extends NodeBase>(
|
||||
@@ -242,16 +244,18 @@ export function calcNextPosition<NodeType extends NodeBase>(
|
||||
}
|
||||
|
||||
if (node.extent === 'parent' && !node.expandParent) {
|
||||
if (node.parentNode && node.width && node.height) {
|
||||
const nodeWidth = node.computed?.width;
|
||||
const nodeHeight = node.computed?.height;
|
||||
if (node.parentNode && nodeWidth && nodeHeight) {
|
||||
const currNodeOrigin = node.origin || nodeOrigin;
|
||||
|
||||
currentExtent =
|
||||
parentNode && isNumeric(parentNode.width) && isNumeric(parentNode.height)
|
||||
parentNode && isNumeric(parentNode.computed?.width) && isNumeric(parentNode.computed?.height)
|
||||
? [
|
||||
[parentPos.x + node.width * currNodeOrigin[0], parentPos.y + node.height * currNodeOrigin[1]],
|
||||
[parentPos.x + nodeWidth * currNodeOrigin[0], parentPos.y + nodeHeight * currNodeOrigin[1]],
|
||||
[
|
||||
parentPos.x + parentNode.width - node.width + node.width * currNodeOrigin[0],
|
||||
parentPos.y + parentNode.height - node.height + node.height * currNodeOrigin[1],
|
||||
parentPos.x + parentNode.computed.width - nodeWidth + nodeWidth * currNodeOrigin[0],
|
||||
parentPos.y + parentNode.computed.height - nodeHeight + nodeHeight * currNodeOrigin[1],
|
||||
],
|
||||
]
|
||||
: currentExtent;
|
||||
|
||||
@@ -40,7 +40,7 @@ export function updateAbsolutePositions<NodeType extends NodeBase>(
|
||||
parentNode?.origin || nodeOrigin
|
||||
);
|
||||
|
||||
node.positionAbsolute = {
|
||||
node.computed!.positionAbsolute = {
|
||||
x,
|
||||
y,
|
||||
};
|
||||
@@ -79,9 +79,11 @@ export function updateNodes<NodeType extends NodeBase>(
|
||||
const node: NodeType = {
|
||||
...options.defaults,
|
||||
...n,
|
||||
positionAbsolute: n.position,
|
||||
width: n.width || currentStoreNode?.width,
|
||||
height: n.height || currentStoreNode?.height,
|
||||
computed: {
|
||||
positionAbsolute: n.position,
|
||||
width: n.computed?.width || currentStoreNode?.computed?.width,
|
||||
height: n.computed?.height || currentStoreNode?.computed?.height,
|
||||
},
|
||||
};
|
||||
const z = (isNumeric(n.zIndex) ? n.zIndex : 0) + (n.selected ? selectedNodeZ : 0);
|
||||
const currInternals = n?.[internalsSymbol] || currentStoreNode?.[internalsSymbol];
|
||||
@@ -160,7 +162,7 @@ export function updateNodeDimensions(
|
||||
const doUpdate = !!(
|
||||
dimensions.width &&
|
||||
dimensions.height &&
|
||||
(node.width !== dimensions.width || node.height !== dimensions.height || update.forceUpdate)
|
||||
(node.computed?.width !== dimensions.width || node.computed?.height !== dimensions.height || update.forceUpdate)
|
||||
);
|
||||
|
||||
if (doUpdate) {
|
||||
@@ -168,7 +170,10 @@ export function updateNodeDimensions(
|
||||
|
||||
const newNode = {
|
||||
...node,
|
||||
...dimensions,
|
||||
computed: {
|
||||
...node.computed,
|
||||
...dimensions,
|
||||
},
|
||||
[internalsSymbol]: {
|
||||
...node[internalsSymbol],
|
||||
handleBounds: {
|
||||
|
||||
@@ -140,11 +140,13 @@ export function XYDrag({
|
||||
];
|
||||
|
||||
if (dragItems.length > 1 && nodeExtent && !n.extent) {
|
||||
adjustedNodeExtent[0][0] = n.positionAbsolute.x - nodesBox.x + nodeExtent[0][0];
|
||||
adjustedNodeExtent[1][0] = n.positionAbsolute.x + (n.width ?? 0) - nodesBox.x2 + nodeExtent[1][0];
|
||||
adjustedNodeExtent[0][0] = n.computed.positionAbsolute.x - nodesBox.x + nodeExtent[0][0];
|
||||
adjustedNodeExtent[1][0] =
|
||||
n.computed.positionAbsolute.x + (n.computed?.width ?? 0) - nodesBox.x2 + nodeExtent[1][0];
|
||||
|
||||
adjustedNodeExtent[0][1] = n.positionAbsolute.y - nodesBox.y + nodeExtent[0][1];
|
||||
adjustedNodeExtent[1][1] = n.positionAbsolute.y + (n.height ?? 0) - nodesBox.y2 + nodeExtent[1][1];
|
||||
adjustedNodeExtent[0][1] = n.computed.positionAbsolute.y - nodesBox.y + nodeExtent[0][1];
|
||||
adjustedNodeExtent[1][1] =
|
||||
n.computed.positionAbsolute.y + (n.computed?.height ?? 0) - nodesBox.y2 + nodeExtent[1][1];
|
||||
}
|
||||
|
||||
const updatedPos = calcNextPosition(n, nextPosition, nodes, adjustedNodeExtent, nodeOrigin, onError);
|
||||
@@ -153,7 +155,7 @@ export function XYDrag({
|
||||
hasChange = hasChange || n.position.x !== updatedPos.position.x || n.position.y !== updatedPos.position.y;
|
||||
|
||||
n.position = updatedPos.position;
|
||||
n.positionAbsolute = updatedPos.positionAbsolute;
|
||||
n.computed.positionAbsolute = updatedPos.positionAbsolute;
|
||||
|
||||
return n;
|
||||
});
|
||||
|
||||
@@ -51,10 +51,9 @@ export function getDragItems<NodeType extends NodeBase>(
|
||||
.map((n) => ({
|
||||
id: n.id,
|
||||
position: n.position || { x: 0, y: 0 },
|
||||
positionAbsolute: n.positionAbsolute || { x: 0, y: 0 },
|
||||
distance: {
|
||||
x: mousePos.x - (n.positionAbsolute?.x ?? 0),
|
||||
y: mousePos.y - (n.positionAbsolute?.y ?? 0),
|
||||
x: mousePos.x - (n.computed?.positionAbsolute?.x ?? 0),
|
||||
y: mousePos.y - (n.computed?.positionAbsolute?.y ?? 0),
|
||||
},
|
||||
delta: {
|
||||
x: 0,
|
||||
@@ -62,10 +61,13 @@ export function getDragItems<NodeType extends NodeBase>(
|
||||
},
|
||||
extent: n.extent,
|
||||
parentNode: n.parentNode,
|
||||
width: n.width,
|
||||
height: n.height,
|
||||
origin: n.origin,
|
||||
expandParent: n.expandParent,
|
||||
computed: {
|
||||
positionAbsolute: n.computed?.positionAbsolute || { x: 0, y: 0 },
|
||||
width: n.computed?.width || 0,
|
||||
height: n.computed?.height || 0,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -81,15 +83,18 @@ export function getEventHandlerParams<NodeType extends NodeBase>({
|
||||
dragItems: NodeDragItem[];
|
||||
nodeLookup: Map<string, NodeType>;
|
||||
}): [NodeType, NodeType[]] {
|
||||
const extentedDragItems: NodeType[] = dragItems.map((n) => {
|
||||
const nodesFromDragItems: NodeType[] = dragItems.map((n) => {
|
||||
const node = nodeLookup.get(n.id)!;
|
||||
|
||||
return {
|
||||
...node,
|
||||
position: n.position,
|
||||
positionAbsolute: n.positionAbsolute,
|
||||
computed: {
|
||||
...n.computed,
|
||||
positionAbsolute: n.computed.positionAbsolute,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
return [nodeId ? extentedDragItems.find((n) => n.id === nodeId)! : extentedDragItems[0], extentedDragItems];
|
||||
return [nodeId ? nodesFromDragItems.find((n) => n.id === nodeId)! : nodesFromDragItems[0], nodesFromDragItems];
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@ export function getHandles(
|
||||
id: h.id || null,
|
||||
type,
|
||||
nodeId: node.id,
|
||||
x: (node.positionAbsolute?.x ?? 0) + h.x + h.width / 2,
|
||||
y: (node.positionAbsolute?.y ?? 0) + h.y + h.height / 2,
|
||||
x: (node.computed?.positionAbsolute?.x ?? 0) + h.x + h.width / 2,
|
||||
y: (node.computed?.positionAbsolute?.y ?? 0) + h.y + h.height / 2,
|
||||
});
|
||||
}
|
||||
return res;
|
||||
|
||||
Reference in New Issue
Block a user