feat(packages): add svelte version
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
<script lang="ts">
|
||||
type EdgePositions = {
|
||||
sourceX: number;
|
||||
sourceY: number;
|
||||
targetX: number;
|
||||
targetY: number;
|
||||
}
|
||||
|
||||
import Edge from '$lib/components/edges/BaseEdge.svelte';
|
||||
import { useStore } from '$lib/store';
|
||||
import { type NodeHandleBounds, type Rect, type Node, type HandleElement, Position, type XYPosition } from '@reactflow/core';
|
||||
import { internalsSymbol } from '@reactflow/core';
|
||||
|
||||
const { edgesStore, widthStore, heightStore, nodesStore } = useStore();
|
||||
|
||||
function getNodeData(node?: Node): [Rect, NodeHandleBounds | null, boolean] {
|
||||
const handleBounds = node?.[internalsSymbol]?.handleBounds || null;
|
||||
|
||||
const isValid =
|
||||
handleBounds &&
|
||||
node?.width &&
|
||||
node?.height &&
|
||||
typeof node?.positionAbsolute?.x !== 'undefined' &&
|
||||
typeof node?.positionAbsolute?.y !== 'undefined';
|
||||
|
||||
return [
|
||||
{
|
||||
x: node?.positionAbsolute?.x || 0,
|
||||
y: node?.positionAbsolute?.y || 0,
|
||||
width: node?.width || 0,
|
||||
height: node?.height || 0,
|
||||
},
|
||||
handleBounds,
|
||||
!!isValid,
|
||||
];
|
||||
}
|
||||
|
||||
function getHandlePosition(position: Position, nodeRect: Rect, handle: HandleElement | null = null): XYPosition {
|
||||
const x = (handle?.x || 0) + nodeRect.x;
|
||||
const y = (handle?.y || 0) + nodeRect.y;
|
||||
const width = handle?.width || nodeRect.width;
|
||||
const height = handle?.height || nodeRect.height;
|
||||
|
||||
switch (position) {
|
||||
case Position.Top:
|
||||
return {
|
||||
x: x + width / 2,
|
||||
y,
|
||||
};
|
||||
case Position.Right:
|
||||
return {
|
||||
x: x + width,
|
||||
y: y + height / 2,
|
||||
};
|
||||
case Position.Bottom:
|
||||
return {
|
||||
x: x + width / 2,
|
||||
y: y + height,
|
||||
};
|
||||
case Position.Left:
|
||||
return {
|
||||
x,
|
||||
y: y + height / 2,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function getHandle(bounds: HandleElement[], handleId?: string | null): HandleElement | null {
|
||||
if (!bounds) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (handleId) {
|
||||
return bounds.find((d) => d.id === handleId)!;
|
||||
} else if (bounds.length === 1) {
|
||||
return bounds[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function getEdgePositions(
|
||||
sourceNodeRect: Rect,
|
||||
sourceHandle: HandleElement,
|
||||
sourcePosition: Position,
|
||||
targetNodeRect: Rect,
|
||||
targetHandle: HandleElement,
|
||||
targetPosition: Position
|
||||
): EdgePositions {
|
||||
const sourceHandlePos = getHandlePosition(sourcePosition, sourceNodeRect, sourceHandle);
|
||||
const targetHandlePos = getHandlePosition(targetPosition, targetNodeRect, targetHandle);
|
||||
|
||||
return {
|
||||
sourceX: sourceHandlePos.x,
|
||||
sourceY: sourceHandlePos.y,
|
||||
targetX: targetHandlePos.x,
|
||||
targetY: targetHandlePos.y,
|
||||
};
|
||||
};
|
||||
|
||||
let edgesWithData: any = [];
|
||||
|
||||
$: {
|
||||
$nodesStore
|
||||
edgesWithData = $edgesStore.map((edge) => {
|
||||
const sourceNode = $nodesStore.find((node) => node.id === edge.source);
|
||||
const targetNode = $nodesStore.find((node) => node.id === edge.target);
|
||||
const [sourceNodeRect, sourceHandleBounds, sourceIsValid] = getNodeData(sourceNode);
|
||||
const [targetNodeRect, targetHandleBounds, targetIsValid] = getNodeData(targetNode);
|
||||
|
||||
if (!sourceIsValid || !targetIsValid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let edgeType = edge.type || 'default';
|
||||
|
||||
const targetNodeHandles = targetHandleBounds!.target;
|
||||
const sourceHandle = getHandle(sourceHandleBounds!.source!, edge.sourceHandle);
|
||||
const targetHandle = getHandle(targetNodeHandles!, edge.targetHandle);
|
||||
const sourcePosition = sourceHandle?.position || Position.Bottom;
|
||||
const targetPosition = targetHandle?.position || Position.Top;
|
||||
|
||||
if (!sourceHandle || !targetHandle) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
const { sourceX, sourceY, targetX, targetY } = getEdgePositions(
|
||||
sourceNodeRect,
|
||||
sourceHandle,
|
||||
sourcePosition,
|
||||
targetNodeRect,
|
||||
targetHandle,
|
||||
targetPosition
|
||||
);
|
||||
|
||||
|
||||
return {
|
||||
id: edge.id,
|
||||
type: edgeType,
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<svg
|
||||
width={$widthStore}
|
||||
height={$heightStore}
|
||||
class="react-flow__edges react-flow__container"
|
||||
>
|
||||
{#each edgesWithData as edge}
|
||||
{#if edge}
|
||||
<Edge {...edge} />
|
||||
{/if}
|
||||
{/each}
|
||||
</svg>
|
||||
|
||||
<style>
|
||||
.react-flow__edges {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { onDestroy } from 'svelte';
|
||||
|
||||
import Node from '$lib/components/nodes/BaseNode.svelte';
|
||||
import { useStore } from '$lib/store';
|
||||
|
||||
const { nodesStore, updateNodeDimensions } = useStore();
|
||||
const resizeObserver: ResizeObserver | null = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver((entries: ResizeObserverEntry[]) => {
|
||||
const updates = entries.map((entry: ResizeObserverEntry) => ({
|
||||
id: entry.target.getAttribute('data-id') as string,
|
||||
nodeElement: entry.target as HTMLDivElement,
|
||||
forceUpdate: true,
|
||||
}))
|
||||
|
||||
updateNodeDimensions(updates);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
resizeObserver?.disconnect();
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="react-flow__nodes">
|
||||
{#each $nodesStore as node}
|
||||
<Node {...node} {resizeObserver} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.react-flow__nodes {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,62 @@
|
||||
<script lang="ts">
|
||||
import { setContext, onMount } from 'svelte'
|
||||
import cc from 'classcat';
|
||||
import type { Node, Edge } from '@reactflow/core';
|
||||
|
||||
import { key, createStore } from '$lib/store';
|
||||
import Viewport from '$lib/container/Viewport.svelte';
|
||||
import NodeRenderer from '$lib/container/NodeRenderer.svelte';
|
||||
import EdgeRenderer from '$lib/container/EdgeRenderer.svelte';
|
||||
|
||||
export let nodes: Node[];
|
||||
export let edges: Edge[];
|
||||
|
||||
export let className: string | null = null;
|
||||
export let id: string = '1';
|
||||
|
||||
let props = { ...$$restProps };
|
||||
let domNode: Element;
|
||||
|
||||
const store = createStore({
|
||||
nodes,
|
||||
edges,
|
||||
});
|
||||
|
||||
setContext(key, {
|
||||
getStore: () => store
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
const { width, height } = domNode.getBoundingClientRect();
|
||||
store.widthStore.set(width);
|
||||
store.heightStore.set(height);
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<div
|
||||
{...props}
|
||||
class={cc(['react-flow', className])}
|
||||
data-testid="rf__wrapper"
|
||||
id={id}
|
||||
bind:this={domNode}
|
||||
>
|
||||
<Viewport>
|
||||
<NodeRenderer />
|
||||
<EdgeRenderer />
|
||||
</Viewport>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.react-flow {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
:root {
|
||||
--node-width: 150px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,35 @@
|
||||
<script lang="ts">
|
||||
import zoom from '$lib/hooks/zoom'
|
||||
import { useStore } from '$lib/store';
|
||||
|
||||
const { transformStore } = useStore();
|
||||
</script>
|
||||
|
||||
<div class="react-flow__pane" use:zoom={{ transformStore }}>
|
||||
<div
|
||||
class="react-flow__viewport"
|
||||
style="transform: translate({$transformStore[0]}px, {$transformStore[1]}px) scale({$transformStore[2]})"
|
||||
>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.react-flow__pane {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top:0;
|
||||
left:0;
|
||||
cursor: grab;
|
||||
}
|
||||
|
||||
.react-flow__viewport {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
transform-origin: 0 0;
|
||||
top:0;
|
||||
left:0;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user