feat(packages): add svelte version

This commit is contained in:
moklick
2023-02-01 09:41:09 +01:00
parent 079e380a31
commit 2af833aee1
30 changed files with 2279 additions and 21 deletions
@@ -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>