Merge pull request #3447 from wbkd/svelte-flow-fit-view-options
fix(xyflow) fitViewOptions
This commit is contained in:
@@ -223,7 +223,7 @@ const OverviewFlow = () => {
|
|||||||
onEdgeMouseLeave={onEdgeMouseLeave}
|
onEdgeMouseLeave={onEdgeMouseLeave}
|
||||||
onEdgeDoubleClick={onEdgeDoubleClick}
|
onEdgeDoubleClick={onEdgeDoubleClick}
|
||||||
fitView
|
fitView
|
||||||
fitViewOptions={{ padding: 0.2 }}
|
fitViewOptions={{ padding: 0.1 /*nodes: [{ id: '1' }]*/ }}
|
||||||
attributionPosition="top-right"
|
attributionPosition="top-right"
|
||||||
maxZoom={Infinity}
|
maxZoom={Infinity}
|
||||||
onNodesDelete={onNodesDelete}
|
onNodesDelete={onNodesDelete}
|
||||||
|
|||||||
@@ -131,8 +131,12 @@
|
|||||||
{nodeTypes}
|
{nodeTypes}
|
||||||
{edgeTypes}
|
{edgeTypes}
|
||||||
fitView
|
fitView
|
||||||
minZoom={0.1}
|
fitViewOptions={{
|
||||||
maxZoom={2.5}
|
padding: 0.1,
|
||||||
|
nodes: [{ id: '1' }, { id: '2' }, { id: '3' }]
|
||||||
|
}}
|
||||||
|
minZoom={0}
|
||||||
|
maxZoom={Infinity}
|
||||||
selectionMode={SelectionMode.Full}
|
selectionMode={SelectionMode.Full}
|
||||||
initialViewport={{ x: 100, y: 100, zoom: 2 }}
|
initialViewport={{ x: 100, y: 100, zoom: 2 }}
|
||||||
snapGrid={[25, 25]}
|
snapGrid={[25, 25]}
|
||||||
|
|||||||
@@ -32,9 +32,21 @@
|
|||||||
const edges = writable([]);
|
const edges = writable([]);
|
||||||
|
|
||||||
const isValidConnection: IsValidConnection = (connection) => connection.target === 'B';
|
const isValidConnection: IsValidConnection = (connection) => connection.target === 'B';
|
||||||
|
|
||||||
|
const defaultEdgeOptions = {
|
||||||
|
animated: true
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<SvelteFlow {nodes} {edges} fitView minZoom={0.1} maxZoom={2.5} {isValidConnection}>
|
<SvelteFlow
|
||||||
|
{nodes}
|
||||||
|
{edges}
|
||||||
|
fitView
|
||||||
|
minZoom={0.1}
|
||||||
|
maxZoom={2.5}
|
||||||
|
{isValidConnection}
|
||||||
|
{defaultEdgeOptions}
|
||||||
|
>
|
||||||
<Controls />
|
<Controls />
|
||||||
<Background variant={BackgroundVariant.Dots} />
|
<Background variant={BackgroundVariant.Dots} />
|
||||||
</SvelteFlow>
|
</SvelteFlow>
|
||||||
|
|||||||
@@ -80,13 +80,13 @@ const createRFStore = () =>
|
|||||||
|
|
||||||
const nextNodes = updateAbsolutePositions(updatedNodes, nodeOrigin);
|
const nextNodes = updateAbsolutePositions(updatedNodes, nodeOrigin);
|
||||||
|
|
||||||
const nextFitViewDone =
|
let nextFitViewDone = fitViewDone;
|
||||||
fitViewDone ||
|
if (!fitViewDone && fitViewOnInit) {
|
||||||
(fitViewOnInit &&
|
nextFitViewDone = fitView(nextNodes, {
|
||||||
fitView({
|
...fitViewOnInitOptions,
|
||||||
...fitViewOnInitOptions,
|
nodes: fitViewOnInitOptions?.nodes || nextNodes,
|
||||||
nodes: nextNodes,
|
});
|
||||||
}));
|
}
|
||||||
|
|
||||||
set({ nodes: nextNodes, fitViewDone: nextFitViewDone });
|
set({ nodes: nextNodes, fitViewDone: nextFitViewDone });
|
||||||
|
|
||||||
@@ -241,9 +241,8 @@ const createRFStore = () =>
|
|||||||
const { transform, width, height, panZoom, translateExtent } = get();
|
const { transform, width, height, panZoom, translateExtent } = get();
|
||||||
return panBySystem({ delta, panZoom, transform, translateExtent, width, height });
|
return panBySystem({ delta, panZoom, transform, translateExtent, width, height });
|
||||||
},
|
},
|
||||||
fitView: (options?: FitViewOptions): boolean => {
|
fitView: (nodes: Node[], options?: FitViewOptions): boolean => {
|
||||||
const { panZoom, nodes, width, height, minZoom, maxZoom, nodeOrigin } = get();
|
const { panZoom, width, height, minZoom, maxZoom, nodeOrigin } = get();
|
||||||
const fitViewNodes = options?.nodes || nodes;
|
|
||||||
|
|
||||||
if (!panZoom) {
|
if (!panZoom) {
|
||||||
return false;
|
return false;
|
||||||
@@ -251,7 +250,7 @@ const createRFStore = () =>
|
|||||||
|
|
||||||
return fitViewSystem(
|
return fitViewSystem(
|
||||||
{
|
{
|
||||||
nodes: fitViewNodes as Node[],
|
nodes,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
panZoom,
|
panZoom,
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ export type ReactFlowActions = {
|
|||||||
reset: () => void;
|
reset: () => void;
|
||||||
triggerNodeChanges: (changes: NodeChange[]) => void;
|
triggerNodeChanges: (changes: NodeChange[]) => void;
|
||||||
panBy: PanBy;
|
panBy: PanBy;
|
||||||
fitView: (options?: FitViewOptions) => boolean;
|
fitView: (nodes: Node[], options?: FitViewOptions) => boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ReactFlowState = ReactFlowStore & ReactFlowActions;
|
export type ReactFlowState = ReactFlowStore & ReactFlowActions;
|
||||||
|
|||||||
@@ -1,9 +1,23 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { onMount } from 'svelte';
|
||||||
import { EdgeWrapper } from '$lib/components/EdgeWrapper';
|
import { EdgeWrapper } from '$lib/components/EdgeWrapper';
|
||||||
import { MarkerDefinition } from '$lib/container/EdgeRenderer/MarkerDefinition';
|
import { MarkerDefinition } from '$lib/container/EdgeRenderer/MarkerDefinition';
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
|
import type { DefaultEdgeOptions } from '$lib/types';
|
||||||
|
|
||||||
const { width, height, elementsSelectable, edgeTree } = useStore();
|
export let defaultEdgeOptions: DefaultEdgeOptions | undefined;
|
||||||
|
|
||||||
|
const {
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
elementsSelectable,
|
||||||
|
edgeTree,
|
||||||
|
edges: { setDefaultOptions }
|
||||||
|
} = useStore();
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
if (defaultEdgeOptions) setDefaultOptions(defaultEdgeOptions);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#each $edgeTree as group (group.level)}
|
{#each $edgeTree as group (group.level)}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
export let nodes: $$Props['nodes'];
|
export let nodes: $$Props['nodes'];
|
||||||
export let edges: $$Props['edges'];
|
export let edges: $$Props['edges'];
|
||||||
export let fitView: $$Props['fitView'] = undefined;
|
export let fitView: $$Props['fitView'] = undefined;
|
||||||
|
export let fitViewOptions: $$Props['fitViewOptions'] = undefined;
|
||||||
export let minZoom: $$Props['minZoom'] = undefined;
|
export let minZoom: $$Props['minZoom'] = undefined;
|
||||||
export let maxZoom: $$Props['maxZoom'] = undefined;
|
export let maxZoom: $$Props['maxZoom'] = undefined;
|
||||||
export let initialViewport: Viewport = { x: 0, y: 0, zoom: 1 };
|
export let initialViewport: Viewport = { x: 0, y: 0, zoom: 1 };
|
||||||
@@ -57,6 +58,7 @@
|
|||||||
export let onError: $$Props['onError'] = undefined;
|
export let onError: $$Props['onError'] = undefined;
|
||||||
export let attributionPosition: $$Props['attributionPosition'] = undefined;
|
export let attributionPosition: $$Props['attributionPosition'] = undefined;
|
||||||
export let proOptions: $$Props['proOptions'] = undefined;
|
export let proOptions: $$Props['proOptions'] = undefined;
|
||||||
|
export let defaultEdgeOptions: $$Props['defaultEdgeOptions'] = undefined;
|
||||||
|
|
||||||
export let defaultMarkerColor = '#b1b1b7';
|
export let defaultMarkerColor = '#b1b1b7';
|
||||||
|
|
||||||
@@ -81,6 +83,10 @@
|
|||||||
store.fitViewOnInit.set(fitView);
|
store.fitViewOnInit.set(fitView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fitViewOptions) {
|
||||||
|
store.fitViewOptions.set(fitViewOptions);
|
||||||
|
}
|
||||||
|
|
||||||
updateStore(store, {
|
updateStore(store, {
|
||||||
nodeTypes,
|
nodeTypes,
|
||||||
edgeTypes,
|
edgeTypes,
|
||||||
@@ -153,7 +159,7 @@
|
|||||||
>
|
>
|
||||||
<Pane on:paneclick panOnDrag={panOnDrag === undefined ? true : panOnDrag}>
|
<Pane on:paneclick panOnDrag={panOnDrag === undefined ? true : panOnDrag}>
|
||||||
<ViewportComponent>
|
<ViewportComponent>
|
||||||
<EdgeRenderer on:edgeclick on:edgecontextmenu />
|
<EdgeRenderer on:edgeclick on:edgecontextmenu {defaultEdgeOptions} />
|
||||||
<ConnectionLine />
|
<ConnectionLine />
|
||||||
<div class="svelte-flow__edgelabel-renderer" />
|
<div class="svelte-flow__edgelabel-renderer" />
|
||||||
<NodeRenderer
|
<NodeRenderer
|
||||||
|
|||||||
@@ -20,7 +20,15 @@ import type {
|
|||||||
ProOptions
|
ProOptions
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import type { Edge, Node, NodeTypes, KeyDefinition, EdgeTypes } from '$lib/types';
|
import type {
|
||||||
|
Edge,
|
||||||
|
Node,
|
||||||
|
NodeTypes,
|
||||||
|
KeyDefinition,
|
||||||
|
EdgeTypes,
|
||||||
|
DefaultEdgeOptions,
|
||||||
|
FitViewOptions
|
||||||
|
} from '$lib/types';
|
||||||
import type { Writable } from 'svelte/store';
|
import type { Writable } from 'svelte/store';
|
||||||
|
|
||||||
export type SvelteFlowProps = DOMAttributes<HTMLDivElement> & {
|
export type SvelteFlowProps = DOMAttributes<HTMLDivElement> & {
|
||||||
@@ -33,6 +41,7 @@ export type SvelteFlowProps = DOMAttributes<HTMLDivElement> & {
|
|||||||
panActivationKey?: KeyDefinition;
|
panActivationKey?: KeyDefinition;
|
||||||
deleteKey?: KeyDefinition;
|
deleteKey?: KeyDefinition;
|
||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
|
fitViewOptions?: FitViewOptions;
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
minZoom?: number;
|
minZoom?: number;
|
||||||
maxZoom?: number;
|
maxZoom?: number;
|
||||||
@@ -58,6 +67,7 @@ export type SvelteFlowProps = DOMAttributes<HTMLDivElement> & {
|
|||||||
autoPanOnNodeDrag?: boolean;
|
autoPanOnNodeDrag?: boolean;
|
||||||
attributionPosition?: PanelPosition;
|
attributionPosition?: PanelPosition;
|
||||||
proOptions?: ProOptions;
|
proOptions?: ProOptions;
|
||||||
|
defaultEdgeOptions?: DefaultEdgeOptions;
|
||||||
|
|
||||||
class?: string;
|
class?: string;
|
||||||
style?: string;
|
style?: string;
|
||||||
|
|||||||
@@ -90,13 +90,39 @@ export function createStore(): SvelteFlowStore {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fitViewOnInitDone =
|
if (!get(store.fitViewOnInitDone) && get(store.fitViewOnInit)) {
|
||||||
get(store.fitViewOnInitDone) || (get(store.fitViewOnInit) && fitView({ nodes: nextNodes }));
|
const fitViewOptions = get(store.fitViewOptions);
|
||||||
|
const fitViewOnInitDone = fitView(nextNodes, {
|
||||||
|
...fitViewOptions,
|
||||||
|
nodes: fitViewOptions?.nodes || nextNodes
|
||||||
|
});
|
||||||
|
store.fitViewOnInitDone.set(fitViewOnInitDone);
|
||||||
|
}
|
||||||
|
|
||||||
store.fitViewOnInitDone.set(fitViewOnInitDone);
|
|
||||||
store.nodes.set(nextNodes);
|
store.nodes.set(nextNodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fitView(nodes: Node[], options?: FitViewOptions) {
|
||||||
|
const panZoom = get(store.panZoom);
|
||||||
|
|
||||||
|
if (!panZoom) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fitViewUtil(
|
||||||
|
{
|
||||||
|
nodes,
|
||||||
|
width: get(store.width),
|
||||||
|
height: get(store.height),
|
||||||
|
minZoom: get(store.minZoom),
|
||||||
|
maxZoom: get(store.maxZoom),
|
||||||
|
panZoom,
|
||||||
|
nodeOrigin: get(store.nodeOrigin)
|
||||||
|
},
|
||||||
|
options
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function zoomBy(factor: number, options?: ViewportHelperFunctionOptions) {
|
function zoomBy(factor: number, options?: ViewportHelperFunctionOptions) {
|
||||||
const panZoom = get(store.panZoom);
|
const panZoom = get(store.panZoom);
|
||||||
|
|
||||||
@@ -140,28 +166,6 @@ export function createStore(): SvelteFlowStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fitView(options?: FitViewOptions) {
|
|
||||||
const panZoom = get(store.panZoom);
|
|
||||||
const fitViewNodes = options?.nodes || get(store.nodes);
|
|
||||||
|
|
||||||
if (!panZoom) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fitViewUtil(
|
|
||||||
{
|
|
||||||
nodes: fitViewNodes as Node[],
|
|
||||||
width: get(store.width),
|
|
||||||
height: get(store.height),
|
|
||||||
minZoom: get(store.minZoom),
|
|
||||||
maxZoom: get(store.maxZoom),
|
|
||||||
panZoom,
|
|
||||||
nodeOrigin: get(store.nodeOrigin)
|
|
||||||
},
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetSelectedItem<T extends Node | Edge>(item: T) {
|
function resetSelectedItem<T extends Node | Edge>(item: T) {
|
||||||
if (item.selected) {
|
if (item.selected) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -24,7 +24,14 @@ import BezierEdge from '$lib/components/edges/BezierEdge.svelte';
|
|||||||
import StraightEdge from '$lib/components/edges/StraightEdge.svelte';
|
import StraightEdge from '$lib/components/edges/StraightEdge.svelte';
|
||||||
import SmoothStepEdge from '$lib/components/edges/SmoothStepEdge.svelte';
|
import SmoothStepEdge from '$lib/components/edges/SmoothStepEdge.svelte';
|
||||||
import StepEdge from '$lib/components/edges/StepEdge.svelte';
|
import StepEdge from '$lib/components/edges/StepEdge.svelte';
|
||||||
import type { ConnectionData, NodeTypes, EdgeTypes, EdgeLayouted, Node } from '$lib/types';
|
import type {
|
||||||
|
ConnectionData,
|
||||||
|
NodeTypes,
|
||||||
|
EdgeTypes,
|
||||||
|
EdgeLayouted,
|
||||||
|
Node,
|
||||||
|
FitViewOptions
|
||||||
|
} from '$lib/types';
|
||||||
import { createNodesStore, createEdgesStore } from './utils';
|
import { createNodesStore, createEdgesStore } from './utils';
|
||||||
|
|
||||||
export const initConnectionData = {
|
export const initConnectionData = {
|
||||||
@@ -64,6 +71,7 @@ export const getInitialStore = () => ({
|
|||||||
autoPanOnConnect: writable<boolean>(true),
|
autoPanOnConnect: writable<boolean>(true),
|
||||||
fitViewOnInit: writable<boolean>(false),
|
fitViewOnInit: writable<boolean>(false),
|
||||||
fitViewOnInitDone: writable<boolean>(false),
|
fitViewOnInitDone: writable<boolean>(false),
|
||||||
|
fitViewOptions: writable<FitViewOptions>(undefined),
|
||||||
panZoom: writable<PanZoomInstance | null>(null),
|
panZoom: writable<PanZoomInstance | null>(null),
|
||||||
snapGrid: writable<SnapGrid | null>(null),
|
snapGrid: writable<SnapGrid | null>(null),
|
||||||
dragging: writable<boolean>(false),
|
dragging: writable<boolean>(false),
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ export const getTransformForBounds = (
|
|||||||
height: number,
|
height: number,
|
||||||
minZoom: number,
|
minZoom: number,
|
||||||
maxZoom: number,
|
maxZoom: number,
|
||||||
padding = 0.1
|
padding: number
|
||||||
): Transform => {
|
): Transform => {
|
||||||
const xZoom = width / (bounds.width * (1 + padding));
|
const xZoom = width / (bounds.width * (1 + padding));
|
||||||
const yZoom = height / (bounds.height * (1 + padding));
|
const yZoom = height / (bounds.height * (1 + padding));
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ export function fitView<Params extends FitViewParamsBase<NodeBase>, Options exte
|
|||||||
options?: Options
|
options?: Options
|
||||||
) {
|
) {
|
||||||
const filteredNodes = nodes.filter((n) => {
|
const filteredNodes = nodes.filter((n) => {
|
||||||
const isVisible = options?.includeHiddenNodes ? n.width && n.height : !n.hidden;
|
const isVisible = n.width && n.height && (options?.includeHiddenNodes || !n.hidden);
|
||||||
|
|
||||||
if (options?.nodes?.length) {
|
if (options?.nodes?.length) {
|
||||||
return isVisible && options?.nodes.some((optionNode) => optionNode.id === n.id);
|
return isVisible && options?.nodes.some((optionNode) => optionNode.id === n.id);
|
||||||
@@ -180,10 +180,8 @@ export function fitView<Params extends FitViewParamsBase<NodeBase>, Options exte
|
|||||||
return isVisible;
|
return isVisible;
|
||||||
});
|
});
|
||||||
|
|
||||||
const nodesInitialized = filteredNodes.every((n) => n.width && n.height);
|
if (filteredNodes.length > 0) {
|
||||||
|
const bounds = getRectOfNodes(filteredNodes, nodeOrigin);
|
||||||
if (nodes.length > 0 && nodesInitialized) {
|
|
||||||
const bounds = getRectOfNodes(nodes, nodeOrigin);
|
|
||||||
|
|
||||||
const [x, y, zoom] = getTransformForBounds(
|
const [x, y, zoom] = getTransformForBounds(
|
||||||
bounds,
|
bounds,
|
||||||
|
|||||||
Reference in New Issue
Block a user