refactor(svelte): cleanup store usage
This commit is contained in:
@@ -25,13 +25,13 @@ type UseDragParams = {
|
||||
handleSelector?: string;
|
||||
nodeId?: string;
|
||||
updateNodePositions: (dragItems: NodeDragItem[], d: boolean, p: boolean) => void;
|
||||
nodesStore: Writable<Node[]>;
|
||||
transformStore: Writable<Transform>;
|
||||
nodes: Writable<Node[]>;
|
||||
transform: Writable<Transform>;
|
||||
};
|
||||
|
||||
export default function drag(
|
||||
nodeRef: Element,
|
||||
{ handleSelector, nodeId, updateNodePositions, nodesStore, transformStore }: UseDragParams
|
||||
{ handleSelector, nodeId, updateNodePositions, nodes, transform: transformStore }: UseDragParams
|
||||
) {
|
||||
let dragging = false;
|
||||
let dragItems: NodeDragItem[] = [];
|
||||
@@ -62,7 +62,7 @@ export default function drag(
|
||||
|
||||
dragItems = dragItems.map((n) => {
|
||||
const nextPosition = { x: x - n.distance.x, y: y - n.distance.y };
|
||||
const updatedPos = calcNextPosition(n, nextPosition, get(nodesStore) as RFNode[]);
|
||||
const updatedPos = calcNextPosition(n, nextPosition, get(nodes) as RFNode[]);
|
||||
|
||||
// we want to make sure that we only fire a change event when there is a changes
|
||||
hasChange =
|
||||
@@ -89,7 +89,7 @@ export default function drag(
|
||||
const pointerPos = getPointerPosition(event);
|
||||
console.log(pointerPos);
|
||||
lastPos = pointerPos;
|
||||
dragItems = getDragItems(get(nodesStore) as RFNode[], pointerPos, nodeId);
|
||||
dragItems = getDragItems(get(nodes) as RFNode[], pointerPos, nodeId);
|
||||
})
|
||||
.on('drag', (event: UseDragEvent) => {
|
||||
const pointerPos = getPointerPosition(event);
|
||||
|
||||
@@ -78,13 +78,13 @@ function filter(event: any, params: ZoomParams): boolean {
|
||||
}
|
||||
|
||||
type ZoomParams = {
|
||||
transformStore: Writable<Transform>;
|
||||
transform: Writable<Transform>;
|
||||
selecting: boolean;
|
||||
d3Store: Writable<{ zoom: D3ZoomInstance | null; selection: D3SelectionInstance | null }>;
|
||||
d3: Writable<{ zoom: D3ZoomInstance | null; selection: D3SelectionInstance | null }>;
|
||||
};
|
||||
|
||||
export default function zoom(domNode: Element, params: ZoomParams) {
|
||||
const { transformStore, d3Store } = params;
|
||||
const { transform, d3 } = params;
|
||||
const d3ZoomInstance = d3Zoom();
|
||||
const selection = select(domNode).call(d3ZoomInstance);
|
||||
const d3ZoomHandler = selection.on('wheel.zoom');
|
||||
@@ -100,13 +100,13 @@ export default function zoom(domNode: Element, params: ZoomParams) {
|
||||
d3ZoomHandler!.call(this, event, d);
|
||||
});
|
||||
|
||||
d3Store.set({
|
||||
d3.set({
|
||||
zoom: d3ZoomInstance,
|
||||
selection
|
||||
});
|
||||
|
||||
d3ZoomInstance.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
transformStore.set([event.transform.x, event.transform.y, event.transform.k]);
|
||||
transform.set([event.transform.x, event.transform.y, event.transform.k]);
|
||||
});
|
||||
|
||||
d3ZoomInstance.filter((event: any) => filter(event, params));
|
||||
|
||||
@@ -6,17 +6,17 @@
|
||||
|
||||
type $$Props = ConnectionLineProps;
|
||||
|
||||
const { connectionPathStore, widthStore, heightStore, connectionStore } = useStore();
|
||||
const { connectionPath, width, height, connection } = useStore();
|
||||
</script>
|
||||
|
||||
{#if $connectionPathStore}
|
||||
{#if $connectionPath}
|
||||
<svg
|
||||
width={$widthStore}
|
||||
height={$heightStore}
|
||||
width={$width}
|
||||
height={$height}
|
||||
class="react-flow__connectionline"
|
||||
>
|
||||
<g class={cc(['react-flow__connection', $connectionStore.status])}>
|
||||
<path d={$connectionPathStore} fill="none" class="react-flow__connection-path" />
|
||||
<g class={cc(['react-flow__connection', $connection.status])}>
|
||||
<path d={$connectionPath} fill="none" class="react-flow__connection-path" />
|
||||
</g>
|
||||
</svg>
|
||||
{/if}
|
||||
|
||||
@@ -38,7 +38,7 @@ export function handlePointerDown({
|
||||
connectionMode,
|
||||
connectionRadius,
|
||||
isTarget,
|
||||
transformStore,
|
||||
transform: transformStore,
|
||||
panBy,
|
||||
updateConnection,
|
||||
cancelConnection,
|
||||
@@ -56,7 +56,7 @@ export function handlePointerDown({
|
||||
nodes: Node[];
|
||||
connectionRadius: number;
|
||||
isValidConnection: ValidConnectionFunc;
|
||||
transformStore: Writable<Transform>;
|
||||
transform: Writable<Transform>;
|
||||
updateConnection: (connection: Partial<ConnectionData>) => void;
|
||||
cancelConnection: () => void;
|
||||
panBy: (delta: XYPosition) => void;
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
const handleId = id || null;
|
||||
|
||||
const {
|
||||
connectionModeStore,
|
||||
domNodeStore,
|
||||
nodesStore,
|
||||
connectionRadiusStore,
|
||||
transformStore,
|
||||
connectionMode,
|
||||
domNode,
|
||||
nodes,
|
||||
connectionRadius,
|
||||
transform,
|
||||
addEdge,
|
||||
panBy,
|
||||
cancelConnection,
|
||||
@@ -49,11 +49,11 @@
|
||||
handleId,
|
||||
nodeId,
|
||||
isTarget,
|
||||
connectionRadius: $connectionRadiusStore,
|
||||
domNode: $domNodeStore,
|
||||
nodes: $nodesStore,
|
||||
connectionMode: $connectionModeStore,
|
||||
transformStore,
|
||||
connectionRadius: $connectionRadius,
|
||||
domNode: $domNode,
|
||||
nodes: $nodes,
|
||||
connectionMode: $connectionMode,
|
||||
transform,
|
||||
isValidConnection: isValidConnection!,
|
||||
onConnect: onConnectExtended,
|
||||
updateConnection,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
import type { KeyHandlerProps } from './KeyHandler'
|
||||
import type { KeyDefinition, KeyDefinitionObject } from '$lib/types';
|
||||
|
||||
const { selectionKeyPressedStore, deleteKeyPressedStore } = useStore();
|
||||
const { selectionKeyPressed, deleteKeyPressed } = useStore();
|
||||
|
||||
type $$Props = KeyHandlerProps;
|
||||
|
||||
@@ -25,19 +25,19 @@
|
||||
|
||||
<svelte:window
|
||||
use:shortcut={{
|
||||
trigger: [{ key: selectionKeyString, modifier: selectionKeyModifier, callback: () => selectionKeyPressedStore.set(true) }],
|
||||
trigger: [{ key: selectionKeyString, modifier: selectionKeyModifier, callback: () => selectionKeyPressed.set(true) }],
|
||||
type: 'keydown'
|
||||
}}
|
||||
use:shortcut={{
|
||||
trigger: [{ key: selectionKeyString, modifier: selectionKeyModifier, callback: () => selectionKeyPressedStore.set(false) }],
|
||||
trigger: [{ key: selectionKeyString, modifier: selectionKeyModifier, callback: () => selectionKeyPressed.set(false) }],
|
||||
type: 'keyup'
|
||||
}}
|
||||
use:shortcut={{
|
||||
trigger: [{ key: deleteKeyString, modifier: deleteKeyModifier, callback: () => deleteKeyPressedStore.set(true) }],
|
||||
trigger: [{ key: deleteKeyString, modifier: deleteKeyModifier, callback: () => deleteKeyPressed.set(true) }],
|
||||
type: 'keydown'
|
||||
}}
|
||||
use:shortcut={{
|
||||
trigger: [{ key: deleteKeyString, modifier: deleteKeyModifier, callback: () => deleteKeyPressedStore.set(false) }],
|
||||
trigger: [{ key: deleteKeyString, modifier: deleteKeyModifier, callback: () => deleteKeyPressed.set(false) }],
|
||||
type: 'keyup'
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
import Selection from '$lib/components/Selection/index.svelte';
|
||||
import drag from '$lib/actions/drag'
|
||||
|
||||
const { selectionRectModeStore, nodesStore, nodeOriginStore, transformStore, updateNodePositions } = useStore();
|
||||
const { selectionRectMode, nodes, nodeOrigin, transform, updateNodePositions } = useStore();
|
||||
|
||||
$: selectedNodes = $nodesStore.filter(n => n.selected);
|
||||
$: rect = getRectOfNodes(selectedNodes, $nodeOriginStore);
|
||||
$: selectedNodes = $nodes.filter(n => n.selected);
|
||||
$: rect = getRectOfNodes(selectedNodes, $nodeOrigin);
|
||||
</script>
|
||||
|
||||
{#if selectedNodes}
|
||||
<div
|
||||
class="selection-wrapper nopan"
|
||||
style={`width: ${rect.width}px; height: ${rect.height}px; transform: translate(${rect.x}px, ${rect.y}px)`}
|
||||
use:drag={{ nodesStore, transformStore, updateNodePositions }}
|
||||
use:drag={{ nodes, transform, updateNodePositions }}
|
||||
/>
|
||||
<Selection
|
||||
isVisible={$selectionRectModeStore === 'nodes'}
|
||||
isVisible={$selectionRectMode === 'nodes'}
|
||||
width={rect.width}
|
||||
height={rect.height}
|
||||
x={rect.x}
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
import { useStore } from '$lib/store';
|
||||
import Selection from '$lib/components/Selection/index.svelte';
|
||||
|
||||
const { selectionRectStore, selectionRectModeStore } = useStore();
|
||||
const { selectionRect, selectionRectMode } = useStore();
|
||||
</script>
|
||||
|
||||
<Selection
|
||||
isVisible={!!($selectionRectStore && $selectionRectModeStore === 'user')}
|
||||
width={$selectionRectStore?.width}
|
||||
height={$selectionRectStore?.height}
|
||||
x={$selectionRectStore?.x}
|
||||
y={$selectionRectStore?.y}
|
||||
isVisible={!!($selectionRect && $selectionRectMode === 'user')}
|
||||
width={$selectionRect?.width}
|
||||
height={$selectionRect?.height}
|
||||
x={$selectionRect?.x}
|
||||
y={$selectionRect?.y}
|
||||
/>
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
export let selected: $$Props['selected'] = false;
|
||||
export let label: $$Props['label'] = undefined;
|
||||
|
||||
const { edgeTypesStore } = useStore();
|
||||
const edgeComponent: typeof SvelteComponentTyped<EdgeProps> = $edgeTypesStore[type!] || BezierEdge;
|
||||
const { edgeTypes } = useStore();
|
||||
const edgeComponent: typeof SvelteComponentTyped<EdgeProps> = $edgeTypes[type!] || BezierEdge;
|
||||
</script>
|
||||
|
||||
<g
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
|
||||
let nodeRef: HTMLDivElement;
|
||||
|
||||
const { nodesStore, transformStore, nodeTypesStore, updateNodePositions, addSelectedNodes } = useStore();
|
||||
const nodeComponent: typeof SvelteComponentTyped<Partial<NodeProps>> = $nodeTypesStore[type] || DefaultNode;
|
||||
const { nodes, transform, nodeTypes, updateNodePositions, addSelectedNodes } = useStore();
|
||||
const nodeComponent: typeof SvelteComponentTyped<Partial<NodeProps>> = $nodeTypes[type] || DefaultNode;
|
||||
const isSelectable = true;
|
||||
const selectNodesOnDrag = false;
|
||||
const isDraggable = true;
|
||||
@@ -58,7 +58,7 @@
|
||||
</script>
|
||||
|
||||
<div
|
||||
use:drag={{ nodeId: id, nodesStore, transformStore, updateNodePositions }}
|
||||
use:drag={{ nodeId: id, nodes, transform, updateNodePositions }}
|
||||
class={cc(['react-flow__node', `react-flow__node-${type}`, className])}
|
||||
class:initializing={!width && !height}
|
||||
class:dragging={dragging}
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
import EdgeWrapper from '$lib/components/edges/EdgeWrapper.svelte';
|
||||
import { useStore } from '$lib/store';
|
||||
|
||||
const { edgesWithDataStore, widthStore, heightStore } = useStore();
|
||||
const { edgesLayouted, width, height } = useStore();
|
||||
|
||||
</script>
|
||||
|
||||
<svg
|
||||
width={$widthStore}
|
||||
<svg
|
||||
width={$width}
|
||||
height={$height}
|
||||
class="react-flow__edges"
|
||||
>
|
||||
>
|
||||
{#each $edgesLayouted as edge (edge.id)}
|
||||
<EdgeWrapper {...edge} />
|
||||
{/each}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import NodeWrapper from '$lib/components/nodes/NodeWrapper.svelte';
|
||||
import { useStore } from '$lib/store';
|
||||
|
||||
const { nodesStore, updateNodeDimensions } = useStore();
|
||||
const { nodes, 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,
|
||||
@@ -21,7 +21,7 @@
|
||||
</script>
|
||||
|
||||
<div class="react-flow__nodes">
|
||||
{#each $nodesStore as node (node.id)}
|
||||
{#each $nodes as node (node.id)}
|
||||
<NodeWrapper {...node} {resizeObserver} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
import { SelectionMode, type Node, type Edge } from '@reactflow/system';
|
||||
import { getConnectedEdges, getEventPosition, getNodesInside } from '@reactflow/utils';
|
||||
|
||||
const { nodesStore, edgesStore, transformStore, nodeOriginStore, draggingStore, selectionRectStore, selectionRectModeStore, selectionKeyPressedStore, resetSelectedElements } = useStore();
|
||||
const { nodes, edges, transform, nodeOrigin, dragging, selectionRect, selectionRectMode, selectionKeyPressed, resetSelectedElements } = useStore();
|
||||
|
||||
// @todo take from props
|
||||
const elementsSelectable = true;
|
||||
@@ -43,15 +43,15 @@
|
||||
let containerBounds: DOMRect | null = null
|
||||
let selectedNodes: Node[] = [];
|
||||
|
||||
$: isSelecting = $selectionKeyPressedStore;
|
||||
$: hasActiveSelection = elementsSelectable && (isSelecting || $selectionRectModeStore === 'user');
|
||||
$: isSelecting = $selectionKeyPressed;
|
||||
$: hasActiveSelection = elementsSelectable && (isSelecting || $selectionRectMode === 'user');
|
||||
|
||||
|
||||
function onClick (event: MouseEvent) {
|
||||
// onPaneClick?.(event);
|
||||
|
||||
resetSelectedElements();
|
||||
selectionRectModeStore.set(null)
|
||||
selectionRectMode.set(null)
|
||||
}
|
||||
|
||||
function onMouseDown(event: MouseEvent) {
|
||||
@@ -71,7 +71,7 @@
|
||||
|
||||
resetSelectedElements();
|
||||
|
||||
selectionRectStore.set({
|
||||
selectionRect.set({
|
||||
width: 0,
|
||||
height: 0,
|
||||
startX: x,
|
||||
@@ -87,14 +87,14 @@
|
||||
|
||||
|
||||
function onMouseMove(event: MouseEvent) {
|
||||
if (!isSelecting || !containerBounds || !$selectionRectStore) {
|
||||
if (!isSelecting || !containerBounds || !$selectionRect) {
|
||||
return;
|
||||
}
|
||||
const mousePos = getEventPosition(event, containerBounds);
|
||||
const startX = $selectionRectStore.startX ?? 0;
|
||||
const startY = $selectionRectStore.startY ?? 0;
|
||||
const startX = $selectionRect.startX ?? 0;
|
||||
const startY = $selectionRect.startY ?? 0;
|
||||
const nextUserSelectRect = {
|
||||
...$selectionRectStore,
|
||||
...$selectionRect,
|
||||
x: mousePos.x < startX ? mousePos.x : startX,
|
||||
y: mousePos.y < startY ? mousePos.y : startY,
|
||||
width: Math.abs(mousePos.x - startX),
|
||||
@@ -102,21 +102,21 @@
|
||||
};
|
||||
|
||||
selectedNodes = getNodesInside(
|
||||
new Map($nodesStore.map(node => [node.id, node])),
|
||||
new Map($nodes.map(node => [node.id, node])),
|
||||
nextUserSelectRect,
|
||||
$transformStore,
|
||||
$transform,
|
||||
selectionMode === SelectionMode.Partial,
|
||||
true,
|
||||
$nodeOriginStore
|
||||
$nodeOrigin
|
||||
);
|
||||
const selectedEdgeIds = getConnectedEdges(selectedNodes, $edgesStore).map((e) => e.id);
|
||||
const selectedEdgeIds = getConnectedEdges(selectedNodes, $edges).map((e) => e.id);
|
||||
const selectedNodeIds = selectedNodes.map((n) => n.id);
|
||||
|
||||
nodesStore.update(nodes => nodes.map(toggleSelected(selectedNodeIds)));
|
||||
edgesStore.update(edges => edges.map(toggleSelected(selectedEdgeIds)));
|
||||
nodes.update(nodes => nodes.map(toggleSelected(selectedNodeIds)));
|
||||
edges.update(edges => edges.map(toggleSelected(selectedEdgeIds)));
|
||||
|
||||
selectionRectModeStore.set('user');
|
||||
selectionRectStore.set(nextUserSelectRect);
|
||||
selectionRectMode.set('user');
|
||||
selectionRect.set(nextUserSelectRect);
|
||||
}
|
||||
|
||||
function onMouseUp(event: MouseEvent) {
|
||||
@@ -129,10 +129,10 @@
|
||||
// if (!userSelectionActive && userSelectionRect && event.target === container.current) {
|
||||
// onClick?.(event);
|
||||
// }
|
||||
selectionRectStore.set(null);
|
||||
selectionRect.set(null);
|
||||
|
||||
if (selectedNodes.length > 0) {
|
||||
selectionRectModeStore.set('nodes');
|
||||
selectionRectMode.set('nodes');
|
||||
}
|
||||
|
||||
|
||||
@@ -140,19 +140,19 @@
|
||||
}
|
||||
|
||||
const onMouseLeave = (event: MouseEvent) => {
|
||||
if ($selectionRectModeStore === 'user') {
|
||||
selectionRectModeStore.set(selectedNodes.length > 0 ? 'nodes' : null);
|
||||
if ($selectionRectMode === 'user') {
|
||||
selectionRectMode.set(selectedNodes.length > 0 ? 'nodes' : null);
|
||||
// onSelectionEnd?.(event);
|
||||
}
|
||||
|
||||
selectionRectStore.set(null);
|
||||
selectionRect.set(null);
|
||||
};
|
||||
</script>
|
||||
|
||||
<div
|
||||
bind:this={container}
|
||||
class="react-flow__pane"
|
||||
class:dragging={$draggingStore}
|
||||
class:dragging={$dragging}
|
||||
class:selection={isSelecting}
|
||||
on:click={hasActiveSelection ? undefined : wrapHandler(onClick, container)}
|
||||
on:mousedown={hasActiveSelection ? onMouseDown : undefined}
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
export let nodeTypes: $$Props['nodeTypes'] = undefined;
|
||||
export let selectionKey: $$Props['selectionKey'] = undefined;
|
||||
export let deleteKey: $$Props['deleteKey'] = undefined;
|
||||
|
||||
export let connectionLineType: $$Props['connectionLineType'] = undefined;
|
||||
|
||||
let className: $$Props['class'] = undefined;
|
||||
export { className as class };
|
||||
|
||||
@@ -40,16 +41,38 @@
|
||||
|
||||
onMount(() => {
|
||||
const { width, height } = domNode.getBoundingClientRect();
|
||||
store.widthStore.set(width);
|
||||
store.heightStore.set(height);
|
||||
store.domNodeStore.set(domNode);
|
||||
store.width.set(width);
|
||||
store.height.set(height);
|
||||
store.domNode.set(domNode);
|
||||
|
||||
// @todo: is this a svelte way for two way binding?
|
||||
store.nodesStore.subscribe((ns) => {
|
||||
store.nodes.subscribe((ns) => {
|
||||
nodes = ns;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// $: {
|
||||
// const updatableProps = {
|
||||
// defaultEdgeOptions,
|
||||
// connectionMode,
|
||||
// snapToGrid,
|
||||
// snapGrid,
|
||||
// nodesDraggable,
|
||||
// connectOnClick,
|
||||
// fitViewOnInit: fitView,
|
||||
// fitViewOnInitOptions: fitViewOptions,
|
||||
// };
|
||||
|
||||
// Object.keys(updatableProps).forEach((key) => {
|
||||
// store.update((state) => ({
|
||||
// ...state,
|
||||
// [key]: valuesToUpdate[key],
|
||||
// }));
|
||||
|
||||
// });
|
||||
// }
|
||||
|
||||
$: {
|
||||
store.setNodes(nodes);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { useStore } from '$lib/store';
|
||||
|
||||
const { transformStore} = useStore();
|
||||
const { transform } = useStore();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="react-flow__viewport"
|
||||
style="transform: translate({$transformStore[0]}px, {$transformStore[1]}px) scale({$transformStore[2]})"
|
||||
style="transform: translate({$transform[0]}px, {$transform[1]}px) scale({$transform[2]})"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
import { useStore } from '$lib/store';
|
||||
import zoom from '$lib/actions/zoom';
|
||||
|
||||
const { transformStore, d3Store, selectionKeyPressedStore, selectionRectModeStore } = useStore();
|
||||
const { transform, d3, selectionKeyPressed, selectionRectMode } = useStore();
|
||||
|
||||
$: selecting = $selectionKeyPressedStore || $selectionRectModeStore === 'user';
|
||||
$: selecting = $selectionKeyPressed || $selectionRectMode === 'user';
|
||||
</script>
|
||||
|
||||
<div class="react-flow__zoom" use:zoom={{ transformStore, d3Store, selecting }}>
|
||||
<div class="react-flow__zoom" use:zoom={{ transform, d3, selecting }}>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
|
||||
@@ -27,16 +27,16 @@
|
||||
[BackgroundVariant.Cross]: 6,
|
||||
};
|
||||
|
||||
const { transformStore, idStore } = useStore();
|
||||
const { transform, id } = useStore();
|
||||
const patternColor = color || defaultColor[variant];
|
||||
const patternSize = size || defaultSize[variant];
|
||||
const isDots = variant === BackgroundVariant.Dots;
|
||||
const isCross = variant === BackgroundVariant.Cross;
|
||||
const gapXY: number[] = Array.isArray(gap) ? gap : [gap, gap];
|
||||
|
||||
$: patternId = `background-pattern-${$idStore}`;
|
||||
$: scaledGap = [gapXY[0] * $transformStore[2] || 1, gapXY[1] * $transformStore[2] || 1];
|
||||
$: scaledSize = patternSize * $transformStore[2];
|
||||
$: patternId = `background-pattern-${$id}`;
|
||||
$: scaledGap = [gapXY[0] * $transform[2] || 1, gapXY[1] * $transform[2] || 1];
|
||||
$: scaledSize = patternSize * $transform[2];
|
||||
$: patternDimensions = (isCross ? [scaledSize, scaledSize] : scaledGap) as [number, number];
|
||||
$: patternOffset = isDots
|
||||
? [scaledSize / 2, scaledSize / 2]
|
||||
@@ -46,8 +46,8 @@
|
||||
<svg class={cc(['react-flow__background', className])} >
|
||||
<pattern
|
||||
id={patternId}
|
||||
x={$transformStore[0] % scaledGap[0]}
|
||||
y={$transformStore[1] % scaledGap[1]}
|
||||
x={$transform[0] % scaledGap[0]}
|
||||
y={$transform[1] % scaledGap[1]}
|
||||
width={scaledGap[0]}
|
||||
height={scaledGap[1]}
|
||||
patternUnits="userSpaceOnUse"
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
const defaultWidth = 200;
|
||||
const defaultHeight = 150;
|
||||
const { nodesStore, transformStore, widthStore, heightStore, nodeOriginStore, idStore } = useStore();
|
||||
const { nodes, transform, width, height, nodeOrigin, id } = useStore();
|
||||
|
||||
type GetMiniMapNodeAttribute = (node: Node) => string;
|
||||
const getAttrFunction = (func: any): GetMiniMapNodeAttribute => (func instanceof Function ? func : () => func);
|
||||
@@ -32,15 +32,15 @@
|
||||
const nodeStrokeColorFunc = getAttrFunction(nodeStrokeColor);
|
||||
const nodeClassNameFunc = getAttrFunction(nodeClassName);
|
||||
const shapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision';
|
||||
const labelledBy = `react-flow__minimap-desc-${$idStore}`;
|
||||
const labelledBy = `react-flow__minimap-desc-${$id}`;
|
||||
|
||||
$: viewBB = {
|
||||
x: -$transformStore[0] / $transformStore[2],
|
||||
y: -$transformStore[1] / $transformStore[2],
|
||||
width: $widthStore / $transformStore[2],
|
||||
height: $heightStore / $transformStore[2],
|
||||
x: -$transform[0] / $transform[2],
|
||||
y: -$transform[1] / $transform[2],
|
||||
width: $width / $transform[2],
|
||||
height: $height / $transform[2],
|
||||
} as Rect;
|
||||
$: boundingRect = $nodesStore.length > 0 ? getBoundsOfRects(getRectOfNodes($nodesStore, $nodeOriginStore), viewBB) : viewBB
|
||||
$: boundingRect = $nodes.length > 0 ? getBoundsOfRects(getRectOfNodes($nodes, $nodeOrigin), viewBB) : viewBB
|
||||
$: elementWidth = (style?.width as number) ?? defaultWidth;
|
||||
$: elementHeight = (style?.height as number) ?? defaultHeight;
|
||||
$: scaledWidth = boundingRect.width / elementWidth;
|
||||
@@ -51,23 +51,23 @@
|
||||
$: offset = 5 * viewScale;
|
||||
$: x = boundingRect.x - (viewWidth - boundingRect.width) / 2 - offset;
|
||||
$: y = boundingRect.y - (viewHeight - boundingRect.height) / 2 - offset;
|
||||
$: width = viewWidth + offset * 2;
|
||||
$: height = viewHeight + offset * 2;
|
||||
$: viewboxWidth = viewWidth + offset * 2;
|
||||
$: viewboxHeight = viewHeight + offset * 2;
|
||||
</script>
|
||||
|
||||
<Panel position={position} class={cc(['react-flow__minimap', className])} style={`background-color: ${bgColor};`}>
|
||||
<svg
|
||||
width={elementWidth}
|
||||
height={elementHeight}
|
||||
viewBox={`${x} ${y} ${width} ${height}`}
|
||||
viewBox={`${x} ${y} ${viewboxWidth} ${viewboxHeight}`}
|
||||
role="img"
|
||||
aria-labelledby={labelledBy}
|
||||
>
|
||||
{#if ariaLabel}<title id={labelledBy}>{ariaLabel}</title>{/if}
|
||||
|
||||
{#each $nodesStore as node}
|
||||
{#each $nodes as node(node.id)}
|
||||
{#if node.width && node.height}
|
||||
{@const pos = getNodePositionWithOrigin(node, $nodeOriginStore).positionAbsolute}
|
||||
{@const pos = getNodePositionWithOrigin(node, $nodeOrigin).positionAbsolute}
|
||||
<MinimapNode
|
||||
x={pos.x}
|
||||
y={pos.y}
|
||||
@@ -85,7 +85,7 @@
|
||||
{/each}
|
||||
<path
|
||||
class="react-flow__minimap-mask"
|
||||
d={`M${x - offset},${y - offset}h${width + offset * 2}v${height + offset * 2}h${-width - offset * 2}z
|
||||
d={`M${x - offset},${y - offset}h${viewboxWidth + offset * 2}v${viewboxHeight + offset * 2}h${-viewboxWidth - offset * 2}z
|
||||
M${viewBB.x},${viewBB.y}h${viewBB.width}v${viewBB.height}h${-viewBB.width}z`}
|
||||
fill={maskColor}
|
||||
fill-rule="evenodd"
|
||||
|
||||
@@ -10,9 +10,8 @@
|
||||
export let shapeRendering: string;
|
||||
export let strokeColor: string;
|
||||
export let strokeWidth: number;
|
||||
export let className: string = '';
|
||||
export let style: Record<string, string>;
|
||||
|
||||
let className: string = '';
|
||||
export { className as class };
|
||||
|
||||
const { background, backgroundColor } = style || {};
|
||||
|
||||
76
packages/svelte/src/lib/store/connection-path.ts
Normal file
76
packages/svelte/src/lib/store/connection-path.ts
Normal file
@@ -0,0 +1,76 @@
|
||||
import { getBezierPath, getSmoothStepPath, getStraightPath } from '@reactflow/edge-utils';
|
||||
import { ConnectionLineType, ConnectionMode, Position } from '@reactflow/system';
|
||||
|
||||
import type { SvelteFlowStoreState } from './types';
|
||||
import { derived } from 'svelte/store';
|
||||
|
||||
const oppositePosition = {
|
||||
[Position.Left]: Position.Right,
|
||||
[Position.Right]: Position.Left,
|
||||
[Position.Top]: Position.Bottom,
|
||||
[Position.Bottom]: Position.Top
|
||||
};
|
||||
|
||||
export function getConnectionPath(store: SvelteFlowStoreState) {
|
||||
return derived(
|
||||
[
|
||||
store.connection,
|
||||
store.connectionLineType,
|
||||
store.connectionMode,
|
||||
store.nodes,
|
||||
store.transform
|
||||
],
|
||||
([$connection, $connectionLineType, $connectionMode, $nodes, $transform]) => {
|
||||
if (!$connection.nodeId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fromNode = $nodes.find((n) => n.id === $connection.nodeId);
|
||||
const fromHandleBounds = fromNode?.[internalsSymbol]?.handleBounds;
|
||||
const handleBoundsStrict = fromHandleBounds?.[$connection.handleType || 'source'] || [];
|
||||
const handleBoundsLoose = handleBoundsStrict
|
||||
? handleBoundsStrict
|
||||
: fromHandleBounds?.[$connection.handleType === 'source' ? 'target' : 'source']!;
|
||||
const handleBounds =
|
||||
$connectionMode === ConnectionMode.Strict ? handleBoundsStrict : handleBoundsLoose;
|
||||
const fromHandle = $connection.handleId
|
||||
? handleBounds.find((d) => d.id === $connection.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 fromPosition = fromHandle?.position;
|
||||
const toPosition = fromPosition ? oppositePosition[fromPosition] : undefined;
|
||||
|
||||
const pathParams = {
|
||||
sourceX: fromX,
|
||||
sourceY: fromY,
|
||||
sourcePosition: fromPosition,
|
||||
targetX: (($connection.position?.x ?? 0) - $transform[0]) / $transform[2],
|
||||
targetY: (($connection.position?.y ?? 0) - $transform[1]) / $transform[2],
|
||||
targetPosition: toPosition
|
||||
};
|
||||
|
||||
let path = '';
|
||||
|
||||
if ($connectionLineType === ConnectionLineType.Bezier) {
|
||||
// we assume the destination position is opposite to the source position
|
||||
[path] = getBezierPath(pathParams);
|
||||
} else if ($connectionLineType === ConnectionLineType.Step) {
|
||||
[path] = getSmoothStepPath({
|
||||
...pathParams,
|
||||
borderRadius: 0
|
||||
});
|
||||
} else if ($connectionLineType === ConnectionLineType.SmoothStep) {
|
||||
[path] = getSmoothStepPath(pathParams);
|
||||
} else {
|
||||
[path] = getStraightPath(pathParams);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
);
|
||||
}
|
||||
61
packages/svelte/src/lib/store/edges-layouted.ts
Normal file
61
packages/svelte/src/lib/store/edges-layouted.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { derived } from 'svelte/store';
|
||||
import { Position } from '@reactflow/system';
|
||||
|
||||
import { getEdgePositions, getHandle, getNodeData } from '$lib/container/EdgeRenderer/utils';
|
||||
import type { WrapEdgeProps } from '$lib/types';
|
||||
import type { SvelteFlowStoreState } from './types';
|
||||
|
||||
export function getEdgesLayouted(store: SvelteFlowStoreState) {
|
||||
return derived([store.edges, store.nodes], ([$edges, $nodes]) => {
|
||||
return $edges
|
||||
.map((edge) => {
|
||||
const sourceNode = $nodes.find((node) => node.id === edge.source);
|
||||
const targetNode = $nodes.find((node) => node.id === edge.target);
|
||||
const [sourceNodeRect, sourceHandleBounds, sourceIsValid] = getNodeData(sourceNode);
|
||||
const [targetNodeRect, targetHandleBounds, targetIsValid] = getNodeData(targetNode);
|
||||
|
||||
if (!sourceIsValid || !targetIsValid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const 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
|
||||
);
|
||||
|
||||
// we nee to do this to match the types
|
||||
const sourceHandleId = edge.sourceHandle;
|
||||
const targetHandleId = edge.targetHandle;
|
||||
|
||||
return {
|
||||
...edge,
|
||||
type: edgeType,
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
sourceHandleId,
|
||||
targetHandleId
|
||||
};
|
||||
})
|
||||
.filter((e) => e !== null) as WrapEdgeProps[];
|
||||
});
|
||||
}
|
||||
@@ -1,23 +1,16 @@
|
||||
import { getContext } from 'svelte';
|
||||
import { derived, get, writable, type Readable, type Writable } from 'svelte/store';
|
||||
import { get } from 'svelte/store';
|
||||
import {
|
||||
type Transform,
|
||||
type NodeDragItem,
|
||||
type NodeDimensionUpdate,
|
||||
Position,
|
||||
internalsSymbol,
|
||||
SelectionMode,
|
||||
type NodeOrigin,
|
||||
type D3ZoomInstance,
|
||||
type D3SelectionInstance,
|
||||
type ViewportHelperFunctionOptions,
|
||||
type SelectionRect,
|
||||
type Node as RFNode,
|
||||
type Connection,
|
||||
ConnectionMode,
|
||||
type XYPosition,
|
||||
type CoordinateExtent,
|
||||
ConnectionLineType
|
||||
type CoordinateExtent
|
||||
} from '@reactflow/system';
|
||||
import {
|
||||
fitView as fitViewUtil,
|
||||
@@ -29,15 +22,12 @@ import {
|
||||
import { zoomIdentity } from 'd3-zoom';
|
||||
|
||||
import { getHandleBounds } from '../../utils';
|
||||
import { getEdgePositions, getHandle, getNodeData } from '$lib/container/EdgeRenderer/utils';
|
||||
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
|
||||
import InputNode from '$lib/components/nodes/InputNode.svelte';
|
||||
import OutputNode from '$lib/components/nodes/OutputNode.svelte';
|
||||
import type { EdgeTypes, NodeTypes, Node, Edge, WrapEdgeProps, ConnectionData } from '$lib/types';
|
||||
import BezierEdge from '$lib/components/edges/BezierEdge.svelte';
|
||||
import StraightEdge from '$lib/components/edges/StraightEdge.svelte';
|
||||
import SmoothStepEdge from '$lib/components/edges/SmoothStepEdge.svelte';
|
||||
import { getBezierPath, getSmoothStepPath, getStraightPath } from '@reactflow/edge-utils';
|
||||
import type { EdgeTypes, NodeTypes, Node, Edge, ConnectionData } from '$lib/types';
|
||||
|
||||
import { getEdgesLayouted } from './edges-layouted';
|
||||
import { getConnectionPath } from './connection-path';
|
||||
import { initConnectionData, initialStoreState } from './initial-store';
|
||||
import type { SvelteFlowStore } from './types';
|
||||
|
||||
export const key = Symbol();
|
||||
|
||||
@@ -50,59 +40,6 @@ type CreateStoreProps = {
|
||||
id?: string;
|
||||
};
|
||||
|
||||
type SvelteFlowStore = {
|
||||
nodesStore: Writable<Node[]>;
|
||||
edgesStore: Writable<Edge[]>;
|
||||
heightStore: Writable<number>;
|
||||
widthStore: Writable<number>;
|
||||
d3Store: Writable<{
|
||||
zoom: D3ZoomInstance | null;
|
||||
selection: D3SelectionInstance | null;
|
||||
}>;
|
||||
transformStore: Writable<Transform>;
|
||||
edgesWithDataStore: Readable<WrapEdgeProps[]>;
|
||||
idStore: Writable<string>;
|
||||
nodeOriginStore: Writable<NodeOrigin>;
|
||||
draggingStore: Writable<boolean>;
|
||||
selectionRectStore: Writable<SelectionRect | null>;
|
||||
selectionRectModeStore: Writable<string | null>;
|
||||
selectionMode: Writable<SelectionMode>;
|
||||
selectionKeyPressedStore: Writable<boolean>;
|
||||
deleteKeyPressedStore: Writable<boolean>;
|
||||
nodeTypesStore: Writable<NodeTypes>;
|
||||
edgeTypesStore: Writable<EdgeTypes>;
|
||||
domNodeStore: Writable<HTMLDivElement | null>;
|
||||
connectionRadiusStore: Writable<number>;
|
||||
connectionModeStore: Writable<ConnectionMode>;
|
||||
connectionStore: Writable<ConnectionData>;
|
||||
connectionPathStore: Readable<string | null>;
|
||||
setNodes: (nodes: Node[]) => void;
|
||||
setEdges: (edges: Edge[]) => void;
|
||||
addEdge: (edge: Edge | Connection) => void;
|
||||
zoomIn: (options?: ViewportHelperFunctionOptions) => void;
|
||||
zoomOut: (options?: ViewportHelperFunctionOptions) => void;
|
||||
fitView: (options?: ViewportHelperFunctionOptions) => boolean;
|
||||
updateNodePositions: (
|
||||
nodeDragItems: NodeDragItem[],
|
||||
positionChanged?: boolean,
|
||||
dragging?: boolean
|
||||
) => void;
|
||||
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void;
|
||||
resetSelectedElements: () => void;
|
||||
addSelectedNodes: (ids: string[]) => void;
|
||||
panBy: (delta: XYPosition) => void;
|
||||
updateConnection: (connection: Partial<ConnectionData>) => void;
|
||||
cancelConnection: () => void;
|
||||
};
|
||||
|
||||
const initConnectionData = {
|
||||
nodeId: null,
|
||||
handleId: null,
|
||||
handleType: null,
|
||||
position: null,
|
||||
status: null
|
||||
};
|
||||
|
||||
export function createStore({
|
||||
transform = [0, 0, 1],
|
||||
nodeOrigin = [0, 0],
|
||||
@@ -111,182 +48,23 @@ export function createStore({
|
||||
edgeTypes = {},
|
||||
id = '1'
|
||||
}: CreateStoreProps): SvelteFlowStore {
|
||||
const nodesStore = writable([] as Node[]);
|
||||
const edgesStore = writable([] as Edge[]);
|
||||
const heightStore = writable(500);
|
||||
const widthStore = writable(500);
|
||||
const nodeOriginStore = writable(nodeOrigin);
|
||||
const d3Store = writable<{ zoom: D3ZoomInstance | null; selection: D3SelectionInstance | null }>({
|
||||
zoom: null,
|
||||
selection: null
|
||||
});
|
||||
const idStore = writable(id);
|
||||
const draggingStore = writable(false);
|
||||
const selectionRectStore = writable(null);
|
||||
const selectionKeyPressedStore = writable(false);
|
||||
const multiselectionKeyPressedStore = writable(false);
|
||||
const deleteKeyPressedStore = writable(false);
|
||||
const selectionRectModeStore = writable(null);
|
||||
const selectionMode = writable(SelectionMode.Partial);
|
||||
const nodeTypesStore = writable({
|
||||
...nodeTypes,
|
||||
input: nodeTypes.input || InputNode,
|
||||
output: nodeTypes.output || OutputNode,
|
||||
default: nodeTypes.default || DefaultNode
|
||||
});
|
||||
|
||||
const edgeTypesStore = writable({
|
||||
...edgeTypes,
|
||||
straight: edgeTypes.straight || StraightEdge,
|
||||
smoothstep: edgeTypes.smoothstep || SmoothStepEdge,
|
||||
default: edgeTypes.default || BezierEdge
|
||||
});
|
||||
const transformStore = writable(transform);
|
||||
const connectionModeStore = writable(ConnectionMode.Strict);
|
||||
const domNodeStore = writable(null);
|
||||
const connectionStore = writable<ConnectionData>(initConnectionData);
|
||||
const connectionRadiusStore = writable(25);
|
||||
const connectionLineTypeStore = writable(ConnectionLineType.Bezier);
|
||||
const store = {
|
||||
...initialStoreState
|
||||
};
|
||||
|
||||
let fitViewOnInitDone = false;
|
||||
|
||||
const edgesWithDataStore = derived([edgesStore, nodesStore], ([$edges, $nodes]) => {
|
||||
return $edges
|
||||
.map((edge) => {
|
||||
const sourceNode = $nodes.find((node) => node.id === edge.source);
|
||||
const targetNode = $nodes.find((node) => node.id === edge.target);
|
||||
const [sourceNodeRect, sourceHandleBounds, sourceIsValid] = getNodeData(
|
||||
sourceNode as RFNode
|
||||
);
|
||||
const [targetNodeRect, targetHandleBounds, targetIsValid] = getNodeData(
|
||||
targetNode as RFNode
|
||||
);
|
||||
|
||||
if (!sourceIsValid || !targetIsValid) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const 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
|
||||
);
|
||||
|
||||
// we nee to do this to match the types
|
||||
const sourceHandleId = edge.sourceHandle;
|
||||
const targetHandleId = edge.targetHandle;
|
||||
|
||||
return {
|
||||
...edge,
|
||||
type: edgeType,
|
||||
sourceX,
|
||||
sourceY,
|
||||
targetX,
|
||||
targetY,
|
||||
sourcePosition,
|
||||
targetPosition,
|
||||
sourceHandleId,
|
||||
targetHandleId
|
||||
};
|
||||
})
|
||||
.filter((e) => e !== null) as WrapEdgeProps[];
|
||||
});
|
||||
|
||||
const oppositePosition = {
|
||||
[Position.Left]: Position.Right,
|
||||
[Position.Right]: Position.Left,
|
||||
[Position.Top]: Position.Bottom,
|
||||
[Position.Bottom]: Position.Top
|
||||
};
|
||||
|
||||
const connectionPathStore = derived(
|
||||
[connectionStore, connectionLineTypeStore, connectionModeStore, nodesStore, transformStore],
|
||||
([
|
||||
$connectionStore,
|
||||
$connectionLineTypeStore,
|
||||
$connectionModeStore,
|
||||
$nodesStore,
|
||||
$transformStore
|
||||
]) => {
|
||||
if (!$connectionStore.nodeId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const fromNode = $nodesStore.find((n) => n.id === $connectionStore.nodeId);
|
||||
const fromHandleBounds = fromNode?.[internalsSymbol]?.handleBounds;
|
||||
const handleBoundsStrict = fromHandleBounds?.[$connectionStore.handleType || 'source'] || [];
|
||||
const handleBoundsLoose = handleBoundsStrict
|
||||
? handleBoundsStrict
|
||||
: fromHandleBounds?.[$connectionStore.handleType === 'source' ? 'target' : 'source']!;
|
||||
const handleBounds =
|
||||
$connectionModeStore === ConnectionMode.Strict ? handleBoundsStrict : handleBoundsLoose;
|
||||
const fromHandle = $connectionStore.handleId
|
||||
? handleBounds.find((d) => d.id === $connectionStore.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 fromPosition = fromHandle?.position;
|
||||
const toPosition = fromPosition ? oppositePosition[fromPosition] : undefined;
|
||||
|
||||
const pathParams = {
|
||||
sourceX: fromX,
|
||||
sourceY: fromY,
|
||||
sourcePosition: fromPosition,
|
||||
targetX: (($connectionStore.position?.x ?? 0) - $transformStore[0]) / $transformStore[2],
|
||||
targetY: (($connectionStore.position?.y ?? 0) - $transformStore[1]) / $transformStore[2],
|
||||
targetPosition: toPosition
|
||||
};
|
||||
|
||||
let path = '';
|
||||
|
||||
if ($connectionLineTypeStore === ConnectionLineType.Bezier) {
|
||||
// we assume the destination position is opposite to the source position
|
||||
[path] = getBezierPath(pathParams);
|
||||
} else if ($connectionLineTypeStore === ConnectionLineType.Step) {
|
||||
[path] = getSmoothStepPath({
|
||||
...pathParams,
|
||||
borderRadius: 0
|
||||
});
|
||||
} else if ($connectionLineTypeStore === ConnectionLineType.SmoothStep) {
|
||||
[path] = getSmoothStepPath(pathParams);
|
||||
} else {
|
||||
[path] = getStraightPath(pathParams);
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
);
|
||||
|
||||
function setEdges(edges: Edge[]) {
|
||||
edgesStore.set(edges);
|
||||
store.edges.set(edges);
|
||||
}
|
||||
|
||||
function addEdge(edgeParams: Edge | Connection) {
|
||||
const edges = get(edgesStore);
|
||||
edgesStore.set(addEdgeUtil(edgeParams, edges));
|
||||
const edges = get(store.edges);
|
||||
store.edges.set(addEdgeUtil(edgeParams, edges));
|
||||
}
|
||||
|
||||
function setNodes(nodes: Node[]) {
|
||||
nodesStore.update((currentNodes) => {
|
||||
store.nodes.update((currentNodes) => {
|
||||
const nextNodes = nodes.map((n) => {
|
||||
const currentNode = currentNodes.find((cn) => cn.id === n.id) || {};
|
||||
|
||||
@@ -302,7 +80,7 @@ export function createStore({
|
||||
}
|
||||
|
||||
function updateNodePositions(nodeDragItems: NodeDragItem[], dragging = false) {
|
||||
nodesStore.update((nds) => {
|
||||
store.nodes.update((nds) => {
|
||||
return nds.map((n) => {
|
||||
const nodeDragItem = nodeDragItems.find((ndi) => ndi.id === n.id);
|
||||
|
||||
@@ -330,7 +108,7 @@ export function createStore({
|
||||
const style = window.getComputedStyle(viewportNode);
|
||||
const { m22: zoom } = new window.DOMMatrixReadOnly(style.transform);
|
||||
|
||||
const nextNodes = get(nodesStore).map((node) => {
|
||||
const nextNodes = get(store.nodes).map((node) => {
|
||||
const update = updates.find((u) => u.id === node.id);
|
||||
|
||||
if (update) {
|
||||
@@ -360,16 +138,16 @@ export function createStore({
|
||||
return node;
|
||||
});
|
||||
|
||||
const { zoom: d3Zoom, selection: d3Selection } = get(d3Store);
|
||||
const { zoom: d3Zoom, selection: d3Selection } = get(store.d3);
|
||||
|
||||
fitViewOnInitDone =
|
||||
fitViewOnInitDone || (fitViewOnInit && !!d3Zoom && !!d3Selection && fitView());
|
||||
|
||||
nodesStore.set(nextNodes);
|
||||
store.nodes.set(nextNodes);
|
||||
}
|
||||
|
||||
function zoomIn(options?: ViewportHelperFunctionOptions) {
|
||||
const { zoom: d3Zoom, selection: d3Selection } = get(d3Store);
|
||||
const { zoom: d3Zoom, selection: d3Selection } = get(store.d3);
|
||||
|
||||
if (d3Zoom && d3Selection) {
|
||||
d3Zoom.scaleBy(getD3Transition(d3Selection, options?.duration), 1.2);
|
||||
@@ -377,14 +155,14 @@ export function createStore({
|
||||
}
|
||||
|
||||
function zoomOut(options?: ViewportHelperFunctionOptions) {
|
||||
const { zoom: d3Zoom, selection: d3Selection } = get(d3Store);
|
||||
const { zoom: d3Zoom, selection: d3Selection } = get(store.d3);
|
||||
if (d3Zoom && d3Selection) {
|
||||
d3Zoom.scaleBy(getD3Transition(d3Selection, options?.duration), 1 / 1.2);
|
||||
}
|
||||
}
|
||||
|
||||
function fitView() {
|
||||
const { zoom: d3Zoom, selection: d3Selection } = get(d3Store);
|
||||
const { zoom: d3Zoom, selection: d3Selection } = get(store.d3);
|
||||
|
||||
if (!d3Zoom || !d3Selection) {
|
||||
return false;
|
||||
@@ -392,14 +170,14 @@ export function createStore({
|
||||
|
||||
return fitViewUtil(
|
||||
{
|
||||
nodes: get(nodesStore) as RFNode[],
|
||||
width: get(widthStore),
|
||||
height: get(heightStore),
|
||||
nodes: get(store.nodes) as RFNode[],
|
||||
width: get(store.width),
|
||||
height: get(store.height),
|
||||
minZoom: 0.2,
|
||||
maxZoom: 2,
|
||||
d3Selection,
|
||||
d3Zoom,
|
||||
nodeOrigin: get(nodeOriginStore)
|
||||
nodeOrigin: get(store.nodeOrigin)
|
||||
},
|
||||
{}
|
||||
);
|
||||
@@ -417,14 +195,14 @@ export function createStore({
|
||||
}
|
||||
|
||||
function resetSelectedElements() {
|
||||
nodesStore.update((ns) => ns.map(resetSelectedItem));
|
||||
edgesStore.update((es) => es.map(resetSelectedItem));
|
||||
store.nodes.update((ns) => ns.map(resetSelectedItem));
|
||||
store.edges.update((es) => es.map(resetSelectedItem));
|
||||
}
|
||||
|
||||
deleteKeyPressedStore.subscribe((deleteKeyPressed) => {
|
||||
store.deleteKeyPressed.subscribe((deleteKeyPressed) => {
|
||||
if (deleteKeyPressed) {
|
||||
const nodes = get(nodesStore);
|
||||
const edges = get(edgesStore);
|
||||
const nodes = get(store.nodes);
|
||||
const edges = get(store.edges);
|
||||
const selectedNodes = nodes.filter((node) => node.selected);
|
||||
const selectedEdges = edges.filter((edge) => edge.selected);
|
||||
|
||||
@@ -458,21 +236,21 @@ export function createStore({
|
||||
return res;
|
||||
}, []);
|
||||
|
||||
nodesStore.update((nds) => nds.filter((node) => !nodeIds.includes(node.id)));
|
||||
edgesStore.update((eds) => eds.filter((edge) => !edgeIdsToRemove.includes(edge.id)));
|
||||
store.nodes.update((nds) => nds.filter((node) => !nodeIds.includes(node.id)));
|
||||
store.edges.update((eds) => eds.filter((edge) => !edgeIdsToRemove.includes(edge.id)));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function addSelectedNodes(ids: string[]) {
|
||||
selectionRectStore.set(null);
|
||||
selectionRectModeStore.set(null);
|
||||
store.selectionRect.set(null);
|
||||
store.selectionRectMode.set(null);
|
||||
|
||||
if (get(multiselectionKeyPressedStore)) {
|
||||
if (get(store.multiselectionKeyPressed)) {
|
||||
// @todo handle multiselection key
|
||||
}
|
||||
|
||||
nodesStore.update((ns) =>
|
||||
store.nodes.update((ns) =>
|
||||
ns.map((node) => {
|
||||
return {
|
||||
...node,
|
||||
@@ -483,10 +261,10 @@ export function createStore({
|
||||
}
|
||||
|
||||
function panBy(delta: XYPosition) {
|
||||
const { zoom: d3Zoom, selection: d3Selection } = get(d3Store);
|
||||
const transform = get(transformStore);
|
||||
const width = get(widthStore);
|
||||
const height = get(heightStore);
|
||||
const { zoom: d3Zoom, selection: d3Selection } = get(store.d3);
|
||||
const transform = get(store.transform);
|
||||
const width = get(store.width);
|
||||
const height = get(store.height);
|
||||
|
||||
if (!d3Zoom || !d3Selection || (!delta.x && !delta.y)) {
|
||||
return;
|
||||
@@ -509,7 +287,7 @@ export function createStore({
|
||||
}
|
||||
|
||||
function updateConnection(connectionUpdate: Partial<ConnectionData> | null) {
|
||||
const currentConnectionData = get(connectionStore);
|
||||
const currentConnectionData = get(store.connection);
|
||||
const nextConnectionData = currentConnectionData
|
||||
? {
|
||||
...initConnectionData,
|
||||
@@ -521,7 +299,7 @@ export function createStore({
|
||||
...connectionUpdate
|
||||
};
|
||||
|
||||
connectionStore.set(nextConnectionData);
|
||||
store.connection.set(nextConnectionData);
|
||||
}
|
||||
|
||||
function cancelConnection() {
|
||||
@@ -529,28 +307,14 @@ export function createStore({
|
||||
}
|
||||
|
||||
return {
|
||||
nodesStore,
|
||||
edgesStore,
|
||||
transformStore,
|
||||
d3Store,
|
||||
heightStore,
|
||||
widthStore,
|
||||
edgesWithDataStore,
|
||||
idStore,
|
||||
nodeOriginStore,
|
||||
draggingStore,
|
||||
selectionRectStore,
|
||||
selectionKeyPressedStore,
|
||||
deleteKeyPressedStore,
|
||||
selectionRectModeStore,
|
||||
selectionMode,
|
||||
nodeTypesStore,
|
||||
edgeTypesStore,
|
||||
connectionModeStore,
|
||||
domNodeStore,
|
||||
connectionStore,
|
||||
connectionRadiusStore,
|
||||
connectionPathStore,
|
||||
// state
|
||||
...store,
|
||||
|
||||
// derived state
|
||||
edgesLayouted: getEdgesLayouted(store),
|
||||
connectionPath: getConnectionPath(store),
|
||||
|
||||
// actions
|
||||
setNodes,
|
||||
setEdges,
|
||||
addEdge,
|
||||
|
||||
65
packages/svelte/src/lib/store/initial-store.ts
Normal file
65
packages/svelte/src/lib/store/initial-store.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { readable, writable } from 'svelte/store';
|
||||
import {
|
||||
SelectionMode,
|
||||
type D3ZoomInstance,
|
||||
type D3SelectionInstance,
|
||||
ConnectionMode,
|
||||
ConnectionLineType,
|
||||
type Transform,
|
||||
type NodeOrigin,
|
||||
type Rect
|
||||
} from '@reactflow/system';
|
||||
|
||||
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
|
||||
import InputNode from '$lib/components/nodes/InputNode.svelte';
|
||||
import OutputNode from '$lib/components/nodes/OutputNode.svelte';
|
||||
import type { Node, Edge, ConnectionData, NodeTypes, EdgeTypes } from '$lib/types';
|
||||
import BezierEdge from '$lib/components/edges/BezierEdge.svelte';
|
||||
import StraightEdge from '$lib/components/edges/StraightEdge.svelte';
|
||||
import SmoothStepEdge from '$lib/components/edges/SmoothStepEdge.svelte';
|
||||
|
||||
export const initConnectionData = {
|
||||
nodeId: null,
|
||||
handleId: null,
|
||||
handleType: null,
|
||||
position: null,
|
||||
status: null
|
||||
};
|
||||
|
||||
export const initialStoreState = {
|
||||
nodes: writable<Node[]>([]),
|
||||
edges: writable<Edge[]>([]),
|
||||
edgesLayouted: readable<Edge[]>([]),
|
||||
height: writable<number>(500),
|
||||
width: writable<number>(500),
|
||||
nodeOrigin: writable<NodeOrigin>([0.5, 0.5]),
|
||||
d3: writable<{ zoom: D3ZoomInstance | null; selection: D3SelectionInstance | null }>({
|
||||
zoom: null,
|
||||
selection: null
|
||||
}),
|
||||
id: writable<string | null>(null),
|
||||
dragging: writable<boolean>(false),
|
||||
selectionRect: writable<(Rect & { startX: number; startY: number }) | null>(null),
|
||||
selectionKeyPressed: writable<boolean>(false),
|
||||
multiselectionKeyPressed: writable<boolean>(false),
|
||||
deleteKeyPressed: writable<boolean>(false),
|
||||
selectionRectMode: writable<string | null>(null),
|
||||
selectionMode: writable<SelectionMode>(SelectionMode.Partial),
|
||||
nodeTypes: writable<NodeTypes>({
|
||||
input: InputNode,
|
||||
output: OutputNode,
|
||||
default: DefaultNode
|
||||
}),
|
||||
edgeTypes: writable<EdgeTypes>({
|
||||
straight: StraightEdge,
|
||||
smoothstep: SmoothStepEdge,
|
||||
default: BezierEdge
|
||||
}),
|
||||
transform: writable<Transform>([0, 0, 1]),
|
||||
connectionMode: writable<ConnectionMode>(ConnectionMode.Strict),
|
||||
domNode: writable<HTMLDivElement | null>(null),
|
||||
connectionPath: readable<string | null>(null),
|
||||
connection: writable<ConnectionData>(initConnectionData),
|
||||
connectionRadius: writable<number>(25),
|
||||
connectionLineType: writable<ConnectionLineType>(ConnectionLineType.Bezier)
|
||||
};
|
||||
34
packages/svelte/src/lib/store/types.d.ts
vendored
Normal file
34
packages/svelte/src/lib/store/types.d.ts
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
import type {
|
||||
NodeDimensionUpdate,
|
||||
XYPosition,
|
||||
ViewportHelperFunctionOptions,
|
||||
Connection,
|
||||
NodeDragItem
|
||||
} from '@reactflow/system';
|
||||
|
||||
import { initialStoreState } from './initial-store';
|
||||
import type { Node, Edge, ConnectionData } from '$lib/types';
|
||||
|
||||
export type SvelteFlowStoreActions = {
|
||||
setNodes: (nodes: Node[]) => void;
|
||||
setEdges: (edges: Edge[]) => void;
|
||||
addEdge: (edge: Edge | Connection) => void;
|
||||
zoomIn: (options?: ViewportHelperFunctionOptions) => void;
|
||||
zoomOut: (options?: ViewportHelperFunctionOptions) => void;
|
||||
fitView: (options?: ViewportHelperFunctionOptions) => boolean;
|
||||
updateNodePositions: (
|
||||
nodeDragItems: NodeDragItem[],
|
||||
positionChanged?: boolean,
|
||||
dragging?: boolean
|
||||
) => void;
|
||||
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void;
|
||||
resetSelectedElements: () => void;
|
||||
addSelectedNodes: (ids: string[]) => void;
|
||||
panBy: (delta: XYPosition) => void;
|
||||
updateConnection: (connection: Partial<ConnectionData>) => void;
|
||||
cancelConnection: () => void;
|
||||
};
|
||||
|
||||
export type SvelteFlowStoreState = typeof initialStoreState;
|
||||
|
||||
export type SvelteFlowStore = SvelteFlowStoreState & SvelteFlowStoreActions;
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Node, NodeTypes } from './nodes';
|
||||
import type { ShortcutModifierDefinition } from '@svelte-put/shortcut';
|
||||
import type { Edge, HandleType, XYPosition } from '@reactflow/system';
|
||||
import type { ConnectionLineType, Edge, HandleType, XYPosition } from '@reactflow/system';
|
||||
|
||||
export type KeyModifier = ShortcutModifierDefinition;
|
||||
export type KeyDefinitionObject = { key: string; modifier?: KeyModifier };
|
||||
@@ -20,6 +20,7 @@ export type SvelteFlowProps = {
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
|
||||
connectionLineType?: ConnectionLineType;
|
||||
selectionKey?: KeyDefinition;
|
||||
deleteKey?: KeyDefinition;
|
||||
nodeTypes?: NodeTypes;
|
||||
|
||||
Reference in New Issue
Block a user