update: Add effect scope to useZoomPanHelper.ts
* Easier cleanup after zoompanhelper has been initialized
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { zoomIdentity } from 'd3-zoom';
|
import { zoomIdentity } from 'd3-zoom';
|
||||||
import { computed, inject } from 'vue';
|
import { computed, effectScope, inject } from 'vue';
|
||||||
import { createEventHook, EventHookOn, until } from '@vueuse/core';
|
import { createEventHook, EventHookOn, until } from '@vueuse/core';
|
||||||
import { Selection, ZoomBehavior } from 'd3';
|
import { Selection, ZoomBehavior } from 'd3';
|
||||||
import { getRectOfNodes, pointToRendererPoint, getTransformForBounds } from '../utils/graph';
|
import { getRectOfNodes, pointToRendererPoint, getTransformForBounds } from '../utils/graph';
|
||||||
@@ -10,73 +10,79 @@ const DEFAULT_PADDING = 0.1;
|
|||||||
const useZoomPanHelper = (): { onReady: EventHookOn<ZoomPanHelperFunctions> } => {
|
const useZoomPanHelper = (): { onReady: EventHookOn<ZoomPanHelperFunctions> } => {
|
||||||
const zoomPanHelperEvent = createEventHook<ZoomPanHelperFunctions>();
|
const zoomPanHelperEvent = createEventHook<ZoomPanHelperFunctions>();
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!;
|
||||||
const d3Zoom = computed(() => store.d3Zoom);
|
|
||||||
const d3Selection = computed(() => store.d3Selection);
|
|
||||||
|
|
||||||
const createZoomPanHelper = (
|
const scope = effectScope();
|
||||||
d3Zoom: ZoomBehavior<Element, unknown>,
|
scope.run(() => {
|
||||||
d3Selection: Selection<Element, any, any, any>
|
const d3Zoom = computed(() => store.d3Zoom);
|
||||||
): ZoomPanHelperFunctions => ({
|
const d3Selection = computed(() => store.d3Selection);
|
||||||
zoomIn: () => d3Zoom?.scaleBy(d3Selection, 1.2),
|
|
||||||
zoomOut: () => d3Zoom?.scaleBy(d3Selection, 1 / 1.2),
|
|
||||||
zoomTo: (zoomLevel: number) => d3Zoom.scaleTo(d3Selection, zoomLevel),
|
|
||||||
transform: (transform: FlowTransform) => {
|
|
||||||
const nextTransform = zoomIdentity.translate(transform.x, transform.y).scale(transform.zoom);
|
|
||||||
|
|
||||||
d3Zoom.transform(d3Selection, nextTransform);
|
const createZoomPanHelper = (
|
||||||
},
|
d3Zoom: ZoomBehavior<Element, unknown>,
|
||||||
fitView: (options: FitViewParams = { padding: DEFAULT_PADDING, includeHiddenNodes: false }) => {
|
d3Selection: Selection<Element, any, any, any>
|
||||||
const { nodes, width, height, minZoom, maxZoom } = store;
|
): ZoomPanHelperFunctions => ({
|
||||||
|
zoomIn: () => d3Zoom?.scaleBy(d3Selection, 1.2),
|
||||||
|
zoomOut: () => d3Zoom?.scaleBy(d3Selection, 1 / 1.2),
|
||||||
|
zoomTo: (zoomLevel: number) => d3Zoom.scaleTo(d3Selection, zoomLevel),
|
||||||
|
transform: (transform: FlowTransform) => {
|
||||||
|
const nextTransform = zoomIdentity.translate(transform.x, transform.y).scale(transform.zoom);
|
||||||
|
|
||||||
if (!nodes.length) {
|
d3Zoom.transform(d3Selection, nextTransform);
|
||||||
return;
|
},
|
||||||
}
|
fitView: (options: FitViewParams = { padding: DEFAULT_PADDING, includeHiddenNodes: false }) => {
|
||||||
|
const { nodes, width, height, minZoom, maxZoom } = store;
|
||||||
|
|
||||||
const bounds = getRectOfNodes(options.includeHiddenNodes ? nodes : nodes.filter((node) => !node.isHidden));
|
if (!nodes.length) {
|
||||||
const [x, y, zoom] = getTransformForBounds(
|
return;
|
||||||
bounds,
|
}
|
||||||
width,
|
|
||||||
height,
|
|
||||||
options.minZoom || minZoom,
|
|
||||||
options.maxZoom || maxZoom,
|
|
||||||
options.padding || DEFAULT_PADDING
|
|
||||||
);
|
|
||||||
const transform = zoomIdentity.translate(x, y).scale(zoom);
|
|
||||||
|
|
||||||
d3Zoom?.transform(d3Selection, transform);
|
const bounds = getRectOfNodes(options.includeHiddenNodes ? nodes : nodes.filter((node) => !node.isHidden));
|
||||||
},
|
const [x, y, zoom] = getTransformForBounds(
|
||||||
setCenter: (x: number, y: number, zoom?: number) => {
|
bounds,
|
||||||
const { width, height, maxZoom } = store;
|
width,
|
||||||
|
height,
|
||||||
|
options.minZoom || minZoom,
|
||||||
|
options.maxZoom || maxZoom,
|
||||||
|
options.padding || DEFAULT_PADDING
|
||||||
|
);
|
||||||
|
const transform = zoomIdentity.translate(x, y).scale(zoom);
|
||||||
|
|
||||||
const nextZoom = typeof zoom !== 'undefined' ? zoom : maxZoom;
|
d3Zoom?.transform(d3Selection, transform);
|
||||||
const centerX = width / 2 - x * nextZoom;
|
},
|
||||||
const centerY = height / 2 - y * nextZoom;
|
setCenter: (x: number, y: number, zoom?: number) => {
|
||||||
const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom);
|
const { width, height, maxZoom } = store;
|
||||||
|
|
||||||
d3Zoom.transform(d3Selection, transform);
|
const nextZoom = typeof zoom !== 'undefined' ? zoom : maxZoom;
|
||||||
},
|
const centerX = width / 2 - x * nextZoom;
|
||||||
fitBounds: (bounds: Rect, padding = DEFAULT_PADDING) => {
|
const centerY = height / 2 - y * nextZoom;
|
||||||
const { width, height, minZoom, maxZoom } = store;
|
const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom);
|
||||||
const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom, maxZoom, padding);
|
|
||||||
const transform = zoomIdentity.translate(x, y).scale(zoom);
|
|
||||||
|
|
||||||
d3Zoom.transform(d3Selection, transform);
|
d3Zoom.transform(d3Selection, transform);
|
||||||
},
|
},
|
||||||
project: (position: XYPosition) => {
|
fitBounds: (bounds: Rect, padding = DEFAULT_PADDING) => {
|
||||||
const { transform, snapToGrid, snapGrid } = store;
|
const { width, height, minZoom, maxZoom } = store;
|
||||||
return pointToRendererPoint(position, transform, snapToGrid, snapGrid);
|
const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom, maxZoom, padding);
|
||||||
}
|
const transform = zoomIdentity.translate(x, y).scale(zoom);
|
||||||
});
|
|
||||||
|
|
||||||
until(d3Zoom)
|
d3Zoom.transform(d3Selection, transform);
|
||||||
.toBeTruthy()
|
},
|
||||||
.then(() => {
|
project: (position: XYPosition) => {
|
||||||
if (d3Zoom.value && d3Selection.value) {
|
const { transform, snapToGrid, snapGrid } = store;
|
||||||
zoomPanHelperEvent.trigger(createZoomPanHelper(d3Zoom.value, d3Selection.value));
|
return pointToRendererPoint(position, transform, snapToGrid, snapGrid);
|
||||||
} else {
|
|
||||||
throw Error('D3 could not be initialized.');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
until(d3Zoom)
|
||||||
|
.toBeTruthy()
|
||||||
|
.then(() => {
|
||||||
|
if (d3Zoom.value && d3Selection.value) {
|
||||||
|
zoomPanHelperEvent.trigger(createZoomPanHelper(d3Zoom.value, d3Selection.value));
|
||||||
|
} else {
|
||||||
|
throw Error('D3 could not be initialized.');
|
||||||
|
}
|
||||||
|
scope.stop();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
onReady: zoomPanHelperEvent.on
|
onReady: zoomPanHelperEvent.on
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user