feat(svelte): add functions to useSvelteFlow
This commit is contained in:
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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)}
|
||||
|
||||
Reference in New Issue
Block a user