Merge pull request #5637 from xyflow/feat/zindexmode
feat(nodes-edges): add zIndexMode
This commit is contained in:
7
.changeset/breezy-gorillas-accept.md
Normal file
7
.changeset/breezy-gorillas-accept.md
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/react': minor
|
||||||
|
'@xyflow/svelte': minor
|
||||||
|
'@xyflow/system': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add `zIndexMode` to control how z-index is calculated for nodes and edges
|
||||||
@@ -60,6 +60,7 @@ import DevTools from '../examples/DevTools';
|
|||||||
import Redux from '../examples/Redux';
|
import Redux from '../examples/Redux';
|
||||||
import MovingHandles from '../examples/MovingHandles';
|
import MovingHandles from '../examples/MovingHandles';
|
||||||
import DetachedHandle from '../examples/DetachedHandle';
|
import DetachedHandle from '../examples/DetachedHandle';
|
||||||
|
import ZIndexMode from '../examples/ZIndexMode';
|
||||||
import Middlewares from '../examples/Middlewares';
|
import Middlewares from '../examples/Middlewares';
|
||||||
|
|
||||||
export interface IRoute {
|
export interface IRoute {
|
||||||
@@ -384,6 +385,11 @@ const routes: IRoute[] = [
|
|||||||
path: 'use-key-press',
|
path: 'use-key-press',
|
||||||
component: UseKeyPress,
|
component: UseKeyPress,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'zIndexMode',
|
||||||
|
path: 'z-index-mode',
|
||||||
|
component: ZIndexMode,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default routes;
|
export default routes;
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ import {
|
|||||||
Position,
|
Position,
|
||||||
} from '@xyflow/react';
|
} from '@xyflow/react';
|
||||||
|
|
||||||
import './style.css';
|
|
||||||
|
|
||||||
const nodeDefaults = {
|
const nodeDefaults = {
|
||||||
sourcePosition: Position.Right,
|
sourcePosition: Position.Right,
|
||||||
targetPosition: Position.Left,
|
targetPosition: Position.Left,
|
||||||
|
|||||||
88
examples/react/src/examples/ZIndexMode/index.tsx
Normal file
88
examples/react/src/examples/ZIndexMode/index.tsx
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import { ChangeEventHandler, useCallback, useState } from 'react';
|
||||||
|
import {
|
||||||
|
ReactFlow,
|
||||||
|
addEdge,
|
||||||
|
Node,
|
||||||
|
useNodesState,
|
||||||
|
useEdgesState,
|
||||||
|
OnConnect,
|
||||||
|
Edge,
|
||||||
|
MiniMap,
|
||||||
|
Background,
|
||||||
|
Controls,
|
||||||
|
Position,
|
||||||
|
ZIndexMode,
|
||||||
|
Panel,
|
||||||
|
} from '@xyflow/react';
|
||||||
|
|
||||||
|
const nodeDefaults = {
|
||||||
|
sourcePosition: Position.Right,
|
||||||
|
targetPosition: Position.Left,
|
||||||
|
};
|
||||||
|
|
||||||
|
const initialNodes: Node[] = [
|
||||||
|
{ id: 'A', type: 'input', position: { x: 0, y: 150 }, data: { label: 'A' }, ...nodeDefaults },
|
||||||
|
{ id: 'B', position: { x: 250, y: 0 }, data: { label: 'B' }, ...nodeDefaults },
|
||||||
|
{ id: 'C', position: { x: 250, y: 150 }, data: { label: 'C' }, ...nodeDefaults },
|
||||||
|
|
||||||
|
// group 1
|
||||||
|
{ id: 'D', position: { x: 0, y: 300 }, width: 200, height: 200, data: { label: 'D' }, ...nodeDefaults },
|
||||||
|
{ id: 'E', parentId: 'D', position: { x: 10, y: 10 }, data: { label: 'E' }, ...nodeDefaults },
|
||||||
|
|
||||||
|
// group 2
|
||||||
|
{ id: 'F', position: { x: 250, y: 300 }, width: 200, height: 200, data: { label: 'F' }, ...nodeDefaults },
|
||||||
|
{ id: 'G', parentId: 'F', position: { x: 10, y: 10 }, data: { label: 'G' }, ...nodeDefaults },
|
||||||
|
|
||||||
|
// group 3
|
||||||
|
{ id: 'H', position: { x: 500, y: 300 }, width: 200, height: 200, data: { label: 'H' }, ...nodeDefaults },
|
||||||
|
{ id: 'I', parentId: 'H', position: { x: 10, y: 10 }, data: { label: 'I' }, ...nodeDefaults },
|
||||||
|
];
|
||||||
|
|
||||||
|
const initialEdges: Edge[] = [
|
||||||
|
{
|
||||||
|
id: 'A-B',
|
||||||
|
source: 'A',
|
||||||
|
target: 'B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'A-C',
|
||||||
|
source: 'A',
|
||||||
|
target: 'C',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function ZIndexModeFlow() {
|
||||||
|
const [zIndexMode, setZIndexMode] = useState<ZIndexMode>('auto');
|
||||||
|
const [nodes, , onNodesChange] = useNodesState(initialNodes);
|
||||||
|
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||||
|
|
||||||
|
const onConnect: OnConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)), [setEdges]);
|
||||||
|
|
||||||
|
const onChange: ChangeEventHandler<HTMLSelectElement> = (evt) => setZIndexMode(evt.target.value as ZIndexMode);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ReactFlow
|
||||||
|
nodes={nodes}
|
||||||
|
edges={edges}
|
||||||
|
onNodesChange={onNodesChange}
|
||||||
|
onEdgesChange={onEdgesChange}
|
||||||
|
onConnect={onConnect}
|
||||||
|
zIndexMode={zIndexMode}
|
||||||
|
fitView
|
||||||
|
>
|
||||||
|
<MiniMap />
|
||||||
|
<Background />
|
||||||
|
<Controls />
|
||||||
|
|
||||||
|
<Panel position="top-right">
|
||||||
|
<select value={zIndexMode} onChange={onChange} data-testid="zindexmode-select">
|
||||||
|
<option value="manual">manual</option>
|
||||||
|
<option value="basic">basic</option>
|
||||||
|
<option value="auto">auto</option>
|
||||||
|
</select>
|
||||||
|
</Panel>
|
||||||
|
</ReactFlow>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ZIndexModeFlow;
|
||||||
@@ -89,6 +89,7 @@ function EdgeWrapper<EdgeType extends Edge = Edge>({
|
|||||||
sourceNode,
|
sourceNode,
|
||||||
targetNode,
|
targetNode,
|
||||||
elevateOnSelect: store.elevateEdgesOnSelect,
|
elevateOnSelect: store.elevateEdgesOnSelect,
|
||||||
|
zIndexMode: store.zIndexMode,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -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, FitViewOptions } from '../../types';
|
import type { Node, Edge, FitViewOptions } from '../../types';
|
||||||
import { CoordinateExtent, NodeOrigin } from '@xyflow/system';
|
import { CoordinateExtent, NodeOrigin, ZIndexMode } from '@xyflow/system';
|
||||||
|
|
||||||
export type ReactFlowProviderProps = {
|
export type ReactFlowProviderProps = {
|
||||||
/** These nodes are used to initialize the flow. They are not dynamic. */
|
/** These nodes are used to initialize the flow. They are not dynamic. */
|
||||||
@@ -48,6 +48,7 @@ export type ReactFlowProviderProps = {
|
|||||||
*/
|
*/
|
||||||
nodeExtent?: CoordinateExtent;
|
nodeExtent?: CoordinateExtent;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
|
zIndexMode?: ZIndexMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -97,6 +98,7 @@ export function ReactFlowProvider({
|
|||||||
fitView,
|
fitView,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
nodeExtent,
|
nodeExtent,
|
||||||
|
zIndexMode,
|
||||||
children,
|
children,
|
||||||
}: ReactFlowProviderProps) {
|
}: ReactFlowProviderProps) {
|
||||||
const [store] = useState(() =>
|
const [store] = useState(() =>
|
||||||
@@ -113,6 +115,7 @@ export function ReactFlowProvider({
|
|||||||
fitViewOptions,
|
fitViewOptions,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
nodeExtent,
|
nodeExtent,
|
||||||
|
zIndexMode,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ const reactFlowFieldsToTrack = [
|
|||||||
'debug',
|
'debug',
|
||||||
'autoPanSpeed',
|
'autoPanSpeed',
|
||||||
'ariaLabelConfig',
|
'ariaLabelConfig',
|
||||||
|
'zIndexMode',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
type ReactFlowFieldsToTrack = (typeof reactFlowFieldsToTrack)[number];
|
type ReactFlowFieldsToTrack = (typeof reactFlowFieldsToTrack)[number];
|
||||||
|
|||||||
@@ -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, FitViewOptions } from '../../types';
|
import type { Node, Edge, FitViewOptions } from '../../types';
|
||||||
import { CoordinateExtent, NodeOrigin } from '@xyflow/system';
|
import { CoordinateExtent, NodeOrigin, ZIndexMode } from '@xyflow/system';
|
||||||
|
|
||||||
export function Wrapper({
|
export function Wrapper({
|
||||||
children,
|
children,
|
||||||
@@ -19,6 +19,7 @@ export function Wrapper({
|
|||||||
maxZoom,
|
maxZoom,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
nodeExtent,
|
nodeExtent,
|
||||||
|
zIndexMode,
|
||||||
}: {
|
}: {
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
nodes?: Node[];
|
nodes?: Node[];
|
||||||
@@ -33,6 +34,7 @@ export function Wrapper({
|
|||||||
maxZoom?: number;
|
maxZoom?: number;
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
nodeExtent?: CoordinateExtent;
|
nodeExtent?: CoordinateExtent;
|
||||||
|
zIndexMode?: ZIndexMode;
|
||||||
}) {
|
}) {
|
||||||
const isWrapped = useContext(StoreContext);
|
const isWrapped = useContext(StoreContext);
|
||||||
|
|
||||||
@@ -58,6 +60,7 @@ export function Wrapper({
|
|||||||
initialMaxZoom={maxZoom}
|
initialMaxZoom={maxZoom}
|
||||||
nodeOrigin={nodeOrigin}
|
nodeOrigin={nodeOrigin}
|
||||||
nodeExtent={nodeExtent}
|
nodeExtent={nodeExtent}
|
||||||
|
zIndexMode={zIndexMode}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</ReactFlowProvider>
|
</ReactFlowProvider>
|
||||||
|
|||||||
@@ -127,8 +127,8 @@ function ReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
|
|||||||
attributionPosition,
|
attributionPosition,
|
||||||
proOptions,
|
proOptions,
|
||||||
defaultEdgeOptions,
|
defaultEdgeOptions,
|
||||||
elevateNodesOnSelect,
|
elevateNodesOnSelect = true,
|
||||||
elevateEdgesOnSelect,
|
elevateEdgesOnSelect = false,
|
||||||
disableKeyboardA11y = false,
|
disableKeyboardA11y = false,
|
||||||
autoPanOnConnect,
|
autoPanOnConnect,
|
||||||
autoPanOnNodeDrag,
|
autoPanOnNodeDrag,
|
||||||
@@ -148,6 +148,7 @@ function ReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
|
|||||||
debug,
|
debug,
|
||||||
onScroll,
|
onScroll,
|
||||||
ariaLabelConfig,
|
ariaLabelConfig,
|
||||||
|
zIndexMode = 'basic',
|
||||||
...rest
|
...rest
|
||||||
}: ReactFlowProps<NodeType, EdgeType>,
|
}: ReactFlowProps<NodeType, EdgeType>,
|
||||||
ref: ForwardedRef<HTMLDivElement>
|
ref: ForwardedRef<HTMLDivElement>
|
||||||
@@ -186,6 +187,7 @@ function ReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
|
|||||||
maxZoom={maxZoom}
|
maxZoom={maxZoom}
|
||||||
nodeOrigin={nodeOrigin}
|
nodeOrigin={nodeOrigin}
|
||||||
nodeExtent={nodeExtent}
|
nodeExtent={nodeExtent}
|
||||||
|
zIndexMode={zIndexMode}
|
||||||
>
|
>
|
||||||
<GraphView<NodeType, EdgeType>
|
<GraphView<NodeType, EdgeType>
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
@@ -311,6 +313,7 @@ function ReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
|
|||||||
onBeforeDelete={onBeforeDelete}
|
onBeforeDelete={onBeforeDelete}
|
||||||
debug={debug}
|
debug={debug}
|
||||||
ariaLabelConfig={ariaLabelConfig}
|
ariaLabelConfig={ariaLabelConfig}
|
||||||
|
zIndexMode={zIndexMode}
|
||||||
/>
|
/>
|
||||||
<SelectionListener<NodeType, EdgeType> onSelectionChange={onSelectionChange} />
|
<SelectionListener<NodeType, EdgeType> onSelectionChange={onSelectionChange} />
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -116,6 +116,7 @@ export {
|
|||||||
type SetViewport,
|
type SetViewport,
|
||||||
type FitBounds,
|
type FitBounds,
|
||||||
type HandleConnection,
|
type HandleConnection,
|
||||||
|
type ZIndexMode,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
// we need this workaround to prevent a duplicate identifier error
|
// we need this workaround to prevent a duplicate identifier error
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
fitViewport,
|
fitViewport,
|
||||||
getHandlePosition,
|
getHandlePosition,
|
||||||
Position,
|
Position,
|
||||||
|
ZIndexMode
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import { applyEdgeChanges, applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes';
|
import { applyEdgeChanges, applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes';
|
||||||
@@ -35,6 +36,7 @@ const createStore = ({
|
|||||||
maxZoom,
|
maxZoom,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
nodeExtent,
|
nodeExtent,
|
||||||
|
zIndexMode,
|
||||||
}: {
|
}: {
|
||||||
nodes?: Node[];
|
nodes?: Node[];
|
||||||
edges?: Edge[];
|
edges?: Edge[];
|
||||||
@@ -48,6 +50,7 @@ const createStore = ({
|
|||||||
maxZoom?: number;
|
maxZoom?: number;
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
nodeExtent?: CoordinateExtent;
|
nodeExtent?: CoordinateExtent;
|
||||||
|
zIndexMode?: ZIndexMode;
|
||||||
}) =>
|
}) =>
|
||||||
createWithEqualityFn<ReactFlowState>((set, get) => {
|
createWithEqualityFn<ReactFlowState>((set, get) => {
|
||||||
async function resolveFitView() {
|
async function resolveFitView() {
|
||||||
@@ -91,9 +94,10 @@ const createStore = ({
|
|||||||
nodeExtent,
|
nodeExtent,
|
||||||
defaultNodes,
|
defaultNodes,
|
||||||
defaultEdges,
|
defaultEdges,
|
||||||
|
zIndexMode,
|
||||||
}),
|
}),
|
||||||
setNodes: (nodes: Node[]) => {
|
setNodes: (nodes: Node[]) => {
|
||||||
const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect, fitViewQueued } = get();
|
const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect, fitViewQueued, zIndexMode } = get();
|
||||||
/*
|
/*
|
||||||
* setNodes() is called exclusively in response to user actions:
|
* setNodes() is called exclusively in response to user actions:
|
||||||
* - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
|
* - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
|
||||||
@@ -108,6 +112,7 @@ const createStore = ({
|
|||||||
nodeExtent,
|
nodeExtent,
|
||||||
elevateNodesOnSelect,
|
elevateNodesOnSelect,
|
||||||
checkEquality: true,
|
checkEquality: true,
|
||||||
|
zIndexMode,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (fitViewQueued && nodesInitialized) {
|
if (fitViewQueued && nodesInitialized) {
|
||||||
@@ -142,8 +147,17 @@ const createStore = ({
|
|||||||
* new dimensions and update the nodes.
|
* new dimensions and update the nodes.
|
||||||
*/
|
*/
|
||||||
updateNodeInternals: (updates) => {
|
updateNodeInternals: (updates) => {
|
||||||
const { triggerNodeChanges, nodeLookup, parentLookup, domNode, nodeOrigin, nodeExtent, debug, fitViewQueued } =
|
const {
|
||||||
get();
|
triggerNodeChanges,
|
||||||
|
nodeLookup,
|
||||||
|
parentLookup,
|
||||||
|
domNode,
|
||||||
|
nodeOrigin,
|
||||||
|
nodeExtent,
|
||||||
|
debug,
|
||||||
|
fitViewQueued,
|
||||||
|
zIndexMode,
|
||||||
|
} = get();
|
||||||
|
|
||||||
const { changes, updatedInternals } = updateNodeInternalsSystem(
|
const { changes, updatedInternals } = updateNodeInternalsSystem(
|
||||||
updates,
|
updates,
|
||||||
@@ -151,14 +165,15 @@ const createStore = ({
|
|||||||
parentLookup,
|
parentLookup,
|
||||||
domNode,
|
domNode,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
nodeExtent
|
nodeExtent,
|
||||||
|
zIndexMode
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!updatedInternals) {
|
if (!updatedInternals) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateAbsolutePositions(nodeLookup, parentLookup, { nodeOrigin, nodeExtent });
|
updateAbsolutePositions(nodeLookup, parentLookup, { nodeOrigin, nodeExtent, zIndexMode });
|
||||||
|
|
||||||
if (fitViewQueued) {
|
if (fitViewQueued) {
|
||||||
resolveFitView();
|
resolveFitView();
|
||||||
@@ -343,7 +358,7 @@ const createStore = ({
|
|||||||
triggerEdgeChanges(edgeChanges);
|
triggerEdgeChanges(edgeChanges);
|
||||||
},
|
},
|
||||||
setNodeExtent: (nextNodeExtent) => {
|
setNodeExtent: (nextNodeExtent) => {
|
||||||
const { nodes, nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect, nodeExtent } = get();
|
const { nodes, nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect, nodeExtent, zIndexMode } = get();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
nextNodeExtent[0][0] === nodeExtent[0][0] &&
|
nextNodeExtent[0][0] === nodeExtent[0][0] &&
|
||||||
@@ -359,6 +374,7 @@ const createStore = ({
|
|||||||
nodeExtent: nextNodeExtent,
|
nodeExtent: nextNodeExtent,
|
||||||
elevateNodesOnSelect,
|
elevateNodesOnSelect,
|
||||||
checkEquality: false,
|
checkEquality: false,
|
||||||
|
zIndexMode,
|
||||||
});
|
});
|
||||||
|
|
||||||
set({ nodeExtent: nextNodeExtent });
|
set({ nodeExtent: nextNodeExtent });
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
initialConnection,
|
initialConnection,
|
||||||
CoordinateExtent,
|
CoordinateExtent,
|
||||||
defaultAriaLabelConfig,
|
defaultAriaLabelConfig,
|
||||||
|
ZIndexMode,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import type { Edge, FitViewOptions, InternalNode, Node, ReactFlowStore } from '../types';
|
import type { Edge, FitViewOptions, InternalNode, Node, ReactFlowStore } from '../types';
|
||||||
@@ -28,6 +29,7 @@ const getInitialState = ({
|
|||||||
maxZoom = 2,
|
maxZoom = 2,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
nodeExtent,
|
nodeExtent,
|
||||||
|
zIndexMode = 'basic',
|
||||||
}: {
|
}: {
|
||||||
nodes?: Node[];
|
nodes?: Node[];
|
||||||
edges?: Edge[];
|
edges?: Edge[];
|
||||||
@@ -41,6 +43,7 @@ const getInitialState = ({
|
|||||||
maxZoom?: number;
|
maxZoom?: number;
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
nodeExtent?: CoordinateExtent;
|
nodeExtent?: CoordinateExtent;
|
||||||
|
zIndexMode?: ZIndexMode;
|
||||||
} = {}): ReactFlowStore => {
|
} = {}): ReactFlowStore => {
|
||||||
const nodeLookup = new Map<string, InternalNode>();
|
const nodeLookup = new Map<string, InternalNode>();
|
||||||
const parentLookup = new Map();
|
const parentLookup = new Map();
|
||||||
@@ -56,7 +59,7 @@ const getInitialState = ({
|
|||||||
const nodesInitialized = adoptUserNodes(storeNodes, nodeLookup, parentLookup, {
|
const nodesInitialized = adoptUserNodes(storeNodes, nodeLookup, parentLookup, {
|
||||||
nodeOrigin: storeNodeOrigin,
|
nodeOrigin: storeNodeOrigin,
|
||||||
nodeExtent: storeNodeExtent,
|
nodeExtent: storeNodeExtent,
|
||||||
elevateNodesOnSelect: false,
|
zIndexMode,
|
||||||
});
|
});
|
||||||
|
|
||||||
let transform: Transform = [0, 0, 1];
|
let transform: Transform = [0, 0, 1];
|
||||||
@@ -119,7 +122,7 @@ const getInitialState = ({
|
|||||||
edgesReconnectable: true,
|
edgesReconnectable: true,
|
||||||
elementsSelectable: true,
|
elementsSelectable: true,
|
||||||
elevateNodesOnSelect: true,
|
elevateNodesOnSelect: true,
|
||||||
elevateEdgesOnSelect: false,
|
elevateEdgesOnSelect: true,
|
||||||
selectNodesOnDrag: true,
|
selectNodesOnDrag: true,
|
||||||
|
|
||||||
multiSelectionActive: false,
|
multiSelectionActive: false,
|
||||||
@@ -146,6 +149,7 @@ const getInitialState = ({
|
|||||||
lib: 'react',
|
lib: 'react',
|
||||||
debug: false,
|
debug: false,
|
||||||
ariaLabelConfig: defaultAriaLabelConfig,
|
ariaLabelConfig: defaultAriaLabelConfig,
|
||||||
|
zIndexMode,
|
||||||
|
|
||||||
onNodesChangeMiddlewareMap: new Map(),
|
onNodesChangeMiddlewareMap: new Map(),
|
||||||
onEdgesChangeMiddlewareMap: new Map(),
|
onEdgesChangeMiddlewareMap: new Map(),
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import type {
|
|||||||
OnReconnect,
|
OnReconnect,
|
||||||
AriaLabelConfig,
|
AriaLabelConfig,
|
||||||
FinalConnectionState,
|
FinalConnectionState,
|
||||||
|
ZIndexMode,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
@@ -691,4 +692,11 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
|||||||
* Allows localization, customization of ARIA descriptions, control labels, minimap labels, and other UI strings.
|
* Allows localization, customization of ARIA descriptions, control labels, minimap labels, and other UI strings.
|
||||||
*/
|
*/
|
||||||
ariaLabelConfig?: Partial<AriaLabelConfig>;
|
ariaLabelConfig?: Partial<AriaLabelConfig>;
|
||||||
|
/**
|
||||||
|
* Used to define how z-indexing is calculated for nodes and edges.
|
||||||
|
* 'auto' is for selections and sub flows, 'basic' for selections only, and 'manual' for no auto z-indexing.
|
||||||
|
*
|
||||||
|
* @default 'basic'
|
||||||
|
*/
|
||||||
|
zIndexMode?: ZIndexMode;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ import {
|
|||||||
type EdgeChange,
|
type EdgeChange,
|
||||||
type ParentLookup,
|
type ParentLookup,
|
||||||
type AriaLabelConfig,
|
type AriaLabelConfig,
|
||||||
SetCenter,
|
type SetCenter,
|
||||||
|
type ZIndexMode,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
@@ -153,6 +154,7 @@ export type ReactFlowStore<NodeType extends Node = Node, EdgeType extends Edge =
|
|||||||
debug: boolean;
|
debug: boolean;
|
||||||
ariaLabelConfig: AriaLabelConfig;
|
ariaLabelConfig: AriaLabelConfig;
|
||||||
|
|
||||||
|
zIndexMode: ZIndexMode;
|
||||||
onNodesChangeMiddlewareMap: Map<symbol, (changes: NodeChange<NodeType>[]) => NodeChange<NodeType>[]>;
|
onNodesChangeMiddlewareMap: Map<symbol, (changes: NodeChange<NodeType>[]) => NodeChange<NodeType>[]>;
|
||||||
onEdgesChangeMiddlewareMap: Map<symbol, (changes: EdgeChange<EdgeType>[]) => EdgeChange<EdgeType>[]>;
|
onEdgesChangeMiddlewareMap: Map<symbol, (changes: EdgeChange<EdgeType>[]) => EdgeChange<EdgeType>[]>;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -75,7 +75,7 @@
|
|||||||
}: SvelteFlowProps<NodeType, EdgeType> &
|
}: SvelteFlowProps<NodeType, EdgeType> &
|
||||||
Omit<HTMLAttributes<HTMLDivElement>, 'onselectionchange'> = $props();
|
Omit<HTMLAttributes<HTMLDivElement>, 'onselectionchange'> = $props();
|
||||||
|
|
||||||
// svelte-ignore non_reactive_update
|
// svelte-ignore non_reactive_update, state_referenced_locally
|
||||||
let store = createStore<NodeType, EdgeType>({
|
let store = createStore<NodeType, EdgeType>({
|
||||||
props,
|
props,
|
||||||
width,
|
width,
|
||||||
|
|||||||
@@ -96,6 +96,7 @@
|
|||||||
ariaLabelConfig,
|
ariaLabelConfig,
|
||||||
autoPanSpeed,
|
autoPanSpeed,
|
||||||
panOnScrollSpeed,
|
panOnScrollSpeed,
|
||||||
|
zIndexMode,
|
||||||
...divAttributes
|
...divAttributes
|
||||||
} = $derived(rest);
|
} = $derived(rest);
|
||||||
/* eslint-enable @typescript-eslint/no-unused-vars */
|
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ import type {
|
|||||||
OnReconnect,
|
OnReconnect,
|
||||||
OnReconnectStart,
|
OnReconnectStart,
|
||||||
OnReconnectEnd,
|
OnReconnectEnd,
|
||||||
AriaLabelConfig
|
AriaLabelConfig,
|
||||||
|
ZIndexMode
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
@@ -500,4 +501,11 @@ export type SvelteFlowProps<
|
|||||||
* Allows localization, customization of ARIA descriptions, control labels, minimap labels, and other UI strings.
|
* Allows localization, customization of ARIA descriptions, control labels, minimap labels, and other UI strings.
|
||||||
*/
|
*/
|
||||||
ariaLabelConfig?: Partial<AriaLabelConfig>;
|
ariaLabelConfig?: Partial<AriaLabelConfig>;
|
||||||
|
/**
|
||||||
|
* Used to define how z-indexing is calculated for nodes and edges.
|
||||||
|
* 'auto' is for selections and sub flows, 'basic' for selections only, and 'manual' for no auto z-indexing.
|
||||||
|
*
|
||||||
|
* @default 'basic'
|
||||||
|
*/
|
||||||
|
zIndexMode?: ZIndexMode;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -122,7 +122,8 @@ export {
|
|||||||
type SetCenter,
|
type SetCenter,
|
||||||
type SetViewport,
|
type SetViewport,
|
||||||
type FitBounds,
|
type FitBounds,
|
||||||
type HandleConnection
|
type HandleConnection,
|
||||||
|
type ZIndexMode
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
// system utils
|
// system utils
|
||||||
|
|||||||
@@ -73,7 +73,9 @@ export function createStore<NodeType extends Node = Node, EdgeType extends Edge
|
|||||||
store.nodeLookup,
|
store.nodeLookup,
|
||||||
store.parentLookup,
|
store.parentLookup,
|
||||||
store.domNode,
|
store.domNode,
|
||||||
store.nodeOrigin
|
store.nodeOrigin,
|
||||||
|
store.nodeExtent,
|
||||||
|
store.zIndexMode
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!updatedInternals) {
|
if (!updatedInternals) {
|
||||||
@@ -82,7 +84,8 @@ export function createStore<NodeType extends Node = Node, EdgeType extends Edge
|
|||||||
|
|
||||||
updateAbsolutePositions(store.nodeLookup, store.parentLookup, {
|
updateAbsolutePositions(store.nodeLookup, store.parentLookup, {
|
||||||
nodeOrigin: store.nodeOrigin,
|
nodeOrigin: store.nodeOrigin,
|
||||||
nodeExtent: store.nodeExtent
|
nodeExtent: store.nodeExtent,
|
||||||
|
zIndexMode: store.zIndexMode
|
||||||
});
|
});
|
||||||
|
|
||||||
if (store.fitViewQueued) {
|
if (store.fitViewQueued) {
|
||||||
|
|||||||
@@ -36,7 +36,8 @@ import {
|
|||||||
type OnReconnect,
|
type OnReconnect,
|
||||||
type OnReconnectStart,
|
type OnReconnectStart,
|
||||||
type OnReconnectEnd,
|
type OnReconnectEnd,
|
||||||
type AriaLabelConfig
|
type AriaLabelConfig,
|
||||||
|
type ZIndexMode
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
|
import DefaultNode from '$lib/components/nodes/DefaultNode.svelte';
|
||||||
@@ -116,13 +117,15 @@ export function getInitialStore<NodeType extends Node = Node, EdgeType extends E
|
|||||||
panZoom: PanZoomInstance | null = $state.raw(null);
|
panZoom: PanZoomInstance | null = $state.raw(null);
|
||||||
width = $state.raw<number>(signals.width ?? 0);
|
width = $state.raw<number>(signals.width ?? 0);
|
||||||
height = $state.raw<number>(signals.height ?? 0);
|
height = $state.raw<number>(signals.height ?? 0);
|
||||||
|
zIndexMode = $state.raw<ZIndexMode>(signals.props.zIndexMode ?? 'basic');
|
||||||
|
|
||||||
nodesInitialized: boolean = $derived.by(() => {
|
nodesInitialized: boolean = $derived.by(() => {
|
||||||
const nodesInitialized = adoptUserNodes(signals.nodes, this.nodeLookup, this.parentLookup, {
|
const nodesInitialized = adoptUserNodes(signals.nodes, this.nodeLookup, this.parentLookup, {
|
||||||
nodeExtent: this.nodeExtent,
|
nodeExtent: this.nodeExtent,
|
||||||
nodeOrigin: this.nodeOrigin,
|
nodeOrigin: this.nodeOrigin,
|
||||||
elevateNodesOnSelect: signals.props.elevateNodesOnSelect ?? true,
|
elevateNodesOnSelect: signals.props.elevateNodesOnSelect ?? true,
|
||||||
checkEquality: true
|
checkEquality: true,
|
||||||
|
zIndexMode: this.zIndexMode
|
||||||
});
|
});
|
||||||
|
|
||||||
if (this.fitViewQueued && nodesInitialized) {
|
if (this.fitViewQueued && nodesInitialized) {
|
||||||
@@ -226,7 +229,8 @@ export function getInitialStore<NodeType extends Node = Node, EdgeType extends E
|
|||||||
connectionMode,
|
connectionMode,
|
||||||
onerror,
|
onerror,
|
||||||
onlyRenderVisibleElements,
|
onlyRenderVisibleElements,
|
||||||
defaultEdgeOptions
|
defaultEdgeOptions,
|
||||||
|
zIndexMode
|
||||||
} = this;
|
} = this;
|
||||||
|
|
||||||
let visibleNodes: Map<string, InternalNode<NodeType>>;
|
let visibleNodes: Map<string, InternalNode<NodeType>>;
|
||||||
@@ -239,6 +243,7 @@ export function getInitialStore<NodeType extends Node = Node, EdgeType extends E
|
|||||||
nodeLookup,
|
nodeLookup,
|
||||||
connectionMode,
|
connectionMode,
|
||||||
elevateEdgesOnSelect: signals.props.elevateEdgesOnSelect ?? true,
|
elevateEdgesOnSelect: signals.props.elevateEdgesOnSelect ?? true,
|
||||||
|
zIndexMode,
|
||||||
onerror
|
onerror
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import {
|
|||||||
isEdgeVisible,
|
isEdgeVisible,
|
||||||
type NodeLookup,
|
type NodeLookup,
|
||||||
type OnError,
|
type OnError,
|
||||||
type Transform
|
type Transform,
|
||||||
|
type ZIndexMode
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
export function getVisibleNodes<NodeType extends Node = Node>(
|
export function getVisibleNodes<NodeType extends Node = Node>(
|
||||||
@@ -42,6 +43,7 @@ export interface EdgeLayoutAllOptions<NodeType extends Node = Node, EdgeType ext
|
|||||||
transform: never;
|
transform: never;
|
||||||
width: never;
|
width: never;
|
||||||
height: never;
|
height: never;
|
||||||
|
zIndexMode: never;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EdgeLayoutOnlyVisibleOptions<
|
export interface EdgeLayoutOnlyVisibleOptions<
|
||||||
@@ -53,6 +55,7 @@ export interface EdgeLayoutOnlyVisibleOptions<
|
|||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
onlyRenderVisible: true;
|
onlyRenderVisible: true;
|
||||||
|
zIndexMode: ZIndexMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EdgeLayoutOptions<NodeType extends Node = Node, EdgeType extends Edge = Edge> =
|
export type EdgeLayoutOptions<NodeType extends Node = Node, EdgeType extends Edge = Edge> =
|
||||||
@@ -70,7 +73,8 @@ export function getLayoutedEdges<NodeType extends Node = Node, EdgeType extends
|
|||||||
connectionMode,
|
connectionMode,
|
||||||
onerror,
|
onerror,
|
||||||
onlyRenderVisible,
|
onlyRenderVisible,
|
||||||
elevateEdgesOnSelect
|
elevateEdgesOnSelect,
|
||||||
|
zIndexMode
|
||||||
} = options;
|
} = options;
|
||||||
const layoutedEdges = new Map<string, EdgeLayouted<EdgeType>>();
|
const layoutedEdges = new Map<string, EdgeLayouted<EdgeType>>();
|
||||||
for (const edge of edges) {
|
for (const edge of edges) {
|
||||||
@@ -134,7 +138,8 @@ export function getLayoutedEdges<NodeType extends Node = Node, EdgeType extends
|
|||||||
zIndex: edge.zIndex ?? defaultEdgeOptions.zIndex,
|
zIndex: edge.zIndex ?? defaultEdgeOptions.zIndex,
|
||||||
sourceNode,
|
sourceNode,
|
||||||
targetNode,
|
targetNode,
|
||||||
elevateOnSelect: elevateEdgesOnSelect
|
elevateOnSelect: elevateEdgesOnSelect,
|
||||||
|
zIndexMode
|
||||||
}),
|
}),
|
||||||
sourceNode,
|
sourceNode,
|
||||||
targetNode,
|
targetNode,
|
||||||
|
|||||||
@@ -367,3 +367,13 @@ export type OnBeforeDeleteBase<NodeType extends NodeBase = NodeBase, EdgeType ex
|
|||||||
nodes: NodeType[];
|
nodes: NodeType[];
|
||||||
edges: EdgeType[];
|
edges: EdgeType[];
|
||||||
}) => Promise<boolean | { nodes: NodeType[]; edges: EdgeType[] }>;
|
}) => Promise<boolean | { nodes: NodeType[]; edges: EdgeType[] }>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The `ZIndexMode` type is used to define how z-indexing is calculated for nodes and edges.
|
||||||
|
* `auto` mode will automatically manage z-indexing for selections and sub flows.
|
||||||
|
* `basic` mode will only manage z-indexing for selections.
|
||||||
|
* `manual` mode does not apply any automatic z-indexing.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export type ZIndexMode = 'auto' | 'basic' | 'manual';
|
||||||
|
|||||||
@@ -1,4 +1,13 @@
|
|||||||
import { Connection, InternalNodeBase, Transform, errorMessages, isEdgeBase, EdgeBase } from '../..';
|
import {
|
||||||
|
Connection,
|
||||||
|
InternalNodeBase,
|
||||||
|
Transform,
|
||||||
|
errorMessages,
|
||||||
|
isEdgeBase,
|
||||||
|
EdgeBase,
|
||||||
|
ZIndexMode,
|
||||||
|
isManualZIndexMode,
|
||||||
|
} from '../..';
|
||||||
import { getOverlappingArea, boxToRect, nodeToBox, getBoundsOfBoxes, devWarn } from '../general';
|
import { getOverlappingArea, boxToRect, nodeToBox, getBoundsOfBoxes, devWarn } from '../general';
|
||||||
|
|
||||||
// this is used for straight edges and simple smoothstep edges (LTR, RTL, BTT, TTB)
|
// this is used for straight edges and simple smoothstep edges (LTR, RTL, BTT, TTB)
|
||||||
@@ -28,6 +37,7 @@ export type GetEdgeZIndexParams = {
|
|||||||
selected?: boolean;
|
selected?: boolean;
|
||||||
zIndex?: number;
|
zIndex?: number;
|
||||||
elevateOnSelect?: boolean;
|
elevateOnSelect?: boolean;
|
||||||
|
zIndexMode?: ZIndexMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,12 +51,19 @@ export function getElevatedEdgeZIndex({
|
|||||||
selected = false,
|
selected = false,
|
||||||
zIndex,
|
zIndex,
|
||||||
elevateOnSelect = false,
|
elevateOnSelect = false,
|
||||||
|
zIndexMode = 'basic',
|
||||||
}: GetEdgeZIndexParams): number {
|
}: GetEdgeZIndexParams): number {
|
||||||
|
const manualZIndexMode = isManualZIndexMode(zIndexMode);
|
||||||
|
|
||||||
if (zIndex !== undefined) {
|
if (zIndex !== undefined) {
|
||||||
return zIndex;
|
return zIndex;
|
||||||
}
|
}
|
||||||
const edgeZ = elevateOnSelect && selected ? 1000 : 0;
|
|
||||||
|
|
||||||
|
if (manualZIndexMode) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const edgeZ = elevateOnSelect && selected ? 1000 : 0;
|
||||||
const nodeZ = Math.max(
|
const nodeZ = Math.max(
|
||||||
sourceNode.parentId || (elevateOnSelect && sourceNode.selected) ? sourceNode.internals.z : 0,
|
sourceNode.parentId || (elevateOnSelect && sourceNode.selected) ? sourceNode.internals.z : 0,
|
||||||
targetNode.parentId || (elevateOnSelect && targetNode.selected) ? targetNode.internals.z : 0
|
targetNode.parentId || (elevateOnSelect && targetNode.selected) ? targetNode.internals.z : 0
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Handle, HandleConnection, infiniteExtent, NodeHandle, NodeHandleBounds } from '..';
|
import { Handle, HandleConnection, infiniteExtent, NodeHandleBounds, ZIndexMode } from '..';
|
||||||
import {
|
import {
|
||||||
NodeBase,
|
NodeBase,
|
||||||
CoordinateExtent,
|
CoordinateExtent,
|
||||||
@@ -37,6 +37,7 @@ const defaultOptions = {
|
|||||||
nodeOrigin: [0, 0] as NodeOrigin,
|
nodeOrigin: [0, 0] as NodeOrigin,
|
||||||
nodeExtent: infiniteExtent,
|
nodeExtent: infiniteExtent,
|
||||||
elevateNodesOnSelect: true,
|
elevateNodesOnSelect: true,
|
||||||
|
zIndexMode: 'basic' as ZIndexMode,
|
||||||
defaults: {},
|
defaults: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -112,21 +113,26 @@ type UpdateNodesOptions<NodeType extends NodeBase> = {
|
|||||||
nodeExtent?: CoordinateExtent;
|
nodeExtent?: CoordinateExtent;
|
||||||
elevateNodesOnSelect?: boolean;
|
elevateNodesOnSelect?: boolean;
|
||||||
defaults?: Partial<NodeType>;
|
defaults?: Partial<NodeType>;
|
||||||
|
zIndexMode?: ZIndexMode;
|
||||||
checkEquality?: boolean;
|
checkEquality?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function isManualZIndexMode(zIndexMode?: ZIndexMode): boolean {
|
||||||
|
return zIndexMode === 'manual';
|
||||||
|
}
|
||||||
|
|
||||||
export function adoptUserNodes<NodeType extends NodeBase>(
|
export function adoptUserNodes<NodeType extends NodeBase>(
|
||||||
nodes: NodeType[],
|
nodes: NodeType[],
|
||||||
nodeLookup: NodeLookup<InternalNodeBase<NodeType>>,
|
nodeLookup: NodeLookup<InternalNodeBase<NodeType>>,
|
||||||
parentLookup: ParentLookup<InternalNodeBase<NodeType>>,
|
parentLookup: ParentLookup<InternalNodeBase<NodeType>>,
|
||||||
options?: UpdateNodesOptions<NodeType>
|
options: UpdateNodesOptions<NodeType> = {}
|
||||||
): boolean {
|
): boolean {
|
||||||
const _options = mergeObjects(adoptUserNodesDefaultOptions, options);
|
const _options = mergeObjects(adoptUserNodesDefaultOptions, options);
|
||||||
|
const rootParentIndex = { i: 0 };
|
||||||
let rootParentIndex = { i: -1 };
|
|
||||||
let nodesInitialized = nodes.length > 0;
|
|
||||||
const tmpLookup = new Map(nodeLookup);
|
const tmpLookup = new Map(nodeLookup);
|
||||||
const selectedNodeZ: number = _options?.elevateNodesOnSelect ? SELECTED_NODE_Z : 0;
|
const selectedNodeZ: number =
|
||||||
|
_options?.elevateNodesOnSelect && !isManualZIndexMode(_options.zIndexMode) ? SELECTED_NODE_Z : 0;
|
||||||
|
let nodesInitialized = nodes.length > 0;
|
||||||
|
|
||||||
nodeLookup.clear();
|
nodeLookup.clear();
|
||||||
parentLookup.clear();
|
parentLookup.clear();
|
||||||
@@ -152,7 +158,7 @@ export function adoptUserNodes<NodeType extends NodeBase>(
|
|||||||
positionAbsolute: clampedPosition,
|
positionAbsolute: clampedPosition,
|
||||||
// if user re-initializes the node or removes `measured` for whatever reason, we reset the handleBounds so that the node gets re-measured
|
// if user re-initializes the node or removes `measured` for whatever reason, we reset the handleBounds so that the node gets re-measured
|
||||||
handleBounds: parseHandles(userNode, internalNode),
|
handleBounds: parseHandles(userNode, internalNode),
|
||||||
z: calculateZ(userNode, selectedNodeZ),
|
z: calculateZ(userNode, selectedNodeZ, _options.zIndexMode),
|
||||||
userNode,
|
userNode,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -201,10 +207,10 @@ function updateChildNode<NodeType extends NodeBase>(
|
|||||||
node: InternalNodeBase<NodeType>,
|
node: InternalNodeBase<NodeType>,
|
||||||
nodeLookup: NodeLookup<InternalNodeBase<NodeType>>,
|
nodeLookup: NodeLookup<InternalNodeBase<NodeType>>,
|
||||||
parentLookup: ParentLookup<InternalNodeBase<NodeType>>,
|
parentLookup: ParentLookup<InternalNodeBase<NodeType>>,
|
||||||
options?: UpdateNodesOptions<NodeType>,
|
options: UpdateNodesOptions<NodeType>,
|
||||||
rootParentIndex?: { i: number }
|
rootParentIndex?: { i: number }
|
||||||
) {
|
) {
|
||||||
const { elevateNodesOnSelect, nodeOrigin, nodeExtent } = mergeObjects(defaultOptions, options);
|
const { elevateNodesOnSelect, nodeOrigin, nodeExtent, zIndexMode } = mergeObjects(defaultOptions, options);
|
||||||
const parentId = node.parentId!;
|
const parentId = node.parentId!;
|
||||||
const parentNode = nodeLookup.get(parentId);
|
const parentNode = nodeLookup.get(parentId);
|
||||||
|
|
||||||
@@ -218,7 +224,12 @@ function updateChildNode<NodeType extends NodeBase>(
|
|||||||
updateParentLookup(node, parentLookup);
|
updateParentLookup(node, parentLookup);
|
||||||
|
|
||||||
// We just want to set the rootParentIndex for the first child
|
// We just want to set the rootParentIndex for the first child
|
||||||
if (rootParentIndex && !parentNode.parentId && parentNode.internals.rootParentIndex === undefined) {
|
if (
|
||||||
|
rootParentIndex &&
|
||||||
|
!parentNode.parentId &&
|
||||||
|
parentNode.internals.rootParentIndex === undefined &&
|
||||||
|
zIndexMode === 'auto'
|
||||||
|
) {
|
||||||
parentNode.internals.rootParentIndex = ++rootParentIndex.i;
|
parentNode.internals.rootParentIndex = ++rootParentIndex.i;
|
||||||
parentNode.internals.z = parentNode.internals.z + rootParentIndex.i * ROOT_PARENT_Z_INCREMENT;
|
parentNode.internals.z = parentNode.internals.z + rootParentIndex.i * ROOT_PARENT_Z_INCREMENT;
|
||||||
}
|
}
|
||||||
@@ -228,8 +239,8 @@ function updateChildNode<NodeType extends NodeBase>(
|
|||||||
rootParentIndex.i = parentNode.internals.rootParentIndex;
|
rootParentIndex.i = parentNode.internals.rootParentIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedNodeZ = elevateNodesOnSelect ? SELECTED_NODE_Z : 0;
|
const selectedNodeZ = elevateNodesOnSelect && !isManualZIndexMode(zIndexMode) ? SELECTED_NODE_Z : 0;
|
||||||
const { x, y, z } = calculateChildXYZ(node, parentNode, nodeOrigin, nodeExtent, selectedNodeZ);
|
const { x, y, z } = calculateChildXYZ(node, parentNode, nodeOrigin, nodeExtent, selectedNodeZ, zIndexMode);
|
||||||
const { positionAbsolute } = node.internals;
|
const { positionAbsolute } = node.internals;
|
||||||
const positionChanged = x !== positionAbsolute.x || y !== positionAbsolute.y;
|
const positionChanged = x !== positionAbsolute.x || y !== positionAbsolute.y;
|
||||||
|
|
||||||
@@ -246,8 +257,14 @@ function updateChildNode<NodeType extends NodeBase>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateZ(node: NodeBase, selectedNodeZ: number) {
|
function calculateZ(node: NodeBase, selectedNodeZ: number, zIndexMode: ZIndexMode): number {
|
||||||
return (isNumeric(node.zIndex) ? node.zIndex : 0) + (node.selected ? selectedNodeZ : 0);
|
const zIndex = isNumeric(node.zIndex) ? node.zIndex : 0;
|
||||||
|
|
||||||
|
if (isManualZIndexMode(zIndexMode)) {
|
||||||
|
return zIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return zIndex + (node.selected ? selectedNodeZ : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
function calculateChildXYZ<NodeType extends NodeBase>(
|
function calculateChildXYZ<NodeType extends NodeBase>(
|
||||||
@@ -255,7 +272,8 @@ function calculateChildXYZ<NodeType extends NodeBase>(
|
|||||||
parentNode: InternalNodeBase<NodeType>,
|
parentNode: InternalNodeBase<NodeType>,
|
||||||
nodeOrigin: NodeOrigin,
|
nodeOrigin: NodeOrigin,
|
||||||
nodeExtent: CoordinateExtent,
|
nodeExtent: CoordinateExtent,
|
||||||
selectedNodeZ: number
|
selectedNodeZ: number,
|
||||||
|
zIndexMode: ZIndexMode
|
||||||
) {
|
) {
|
||||||
const { x: parentX, y: parentY } = parentNode.internals.positionAbsolute;
|
const { x: parentX, y: parentY } = parentNode.internals.positionAbsolute;
|
||||||
const childDimensions = getNodeDimensions(childNode);
|
const childDimensions = getNodeDimensions(childNode);
|
||||||
@@ -274,7 +292,7 @@ function calculateChildXYZ<NodeType extends NodeBase>(
|
|||||||
absolutePosition = clampPositionToParent(absolutePosition, childDimensions, parentNode);
|
absolutePosition = clampPositionToParent(absolutePosition, childDimensions, parentNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
const childZ = calculateZ(childNode, selectedNodeZ);
|
const childZ = calculateZ(childNode, selectedNodeZ, zIndexMode);
|
||||||
const parentZ = parentNode.internals.z ?? 0;
|
const parentZ = parentNode.internals.z ?? 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -378,7 +396,8 @@ export function updateNodeInternals<NodeType extends InternalNodeBase>(
|
|||||||
parentLookup: ParentLookup<NodeType>,
|
parentLookup: ParentLookup<NodeType>,
|
||||||
domNode: HTMLElement | null,
|
domNode: HTMLElement | null,
|
||||||
nodeOrigin?: NodeOrigin,
|
nodeOrigin?: NodeOrigin,
|
||||||
nodeExtent?: CoordinateExtent
|
nodeExtent?: CoordinateExtent,
|
||||||
|
zIndexMode?: ZIndexMode
|
||||||
): { changes: (NodeDimensionChange | NodePositionChange)[]; updatedInternals: boolean } {
|
): { changes: (NodeDimensionChange | NodePositionChange)[]; updatedInternals: boolean } {
|
||||||
const viewportNode = domNode?.querySelector('.xyflow__viewport');
|
const viewportNode = domNode?.querySelector('.xyflow__viewport');
|
||||||
let updatedInternals = false;
|
let updatedInternals = false;
|
||||||
@@ -446,7 +465,7 @@ export function updateNodeInternals<NodeType extends InternalNodeBase>(
|
|||||||
nodeLookup.set(node.id, newNode);
|
nodeLookup.set(node.id, newNode);
|
||||||
|
|
||||||
if (node.parentId) {
|
if (node.parentId) {
|
||||||
updateChildNode(newNode, nodeLookup, parentLookup, { nodeOrigin });
|
updateChildNode(newNode, nodeLookup, parentLookup, { nodeOrigin, zIndexMode });
|
||||||
}
|
}
|
||||||
|
|
||||||
updatedInternals = true;
|
updatedInternals = true;
|
||||||
|
|||||||
Reference in New Issue
Block a user