event.target.closest(`.${className}`);
export default function zoom(
domNode: Element,
- { transformStore }: { transformStore: Writable }
+ {
+ transformStore,
+ d3Store
+ }: {
+ transformStore: Writable;
+ d3Store: Writable<{ zoom: D3ZoomInstance | null; selection: D3SelectionInstance | null }>;
+ }
) {
const d3ZoomInstance = d3Zoom();
const selection = select(domNode).call(d3ZoomInstance);
@@ -26,6 +32,11 @@ export default function zoom(
d3ZoomHandler!.call(this, event, d);
});
+ d3Store.set({
+ zoom: d3ZoomInstance,
+ selection
+ });
+
d3ZoomInstance.on('zoom', (event: D3ZoomEvent) => {
transformStore.set([event.transform.x, event.transform.y, event.transform.k]);
});
diff --git a/packages/svelte/src/lib/store/index.ts b/packages/svelte/src/lib/store/index.ts
index 865a6f14..ac038588 100644
--- a/packages/svelte/src/lib/store/index.ts
+++ b/packages/svelte/src/lib/store/index.ts
@@ -7,8 +7,12 @@ import {
type NodeDimensionUpdate,
type Edge,
Position,
- internalsSymbol
+ internalsSymbol,
+ type NodeOrigin,
+ type D3ZoomInstance,
+ type D3SelectionInstance
} from '@reactflow/system';
+import { fitView } from '@reactflow/utils';
import { getDimensions, getHandleBounds } from '../../utils';
import {
@@ -24,6 +28,7 @@ type CreateStoreProps = {
nodes: Node[];
edges: Edge[];
fitView: boolean;
+ nodeOrigin?: NodeOrigin;
transform?: Transform;
};
@@ -37,6 +42,10 @@ type SvelteFlowStore = {
edgesStore: Writable;
heightStore: Writable;
widthStore: Writable;
+ d3Store: Writable<{
+ zoom: D3ZoomInstance | null;
+ selection: D3SelectionInstance | null;
+ }>;
transformStore: Writable;
edgesWithDataStore: Readable;
updateNodePositions: (
@@ -51,12 +60,15 @@ export function createStore({
nodes = [],
edges = [],
transform = [0, 0, 1],
+ nodeOrigin = [0, 0],
fitView: fitViewOnInit = false
}: CreateStoreProps): SvelteFlowStore {
const nodesStore = writable(nodes.map((n) => ({ ...n, positionAbsolute: n.position })));
const edgesStore = writable(edges);
const heightStore = writable(500);
const widthStore = writable(500);
+ const nodeOriginStore = writable(nodeOrigin);
+ const d3Store = writable({ zoom: null, selection: null });
let fitViewOnInitDone = false;
@@ -166,10 +178,29 @@ export function createStore({
return node;
});
- // const nextFitViewOnInitDone =
- // fitViewOnInitDone || (fitViewOnInit && !fitViewOnInitDone && fitView(get, { initial: true }));
+ const { zoom: d3Zoom, selection: d3Selection } = get(d3Store);
- // fitViewOnInitDone = nextFitViewOnInitDone;
+ const nextFitViewOnInitDone =
+ fitViewOnInitDone ||
+ (fitViewOnInit &&
+ !fitViewOnInitDone &&
+ !!d3Zoom &&
+ !!d3Selection &&
+ fitView(
+ {
+ nodes: get(nodesStore),
+ width: get(widthStore),
+ height: get(heightStore),
+ minZoom: 0.2,
+ maxZoom: 2,
+ d3Selection,
+ d3Zoom,
+ nodeOrigin: get(nodeOriginStore)
+ },
+ {}
+ ));
+
+ fitViewOnInitDone = nextFitViewOnInitDone;
nodesStore.set(nextNodes);
}
@@ -178,6 +209,7 @@ export function createStore({
nodesStore,
edgesStore,
transformStore,
+ d3Store,
heightStore,
widthStore,
edgesWithDataStore,
diff --git a/packages/system/src/types/general.ts b/packages/system/src/types/general.ts
index f628727e..3a488d88 100644
--- a/packages/system/src/types/general.ts
+++ b/packages/system/src/types/general.ts
@@ -70,6 +70,15 @@ export enum ConnectionMode {
export type OnConnect = (connection: Connection) => void;
+export type FitViewParams = {
+ nodes: Node[];
+ width: number;
+ height: number;
+ nodeOrigin: NodeOrigin;
+ d3Zoom: D3ZoomInstance;
+ d3Selection: D3SelectionInstance;
+};
+
export type FitViewOptions = {
padding?: number;
includeHiddenNodes?: boolean;
@@ -137,6 +146,9 @@ export type ViewportHelperFunctions = {
viewportInitialized: boolean;
};
+export type D3ZoomInstance = ZoomBehavior;
+export type D3SelectionInstance = D3Selection;
+
export type ReactFlowStore = {
rfId: string;
width: number;
@@ -152,8 +164,8 @@ export type ReactFlowStore = {
paneDragging: boolean;
noPanClassName: string;
- d3Zoom: ZoomBehavior | null;
- d3Selection: D3Selection | null;
+ d3Zoom: D3ZoomInstance | null;
+ d3Selection: D3SelectionInstance | null;
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined;
minZoom: number;
maxZoom: number;
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 5897d059..05e0cad5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -310,7 +310,8 @@ importers:
packages/svelte:
specifiers:
- '@reactflow/system': workspace:*
+ '@reactflow/system': workspace:11.5.5
+ '@reactflow/utils': workspace:^11.5.5
'@sveltejs/adapter-auto': ^2.0.0
'@sveltejs/kit': ^1.5.6
'@sveltejs/package': ^1.0.2
@@ -334,6 +335,7 @@ importers:
vite: ^4.1.1
dependencies:
'@reactflow/system': link:../system
+ '@reactflow/utils': link:../utils
cc: registry.npmjs.org/cc/3.0.1
d3-drag: registry.npmjs.org/d3-drag/3.0.0
d3-selection: registry.npmjs.org/d3-selection/3.0.0
@@ -399,6 +401,7 @@ importers:
'@types/node': ^18.7.16
'@types/react': '>=17'
'@types/react-dom': '>=17'
+ d3-zoom: ^3.0.0
react: ^18.2.0
typescript: ^4.9.4
dependencies:
@@ -407,6 +410,7 @@ importers:
'@types/d3-drag': registry.npmjs.org/@types/d3-drag/3.0.1
'@types/d3-selection': registry.npmjs.org/@types/d3-selection/3.0.3
'@types/d3-zoom': registry.npmjs.org/@types/d3-zoom/3.0.1
+ d3-zoom: registry.npmjs.org/d3-zoom/3.0.0
devDependencies:
'@reactflow/eslint-config': link:../../tooling/eslint-config
'@reactflow/rollup-config': link:../../tooling/rollup-config