feat(utils): add vanilla drag and panzoom (#3108)
* refactor(panzoom): create vanilla helper * feat(svelte): add interaction example * refactor(panzoom): cleanup * Update function-runner.ts * refactor(panzoom): cleanup * refactor(panzoom): rename functions * feat(utils): add vanilla drag helper (#3107) * feat(utils): add vanilla drag helper * refactor(drag): cleanup * refactor(drag): cleanup * chore(packages): cleanup * refactor(panzoom): cleanup and simplify
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
import { getContext } from 'svelte';
|
||||
import { derived, get } from 'svelte/store';
|
||||
import { zoomIdentity } from 'd3-zoom';
|
||||
import {
|
||||
type NodeDragItem,
|
||||
type UpdateNodePositions,
|
||||
type NodeDimensionUpdate,
|
||||
internalsSymbol,
|
||||
type ViewportHelperFunctionOptions,
|
||||
@@ -13,10 +12,10 @@ import {
|
||||
import {
|
||||
createMarkerIds,
|
||||
fitView as fitViewUtil,
|
||||
getD3Transition,
|
||||
getDimensions,
|
||||
getElementsToRemove,
|
||||
getHandleBounds
|
||||
getHandleBounds,
|
||||
infiniteExtent
|
||||
} from '@reactflow/utils';
|
||||
|
||||
import { addEdge as addEdgeUtil } from '$lib/utils';
|
||||
@@ -60,7 +59,7 @@ export function createStore(params: CreateStoreParams): SvelteFlowStore {
|
||||
store.edges.set(addEdgeUtil(edgeParams, edges));
|
||||
}
|
||||
|
||||
function updateNodePositions(nodeDragItems: NodeDragItem[], dragging = false) {
|
||||
const updateNodePositions: UpdateNodePositions = (nodeDragItems, dragging = false) => {
|
||||
store.nodes.update((nds) => {
|
||||
return nds.map((n) => {
|
||||
const nodeDragItem = nodeDragItems.find((ndi) => ndi.id === n.id);
|
||||
@@ -77,7 +76,7 @@ export function createStore(params: CreateStoreParams): SvelteFlowStore {
|
||||
return n;
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function updateNodeDimensions(updates: NodeDimensionUpdate[]) {
|
||||
const viewportNode = document?.querySelector('.svelte-flow__viewport');
|
||||
@@ -123,21 +122,21 @@ export function createStore(params: CreateStoreParams): SvelteFlowStore {
|
||||
return node;
|
||||
});
|
||||
|
||||
const { zoom: d3Zoom, selection: d3Selection } = get(store.d3);
|
||||
const panZoom = get(store.panZoom);
|
||||
|
||||
const fitViewOnInitDone =
|
||||
get(store.fitViewOnInitDone) ||
|
||||
(get(store.fitViewOnInit) && !!d3Zoom && !!d3Selection && fitView({ nodes: nextNodes }));
|
||||
(get(store.fitViewOnInit) && !!panZoom && fitView({ nodes: nextNodes }));
|
||||
|
||||
store.fitViewOnInitDone.set(fitViewOnInitDone);
|
||||
store.nodes.set(nextNodes);
|
||||
}
|
||||
|
||||
function zoomBy(factor: number, options?: ViewportHelperFunctionOptions) {
|
||||
const { zoom: d3Zoom, selection: d3Selection } = get(store.d3);
|
||||
const panZoom = get(store.panZoom);
|
||||
|
||||
if (d3Zoom && d3Selection) {
|
||||
d3Zoom.scaleBy(getD3Transition(d3Selection, options?.duration), factor);
|
||||
if (panZoom) {
|
||||
panZoom.scaleBy(factor, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,28 +149,36 @@ export function createStore(params: CreateStoreParams): SvelteFlowStore {
|
||||
}
|
||||
|
||||
function setMinZoom(minZoom: number) {
|
||||
const d3Zoom = get(store.d3).zoom;
|
||||
|
||||
if (d3Zoom) {
|
||||
d3Zoom?.scaleExtent([minZoom, get(store.maxZoom)]);
|
||||
const panZoom = get(store.panZoom);
|
||||
|
||||
if (panZoom) {
|
||||
panZoom.setScaleExtent([minZoom, get(store.maxZoom)]);
|
||||
store.minZoom.set(minZoom);
|
||||
}
|
||||
}
|
||||
|
||||
function setMaxZoom(maxZoom: number) {
|
||||
const d3Zoom = get(store.d3).zoom;
|
||||
if (d3Zoom) {
|
||||
d3Zoom?.scaleExtent([get(store.minZoom), maxZoom]);
|
||||
const panZoom = get(store.panZoom);
|
||||
|
||||
if (panZoom) {
|
||||
panZoom.setScaleExtent([get(store.minZoom), maxZoom]);
|
||||
store.maxZoom.set(maxZoom);
|
||||
}
|
||||
}
|
||||
|
||||
function fitView(options?: FitViewOptions) {
|
||||
const { zoom: d3Zoom, selection: d3Selection } = get(store.d3);
|
||||
function setTranslateExtent(extent: CoordinateExtent) {
|
||||
const panZoom = get(store.panZoom);
|
||||
|
||||
if (!d3Zoom || !d3Selection) {
|
||||
if (panZoom) {
|
||||
panZoom.setTranslateExtent(extent);
|
||||
store.translateExtent.set(extent);
|
||||
}
|
||||
}
|
||||
|
||||
function fitView(options?: FitViewOptions) {
|
||||
const panZoom = get(store.panZoom);
|
||||
|
||||
if (!panZoom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -184,8 +191,7 @@ export function createStore(params: CreateStoreParams): SvelteFlowStore {
|
||||
height: get(store.height),
|
||||
minZoom: 0.2,
|
||||
maxZoom: 2,
|
||||
d3Selection,
|
||||
d3Zoom,
|
||||
panZoom,
|
||||
nodeOrigin: [0, 0]
|
||||
},
|
||||
{}
|
||||
@@ -203,7 +209,7 @@ export function createStore(params: CreateStoreParams): SvelteFlowStore {
|
||||
return item;
|
||||
}
|
||||
|
||||
function resetSelectedElements() {
|
||||
function unselectNodesAndEdges() {
|
||||
store.nodes.update((ns) => ns.map(resetSelectedItem));
|
||||
store.edges.update((es) => es.map(resetSelectedItem));
|
||||
}
|
||||
@@ -267,29 +273,35 @@ export function createStore(params: CreateStoreParams): SvelteFlowStore {
|
||||
}
|
||||
|
||||
function panBy(delta: XYPosition) {
|
||||
const { zoom: d3Zoom, selection: d3Selection } = get(store.d3);
|
||||
const panZoom = get(store.panZoom);
|
||||
const transform = get(store.transform);
|
||||
const width = get(store.width);
|
||||
const height = get(store.height);
|
||||
|
||||
if (!d3Zoom || !d3Selection || (!delta.x && !delta.y)) {
|
||||
return;
|
||||
if (!panZoom || (!delta.x && !delta.y)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const nextTransform = zoomIdentity
|
||||
.translate(transform[0] + delta.x, transform[1] + delta.y)
|
||||
.scale(transform[2]);
|
||||
const nextViewport = panZoom.setViewportConstrained(
|
||||
{
|
||||
x: transform[0] + delta.x,
|
||||
y: transform[1] + delta.y,
|
||||
zoom: transform[2]
|
||||
},
|
||||
[
|
||||
[0, 0],
|
||||
[width, height]
|
||||
],
|
||||
infiniteExtent
|
||||
);
|
||||
|
||||
const extent: CoordinateExtent = [
|
||||
[0, 0],
|
||||
[width, height]
|
||||
];
|
||||
const transformChanged =
|
||||
!!nextViewport &&
|
||||
(nextViewport.x !== transform[0] ||
|
||||
nextViewport.y !== transform[1] ||
|
||||
nextViewport.k !== transform[2]);
|
||||
|
||||
const constrainedTransform = d3Zoom?.constrain()(nextTransform, extent, [
|
||||
[Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
|
||||
[Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY]
|
||||
]);
|
||||
d3Zoom.transform(d3Selection, constrainedTransform);
|
||||
return transformChanged;
|
||||
}
|
||||
|
||||
function updateConnection(connectionUpdate: Partial<ConnectionData> | null) {
|
||||
@@ -319,7 +331,7 @@ export function createStore(params: CreateStoreParams): SvelteFlowStore {
|
||||
store.snapGrid.set(null);
|
||||
store.isValidConnection.set(() => true);
|
||||
|
||||
resetSelectedElements();
|
||||
unselectNodesAndEdges();
|
||||
cancelConnection();
|
||||
}
|
||||
|
||||
@@ -346,7 +358,8 @@ export function createStore(params: CreateStoreParams): SvelteFlowStore {
|
||||
fitView,
|
||||
setMinZoom,
|
||||
setMaxZoom,
|
||||
resetSelectedElements,
|
||||
setTranslateExtent,
|
||||
unselectNodesAndEdges,
|
||||
addSelectedNodes,
|
||||
addSelectedEdges,
|
||||
panBy,
|
||||
|
||||
Reference in New Issue
Block a user