cleaned up code for onlyRenderVisibleElements
This commit is contained in:
@@ -344,11 +344,11 @@ export function createStore(signals: StoreSignals): SvelteFlowStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const updateConnection: UpdateConnection = (newConnection: ConnectionState) => {
|
const updateConnection: UpdateConnection = (newConnection: ConnectionState) => {
|
||||||
store.rawConnection = { ...newConnection };
|
store._connection = { ...newConnection };
|
||||||
};
|
};
|
||||||
|
|
||||||
function cancelConnection() {
|
function cancelConnection() {
|
||||||
store.rawConnection = initialConnection;
|
store._connection = initialConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
function reset() {
|
function reset() {
|
||||||
|
|||||||
@@ -53,12 +53,13 @@ import type {
|
|||||||
IsValidConnection,
|
IsValidConnection,
|
||||||
Edge,
|
Edge,
|
||||||
Node,
|
Node,
|
||||||
EdgeLayouted
|
EdgeLayouted,
|
||||||
|
InternalNode
|
||||||
} from '$lib/types';
|
} from '$lib/types';
|
||||||
|
|
||||||
import type { StoreSignals } from './types';
|
import type { StoreSignals } from './types';
|
||||||
import { MediaQuery } from 'svelte/reactivity';
|
import { MediaQuery } from 'svelte/reactivity';
|
||||||
import { gatherLayoutedEdges, getVisibleNodes } from './visibleElements';
|
import { getLayoutedEdges, getVisibleNodes, type EdgeLayoutAllOptions } from './visibleElements';
|
||||||
|
|
||||||
export const initialNodeTypes = {
|
export const initialNodeTypes = {
|
||||||
input: InputNode,
|
input: InputNode,
|
||||||
@@ -75,7 +76,6 @@ export const initialEdgeTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getInitialStore = (signals: StoreSignals) => {
|
export const getInitialStore = (signals: StoreSignals) => {
|
||||||
const previousLayoutedEdges = new Map<string, EdgeLayouted>();
|
|
||||||
// We use a class here, because Svelte adds getters & setter for us.
|
// We use a class here, because Svelte adds getters & setter for us.
|
||||||
// Inline classes have some performance implications but we just call it once (max twice).
|
// Inline classes have some performance implications but we just call it once (max twice).
|
||||||
class SvelteFlowStore {
|
class SvelteFlowStore {
|
||||||
@@ -112,47 +112,47 @@ export const getInitialStore = (signals: StoreSignals) => {
|
|||||||
connectionLookup: ConnectionLookup = new Map();
|
connectionLookup: ConnectionLookup = new Map();
|
||||||
edgeLookup: EdgeLookup = new Map();
|
edgeLookup: EdgeLookup = new Map();
|
||||||
|
|
||||||
|
_prevVisibleEdges = new Map<string, EdgeLayouted>();
|
||||||
visible = $derived.by(() => {
|
visible = $derived.by(() => {
|
||||||
// We need to access this._nodes to trigger on changes
|
const {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
// We need to access this._nodes to trigger on changes
|
||||||
this._nodes;
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
_nodes,
|
||||||
|
_edges: edges,
|
||||||
|
_prevVisibleEdges: previousEdges,
|
||||||
|
nodeLookup,
|
||||||
|
connectionMode,
|
||||||
|
onerror,
|
||||||
|
onlyRenderVisibleElements
|
||||||
|
} = this;
|
||||||
|
|
||||||
// We either add all or only visible nodes to visibleNodes here.
|
let visibleNodes: Map<string, InternalNode>;
|
||||||
let visibleNodes = new Map();
|
let visibleEdges: Map<string, EdgeLayouted>;
|
||||||
let layoutedEdges = new Map<string, EdgeLayouted>();
|
|
||||||
// TODO: is there a more elegant solution??
|
const options = { edges, previousEdges, nodeLookup, connectionMode, onerror };
|
||||||
if (this.onlyRenderVisibleElements) {
|
|
||||||
const transform: Transform = [this.viewport.x, this.viewport.y, this.viewport.zoom];
|
if (onlyRenderVisibleElements) {
|
||||||
getVisibleNodes(this.nodeLookup, transform, this.width, this.height).forEach((node) => {
|
// We only subscribe to viewport, width, height if onlyRenderVisibleElements is true
|
||||||
visibleNodes.set(node.id, node);
|
const { viewport, width, height } = this;
|
||||||
});
|
const transform: Transform = [viewport.x, viewport.y, viewport.zoom];
|
||||||
layoutedEdges = gatherLayoutedEdges(
|
|
||||||
this._edges,
|
visibleNodes = getVisibleNodes(nodeLookup, transform, width, height);
|
||||||
this.nodeLookup,
|
visibleEdges = getLayoutedEdges({
|
||||||
previousLayoutedEdges,
|
...options,
|
||||||
this.connectionMode,
|
onlyRenderVisible: true,
|
||||||
this.onerror,
|
visibleNodes,
|
||||||
true,
|
|
||||||
transform,
|
transform,
|
||||||
this.width,
|
width,
|
||||||
this.height,
|
height
|
||||||
visibleNodes
|
});
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
visibleNodes = this.nodeLookup;
|
visibleNodes = this.nodeLookup;
|
||||||
layoutedEdges = gatherLayoutedEdges(
|
visibleEdges = getLayoutedEdges(options as EdgeLayoutAllOptions);
|
||||||
this._edges,
|
|
||||||
this.nodeLookup,
|
|
||||||
previousLayoutedEdges,
|
|
||||||
this.connectionMode,
|
|
||||||
this.onerror,
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
nodes: visibleNodes,
|
nodes: visibleNodes,
|
||||||
edges: layoutedEdges
|
edges: visibleEdges
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -196,7 +196,8 @@ export const getInitialStore = (signals: StoreSignals) => {
|
|||||||
nodeTypes: NodeTypes = $derived({ ...initialNodeTypes, ...signals.props.nodeTypes });
|
nodeTypes: NodeTypes = $derived({ ...initialNodeTypes, ...signals.props.nodeTypes });
|
||||||
edgeTypes: EdgeTypes = $derived({ ...initialEdgeTypes, ...signals.props.edgeTypes });
|
edgeTypes: EdgeTypes = $derived({ ...initialEdgeTypes, ...signals.props.edgeTypes });
|
||||||
|
|
||||||
// _viewport is the internal viewport. We either return signals.viewport or _viewport
|
// _viewport is the internal viewport.
|
||||||
|
// when binding to viewport, we operate on signals.viewport instead
|
||||||
_viewport: Viewport = $state(signals.props.initialViewport ?? { x: 0, y: 0, zoom: 1 });
|
_viewport: Viewport = $state(signals.props.initialViewport ?? { x: 0, y: 0, zoom: 1 });
|
||||||
get viewport() {
|
get viewport() {
|
||||||
return signals.viewport ?? this._viewport;
|
return signals.viewport ?? this._viewport;
|
||||||
@@ -208,27 +209,29 @@ export const getInitialStore = (signals: StoreSignals) => {
|
|||||||
this._viewport = viewport;
|
this._viewport = viewport;
|
||||||
}
|
}
|
||||||
|
|
||||||
connectionMode: ConnectionMode = $derived(
|
// _connection is viewport independent and originating from XYHandle
|
||||||
signals.props.connectionMode ?? ConnectionMode.Strict
|
_connection: ConnectionState = $state(initialConnection);
|
||||||
);
|
// We derive a viewport dependent connection here
|
||||||
rawConnection: ConnectionState = $state(initialConnection);
|
|
||||||
connection: ConnectionState = $derived.by(() => {
|
connection: ConnectionState = $derived.by(() => {
|
||||||
if (this.rawConnection.inProgress) {
|
if (this._connection.inProgress) {
|
||||||
return {
|
return {
|
||||||
...this.rawConnection,
|
...this._connection,
|
||||||
to: pointToRendererPoint(this.rawConnection.to, [
|
to: pointToRendererPoint(this._connection.to, [
|
||||||
this.viewport.x,
|
this.viewport.x,
|
||||||
this.viewport.y,
|
this.viewport.y,
|
||||||
this.viewport.zoom
|
this.viewport.zoom
|
||||||
])
|
])
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
return { ...this.rawConnection };
|
return this._connection;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connectionLineType: ConnectionLineType = $derived(
|
connectionLineType: ConnectionLineType = $derived(
|
||||||
signals.props.connectionLineType ?? ConnectionLineType.Bezier
|
signals.props.connectionLineType ?? ConnectionLineType.Bezier
|
||||||
);
|
);
|
||||||
|
connectionMode: ConnectionMode = $derived(
|
||||||
|
signals.props.connectionMode ?? ConnectionMode.Strict
|
||||||
|
);
|
||||||
connectionRadius: number = $derived(signals.props.connectionRadius ?? 20);
|
connectionRadius: number = $derived(signals.props.connectionRadius ?? 20);
|
||||||
isValidConnection: IsValidConnection = $derived(
|
isValidConnection: IsValidConnection = $derived(
|
||||||
signals.props.isValidConnection ?? (() => true)
|
signals.props.isValidConnection ?? (() => true)
|
||||||
@@ -258,14 +261,14 @@ export const getInitialStore = (signals: StoreSignals) => {
|
|||||||
edgesInitialized: boolean = $state(false);
|
edgesInitialized: boolean = $state(false);
|
||||||
viewportInitialized: boolean = $state(false);
|
viewportInitialized: boolean = $state(false);
|
||||||
|
|
||||||
initialNodesLength: number = signals.nodes?.length ?? 0;
|
_initialNodesLength: number = signals.nodes?.length ?? 0;
|
||||||
initialEdgesLength: number = signals.edges?.length ?? 0;
|
_initialEdgesLength: number = signals.edges?.length ?? 0;
|
||||||
initialized: boolean = $derived.by(() => {
|
initialized: boolean = $derived.by(() => {
|
||||||
let initialized = false;
|
let initialized = false;
|
||||||
// if it hasn't been initialised check if it's now
|
// if it hasn't been initialised check if it's now
|
||||||
if (this.initialNodesLength === 0) {
|
if (this._initialNodesLength === 0) {
|
||||||
initialized = this.viewportInitialized;
|
initialized = this.viewportInitialized;
|
||||||
} else if (this.initialEdgesLength === 0) {
|
} else if (this._initialEdgesLength === 0) {
|
||||||
initialized = this.viewportInitialized && this.nodesInitialized;
|
initialized = this.viewportInitialized && this.nodesInitialized;
|
||||||
} else {
|
} else {
|
||||||
initialized = this.viewportInitialized && this.nodesInitialized && this.edgesInitialized;
|
initialized = this.viewportInitialized && this.nodesInitialized && this.edgesInitialized;
|
||||||
@@ -273,13 +276,14 @@ export const getInitialStore = (signals: StoreSignals) => {
|
|||||||
|
|
||||||
return initialized;
|
return initialized;
|
||||||
});
|
});
|
||||||
prefersDark = new MediaQuery(
|
|
||||||
|
_prefersDark = new MediaQuery(
|
||||||
'(prefers-color-scheme: dark)',
|
'(prefers-color-scheme: dark)',
|
||||||
signals.props.colorModeSSR === 'dark'
|
signals.props.colorModeSSR === 'dark'
|
||||||
);
|
);
|
||||||
colorMode: ColorModeClass = $derived(
|
colorMode: ColorModeClass = $derived(
|
||||||
signals.props.colorMode === 'system'
|
signals.props.colorMode === 'system'
|
||||||
? this.prefersDark.current
|
? this._prefersDark.current
|
||||||
? 'dark'
|
? 'dark'
|
||||||
: 'light'
|
: 'light'
|
||||||
: (signals.props.colorMode ?? 'light')
|
: (signals.props.colorMode ?? 'light')
|
||||||
|
|||||||
@@ -16,42 +16,43 @@ export function getVisibleNodes(
|
|||||||
width: number,
|
width: number,
|
||||||
height: number
|
height: number
|
||||||
) {
|
) {
|
||||||
return getNodesInside(nodeLookup, { x: 0, y: 0, width: width, height: height }, transform, true);
|
const visibleNodes = new Map<string, InternalNode>();
|
||||||
|
getNodesInside(nodeLookup, { x: 0, y: 0, width: width, height: height }, transform, true).forEach(
|
||||||
|
(node) => {
|
||||||
|
visibleNodes.set(node.id, node);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return visibleNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: is this what we want?
|
export interface EdgeLayoutBaseOptions {
|
||||||
export function gatherLayoutedEdges(
|
edges: Edge[];
|
||||||
edges: Edge[],
|
previousEdges: Map<string, EdgeLayouted>;
|
||||||
nodeLookup: NodeLookup,
|
nodeLookup: NodeLookup;
|
||||||
previousLayoutedEdges: Map<string, EdgeLayouted>,
|
connectionMode: ConnectionMode;
|
||||||
connectionMode: ConnectionMode,
|
onerror: OnError;
|
||||||
onerror: OnError,
|
}
|
||||||
onlyRenderVisibleElements: true,
|
|
||||||
transform: Transform,
|
export interface EdgeLayoutAllOptions extends EdgeLayoutBaseOptions {
|
||||||
width: number,
|
onlyRenderVisible: never;
|
||||||
height: number,
|
visibleNodes: never;
|
||||||
visibleNodes: Map<string, InternalNode>
|
transform: never;
|
||||||
): Map<string, EdgeLayouted>;
|
width: never;
|
||||||
export function gatherLayoutedEdges(
|
height: never;
|
||||||
edges: Edge[],
|
}
|
||||||
nodeLookup: NodeLookup,
|
|
||||||
previousLayoutedEdges: Map<string, EdgeLayouted>,
|
export interface EdgeLayoutOnlyVisibleOptions extends EdgeLayoutBaseOptions {
|
||||||
connectionMode: ConnectionMode,
|
visibleNodes: Map<string, InternalNode>;
|
||||||
onerror: OnError,
|
transform: Transform;
|
||||||
onlyRenderVisibleElements: false
|
width: number;
|
||||||
): Map<string, EdgeLayouted>;
|
height: number;
|
||||||
export function gatherLayoutedEdges(
|
onlyRenderVisible: true;
|
||||||
edges: Edge[],
|
}
|
||||||
nodeLookup: NodeLookup,
|
|
||||||
previousLayoutedEdges: Map<string, EdgeLayouted>,
|
export type EdgeLayoutOptions = EdgeLayoutAllOptions | EdgeLayoutOnlyVisibleOptions;
|
||||||
connectionMode: ConnectionMode,
|
|
||||||
onerror: OnError,
|
export function getLayoutedEdges(options: EdgeLayoutOptions): Map<string, EdgeLayouted> {
|
||||||
onlyRenderVisibleElements?: boolean,
|
const { edges, nodeLookup, previousEdges, connectionMode, onerror, onlyRenderVisible } = options;
|
||||||
transform?: Transform,
|
|
||||||
width?: number,
|
|
||||||
height?: number,
|
|
||||||
visibleNodes?: Map<string, InternalNode>
|
|
||||||
): Map<string, EdgeLayouted> {
|
|
||||||
const layoutedEdges = new Map<string, EdgeLayouted>();
|
const layoutedEdges = new Map<string, EdgeLayouted>();
|
||||||
for (const edge of edges) {
|
for (const edge of edges) {
|
||||||
const sourceNode = nodeLookup.get(edge.source);
|
const sourceNode = nodeLookup.get(edge.source);
|
||||||
@@ -61,7 +62,8 @@ export function gatherLayoutedEdges(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onlyRenderVisibleElements) {
|
if (onlyRenderVisible) {
|
||||||
|
const { visibleNodes, transform, width, height } = options;
|
||||||
if (
|
if (
|
||||||
isEdgeVisible({
|
isEdgeVisible({
|
||||||
sourceNode,
|
sourceNode,
|
||||||
@@ -82,7 +84,7 @@ export function gatherLayoutedEdges(
|
|||||||
// the current and previous edge are the same
|
// the current and previous edge are the same
|
||||||
// and the source and target node are the same
|
// and the source and target node are the same
|
||||||
// and references to internalNodes are the same
|
// and references to internalNodes are the same
|
||||||
const previous = previousLayoutedEdges.get(edge.id);
|
const previous = previousEdges.get(edge.id);
|
||||||
if (
|
if (
|
||||||
previous &&
|
previous &&
|
||||||
edge === previous.edge &&
|
edge === previous.edge &&
|
||||||
@@ -99,7 +101,7 @@ export function gatherLayoutedEdges(
|
|||||||
targetNode,
|
targetNode,
|
||||||
sourceHandle: edge.sourceHandle || null,
|
sourceHandle: edge.sourceHandle || null,
|
||||||
targetHandle: edge.targetHandle || null,
|
targetHandle: edge.targetHandle || null,
|
||||||
connectionMode: connectionMode,
|
connectionMode,
|
||||||
onError: onerror
|
onError: onerror
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -121,7 +123,5 @@ export function gatherLayoutedEdges(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
previousLayoutedEdges = layoutedEdges;
|
|
||||||
|
|
||||||
return layoutedEdges;
|
return layoutedEdges;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user