Merge pull request #3727 from xyflow/perf/noderenderer-minimap
Perf: NodeRenderer, Minimap and Nodes
This commit is contained in:
@@ -21,7 +21,6 @@ import Interaction from '../examples/Interaction';
|
|||||||
import Intersection from '../examples/Intersection';
|
import Intersection from '../examples/Intersection';
|
||||||
import Layouting from '../examples/Layouting';
|
import Layouting from '../examples/Layouting';
|
||||||
import MultiFlows from '../examples/MultiFlows';
|
import MultiFlows from '../examples/MultiFlows';
|
||||||
import NestedNodes from '../examples/NestedNodes';
|
|
||||||
import NodeResizer from '../examples/NodeResizer';
|
import NodeResizer from '../examples/NodeResizer';
|
||||||
import NodeTypeChange from '../examples/NodeTypeChange';
|
import NodeTypeChange from '../examples/NodeTypeChange';
|
||||||
import NodeTypesObjectChange from '../examples/NodeTypesObjectChange';
|
import NodeTypesObjectChange from '../examples/NodeTypesObjectChange';
|
||||||
@@ -185,11 +184,6 @@ const routes: IRoute[] = [
|
|||||||
path: 'multiflows',
|
path: 'multiflows',
|
||||||
component: MultiFlows,
|
component: MultiFlows,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'Nested Nodes',
|
|
||||||
path: 'nested-nodes',
|
|
||||||
component: NestedNodes,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'Node Type Change',
|
name: 'Node Type Change',
|
||||||
path: 'nodetype-change',
|
path: 'nodetype-change',
|
||||||
|
|||||||
@@ -4,8 +4,12 @@ import { Node, Position, MarkerType, XYPosition } from '@xyflow/react';
|
|||||||
// of the line between the center of the intersectionNode and the target node
|
// of the line between the center of the intersectionNode and the target node
|
||||||
function getNodeIntersection(intersectionNode: Node, targetNode: Node) {
|
function getNodeIntersection(intersectionNode: Node, targetNode: Node) {
|
||||||
// https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a
|
// https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a
|
||||||
const { width: intersectionNodeWidth, height: intersectionNodeHeight } = intersectionNode;
|
|
||||||
const intersectionNodePosition = intersectionNode.computed?.positionAbsolute!;
|
const {
|
||||||
|
width: intersectionNodeWidth,
|
||||||
|
height: intersectionNodeHeight,
|
||||||
|
positionAbsolute: intersectionNodePosition,
|
||||||
|
} = intersectionNode.computed || {};
|
||||||
const targetPosition = targetNode.computed?.positionAbsolute!;
|
const targetPosition = targetNode.computed?.positionAbsolute!;
|
||||||
|
|
||||||
const w = intersectionNodeWidth! / 2;
|
const w = intersectionNodeWidth! / 2;
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import { Position, XYPosition, Node, Edge } from '@xyflow/react';
|
|||||||
function getNodeIntersection(intersectionNode: Node, targetNode: Node): XYPosition {
|
function getNodeIntersection(intersectionNode: Node, targetNode: Node): XYPosition {
|
||||||
// https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a
|
// https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a
|
||||||
|
|
||||||
const {
|
const { position: intersectionNodePosition } = intersectionNode;
|
||||||
width: intersectionNodeWidth,
|
const { width: intersectionNodeWidth, height: intersectionNodeHeight } = intersectionNode.computed ?? {
|
||||||
height: intersectionNodeHeight,
|
width: 0,
|
||||||
position: intersectionNodePosition,
|
height: 0,
|
||||||
} = intersectionNode;
|
};
|
||||||
const targetPosition = targetNode.position;
|
const targetPosition = targetNode.position;
|
||||||
|
|
||||||
const w = (intersectionNodeWidth ?? 0) / 2;
|
const w = (intersectionNodeWidth ?? 0) / 2;
|
||||||
|
|||||||
@@ -48,8 +48,10 @@ const initialEdges: Edge[] = [
|
|||||||
|
|
||||||
const setHidden = (hidden: boolean) => (els: any[]) =>
|
const setHidden = (hidden: boolean) => (els: any[]) =>
|
||||||
els.map((e: any) => {
|
els.map((e: any) => {
|
||||||
e.hidden = hidden;
|
return {
|
||||||
return e;
|
...e,
|
||||||
|
hidden,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const HiddenFlow = () => {
|
const HiddenFlow = () => {
|
||||||
|
|||||||
@@ -56,16 +56,16 @@ const LayoutFlow = () => {
|
|||||||
|
|
||||||
const layoutedNodes = nodes.map((node) => {
|
const layoutedNodes = nodes.map((node) => {
|
||||||
const nodeWithPosition = dagreGraph.node(node.id);
|
const nodeWithPosition = dagreGraph.node(node.id);
|
||||||
node.targetPosition = isHorizontal ? Position.Left : Position.Top;
|
|
||||||
node.sourcePosition = isHorizontal ? Position.Right : Position.Bottom;
|
|
||||||
// we need to pass a slightly different position in order to notify react flow about the change
|
|
||||||
// @TODO how can we change the position handling so that we dont need this hack?
|
|
||||||
node.position = {
|
|
||||||
x: nodeWithPosition.x + Math.random() / 1000,
|
|
||||||
y: nodeWithPosition.y,
|
|
||||||
};
|
|
||||||
|
|
||||||
return node;
|
return {
|
||||||
|
...node,
|
||||||
|
targetPosition: isHorizontal ? Position.Left : Position.Top,
|
||||||
|
sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
|
||||||
|
position: {
|
||||||
|
x: nodeWithPosition.x,
|
||||||
|
y: nodeWithPosition.y,
|
||||||
|
},
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
setNodes(layoutedNodes);
|
setNodes(layoutedNodes);
|
||||||
|
|||||||
@@ -1,187 +0,0 @@
|
|||||||
import { useState, MouseEvent, useCallback } from 'react';
|
|
||||||
import {
|
|
||||||
ReactFlow,
|
|
||||||
Controls,
|
|
||||||
MiniMap,
|
|
||||||
Background,
|
|
||||||
addEdge,
|
|
||||||
useNodesState,
|
|
||||||
useEdgesState,
|
|
||||||
Node,
|
|
||||||
Edge,
|
|
||||||
ReactFlowInstance,
|
|
||||||
Connection,
|
|
||||||
} from '@xyflow/react';
|
|
||||||
|
|
||||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
|
||||||
const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node);
|
|
||||||
const onEdgeClick = (_: MouseEvent, edge: Edge) => console.log('click', edge);
|
|
||||||
|
|
||||||
const initialNodes: Node[] = [
|
|
||||||
{
|
|
||||||
id: '1',
|
|
||||||
type: 'input',
|
|
||||||
data: { label: 'Node 1' },
|
|
||||||
position: { x: 250, y: 5 },
|
|
||||||
className: 'light',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2',
|
|
||||||
data: { label: 'Node 2' },
|
|
||||||
position: { x: 100, y: 100 },
|
|
||||||
className: 'light',
|
|
||||||
style: { backgroundColor: 'rgba(255, 0, 0, 0.8)', width: 200, height: 200 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2a',
|
|
||||||
data: { label: 'Node 2a' },
|
|
||||||
position: { x: 10, y: 50 },
|
|
||||||
parentNode: '2',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '3',
|
|
||||||
data: { label: 'Node 3' },
|
|
||||||
position: { x: 320, y: 100 },
|
|
||||||
className: 'light',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4',
|
|
||||||
data: { label: 'Node 4' },
|
|
||||||
position: { x: 320, y: 200 },
|
|
||||||
className: 'light',
|
|
||||||
style: { backgroundColor: 'rgba(255, 0, 0, 0.7)', width: 300, height: 300 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4a',
|
|
||||||
data: { label: 'Node 4a' },
|
|
||||||
position: { x: 15, y: 65 },
|
|
||||||
className: 'light',
|
|
||||||
parentNode: '4',
|
|
||||||
extent: 'parent',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4b',
|
|
||||||
data: { label: 'Node 4b' },
|
|
||||||
position: { x: 15, y: 120 },
|
|
||||||
className: 'light',
|
|
||||||
style: {
|
|
||||||
backgroundColor: 'rgba(255, 0, 255, 0.7)',
|
|
||||||
height: 150,
|
|
||||||
width: 270,
|
|
||||||
},
|
|
||||||
parentNode: '4',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4b1',
|
|
||||||
data: { label: 'Node 4b1' },
|
|
||||||
position: { x: 20, y: 40 },
|
|
||||||
className: 'light',
|
|
||||||
parentNode: '4b',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '4b2',
|
|
||||||
data: { label: 'Node 4b2' },
|
|
||||||
position: { x: 100, y: 100 },
|
|
||||||
className: 'light',
|
|
||||||
parentNode: '4b',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const initialEdges: Edge[] = [
|
|
||||||
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
|
||||||
{ id: 'e1-3', source: '1', target: '3' },
|
|
||||||
{ id: 'e2a-4a', source: '2a', target: '4a' },
|
|
||||||
{ id: 'e3-4', source: '3', target: '4' },
|
|
||||||
{ id: 'e3-4b', source: '3', target: '4b' },
|
|
||||||
{ id: 'e4a-4b1', source: '4a', target: '4b1' },
|
|
||||||
{ id: 'e4a-4b2', source: '4a', target: '4b2' },
|
|
||||||
{ id: 'e4b1-4b2', source: '4b1', target: '4b2' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const NestedFlow = () => {
|
|
||||||
const [rfInstance, setRfInstance] = useState<ReactFlowInstance | null>(null);
|
|
||||||
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
|
||||||
|
|
||||||
const onConnect = useCallback(
|
|
||||||
(connection: Connection) => {
|
|
||||||
setEdges((eds) => addEdge(connection, eds));
|
|
||||||
},
|
|
||||||
[setEdges]
|
|
||||||
);
|
|
||||||
const onInit = useCallback((reactFlowInstance: ReactFlowInstance) => setRfInstance(reactFlowInstance), []);
|
|
||||||
|
|
||||||
const updatePos = () => {
|
|
||||||
setNodes((nds) => {
|
|
||||||
return nds.map((n) => {
|
|
||||||
n.position = {
|
|
||||||
x: Math.random() * 400,
|
|
||||||
y: Math.random() * 400,
|
|
||||||
};
|
|
||||||
|
|
||||||
return n;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const logToObject = () => console.log(rfInstance?.toObject());
|
|
||||||
const resetTransform = () => rfInstance?.setViewport({ x: 0, y: 0, zoom: 1 });
|
|
||||||
|
|
||||||
const toggleClassnames = () => {
|
|
||||||
setNodes((nds) => {
|
|
||||||
return nds.map((n) => {
|
|
||||||
n.className = n.className === 'light' ? 'dark' : 'light';
|
|
||||||
return n;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleChildNodes = () => {
|
|
||||||
setNodes((nds) => {
|
|
||||||
return nds.map((n) => {
|
|
||||||
n.hidden = !!n.parentNode && !n.hidden;
|
|
||||||
return n;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlow
|
|
||||||
nodes={nodes}
|
|
||||||
edges={edges}
|
|
||||||
onInit={onInit}
|
|
||||||
onNodesChange={onNodesChange}
|
|
||||||
onEdgesChange={onEdgesChange}
|
|
||||||
onNodeClick={onNodeClick}
|
|
||||||
onEdgeClick={onEdgeClick}
|
|
||||||
onConnect={onConnect}
|
|
||||||
onNodeDragStop={onNodeDragStop}
|
|
||||||
className="react-flow-basic-example"
|
|
||||||
minZoom={0.2}
|
|
||||||
maxZoom={4}
|
|
||||||
onlyRenderVisibleElements={false}
|
|
||||||
>
|
|
||||||
<MiniMap pannable />
|
|
||||||
<Controls />
|
|
||||||
<Background />
|
|
||||||
|
|
||||||
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
|
|
||||||
<button onClick={resetTransform} style={{ marginRight: 5 }}>
|
|
||||||
reset transform
|
|
||||||
</button>
|
|
||||||
<button onClick={updatePos} style={{ marginRight: 5 }}>
|
|
||||||
change pos
|
|
||||||
</button>
|
|
||||||
<button onClick={toggleClassnames} style={{ marginRight: 5 }}>
|
|
||||||
toggle classnames
|
|
||||||
</button>
|
|
||||||
<button style={{ marginRight: 5 }} onClick={toggleChildNodes}>
|
|
||||||
toggleChildNodes
|
|
||||||
</button>
|
|
||||||
<button onClick={logToObject}>toObject</button>
|
|
||||||
</div>
|
|
||||||
</ReactFlow>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default NestedFlow;
|
|
||||||
@@ -64,7 +64,6 @@ const StressFlow = () => {
|
|||||||
minZoom={0.2}
|
minZoom={0.2}
|
||||||
fitView
|
fitView
|
||||||
>
|
>
|
||||||
<MiniMap />
|
|
||||||
<Controls />
|
<Controls />
|
||||||
<Background />
|
<Background />
|
||||||
|
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ const idStyle: CSSProperties = {
|
|||||||
left: 2,
|
left: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DebugNode: FC<NodeProps> = ({ zIndex, positionAbsolute, id }) => {
|
const DebugNode: FC<NodeProps> = ({ zIndex, positionAbsoluteX, positionAbsoluteY, id }) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Handle type="target" position={Position.Top} />
|
<Handle type="target" position={Position.Top} />
|
||||||
<div style={idStyle}>{id}</div>
|
<div style={idStyle}>{id}</div>
|
||||||
<div style={infoStyle}>
|
<div style={infoStyle}>
|
||||||
x:{Math.round(positionAbsolute.x)} y:{Math.round(positionAbsolute.y)} z:{zIndex}
|
x:{Math.round(positionAbsoluteX)} y:{Math.round(positionAbsoluteY)} z:{zIndex}
|
||||||
</div>
|
</div>
|
||||||
<Handle type="source" position={Position.Bottom} />
|
<Handle type="source" position={Position.Bottom} />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -24,9 +24,12 @@ const UpdateNode = () => {
|
|||||||
nds.map((n) => {
|
nds.map((n) => {
|
||||||
if (n.id === '1') {
|
if (n.id === '1') {
|
||||||
// it's important that you create a new object here in order to notify react flow about the change
|
// it's important that you create a new object here in order to notify react flow about the change
|
||||||
n.data = {
|
return {
|
||||||
...n.data,
|
...n,
|
||||||
label: nodeName,
|
data: {
|
||||||
|
...n.data,
|
||||||
|
label: nodeName,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,7 +43,10 @@ const UpdateNode = () => {
|
|||||||
nds.map((n) => {
|
nds.map((n) => {
|
||||||
if (n.id === '1') {
|
if (n.id === '1') {
|
||||||
// it's important that you create a new object here in order to notify react flow about the change
|
// it's important that you create a new object here in order to notify react flow about the change
|
||||||
n.style = { ...n.style, backgroundColor: nodeBg };
|
return {
|
||||||
|
...n,
|
||||||
|
style: { ...n.style, backgroundColor: nodeBg },
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
@@ -52,8 +58,10 @@ const UpdateNode = () => {
|
|||||||
setNodes((nds) =>
|
setNodes((nds) =>
|
||||||
nds.map((n) => {
|
nds.map((n) => {
|
||||||
if (n.id === '1' || n.id === 'e1-2') {
|
if (n.id === '1' || n.id === 'e1-2') {
|
||||||
// when you update a simple type you can just update the value
|
return {
|
||||||
n.hidden = nodeHidden;
|
...n,
|
||||||
|
hidden: nodeHidden,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ function CustomHandle({ nodeId, ...handleProps }: HandleComponentProps & { nodeI
|
|||||||
[nodeId]
|
[nodeId]
|
||||||
);
|
);
|
||||||
const connections = useHandleConnections({
|
const connections = useHandleConnections({
|
||||||
handleType: handleProps.type,
|
type: handleProps.type,
|
||||||
handleId: handleProps.id,
|
id: handleProps.id,
|
||||||
onConnect,
|
onConnect,
|
||||||
onDisconnect,
|
onDisconnect,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ function CustomHandle({ nodeId, ...handleProps }: HandleComponentProps & { nodeI
|
|||||||
[nodeId]
|
[nodeId]
|
||||||
);
|
);
|
||||||
const connections = useHandleConnections({
|
const connections = useHandleConnections({
|
||||||
handleType: handleProps.type,
|
type: handleProps.type,
|
||||||
handleId: handleProps.id,
|
id: handleProps.id,
|
||||||
onConnect,
|
onConnect,
|
||||||
onDisconnect,
|
onDisconnect,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Handle, Position, type NodeProps, type XYPosition } from '@xyflow/svelte';
|
import { Handle, Position, type NodeProps } from '@xyflow/svelte';
|
||||||
|
|
||||||
type $$Props = NodeProps;
|
type $$Props = NodeProps;
|
||||||
|
|
||||||
export let data: { label: string } = { label: 'Node' };
|
export let data: { label: string } = { label: 'Node' };
|
||||||
export let positionAbsolute: XYPosition = { x: 0, y: 0 };
|
export let positionAbsoluteX: number = 0;
|
||||||
|
export let positionAbsoluteY: number = 0;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="custom">
|
<div class="custom">
|
||||||
<div>{data.label}</div>
|
<div>{data.label}</div>
|
||||||
<div>{~~positionAbsolute.x}, {~~positionAbsolute.y}</div>
|
<div>{~~positionAbsoluteX}, {~~positionAbsoluteY}</div>
|
||||||
|
|
||||||
<Handle type="target" position={Position.Top} />
|
<Handle type="target" position={Position.Top} />
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,18 @@
|
|||||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import { memo } from 'react';
|
import { ComponentType, memo } from 'react';
|
||||||
|
import { NodeOrigin, getNodePositionWithOrigin } from '@xyflow/system';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
import { getNodePositionWithOrigin } from '@xyflow/system';
|
|
||||||
|
|
||||||
import { useStore } from '../../hooks/useStore';
|
import { useStore } from '../../hooks/useStore';
|
||||||
import type { ReactFlowState } from '../../types';
|
import type { ReactFlowState } from '../../types';
|
||||||
import MiniMapNode from './MiniMapNode';
|
import MiniMapNode from './MiniMapNode';
|
||||||
import type { MiniMapNodes, GetMiniMapNodeAttribute } from './types';
|
import type { MiniMapNodes as MiniMapNodesProps, GetMiniMapNodeAttribute, MiniMapNodeProps } from './types';
|
||||||
|
|
||||||
declare const window: any;
|
declare const window: any;
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => s.nodeOrigin;
|
const selector = (s: ReactFlowState) => s.nodeOrigin;
|
||||||
const selectorNodes = (s: ReactFlowState) =>
|
const selectorNodeIds = (s: ReactFlowState) => s.nodes.map((node) => node.id);
|
||||||
s.nodes.filter(
|
|
||||||
(node) => !node.hidden && (node.computed?.width || node.width) && (node.computed?.height || node.height)
|
|
||||||
);
|
|
||||||
const getAttrFunction = (func: any): GetMiniMapNodeAttribute => (func instanceof Function ? func : () => func);
|
const getAttrFunction = (func: any): GetMiniMapNodeAttribute => (func instanceof Function ? func : () => func);
|
||||||
|
|
||||||
function MiniMapNodes({
|
function MiniMapNodes({
|
||||||
@@ -28,8 +25,8 @@ function MiniMapNodes({
|
|||||||
// a component properly.
|
// a component properly.
|
||||||
nodeComponent: NodeComponent = MiniMapNode,
|
nodeComponent: NodeComponent = MiniMapNode,
|
||||||
onClick,
|
onClick,
|
||||||
}: MiniMapNodes) {
|
}: MiniMapNodesProps) {
|
||||||
const nodes = useStore(selectorNodes, shallow);
|
const nodeIds = useStore(selectorNodeIds, shallow);
|
||||||
const nodeOrigin = useStore(selector);
|
const nodeOrigin = useStore(selector);
|
||||||
const nodeColorFunc = getAttrFunction(nodeColor);
|
const nodeColorFunc = getAttrFunction(nodeColor);
|
||||||
const nodeStrokeColorFunc = getAttrFunction(nodeStrokeColor);
|
const nodeStrokeColorFunc = getAttrFunction(nodeStrokeColor);
|
||||||
@@ -39,33 +36,78 @@ function MiniMapNodes({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{nodes.map((node) => {
|
{nodeIds.map((nodeId) => (
|
||||||
const { x, y } = getNodePositionWithOrigin(node, node.origin || nodeOrigin).positionAbsolute;
|
// The split of responsibilities between MiniMapNodes and
|
||||||
const color = nodeColor === undefined ? undefined : nodeColorFunc(node);
|
// NodeComponentWrapper may appear weird. However, it’s designed to
|
||||||
const strokeColor = nodeStrokeColor === undefined ? undefined : nodeStrokeColorFunc(node);
|
// minimize the cost of updates when individual nodes change.
|
||||||
|
//
|
||||||
return (
|
// For more details, see a similar commit in `NodeRenderer/index.tsx`.
|
||||||
<NodeComponent
|
<NodeComponentWrapper
|
||||||
key={node.id}
|
key={nodeId}
|
||||||
x={x}
|
id={nodeId}
|
||||||
y={y}
|
nodeOrigin={nodeOrigin}
|
||||||
width={node.computed?.width ?? node.width ?? 0}
|
nodeColorFunc={nodeColorFunc}
|
||||||
height={node.computed?.height ?? node.height ?? 0}
|
nodeStrokeColorFunc={nodeStrokeColorFunc}
|
||||||
style={node.style}
|
nodeClassNameFunc={nodeClassNameFunc}
|
||||||
selected={!!node.selected}
|
nodeBorderRadius={nodeBorderRadius}
|
||||||
className={nodeClassNameFunc(node)}
|
nodeStrokeWidth={nodeStrokeWidth}
|
||||||
color={color}
|
NodeComponent={NodeComponent}
|
||||||
borderRadius={nodeBorderRadius}
|
onClick={onClick}
|
||||||
strokeColor={strokeColor}
|
shapeRendering={shapeRendering}
|
||||||
strokeWidth={nodeStrokeWidth}
|
/>
|
||||||
shapeRendering={shapeRendering}
|
))}
|
||||||
onClick={onClick}
|
|
||||||
id={node.id}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const NodeComponentWrapper = memo(function NodeComponentWrapper({
|
||||||
|
id,
|
||||||
|
nodeOrigin,
|
||||||
|
nodeColorFunc,
|
||||||
|
nodeStrokeColorFunc,
|
||||||
|
nodeClassNameFunc,
|
||||||
|
nodeBorderRadius,
|
||||||
|
nodeStrokeWidth,
|
||||||
|
shapeRendering,
|
||||||
|
NodeComponent,
|
||||||
|
onClick,
|
||||||
|
}: {
|
||||||
|
id: string;
|
||||||
|
nodeOrigin: NodeOrigin;
|
||||||
|
nodeColorFunc: GetMiniMapNodeAttribute;
|
||||||
|
nodeStrokeColorFunc: GetMiniMapNodeAttribute;
|
||||||
|
nodeClassNameFunc: GetMiniMapNodeAttribute;
|
||||||
|
nodeBorderRadius: number;
|
||||||
|
nodeStrokeWidth?: number;
|
||||||
|
NodeComponent: ComponentType<MiniMapNodeProps>;
|
||||||
|
onClick: MiniMapNodesProps['onClick'];
|
||||||
|
shapeRendering: string;
|
||||||
|
}) {
|
||||||
|
const node = useStore((s) => s.nodeLookup.get(id));
|
||||||
|
if (!node || node.hidden || !(node.computed?.width || node.width) || !(node.computed?.height || node.height)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const positionOrigin = getNodePositionWithOrigin(node, node.origin || nodeOrigin).positionAbsolute;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<NodeComponent
|
||||||
|
x={positionOrigin.x}
|
||||||
|
y={positionOrigin.y}
|
||||||
|
width={node.computed?.width ?? node.width ?? 0}
|
||||||
|
height={node.computed?.height ?? node.height ?? 0}
|
||||||
|
style={node.style}
|
||||||
|
selected={!!node.selected}
|
||||||
|
className={nodeClassNameFunc(node)}
|
||||||
|
color={nodeColorFunc(node)}
|
||||||
|
borderRadius={nodeBorderRadius}
|
||||||
|
strokeColor={nodeStrokeColorFunc(node)}
|
||||||
|
strokeWidth={nodeStrokeWidth}
|
||||||
|
shapeRendering={shapeRendering}
|
||||||
|
onClick={onClick}
|
||||||
|
id={node.id}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
export default memo(MiniMapNodes);
|
export default memo(MiniMapNodes);
|
||||||
|
|||||||
@@ -9,16 +9,20 @@ import { useNodeId } from '../../contexts/NodeIdContext';
|
|||||||
import NodeToolbarPortal from './NodeToolbarPortal';
|
import NodeToolbarPortal from './NodeToolbarPortal';
|
||||||
import { NodeToolbarProps } from './types';
|
import { NodeToolbarProps } from './types';
|
||||||
|
|
||||||
const nodeEqualityFn = (a: Node | undefined, b: Node | undefined) =>
|
const nodeEqualityFn = (a?: Node, b?: Node) =>
|
||||||
a?.computed?.positionAbsolute?.x === b?.computed?.positionAbsolute?.x &&
|
a?.computed?.positionAbsolute?.x !== b?.computed?.positionAbsolute?.x ||
|
||||||
a?.computed?.positionAbsolute?.y === b?.computed?.positionAbsolute?.y &&
|
a?.computed?.positionAbsolute?.y !== b?.computed?.positionAbsolute?.y ||
|
||||||
a?.width === b?.width &&
|
a?.computed?.width !== b?.computed?.width ||
|
||||||
a?.height === b?.height &&
|
a?.computed?.height !== b?.computed?.height ||
|
||||||
a?.selected === b?.selected &&
|
a?.selected !== b?.selected ||
|
||||||
a?.[internalsSymbol]?.z === b?.[internalsSymbol]?.z;
|
a?.[internalsSymbol]?.z !== b?.[internalsSymbol]?.z;
|
||||||
|
|
||||||
const nodesEqualityFn = (a: Node[], b: Node[]) => {
|
const nodesEqualityFn = (a: Node[], b: Node[]) => {
|
||||||
return a.length === b.length && a.every((node, i) => nodeEqualityFn(node, b[i]));
|
if (a.length !== b.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !a.some((node, i) => nodeEqualityFn(node, b[i]));
|
||||||
};
|
};
|
||||||
|
|
||||||
const storeSelector = (state: ReactFlowState) => ({
|
const storeSelector = (state: ReactFlowState) => ({
|
||||||
|
|||||||
@@ -0,0 +1,307 @@
|
|||||||
|
import { memo, useState, useMemo, useRef, type KeyboardEvent, useCallback } from 'react';
|
||||||
|
import cc from 'classcat';
|
||||||
|
import { shallow } from 'zustand/shallow';
|
||||||
|
import {
|
||||||
|
getMarkerId,
|
||||||
|
elementSelectionKeys,
|
||||||
|
XYHandle,
|
||||||
|
type Connection,
|
||||||
|
getEdgePosition,
|
||||||
|
errorMessages,
|
||||||
|
} from '@xyflow/system';
|
||||||
|
|
||||||
|
import { useStoreApi, useStore } from '../../hooks/useStore';
|
||||||
|
import { ARIA_EDGE_DESC_KEY } from '../A11yDescriptions';
|
||||||
|
import { EdgeAnchor } from '../Edges/EdgeAnchor';
|
||||||
|
import { getMouseHandler } from '../Edges/utils';
|
||||||
|
import type { EdgeWrapperProps } from '../../types';
|
||||||
|
import { builtinEdgeTypes } from './utils';
|
||||||
|
|
||||||
|
function EdgeWrapper({
|
||||||
|
id,
|
||||||
|
className,
|
||||||
|
type,
|
||||||
|
data,
|
||||||
|
onClick,
|
||||||
|
onEdgeDoubleClick,
|
||||||
|
selected,
|
||||||
|
animated,
|
||||||
|
label,
|
||||||
|
labelStyle,
|
||||||
|
labelShowBg,
|
||||||
|
labelBgStyle,
|
||||||
|
labelBgPadding,
|
||||||
|
labelBgBorderRadius,
|
||||||
|
style,
|
||||||
|
source,
|
||||||
|
target,
|
||||||
|
isSelectable,
|
||||||
|
hidden,
|
||||||
|
sourceHandleId,
|
||||||
|
targetHandleId,
|
||||||
|
onContextMenu,
|
||||||
|
onMouseEnter,
|
||||||
|
onMouseMove,
|
||||||
|
onMouseLeave,
|
||||||
|
edgeUpdaterRadius,
|
||||||
|
onEdgeUpdate,
|
||||||
|
onEdgeUpdateStart,
|
||||||
|
onEdgeUpdateEnd,
|
||||||
|
markerEnd,
|
||||||
|
markerStart,
|
||||||
|
rfId,
|
||||||
|
ariaLabel,
|
||||||
|
isFocusable,
|
||||||
|
isUpdatable,
|
||||||
|
pathOptions,
|
||||||
|
interactionWidth,
|
||||||
|
edgeTypes,
|
||||||
|
onError,
|
||||||
|
}: EdgeWrapperProps): JSX.Element | null {
|
||||||
|
let edgeType = type || 'default';
|
||||||
|
let EdgeComponent = edgeTypes?.[edgeType] || builtinEdgeTypes[edgeType];
|
||||||
|
|
||||||
|
if (EdgeComponent === undefined) {
|
||||||
|
onError?.('011', errorMessages['error011'](edgeType));
|
||||||
|
edgeType = 'default';
|
||||||
|
EdgeComponent = builtinEdgeTypes.default;
|
||||||
|
}
|
||||||
|
|
||||||
|
const edgeRef = useRef<SVGGElement>(null);
|
||||||
|
const [updateHover, setUpdateHover] = useState<boolean>(false);
|
||||||
|
const [updating, setUpdating] = useState<boolean>(false);
|
||||||
|
const store = useStoreApi();
|
||||||
|
const edgePosition = useStore(
|
||||||
|
useCallback(
|
||||||
|
(state) => {
|
||||||
|
const sourceNode = state.nodeLookup.get(source);
|
||||||
|
const targetNode = state.nodeLookup.get(target);
|
||||||
|
|
||||||
|
if (!sourceNode || !targetNode) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getEdgePosition({
|
||||||
|
id,
|
||||||
|
sourceNode,
|
||||||
|
targetNode,
|
||||||
|
sourceHandle: sourceHandleId || null,
|
||||||
|
targetHandle: targetHandleId || null,
|
||||||
|
connectionMode: state.connectionMode,
|
||||||
|
onError: state.onError,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[source, target]
|
||||||
|
),
|
||||||
|
shallow
|
||||||
|
);
|
||||||
|
|
||||||
|
const markerStartUrl = useMemo(() => `url(#${getMarkerId(markerStart, rfId)})`, [markerStart, rfId]);
|
||||||
|
const markerEndUrl = useMemo(() => `url(#${getMarkerId(markerEnd, rfId)})`, [markerEnd, rfId]);
|
||||||
|
|
||||||
|
if (hidden || !edgePosition) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const onEdgeClick = (event: React.MouseEvent<SVGGElement, MouseEvent>): void => {
|
||||||
|
const { edges, addSelectedEdges, unselectNodesAndEdges, multiSelectionActive } = store.getState();
|
||||||
|
const edge = edges.find((e) => e.id === id);
|
||||||
|
|
||||||
|
if (!edge) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSelectable) {
|
||||||
|
store.setState({ nodesSelectionActive: false });
|
||||||
|
|
||||||
|
if (edge.selected && multiSelectionActive) {
|
||||||
|
unselectNodesAndEdges({ nodes: [], edges: [edge] });
|
||||||
|
edgeRef.current?.blur();
|
||||||
|
} else {
|
||||||
|
addSelectedEdges([id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onClick) {
|
||||||
|
onClick(event, edge);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onEdgeDoubleClickHandler = getMouseHandler(id, store.getState, onEdgeDoubleClick);
|
||||||
|
const onEdgeContextMenu = getMouseHandler(id, store.getState, onContextMenu);
|
||||||
|
const onEdgeMouseEnter = getMouseHandler(id, store.getState, onMouseEnter);
|
||||||
|
const onEdgeMouseMove = getMouseHandler(id, store.getState, onMouseMove);
|
||||||
|
const onEdgeMouseLeave = getMouseHandler(id, store.getState, onMouseLeave);
|
||||||
|
|
||||||
|
const handleEdgeUpdater = (event: React.MouseEvent<SVGGElement, MouseEvent>, isSourceHandle: boolean) => {
|
||||||
|
// avoid triggering edge updater if mouse btn is not left
|
||||||
|
if (event.button !== 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
autoPanOnConnect,
|
||||||
|
domNode,
|
||||||
|
edges,
|
||||||
|
isValidConnection,
|
||||||
|
connectionMode,
|
||||||
|
connectionRadius,
|
||||||
|
lib,
|
||||||
|
onConnectStart,
|
||||||
|
onConnectEnd,
|
||||||
|
cancelConnection,
|
||||||
|
nodes,
|
||||||
|
panBy,
|
||||||
|
updateConnection,
|
||||||
|
} = store.getState();
|
||||||
|
const nodeId = isSourceHandle ? target : source;
|
||||||
|
const handleId = (isSourceHandle ? targetHandleId : sourceHandleId) || null;
|
||||||
|
const handleType = isSourceHandle ? 'target' : 'source';
|
||||||
|
|
||||||
|
const isTarget = isSourceHandle;
|
||||||
|
const edge = edges.find((e) => e.id === id)!;
|
||||||
|
|
||||||
|
setUpdating(true);
|
||||||
|
onEdgeUpdateStart?.(event, edge, handleType);
|
||||||
|
|
||||||
|
const _onEdgeUpdateEnd = (evt: MouseEvent | TouchEvent) => {
|
||||||
|
setUpdating(false);
|
||||||
|
onEdgeUpdateEnd?.(evt, edge, handleType);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onConnectEdge = (connection: Connection) => onEdgeUpdate?.(edge, connection);
|
||||||
|
|
||||||
|
XYHandle.onPointerDown(event.nativeEvent, {
|
||||||
|
autoPanOnConnect,
|
||||||
|
connectionMode,
|
||||||
|
connectionRadius,
|
||||||
|
domNode,
|
||||||
|
handleId,
|
||||||
|
nodeId,
|
||||||
|
nodes,
|
||||||
|
isTarget,
|
||||||
|
edgeUpdaterType: handleType,
|
||||||
|
lib,
|
||||||
|
cancelConnection,
|
||||||
|
panBy,
|
||||||
|
isValidConnection,
|
||||||
|
onConnect: onConnectEdge,
|
||||||
|
onConnectStart,
|
||||||
|
onConnectEnd,
|
||||||
|
onEdgeUpdateEnd: _onEdgeUpdateEnd,
|
||||||
|
updateConnection,
|
||||||
|
getTransform: () => store.getState().transform,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onEdgeUpdaterSourceMouseDown = (event: React.MouseEvent<SVGGElement, MouseEvent>): void =>
|
||||||
|
handleEdgeUpdater(event, true);
|
||||||
|
const onEdgeUpdaterTargetMouseDown = (event: React.MouseEvent<SVGGElement, MouseEvent>): void =>
|
||||||
|
handleEdgeUpdater(event, false);
|
||||||
|
|
||||||
|
const onEdgeUpdaterMouseEnter = () => setUpdateHover(true);
|
||||||
|
const onEdgeUpdaterMouseOut = () => setUpdateHover(false);
|
||||||
|
|
||||||
|
const inactive = !isSelectable && !onClick;
|
||||||
|
|
||||||
|
const onKeyDown = (event: KeyboardEvent) => {
|
||||||
|
if (elementSelectionKeys.includes(event.key) && isSelectable) {
|
||||||
|
const { unselectNodesAndEdges, addSelectedEdges, edges } = store.getState();
|
||||||
|
const unselect = event.key === 'Escape';
|
||||||
|
|
||||||
|
if (unselect) {
|
||||||
|
edgeRef.current?.blur();
|
||||||
|
unselectNodesAndEdges({ edges: [edges.find((e) => e.id === id)!] });
|
||||||
|
} else {
|
||||||
|
addSelectedEdges([id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<g
|
||||||
|
className={cc([
|
||||||
|
'react-flow__edge',
|
||||||
|
`react-flow__edge-${type}`,
|
||||||
|
className,
|
||||||
|
{ selected, animated, inactive, updating: updateHover },
|
||||||
|
])}
|
||||||
|
onClick={onEdgeClick}
|
||||||
|
onDoubleClick={onEdgeDoubleClickHandler}
|
||||||
|
onContextMenu={onEdgeContextMenu}
|
||||||
|
onMouseEnter={onEdgeMouseEnter}
|
||||||
|
onMouseMove={onEdgeMouseMove}
|
||||||
|
onMouseLeave={onEdgeMouseLeave}
|
||||||
|
onKeyDown={isFocusable ? onKeyDown : undefined}
|
||||||
|
tabIndex={isFocusable ? 0 : undefined}
|
||||||
|
role={isFocusable ? 'button' : 'img'}
|
||||||
|
data-id={id}
|
||||||
|
data-testid={`rf__edge-${id}`}
|
||||||
|
aria-label={ariaLabel === null ? undefined : ariaLabel ? ariaLabel : `Edge from ${source} to ${target}`}
|
||||||
|
aria-describedby={isFocusable ? `${ARIA_EDGE_DESC_KEY}-${rfId}` : undefined}
|
||||||
|
ref={edgeRef}
|
||||||
|
>
|
||||||
|
{!updating && (
|
||||||
|
<EdgeComponent
|
||||||
|
id={id}
|
||||||
|
source={source}
|
||||||
|
target={target}
|
||||||
|
selected={selected}
|
||||||
|
animated={animated}
|
||||||
|
label={label}
|
||||||
|
labelStyle={labelStyle}
|
||||||
|
labelShowBg={labelShowBg}
|
||||||
|
labelBgStyle={labelBgStyle}
|
||||||
|
labelBgPadding={labelBgPadding}
|
||||||
|
labelBgBorderRadius={labelBgBorderRadius}
|
||||||
|
data={data}
|
||||||
|
style={style}
|
||||||
|
sourceX={edgePosition.sourceX}
|
||||||
|
sourceY={edgePosition.sourceY}
|
||||||
|
targetX={edgePosition.targetX}
|
||||||
|
targetY={edgePosition.targetY}
|
||||||
|
sourcePosition={edgePosition.sourcePosition}
|
||||||
|
targetPosition={edgePosition.targetPosition}
|
||||||
|
sourceHandleId={sourceHandleId}
|
||||||
|
targetHandleId={targetHandleId}
|
||||||
|
markerStart={markerStartUrl}
|
||||||
|
markerEnd={markerEndUrl}
|
||||||
|
pathOptions={pathOptions}
|
||||||
|
interactionWidth={interactionWidth}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{isUpdatable && (
|
||||||
|
<>
|
||||||
|
{(isUpdatable === 'source' || isUpdatable === true) && (
|
||||||
|
<EdgeAnchor
|
||||||
|
position={edgePosition.sourcePosition}
|
||||||
|
centerX={edgePosition.sourceX}
|
||||||
|
centerY={edgePosition.sourceY}
|
||||||
|
radius={edgeUpdaterRadius}
|
||||||
|
onMouseDown={onEdgeUpdaterSourceMouseDown}
|
||||||
|
onMouseEnter={onEdgeUpdaterMouseEnter}
|
||||||
|
onMouseOut={onEdgeUpdaterMouseOut}
|
||||||
|
type="source"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{(isUpdatable === 'target' || isUpdatable === true) && (
|
||||||
|
<EdgeAnchor
|
||||||
|
position={edgePosition.targetPosition}
|
||||||
|
centerX={edgePosition.targetX}
|
||||||
|
centerY={edgePosition.targetY}
|
||||||
|
radius={edgeUpdaterRadius}
|
||||||
|
onMouseDown={onEdgeUpdaterTargetMouseDown}
|
||||||
|
onMouseEnter={onEdgeUpdaterMouseEnter}
|
||||||
|
onMouseOut={onEdgeUpdaterMouseOut}
|
||||||
|
type="target"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</g>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
EdgeWrapper.displayName = 'EdgeWrapper';
|
||||||
|
|
||||||
|
export default memo(EdgeWrapper);
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import type { ComponentType } from 'react';
|
||||||
|
import type { EdgeProps, EdgeTypes } from '../../types';
|
||||||
|
import {
|
||||||
|
BezierEdgeInternal,
|
||||||
|
StraightEdgeInternal,
|
||||||
|
StepEdgeInternal,
|
||||||
|
SmoothStepEdgeInternal,
|
||||||
|
SimpleBezierEdgeInternal,
|
||||||
|
} from '../Edges';
|
||||||
|
|
||||||
|
export const builtinEdgeTypes: EdgeTypes = {
|
||||||
|
default: BezierEdgeInternal as ComponentType<EdgeProps>,
|
||||||
|
straight: StraightEdgeInternal as ComponentType<EdgeProps>,
|
||||||
|
step: StepEdgeInternal as ComponentType<EdgeProps>,
|
||||||
|
smoothstep: SmoothStepEdgeInternal as ComponentType<EdgeProps>,
|
||||||
|
simplebezier: SimpleBezierEdgeInternal as ComponentType<EdgeProps>,
|
||||||
|
};
|
||||||
@@ -1,290 +0,0 @@
|
|||||||
import { memo, useState, useMemo, useRef, type ComponentType, type KeyboardEvent, useCallback } from 'react';
|
|
||||||
import cc from 'classcat';
|
|
||||||
import { shallow } from 'zustand/shallow';
|
|
||||||
import { getMarkerId, elementSelectionKeys, XYHandle, type Connection, getEdgePosition } from '@xyflow/system';
|
|
||||||
|
|
||||||
import { useStoreApi, useStore } from '../../hooks/useStore';
|
|
||||||
import { ARIA_EDGE_DESC_KEY } from '../A11yDescriptions';
|
|
||||||
import { EdgeAnchor } from './EdgeAnchor';
|
|
||||||
import { getMouseHandler } from './utils';
|
|
||||||
import type { EdgeProps, WrapEdgeProps } from '../../types';
|
|
||||||
|
|
||||||
export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
|
||||||
const EdgeWrapper = ({
|
|
||||||
id,
|
|
||||||
className,
|
|
||||||
type,
|
|
||||||
data,
|
|
||||||
onClick,
|
|
||||||
onEdgeDoubleClick,
|
|
||||||
selected,
|
|
||||||
animated,
|
|
||||||
label,
|
|
||||||
labelStyle,
|
|
||||||
labelShowBg,
|
|
||||||
labelBgStyle,
|
|
||||||
labelBgPadding,
|
|
||||||
labelBgBorderRadius,
|
|
||||||
style,
|
|
||||||
source,
|
|
||||||
target,
|
|
||||||
isSelectable,
|
|
||||||
hidden,
|
|
||||||
sourceHandleId,
|
|
||||||
targetHandleId,
|
|
||||||
onContextMenu,
|
|
||||||
onMouseEnter,
|
|
||||||
onMouseMove,
|
|
||||||
onMouseLeave,
|
|
||||||
edgeUpdaterRadius,
|
|
||||||
onEdgeUpdate,
|
|
||||||
onEdgeUpdateStart,
|
|
||||||
onEdgeUpdateEnd,
|
|
||||||
markerEnd,
|
|
||||||
markerStart,
|
|
||||||
rfId,
|
|
||||||
ariaLabel,
|
|
||||||
isFocusable,
|
|
||||||
isUpdatable,
|
|
||||||
pathOptions,
|
|
||||||
interactionWidth,
|
|
||||||
}: WrapEdgeProps): JSX.Element | null => {
|
|
||||||
const edgeRef = useRef<SVGGElement>(null);
|
|
||||||
const [updateHover, setUpdateHover] = useState<boolean>(false);
|
|
||||||
const [updating, setUpdating] = useState<boolean>(false);
|
|
||||||
const store = useStoreApi();
|
|
||||||
const edgePosition = useStore(
|
|
||||||
useCallback(
|
|
||||||
(state) => {
|
|
||||||
const sourceNode = state.nodeLookup.get(source);
|
|
||||||
const targetNode = state.nodeLookup.get(target);
|
|
||||||
|
|
||||||
if (!sourceNode || !targetNode) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getEdgePosition({
|
|
||||||
id,
|
|
||||||
sourceNode,
|
|
||||||
targetNode,
|
|
||||||
sourceHandle: sourceHandleId || null,
|
|
||||||
targetHandle: targetHandleId || null,
|
|
||||||
connectionMode: state.connectionMode,
|
|
||||||
onError: state.onError,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[source, target]
|
|
||||||
),
|
|
||||||
shallow
|
|
||||||
);
|
|
||||||
|
|
||||||
const markerStartUrl = useMemo(() => `url(#${getMarkerId(markerStart, rfId)})`, [markerStart, rfId]);
|
|
||||||
const markerEndUrl = useMemo(() => `url(#${getMarkerId(markerEnd, rfId)})`, [markerEnd, rfId]);
|
|
||||||
|
|
||||||
if (hidden || !edgePosition) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const onEdgeClick = (event: React.MouseEvent<SVGGElement, MouseEvent>): void => {
|
|
||||||
const { edges, addSelectedEdges, unselectNodesAndEdges, multiSelectionActive } = store.getState();
|
|
||||||
const edge = edges.find((e) => e.id === id);
|
|
||||||
|
|
||||||
if (!edge) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isSelectable) {
|
|
||||||
store.setState({ nodesSelectionActive: false });
|
|
||||||
|
|
||||||
if (edge.selected && multiSelectionActive) {
|
|
||||||
unselectNodesAndEdges({ nodes: [], edges: [edge] });
|
|
||||||
edgeRef.current?.blur();
|
|
||||||
} else {
|
|
||||||
addSelectedEdges([id]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onClick) {
|
|
||||||
onClick(event, edge);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onEdgeDoubleClickHandler = getMouseHandler(id, store.getState, onEdgeDoubleClick);
|
|
||||||
const onEdgeContextMenu = getMouseHandler(id, store.getState, onContextMenu);
|
|
||||||
const onEdgeMouseEnter = getMouseHandler(id, store.getState, onMouseEnter);
|
|
||||||
const onEdgeMouseMove = getMouseHandler(id, store.getState, onMouseMove);
|
|
||||||
const onEdgeMouseLeave = getMouseHandler(id, store.getState, onMouseLeave);
|
|
||||||
|
|
||||||
const handleEdgeUpdater = (event: React.MouseEvent<SVGGElement, MouseEvent>, isSourceHandle: boolean) => {
|
|
||||||
// avoid triggering edge updater if mouse btn is not left
|
|
||||||
if (event.button !== 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
|
||||||
autoPanOnConnect,
|
|
||||||
domNode,
|
|
||||||
edges,
|
|
||||||
isValidConnection,
|
|
||||||
connectionMode,
|
|
||||||
connectionRadius,
|
|
||||||
lib,
|
|
||||||
onConnectStart,
|
|
||||||
onConnectEnd,
|
|
||||||
cancelConnection,
|
|
||||||
nodes,
|
|
||||||
panBy,
|
|
||||||
updateConnection,
|
|
||||||
} = store.getState();
|
|
||||||
const nodeId = isSourceHandle ? target : source;
|
|
||||||
const handleId = (isSourceHandle ? targetHandleId : sourceHandleId) || null;
|
|
||||||
const handleType = isSourceHandle ? 'target' : 'source';
|
|
||||||
|
|
||||||
const isTarget = isSourceHandle;
|
|
||||||
const edge = edges.find((e) => e.id === id)!;
|
|
||||||
|
|
||||||
setUpdating(true);
|
|
||||||
onEdgeUpdateStart?.(event, edge, handleType);
|
|
||||||
|
|
||||||
const _onEdgeUpdateEnd = (evt: MouseEvent | TouchEvent) => {
|
|
||||||
setUpdating(false);
|
|
||||||
onEdgeUpdateEnd?.(evt, edge, handleType);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onConnectEdge = (connection: Connection) => onEdgeUpdate?.(edge, connection);
|
|
||||||
|
|
||||||
XYHandle.onPointerDown(event.nativeEvent, {
|
|
||||||
autoPanOnConnect,
|
|
||||||
connectionMode,
|
|
||||||
connectionRadius,
|
|
||||||
domNode,
|
|
||||||
handleId,
|
|
||||||
nodeId,
|
|
||||||
nodes,
|
|
||||||
isTarget,
|
|
||||||
edgeUpdaterType: handleType,
|
|
||||||
lib,
|
|
||||||
cancelConnection,
|
|
||||||
panBy,
|
|
||||||
isValidConnection,
|
|
||||||
onConnect: onConnectEdge,
|
|
||||||
onConnectStart,
|
|
||||||
onConnectEnd,
|
|
||||||
onEdgeUpdateEnd: _onEdgeUpdateEnd,
|
|
||||||
updateConnection,
|
|
||||||
getTransform: () => store.getState().transform,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onEdgeUpdaterSourceMouseDown = (event: React.MouseEvent<SVGGElement, MouseEvent>): void =>
|
|
||||||
handleEdgeUpdater(event, true);
|
|
||||||
const onEdgeUpdaterTargetMouseDown = (event: React.MouseEvent<SVGGElement, MouseEvent>): void =>
|
|
||||||
handleEdgeUpdater(event, false);
|
|
||||||
|
|
||||||
const onEdgeUpdaterMouseEnter = () => setUpdateHover(true);
|
|
||||||
const onEdgeUpdaterMouseOut = () => setUpdateHover(false);
|
|
||||||
|
|
||||||
const inactive = !isSelectable && !onClick;
|
|
||||||
|
|
||||||
const onKeyDown = (event: KeyboardEvent) => {
|
|
||||||
if (elementSelectionKeys.includes(event.key) && isSelectable) {
|
|
||||||
const { unselectNodesAndEdges, addSelectedEdges, edges } = store.getState();
|
|
||||||
const unselect = event.key === 'Escape';
|
|
||||||
|
|
||||||
if (unselect) {
|
|
||||||
edgeRef.current?.blur();
|
|
||||||
unselectNodesAndEdges({ edges: [edges.find((e) => e.id === id)!] });
|
|
||||||
} else {
|
|
||||||
addSelectedEdges([id]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<g
|
|
||||||
className={cc([
|
|
||||||
'react-flow__edge',
|
|
||||||
`react-flow__edge-${type}`,
|
|
||||||
className,
|
|
||||||
{ selected, animated, inactive, updating: updateHover },
|
|
||||||
])}
|
|
||||||
onClick={onEdgeClick}
|
|
||||||
onDoubleClick={onEdgeDoubleClickHandler}
|
|
||||||
onContextMenu={onEdgeContextMenu}
|
|
||||||
onMouseEnter={onEdgeMouseEnter}
|
|
||||||
onMouseMove={onEdgeMouseMove}
|
|
||||||
onMouseLeave={onEdgeMouseLeave}
|
|
||||||
onKeyDown={isFocusable ? onKeyDown : undefined}
|
|
||||||
tabIndex={isFocusable ? 0 : undefined}
|
|
||||||
role={isFocusable ? 'button' : 'img'}
|
|
||||||
data-id={id}
|
|
||||||
data-testid={`rf__edge-${id}`}
|
|
||||||
aria-label={ariaLabel === null ? undefined : ariaLabel ? ariaLabel : `Edge from ${source} to ${target}`}
|
|
||||||
aria-describedby={isFocusable ? `${ARIA_EDGE_DESC_KEY}-${rfId}` : undefined}
|
|
||||||
ref={edgeRef}
|
|
||||||
>
|
|
||||||
{!updating && (
|
|
||||||
<EdgeComponent
|
|
||||||
id={id}
|
|
||||||
source={source}
|
|
||||||
target={target}
|
|
||||||
selected={selected}
|
|
||||||
animated={animated}
|
|
||||||
label={label}
|
|
||||||
labelStyle={labelStyle}
|
|
||||||
labelShowBg={labelShowBg}
|
|
||||||
labelBgStyle={labelBgStyle}
|
|
||||||
labelBgPadding={labelBgPadding}
|
|
||||||
labelBgBorderRadius={labelBgBorderRadius}
|
|
||||||
data={data}
|
|
||||||
style={style}
|
|
||||||
sourceX={edgePosition.sourceX}
|
|
||||||
sourceY={edgePosition.sourceY}
|
|
||||||
targetX={edgePosition.targetX}
|
|
||||||
targetY={edgePosition.targetY}
|
|
||||||
sourcePosition={edgePosition.sourcePosition}
|
|
||||||
targetPosition={edgePosition.targetPosition}
|
|
||||||
sourceHandleId={sourceHandleId}
|
|
||||||
targetHandleId={targetHandleId}
|
|
||||||
markerStart={markerStartUrl}
|
|
||||||
markerEnd={markerEndUrl}
|
|
||||||
pathOptions={pathOptions}
|
|
||||||
interactionWidth={interactionWidth}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{isUpdatable && (
|
|
||||||
<>
|
|
||||||
{(isUpdatable === 'source' || isUpdatable === true) && (
|
|
||||||
<EdgeAnchor
|
|
||||||
position={edgePosition.sourcePosition}
|
|
||||||
centerX={edgePosition.sourceX}
|
|
||||||
centerY={edgePosition.sourceY}
|
|
||||||
radius={edgeUpdaterRadius}
|
|
||||||
onMouseDown={onEdgeUpdaterSourceMouseDown}
|
|
||||||
onMouseEnter={onEdgeUpdaterMouseEnter}
|
|
||||||
onMouseOut={onEdgeUpdaterMouseOut}
|
|
||||||
type="source"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
{(isUpdatable === 'target' || isUpdatable === true) && (
|
|
||||||
<EdgeAnchor
|
|
||||||
position={edgePosition.targetPosition}
|
|
||||||
centerX={edgePosition.targetX}
|
|
||||||
centerY={edgePosition.targetY}
|
|
||||||
radius={edgeUpdaterRadius}
|
|
||||||
onMouseDown={onEdgeUpdaterTargetMouseDown}
|
|
||||||
onMouseEnter={onEdgeUpdaterMouseEnter}
|
|
||||||
onMouseOut={onEdgeUpdaterMouseOut}
|
|
||||||
type="target"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</g>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
EdgeWrapper.displayName = 'EdgeWrapper';
|
|
||||||
|
|
||||||
return memo(EdgeWrapper);
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,256 @@
|
|||||||
|
import { useEffect, useRef, memo, type MouseEvent, type KeyboardEvent } from 'react';
|
||||||
|
import cc from 'classcat';
|
||||||
|
import {
|
||||||
|
clampPosition,
|
||||||
|
elementSelectionKeys,
|
||||||
|
errorMessages,
|
||||||
|
getPositionWithOrigin,
|
||||||
|
internalsSymbol,
|
||||||
|
isInputDOMNode,
|
||||||
|
} from '@xyflow/system';
|
||||||
|
|
||||||
|
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||||
|
import { Provider } from '../../contexts/NodeIdContext';
|
||||||
|
import { ARIA_NODE_DESC_KEY } from '../A11yDescriptions';
|
||||||
|
import useDrag from '../../hooks/useDrag';
|
||||||
|
import useUpdateNodePositions from '../../hooks/useUpdateNodePositions';
|
||||||
|
import { handleNodeClick } from '../Nodes/utils';
|
||||||
|
import type { NodeWrapperProps } from '../../types';
|
||||||
|
import { arrowKeyDiffs, builtinNodeTypes } from './utils';
|
||||||
|
|
||||||
|
const NodeWrapper = ({
|
||||||
|
id,
|
||||||
|
onClick,
|
||||||
|
onMouseEnter,
|
||||||
|
onMouseMove,
|
||||||
|
onMouseLeave,
|
||||||
|
onContextMenu,
|
||||||
|
onDoubleClick,
|
||||||
|
nodesDraggable,
|
||||||
|
elementsSelectable,
|
||||||
|
nodesConnectable,
|
||||||
|
nodesFocusable,
|
||||||
|
resizeObserver,
|
||||||
|
noDragClassName,
|
||||||
|
noPanClassName,
|
||||||
|
disableKeyboardA11y,
|
||||||
|
rfId,
|
||||||
|
nodeTypes,
|
||||||
|
nodeExtent,
|
||||||
|
nodeOrigin,
|
||||||
|
onError,
|
||||||
|
}: NodeWrapperProps) => {
|
||||||
|
const node = useStore((s) => s.nodeLookup.get(id)!);
|
||||||
|
|
||||||
|
let nodeType = node.type || 'default';
|
||||||
|
let NodeComponent = nodeTypes?.[nodeType] || builtinNodeTypes[nodeType];
|
||||||
|
|
||||||
|
if (NodeComponent === undefined) {
|
||||||
|
onError?.('003', errorMessages['error003'](nodeType));
|
||||||
|
nodeType = 'default';
|
||||||
|
NodeComponent = builtinNodeTypes.default;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDraggable = !!(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined'));
|
||||||
|
const isSelectable = !!(node.selectable || (elementsSelectable && typeof node.selectable === 'undefined'));
|
||||||
|
const isConnectable = !!(node.connectable || (nodesConnectable && typeof node.connectable === 'undefined'));
|
||||||
|
const isFocusable = !!(node.focusable || (nodesFocusable && typeof node.focusable === 'undefined'));
|
||||||
|
|
||||||
|
const store = useStoreApi();
|
||||||
|
const nodeRef = useRef<HTMLDivElement>(null);
|
||||||
|
const prevSourcePosition = useRef(node.sourcePosition);
|
||||||
|
const prevTargetPosition = useRef(node.targetPosition);
|
||||||
|
const prevType = useRef(nodeType);
|
||||||
|
|
||||||
|
const updatePositions = useUpdateNodePositions();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (nodeRef.current && !node.hidden) {
|
||||||
|
const currNode = nodeRef.current;
|
||||||
|
resizeObserver?.observe(currNode);
|
||||||
|
|
||||||
|
return () => resizeObserver?.unobserve(currNode);
|
||||||
|
}
|
||||||
|
}, [node.hidden]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// when the user programmatically changes the source or handle position, we re-initialize the node
|
||||||
|
const typeChanged = prevType.current !== nodeType;
|
||||||
|
const sourcePosChanged = prevSourcePosition.current !== node.sourcePosition;
|
||||||
|
const targetPosChanged = prevTargetPosition.current !== node.targetPosition;
|
||||||
|
|
||||||
|
if (nodeRef.current && (typeChanged || sourcePosChanged || targetPosChanged)) {
|
||||||
|
if (typeChanged) {
|
||||||
|
prevType.current = nodeType;
|
||||||
|
}
|
||||||
|
if (sourcePosChanged) {
|
||||||
|
prevSourcePosition.current = node.sourcePosition;
|
||||||
|
}
|
||||||
|
if (targetPosChanged) {
|
||||||
|
prevTargetPosition.current = node.targetPosition;
|
||||||
|
}
|
||||||
|
store.getState().updateNodeDimensions(new Map([[id, { id, nodeElement: nodeRef.current, forceUpdate: true }]]));
|
||||||
|
}
|
||||||
|
}, [id, nodeType, node.sourcePosition, node.targetPosition]);
|
||||||
|
|
||||||
|
const dragging = useDrag({
|
||||||
|
nodeRef,
|
||||||
|
disabled: node.hidden || !isDraggable,
|
||||||
|
noDragClassName,
|
||||||
|
handleSelector: node.dragHandle,
|
||||||
|
nodeId: id,
|
||||||
|
isSelectable,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (node.hidden) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const width = node.width ?? undefined;
|
||||||
|
const height = node.height ?? undefined;
|
||||||
|
const computedWidth = node.computed?.width;
|
||||||
|
const computedHeight = node.computed?.height;
|
||||||
|
const positionAbsolute = nodeExtent
|
||||||
|
? clampPosition(node.computed?.positionAbsolute, nodeExtent)
|
||||||
|
: node.computed?.positionAbsolute || { x: 0, y: 0 };
|
||||||
|
const positionAbsoluteOrigin = getPositionWithOrigin({
|
||||||
|
x: positionAbsolute.x,
|
||||||
|
y: positionAbsolute.y,
|
||||||
|
width: computedWidth ?? width ?? 0,
|
||||||
|
height: computedHeight ?? height ?? 0,
|
||||||
|
origin: node.origin || nodeOrigin,
|
||||||
|
});
|
||||||
|
const initialized = (!!computedWidth && !!computedHeight) || (!!width && !!height);
|
||||||
|
const zIndex = node[internalsSymbol]?.z ?? 0;
|
||||||
|
const isParent = !!node[internalsSymbol]?.isParent;
|
||||||
|
const hasPointerEvents = isSelectable || isDraggable || onClick || onMouseEnter || onMouseMove || onMouseLeave;
|
||||||
|
|
||||||
|
const onMouseEnterHandler =
|
||||||
|
onMouseEnter === undefined ? undefined : (event: MouseEvent) => onMouseEnter(event, { ...node });
|
||||||
|
const onMouseMoveHandler =
|
||||||
|
onMouseMove === undefined ? undefined : (event: MouseEvent) => onMouseMove(event, { ...node });
|
||||||
|
const onMouseLeaveHandler =
|
||||||
|
onMouseLeave === undefined ? undefined : (event: MouseEvent) => onMouseLeave(event, { ...node });
|
||||||
|
const onContextMenuHandler =
|
||||||
|
onContextMenu === undefined ? undefined : (event: MouseEvent) => onContextMenu(event, { ...node });
|
||||||
|
const onDoubleClickHandler =
|
||||||
|
onDoubleClick === undefined ? undefined : (event: MouseEvent) => onDoubleClick(event, { ...node });
|
||||||
|
|
||||||
|
const onSelectNodeHandler = (event: MouseEvent) => {
|
||||||
|
const { selectNodesOnDrag, nodeDragThreshold } = store.getState();
|
||||||
|
|
||||||
|
if (isSelectable && (!selectNodesOnDrag || !isDraggable || nodeDragThreshold > 0)) {
|
||||||
|
// this handler gets called by XYDrag on drag start when selectNodesOnDrag=true
|
||||||
|
// here we only need to call it when selectNodesOnDrag=false
|
||||||
|
handleNodeClick({
|
||||||
|
id,
|
||||||
|
store,
|
||||||
|
nodeRef,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (onClick) {
|
||||||
|
onClick(event, { ...node });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onKeyDown = (event: KeyboardEvent) => {
|
||||||
|
if (isInputDOMNode(event.nativeEvent)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (elementSelectionKeys.includes(event.key) && isSelectable) {
|
||||||
|
const unselect = event.key === 'Escape';
|
||||||
|
|
||||||
|
handleNodeClick({
|
||||||
|
id,
|
||||||
|
store,
|
||||||
|
unselect,
|
||||||
|
nodeRef,
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
|
!disableKeyboardA11y &&
|
||||||
|
isDraggable &&
|
||||||
|
node.selected &&
|
||||||
|
Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)
|
||||||
|
) {
|
||||||
|
store.setState({
|
||||||
|
ariaLiveMessage: `Moved selected node ${event.key
|
||||||
|
.replace('Arrow', '')
|
||||||
|
.toLowerCase()}. New position, x: ${~~positionAbsolute.x}, y: ${~~positionAbsolute.y}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
updatePositions({
|
||||||
|
x: arrowKeyDiffs[event.key].x,
|
||||||
|
y: arrowKeyDiffs[event.key].y,
|
||||||
|
isShiftPressed: event.shiftKey,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cc([
|
||||||
|
'react-flow__node',
|
||||||
|
`react-flow__node-${nodeType}`,
|
||||||
|
{
|
||||||
|
// this is overwritable by passing `nopan` as a class name
|
||||||
|
[noPanClassName]: isDraggable,
|
||||||
|
},
|
||||||
|
node.className,
|
||||||
|
{
|
||||||
|
selected: node.selected,
|
||||||
|
selectable: isSelectable,
|
||||||
|
parent: isParent,
|
||||||
|
dragging,
|
||||||
|
},
|
||||||
|
])}
|
||||||
|
ref={nodeRef}
|
||||||
|
style={{
|
||||||
|
zIndex,
|
||||||
|
transform: `translate(${positionAbsoluteOrigin.x}px,${positionAbsoluteOrigin.y}px)`,
|
||||||
|
pointerEvents: hasPointerEvents ? 'all' : 'none',
|
||||||
|
visibility: initialized ? 'visible' : 'hidden',
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
...node.style,
|
||||||
|
}}
|
||||||
|
data-id={id}
|
||||||
|
data-testid={`rf__node-${id}`}
|
||||||
|
onMouseEnter={onMouseEnterHandler}
|
||||||
|
onMouseMove={onMouseMoveHandler}
|
||||||
|
onMouseLeave={onMouseLeaveHandler}
|
||||||
|
onContextMenu={onContextMenuHandler}
|
||||||
|
onClick={onSelectNodeHandler}
|
||||||
|
onDoubleClick={onDoubleClickHandler}
|
||||||
|
onKeyDown={isFocusable ? onKeyDown : undefined}
|
||||||
|
tabIndex={isFocusable ? 0 : undefined}
|
||||||
|
role={isFocusable ? 'button' : undefined}
|
||||||
|
aria-describedby={disableKeyboardA11y ? undefined : `${ARIA_NODE_DESC_KEY}-${rfId}`}
|
||||||
|
aria-label={node.ariaLabel}
|
||||||
|
>
|
||||||
|
<Provider value={id}>
|
||||||
|
<NodeComponent
|
||||||
|
id={id}
|
||||||
|
data={node.data}
|
||||||
|
type={nodeType}
|
||||||
|
width={computedWidth}
|
||||||
|
height={computedHeight}
|
||||||
|
positionAbsoluteX={positionAbsolute.x}
|
||||||
|
positionAbsoluteY={positionAbsolute.y}
|
||||||
|
selected={node.selected}
|
||||||
|
isConnectable={isConnectable}
|
||||||
|
sourcePosition={node.sourcePosition}
|
||||||
|
targetPosition={node.targetPosition}
|
||||||
|
dragging={dragging}
|
||||||
|
dragHandle={node.dragHandle}
|
||||||
|
zIndex={zIndex}
|
||||||
|
/>
|
||||||
|
</Provider>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
NodeWrapper.displayName = 'NodeWrapper';
|
||||||
|
|
||||||
|
export default memo(NodeWrapper);
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
import type { ComponentType } from 'react';
|
||||||
|
import type { NodeProps, XYPosition } from '@xyflow/system';
|
||||||
|
|
||||||
|
import InputNode from '../Nodes/InputNode';
|
||||||
|
import DefaultNode from '../Nodes/DefaultNode';
|
||||||
|
import GroupNode from '../Nodes/GroupNode';
|
||||||
|
import OutputNode from '../Nodes/OutputNode';
|
||||||
|
import type { NodeTypes } from '../../types';
|
||||||
|
|
||||||
|
export const arrowKeyDiffs: Record<string, XYPosition> = {
|
||||||
|
ArrowUp: { x: 0, y: -1 },
|
||||||
|
ArrowDown: { x: 0, y: 1 },
|
||||||
|
ArrowLeft: { x: -1, y: 0 },
|
||||||
|
ArrowRight: { x: 1, y: 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const builtinNodeTypes: NodeTypes = {
|
||||||
|
input: InputNode as ComponentType<NodeProps>,
|
||||||
|
default: DefaultNode as ComponentType<NodeProps>,
|
||||||
|
output: OutputNode as ComponentType<NodeProps>,
|
||||||
|
group: GroupNode as ComponentType<NodeProps>,
|
||||||
|
};
|
||||||
@@ -1,22 +1,9 @@
|
|||||||
import type { MouseEvent, RefObject } from 'react';
|
import type { RefObject } from 'react';
|
||||||
import type { StoreApi } from 'zustand';
|
import type { StoreApi } from 'zustand';
|
||||||
|
|
||||||
import type { Node, ReactFlowState } from '../../types';
|
import type { ReactFlowState } from '../../types';
|
||||||
import { errorMessages } from '@xyflow/system';
|
import { errorMessages } from '@xyflow/system';
|
||||||
|
|
||||||
export function getMouseHandler(
|
|
||||||
id: string,
|
|
||||||
getState: StoreApi<ReactFlowState>['getState'],
|
|
||||||
handler?: (event: MouseEvent, node: Node) => void
|
|
||||||
) {
|
|
||||||
return handler === undefined
|
|
||||||
? handler
|
|
||||||
: (event: MouseEvent) => {
|
|
||||||
const node = getState().nodeLookup.get(id)!;
|
|
||||||
handler(event, { ...node });
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// this handler is called by
|
// this handler is called by
|
||||||
// 1. the click handler when node is not draggable or selectNodesOnDrag = false
|
// 1. the click handler when node is not draggable or selectNodesOnDrag = false
|
||||||
// or
|
// or
|
||||||
|
|||||||
@@ -1,232 +0,0 @@
|
|||||||
import { useEffect, useRef, memo, type ComponentType, type MouseEvent, type KeyboardEvent } from 'react';
|
|
||||||
import cc from 'classcat';
|
|
||||||
import { elementSelectionKeys, isInputDOMNode, type NodeProps, type XYPosition } from '@xyflow/system';
|
|
||||||
|
|
||||||
import { useStoreApi } from '../../hooks/useStore';
|
|
||||||
import { Provider } from '../../contexts/NodeIdContext';
|
|
||||||
import { ARIA_NODE_DESC_KEY } from '../A11yDescriptions';
|
|
||||||
import useDrag from '../../hooks/useDrag';
|
|
||||||
import useUpdateNodePositions from '../../hooks/useUpdateNodePositions';
|
|
||||||
import { getMouseHandler, handleNodeClick } from './utils';
|
|
||||||
import type { WrapNodeProps } from '../../types';
|
|
||||||
|
|
||||||
export const arrowKeyDiffs: Record<string, XYPosition> = {
|
|
||||||
ArrowUp: { x: 0, y: -1 },
|
|
||||||
ArrowDown: { x: 0, y: 1 },
|
|
||||||
ArrowLeft: { x: -1, y: 0 },
|
|
||||||
ArrowRight: { x: 1, y: 0 },
|
|
||||||
};
|
|
||||||
|
|
||||||
export default (NodeComponent: ComponentType<NodeProps>) => {
|
|
||||||
const NodeWrapper = ({
|
|
||||||
id,
|
|
||||||
type,
|
|
||||||
data,
|
|
||||||
xPos,
|
|
||||||
yPos,
|
|
||||||
xPosOrigin,
|
|
||||||
yPosOrigin,
|
|
||||||
selected,
|
|
||||||
onClick,
|
|
||||||
onMouseEnter,
|
|
||||||
onMouseMove,
|
|
||||||
onMouseLeave,
|
|
||||||
onContextMenu,
|
|
||||||
onDoubleClick,
|
|
||||||
style,
|
|
||||||
className,
|
|
||||||
isDraggable,
|
|
||||||
isSelectable,
|
|
||||||
isConnectable,
|
|
||||||
isFocusable,
|
|
||||||
sourcePosition,
|
|
||||||
targetPosition,
|
|
||||||
hidden,
|
|
||||||
resizeObserver,
|
|
||||||
dragHandle,
|
|
||||||
zIndex,
|
|
||||||
isParent,
|
|
||||||
noDragClassName,
|
|
||||||
noPanClassName,
|
|
||||||
initialized,
|
|
||||||
disableKeyboardA11y,
|
|
||||||
ariaLabel,
|
|
||||||
rfId,
|
|
||||||
positionAbsolute,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
}: WrapNodeProps) => {
|
|
||||||
const store = useStoreApi();
|
|
||||||
const nodeRef = useRef<HTMLDivElement>(null);
|
|
||||||
const prevSourcePosition = useRef(sourcePosition);
|
|
||||||
const prevTargetPosition = useRef(targetPosition);
|
|
||||||
const prevType = useRef(type);
|
|
||||||
const hasPointerEvents = isSelectable || isDraggable || onClick || onMouseEnter || onMouseMove || onMouseLeave;
|
|
||||||
const updatePositions = useUpdateNodePositions();
|
|
||||||
|
|
||||||
const onMouseEnterHandler = getMouseHandler(id, store.getState, onMouseEnter);
|
|
||||||
const onMouseMoveHandler = getMouseHandler(id, store.getState, onMouseMove);
|
|
||||||
const onMouseLeaveHandler = getMouseHandler(id, store.getState, onMouseLeave);
|
|
||||||
const onContextMenuHandler = getMouseHandler(id, store.getState, onContextMenu);
|
|
||||||
const onDoubleClickHandler = getMouseHandler(id, store.getState, onDoubleClick);
|
|
||||||
const onSelectNodeHandler = (event: MouseEvent) => {
|
|
||||||
const { selectNodesOnDrag, nodeDragThreshold } = store.getState();
|
|
||||||
|
|
||||||
if (isSelectable && (!selectNodesOnDrag || !isDraggable || nodeDragThreshold > 0)) {
|
|
||||||
// this handler gets called by XYDrag on drag start when selectNodesOnDrag=true
|
|
||||||
// here we only need to call it when selectNodesOnDrag=false
|
|
||||||
handleNodeClick({
|
|
||||||
id,
|
|
||||||
store,
|
|
||||||
nodeRef,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (onClick) {
|
|
||||||
const node = store.getState().nodes.find((n) => n.id === id)!;
|
|
||||||
onClick(event, { ...node });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onKeyDown = (event: KeyboardEvent) => {
|
|
||||||
if (isInputDOMNode(event.nativeEvent)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elementSelectionKeys.includes(event.key) && isSelectable) {
|
|
||||||
const unselect = event.key === 'Escape';
|
|
||||||
|
|
||||||
handleNodeClick({
|
|
||||||
id,
|
|
||||||
store,
|
|
||||||
unselect,
|
|
||||||
nodeRef,
|
|
||||||
});
|
|
||||||
} else if (
|
|
||||||
!disableKeyboardA11y &&
|
|
||||||
isDraggable &&
|
|
||||||
selected &&
|
|
||||||
Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)
|
|
||||||
) {
|
|
||||||
store.setState({
|
|
||||||
ariaLiveMessage: `Moved selected node ${event.key
|
|
||||||
.replace('Arrow', '')
|
|
||||||
.toLowerCase()}. New position, x: ${~~xPos}, y: ${~~yPos}`,
|
|
||||||
});
|
|
||||||
|
|
||||||
updatePositions({
|
|
||||||
x: arrowKeyDiffs[event.key].x,
|
|
||||||
y: arrowKeyDiffs[event.key].y,
|
|
||||||
isShiftPressed: event.shiftKey,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (nodeRef.current && !hidden) {
|
|
||||||
const currNode = nodeRef.current;
|
|
||||||
resizeObserver?.observe(currNode);
|
|
||||||
|
|
||||||
return () => resizeObserver?.unobserve(currNode);
|
|
||||||
}
|
|
||||||
}, [hidden]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
// when the user programmatically changes the source or handle position, we re-initialize the node
|
|
||||||
const typeChanged = prevType.current !== type;
|
|
||||||
const sourcePosChanged = prevSourcePosition.current !== sourcePosition;
|
|
||||||
const targetPosChanged = prevTargetPosition.current !== targetPosition;
|
|
||||||
|
|
||||||
if (nodeRef.current && (typeChanged || sourcePosChanged || targetPosChanged)) {
|
|
||||||
if (typeChanged) {
|
|
||||||
prevType.current = type;
|
|
||||||
}
|
|
||||||
if (sourcePosChanged) {
|
|
||||||
prevSourcePosition.current = sourcePosition;
|
|
||||||
}
|
|
||||||
if (targetPosChanged) {
|
|
||||||
prevTargetPosition.current = targetPosition;
|
|
||||||
}
|
|
||||||
store.getState().updateNodeDimensions(new Map([[id, { id, nodeElement: nodeRef.current, forceUpdate: true }]]));
|
|
||||||
}
|
|
||||||
}, [id, type, sourcePosition, targetPosition]);
|
|
||||||
|
|
||||||
const dragging = useDrag({
|
|
||||||
nodeRef,
|
|
||||||
disabled: hidden || !isDraggable,
|
|
||||||
noDragClassName,
|
|
||||||
handleSelector: dragHandle,
|
|
||||||
nodeId: id,
|
|
||||||
isSelectable,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (hidden) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={cc([
|
|
||||||
'react-flow__node',
|
|
||||||
`react-flow__node-${type}`,
|
|
||||||
{
|
|
||||||
// this is overwritable by passing `nopan` as a class name
|
|
||||||
[noPanClassName]: isDraggable,
|
|
||||||
},
|
|
||||||
className,
|
|
||||||
{
|
|
||||||
selected,
|
|
||||||
selectable: isSelectable,
|
|
||||||
parent: isParent,
|
|
||||||
dragging,
|
|
||||||
},
|
|
||||||
])}
|
|
||||||
ref={nodeRef}
|
|
||||||
style={{
|
|
||||||
zIndex,
|
|
||||||
transform: `translate(${xPosOrigin}px,${yPosOrigin}px)`,
|
|
||||||
pointerEvents: hasPointerEvents ? 'all' : 'none',
|
|
||||||
visibility: initialized ? 'visible' : 'hidden',
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
...style,
|
|
||||||
}}
|
|
||||||
data-id={id}
|
|
||||||
data-testid={`rf__node-${id}`}
|
|
||||||
onMouseEnter={onMouseEnterHandler}
|
|
||||||
onMouseMove={onMouseMoveHandler}
|
|
||||||
onMouseLeave={onMouseLeaveHandler}
|
|
||||||
onContextMenu={onContextMenuHandler}
|
|
||||||
onClick={onSelectNodeHandler}
|
|
||||||
onDoubleClick={onDoubleClickHandler}
|
|
||||||
onKeyDown={isFocusable ? onKeyDown : undefined}
|
|
||||||
tabIndex={isFocusable ? 0 : undefined}
|
|
||||||
role={isFocusable ? 'button' : undefined}
|
|
||||||
aria-describedby={disableKeyboardA11y ? undefined : `${ARIA_NODE_DESC_KEY}-${rfId}`}
|
|
||||||
aria-label={ariaLabel}
|
|
||||||
>
|
|
||||||
<Provider value={id}>
|
|
||||||
<NodeComponent
|
|
||||||
id={id}
|
|
||||||
data={data}
|
|
||||||
type={type}
|
|
||||||
width={width}
|
|
||||||
height={height}
|
|
||||||
positionAbsolute={positionAbsolute}
|
|
||||||
selected={selected}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
sourcePosition={sourcePosition}
|
|
||||||
targetPosition={targetPosition}
|
|
||||||
dragging={dragging}
|
|
||||||
dragHandle={dragHandle}
|
|
||||||
zIndex={zIndex}
|
|
||||||
/>
|
|
||||||
</Provider>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
NodeWrapper.displayName = 'NodeWrapper';
|
|
||||||
|
|
||||||
return memo(NodeWrapper);
|
|
||||||
};
|
|
||||||
@@ -10,9 +10,9 @@ import { getNodesBounds } from '@xyflow/system';
|
|||||||
|
|
||||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||||
import useDrag from '../../hooks/useDrag';
|
import useDrag from '../../hooks/useDrag';
|
||||||
import { arrowKeyDiffs } from '../Nodes/wrapNode';
|
|
||||||
import useUpdateNodePositions from '../../hooks/useUpdateNodePositions';
|
import useUpdateNodePositions from '../../hooks/useUpdateNodePositions';
|
||||||
import type { Node, ReactFlowState } from '../../types';
|
import type { Node, ReactFlowState } from '../../types';
|
||||||
|
import { arrowKeyDiffs } from '../NodeWrapper/utils';
|
||||||
|
|
||||||
export type NodesSelectionProps = {
|
export type NodesSelectionProps = {
|
||||||
onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void;
|
onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void;
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { memo, ReactNode } from 'react';
|
import { memo, ReactNode } from 'react';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
import { errorMessages } from '@xyflow/system';
|
|
||||||
|
|
||||||
import { useStore } from '../../hooks/useStore';
|
import { useStore } from '../../hooks/useStore';
|
||||||
import useVisibleEdges from '../../hooks/useVisibleEdges';
|
import useVisibleEdges from '../../hooks/useVisibleEdges';
|
||||||
import MarkerDefinitions from './MarkerDefinitions';
|
import MarkerDefinitions from './MarkerDefinitions';
|
||||||
import { GraphViewProps } from '../GraphView';
|
import { GraphViewProps } from '../GraphView';
|
||||||
import type { EdgeTypesWrapped, ReactFlowState } from '../../types';
|
import type { ReactFlowState } from '../../types';
|
||||||
|
import EdgeWrapper from '../../components/EdgeWrapper';
|
||||||
|
|
||||||
type EdgeRendererProps = Pick<
|
type EdgeRendererProps = Pick<
|
||||||
GraphViewProps,
|
GraphViewProps,
|
||||||
@@ -27,8 +27,8 @@ type EdgeRendererProps = Pick<
|
|||||||
| 'elevateEdgesOnSelect'
|
| 'elevateEdgesOnSelect'
|
||||||
| 'rfId'
|
| 'rfId'
|
||||||
| 'disableKeyboardA11y'
|
| 'disableKeyboardA11y'
|
||||||
|
| 'edgeTypes'
|
||||||
> & {
|
> & {
|
||||||
edgeTypes: EdgeTypesWrapped;
|
|
||||||
elevateEdgesOnSelect: boolean;
|
elevateEdgesOnSelect: boolean;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
};
|
};
|
||||||
@@ -74,14 +74,6 @@ const EdgeRenderer = ({
|
|||||||
{isMaxLevel && <MarkerDefinitions defaultColor={defaultMarkerColor} rfId={rfId} />}
|
{isMaxLevel && <MarkerDefinitions defaultColor={defaultMarkerColor} rfId={rfId} />}
|
||||||
<>
|
<>
|
||||||
{edges.map((edge) => {
|
{edges.map((edge) => {
|
||||||
let edgeType = edge.type || 'default';
|
|
||||||
|
|
||||||
if (!edgeTypes[edgeType]) {
|
|
||||||
onError?.('011', errorMessages['error011'](edgeType));
|
|
||||||
edgeType = 'default';
|
|
||||||
}
|
|
||||||
|
|
||||||
const EdgeComponent = edgeTypes[edgeType];
|
|
||||||
const isFocusable = !!(edge.focusable || (edgesFocusable && typeof edge.focusable === 'undefined'));
|
const isFocusable = !!(edge.focusable || (edgesFocusable && typeof edge.focusable === 'undefined'));
|
||||||
const isUpdatable =
|
const isUpdatable =
|
||||||
typeof onEdgeUpdate !== 'undefined' &&
|
typeof onEdgeUpdate !== 'undefined' &&
|
||||||
@@ -92,7 +84,7 @@ const EdgeRenderer = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EdgeComponent
|
<EdgeWrapper
|
||||||
key={edge.id}
|
key={edge.id}
|
||||||
id={edge.id}
|
id={edge.id}
|
||||||
className={cc([edge.className, noPanClassName])}
|
className={cc([edge.className, noPanClassName])}
|
||||||
@@ -131,6 +123,8 @@ const EdgeRenderer = ({
|
|||||||
isUpdatable={isUpdatable}
|
isUpdatable={isUpdatable}
|
||||||
pathOptions={'pathOptions' in edge ? edge.pathOptions : undefined}
|
pathOptions={'pathOptions' in edge ? edge.pathOptions : undefined}
|
||||||
interactionWidth={edge.interactionWidth}
|
interactionWidth={edge.interactionWidth}
|
||||||
|
onError={onError}
|
||||||
|
edgeTypes={edgeTypes}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
import type { ComponentType } from 'react';
|
|
||||||
|
|
||||||
import {
|
|
||||||
BezierEdgeInternal,
|
|
||||||
SmoothStepEdgeInternal,
|
|
||||||
StepEdgeInternal,
|
|
||||||
StraightEdgeInternal,
|
|
||||||
SimpleBezierEdgeInternal,
|
|
||||||
} from '../../components/Edges';
|
|
||||||
import wrapEdge from '../../components/Edges/wrapEdge';
|
|
||||||
import type { EdgeProps, EdgeTypes, EdgeTypesWrapped } from '../../types';
|
|
||||||
|
|
||||||
export type CreateEdgeTypes = (edgeTypes: EdgeTypes) => EdgeTypesWrapped;
|
|
||||||
|
|
||||||
export function createEdgeTypes(edgeTypes: EdgeTypes): EdgeTypesWrapped {
|
|
||||||
const standardTypes: EdgeTypesWrapped = {
|
|
||||||
default: wrapEdge((edgeTypes.default || BezierEdgeInternal) as ComponentType<EdgeProps>),
|
|
||||||
straight: wrapEdge((edgeTypes.bezier || StraightEdgeInternal) as ComponentType<EdgeProps>),
|
|
||||||
step: wrapEdge((edgeTypes.step || StepEdgeInternal) as ComponentType<EdgeProps>),
|
|
||||||
smoothstep: wrapEdge((edgeTypes.step || SmoothStepEdgeInternal) as ComponentType<EdgeProps>),
|
|
||||||
simplebezier: wrapEdge((edgeTypes.simplebezier || SimpleBezierEdgeInternal) as ComponentType<EdgeProps>),
|
|
||||||
};
|
|
||||||
|
|
||||||
const wrappedTypes = {} as EdgeTypesWrapped;
|
|
||||||
const specialTypes: EdgeTypesWrapped = Object.keys(edgeTypes)
|
|
||||||
.filter((k) => !['default', 'bezier'].includes(k))
|
|
||||||
.reduce((res, key) => {
|
|
||||||
res[key] = wrapEdge((edgeTypes[key] || BezierEdgeInternal) as ComponentType<EdgeProps>);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}, wrappedTypes);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...standardTypes,
|
|
||||||
...specialTypes,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -8,19 +8,15 @@ import useOnInitHandler from '../../hooks/useOnInitHandler';
|
|||||||
import useViewportSync from '../../hooks/useViewportSync';
|
import useViewportSync from '../../hooks/useViewportSync';
|
||||||
import ConnectionLine from '../../components/ConnectionLine';
|
import ConnectionLine from '../../components/ConnectionLine';
|
||||||
import type { ReactFlowProps } from '../../types';
|
import type { ReactFlowProps } from '../../types';
|
||||||
import { createNodeTypes } from '../NodeRenderer/utils';
|
import useNodeOrEdgeTypesWarning from './useNodeOrEdgeTypesWarning';
|
||||||
import { createEdgeTypes } from '../EdgeRenderer/utils';
|
|
||||||
import { useNodeOrEdgeTypes } from './utils';
|
|
||||||
|
|
||||||
export type GraphViewProps = Omit<
|
export type GraphViewProps = Omit<
|
||||||
ReactFlowProps,
|
ReactFlowProps,
|
||||||
'onSelectionChange' | 'nodes' | 'edges' | 'nodeTypes' | 'edgeTypes' | 'onMove' | 'onMoveStart' | 'onMoveEnd'
|
'onSelectionChange' | 'nodes' | 'edges' | 'onMove' | 'onMoveStart' | 'onMoveEnd'
|
||||||
> &
|
> &
|
||||||
Required<
|
Required<
|
||||||
Pick<
|
Pick<
|
||||||
ReactFlowProps,
|
ReactFlowProps,
|
||||||
| 'nodeTypes'
|
|
||||||
| 'edgeTypes'
|
|
||||||
| 'selectionKeyCode'
|
| 'selectionKeyCode'
|
||||||
| 'deleteKeyCode'
|
| 'deleteKeyCode'
|
||||||
| 'multiSelectionKeyCode'
|
| 'multiSelectionKeyCode'
|
||||||
@@ -108,8 +104,8 @@ const GraphView = ({
|
|||||||
viewport,
|
viewport,
|
||||||
onViewportChange,
|
onViewportChange,
|
||||||
}: GraphViewProps) => {
|
}: GraphViewProps) => {
|
||||||
const nodeTypesWrapped = useNodeOrEdgeTypes(nodeTypes, createNodeTypes);
|
useNodeOrEdgeTypesWarning(nodeTypes);
|
||||||
const edgeTypesWrapped = useNodeOrEdgeTypes(edgeTypes, createEdgeTypes);
|
useNodeOrEdgeTypesWarning(edgeTypes);
|
||||||
|
|
||||||
useOnInitHandler(onInit);
|
useOnInitHandler(onInit);
|
||||||
useViewportSync(viewport);
|
useViewportSync(viewport);
|
||||||
@@ -154,7 +150,7 @@ const GraphView = ({
|
|||||||
>
|
>
|
||||||
<ViewportWrapper>
|
<ViewportWrapper>
|
||||||
<EdgeRenderer
|
<EdgeRenderer
|
||||||
edgeTypes={edgeTypesWrapped}
|
edgeTypes={edgeTypes}
|
||||||
onEdgeClick={onEdgeClick}
|
onEdgeClick={onEdgeClick}
|
||||||
onEdgeDoubleClick={onEdgeDoubleClick}
|
onEdgeDoubleClick={onEdgeDoubleClick}
|
||||||
onEdgeUpdate={onEdgeUpdate}
|
onEdgeUpdate={onEdgeUpdate}
|
||||||
@@ -182,7 +178,7 @@ const GraphView = ({
|
|||||||
<div className="react-flow__edgelabel-renderer" />
|
<div className="react-flow__edgelabel-renderer" />
|
||||||
|
|
||||||
<NodeRenderer
|
<NodeRenderer
|
||||||
nodeTypes={nodeTypesWrapped}
|
nodeTypes={nodeTypes}
|
||||||
onNodeClick={onNodeClick}
|
onNodeClick={onNodeClick}
|
||||||
onNodeDoubleClick={onNodeDoubleClick}
|
onNodeDoubleClick={onNodeDoubleClick}
|
||||||
onNodeMouseEnter={onNodeMouseEnter}
|
onNodeMouseEnter={onNodeMouseEnter}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
import { errorMessages } from '@xyflow/system';
|
||||||
|
|
||||||
|
import type { EdgeTypes, NodeTypes } from '../../types';
|
||||||
|
import { useStoreApi } from '../../hooks/useStore';
|
||||||
|
|
||||||
|
const emptyTypes = {};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This hook warns the user if node or edgeTypes change.
|
||||||
|
*/
|
||||||
|
export function useNodeOrEdgeTypesWarning(nodeOrEdgeTypes?: NodeTypes): void;
|
||||||
|
export function useNodeOrEdgeTypesWarning(nodeOrEdgeTypes?: EdgeTypes): void;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export default function useNodeOrEdgeTypesWarning(nodeOrEdgeTypes: any = emptyTypes): any {
|
||||||
|
const updateCount = useRef(0);
|
||||||
|
const store = useStoreApi();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (process.env.NODE_ENV === 'development') {
|
||||||
|
if (updateCount.current > 1) {
|
||||||
|
store.getState().onError?.('002', errorMessages['error002']());
|
||||||
|
}
|
||||||
|
updateCount.current += 1;
|
||||||
|
}
|
||||||
|
}, [nodeOrEdgeTypes]);
|
||||||
|
}
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
import { useMemo, useRef } from 'react';
|
|
||||||
import { shallow } from 'zustand/shallow';
|
|
||||||
import { errorMessages } from '@xyflow/system';
|
|
||||||
|
|
||||||
import { CreateEdgeTypes } from '../EdgeRenderer/utils';
|
|
||||||
import { CreateNodeTypes } from '../NodeRenderer/utils';
|
|
||||||
import type { EdgeTypes, EdgeTypesWrapped, NodeTypes, NodeTypesWrapped } from '../../types';
|
|
||||||
import { useStoreApi } from '../../hooks/useStore';
|
|
||||||
|
|
||||||
export function useNodeOrEdgeTypes(nodeOrEdgeTypes: NodeTypes, createTypes: CreateNodeTypes): NodeTypesWrapped;
|
|
||||||
export function useNodeOrEdgeTypes(nodeOrEdgeTypes: EdgeTypes, createTypes: CreateEdgeTypes): EdgeTypesWrapped;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
export function useNodeOrEdgeTypes(nodeOrEdgeTypes: any, createTypes: any): any {
|
|
||||||
const typesKeysRef = useRef<string[] | null>(null);
|
|
||||||
const store = useStoreApi();
|
|
||||||
|
|
||||||
const typesParsed = useMemo(() => {
|
|
||||||
if (process.env.NODE_ENV === 'development') {
|
|
||||||
const typeKeys = Object.keys(nodeOrEdgeTypes);
|
|
||||||
|
|
||||||
if (shallow(typesKeysRef.current, typeKeys)) {
|
|
||||||
store.getState().onError?.('002', errorMessages['error002']());
|
|
||||||
}
|
|
||||||
|
|
||||||
typesKeysRef.current = typeKeys;
|
|
||||||
}
|
|
||||||
return createTypes(nodeOrEdgeTypes);
|
|
||||||
}, [nodeOrEdgeTypes]);
|
|
||||||
|
|
||||||
return typesParsed;
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
import { memo, useMemo, useEffect, useRef, type ComponentType } from 'react';
|
import { memo } from 'react';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
import { internalsSymbol, errorMessages, Position, clampPosition, getPositionWithOrigin } from '@xyflow/system';
|
|
||||||
|
|
||||||
import useVisibleNodes from '../../hooks/useVisibleNodes';
|
import useVisibleNodesIds from '../../hooks/useVisibleNodes';
|
||||||
import { useStore } from '../../hooks/useStore';
|
import { useStore } from '../../hooks/useStore';
|
||||||
import { containerStyle } from '../../styles/utils';
|
import { containerStyle } from '../../styles/utils';
|
||||||
import { GraphViewProps } from '../GraphView';
|
import { GraphViewProps } from '../GraphView';
|
||||||
import type { NodeTypesWrapped, ReactFlowState, WrapNodeProps } from '../../types';
|
import type { ReactFlowState } from '../../types';
|
||||||
|
import useResizeObserver from './useResizeObserver';
|
||||||
|
import NodeWrapper from '../../components/NodeWrapper';
|
||||||
|
|
||||||
type NodeRendererProps = Pick<
|
export type NodeRendererProps = Pick<
|
||||||
GraphViewProps,
|
GraphViewProps,
|
||||||
| 'onNodeClick'
|
| 'onNodeClick'
|
||||||
| 'onNodeDoubleClick'
|
| 'onNodeDoubleClick'
|
||||||
@@ -23,127 +24,71 @@ type NodeRendererProps = Pick<
|
|||||||
| 'disableKeyboardA11y'
|
| 'disableKeyboardA11y'
|
||||||
| 'nodeOrigin'
|
| 'nodeOrigin'
|
||||||
| 'nodeExtent'
|
| 'nodeExtent'
|
||||||
> & {
|
| 'nodeTypes'
|
||||||
nodeTypes: NodeTypesWrapped;
|
>;
|
||||||
};
|
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
nodesDraggable: s.nodesDraggable,
|
nodesDraggable: s.nodesDraggable,
|
||||||
nodesConnectable: s.nodesConnectable,
|
nodesConnectable: s.nodesConnectable,
|
||||||
nodesFocusable: s.nodesFocusable,
|
nodesFocusable: s.nodesFocusable,
|
||||||
elementsSelectable: s.elementsSelectable,
|
elementsSelectable: s.elementsSelectable,
|
||||||
updateNodeDimensions: s.updateNodeDimensions,
|
|
||||||
onError: s.onError,
|
onError: s.onError,
|
||||||
});
|
});
|
||||||
|
|
||||||
const NodeRenderer = (props: NodeRendererProps) => {
|
const NodeRenderer = (props: NodeRendererProps) => {
|
||||||
const { nodesDraggable, nodesConnectable, nodesFocusable, elementsSelectable, updateNodeDimensions, onError } =
|
const { nodesDraggable, nodesConnectable, nodesFocusable, elementsSelectable, onError } = useStore(selector, shallow);
|
||||||
useStore(selector, shallow);
|
const nodeIds = useVisibleNodesIds(props.onlyRenderVisibleElements);
|
||||||
const nodes = useVisibleNodes(props.onlyRenderVisibleElements);
|
const resizeObserver = useResizeObserver();
|
||||||
const resizeObserverRef = useRef<ResizeObserver>();
|
|
||||||
|
|
||||||
const resizeObserver = useMemo(() => {
|
|
||||||
if (typeof ResizeObserver === 'undefined') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const observer = new ResizeObserver((entries: ResizeObserverEntry[]) => {
|
|
||||||
const updates = new Map();
|
|
||||||
|
|
||||||
entries.forEach((entry: ResizeObserverEntry) => {
|
|
||||||
const id = entry.target.getAttribute('data-id') as string;
|
|
||||||
updates.set(id, {
|
|
||||||
id,
|
|
||||||
nodeElement: entry.target as HTMLDivElement,
|
|
||||||
forceUpdate: true,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
updateNodeDimensions(updates);
|
|
||||||
});
|
|
||||||
|
|
||||||
resizeObserverRef.current = observer;
|
|
||||||
|
|
||||||
return observer;
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
resizeObserverRef?.current?.disconnect();
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="react-flow__nodes" style={containerStyle}>
|
<div className="react-flow__nodes" style={containerStyle}>
|
||||||
{nodes.map((node) => {
|
{nodeIds.map((nodeId) => {
|
||||||
let nodeType = node.type || 'default';
|
|
||||||
|
|
||||||
if (!props.nodeTypes[nodeType]) {
|
|
||||||
onError?.('003', errorMessages['error003'](nodeType));
|
|
||||||
|
|
||||||
nodeType = 'default';
|
|
||||||
}
|
|
||||||
|
|
||||||
const NodeComponent = (props.nodeTypes[nodeType] || props.nodeTypes.default) as ComponentType<WrapNodeProps>;
|
|
||||||
const isDraggable = !!(node.draggable || (nodesDraggable && typeof node.draggable === 'undefined'));
|
|
||||||
const isSelectable = !!(node.selectable || (elementsSelectable && typeof node.selectable === 'undefined'));
|
|
||||||
const isConnectable = !!(node.connectable || (nodesConnectable && typeof node.connectable === 'undefined'));
|
|
||||||
const isFocusable = !!(node.focusable || (nodesFocusable && typeof node.focusable === 'undefined'));
|
|
||||||
|
|
||||||
const clampedPosition = props.nodeExtent
|
|
||||||
? clampPosition(node.computed?.positionAbsolute, props.nodeExtent)
|
|
||||||
: node.computed?.positionAbsolute;
|
|
||||||
|
|
||||||
const posX = clampedPosition?.x ?? 0;
|
|
||||||
const posY = clampedPosition?.y ?? 0;
|
|
||||||
const posOrigin = getPositionWithOrigin({
|
|
||||||
x: posX,
|
|
||||||
y: posY,
|
|
||||||
width: node.computed?.width ?? node.width ?? 0,
|
|
||||||
height: node.computed?.height ?? node.height ?? 0,
|
|
||||||
origin: node.origin || props.nodeOrigin,
|
|
||||||
});
|
|
||||||
const initialized = (!!node.computed?.width && !!node.computed?.height) || (!!node.width && !!node.height);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NodeComponent
|
// The split of responsibilities between NodeRenderer and
|
||||||
key={node.id}
|
// NodeComponentWrapper may appear weird. However, it’s designed to
|
||||||
id={node.id}
|
// minimize the cost of updates when individual nodes change.
|
||||||
className={node.className}
|
//
|
||||||
style={node.style}
|
// For example, when you’re dragging a single node, that node gets
|
||||||
width={node.width ?? undefined}
|
// updated multiple times per second. If `NodeRenderer` were to update
|
||||||
height={node.height ?? undefined}
|
// every time, it would have to re-run the `nodes.map()` loop every
|
||||||
type={nodeType}
|
// time. This gets pricey with hundreds of nodes, especially if every
|
||||||
data={node.data}
|
// loop cycle does more than just rendering a JSX element!
|
||||||
sourcePosition={node.sourcePosition || Position.Bottom}
|
//
|
||||||
targetPosition={node.targetPosition || Position.Top}
|
// As a result of this choice, we took the following implementation
|
||||||
hidden={node.hidden}
|
// decisions:
|
||||||
xPos={posX}
|
// - NodeRenderer subscribes *only* to node IDs – and therefore
|
||||||
yPos={posY}
|
// rerender *only* when visible nodes are added or removed.
|
||||||
xPosOrigin={posOrigin.x}
|
// - NodeRenderer performs all operations the result of which can be
|
||||||
yPosOrigin={posOrigin.y}
|
// shared between nodes (such as creating the `ResizeObserver`
|
||||||
positionAbsolute={clampedPosition || { x: 0, y: 0 }}
|
// instance, or subscribing to `selector`). This means extra prop
|
||||||
|
// drilling into `NodeComponentWrapper`, but it means we need to run
|
||||||
|
// these operations only once – instead of once per node.
|
||||||
|
// - Any operations that you’d normally write inside `nodes.map` are
|
||||||
|
// moved into `NodeComponentWrapper`. This ensures they are
|
||||||
|
// memorized – so if `NodeRenderer` *has* to rerender, it only
|
||||||
|
// needs to regenerate the list of nodes, nothing else.
|
||||||
|
<NodeWrapper
|
||||||
|
key={nodeId}
|
||||||
|
id={nodeId}
|
||||||
|
nodeTypes={props.nodeTypes}
|
||||||
|
nodeExtent={props.nodeExtent}
|
||||||
|
nodeOrigin={props.nodeOrigin}
|
||||||
onClick={props.onNodeClick}
|
onClick={props.onNodeClick}
|
||||||
onMouseEnter={props.onNodeMouseEnter}
|
onMouseEnter={props.onNodeMouseEnter}
|
||||||
onMouseMove={props.onNodeMouseMove}
|
onMouseMove={props.onNodeMouseMove}
|
||||||
onMouseLeave={props.onNodeMouseLeave}
|
onMouseLeave={props.onNodeMouseLeave}
|
||||||
onContextMenu={props.onNodeContextMenu}
|
onContextMenu={props.onNodeContextMenu}
|
||||||
onDoubleClick={props.onNodeDoubleClick}
|
onDoubleClick={props.onNodeDoubleClick}
|
||||||
selected={!!node.selected}
|
|
||||||
isDraggable={isDraggable}
|
|
||||||
isSelectable={isSelectable}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
isFocusable={isFocusable}
|
|
||||||
resizeObserver={resizeObserver}
|
|
||||||
dragHandle={node.dragHandle}
|
|
||||||
zIndex={node[internalsSymbol]?.z ?? 0}
|
|
||||||
isParent={!!node[internalsSymbol]?.isParent}
|
|
||||||
noDragClassName={props.noDragClassName}
|
noDragClassName={props.noDragClassName}
|
||||||
noPanClassName={props.noPanClassName}
|
noPanClassName={props.noPanClassName}
|
||||||
initialized={initialized}
|
|
||||||
rfId={props.rfId}
|
rfId={props.rfId}
|
||||||
disableKeyboardA11y={props.disableKeyboardA11y}
|
disableKeyboardA11y={props.disableKeyboardA11y}
|
||||||
ariaLabel={node.ariaLabel}
|
resizeObserver={resizeObserver}
|
||||||
|
nodesDraggable={nodesDraggable}
|
||||||
|
nodesConnectable={nodesConnectable}
|
||||||
|
nodesFocusable={nodesFocusable}
|
||||||
|
elementsSelectable={elementsSelectable}
|
||||||
|
onError={onError}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { useEffect, useMemo, useRef } from 'react';
|
||||||
|
|
||||||
|
import { ReactFlowState } from '../../types';
|
||||||
|
import { useStore } from '../../hooks/useStore';
|
||||||
|
|
||||||
|
const selector = (s: ReactFlowState) => s.updateNodeDimensions;
|
||||||
|
|
||||||
|
export default function useResizeObserver() {
|
||||||
|
const updateNodeDimensions = useStore(selector);
|
||||||
|
const resizeObserverRef = useRef<ResizeObserver>();
|
||||||
|
|
||||||
|
const resizeObserver = useMemo(() => {
|
||||||
|
if (typeof ResizeObserver === 'undefined') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const observer = new ResizeObserver((entries: ResizeObserverEntry[]) => {
|
||||||
|
const updates = new Map();
|
||||||
|
|
||||||
|
entries.forEach((entry: ResizeObserverEntry) => {
|
||||||
|
const id = entry.target.getAttribute('data-id') as string;
|
||||||
|
updates.set(id, {
|
||||||
|
id,
|
||||||
|
nodeElement: entry.target as HTMLDivElement,
|
||||||
|
forceUpdate: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
updateNodeDimensions(updates);
|
||||||
|
});
|
||||||
|
|
||||||
|
resizeObserverRef.current = observer;
|
||||||
|
|
||||||
|
return observer;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
resizeObserverRef?.current?.disconnect();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return resizeObserver;
|
||||||
|
}
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
import type { ComponentType } from 'react';
|
|
||||||
import type { NodeProps } from '@xyflow/system';
|
|
||||||
|
|
||||||
import DefaultNode from '../../components/Nodes/DefaultNode';
|
|
||||||
import InputNode from '../../components/Nodes/InputNode';
|
|
||||||
import OutputNode from '../../components/Nodes/OutputNode';
|
|
||||||
import GroupNode from '../../components/Nodes/GroupNode';
|
|
||||||
import wrapNode from '../../components/Nodes/wrapNode';
|
|
||||||
import type { NodeTypes, NodeTypesWrapped } from '../../types';
|
|
||||||
|
|
||||||
export type CreateNodeTypes = (nodeTypes: NodeTypes) => NodeTypesWrapped;
|
|
||||||
|
|
||||||
export function createNodeTypes(nodeTypes: NodeTypes): NodeTypesWrapped {
|
|
||||||
const standardTypes: NodeTypesWrapped = {
|
|
||||||
input: wrapNode((nodeTypes.input || InputNode) as ComponentType<NodeProps>),
|
|
||||||
default: wrapNode((nodeTypes.default || DefaultNode) as ComponentType<NodeProps>),
|
|
||||||
output: wrapNode((nodeTypes.output || OutputNode) as ComponentType<NodeProps>),
|
|
||||||
group: wrapNode((nodeTypes.group || GroupNode) as ComponentType<NodeProps>),
|
|
||||||
};
|
|
||||||
|
|
||||||
const wrappedTypes = {} as NodeTypesWrapped;
|
|
||||||
const specialTypes: NodeTypesWrapped = Object.keys(nodeTypes)
|
|
||||||
.filter((k) => !['input', 'default', 'output', 'group'].includes(k))
|
|
||||||
.reduce((res, key) => {
|
|
||||||
res[key] = wrapNode((nodeTypes[key] || DefaultNode) as ComponentType<NodeProps>);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}, wrappedTypes);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...standardTypes,
|
|
||||||
...specialTypes,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -12,40 +12,15 @@ import {
|
|||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import Attribution from '../../components/Attribution';
|
import Attribution from '../../components/Attribution';
|
||||||
import {
|
|
||||||
BezierEdgeInternal,
|
|
||||||
SmoothStepEdgeInternal,
|
|
||||||
StepEdgeInternal,
|
|
||||||
StraightEdgeInternal,
|
|
||||||
SimpleBezierEdgeInternal,
|
|
||||||
} from '../../components/Edges';
|
|
||||||
import DefaultNode from '../../components/Nodes/DefaultNode';
|
|
||||||
import InputNode from '../../components/Nodes/InputNode';
|
|
||||||
import OutputNode from '../../components/Nodes/OutputNode';
|
|
||||||
import GroupNode from '../../components/Nodes/GroupNode';
|
|
||||||
import SelectionListener from '../../components/SelectionListener';
|
import SelectionListener from '../../components/SelectionListener';
|
||||||
import StoreUpdater from '../../components/StoreUpdater';
|
import StoreUpdater from '../../components/StoreUpdater';
|
||||||
import A11yDescriptions from '../../components/A11yDescriptions';
|
import A11yDescriptions from '../../components/A11yDescriptions';
|
||||||
import GraphView from '../GraphView';
|
import GraphView from '../GraphView';
|
||||||
import Wrapper from './Wrapper';
|
import Wrapper from './Wrapper';
|
||||||
import type { EdgeTypes, NodeTypes, ReactFlowProps, ReactFlowRefType } from '../../types';
|
import type { ReactFlowProps, ReactFlowRefType } from '../../types';
|
||||||
import useColorModeClass from '../../hooks/useColorModeClass';
|
import useColorModeClass from '../../hooks/useColorModeClass';
|
||||||
|
|
||||||
const defaultNodeTypes: NodeTypes = {
|
|
||||||
input: InputNode,
|
|
||||||
default: DefaultNode,
|
|
||||||
output: OutputNode,
|
|
||||||
group: GroupNode,
|
|
||||||
};
|
|
||||||
|
|
||||||
const defaultEdgeTypes: EdgeTypes = {
|
|
||||||
default: BezierEdgeInternal,
|
|
||||||
straight: StraightEdgeInternal,
|
|
||||||
step: StepEdgeInternal,
|
|
||||||
smoothstep: SmoothStepEdgeInternal,
|
|
||||||
simplebezier: SimpleBezierEdgeInternal,
|
|
||||||
};
|
|
||||||
|
|
||||||
const initNodeOrigin: NodeOrigin = [0, 0];
|
const initNodeOrigin: NodeOrigin = [0, 0];
|
||||||
const initSnapGrid: [number, number] = [15, 15];
|
const initSnapGrid: [number, number] = [15, 15];
|
||||||
const initDefaultViewport: Viewport = { x: 0, y: 0, zoom: 1 };
|
const initDefaultViewport: Viewport = { x: 0, y: 0, zoom: 1 };
|
||||||
@@ -66,8 +41,8 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
|||||||
defaultNodes,
|
defaultNodes,
|
||||||
defaultEdges,
|
defaultEdges,
|
||||||
className,
|
className,
|
||||||
nodeTypes = defaultNodeTypes,
|
nodeTypes,
|
||||||
edgeTypes = defaultEdgeTypes,
|
edgeTypes,
|
||||||
onNodeClick,
|
onNodeClick,
|
||||||
onEdgeClick,
|
onEdgeClick,
|
||||||
onInit,
|
onInit,
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
import { useCallback } from 'react';
|
|
||||||
import { getNodesInside } from '@xyflow/system';
|
import { getNodesInside } from '@xyflow/system';
|
||||||
|
import { shallow } from 'zustand/shallow';
|
||||||
|
|
||||||
import { useStore } from '../hooks/useStore';
|
import { useStore } from '../hooks/useStore';
|
||||||
import type { Node, ReactFlowState } from '../types';
|
import type { Node, ReactFlowState } from '../types';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
function useVisibleNodes(onlyRenderVisible: boolean) {
|
const selector = (onlyRenderVisible: boolean) => (s: ReactFlowState) => {
|
||||||
const nodes = useStore(
|
return onlyRenderVisible
|
||||||
useCallback(
|
? getNodesInside<Node>(s.nodes, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true).map(
|
||||||
(s: ReactFlowState) =>
|
(node) => node.id
|
||||||
onlyRenderVisible
|
)
|
||||||
? getNodesInside<Node>(s.nodes, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true)
|
: Array.from(s.nodeLookup.keys());
|
||||||
: s.nodes,
|
};
|
||||||
[onlyRenderVisible]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return nodes;
|
function useVisibleNodeIds(onlyRenderVisible: boolean) {
|
||||||
|
const nodeIds = useStore(useCallback(selector(onlyRenderVisible), [onlyRenderVisible]), shallow);
|
||||||
|
|
||||||
|
return nodeIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default useVisibleNodes;
|
export default useVisibleNodeIds;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createWithEqualityFn } from 'zustand/traditional';
|
|||||||
import {
|
import {
|
||||||
clampPosition,
|
clampPosition,
|
||||||
fitView as fitViewSystem,
|
fitView as fitViewSystem,
|
||||||
updateNodes,
|
adoptUserProvidedNodes,
|
||||||
updateAbsolutePositions,
|
updateAbsolutePositions,
|
||||||
panBy as panBySystem,
|
panBy as panBySystem,
|
||||||
Dimensions,
|
Dimensions,
|
||||||
@@ -43,11 +43,16 @@ const createRFStore = ({
|
|||||||
...getInitialState({ nodes, edges, width, height, fitView }),
|
...getInitialState({ nodes, edges, width, height, fitView }),
|
||||||
setNodes: (nodes: Node[]) => {
|
setNodes: (nodes: Node[]) => {
|
||||||
const { nodeLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
const { nodeLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
||||||
// Whenver new nodes are set, we need to calculate the absolute positions of the nodes
|
// setNodes() is called exclusively in response to user actions:
|
||||||
// and update the nodeLookup.
|
// - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
|
||||||
const nextNodes = updateNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect });
|
// - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup.
|
||||||
|
//
|
||||||
|
// When this happens, we take the note objects passed by the user and extend them with fields
|
||||||
|
// relevant for internal React Flow operations.
|
||||||
|
// TODO: consider updating the types to reflect the distinction between user-provided nodes and internal nodes.
|
||||||
|
const nodesWithInternalData = adoptUserProvidedNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect });
|
||||||
|
|
||||||
set({ nodes: nextNodes });
|
set({ nodes: nodesWithInternalData });
|
||||||
},
|
},
|
||||||
setEdges: (edges: Edge[]) => {
|
setEdges: (edges: Edge[]) => {
|
||||||
const { defaultEdgeOptions = {}, connectionLookup } = get();
|
const { defaultEdgeOptions = {}, connectionLookup } = get();
|
||||||
@@ -74,7 +79,8 @@ const createRFStore = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (hasDefaultNodes) {
|
if (hasDefaultNodes) {
|
||||||
nextState.nodes = updateNodes(nodes, new Map(), {
|
const { nodeLookup } = get();
|
||||||
|
nextState.nodes = adoptUserProvidedNodes(nodes, nodeLookup, {
|
||||||
nodeOrigin: get().nodeOrigin,
|
nodeOrigin: get().nodeOrigin,
|
||||||
elevateNodesOnSelect: get().elevateNodesOnSelect,
|
elevateNodesOnSelect: get().elevateNodesOnSelect,
|
||||||
});
|
});
|
||||||
@@ -168,7 +174,7 @@ const createRFStore = ({
|
|||||||
if (changes?.length) {
|
if (changes?.length) {
|
||||||
if (hasDefaultNodes) {
|
if (hasDefaultNodes) {
|
||||||
const updatedNodes = applyNodeChanges(changes, nodes);
|
const updatedNodes = applyNodeChanges(changes, nodes);
|
||||||
const nextNodes = updateNodes(updatedNodes, nodeLookup, {
|
const nextNodes = adoptUserProvidedNodes(updatedNodes, nodeLookup, {
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
elevateNodesOnSelect,
|
elevateNodesOnSelect,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
infiniteExtent,
|
infiniteExtent,
|
||||||
ConnectionMode,
|
ConnectionMode,
|
||||||
updateNodes,
|
adoptUserProvidedNodes,
|
||||||
getNodesBounds,
|
getNodesBounds,
|
||||||
getViewportForBounds,
|
getViewportForBounds,
|
||||||
Transform,
|
Transform,
|
||||||
@@ -25,7 +25,10 @@ const getInitialState = ({
|
|||||||
} = {}): ReactFlowStore => {
|
} = {}): ReactFlowStore => {
|
||||||
const nodeLookup = new Map();
|
const nodeLookup = new Map();
|
||||||
const connectionLookup = updateConnectionLookup(new Map(), edges);
|
const connectionLookup = updateConnectionLookup(new Map(), edges);
|
||||||
const nextNodes = updateNodes(nodes, nodeLookup, { nodeOrigin: [0, 0], elevateNodesOnSelect: false });
|
const nextNodes = adoptUserProvidedNodes(nodes, nodeLookup, {
|
||||||
|
nodeOrigin: [0, 0],
|
||||||
|
elevateNodesOnSelect: false,
|
||||||
|
});
|
||||||
|
|
||||||
let transform: Transform = [0, 0, 1];
|
let transform: Transform = [0, 0, 1];
|
||||||
|
|
||||||
|
|||||||
@@ -15,9 +15,10 @@ import type {
|
|||||||
EdgePosition,
|
EdgePosition,
|
||||||
Optional,
|
Optional,
|
||||||
StepPathOptions,
|
StepPathOptions,
|
||||||
|
OnError,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import { Node } from '.';
|
import { EdgeTypes, Node } from '.';
|
||||||
|
|
||||||
export type EdgeLabelOptions = {
|
export type EdgeLabelOptions = {
|
||||||
label?: string | ReactNode;
|
label?: string | ReactNode;
|
||||||
@@ -57,7 +58,7 @@ export type Edge<T = any> = DefaultEdge<T> | SmoothStepEdgeType<T> | BezierEdgeT
|
|||||||
|
|
||||||
export type EdgeMouseHandler = (event: ReactMouseEvent, edge: Edge) => void;
|
export type EdgeMouseHandler = (event: ReactMouseEvent, edge: Edge) => void;
|
||||||
|
|
||||||
export type WrapEdgeProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHandle'> & {
|
export type EdgeWrapperProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHandle'> & {
|
||||||
onClick?: EdgeMouseHandler;
|
onClick?: EdgeMouseHandler;
|
||||||
onEdgeDoubleClick?: EdgeMouseHandler;
|
onEdgeDoubleClick?: EdgeMouseHandler;
|
||||||
sourceHandleId?: string | null;
|
sourceHandleId?: string | null;
|
||||||
@@ -75,6 +76,8 @@ export type WrapEdgeProps<T = any> = Omit<Edge<T>, 'sourceHandle' | 'targetHandl
|
|||||||
isUpdatable: EdgeUpdatable;
|
isUpdatable: EdgeUpdatable;
|
||||||
isSelectable: boolean;
|
isSelectable: boolean;
|
||||||
pathOptions?: BezierPathOptions | SmoothStepPathOptions;
|
pathOptions?: BezierPathOptions | SmoothStepPathOptions;
|
||||||
|
edgeTypes?: EdgeTypes;
|
||||||
|
onError?: OnError;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type DefaultEdgeOptions = DefaultEdgeOptionsBase<Edge>;
|
export type DefaultEdgeOptions = DefaultEdgeOptionsBase<Edge>;
|
||||||
@@ -90,7 +93,7 @@ export type EdgeProps<T = any> = Pick<
|
|||||||
Edge<T>,
|
Edge<T>,
|
||||||
'id' | 'animated' | 'data' | 'style' | 'selected' | 'source' | 'target'
|
'id' | 'animated' | 'data' | 'style' | 'selected' | 'source' | 'target'
|
||||||
> &
|
> &
|
||||||
Pick<WrapEdgeProps, 'sourceHandleId' | 'targetHandleId' | 'interactionWidth'> &
|
Pick<EdgeWrapperProps, 'sourceHandleId' | 'targetHandleId' | 'interactionWidth'> &
|
||||||
EdgePosition &
|
EdgePosition &
|
||||||
EdgeLabelOptions & {
|
EdgeLabelOptions & {
|
||||||
markerStart?: string;
|
markerStart?: string;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import type { ComponentType, MemoExoticComponent } from 'react';
|
import type { ComponentType } from 'react';
|
||||||
import {
|
import {
|
||||||
FitViewParamsBase,
|
FitViewParamsBase,
|
||||||
FitViewOptionsBase,
|
FitViewOptionsBase,
|
||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
XYPosition,
|
XYPosition,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import type { NodeChange, EdgeChange, Node, WrapNodeProps, Edge, EdgeProps, WrapEdgeProps, ReactFlowInstance } from '.';
|
import type { NodeChange, EdgeChange, Node, Edge, EdgeProps, ReactFlowInstance } from '.';
|
||||||
|
|
||||||
export type OnNodesChange = (changes: NodeChange[]) => void;
|
export type OnNodesChange = (changes: NodeChange[]) => void;
|
||||||
export type OnEdgesChange = (changes: EdgeChange[]) => void;
|
export type OnEdgesChange = (changes: EdgeChange[]) => void;
|
||||||
@@ -24,9 +24,7 @@ export type OnEdgesDelete = (edges: Edge[]) => void;
|
|||||||
export type OnDelete = (params: { nodes: Node[]; edges: Edge[] }) => void;
|
export type OnDelete = (params: { nodes: Node[]; edges: Edge[] }) => void;
|
||||||
|
|
||||||
export type NodeTypes = { [key: string]: ComponentType<NodeProps> };
|
export type NodeTypes = { [key: string]: ComponentType<NodeProps> };
|
||||||
export type NodeTypesWrapped = { [key: string]: MemoExoticComponent<ComponentType<WrapNodeProps>> };
|
|
||||||
export type EdgeTypes = { [key: string]: ComponentType<EdgeProps> };
|
export type EdgeTypes = { [key: string]: ComponentType<EdgeProps> };
|
||||||
export type EdgeTypesWrapped = { [key: string]: MemoExoticComponent<ComponentType<WrapEdgeProps>> };
|
|
||||||
|
|
||||||
export type UnselectNodesAndEdgesParams = {
|
export type UnselectNodesAndEdgesParams = {
|
||||||
nodes?: Node[];
|
nodes?: Node[];
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
|
import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
|
||||||
import type { NodeBase, XYPosition } from '@xyflow/system';
|
import type { CoordinateExtent, NodeBase, NodeOrigin, OnError } from '@xyflow/system';
|
||||||
|
import { NodeTypes } from './general';
|
||||||
|
|
||||||
export type Node<NodeData = any, NodeType extends string | undefined = string | undefined> = NodeBase<
|
export type Node<NodeData = any, NodeType extends string | undefined = string | undefined> = NodeBase<
|
||||||
NodeData,
|
NodeData,
|
||||||
@@ -14,33 +15,25 @@ export type NodeMouseHandler = (event: ReactMouseEvent, node: Node) => void;
|
|||||||
export type NodeDragHandler = (event: ReactMouseEvent, node: Node, nodes: Node[]) => void;
|
export type NodeDragHandler = (event: ReactMouseEvent, node: Node, nodes: Node[]) => void;
|
||||||
export type SelectionDragHandler = (event: ReactMouseEvent, nodes: Node[]) => void;
|
export type SelectionDragHandler = (event: ReactMouseEvent, nodes: Node[]) => void;
|
||||||
|
|
||||||
export type WrapNodeProps<NodeData = any> = Pick<
|
export type NodeWrapperProps = {
|
||||||
Node<NodeData>,
|
id: string;
|
||||||
'id' | 'data' | 'style' | 'className' | 'dragHandle' | 'sourcePosition' | 'targetPosition' | 'hidden' | 'ariaLabel'
|
nodesConnectable: boolean;
|
||||||
> &
|
elementsSelectable: boolean;
|
||||||
Required<Pick<Node<NodeData>, 'selected' | 'type' | 'zIndex'>> & {
|
nodesDraggable: boolean;
|
||||||
isConnectable: boolean;
|
nodesFocusable: boolean;
|
||||||
xPos: number;
|
onClick?: NodeMouseHandler;
|
||||||
yPos: number;
|
onDoubleClick?: NodeMouseHandler;
|
||||||
xPosOrigin: number;
|
onMouseEnter?: NodeMouseHandler;
|
||||||
yPosOrigin: number;
|
onMouseMove?: NodeMouseHandler;
|
||||||
positionAbsolute: XYPosition;
|
onMouseLeave?: NodeMouseHandler;
|
||||||
initialized: boolean;
|
onContextMenu?: NodeMouseHandler;
|
||||||
isSelectable: boolean;
|
resizeObserver: ResizeObserver | null;
|
||||||
isDraggable: boolean;
|
noDragClassName: string;
|
||||||
isFocusable: boolean;
|
noPanClassName: string;
|
||||||
onClick?: NodeMouseHandler;
|
rfId: string;
|
||||||
onDoubleClick?: NodeMouseHandler;
|
disableKeyboardA11y: boolean;
|
||||||
onMouseEnter?: NodeMouseHandler;
|
nodeTypes?: NodeTypes;
|
||||||
onMouseMove?: NodeMouseHandler;
|
nodeExtent?: CoordinateExtent;
|
||||||
onMouseLeave?: NodeMouseHandler;
|
nodeOrigin: NodeOrigin;
|
||||||
onContextMenu?: NodeMouseHandler;
|
onError?: OnError;
|
||||||
resizeObserver: ResizeObserver | null;
|
};
|
||||||
isParent: boolean;
|
|
||||||
noDragClassName: string;
|
|
||||||
noPanClassName: string;
|
|
||||||
rfId: string;
|
|
||||||
disableKeyboardA11y: boolean;
|
|
||||||
width?: number;
|
|
||||||
height?: number;
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -55,14 +55,17 @@ function applyChanges(changes: any[], elements: any[]): any[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let remainingChanges = changes;
|
let remainingChanges = changes;
|
||||||
const initElements: any[] = changes.filter((c) => c.type === 'add').map((c) => c.item);
|
const updatedElements: any[] = [];
|
||||||
|
|
||||||
return elements.reduce((res: any[], item: any) => {
|
for (let i = 0; i < elements.length; i++) {
|
||||||
const nextChanges: any[] = [];
|
const nextChanges: any[] = [];
|
||||||
const _remainingChanges: any[] = [];
|
const _remainingChanges: any[] = [];
|
||||||
|
const item = elements[i];
|
||||||
|
|
||||||
remainingChanges.forEach((c) => {
|
remainingChanges.forEach((c) => {
|
||||||
if (c.id === item.id) {
|
if (c.type === 'add') {
|
||||||
|
updatedElements.push(c.item);
|
||||||
|
} else if (c.id === item.id) {
|
||||||
nextChanges.push(c);
|
nextChanges.push(c);
|
||||||
} else {
|
} else {
|
||||||
_remainingChanges.push(c);
|
_remainingChanges.push(c);
|
||||||
@@ -71,8 +74,8 @@ function applyChanges(changes: any[], elements: any[]): any[] {
|
|||||||
remainingChanges = _remainingChanges;
|
remainingChanges = _remainingChanges;
|
||||||
|
|
||||||
if (nextChanges.length === 0) {
|
if (nextChanges.length === 0) {
|
||||||
res.push(item);
|
updatedElements.push(item);
|
||||||
return res;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const updateItem = { ...item };
|
const updateItem = { ...item };
|
||||||
@@ -101,7 +104,7 @@ function applyChanges(changes: any[], elements: any[]): any[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (updateItem.expandParent) {
|
if (updateItem.expandParent) {
|
||||||
handleParentExpand(res, updateItem);
|
handleParentExpand(updatedElements, updateItem);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -123,20 +126,20 @@ function applyChanges(changes: any[], elements: any[]): any[] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (updateItem.expandParent) {
|
if (updateItem.expandParent) {
|
||||||
handleParentExpand(res, updateItem);
|
handleParentExpand(updatedElements, updateItem);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'remove': {
|
case 'remove': {
|
||||||
return res;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
updatedElements.push(updateItem);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
res.push(updateItem);
|
return updatedElements;
|
||||||
return res;
|
|
||||||
}, initElements);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyNodeChanges<NodeData = any>(changes: NodeChange[], nodes: Node<NodeData>[]): Node<NodeData>[] {
|
export function applyNodeChanges<NodeData = any>(changes: NodeChange[], nodes: Node<NodeData>[]): Node<NodeData>[] {
|
||||||
|
|||||||
@@ -191,7 +191,8 @@
|
|||||||
{dragging}
|
{dragging}
|
||||||
{dragHandle}
|
{dragHandle}
|
||||||
isConnectable={connectable}
|
isConnectable={connectable}
|
||||||
positionAbsolute={{ x: positionX, y: positionY }}
|
positionAbsoluteX={positionX}
|
||||||
|
positionAbsoluteY={positionY}
|
||||||
{width}
|
{width}
|
||||||
{height}
|
{height}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -18,10 +18,8 @@
|
|||||||
export let zIndex: $$Props['zIndex'] = undefined;
|
export let zIndex: $$Props['zIndex'] = undefined;
|
||||||
export let dragging: $$Props['dragging'] = false;
|
export let dragging: $$Props['dragging'] = false;
|
||||||
export let dragHandle: $$Props['dragHandle'] = undefined;
|
export let dragHandle: $$Props['dragHandle'] = undefined;
|
||||||
export let positionAbsolute: $$Props['positionAbsolute'] = {
|
export let positionAbsoluteX: $$Props['positionAbsoluteX'] = 0;
|
||||||
x: 0,
|
export let positionAbsoluteY: $$Props['positionAbsoluteY'] = 0;
|
||||||
y: 0
|
|
||||||
};
|
|
||||||
export let isConnectable: $$Props['isConnectable'] = undefined;
|
export let isConnectable: $$Props['isConnectable'] = undefined;
|
||||||
|
|
||||||
// @todo: there must be a better way to do this
|
// @todo: there must be a better way to do this
|
||||||
@@ -33,7 +31,8 @@
|
|||||||
zIndex;
|
zIndex;
|
||||||
dragging;
|
dragging;
|
||||||
dragHandle;
|
dragHandle;
|
||||||
positionAbsolute;
|
positionAbsoluteX;
|
||||||
|
positionAbsoluteY;
|
||||||
isConnectable;
|
isConnectable;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -15,10 +15,8 @@
|
|||||||
export let zIndex: $$Props['zIndex'] = undefined;
|
export let zIndex: $$Props['zIndex'] = undefined;
|
||||||
export let dragging: $$Props['dragging'] = false;
|
export let dragging: $$Props['dragging'] = false;
|
||||||
export let dragHandle: $$Props['dragHandle'] = undefined;
|
export let dragHandle: $$Props['dragHandle'] = undefined;
|
||||||
export let positionAbsolute: $$Props['positionAbsolute'] = {
|
export let positionAbsoluteX: $$Props['positionAbsoluteX'] = 0;
|
||||||
x: 0,
|
export let positionAbsoluteY: $$Props['positionAbsoluteY'] = 0;
|
||||||
y: 0
|
|
||||||
};
|
|
||||||
export let isConnectable: $$Props['isConnectable'] = undefined;
|
export let isConnectable: $$Props['isConnectable'] = undefined;
|
||||||
|
|
||||||
// @todo: there must be a better way to do this
|
// @todo: there must be a better way to do this
|
||||||
@@ -33,6 +31,7 @@
|
|||||||
zIndex;
|
zIndex;
|
||||||
dragging;
|
dragging;
|
||||||
dragHandle;
|
dragHandle;
|
||||||
positionAbsolute;
|
positionAbsoluteX;
|
||||||
|
positionAbsoluteY;
|
||||||
isConnectable;
|
isConnectable;
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -18,10 +18,8 @@
|
|||||||
export let zIndex: $$Props['zIndex'] = undefined;
|
export let zIndex: $$Props['zIndex'] = undefined;
|
||||||
export let dragging: $$Props['dragging'] = false;
|
export let dragging: $$Props['dragging'] = false;
|
||||||
export let dragHandle: $$Props['dragHandle'] = undefined;
|
export let dragHandle: $$Props['dragHandle'] = undefined;
|
||||||
export let positionAbsolute: $$Props['positionAbsolute'] = {
|
export let positionAbsoluteX: $$Props['positionAbsoluteX'] = 0;
|
||||||
x: 0,
|
export let positionAbsoluteY: $$Props['positionAbsoluteY'] = 0;
|
||||||
y: 0
|
|
||||||
};
|
|
||||||
export let isConnectable: $$Props['isConnectable'] = undefined;
|
export let isConnectable: $$Props['isConnectable'] = undefined;
|
||||||
|
|
||||||
// @todo: there must be a better way to do this
|
// @todo: there must be a better way to do this
|
||||||
@@ -34,7 +32,8 @@
|
|||||||
zIndex;
|
zIndex;
|
||||||
dragging;
|
dragging;
|
||||||
dragHandle;
|
dragHandle;
|
||||||
positionAbsolute;
|
positionAbsoluteX;
|
||||||
|
positionAbsoluteY;
|
||||||
isConnectable;
|
isConnectable;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,8 @@
|
|||||||
export let zIndex: $$Props['zIndex'] = undefined;
|
export let zIndex: $$Props['zIndex'] = undefined;
|
||||||
export let dragging: $$Props['dragging'] = false;
|
export let dragging: $$Props['dragging'] = false;
|
||||||
export let dragHandle: $$Props['dragHandle'] = undefined;
|
export let dragHandle: $$Props['dragHandle'] = undefined;
|
||||||
export let positionAbsolute: $$Props['positionAbsolute'] = { x: 0, y: 0 };
|
export let positionAbsoluteX: $$Props['positionAbsoluteX'] = 0;
|
||||||
|
export let positionAbsoluteY: $$Props['positionAbsoluteY'] = 0;
|
||||||
export let isConnectable: $$Props['isConnectable'] = undefined;
|
export let isConnectable: $$Props['isConnectable'] = undefined;
|
||||||
|
|
||||||
// @todo: there must be a better way to do this
|
// @todo: there must be a better way to do this
|
||||||
@@ -31,7 +32,8 @@
|
|||||||
zIndex;
|
zIndex;
|
||||||
dragging;
|
dragging;
|
||||||
dragHandle;
|
dragHandle;
|
||||||
positionAbsolute;
|
positionAbsoluteX;
|
||||||
|
positionAbsoluteY;
|
||||||
isConnectable;
|
isConnectable;
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import {
|
|||||||
type OnError,
|
type OnError,
|
||||||
devWarn,
|
devWarn,
|
||||||
type Viewport,
|
type Viewport,
|
||||||
updateNodes,
|
adoptUserProvidedNodes,
|
||||||
getNodesBounds,
|
getNodesBounds,
|
||||||
getViewportForBounds,
|
getViewportForBounds,
|
||||||
updateConnectionLookup,
|
updateConnectionLookup,
|
||||||
@@ -78,7 +78,7 @@ export const getInitialStore = ({
|
|||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const nodeLookup = new Map();
|
const nodeLookup = new Map();
|
||||||
const nextNodes = updateNodes(nodes, nodeLookup, {
|
const nextNodes = adoptUserProvidedNodes(nodes, nodeLookup, {
|
||||||
nodeOrigin: [0, 0],
|
nodeOrigin: [0, 0],
|
||||||
elevateNodesOnSelect: false
|
elevateNodesOnSelect: false
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ import {
|
|||||||
get
|
get
|
||||||
} from 'svelte/store';
|
} from 'svelte/store';
|
||||||
import {
|
import {
|
||||||
updateNodes,
|
adoptUserProvidedNodes,
|
||||||
|
updateConnectionLookup,
|
||||||
type Viewport,
|
type Viewport,
|
||||||
type PanZoomInstance,
|
type PanZoomInstance,
|
||||||
type ConnectionLookup,
|
type ConnectionLookup
|
||||||
updateConnectionLookup
|
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import type { DefaultEdgeOptions, DefaultNodeOptions, Edge, Node } from '$lib/types';
|
import type { DefaultEdgeOptions, DefaultNodeOptions, Edge, Node } from '$lib/types';
|
||||||
@@ -139,7 +139,7 @@ export const createNodesStore = (
|
|||||||
let elevateNodesOnSelect = true;
|
let elevateNodesOnSelect = true;
|
||||||
|
|
||||||
const _set = (nds: Node[]): Node[] => {
|
const _set = (nds: Node[]): Node[] => {
|
||||||
const nextNodes = updateNodes(nds, nodeLookup, {
|
const nextNodes = adoptUserProvidedNodes(nds, nodeLookup, {
|
||||||
elevateNodesOnSelect,
|
elevateNodesOnSelect,
|
||||||
defaults
|
defaults
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ export type NodeBase<T = any, U extends string | undefined = string | undefined>
|
|||||||
z?: number;
|
z?: number;
|
||||||
handleBounds?: NodeHandleBounds;
|
handleBounds?: NodeHandleBounds;
|
||||||
isParent?: boolean;
|
isParent?: boolean;
|
||||||
|
/** Holds a reference to the original node object provided by the user
|
||||||
|
* (which may lack some fields, like `computed` or `[internalSymbol]`. Used
|
||||||
|
* as an optimization to avoid certain operations. */
|
||||||
|
userProvidedNode: NodeBase<T, U>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,7 +56,8 @@ export type NodeProps<T = any> = {
|
|||||||
selected: NodeBase['selected'];
|
selected: NodeBase['selected'];
|
||||||
isConnectable: NodeBase['connectable'];
|
isConnectable: NodeBase['connectable'];
|
||||||
zIndex: NodeBase['zIndex'];
|
zIndex: NodeBase['zIndex'];
|
||||||
positionAbsolute: XYPosition;
|
positionAbsoluteX: number;
|
||||||
|
positionAbsoluteY: number;
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
dragging: boolean;
|
dragging: boolean;
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ type UpdateNodesOptions<NodeType extends NodeBase> = {
|
|||||||
defaults?: Partial<NodeType>;
|
defaults?: Partial<NodeType>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function updateNodes<NodeType extends NodeBase>(
|
export function adoptUserProvidedNodes<NodeType extends NodeBase>(
|
||||||
nodes: NodeType[],
|
nodes: NodeType[],
|
||||||
nodeLookup: Map<string, NodeType>,
|
nodeLookup: Map<string, NodeType>,
|
||||||
options: UpdateNodesOptions<NodeType> = {
|
options: UpdateNodesOptions<NodeType> = {
|
||||||
@@ -80,6 +80,11 @@ export function updateNodes<NodeType extends NodeBase>(
|
|||||||
|
|
||||||
const nextNodes = nodes.map((n) => {
|
const nextNodes = nodes.map((n) => {
|
||||||
const currentStoreNode = tmpLookup.get(n.id);
|
const currentStoreNode = tmpLookup.get(n.id);
|
||||||
|
if (n === currentStoreNode?.[internalsSymbol]?.userProvidedNode) {
|
||||||
|
nodeLookup.set(n.id, currentStoreNode);
|
||||||
|
return currentStoreNode;
|
||||||
|
}
|
||||||
|
|
||||||
const node: NodeType = {
|
const node: NodeType = {
|
||||||
...options.defaults,
|
...options.defaults,
|
||||||
...n,
|
...n,
|
||||||
@@ -101,6 +106,7 @@ export function updateNodes<NodeType extends NodeBase>(
|
|||||||
value: {
|
value: {
|
||||||
handleBounds: currInternals?.handleBounds,
|
handleBounds: currInternals?.handleBounds,
|
||||||
z,
|
z,
|
||||||
|
userProvidedNode: n,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -141,14 +147,14 @@ function calculateXYZPosition<NodeType extends NodeBase>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function updateNodeDimensions(
|
export function updateNodeDimensions<NodeType extends NodeBase>(
|
||||||
updates: Map<string, NodeDimensionUpdate>,
|
updates: Map<string, NodeDimensionUpdate>,
|
||||||
nodes: NodeBase[],
|
nodes: NodeType[],
|
||||||
nodeLookup: Map<string, NodeBase>,
|
nodeLookup: Map<string, NodeType>,
|
||||||
domNode: HTMLElement | null,
|
domNode: HTMLElement | null,
|
||||||
nodeOrigin?: NodeOrigin,
|
nodeOrigin?: NodeOrigin,
|
||||||
onUpdate?: (id: string, dimensions: Dimensions) => void
|
onUpdate?: (id: string, dimensions: Dimensions) => void
|
||||||
): NodeBase[] | null {
|
): NodeType[] | null {
|
||||||
const viewportNode = domNode?.querySelector('.xyflow__viewport');
|
const viewportNode = domNode?.querySelector('.xyflow__viewport');
|
||||||
|
|
||||||
if (!viewportNode) {
|
if (!viewportNode) {
|
||||||
|
|||||||
Generated
+27
-121
@@ -61,10 +61,10 @@ importers:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@astrojs/react':
|
'@astrojs/react':
|
||||||
specifier: ^3.0.2
|
specifier: ^3.0.2
|
||||||
version: registry.npmjs.org/@astrojs/react@3.0.2(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0)(vite@4.5.1)
|
version: registry.npmjs.org/@astrojs/react@3.0.2(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0)(vite@4.5.0)
|
||||||
'@astrojs/svelte':
|
'@astrojs/svelte':
|
||||||
specifier: ^4.0.2
|
specifier: ^4.0.2
|
||||||
version: registry.npmjs.org/@astrojs/svelte@4.0.2(astro@3.2.2)(svelte@4.2.1)(typescript@5.3.3)(vite@4.5.1)
|
version: registry.npmjs.org/@astrojs/svelte@4.0.2(astro@3.2.2)(svelte@4.2.1)(typescript@5.2.2)(vite@4.5.0)
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: ^18.2.24
|
specifier: ^18.2.24
|
||||||
version: registry.npmjs.org/@types/react@18.2.24
|
version: registry.npmjs.org/@types/react@18.2.24
|
||||||
@@ -305,7 +305,7 @@ importers:
|
|||||||
version: registry.npmjs.org/@sveltejs/adapter-auto@2.1.0(@sveltejs/kit@1.22.6)
|
version: registry.npmjs.org/@sveltejs/adapter-auto@2.1.0(@sveltejs/kit@1.22.6)
|
||||||
'@sveltejs/kit':
|
'@sveltejs/kit':
|
||||||
specifier: ^1.22.6
|
specifier: ^1.22.6
|
||||||
version: registry.npmjs.org/@sveltejs/kit@1.22.6(svelte@4.2.1)(vite@4.5.1)
|
version: registry.npmjs.org/@sveltejs/kit@1.22.6(svelte@4.2.1)(vite@4.5.0)
|
||||||
'@sveltejs/package':
|
'@sveltejs/package':
|
||||||
specifier: ^2.2.1
|
specifier: ^2.2.1
|
||||||
version: registry.npmjs.org/@sveltejs/package@2.2.1(svelte@4.2.1)(typescript@5.1.3)
|
version: registry.npmjs.org/@sveltejs/package@2.2.1(svelte@4.2.1)(typescript@5.1.3)
|
||||||
@@ -417,7 +417,7 @@ importers:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@playwright/experimental-ct-react':
|
'@playwright/experimental-ct-react':
|
||||||
specifier: ^1.39.0
|
specifier: ^1.39.0
|
||||||
version: registry.npmjs.org/@playwright/experimental-ct-react@1.39.0(@types/node@18.7.16)(vite@4.5.1)
|
version: registry.npmjs.org/@playwright/experimental-ct-react@1.39.0(@types/node@18.7.16)(vite@4.5.0)
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: ^18.2.31
|
specifier: ^18.2.31
|
||||||
version: registry.npmjs.org/@types/react@18.2.31
|
version: registry.npmjs.org/@types/react@18.2.31
|
||||||
@@ -551,7 +551,7 @@ packages:
|
|||||||
prismjs: registry.npmjs.org/prismjs@1.29.0
|
prismjs: registry.npmjs.org/prismjs@1.29.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
registry.npmjs.org/@astrojs/react@3.0.2(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0)(vite@4.5.1):
|
registry.npmjs.org/@astrojs/react@3.0.2(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0)(vite@4.5.0):
|
||||||
resolution: {integrity: sha512-aooNIuQxTg+IWGMZPuIEwBeBi4/TCPCMsr3714zuLjAjukVd5ZrX/bCNxJqDWU4HNwUm4XFU1OhcEvYOHa5uMQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@astrojs/react/-/react-3.0.2.tgz}
|
resolution: {integrity: sha512-aooNIuQxTg+IWGMZPuIEwBeBi4/TCPCMsr3714zuLjAjukVd5ZrX/bCNxJqDWU4HNwUm4XFU1OhcEvYOHa5uMQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@astrojs/react/-/react-3.0.2.tgz}
|
||||||
id: registry.npmjs.org/@astrojs/react/3.0.2
|
id: registry.npmjs.org/@astrojs/react/3.0.2
|
||||||
name: '@astrojs/react'
|
name: '@astrojs/react'
|
||||||
@@ -565,7 +565,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@types/react': registry.npmjs.org/@types/react@18.2.24
|
'@types/react': registry.npmjs.org/@types/react@18.2.24
|
||||||
'@types/react-dom': registry.npmjs.org/@types/react-dom@18.2.8
|
'@types/react-dom': registry.npmjs.org/@types/react-dom@18.2.8
|
||||||
'@vitejs/plugin-react': registry.npmjs.org/@vitejs/plugin-react@4.1.1(vite@4.5.1)
|
'@vitejs/plugin-react': registry.npmjs.org/@vitejs/plugin-react@4.1.1(vite@4.5.0)
|
||||||
react: registry.npmjs.org/react@18.2.0
|
react: registry.npmjs.org/react@18.2.0
|
||||||
react-dom: registry.npmjs.org/react-dom@18.2.0(react@18.2.0)
|
react-dom: registry.npmjs.org/react-dom@18.2.0(react@18.2.0)
|
||||||
ultrahtml: registry.npmjs.org/ultrahtml@1.5.2
|
ultrahtml: registry.npmjs.org/ultrahtml@1.5.2
|
||||||
@@ -574,7 +574,7 @@ packages:
|
|||||||
- vite
|
- vite
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
registry.npmjs.org/@astrojs/svelte@4.0.2(astro@3.2.2)(svelte@4.2.1)(typescript@5.3.3)(vite@4.5.1):
|
registry.npmjs.org/@astrojs/svelte@4.0.2(astro@3.2.2)(svelte@4.2.1)(typescript@5.2.2)(vite@4.5.0):
|
||||||
resolution: {integrity: sha512-XB9Sexq+iW5aUpctDk6zuKHbWIAuPlAnZKbfgaS9VOaOUtx1t12crP9YoKEVcTifXYgoRuWWfeEAm2I8pYlLIQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@astrojs/svelte/-/svelte-4.0.2.tgz}
|
resolution: {integrity: sha512-XB9Sexq+iW5aUpctDk6zuKHbWIAuPlAnZKbfgaS9VOaOUtx1t12crP9YoKEVcTifXYgoRuWWfeEAm2I8pYlLIQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@astrojs/svelte/-/svelte-4.0.2.tgz}
|
||||||
id: registry.npmjs.org/@astrojs/svelte/4.0.2
|
id: registry.npmjs.org/@astrojs/svelte/4.0.2
|
||||||
name: '@astrojs/svelte'
|
name: '@astrojs/svelte'
|
||||||
@@ -584,10 +584,10 @@ packages:
|
|||||||
astro: ^3.0.11
|
astro: ^3.0.11
|
||||||
svelte: ^3.55.0 || ^4.0.0
|
svelte: ^3.55.0 || ^4.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sveltejs/vite-plugin-svelte': registry.npmjs.org/@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.1)
|
'@sveltejs/vite-plugin-svelte': registry.npmjs.org/@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.0)
|
||||||
astro: registry.npmjs.org/astro@3.2.2
|
astro: registry.npmjs.org/astro@3.2.2
|
||||||
svelte: registry.npmjs.org/svelte@4.2.1
|
svelte: registry.npmjs.org/svelte@4.2.1
|
||||||
svelte2tsx: registry.npmjs.org/svelte2tsx@0.6.23(svelte@4.2.1)(typescript@5.3.3)
|
svelte2tsx: registry.npmjs.org/svelte2tsx@0.6.23(svelte@4.2.1)(typescript@5.2.2)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
- typescript
|
- typescript
|
||||||
@@ -1756,7 +1756,7 @@ packages:
|
|||||||
debug: registry.npmjs.org/debug@4.3.4(supports-color@8.1.1)
|
debug: registry.npmjs.org/debug@4.3.4(supports-color@8.1.1)
|
||||||
espree: registry.npmjs.org/espree@9.6.1
|
espree: registry.npmjs.org/espree@9.6.1
|
||||||
globals: registry.npmjs.org/globals@13.23.0
|
globals: registry.npmjs.org/globals@13.23.0
|
||||||
ignore: registry.npmjs.org/ignore@5.2.4
|
ignore: registry.npmjs.org/ignore@5.3.0
|
||||||
import-fresh: registry.npmjs.org/import-fresh@3.3.0
|
import-fresh: registry.npmjs.org/import-fresh@3.3.0
|
||||||
js-yaml: registry.npmjs.org/js-yaml@4.1.0
|
js-yaml: registry.npmjs.org/js-yaml@4.1.0
|
||||||
minimatch: registry.npmjs.org/minimatch@3.1.2
|
minimatch: registry.npmjs.org/minimatch@3.1.2
|
||||||
@@ -1938,7 +1938,7 @@ packages:
|
|||||||
- terser
|
- terser
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
registry.npmjs.org/@playwright/experimental-ct-react@1.39.0(@types/node@18.7.16)(vite@4.5.1):
|
registry.npmjs.org/@playwright/experimental-ct-react@1.39.0(@types/node@18.7.16)(vite@4.5.0):
|
||||||
resolution: {integrity: sha512-6GaDVVo6x8P9Jdw3yPbVaEP59nesRsETn3kznHAy+2493VsP3IaVATat2G28z0Sivf/K9Tkh5xRYjiawN0ebgw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@playwright/experimental-ct-react/-/experimental-ct-react-1.39.0.tgz}
|
resolution: {integrity: sha512-6GaDVVo6x8P9Jdw3yPbVaEP59nesRsETn3kznHAy+2493VsP3IaVATat2G28z0Sivf/K9Tkh5xRYjiawN0ebgw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@playwright/experimental-ct-react/-/experimental-ct-react-1.39.0.tgz}
|
||||||
id: registry.npmjs.org/@playwright/experimental-ct-react/1.39.0
|
id: registry.npmjs.org/@playwright/experimental-ct-react/1.39.0
|
||||||
name: '@playwright/experimental-ct-react'
|
name: '@playwright/experimental-ct-react'
|
||||||
@@ -1947,7 +1947,7 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dependencies:
|
dependencies:
|
||||||
'@playwright/experimental-ct-core': registry.npmjs.org/@playwright/experimental-ct-core@1.39.0(@types/node@18.7.16)
|
'@playwright/experimental-ct-core': registry.npmjs.org/@playwright/experimental-ct-core@1.39.0(@types/node@18.7.16)
|
||||||
'@vitejs/plugin-react': registry.npmjs.org/@vitejs/plugin-react@4.1.1(vite@4.5.1)
|
'@vitejs/plugin-react': registry.npmjs.org/@vitejs/plugin-react@4.1.1(vite@4.5.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@types/node'
|
- '@types/node'
|
||||||
- less
|
- less
|
||||||
@@ -2134,7 +2134,7 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@sveltejs/kit': ^1.0.0
|
'@sveltejs/kit': ^1.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sveltejs/kit': registry.npmjs.org/@sveltejs/kit@1.22.6(svelte@4.2.1)(vite@4.5.1)
|
'@sveltejs/kit': registry.npmjs.org/@sveltejs/kit@1.22.6(svelte@4.2.1)(vite@4.5.0)
|
||||||
import-meta-resolve: registry.npmjs.org/import-meta-resolve@3.0.0
|
import-meta-resolve: registry.npmjs.org/import-meta-resolve@3.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
@@ -2150,7 +2150,7 @@ packages:
|
|||||||
import-meta-resolve: registry.npmjs.org/import-meta-resolve@4.0.0
|
import-meta-resolve: registry.npmjs.org/import-meta-resolve@4.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
registry.npmjs.org/@sveltejs/kit@1.22.6(svelte@4.2.1)(vite@4.5.1):
|
registry.npmjs.org/@sveltejs/kit@1.22.6(svelte@4.2.1)(vite@4.5.0):
|
||||||
resolution: {integrity: sha512-SDKxI/QpsReCwIn5czjT53fKlPBybbmMk67d317gUqfeORroBAFN1Z6s/x0E1JYi+04i7kKllS+Sz9wVfmUkAQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@sveltejs/kit/-/kit-1.22.6.tgz}
|
resolution: {integrity: sha512-SDKxI/QpsReCwIn5czjT53fKlPBybbmMk67d317gUqfeORroBAFN1Z6s/x0E1JYi+04i7kKllS+Sz9wVfmUkAQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@sveltejs/kit/-/kit-1.22.6.tgz}
|
||||||
id: registry.npmjs.org/@sveltejs/kit/1.22.6
|
id: registry.npmjs.org/@sveltejs/kit/1.22.6
|
||||||
name: '@sveltejs/kit'
|
name: '@sveltejs/kit'
|
||||||
@@ -2162,7 +2162,7 @@ packages:
|
|||||||
svelte: ^3.54.0 || ^4.0.0-next.0
|
svelte: ^3.54.0 || ^4.0.0-next.0
|
||||||
vite: ^4.0.0
|
vite: ^4.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sveltejs/vite-plugin-svelte': registry.npmjs.org/@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.1)
|
'@sveltejs/vite-plugin-svelte': registry.npmjs.org/@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.0)
|
||||||
'@types/cookie': registry.npmjs.org/@types/cookie@0.5.3
|
'@types/cookie': registry.npmjs.org/@types/cookie@0.5.3
|
||||||
cookie: registry.npmjs.org/cookie@0.5.0
|
cookie: registry.npmjs.org/cookie@0.5.0
|
||||||
devalue: registry.npmjs.org/devalue@4.3.2
|
devalue: registry.npmjs.org/devalue@4.3.2
|
||||||
@@ -2175,7 +2175,7 @@ packages:
|
|||||||
sirv: registry.npmjs.org/sirv@2.0.3
|
sirv: registry.npmjs.org/sirv@2.0.3
|
||||||
svelte: registry.npmjs.org/svelte@4.2.1
|
svelte: registry.npmjs.org/svelte@4.2.1
|
||||||
undici: registry.npmjs.org/undici@5.23.0
|
undici: registry.npmjs.org/undici@5.23.0
|
||||||
vite: registry.npmjs.org/vite@4.5.1(@types/node@18.7.16)
|
vite: registry.npmjs.org/vite@4.5.0(@types/node@18.7.16)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
@@ -2231,7 +2231,7 @@ packages:
|
|||||||
- typescript
|
- typescript
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector@1.0.4(@sveltejs/vite-plugin-svelte@2.4.6)(svelte@4.2.1)(vite@4.5.1):
|
registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector@1.0.4(@sveltejs/vite-plugin-svelte@2.4.6)(svelte@4.2.1)(vite@4.5.0):
|
||||||
resolution: {integrity: sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-1.0.4.tgz}
|
resolution: {integrity: sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-1.0.4.tgz}
|
||||||
id: registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/1.0.4
|
id: registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/1.0.4
|
||||||
name: '@sveltejs/vite-plugin-svelte-inspector'
|
name: '@sveltejs/vite-plugin-svelte-inspector'
|
||||||
@@ -2242,10 +2242,10 @@ packages:
|
|||||||
svelte: ^3.54.0 || ^4.0.0
|
svelte: ^3.54.0 || ^4.0.0
|
||||||
vite: ^4.0.0
|
vite: ^4.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sveltejs/vite-plugin-svelte': registry.npmjs.org/@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.1)
|
'@sveltejs/vite-plugin-svelte': registry.npmjs.org/@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.0)
|
||||||
debug: registry.npmjs.org/debug@4.3.4(supports-color@8.1.1)
|
debug: registry.npmjs.org/debug@4.3.4(supports-color@8.1.1)
|
||||||
svelte: registry.npmjs.org/svelte@4.2.1
|
svelte: registry.npmjs.org/svelte@4.2.1
|
||||||
vite: registry.npmjs.org/vite@4.5.1(@types/node@18.7.16)
|
vite: registry.npmjs.org/vite@4.5.0(@types/node@18.7.16)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -2268,7 +2268,7 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
registry.npmjs.org/@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.1):
|
registry.npmjs.org/@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.0):
|
||||||
resolution: {integrity: sha512-zO79p0+DZnXPnF0ltIigWDx/ux7Ni+HRaFOw720Qeivc1azFUrJxTl0OryXVibYNx1hCboGia1NRV3x8RNv4cA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.4.6.tgz}
|
resolution: {integrity: sha512-zO79p0+DZnXPnF0ltIigWDx/ux7Ni+HRaFOw720Qeivc1azFUrJxTl0OryXVibYNx1hCboGia1NRV3x8RNv4cA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.4.6.tgz}
|
||||||
id: registry.npmjs.org/@sveltejs/vite-plugin-svelte/2.4.6
|
id: registry.npmjs.org/@sveltejs/vite-plugin-svelte/2.4.6
|
||||||
name: '@sveltejs/vite-plugin-svelte'
|
name: '@sveltejs/vite-plugin-svelte'
|
||||||
@@ -2278,15 +2278,15 @@ packages:
|
|||||||
svelte: ^3.54.0 || ^4.0.0
|
svelte: ^3.54.0 || ^4.0.0
|
||||||
vite: ^4.0.0
|
vite: ^4.0.0
|
||||||
dependencies:
|
dependencies:
|
||||||
'@sveltejs/vite-plugin-svelte-inspector': registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector@1.0.4(@sveltejs/vite-plugin-svelte@2.4.6)(svelte@4.2.1)(vite@4.5.1)
|
'@sveltejs/vite-plugin-svelte-inspector': registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector@1.0.4(@sveltejs/vite-plugin-svelte@2.4.6)(svelte@4.2.1)(vite@4.5.0)
|
||||||
debug: registry.npmjs.org/debug@4.3.4(supports-color@8.1.1)
|
debug: registry.npmjs.org/debug@4.3.4(supports-color@8.1.1)
|
||||||
deepmerge: registry.npmjs.org/deepmerge@4.3.1
|
deepmerge: registry.npmjs.org/deepmerge@4.3.1
|
||||||
kleur: registry.npmjs.org/kleur@4.1.5
|
kleur: registry.npmjs.org/kleur@4.1.5
|
||||||
magic-string: registry.npmjs.org/magic-string@0.30.5
|
magic-string: registry.npmjs.org/magic-string@0.30.5
|
||||||
svelte: registry.npmjs.org/svelte@4.2.1
|
svelte: registry.npmjs.org/svelte@4.2.1
|
||||||
svelte-hmr: registry.npmjs.org/svelte-hmr@0.15.3(svelte@4.2.1)
|
svelte-hmr: registry.npmjs.org/svelte-hmr@0.15.3(svelte@4.2.1)
|
||||||
vite: registry.npmjs.org/vite@4.5.1(@types/node@18.7.16)
|
vite: registry.npmjs.org/vite@4.5.0(@types/node@18.7.16)
|
||||||
vitefu: registry.npmjs.org/vitefu@0.2.5(vite@4.5.1)
|
vitefu: registry.npmjs.org/vitefu@0.2.5(vite@4.5.0)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
@@ -2872,6 +2872,7 @@ packages:
|
|||||||
resolution: {integrity: sha512-EQHhixfu+mkqHMZl1R2Ovuvn47PUw18azMJOTwSZr9/fhzHNGXAJ0ma0dayRVchprpCj0Kc1K1xKoWaATWF1qg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@types/node/-/node-18.7.16.tgz}
|
resolution: {integrity: sha512-EQHhixfu+mkqHMZl1R2Ovuvn47PUw18azMJOTwSZr9/fhzHNGXAJ0ma0dayRVchprpCj0Kc1K1xKoWaATWF1qg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@types/node/-/node-18.7.16.tgz}
|
||||||
name: '@types/node'
|
name: '@types/node'
|
||||||
version: 18.7.16
|
version: 18.7.16
|
||||||
|
requiresBuild: true
|
||||||
|
|
||||||
registry.npmjs.org/@types/normalize-package-data@2.4.3:
|
registry.npmjs.org/@types/normalize-package-data@2.4.3:
|
||||||
resolution: {integrity: sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.3.tgz}
|
resolution: {integrity: sha512-ehPtgRgaULsFG8x0NeYJvmyH1hmlfsNLujHe9dQEia/7MAJYdzMSi19JtchUHjmBA6XC/75dK55mzZH+RyieSg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.3.tgz}
|
||||||
@@ -2966,12 +2967,6 @@ packages:
|
|||||||
version: 6.2.5
|
version: 6.2.5
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
registry.npmjs.org/@types/semver@7.5.4:
|
|
||||||
resolution: {integrity: sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz}
|
|
||||||
name: '@types/semver'
|
|
||||||
version: 7.5.4
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
registry.npmjs.org/@types/semver@7.5.6:
|
registry.npmjs.org/@types/semver@7.5.6:
|
||||||
resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz}
|
resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz}
|
||||||
name: '@types/semver'
|
name: '@types/semver'
|
||||||
@@ -3381,7 +3376,7 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@eslint-community/eslint-utils': registry.npmjs.org/@eslint-community/eslint-utils@4.4.0(eslint@8.43.0)
|
'@eslint-community/eslint-utils': registry.npmjs.org/@eslint-community/eslint-utils@4.4.0(eslint@8.43.0)
|
||||||
'@types/json-schema': registry.npmjs.org/@types/json-schema@7.0.15
|
'@types/json-schema': registry.npmjs.org/@types/json-schema@7.0.15
|
||||||
'@types/semver': registry.npmjs.org/@types/semver@7.5.4
|
'@types/semver': registry.npmjs.org/@types/semver@7.5.6
|
||||||
'@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager@5.60.0
|
'@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager@5.60.0
|
||||||
'@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types@5.60.0
|
'@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types@5.60.0
|
||||||
'@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree@5.60.0(typescript@5.1.3)
|
'@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree@5.60.0(typescript@5.1.3)
|
||||||
@@ -3504,26 +3499,6 @@ packages:
|
|||||||
vite: registry.npmjs.org/vite@4.5.0(@types/node@18.7.16)
|
vite: registry.npmjs.org/vite@4.5.0(@types/node@18.7.16)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
|
||||||
|
|
||||||
registry.npmjs.org/@vitejs/plugin-react@4.1.1(vite@4.5.1):
|
|
||||||
resolution: {integrity: sha512-Jie2HERK+uh27e+ORXXwEP5h0Y2lS9T2PRGbfebiHGlwzDO0dEnd2aNtOR/qjBlPb1YgxwAONeblL1xqLikLag==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.1.1.tgz}
|
|
||||||
id: registry.npmjs.org/@vitejs/plugin-react/4.1.1
|
|
||||||
name: '@vitejs/plugin-react'
|
|
||||||
version: 4.1.1
|
|
||||||
engines: {node: ^14.18.0 || >=16.0.0}
|
|
||||||
peerDependencies:
|
|
||||||
vite: ^4.2.0
|
|
||||||
dependencies:
|
|
||||||
'@babel/core': registry.npmjs.org/@babel/core@7.23.2
|
|
||||||
'@babel/plugin-transform-react-jsx-self': registry.npmjs.org/@babel/plugin-transform-react-jsx-self@7.22.5(@babel/core@7.23.2)
|
|
||||||
'@babel/plugin-transform-react-jsx-source': registry.npmjs.org/@babel/plugin-transform-react-jsx-source@7.22.5(@babel/core@7.23.2)
|
|
||||||
'@types/babel__core': registry.npmjs.org/@types/babel__core@7.20.4
|
|
||||||
react-refresh: registry.npmjs.org/react-refresh@0.14.0
|
|
||||||
vite: registry.npmjs.org/vite@4.5.1(@types/node@18.7.16)
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
registry.npmjs.org/acorn-jsx@5.3.2(acorn@8.10.0):
|
registry.npmjs.org/acorn-jsx@5.3.2(acorn@8.10.0):
|
||||||
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz}
|
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz}
|
||||||
@@ -9520,16 +9495,6 @@ packages:
|
|||||||
picocolors: registry.npmjs.org/picocolors@1.0.0
|
picocolors: registry.npmjs.org/picocolors@1.0.0
|
||||||
source-map-js: registry.npmjs.org/source-map-js@1.0.2
|
source-map-js: registry.npmjs.org/source-map-js@1.0.2
|
||||||
|
|
||||||
registry.npmjs.org/postcss@8.4.32:
|
|
||||||
resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz}
|
|
||||||
name: postcss
|
|
||||||
version: 8.4.32
|
|
||||||
engines: {node: ^10 || ^12 || >=14}
|
|
||||||
dependencies:
|
|
||||||
nanoid: registry.npmjs.org/nanoid@3.3.7
|
|
||||||
picocolors: registry.npmjs.org/picocolors@1.0.0
|
|
||||||
source-map-js: registry.npmjs.org/source-map-js@1.0.2
|
|
||||||
|
|
||||||
registry.npmjs.org/prebuild-install@7.1.1:
|
registry.npmjs.org/prebuild-install@7.1.1:
|
||||||
resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz}
|
resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz}
|
||||||
name: prebuild-install
|
name: prebuild-install
|
||||||
@@ -11249,7 +11214,7 @@ packages:
|
|||||||
typescript: registry.npmjs.org/typescript@5.1.3
|
typescript: registry.npmjs.org/typescript@5.1.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
registry.npmjs.org/svelte2tsx@0.6.23(svelte@4.2.1)(typescript@5.3.3):
|
registry.npmjs.org/svelte2tsx@0.6.23(svelte@4.2.1)(typescript@5.2.2):
|
||||||
resolution: {integrity: sha512-3bwd1PuWUA3oEXy8+85zrLDnmJOsVpShpKVAehGWeYsz/66zMihTpRpUN97VVAKTZbO5tP4wnchHUXYs0zOwdw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.6.23.tgz}
|
resolution: {integrity: sha512-3bwd1PuWUA3oEXy8+85zrLDnmJOsVpShpKVAehGWeYsz/66zMihTpRpUN97VVAKTZbO5tP4wnchHUXYs0zOwdw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.6.23.tgz}
|
||||||
id: registry.npmjs.org/svelte2tsx/0.6.23
|
id: registry.npmjs.org/svelte2tsx/0.6.23
|
||||||
name: svelte2tsx
|
name: svelte2tsx
|
||||||
@@ -11261,7 +11226,7 @@ packages:
|
|||||||
dedent-js: registry.npmjs.org/dedent-js@1.0.1
|
dedent-js: registry.npmjs.org/dedent-js@1.0.1
|
||||||
pascal-case: registry.npmjs.org/pascal-case@3.1.2
|
pascal-case: registry.npmjs.org/pascal-case@3.1.2
|
||||||
svelte: registry.npmjs.org/svelte@4.2.1
|
svelte: registry.npmjs.org/svelte@4.2.1
|
||||||
typescript: registry.npmjs.org/typescript@5.3.3
|
typescript: registry.npmjs.org/typescript@5.2.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
registry.npmjs.org/svelte@4.2.1:
|
registry.npmjs.org/svelte@4.2.1:
|
||||||
@@ -11798,14 +11763,6 @@ packages:
|
|||||||
engines: {node: '>=14.17'}
|
engines: {node: '>=14.17'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
registry.npmjs.org/typescript@5.3.3:
|
|
||||||
resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz}
|
|
||||||
name: typescript
|
|
||||||
version: 5.3.3
|
|
||||||
engines: {node: '>=14.17'}
|
|
||||||
hasBin: true
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
registry.npmjs.org/ultrahtml@1.5.2:
|
registry.npmjs.org/ultrahtml@1.5.2:
|
||||||
resolution: {integrity: sha512-qh4mBffhlkiXwDAOxvSGxhL0QEQsTbnP9BozOK3OYPEGvPvdWzvAUaXNtUSMdNsKDtuyjEbyVUPFZ52SSLhLqw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.5.2.tgz}
|
resolution: {integrity: sha512-qh4mBffhlkiXwDAOxvSGxhL0QEQsTbnP9BozOK3OYPEGvPvdWzvAUaXNtUSMdNsKDtuyjEbyVUPFZ52SSLhLqw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.5.2.tgz}
|
||||||
name: ultrahtml
|
name: ultrahtml
|
||||||
@@ -12141,44 +12098,6 @@ packages:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents: registry.npmjs.org/fsevents@2.3.3
|
fsevents: registry.npmjs.org/fsevents@2.3.3
|
||||||
|
|
||||||
registry.npmjs.org/vite@4.5.1(@types/node@18.7.16):
|
|
||||||
resolution: {integrity: sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/vite/-/vite-4.5.1.tgz}
|
|
||||||
id: registry.npmjs.org/vite/4.5.1
|
|
||||||
name: vite
|
|
||||||
version: 4.5.1
|
|
||||||
engines: {node: ^14.18.0 || >=16.0.0}
|
|
||||||
hasBin: true
|
|
||||||
peerDependencies:
|
|
||||||
'@types/node': '>= 14'
|
|
||||||
less: '*'
|
|
||||||
lightningcss: ^1.21.0
|
|
||||||
sass: '*'
|
|
||||||
stylus: '*'
|
|
||||||
sugarss: '*'
|
|
||||||
terser: ^5.4.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/node':
|
|
||||||
optional: true
|
|
||||||
less:
|
|
||||||
optional: true
|
|
||||||
lightningcss:
|
|
||||||
optional: true
|
|
||||||
sass:
|
|
||||||
optional: true
|
|
||||||
stylus:
|
|
||||||
optional: true
|
|
||||||
sugarss:
|
|
||||||
optional: true
|
|
||||||
terser:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@types/node': registry.npmjs.org/@types/node@18.7.16
|
|
||||||
esbuild: registry.npmjs.org/esbuild@0.18.20
|
|
||||||
postcss: registry.npmjs.org/postcss@8.4.32
|
|
||||||
rollup: registry.npmjs.org/rollup@3.29.4
|
|
||||||
optionalDependencies:
|
|
||||||
fsevents: registry.npmjs.org/fsevents@2.3.3
|
|
||||||
|
|
||||||
registry.npmjs.org/vitefu@0.2.5(vite@4.5.0):
|
registry.npmjs.org/vitefu@0.2.5(vite@4.5.0):
|
||||||
resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz}
|
resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz}
|
||||||
id: registry.npmjs.org/vitefu/0.2.5
|
id: registry.npmjs.org/vitefu/0.2.5
|
||||||
@@ -12192,19 +12111,6 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
vite: registry.npmjs.org/vite@4.5.0(@types/node@18.7.16)
|
vite: registry.npmjs.org/vite@4.5.0(@types/node@18.7.16)
|
||||||
|
|
||||||
registry.npmjs.org/vitefu@0.2.5(vite@4.5.1):
|
|
||||||
resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz}
|
|
||||||
id: registry.npmjs.org/vitefu/0.2.5
|
|
||||||
name: vitefu
|
|
||||||
version: 0.2.5
|
|
||||||
peerDependencies:
|
|
||||||
vite: ^3.0.0 || ^4.0.0 || ^5.0.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
vite:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
vite: registry.npmjs.org/vite@4.5.1(@types/node@18.7.16)
|
|
||||||
|
|
||||||
registry.npmjs.org/vscode-oniguruma@1.7.0:
|
registry.npmjs.org/vscode-oniguruma@1.7.0:
|
||||||
resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz}
|
resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz}
|
||||||
name: vscode-oniguruma
|
name: vscode-oniguruma
|
||||||
|
|||||||
Reference in New Issue
Block a user