added nodeExtent to Svelte Flow and React Flow Provider
This commit is contained in:
@@ -90,7 +90,7 @@ export function NodeWrapper<NodeType extends Node>({
|
|||||||
const inlineDimensions = getNodeInlineStyleDimensions(node);
|
const inlineDimensions = getNodeInlineStyleDimensions(node);
|
||||||
// TODO: clamping should happen earlier
|
// TODO: clamping should happen earlier
|
||||||
const clampedPosition = nodeExtent
|
const clampedPosition = nodeExtent
|
||||||
? clampPosition(internals.positionAbsolute, nodeExtent, node.measured)
|
? clampPosition(internals.positionAbsolute, nodeExtent, nodeDimensions)
|
||||||
: internals.positionAbsolute;
|
: internals.positionAbsolute;
|
||||||
|
|
||||||
const hasPointerEvents = isSelectable || isDraggable || onClick || onMouseEnter || onMouseMove || onMouseLeave;
|
const hasPointerEvents = isSelectable || isDraggable || onClick || onMouseEnter || onMouseMove || onMouseLeave;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { Provider } from '../../contexts/StoreContext';
|
|||||||
import { createStore } from '../../store';
|
import { createStore } from '../../store';
|
||||||
import { BatchProvider } from '../BatchProvider';
|
import { BatchProvider } from '../BatchProvider';
|
||||||
import type { Node, Edge } from '../../types';
|
import type { Node, Edge } from '../../types';
|
||||||
import { NodeOrigin } from '@xyflow/system';
|
import { CoordinateExtent, NodeOrigin } from '@xyflow/system';
|
||||||
|
|
||||||
export type ReactFlowProviderProps = {
|
export type ReactFlowProviderProps = {
|
||||||
initialNodes?: Node[];
|
initialNodes?: Node[];
|
||||||
@@ -15,6 +15,7 @@ export type ReactFlowProviderProps = {
|
|||||||
initialHeight?: number;
|
initialHeight?: number;
|
||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
|
nodeExtent?: CoordinateExtent;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -27,6 +28,7 @@ export function ReactFlowProvider({
|
|||||||
initialHeight: height,
|
initialHeight: height,
|
||||||
fitView,
|
fitView,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
|
nodeExtent,
|
||||||
children,
|
children,
|
||||||
}: ReactFlowProviderProps) {
|
}: ReactFlowProviderProps) {
|
||||||
const [store] = useState(() =>
|
const [store] = useState(() =>
|
||||||
@@ -39,6 +41,7 @@ export function ReactFlowProvider({
|
|||||||
height,
|
height,
|
||||||
fitView,
|
fitView,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
|
nodeExtent,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useContext, type ReactNode } from 'react';
|
|||||||
import StoreContext from '../../contexts/StoreContext';
|
import StoreContext from '../../contexts/StoreContext';
|
||||||
import { ReactFlowProvider } from '../../components/ReactFlowProvider';
|
import { ReactFlowProvider } from '../../components/ReactFlowProvider';
|
||||||
import type { Node, Edge } from '../../types';
|
import type { Node, Edge } from '../../types';
|
||||||
import { NodeOrigin } from '@xyflow/system';
|
import { CoordinateExtent, NodeOrigin } from '@xyflow/system';
|
||||||
|
|
||||||
export function Wrapper({
|
export function Wrapper({
|
||||||
children,
|
children,
|
||||||
@@ -15,6 +15,7 @@ export function Wrapper({
|
|||||||
height,
|
height,
|
||||||
fitView,
|
fitView,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
|
nodeExtent,
|
||||||
}: {
|
}: {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
nodes?: Node[];
|
nodes?: Node[];
|
||||||
@@ -25,6 +26,7 @@ export function Wrapper({
|
|||||||
height?: number;
|
height?: number;
|
||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
|
nodeExtent?: CoordinateExtent;
|
||||||
}) {
|
}) {
|
||||||
const isWrapped = useContext(StoreContext);
|
const isWrapped = useContext(StoreContext);
|
||||||
|
|
||||||
@@ -44,6 +46,7 @@ export function Wrapper({
|
|||||||
initialHeight={height}
|
initialHeight={height}
|
||||||
fitView={fitView}
|
fitView={fitView}
|
||||||
nodeOrigin={nodeOrigin}
|
nodeOrigin={nodeOrigin}
|
||||||
|
nodeExtent={nodeExtent}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</ReactFlowProvider>
|
</ReactFlowProvider>
|
||||||
|
|||||||
@@ -160,7 +160,15 @@ function ReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
|
|||||||
data-testid="rf__wrapper"
|
data-testid="rf__wrapper"
|
||||||
id={id}
|
id={id}
|
||||||
>
|
>
|
||||||
<Wrapper nodes={nodes} edges={edges} width={width} height={height} fitView={fitView} nodeOrigin={nodeOrigin}>
|
<Wrapper
|
||||||
|
nodes={nodes}
|
||||||
|
edges={edges}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
fitView={fitView}
|
||||||
|
nodeOrigin={nodeOrigin}
|
||||||
|
nodeExtent={nodeExtent}
|
||||||
|
>
|
||||||
<GraphView<NodeType, EdgeType>
|
<GraphView<NodeType, EdgeType>
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
onNodeClick={onNodeClick}
|
onNodeClick={onNodeClick}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
ParentExpandChild,
|
ParentExpandChild,
|
||||||
initialConnection,
|
initialConnection,
|
||||||
NodeOrigin,
|
NodeOrigin,
|
||||||
|
CoordinateExtent,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import { applyEdgeChanges, applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes';
|
import { applyEdgeChanges, applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes';
|
||||||
@@ -30,6 +31,7 @@ const createStore = ({
|
|||||||
height,
|
height,
|
||||||
fitView,
|
fitView,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
|
nodeExtent,
|
||||||
}: {
|
}: {
|
||||||
nodes?: Node[];
|
nodes?: Node[];
|
||||||
edges?: Edge[];
|
edges?: Edge[];
|
||||||
@@ -39,10 +41,11 @@ const createStore = ({
|
|||||||
height?: number;
|
height?: number;
|
||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
|
nodeExtent?: CoordinateExtent;
|
||||||
}) =>
|
}) =>
|
||||||
createWithEqualityFn<ReactFlowState>(
|
createWithEqualityFn<ReactFlowState>(
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
...getInitialState({ nodes, edges, width, height, fitView, nodeOrigin, defaultNodes, defaultEdges }),
|
...getInitialState({ nodes, edges, width, height, fitView, nodeOrigin, nodeExtent, defaultNodes, defaultEdges }),
|
||||||
setNodes: (nodes: Node[]) => {
|
setNodes: (nodes: Node[]) => {
|
||||||
const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
||||||
// setNodes() is called exclusively in response to user actions:
|
// setNodes() is called exclusively in response to user actions:
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
getInternalNodesBounds,
|
getInternalNodesBounds,
|
||||||
NodeOrigin,
|
NodeOrigin,
|
||||||
initialConnection,
|
initialConnection,
|
||||||
|
CoordinateExtent,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import type { Edge, InternalNode, Node, ReactFlowStore } from '../types';
|
import type { Edge, InternalNode, Node, ReactFlowStore } from '../types';
|
||||||
@@ -22,6 +23,7 @@ const getInitialState = ({
|
|||||||
height,
|
height,
|
||||||
fitView,
|
fitView,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
|
nodeExtent,
|
||||||
}: {
|
}: {
|
||||||
nodes?: Node[];
|
nodes?: Node[];
|
||||||
edges?: Edge[];
|
edges?: Edge[];
|
||||||
@@ -31,18 +33,22 @@ const getInitialState = ({
|
|||||||
height?: number;
|
height?: number;
|
||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
|
nodeExtent?: CoordinateExtent;
|
||||||
} = {}): ReactFlowStore => {
|
} = {}): ReactFlowStore => {
|
||||||
const nodeLookup = new Map<string, InternalNode>();
|
const nodeLookup = new Map<string, InternalNode>();
|
||||||
const parentLookup = new Map();
|
const parentLookup = new Map();
|
||||||
const connectionLookup = new Map();
|
const connectionLookup = new Map();
|
||||||
const edgeLookup = new Map();
|
const edgeLookup = new Map();
|
||||||
|
|
||||||
const storeEdges = defaultEdges ?? edges ?? [];
|
const storeEdges = defaultEdges ?? edges ?? [];
|
||||||
const storeNodes = defaultNodes ?? nodes ?? [];
|
const storeNodes = defaultNodes ?? nodes ?? [];
|
||||||
const storeNodeOrigin = nodeOrigin ?? [0, 0];
|
const storeNodeOrigin = nodeOrigin ?? [0, 0];
|
||||||
|
const storeNodeExtent = nodeExtent ?? infiniteExtent;
|
||||||
|
|
||||||
updateConnectionLookup(connectionLookup, edgeLookup, storeEdges);
|
updateConnectionLookup(connectionLookup, edgeLookup, storeEdges);
|
||||||
adoptUserNodes(storeNodes, nodeLookup, parentLookup, {
|
adoptUserNodes(storeNodes, nodeLookup, parentLookup, {
|
||||||
nodeOrigin: storeNodeOrigin,
|
nodeOrigin: storeNodeOrigin,
|
||||||
|
nodeExtent: storeNodeExtent,
|
||||||
elevateNodesOnSelect: false,
|
elevateNodesOnSelect: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -76,7 +82,7 @@ const getInitialState = ({
|
|||||||
minZoom: 0.5,
|
minZoom: 0.5,
|
||||||
maxZoom: 2,
|
maxZoom: 2,
|
||||||
translateExtent: infiniteExtent,
|
translateExtent: infiniteExtent,
|
||||||
nodeExtent: infiniteExtent,
|
nodeExtent: storeNodeExtent,
|
||||||
nodesSelectionActive: false,
|
nodesSelectionActive: false,
|
||||||
userSelectionActive: false,
|
userSelectionActive: false,
|
||||||
userSelectionRect: null,
|
userSelectionRect: null,
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
export let onMoveEnd: $$Props['onMoveEnd'] = undefined;
|
export let onMoveEnd: $$Props['onMoveEnd'] = undefined;
|
||||||
export let isValidConnection: $$Props['isValidConnection'] = undefined;
|
export let isValidConnection: $$Props['isValidConnection'] = undefined;
|
||||||
export let translateExtent: $$Props['translateExtent'] = undefined;
|
export let translateExtent: $$Props['translateExtent'] = undefined;
|
||||||
|
export let nodeExtent: $$Props['nodeExtent'] = undefined;
|
||||||
export let onlyRenderVisibleElements: $$Props['onlyRenderVisibleElements'] = undefined;
|
export let onlyRenderVisibleElements: $$Props['onlyRenderVisibleElements'] = undefined;
|
||||||
export let panOnScrollMode: $$Props['panOnScrollMode'] = PanOnScrollMode.Free;
|
export let panOnScrollMode: $$Props['panOnScrollMode'] = PanOnScrollMode.Free;
|
||||||
export let preventScrolling: $$Props['preventScrolling'] = true;
|
export let preventScrolling: $$Props['preventScrolling'] = true;
|
||||||
@@ -102,7 +103,8 @@
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fitView,
|
fitView,
|
||||||
nodeOrigin
|
nodeOrigin,
|
||||||
|
nodeExtent
|
||||||
});
|
});
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
|
|||||||
@@ -206,6 +206,12 @@ export type SvelteFlowProps = DOMAttributes<HTMLDivElement> & {
|
|||||||
* @example [[-1000, -10000], [1000, 1000]]
|
* @example [[-1000, -10000], [1000, 1000]]
|
||||||
*/
|
*/
|
||||||
translateExtent?: CoordinateExtent;
|
translateExtent?: CoordinateExtent;
|
||||||
|
/** By default the nodes can be placed anywhere. You can use this prop to set a boundary.
|
||||||
|
*
|
||||||
|
* The first pair of coordinates is the top left boundary and the second pair is the bottom right.
|
||||||
|
* @example [[-1000, -10000], [1000, 1000]]
|
||||||
|
*/
|
||||||
|
nodeExtent?: CoordinateExtent;
|
||||||
/** Disabling this prop will allow the user to scroll the page even when their pointer is over the flow.
|
/** Disabling this prop will allow the user to scroll the page even when their pointer is over the flow.
|
||||||
* @default true
|
* @default true
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -37,7 +37,8 @@ export function createStore({
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fitView: fitViewOnCreate,
|
fitView: fitViewOnCreate,
|
||||||
nodeOrigin
|
nodeOrigin,
|
||||||
|
nodeExtent
|
||||||
}: {
|
}: {
|
||||||
nodes?: Node[];
|
nodes?: Node[];
|
||||||
edges?: Edge[];
|
edges?: Edge[];
|
||||||
@@ -45,6 +46,7 @@ export function createStore({
|
|||||||
height?: number;
|
height?: number;
|
||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
|
nodeExtent?: CoordinateExtent;
|
||||||
}): SvelteFlowStore {
|
}): SvelteFlowStore {
|
||||||
const store = getInitialStore({
|
const store = getInitialStore({
|
||||||
nodes,
|
nodes,
|
||||||
@@ -52,7 +54,8 @@ export function createStore({
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fitView: fitViewOnCreate,
|
fitView: fitViewOnCreate,
|
||||||
nodeOrigin
|
nodeOrigin,
|
||||||
|
nodeExtent
|
||||||
});
|
});
|
||||||
|
|
||||||
function setNodeTypes(nodeTypes: NodeTypes) {
|
function setNodeTypes(nodeTypes: NodeTypes) {
|
||||||
@@ -482,7 +485,8 @@ export function createStoreContext({
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fitView,
|
fitView,
|
||||||
nodeOrigin
|
nodeOrigin,
|
||||||
|
nodeExtent
|
||||||
}: {
|
}: {
|
||||||
nodes?: Node[];
|
nodes?: Node[];
|
||||||
edges?: Edge[];
|
edges?: Edge[];
|
||||||
@@ -490,8 +494,9 @@ export function createStoreContext({
|
|||||||
height?: number;
|
height?: number;
|
||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
|
nodeExtent?: CoordinateExtent;
|
||||||
}) {
|
}) {
|
||||||
const store = createStore({ nodes, edges, width, height, fitView, nodeOrigin });
|
const store = createStore({ nodes, edges, width, height, fitView, nodeOrigin, nodeExtent });
|
||||||
|
|
||||||
setContext(key, {
|
setContext(key, {
|
||||||
getStore: () => store
|
getStore: () => store
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ export const getInitialStore = ({
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fitView,
|
fitView,
|
||||||
nodeOrigin
|
nodeOrigin,
|
||||||
|
nodeExtent
|
||||||
}: {
|
}: {
|
||||||
nodes?: Node[];
|
nodes?: Node[];
|
||||||
edges?: Edge[];
|
edges?: Edge[];
|
||||||
@@ -83,17 +84,23 @@ export const getInitialStore = ({
|
|||||||
height?: number;
|
height?: number;
|
||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
|
nodeExtent?: CoordinateExtent;
|
||||||
}) => {
|
}) => {
|
||||||
const nodeLookup: NodeLookup = new Map();
|
const nodeLookup: NodeLookup = new Map();
|
||||||
const parentLookup = new Map();
|
const parentLookup = new Map();
|
||||||
|
const connectionLookup = new Map();
|
||||||
|
const edgeLookup = new Map();
|
||||||
|
|
||||||
const storeNodeOrigin = nodeOrigin ?? [0, 0];
|
const storeNodeOrigin = nodeOrigin ?? [0, 0];
|
||||||
|
const storeNodeExtent = nodeExtent ?? infiniteExtent;
|
||||||
|
|
||||||
adoptUserNodes(nodes, nodeLookup, parentLookup, {
|
adoptUserNodes(nodes, nodeLookup, parentLookup, {
|
||||||
|
nodeExtent: storeNodeExtent,
|
||||||
nodeOrigin: storeNodeOrigin,
|
nodeOrigin: storeNodeOrigin,
|
||||||
elevateNodesOnSelect: false,
|
elevateNodesOnSelect: false,
|
||||||
checkEquality: false
|
checkEquality: false
|
||||||
});
|
});
|
||||||
const connectionLookup = new Map();
|
|
||||||
const edgeLookup = new Map();
|
|
||||||
updateConnectionLookup(connectionLookup, edgeLookup, edges);
|
updateConnectionLookup(connectionLookup, edgeLookup, edges);
|
||||||
|
|
||||||
let viewport: Viewport = { x: 0, y: 0, zoom: 1 };
|
let viewport: Viewport = { x: 0, y: 0, zoom: 1 };
|
||||||
@@ -121,7 +128,7 @@ export const getInitialStore = ({
|
|||||||
maxZoom: writable<number>(2),
|
maxZoom: writable<number>(2),
|
||||||
nodeOrigin: writable<NodeOrigin>(storeNodeOrigin),
|
nodeOrigin: writable<NodeOrigin>(storeNodeOrigin),
|
||||||
nodeDragThreshold: writable<number>(1),
|
nodeDragThreshold: writable<number>(1),
|
||||||
nodeExtent: writable<CoordinateExtent>(infiniteExtent),
|
nodeExtent: writable<CoordinateExtent>(storeNodeExtent),
|
||||||
translateExtent: writable<CoordinateExtent>(infiniteExtent),
|
translateExtent: writable<CoordinateExtent>(infiniteExtent),
|
||||||
autoPanOnNodeDrag: writable<boolean>(true),
|
autoPanOnNodeDrag: writable<boolean>(true),
|
||||||
autoPanOnConnect: writable<boolean>(true),
|
autoPanOnConnect: writable<boolean>(true),
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { infiniteExtent } from '..';
|
||||||
import {
|
import {
|
||||||
NodeBase,
|
NodeBase,
|
||||||
CoordinateExtent,
|
CoordinateExtent,
|
||||||
@@ -23,6 +24,7 @@ import { ParentExpandChild } from './types';
|
|||||||
|
|
||||||
const defaultOptions = {
|
const defaultOptions = {
|
||||||
nodeOrigin: [0, 0] as NodeOrigin,
|
nodeOrigin: [0, 0] as NodeOrigin,
|
||||||
|
nodeExtent: infiniteExtent,
|
||||||
elevateNodesOnSelect: true,
|
elevateNodesOnSelect: true,
|
||||||
defaults: {},
|
defaults: {},
|
||||||
};
|
};
|
||||||
@@ -48,6 +50,7 @@ export function updateAbsolutePositions<NodeType extends NodeBase>(
|
|||||||
|
|
||||||
type UpdateNodesOptions<NodeType extends NodeBase> = {
|
type UpdateNodesOptions<NodeType extends NodeBase> = {
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
|
nodeExtent?: CoordinateExtent;
|
||||||
elevateNodesOnSelect?: boolean;
|
elevateNodesOnSelect?: boolean;
|
||||||
defaults?: Partial<NodeType>;
|
defaults?: Partial<NodeType>;
|
||||||
checkEquality?: boolean;
|
checkEquality?: boolean;
|
||||||
|
|||||||
Reference in New Issue
Block a user