Merge branch 'xyflow' into svelte-flow-get-intersecting-nodes
This commit is contained in:
@@ -49,9 +49,9 @@ const DnDFlow = () => {
|
|||||||
|
|
||||||
if (reactFlowInstance) {
|
if (reactFlowInstance) {
|
||||||
const type = event.dataTransfer.getData('application/reactflow');
|
const type = event.dataTransfer.getData('application/reactflow');
|
||||||
const position = reactFlowInstance.project({
|
const position = reactFlowInstance.screenToFlowCoordinate({
|
||||||
x: event.clientX,
|
x: event.clientX,
|
||||||
y: event.clientY - 40,
|
y: event.clientY,
|
||||||
});
|
});
|
||||||
const newNode: Node = {
|
const newNode: Node = {
|
||||||
id: getId(),
|
id: getId(),
|
||||||
|
|||||||
@@ -16,7 +16,8 @@
|
|||||||
'usesvelteflow',
|
'usesvelteflow',
|
||||||
'useupdatenodeinternals',
|
'useupdatenodeinternals',
|
||||||
'validation',
|
'validation',
|
||||||
'intersections'
|
'intersections',
|
||||||
|
'add-node-on-drop'
|
||||||
];
|
];
|
||||||
|
|
||||||
const onChange = (event: Event) => {
|
const onChange = (event: Event) => {
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { SvelteFlowProvider } from '@xyflow/svelte';
|
||||||
|
import Flow from './Flow.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- You need the SvelteFlowProvider so you can useSvelteFlow -->
|
||||||
|
<SvelteFlowProvider>
|
||||||
|
<Flow />
|
||||||
|
</SvelteFlowProvider>
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { SvelteFlow, useSvelteFlow } from '@xyflow/svelte';
|
||||||
|
import type { Edge, Node } from '@xyflow/svelte';
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
|
||||||
|
import '@xyflow/svelte/dist/style.css';
|
||||||
|
|
||||||
|
const initialNodes: Node[] = [
|
||||||
|
{
|
||||||
|
id: '0',
|
||||||
|
type: 'input',
|
||||||
|
data: { label: 'Node' },
|
||||||
|
position: { x: 0, y: 50 }
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const nodes = writable<Node[]>(initialNodes);
|
||||||
|
const edges = writable<Edge[]>([]);
|
||||||
|
|
||||||
|
let connectingNodeId: string = '0';
|
||||||
|
let rect: DOMRectReadOnly;
|
||||||
|
let id = 1;
|
||||||
|
const getId = () => `${id++}`;
|
||||||
|
|
||||||
|
const { screenToFlowCoordinate, flowToScreenCoordinate } = useSvelteFlow();
|
||||||
|
|
||||||
|
function handleConnectEnd({ detail: { event } }: { detail: { event: MouseEvent | TouchEvent } }) {
|
||||||
|
// See of connection landed inside the flow pane
|
||||||
|
const targetIsPane = event.target?.classList.contains('svelte-flow__pane');
|
||||||
|
if (targetIsPane) {
|
||||||
|
const id = getId();
|
||||||
|
const position = {
|
||||||
|
x: event.clientX,
|
||||||
|
y: event.clientY
|
||||||
|
};
|
||||||
|
|
||||||
|
const doubleTransformedPosition = flowToScreenCoordinate(screenToFlowCoordinate(position));
|
||||||
|
console.log(
|
||||||
|
'Is transforming in both directions (screen-flow, flow-screen) the same?',
|
||||||
|
position.x === doubleTransformedPosition.x && position.y === doubleTransformedPosition.y
|
||||||
|
);
|
||||||
|
|
||||||
|
const newNode: Node = {
|
||||||
|
id,
|
||||||
|
data: { label: `Node ${id}` },
|
||||||
|
// project the screen coordinates to pane coordinates
|
||||||
|
position: screenToFlowCoordinate(position),
|
||||||
|
// set the origin of the new node so it is centered
|
||||||
|
origin: [0.5, 0.0]
|
||||||
|
};
|
||||||
|
|
||||||
|
$nodes.push(newNode);
|
||||||
|
$edges.push({
|
||||||
|
source: connectingNodeId,
|
||||||
|
target: id,
|
||||||
|
id: `${connectingNodeId}--${id}`
|
||||||
|
});
|
||||||
|
|
||||||
|
$nodes = $nodes;
|
||||||
|
$edges = $edges;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:window />
|
||||||
|
|
||||||
|
<div class="wrapper" bind:contentRect={rect}>
|
||||||
|
<SvelteFlow
|
||||||
|
{nodes}
|
||||||
|
{edges}
|
||||||
|
fitView
|
||||||
|
fitViewOptions={{ padding: 2 }}
|
||||||
|
on:connectstart={({ detail: { nodeId } }) => {
|
||||||
|
// Memorize the nodeId you start draggin a connection line from a node
|
||||||
|
connectingNodeId = nodeId;
|
||||||
|
}}
|
||||||
|
on:connectend={handleConnectEnd}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:global(.svelte-flow .svelte-flow__handle) {
|
||||||
|
width: 30px;
|
||||||
|
height: 14px;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: #784be8;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.svelte-flow .svelte-flow__handle-top) {
|
||||||
|
top: -10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.svelte-flow .svelte-flow__handle-bottom) {
|
||||||
|
bottom: -10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.svelte-flow .svelte-flow__node) {
|
||||||
|
height: 40px;
|
||||||
|
width: 150px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
border-width: 2px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.svelte-flow .svelte-flow__edge path, .svelte-flow__connectionline path) {
|
||||||
|
stroke-width: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100vw;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,7 +2,6 @@ import type { ReactNode } from 'react';
|
|||||||
|
|
||||||
import { useStore } from '../../hooks/useStore';
|
import { useStore } from '../../hooks/useStore';
|
||||||
import type { ReactFlowState } from '../../types';
|
import type { ReactFlowState } from '../../types';
|
||||||
import { Viewport } from '@xyflow/system';
|
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => `translate(${s.transform[0]}px,${s.transform[1]}px) scale(${s.transform[2]})`;
|
const selector = (s: ReactFlowState) => `translate(${s.transform[0]}px,${s.transform[1]}px) scale(${s.transform[2]})`;
|
||||||
|
|
||||||
@@ -10,7 +9,7 @@ type ViewportProps = {
|
|||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
function Viewport({ children }: ViewportProps) {
|
export default function Viewport({ children }: ViewportProps) {
|
||||||
const transform = useStore(selector);
|
const transform = useStore(selector);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -19,5 +18,3 @@ function Viewport({ children }: ViewportProps) {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Viewport;
|
|
||||||
|
|||||||
@@ -164,6 +164,8 @@ export default function useReactFlow<NodeData = any, EdgeData = any>(): ReactFlo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return { deletedNodes: matchingNodes, deletedEdges: matchingEdges };
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const getNodeRect = useCallback(
|
const getNodeRect = useCallback(
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { pointToRendererPoint, getTransformForBounds, fitView, type XYPosition } from '@xyflow/system';
|
import {
|
||||||
|
pointToRendererPoint,
|
||||||
|
getTransformForBounds,
|
||||||
|
fitView,
|
||||||
|
type XYPosition,
|
||||||
|
rendererPointToPoint,
|
||||||
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import { useStoreApi, useStore } from '../hooks/useStore';
|
import { useStoreApi, useStore } from '../hooks/useStore';
|
||||||
import type { ViewportHelperFunctions, ReactFlowState } from '../types';
|
import type { ViewportHelperFunctions, ReactFlowState } from '../types';
|
||||||
@@ -85,6 +91,36 @@ const useViewportHelper = (): ViewportHelperFunctions => {
|
|||||||
const { transform, snapToGrid, snapGrid } = store.getState();
|
const { transform, snapToGrid, snapGrid } = store.getState();
|
||||||
return pointToRendererPoint(position, transform, snapToGrid, snapGrid);
|
return pointToRendererPoint(position, transform, snapToGrid, snapGrid);
|
||||||
},
|
},
|
||||||
|
screenToFlowCoordinate: (position: XYPosition) => {
|
||||||
|
const { transform, snapToGrid, snapGrid, domNode } = store.getState();
|
||||||
|
if (domNode) {
|
||||||
|
const { x: domX, y: domY } = domNode.getBoundingClientRect();
|
||||||
|
|
||||||
|
const correctedPosition = {
|
||||||
|
x: position.x - domX,
|
||||||
|
y: position.y - domY,
|
||||||
|
};
|
||||||
|
|
||||||
|
return pointToRendererPoint(correctedPosition, transform, snapToGrid, snapGrid || [1, 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { x: 0, y: 0 };
|
||||||
|
},
|
||||||
|
flowToScreenCoordinate: (position: XYPosition) => {
|
||||||
|
const { transform, domNode } = store.getState();
|
||||||
|
if (domNode) {
|
||||||
|
const { x: domX, y: domY } = domNode.getBoundingClientRect();
|
||||||
|
|
||||||
|
const rendererPosition = rendererPointToPoint(position, transform);
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: rendererPosition.x + domX,
|
||||||
|
y: rendererPosition.y + domY,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { x: 0, y: 0 };
|
||||||
|
},
|
||||||
viewportInitialized: panZoomInitialized,
|
viewportInitialized: panZoomInitialized,
|
||||||
};
|
};
|
||||||
}, [panZoomInitialized]);
|
}, [panZoomInitialized]);
|
||||||
|
|||||||
@@ -55,5 +55,7 @@ export type ViewportHelperFunctions = {
|
|||||||
setCenter: SetCenter;
|
setCenter: SetCenter;
|
||||||
fitBounds: FitBounds;
|
fitBounds: FitBounds;
|
||||||
project: Project;
|
project: Project;
|
||||||
|
screenToFlowCoordinate: Project;
|
||||||
|
flowToScreenCoordinate: Project;
|
||||||
viewportInitialized: boolean;
|
viewportInitialized: boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -28,7 +28,10 @@ export namespace Instance {
|
|||||||
export type GetEdge<EdgeData> = (id: string) => Edge<EdgeData> | undefined;
|
export type GetEdge<EdgeData> = (id: string) => Edge<EdgeData> | undefined;
|
||||||
export type AddEdges<EdgeData> = (payload: Edge<EdgeData>[] | Edge<EdgeData>) => void;
|
export type AddEdges<EdgeData> = (payload: Edge<EdgeData>[] | Edge<EdgeData>) => void;
|
||||||
export type ToObject<NodeData = any, EdgeData = any> = () => ReactFlowJsonObject<NodeData, EdgeData>;
|
export type ToObject<NodeData = any, EdgeData = any> = () => ReactFlowJsonObject<NodeData, EdgeData>;
|
||||||
export type DeleteElements = ({ nodes, edges }: DeleteElementsOptions) => void;
|
export type DeleteElements = ({ nodes, edges }: DeleteElementsOptions) => {
|
||||||
|
deletedNodes: Node[];
|
||||||
|
deletedEdges: Edge[];
|
||||||
|
};
|
||||||
export type GetIntersectingNodes<NodeData> = (
|
export type GetIntersectingNodes<NodeData> = (
|
||||||
node: (Partial<Node<NodeData>> & { id: Node['id'] }) | Rect,
|
node: (Partial<Node<NodeData>> & { id: Node['id'] }) | Rect,
|
||||||
partially?: boolean,
|
partially?: boolean,
|
||||||
|
|||||||
@@ -12,11 +12,11 @@
|
|||||||
|
|
||||||
{#if $connection.path}
|
{#if $connection.path}
|
||||||
<svg width={$width} height={$height} class="svelte-flow__connectionline" style={containerStyle}>
|
<svg width={$width} height={$height} class="svelte-flow__connectionline" style={containerStyle}>
|
||||||
<g class={cc(['svelte-flow__connection', $connection.status])} {style}>
|
<g class={cc(['svelte-flow__connection', $connection.status])}>
|
||||||
<slot name="connectionLine" />
|
<slot name="connectionLine" />
|
||||||
<!-- slot fallbacks do not work if slots are forwarded in parent -->
|
<!-- slot fallbacks do not work if slots are forwarded in parent -->
|
||||||
{#if !isCustomComponent}
|
{#if !isCustomComponent}
|
||||||
<path d={$connection.path} fill="none" class="svelte-flow__connection-path" />
|
<path d={$connection.path} {style} fill="none" class="svelte-flow__connection-path" />
|
||||||
{/if}
|
{/if}
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
<svelte:options immutable />
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
import { getMarkerId } from '@xyflow/system';
|
import { getMarkerId } from '@xyflow/system';
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
<svelte:options immutable />
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import {
|
||||||
createEventDispatcher,
|
createEventDispatcher,
|
||||||
|
|||||||
@@ -71,13 +71,14 @@
|
|||||||
export { className as class };
|
export { className as class };
|
||||||
|
|
||||||
let domNode: HTMLDivElement;
|
let domNode: HTMLDivElement;
|
||||||
|
let clientWidth: number;
|
||||||
|
let clientHeight: number;
|
||||||
|
|
||||||
const store = hasContext(key) ? useStore() : createStoreContext();
|
const store = hasContext(key) ? useStore() : createStoreContext();
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
const { width, height } = domNode.getBoundingClientRect();
|
store.width.set(clientWidth);
|
||||||
store.width.set(width);
|
store.height.set(clientHeight);
|
||||||
store.height.set(height);
|
|
||||||
store.domNode.set(domNode);
|
store.domNode.set(domNode);
|
||||||
|
|
||||||
store.syncNodeStores(nodes);
|
store.syncNodeStores(nodes);
|
||||||
@@ -141,6 +142,8 @@
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
bind:this={domNode}
|
bind:this={domNode}
|
||||||
|
bind:clientWidth
|
||||||
|
bind:clientHeight
|
||||||
{style}
|
{style}
|
||||||
class={cc(['svelte-flow', className])}
|
class={cc(['svelte-flow', className])}
|
||||||
data-testid="svelte-flow__wrapper"
|
data-testid="svelte-flow__wrapper"
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { useStore } from '$lib/store';
|
||||||
|
|
||||||
|
export function useNodes() {
|
||||||
|
const { nodes } = useStore();
|
||||||
|
return nodes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useEdges() {
|
||||||
|
const { edges } = useStore();
|
||||||
|
return edges;
|
||||||
|
}
|
||||||
@@ -4,18 +4,20 @@ import {
|
|||||||
isRectObject,
|
isRectObject,
|
||||||
nodeToRect,
|
nodeToRect,
|
||||||
pointToRendererPoint,
|
pointToRendererPoint,
|
||||||
type Project,
|
type FitBoundsOptions,
|
||||||
type Rect,
|
|
||||||
type SetCenterOptions,
|
type SetCenterOptions,
|
||||||
type Viewport,
|
type Viewport,
|
||||||
type ViewportHelperFunctionOptions,
|
type ViewportHelperFunctionOptions,
|
||||||
type XYPosition,
|
type XYPosition,
|
||||||
type ZoomInOut
|
type ZoomInOut,
|
||||||
|
type Rect,
|
||||||
|
getTransformForBounds,
|
||||||
|
getElementsToRemove,
|
||||||
|
rendererPointToPoint
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
import type { FitViewOptions, Node } from '$lib/types';
|
import type { Edge, FitViewOptions, Node } from '$lib/types';
|
||||||
import type { SvelteFlowStore } from '$lib/store/types';
|
|
||||||
|
|
||||||
export function useSvelteFlow(): {
|
export function useSvelteFlow(): {
|
||||||
zoomIn: ZoomInOut;
|
zoomIn: ZoomInOut;
|
||||||
@@ -27,19 +29,23 @@ export function useSvelteFlow(): {
|
|||||||
getViewport: () => Viewport;
|
getViewport: () => Viewport;
|
||||||
fitView: (options?: FitViewOptions) => void;
|
fitView: (options?: FitViewOptions) => void;
|
||||||
getIntersectingNodes: (
|
getIntersectingNodes: (
|
||||||
nodeOrRect: (Partial<Node<any>> & { id: Node['id'] }) | Rect,
|
nodeOrRect: (Partial<Node> & { id: Node['id'] }) | Rect,
|
||||||
partially?: boolean,
|
partially?: boolean,
|
||||||
nodesToIntersect?: Node[]
|
nodesToIntersect?: Node[]
|
||||||
) => Node[];
|
) => Node[];
|
||||||
isNodeIntersecting: (
|
isNodeIntersecting: (
|
||||||
nodeOrRect: (Partial<Node<any>> & { id: Node['id'] }) | Rect,
|
nodeOrRect: (Partial<Node> & { id: Node['id'] }) | Rect,
|
||||||
area: Rect,
|
area: Rect,
|
||||||
partially?: boolean
|
partially?: boolean
|
||||||
) => boolean;
|
) => boolean;
|
||||||
project: Project;
|
fitBounds: (bounds: Rect, options?: FitBoundsOptions) => void;
|
||||||
|
deleteElements: (
|
||||||
|
nodesToRemove?: Partial<Node> & { id: string }[],
|
||||||
|
edgesToRemove?: Partial<Edge> & { id: string }[]
|
||||||
|
) => { deletedNodes: Node[]; deletedEdges: Edge[] };
|
||||||
|
screenToFlowCoordinate: (position: XYPosition) => XYPosition;
|
||||||
|
flowToScreenCoordinate: (position: XYPosition) => XYPosition;
|
||||||
viewport: Writable<Viewport>;
|
viewport: Writable<Viewport>;
|
||||||
nodes: SvelteFlowStore['nodes'];
|
|
||||||
edges: SvelteFlowStore['edges'];
|
|
||||||
} {
|
} {
|
||||||
const {
|
const {
|
||||||
zoomIn,
|
zoomIn,
|
||||||
@@ -49,20 +55,22 @@ export function useSvelteFlow(): {
|
|||||||
viewport,
|
viewport,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
minZoom,
|
||||||
maxZoom,
|
maxZoom,
|
||||||
panZoom,
|
panZoom,
|
||||||
nodes,
|
nodes,
|
||||||
edges
|
edges,
|
||||||
|
domNode
|
||||||
} = useStore();
|
} = useStore();
|
||||||
|
|
||||||
const getNodeRect = (
|
const getNodeRect = (
|
||||||
nodeOrRect: (Partial<Node<any>> & { id: Node['id'] }) | Rect
|
nodeOrRect: (Partial<Node> & { id: Node['id'] }) | Rect
|
||||||
): [Rect | null, Node<any> | null | undefined, boolean] => {
|
): [Rect | null, Node | null | undefined, boolean] => {
|
||||||
const isRect = isRectObject(nodeOrRect);
|
const isRect = isRectObject(nodeOrRect);
|
||||||
const node = isRect ? null : get(nodes).find((n) => n.id === nodeOrRect.id);
|
const node = isRect ? null : get(nodes).find((n) => n.id === nodeOrRect.id);
|
||||||
|
|
||||||
if (!isRect && !node) {
|
if (!isRect && !node) {
|
||||||
[null, null, isRect];
|
return [null, null, isRect];
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeRect = isRect ? nodeOrRect : nodeToRect(node!);
|
const nodeRect = isRect ? nodeOrRect : nodeToRect(node!);
|
||||||
@@ -107,19 +115,43 @@ export function useSvelteFlow(): {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
fitView,
|
fitView,
|
||||||
|
fitBounds: (bounds: Rect, options?: FitBoundsOptions) => {
|
||||||
|
const _width = get(width);
|
||||||
|
const _height = get(height);
|
||||||
|
const _maxZoom = get(maxZoom);
|
||||||
|
const _minZoom = get(minZoom);
|
||||||
|
|
||||||
|
const [x, y, zoom] = getTransformForBounds(
|
||||||
|
bounds,
|
||||||
|
_width,
|
||||||
|
_height,
|
||||||
|
_minZoom,
|
||||||
|
_maxZoom,
|
||||||
|
options?.padding ?? 0.1
|
||||||
|
);
|
||||||
|
|
||||||
|
get(panZoom)?.setViewport(
|
||||||
|
{
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
zoom
|
||||||
|
},
|
||||||
|
{ duration: options?.duration }
|
||||||
|
);
|
||||||
|
},
|
||||||
getIntersectingNodes: (
|
getIntersectingNodes: (
|
||||||
nodeOrRect: (Partial<Node<any>> & { id: Node['id'] }) | Rect,
|
nodeOrRect: (Partial<Node> & { id: Node['id'] }) | Rect,
|
||||||
partially = true,
|
partially = true,
|
||||||
nodesToIntersect?: Node[]
|
nodesToIntersect?: Node[]
|
||||||
) => {
|
) => {
|
||||||
const [nodeRect, node, isRect] = getNodeRect(nodeOrRect);
|
const [nodeRect, node, isRect] = getNodeRect(nodeOrRect);
|
||||||
|
|
||||||
if (!nodeRect) {
|
if (!nodeRect || !node) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return (nodesToIntersect || get(nodes)).filter((n) => {
|
return (nodesToIntersect || get(nodes)).filter((n) => {
|
||||||
if (!isRect && (n.id === node!.id || !n.positionAbsolute)) {
|
if (!isRect && (n.id === node.id || !n.positionAbsolute)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,7 +163,7 @@ export function useSvelteFlow(): {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
isNodeIntersecting: (
|
isNodeIntersecting: (
|
||||||
nodeOrRect: (Partial<Node<any>> & { id: Node['id'] }) | Rect,
|
nodeOrRect: (Partial<Node> & { id: Node['id'] }) | Rect,
|
||||||
area: Rect,
|
area: Rect,
|
||||||
partially = true
|
partially = true
|
||||||
) => {
|
) => {
|
||||||
@@ -146,14 +178,70 @@ export function useSvelteFlow(): {
|
|||||||
|
|
||||||
return partiallyVisible || overlappingArea >= nodeOrRect.width! * nodeOrRect.height!;
|
return partiallyVisible || overlappingArea >= nodeOrRect.width! * nodeOrRect.height!;
|
||||||
},
|
},
|
||||||
project: (position: XYPosition) => {
|
deleteElements: (
|
||||||
const _snapGrid = get(snapGrid);
|
nodesToRemove: Partial<Node> & { id: string }[] = [],
|
||||||
const { x, y, zoom } = get(viewport);
|
edgesToRemove: Partial<Edge> & { id: string }[] = []
|
||||||
|
) => {
|
||||||
|
const _nodes = get(nodes);
|
||||||
|
const _edges = get(edges);
|
||||||
|
const { matchingNodes, matchingEdges } = getElementsToRemove<Node, Edge>({
|
||||||
|
nodesToRemove,
|
||||||
|
edgesToRemove,
|
||||||
|
nodes: _nodes,
|
||||||
|
edges: _edges
|
||||||
|
});
|
||||||
|
|
||||||
return pointToRendererPoint(position, [x, y, zoom], _snapGrid !== null, _snapGrid || [1, 1]);
|
if (matchingNodes) {
|
||||||
|
nodes.set(_nodes.filter((node) => !matchingNodes.some(({ id }) => id === node.id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (matchingEdges) {
|
||||||
|
edges.set(_edges.filter((edge) => !matchingEdges.some(({ id }) => id === edge.id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
deletedNodes: matchingNodes,
|
||||||
|
deletedEdges: matchingEdges
|
||||||
|
};
|
||||||
|
},
|
||||||
|
screenToFlowCoordinate: (position: XYPosition) => {
|
||||||
|
const _domNode = get(domNode);
|
||||||
|
if (_domNode) {
|
||||||
|
const _snapGrid = get(snapGrid);
|
||||||
|
const { x, y, zoom } = get(viewport);
|
||||||
|
const { x: domX, y: domY } = _domNode.getBoundingClientRect();
|
||||||
|
|
||||||
|
const correctedPosition = {
|
||||||
|
x: position.x - domX,
|
||||||
|
y: position.y - domY
|
||||||
|
};
|
||||||
|
|
||||||
|
return pointToRendererPoint(
|
||||||
|
correctedPosition,
|
||||||
|
[x, y, zoom],
|
||||||
|
_snapGrid !== null,
|
||||||
|
_snapGrid || [1, 1]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { x: 0, y: 0 };
|
||||||
|
},
|
||||||
|
flowToScreenCoordinate: (position: XYPosition) => {
|
||||||
|
const _domNode = get(domNode);
|
||||||
|
if (_domNode) {
|
||||||
|
const { x, y, zoom } = get(viewport);
|
||||||
|
const { x: domX, y: domY } = _domNode.getBoundingClientRect();
|
||||||
|
|
||||||
|
const rendererPosition = rendererPointToPoint(position, [x, y, zoom]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: rendererPosition.x + domX,
|
||||||
|
y: rendererPosition.y + domY
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return { x: 0, y: 0 };
|
||||||
},
|
},
|
||||||
nodes,
|
|
||||||
edges,
|
|
||||||
viewport: viewport
|
viewport: viewport
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,12 @@ export { useStore } from '$lib/store';
|
|||||||
|
|
||||||
// utils
|
// utils
|
||||||
export * from '$lib/utils';
|
export * from '$lib/utils';
|
||||||
|
|
||||||
|
//hooks
|
||||||
export * from '$lib/hooks/useSvelteFlow';
|
export * from '$lib/hooks/useSvelteFlow';
|
||||||
export * from '$lib/hooks/useUpdateNodeInternals';
|
export * from '$lib/hooks/useUpdateNodeInternals';
|
||||||
export * from '$lib/hooks/useConnection';
|
export * from '$lib/hooks/useConnection';
|
||||||
|
export * from '$lib/hooks/useNodesEdges';
|
||||||
|
|
||||||
// types
|
// types
|
||||||
export type { Edge, EdgeProps, EdgeTypes, DefaultEdgeOptions } from '$lib/types/edges';
|
export type { Edge, EdgeProps, EdgeTypes, DefaultEdgeOptions } from '$lib/types/edges';
|
||||||
|
|||||||
@@ -5,4 +5,5 @@
|
|||||||
.svelte-flow__edge-label {
|
.svelte-flow__edge-label {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user