feat(svelte): add functions to useSvelteFlow

This commit is contained in:
moklick
2023-03-14 17:24:01 +01:00
parent 2e486384a8
commit 5b3c260e45
9 changed files with 102 additions and 25 deletions
@@ -2,17 +2,18 @@
import cc from 'classcat';
import type { PanelProps } from './types';
interface $$Props extends PanelProps {}
type $$Props = PanelProps;
export let position: $$Props['position'] = 'top-right';
export let style: $$Props['style'] = '';
export let style: $$Props['style'] = undefined;
let className: $$Props['class'] = undefined;
export { className as class };
$: positionClasses = `${position}`.split('-');
</script>
<div class={cc(['svelte-flow__panel', className, ...positionClasses])} {style}>
<div class={cc(['svelte-flow__panel', className, ...positionClasses])} {style} {...$$restProps}>
<slot />
</div>
@@ -1,6 +1,8 @@
import type { PanelPosition } from '@reactflow/system';
import type { HTMLAttributes } from 'svelte/elements';
export type PanelProps = {
export type PanelProps = HTMLAttributes<HTMLDivElement> & {
'data-testid'?: string;
position?: PanelPosition;
style?: string;
class?: string;
@@ -96,7 +96,7 @@
bind:this={domNode}
style={style}
class={cc(['svelte-flow', className])}
data-testid="rf__wrapper"
data-testid="svelte-flow__wrapper"
on:dragover
on:drop
{...$$restProps}
+75 -7
View File
@@ -1,22 +1,47 @@
import type { Project, Viewport, XYPosition, ZoomInOut } from '@reactflow/system';
import { get, writable, type Writable } from 'svelte/store';
import type {
Project,
SetCenterOptions,
Viewport,
ViewportHelperFunctionOptions,
XYPosition,
ZoomInOut
} from '@reactflow/system';
import { zoomIdentity } from 'd3-zoom';
import { useStore } from '$lib/store';
import type { FitViewOptions } from '$lib/types';
import { get, writable, type Writable } from 'svelte/store';
import type { SvelteFlowStore } from '$lib/store/types';
import { pointToRendererPoint } from '@reactflow/utils';
import { getD3Transition, pointToRendererPoint } from '@reactflow/utils';
export function useSvelteFlow(): {
zoomIn: ZoomInOut;
zoomOut: ZoomInOut;
setZoom: (zoomLevel: number, options?: ViewportHelperFunctionOptions) => void;
getZoom: () => number;
setCenter: (x: number, y: number, options?: SetCenterOptions) => void;
setViewport: (viewport: Viewport, options?: ViewportHelperFunctionOptions) => void;
getViewport: () => Viewport;
fitView: (options?: FitViewOptions) => void;
project: Project;
viewport: Writable<Viewport>;
nodes: SvelteFlowStore['nodes'];
edges: SvelteFlowStore['edges'];
project: Project;
} {
// how to get the new context here? fit view doesn't work, because the store is not updated (uses old nodes store)
const { zoomIn, zoomOut, fitView, snapGrid: snapGridStore, transform, nodes, edges } = useStore();
const {
zoomIn,
zoomOut,
fitView,
snapGrid,
transform,
width,
height,
maxZoom,
d3,
nodes,
edges
} = useStore();
const transformValues = get(transform);
const viewportWritable = writable({
@@ -36,10 +61,53 @@ export function useSvelteFlow(): {
return {
zoomIn,
zoomOut,
setZoom: (zoomLevel, options) => {
const { zoom, selection } = get(d3);
if (zoom && selection) {
zoom.scaleTo(getD3Transition(selection, options?.duration), zoomLevel);
}
},
getZoom: () => get(transform)[2],
setViewport: (viewport, options) => {
const [x, y, zoom] = get(transform);
const { zoom: d3Zoom, selection } = get(d3);
if (d3Zoom && selection) {
const nextTransform = zoomIdentity
.translate(viewport.x ?? x, viewport.y ?? y)
.scale(viewport.zoom ?? zoom);
d3Zoom.transform(getD3Transition(selection, options?.duration), nextTransform);
}
},
getViewport: () => {
const [x, y, zoom] = get(transform);
return { x, y, zoom };
},
setCenter: (x, y, options) => {
const _width = get(width);
const _height = get(height);
const _maxZoom = get(maxZoom);
const { zoom, selection } = get(d3);
if (zoom && selection) {
const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : _maxZoom;
const centerX = _width / 2 - x * nextZoom;
const centerY = _height / 2 - y * nextZoom;
const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom);
zoom.transform(getD3Transition(selection, options?.duration), transform);
}
},
fitView,
project: (position: XYPosition) => {
const snapGrid = get(snapGridStore);
return pointToRendererPoint(position, get(transform), snapGrid !== null, snapGrid || [1, 1]);
const _snapGrid = get(snapGrid);
return pointToRendererPoint(
position,
get(transform),
_snapGrid !== null,
_snapGrid || [1, 1]
);
},
nodes,
edges,
@@ -47,7 +47,7 @@
: [patternDimensions[0] / 2, patternDimensions[1] / 2];
</script>
<svg class={cc(['svelte-flow__background', className])} style={style}>
<svg class={cc(['svelte-flow__background', className])} {style} data-testid="svelte-flow__background">
<pattern
id={patternId}
x={$transform[0] % scaledGap[0]}
@@ -42,8 +42,8 @@
// onInteractiveChange?.(!isInteractive);
};
</script>
<Panel class="svelte-flow__controls" {position}>
<Panel class="svelte-flow__controls" {position} data-testid="svelte-flow__controls">
{#if showZoom}
<ControlButton
on:click={onZoomInHandler}
@@ -35,7 +35,7 @@
const defaultWidth = 200;
const defaultHeight = 150;
const { nodes, transform, width: containerWidth, height: containerHeight, nodeOrigin, id } = useStore();
const { nodes, transform, width: containerWidth, height: containerHeight, id } = useStore();
const nodeColorFunc = getAttrFunction(nodeColor);
@@ -52,7 +52,7 @@
height: $containerHeight / $transform[2]
};
$: boundingRect =
$nodes.length > 0 ? getBoundsOfRects(getRectOfNodes($nodes, $nodeOrigin), viewBB) : viewBB;
$nodes.length > 0 ? getBoundsOfRects(getRectOfNodes($nodes), viewBB) : viewBB;
$: elementWidth = width ?? defaultWidth;
$: elementHeight = height ?? defaultHeight;
$: scaledWidth = boundingRect.width / elementWidth;
@@ -71,6 +71,7 @@
{position}
class={cc(['svelte-flow__minimap', className])}
style={`background-color: ${bgColor}; ${style}`}
data-testid="svelte-flow__minimap"
>
<svg
width={elementWidth}
@@ -83,7 +84,7 @@
{#each $nodes as node (node.id)}
{#if node.width && node.height}
{@const pos = getNodePositionWithOrigin(node, $nodeOrigin).positionAbsolute}
{@const pos = getNodePositionWithOrigin(node).positionAbsolute}
<MinimapNode
x={pos.x}
y={pos.y}
+7 -6
View File
@@ -127,19 +127,20 @@ export function createStore(params: CreateStoreParams): SvelteFlowStore {
store.nodes.set(nextNodes);
}
function zoomIn(options?: ViewportHelperFunctionOptions) {
function zoomBy(factor: number, options?: ViewportHelperFunctionOptions) {
const { zoom: d3Zoom, selection: d3Selection } = get(store.d3);
if (d3Zoom && d3Selection) {
d3Zoom.scaleBy(getD3Transition(d3Selection, options?.duration), 1.2);
d3Zoom.scaleBy(getD3Transition(d3Selection, options?.duration), factor);
}
}
function zoomIn(options?: ViewportHelperFunctionOptions) {
zoomBy(1.2, options);
}
function zoomOut(options?: ViewportHelperFunctionOptions) {
const { zoom: d3Zoom, selection: d3Selection } = get(store.d3);
if (d3Zoom && d3Selection) {
d3Zoom.scaleBy(getD3Transition(d3Selection, options?.duration), 1 / 1.2);
}
zoomBy(1 / 1.2, options);
}
function setMinZoom(minZoom: number) {
@@ -3,14 +3,18 @@
useSvelteFlow
} from '../../lib/index';
const { zoomIn, zoomOut, fitView, viewport, nodes } = useSvelteFlow();
const { zoomIn, zoomOut, setZoom, fitView, setCenter, setViewport, getViewport, viewport, nodes } = useSvelteFlow();
</script>
<aside>
<div class="label">Functions:</div>
<button on:click={() => zoomIn()}>zoom in</button>
<button on:click={() => zoomOut({ duration: 1000 })}>zoom out transition</button>
<button on:click={() => setZoom(2)}>set zoom</button>
<button on:click={() => fitView()}>fitView</button>
<button on:click={() => setCenter(0, 0)}>setCenter 0, 0</button>
<button on:click={() => setViewport({ x: 100, y: 100, zoom: 2 })}>setViewport</button>
<button on:click={() => console.log(getViewport())}>getViewport</button>
<div class="label">Nodes:</div>
{#each $nodes as node (node.id)}