feat(react/svelte): controlled viewport (#3461)
* feat(react): controlled viewport * refactor(react): cleanup viewport * feat(svelte): two way data binding for viewport, cleanup transform <> viewport stuff * refactor(svelte): viewport stuff
This commit is contained in:
@@ -24,6 +24,7 @@ export default function drag(domNode: Element, params: UseDragParams) {
|
||||
onDragStop,
|
||||
getStoreItems: () => {
|
||||
const snapGrid = get(store.snapGrid);
|
||||
const vp = get(store.viewport);
|
||||
|
||||
return {
|
||||
nodes: get(store.nodes),
|
||||
@@ -34,7 +35,7 @@ export default function drag(domNode: Element, params: UseDragParams) {
|
||||
nodeOrigin: [0, 0],
|
||||
multiSelectionActive: false,
|
||||
domNode: get(store.domNode),
|
||||
transform: get(store.transform),
|
||||
transform: [vp.x, vp.y, vp.zoom],
|
||||
autoPanOnNodeDrag: get(store.autoPanOnNodeDrag),
|
||||
nodesDraggable: get(store.nodesDraggable),
|
||||
selectNodesOnDrag: get(store.selectNodesOnDrag),
|
||||
|
||||
@@ -5,15 +5,14 @@ import {
|
||||
type CoordinateExtent,
|
||||
type OnPanZoom,
|
||||
type PanZoomInstance,
|
||||
type Transform,
|
||||
type Viewport
|
||||
} from '@xyflow/system';
|
||||
|
||||
type ZoomParams = {
|
||||
transform: Writable<Transform>;
|
||||
viewport: Writable<Viewport>;
|
||||
initialViewport: Viewport;
|
||||
minZoom: number;
|
||||
maxZoom: number;
|
||||
initialViewport: Viewport;
|
||||
dragging: Writable<boolean>;
|
||||
onPanZoomStart?: OnPanZoom;
|
||||
onPanZoom?: OnPanZoom;
|
||||
@@ -39,7 +38,7 @@ type ZoomParams = {
|
||||
};
|
||||
|
||||
export default function zoom(domNode: Element, params: ZoomParams) {
|
||||
const { panZoom, minZoom, maxZoom, initialViewport, transform, dragging, translateExtent } =
|
||||
const { panZoom, minZoom, maxZoom, initialViewport, viewport, dragging, translateExtent } =
|
||||
params;
|
||||
|
||||
const panZoomInstance = XYPanZoom({
|
||||
@@ -48,11 +47,12 @@ export default function zoom(domNode: Element, params: ZoomParams) {
|
||||
maxZoom,
|
||||
translateExtent,
|
||||
viewport: initialViewport,
|
||||
onTransformChange: transform.set,
|
||||
onTransformChange: (transform) =>
|
||||
viewport.set({ x: transform[0], y: transform[1], zoom: transform[2] }),
|
||||
onDraggingChange: dragging.set
|
||||
});
|
||||
const { x, y, zoom } = panZoomInstance.getViewport();
|
||||
transform.set([x, y, zoom]);
|
||||
const currentViewport = panZoomInstance.getViewport();
|
||||
viewport.set(currentViewport);
|
||||
panZoom.set(panZoomInstance);
|
||||
|
||||
panZoomInstance.update(params);
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
domNode,
|
||||
nodes,
|
||||
connectionRadius,
|
||||
transform,
|
||||
viewport,
|
||||
isValidConnection,
|
||||
lib,
|
||||
addEdge,
|
||||
@@ -80,7 +80,7 @@
|
||||
onConnectEnd: (event) => {
|
||||
dispatch('connectend', { event });
|
||||
},
|
||||
getTransform: () => $transform
|
||||
getTransform: () => [$viewport.x, $viewport.y, $viewport.zoom]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
const {
|
||||
nodes,
|
||||
edges,
|
||||
transform,
|
||||
viewport,
|
||||
dragging,
|
||||
elementsSelectable,
|
||||
selectionRect,
|
||||
@@ -120,7 +120,7 @@
|
||||
selectedNodes = getNodesInside<Node>(
|
||||
$nodes,
|
||||
nextUserSelectRect,
|
||||
$transform,
|
||||
[$viewport.x, $viewport.y, $viewport.zoom],
|
||||
$selectionMode === SelectionMode.Partial,
|
||||
true
|
||||
);
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
export let minZoom: $$Props['minZoom'] = undefined;
|
||||
export let maxZoom: $$Props['maxZoom'] = undefined;
|
||||
export let initialViewport: Viewport = { x: 0, y: 0, zoom: 1 };
|
||||
export let viewport: $$Props['viewport'] = undefined;
|
||||
export let nodeTypes: $$Props['nodeTypes'] = undefined;
|
||||
export let edgeTypes: $$Props['edgeTypes'] = undefined;
|
||||
export let selectionKey: $$Props['selectionKey'] = undefined;
|
||||
@@ -81,6 +82,7 @@
|
||||
|
||||
store.syncNodeStores(nodes);
|
||||
store.syncEdgeStores(edges);
|
||||
store.syncViewport(viewport);
|
||||
|
||||
if (fitView !== undefined) {
|
||||
store.fitViewOnInit.set(fitView);
|
||||
|
||||
@@ -47,6 +47,7 @@ export type SvelteFlowProps = DOMAttributes<HTMLDivElement> & {
|
||||
minZoom?: number;
|
||||
maxZoom?: number;
|
||||
initialViewport?: Viewport;
|
||||
viewport?: Writable<Viewport>;
|
||||
connectionRadius?: number;
|
||||
connectionMode?: ConnectionMode;
|
||||
connectionLineStyle?: string;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<script lang="ts">
|
||||
import { useStore } from '$lib/store';
|
||||
|
||||
const { transform } = useStore();
|
||||
const { viewport } = useStore();
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="svelte-flow__viewport xyflow__viewport"
|
||||
style="transform: translate({$transform[0]}px, {$transform[1]}px) scale({$transform[2]})"
|
||||
style="transform: translate({$viewport.x}px, {$viewport.y}px) scale({$viewport.zoom})"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
export let panOnScroll: $$Props['panOnScroll'];
|
||||
|
||||
const {
|
||||
transform,
|
||||
viewport,
|
||||
panZoom,
|
||||
selectionKeyPressed,
|
||||
minZoom,
|
||||
@@ -38,7 +38,7 @@
|
||||
<div
|
||||
class="svelte-flow__zoom"
|
||||
use:zoom={{
|
||||
transform,
|
||||
viewport,
|
||||
minZoom: $minZoom,
|
||||
maxZoom: $maxZoom,
|
||||
initialViewport: viewPort,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { get, writable, type Writable } from 'svelte/store';
|
||||
import { get, type Writable } from 'svelte/store';
|
||||
import {
|
||||
pointToRendererPoint,
|
||||
type Project,
|
||||
@@ -32,7 +32,7 @@ export function useSvelteFlow(): {
|
||||
zoomOut,
|
||||
fitView,
|
||||
snapGrid,
|
||||
transform,
|
||||
viewport,
|
||||
width,
|
||||
height,
|
||||
maxZoom,
|
||||
@@ -41,44 +41,26 @@ export function useSvelteFlow(): {
|
||||
edges
|
||||
} = useStore();
|
||||
|
||||
const transformValues = get(transform);
|
||||
const viewportWritable = writable({
|
||||
x: transformValues[0],
|
||||
y: transformValues[1],
|
||||
zoom: transformValues[2]
|
||||
});
|
||||
|
||||
transform.subscribe((ts) =>
|
||||
viewportWritable.set({
|
||||
x: ts[0],
|
||||
y: ts[1],
|
||||
zoom: ts[2]
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
zoomIn,
|
||||
zoomOut,
|
||||
setZoom: (zoomLevel, options) => {
|
||||
get(panZoom)?.scaleTo(zoomLevel, { duration: options?.duration });
|
||||
},
|
||||
getZoom: () => get(transform)[2],
|
||||
setViewport: (viewport, options) => {
|
||||
const [x, y, zoom] = get(transform);
|
||||
getZoom: () => get(viewport).zoom,
|
||||
setViewport: (vieport, options) => {
|
||||
const currentViewport = get(viewport);
|
||||
|
||||
get(panZoom)?.setViewport(
|
||||
{
|
||||
x: viewport.x ?? x,
|
||||
y: viewport.y ?? y,
|
||||
zoom: viewport.zoom ?? zoom
|
||||
x: vieport.x ?? currentViewport.x,
|
||||
y: vieport.y ?? currentViewport.y,
|
||||
zoom: vieport.zoom ?? currentViewport.zoom
|
||||
},
|
||||
{ duration: options?.duration }
|
||||
);
|
||||
},
|
||||
getViewport: () => {
|
||||
const [x, y, zoom] = get(transform);
|
||||
return { x, y, zoom };
|
||||
},
|
||||
getViewport: () => get(viewport),
|
||||
setCenter: (x, y, options) => {
|
||||
const _width = get(width);
|
||||
const _height = get(height);
|
||||
@@ -98,15 +80,12 @@ export function useSvelteFlow(): {
|
||||
fitView,
|
||||
project: (position: XYPosition) => {
|
||||
const _snapGrid = get(snapGrid);
|
||||
return pointToRendererPoint(
|
||||
position,
|
||||
get(transform),
|
||||
_snapGrid !== null,
|
||||
_snapGrid || [1, 1]
|
||||
);
|
||||
const { x, y, zoom } = get(viewport);
|
||||
|
||||
return pointToRendererPoint(position, [x, y, zoom], _snapGrid !== null, _snapGrid || [1, 1]);
|
||||
},
|
||||
nodes,
|
||||
edges,
|
||||
viewport: viewportWritable
|
||||
viewport: viewport
|
||||
};
|
||||
}
|
||||
|
||||
@@ -26,15 +26,15 @@
|
||||
let className: $$Props['class'] = '';
|
||||
export { className as class };
|
||||
|
||||
const { transform, flowId } = useStore();
|
||||
const { viewport, flowId } = useStore();
|
||||
const patternSize = size || defaultSize[variant!];
|
||||
const isDots = variant === BackgroundVariant.Dots;
|
||||
const isCross = variant === BackgroundVariant.Cross;
|
||||
const gapXY: number[] = Array.isArray(gap!) ? gap! : [gap!, gap!];
|
||||
|
||||
$: patternId = `background-pattern-${$flowId}`;
|
||||
$: scaledGap = [gapXY[0] * $transform[2] || 1, gapXY[1] * $transform[2] || 1];
|
||||
$: scaledSize = patternSize * $transform[2];
|
||||
$: scaledGap = [gapXY[0] * $viewport.zoom || 1, gapXY[1] * $viewport.zoom || 1];
|
||||
$: scaledSize = patternSize * $viewport.zoom;
|
||||
$: patternDimensions = (isCross ? [scaledSize, scaledSize] : scaledGap) as [number, number];
|
||||
$: patternOffset = isDots
|
||||
? [scaledSize / 2, scaledSize / 2]
|
||||
@@ -49,8 +49,8 @@
|
||||
>
|
||||
<pattern
|
||||
id={patternId}
|
||||
x={$transform[0] % scaledGap[0]}
|
||||
y={$transform[1] % scaledGap[1]}
|
||||
x={$viewport.x % scaledGap[0]}
|
||||
y={$viewport.y % scaledGap[1]}
|
||||
width={scaledGap[0]}
|
||||
height={scaledGap[1]}
|
||||
patternUnits="userSpaceOnUse"
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
zoomIn,
|
||||
zoomOut,
|
||||
fitView,
|
||||
transform,
|
||||
viewport,
|
||||
minZoom,
|
||||
maxZoom,
|
||||
nodesDraggable,
|
||||
@@ -43,8 +43,8 @@
|
||||
};
|
||||
|
||||
$: isInteractive = $nodesDraggable || $nodesConnectable || $elementsSelectable;
|
||||
$: minZoomReached = $transform[2] <= $minZoom;
|
||||
$: maxZoomReached = $transform[2] >= $maxZoom;
|
||||
$: minZoomReached = $viewport.zoom <= $minZoom;
|
||||
$: maxZoomReached = $viewport.zoom >= $maxZoom;
|
||||
|
||||
const onZoomInHandler = () => {
|
||||
zoomIn();
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
const defaultHeight = 150;
|
||||
const {
|
||||
nodes,
|
||||
transform,
|
||||
viewport,
|
||||
width: containerWidth,
|
||||
height: containerHeight,
|
||||
flowId,
|
||||
@@ -59,10 +59,10 @@
|
||||
const labelledBy = `svelte-flow__minimap-desc-${$flowId}`;
|
||||
|
||||
$: viewBB = {
|
||||
x: -$transform[0] / $transform[2],
|
||||
y: -$transform[1] / $transform[2],
|
||||
width: $containerWidth / $transform[2],
|
||||
height: $containerHeight / $transform[2]
|
||||
x: -$viewport.x / $viewport.zoom,
|
||||
y: -$viewport.y / $viewport.zoom,
|
||||
width: $containerWidth / $viewport.zoom,
|
||||
height: $containerHeight / $viewport.zoom
|
||||
};
|
||||
$: boundingRect = $nodes.length > 0 ? getBoundsOfRects(getRectOfNodes($nodes), viewBB) : viewBB;
|
||||
$: elementWidth = width ?? defaultWidth;
|
||||
@@ -100,7 +100,7 @@
|
||||
style:--minimap-mask-stroke-width-props={maskStrokeWidth}
|
||||
use:interactive={{
|
||||
panZoom: $panZoom,
|
||||
transform,
|
||||
viewport,
|
||||
getViewScale,
|
||||
translateExtent: $translateExtent,
|
||||
width: $containerWidth,
|
||||
|
||||
@@ -2,13 +2,13 @@ import { get, type Writable } from 'svelte/store';
|
||||
import {
|
||||
XYMinimap,
|
||||
type PanZoomInstance,
|
||||
type Transform,
|
||||
type Viewport,
|
||||
type XYMinimapUpdate
|
||||
} from '@xyflow/system';
|
||||
|
||||
export type UseInteractiveParams = {
|
||||
panZoom: PanZoomInstance;
|
||||
transform: Writable<Transform>;
|
||||
viewport: Writable<Viewport>;
|
||||
getViewScale: () => number;
|
||||
} & XYMinimapUpdate;
|
||||
|
||||
@@ -16,7 +16,10 @@ export default function interactive(domNode: Element, params: UseInteractivePara
|
||||
const minimap = XYMinimap({
|
||||
domNode,
|
||||
panZoom: params.panZoom,
|
||||
getTransform: () => get(params.transform),
|
||||
getTransform: () => {
|
||||
const viewport = get(params.viewport);
|
||||
return [viewport.x, viewport.y, viewport.zoom];
|
||||
},
|
||||
getViewScale: params.getViewScale
|
||||
});
|
||||
|
||||
|
||||
@@ -57,9 +57,9 @@ export function getDerivedConnectionProps(
|
||||
store.connectionLineType,
|
||||
store.connectionMode,
|
||||
store.nodes,
|
||||
store.transform
|
||||
store.viewport
|
||||
],
|
||||
([connection, connectionLineType, connectionMode, nodes, transform]) => {
|
||||
([connection, connectionLineType, connectionMode, nodes, viewport]) => {
|
||||
if (!connection.connectionStartHandle?.nodeId) {
|
||||
return initConnectionProps;
|
||||
}
|
||||
@@ -91,8 +91,8 @@ export function getDerivedConnectionProps(
|
||||
sourceX: fromX,
|
||||
sourceY: fromY,
|
||||
sourcePosition: fromPosition,
|
||||
targetX: ((connection.connectionPosition?.x ?? 0) - transform[0]) / transform[2],
|
||||
targetY: ((connection.connectionPosition?.y ?? 0) - transform[1]) / transform[2],
|
||||
targetX: ((connection.connectionPosition?.x ?? 0) - viewport.x) / viewport.zoom,
|
||||
targetY: ((connection.connectionPosition?.y ?? 0) - viewport.y) / viewport.zoom,
|
||||
targetPosition: toPosition
|
||||
};
|
||||
|
||||
|
||||
@@ -10,11 +10,11 @@ export function getEdgeTree(store: SvelteFlowStoreState) {
|
||||
store.edges,
|
||||
store.nodes,
|
||||
store.onlyRenderVisibleElements,
|
||||
store.transform,
|
||||
store.viewport,
|
||||
store.width,
|
||||
store.height
|
||||
],
|
||||
([edges, nodes, onlyRenderVisibleElements, transform, width, height]) => {
|
||||
([edges, nodes, onlyRenderVisibleElements, viewport, width, height]) => {
|
||||
const visibleEdges =
|
||||
onlyRenderVisibleElements && width && height
|
||||
? edges.filter((edge) => {
|
||||
@@ -29,7 +29,7 @@ export function getEdgeTree(store: SvelteFlowStoreState) {
|
||||
targetNode,
|
||||
width,
|
||||
height,
|
||||
transform
|
||||
transform: [viewport.x, viewport.y, viewport.zoom]
|
||||
})
|
||||
);
|
||||
})
|
||||
|
||||
@@ -22,7 +22,7 @@ import { addEdge as addEdgeUtil } from '$lib/utils';
|
||||
import type { EdgeTypes, NodeTypes, Node, Edge, FitViewOptions, ConnectionData } from '$lib/types';
|
||||
import { initialEdgeTypes, initialNodeTypes, getInitialStore } from './initial-store';
|
||||
import type { SvelteFlowStore } from './types';
|
||||
import { syncNodeStores, syncEdgeStores } from './utils';
|
||||
import { syncNodeStores, syncEdgeStores, syncViewportStores } from './utils';
|
||||
import { getEdgeTree } from './edge-tree';
|
||||
import { getVisibleNodes } from './visible-nodes';
|
||||
import { getDerivedConnectionProps } from './derived-connection-props';
|
||||
@@ -241,10 +241,11 @@ export function createStore(): SvelteFlowStore {
|
||||
}
|
||||
|
||||
function panBy(delta: XYPosition) {
|
||||
const viewport = get(store.viewport);
|
||||
return panBySystem({
|
||||
delta,
|
||||
panZoom: get(store.panZoom),
|
||||
transform: get(store.transform),
|
||||
transform: [viewport.x, viewport.y, viewport.zoom],
|
||||
translateExtent: get(store.translateExtent),
|
||||
width: get(store.width),
|
||||
height: get(store.height)
|
||||
@@ -298,6 +299,7 @@ export function createStore(): SvelteFlowStore {
|
||||
// actions
|
||||
syncNodeStores: (nodes) => syncNodeStores(store.nodes, nodes),
|
||||
syncEdgeStores: (edges) => syncEdgeStores(store.edges, edges),
|
||||
syncViewport: (viewport) => syncViewportStores(store.panZoom, store.viewport, viewport),
|
||||
setNodeTypes,
|
||||
setEdgeTypes,
|
||||
addEdge,
|
||||
@@ -305,9 +307,7 @@ export function createStore(): SvelteFlowStore {
|
||||
updateNodeDimensions,
|
||||
zoomIn,
|
||||
zoomOut,
|
||||
fitView: (options?: FitViewOptions) => {
|
||||
return fitView(get(store.nodes), options);
|
||||
},
|
||||
fitView: (options?: FitViewOptions) => fitView(get(store.nodes), options),
|
||||
setMinZoom,
|
||||
setMaxZoom,
|
||||
setTranslateExtent,
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
ConnectionMode,
|
||||
ConnectionLineType,
|
||||
type SelectionRect,
|
||||
type Transform,
|
||||
type SnapGrid,
|
||||
type MarkerProps,
|
||||
type PanZoomInstance,
|
||||
@@ -14,7 +13,8 @@ import {
|
||||
type GroupedEdges,
|
||||
type NodeOrigin,
|
||||
type OnError,
|
||||
devWarn
|
||||
devWarn,
|
||||
type Viewport
|
||||
} from '@xyflow/system';
|
||||
|
||||
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
|
||||
@@ -72,7 +72,7 @@ export const getInitialStore = () => ({
|
||||
selectionMode: writable<SelectionMode>(SelectionMode.Partial),
|
||||
nodeTypes: writable<NodeTypes>(initialNodeTypes),
|
||||
edgeTypes: writable<EdgeTypes>(initialEdgeTypes),
|
||||
transform: writable<Transform>([0, 0, 1]),
|
||||
viewport: writable<Viewport>({ x: 0, y: 0, zoom: 1 }),
|
||||
connectionMode: writable<ConnectionMode>(ConnectionMode.Strict),
|
||||
domNode: writable<HTMLDivElement | null>(null),
|
||||
connection: readable<ConnectionProps>(initConnectionProps),
|
||||
|
||||
@@ -6,7 +6,8 @@ import type {
|
||||
Connection,
|
||||
UpdateNodePositions,
|
||||
CoordinateExtent,
|
||||
UpdateConnection
|
||||
UpdateConnection,
|
||||
Viewport
|
||||
} from '@xyflow/system';
|
||||
|
||||
import type { getInitialStore } from './initial-store';
|
||||
@@ -15,6 +16,7 @@ import type { Node, Edge, NodeTypes, EdgeTypes, FitViewOptions } from '$lib/type
|
||||
export type SvelteFlowStoreActions = {
|
||||
syncNodeStores: (nodesStore: Writable<Node[]>) => void;
|
||||
syncEdgeStores: (edgeStore: Writable<Edge[]>) => void;
|
||||
syncViewport: (viewportStore?: Writable<Viewport>) => void;
|
||||
setNodeTypes: (nodeTypes: NodeTypes) => void;
|
||||
setEdgeTypes: (edgeTypes: EdgeTypes) => void;
|
||||
addEdge: (edge: Edge | Connection) => void;
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
type Writable,
|
||||
get
|
||||
} from 'svelte/store';
|
||||
import { updateNodes } from '@xyflow/system';
|
||||
import { updateNodes, type Transform, type Viewport, type PanZoomInstance } from '@xyflow/system';
|
||||
|
||||
import type { DefaultEdgeOptions, DefaultNodeOptions, Edge, Node } from '$lib/types';
|
||||
|
||||
@@ -56,6 +56,54 @@ export function syncEdgeStores(
|
||||
edgesStore.update = userEdgesStore.update = (fn: (nds: Edge[]) => Edge[]) => _set(fn(val));
|
||||
}
|
||||
|
||||
// it is possible to pass a viewport store to SvelteFlow for having more control
|
||||
// if that's the case we need to sync the internal viewport with the user viewport
|
||||
export const syncViewportStores = (
|
||||
panZoomStore: Writable<PanZoomInstance | null>,
|
||||
viewportStore: Writable<Viewport>,
|
||||
userViewportStore?: Writable<Viewport>
|
||||
) => {
|
||||
if (!userViewportStore) {
|
||||
return;
|
||||
}
|
||||
|
||||
const panZoom = get(panZoomStore);
|
||||
|
||||
const viewportStoreSetter = viewportStore.set;
|
||||
const userViewportStoreSetter = userViewportStore.set;
|
||||
|
||||
let val = userViewportStore ? get(userViewportStore) : { x: 0, y: 0, zoom: 1 };
|
||||
viewportStore.set(val);
|
||||
|
||||
viewportStore.set = (vp: Viewport) => {
|
||||
viewportStoreSetter(vp);
|
||||
userViewportStoreSetter(vp);
|
||||
|
||||
val = vp;
|
||||
|
||||
return vp;
|
||||
};
|
||||
|
||||
userViewportStore.set = (vp: Viewport) => {
|
||||
panZoom?.syncViewport(vp);
|
||||
|
||||
viewportStoreSetter(vp);
|
||||
userViewportStoreSetter(vp);
|
||||
|
||||
val = vp;
|
||||
|
||||
return vp;
|
||||
};
|
||||
|
||||
viewportStore.update = (fn: (vp: Viewport) => Viewport) => {
|
||||
viewportStore.set(fn(val));
|
||||
};
|
||||
|
||||
userViewportStore.update = (fn: (vp: Viewport) => Viewport) => {
|
||||
userViewportStore.set(fn(val));
|
||||
};
|
||||
};
|
||||
|
||||
export type NodeStoreOptions = {
|
||||
elevateNodesOnSelect?: boolean;
|
||||
};
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { derived } from 'svelte/store';
|
||||
import { getNodesInside } from '@xyflow/system';
|
||||
import { getNodesInside, type Transform } from '@xyflow/system';
|
||||
|
||||
import type { Node } from '$lib/types';
|
||||
import type { SvelteFlowStoreState } from './types';
|
||||
|
||||
export function getVisibleNodes(store: SvelteFlowStoreState) {
|
||||
return derived(
|
||||
[store.nodes, store.onlyRenderVisibleElements, store.width, store.height, store.transform],
|
||||
([nodes, onlyRenderVisibleElements, width, height, transform]) => {
|
||||
[store.nodes, store.onlyRenderVisibleElements, store.width, store.height, store.viewport],
|
||||
([nodes, onlyRenderVisibleElements, width, height, viewport]) => {
|
||||
const transform: Transform = [viewport.x, viewport.y, viewport.zoom];
|
||||
return onlyRenderVisibleElements
|
||||
? getNodesInside<Node>(nodes, { x: 0, y: 0, width, height }, transform, true)
|
||||
: nodes;
|
||||
|
||||
Reference in New Issue
Block a user