merg main
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/system': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Added OnReconnect types
|
||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
useReactFlow,
|
useReactFlow,
|
||||||
Panel,
|
Panel,
|
||||||
OnNodeDrag,
|
OnNodeDrag,
|
||||||
|
FitViewOptions,
|
||||||
} from '@xyflow/react';
|
} from '@xyflow/react';
|
||||||
|
|
||||||
const onNodeDrag: OnNodeDrag = (_, node: Node, nodes: Node[]) => console.log('drag', node, nodes);
|
const onNodeDrag: OnNodeDrag = (_, node: Node, nodes: Node[]) => console.log('drag', node, nodes);
|
||||||
@@ -54,6 +55,9 @@ const initialEdges: Edge[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
const defaultEdgeOptions = {};
|
const defaultEdgeOptions = {};
|
||||||
|
const fitViewOptions: FitViewOptions = {
|
||||||
|
padding: { top: '100px', left: '0%', right: '10%', bottom: 0.1 },
|
||||||
|
};
|
||||||
|
|
||||||
const BasicFlow = () => {
|
const BasicFlow = () => {
|
||||||
const {
|
const {
|
||||||
@@ -135,6 +139,7 @@ const BasicFlow = () => {
|
|||||||
<ReactFlow
|
<ReactFlow
|
||||||
defaultNodes={initialNodes}
|
defaultNodes={initialNodes}
|
||||||
defaultEdges={initialEdges}
|
defaultEdges={initialEdges}
|
||||||
|
onNodesChange={console.log}
|
||||||
onNodeClick={onNodeClick}
|
onNodeClick={onNodeClick}
|
||||||
onNodeDragStop={onNodeDragStop}
|
onNodeDragStop={onNodeDragStop}
|
||||||
onNodeDragStart={onNodeDragStart}
|
onNodeDragStart={onNodeDragStart}
|
||||||
@@ -146,9 +151,7 @@ const BasicFlow = () => {
|
|||||||
minZoom={0.2}
|
minZoom={0.2}
|
||||||
maxZoom={4}
|
maxZoom={4}
|
||||||
fitView
|
fitView
|
||||||
fitViewOptions={{
|
fitViewOptions={fitViewOptions}
|
||||||
padding: { top: '100px', left: '0%', right: '10%', bottom: 0.1 },
|
|
||||||
}}
|
|
||||||
defaultEdgeOptions={defaultEdgeOptions}
|
defaultEdgeOptions={defaultEdgeOptions}
|
||||||
selectNodesOnDrag={false}
|
selectNodesOnDrag={false}
|
||||||
elevateEdgesOnSelect
|
elevateEdgesOnSelect
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { memo, FC } from 'react';
|
||||||
|
import { Handle, Position, NodeProps, NodeResizeControl, ResizeControlVariant } from '@xyflow/react';
|
||||||
|
|
||||||
|
const CustomResizerNode: FC<NodeProps> = ({ data }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<NodeResizeControl
|
||||||
|
variant={ResizeControlVariant.Handle}
|
||||||
|
position="bottom-right"
|
||||||
|
resizeDirection="horizontal"
|
||||||
|
minWidth={100}
|
||||||
|
maxWidth={500}
|
||||||
|
/>
|
||||||
|
<Handle type="target" position={Position.Left} />
|
||||||
|
<div>{data.label}</div>
|
||||||
|
<Handle type="source" position={Position.Right} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(CustomResizerNode);
|
||||||
@@ -15,12 +15,14 @@ import DefaultResizer from './DefaultResizer';
|
|||||||
import CustomResizer from './CustomResizer';
|
import CustomResizer from './CustomResizer';
|
||||||
import VerticalResizer from './VerticalResizer';
|
import VerticalResizer from './VerticalResizer';
|
||||||
import HorizontalResizer from './HorizontalResizer';
|
import HorizontalResizer from './HorizontalResizer';
|
||||||
|
import BottomRightResizer from './BottomRightResizer';
|
||||||
|
|
||||||
const nodeTypes = {
|
const nodeTypes = {
|
||||||
defaultResizer: DefaultResizer,
|
defaultResizer: DefaultResizer,
|
||||||
customResizer: CustomResizer,
|
customResizer: CustomResizer,
|
||||||
verticalResizer: VerticalResizer,
|
verticalResizer: VerticalResizer,
|
||||||
horizontalResizer: HorizontalResizer,
|
horizontalResizer: HorizontalResizer,
|
||||||
|
bottomRightResizer: BottomRightResizer,
|
||||||
};
|
};
|
||||||
|
|
||||||
const nodeStyle = {
|
const nodeStyle = {
|
||||||
@@ -166,6 +168,13 @@ const initialNodes: Node[] = [
|
|||||||
expandParent: true,
|
expandParent: true,
|
||||||
style: { ...nodeStyle },
|
style: { ...nodeStyle },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
type: 'bottomRightResizer',
|
||||||
|
data: { label: 'Bottom Right with horizontal direction' },
|
||||||
|
position: { x: 500, y: 500 },
|
||||||
|
style: { ...nodeStyle },
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const CustomNodeFlow = () => {
|
const CustomNodeFlow = () => {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
useEdgesState,
|
useEdgesState,
|
||||||
useOnSelectionChange,
|
useOnSelectionChange,
|
||||||
OnSelectionChangeParams,
|
OnSelectionChangeParams,
|
||||||
|
Panel,
|
||||||
} from '@xyflow/react';
|
} from '@xyflow/react';
|
||||||
|
|
||||||
const initialNodes: Node[] = [
|
const initialNodes: Node[] = [
|
||||||
@@ -51,6 +52,12 @@ const Flow = () => {
|
|||||||
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||||
const onConnect = useCallback((params: Edge | Connection) => setEdges((els) => addEdge(params, els)), [setEdges]);
|
const onConnect = useCallback((params: Edge | Connection) => setEdges((els) => addEdge(params, els)), [setEdges]);
|
||||||
|
const [elementsSelectable, setElementsSelectable] = useState<boolean>(true);
|
||||||
|
const [secondLoggerActive, setSecondLoggerActive] = useState<boolean>(true);
|
||||||
|
|
||||||
|
const toggleSecondLogger = () => {
|
||||||
|
setSecondLoggerActive(!secondLoggerActive);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlow
|
<ReactFlow
|
||||||
@@ -59,27 +66,17 @@ const Flow = () => {
|
|||||||
onNodesChange={onNodesChange}
|
onNodesChange={onNodesChange}
|
||||||
onEdgesChange={onEdgesChange}
|
onEdgesChange={onEdgesChange}
|
||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
/>
|
elementsSelectable={elementsSelectable}
|
||||||
);
|
>
|
||||||
};
|
<Panel>
|
||||||
|
<button onClick={toggleSecondLogger}>{secondLoggerActive ? 'Disable' : 'Enable'} Logger 2</button>
|
||||||
|
<button onClick={() => setElementsSelectable((s) => !s)}>toggle selectable</button>
|
||||||
|
</Panel>
|
||||||
|
|
||||||
const WrappedFlow = () => {
|
|
||||||
const [secondLoggerActive, setSecondLoggerActive] = useState<boolean>(true);
|
|
||||||
|
|
||||||
const toggleSecondLogger = () => {
|
|
||||||
setSecondLoggerActive(!secondLoggerActive);
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ReactFlowProvider>
|
|
||||||
<Flow />
|
|
||||||
<SelectionLogger id="Logger 1" />
|
<SelectionLogger id="Logger 1" />
|
||||||
{secondLoggerActive && <SelectionLogger id="Logger 2" />}
|
{secondLoggerActive && <SelectionLogger id="Logger 2" />}
|
||||||
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
|
</ReactFlow>
|
||||||
<button onClick={toggleSecondLogger}>{secondLoggerActive ? 'Disable' : 'Enable'} Logger 2</button>
|
|
||||||
</div>
|
|
||||||
</ReactFlowProvider>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default WrappedFlow;
|
export default Flow;
|
||||||
|
|||||||
@@ -1,5 +1,48 @@
|
|||||||
# @xyflow/react
|
# @xyflow/react
|
||||||
|
|
||||||
|
## 12.6.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- [#5249](https://github.com/xyflow/xyflow/pull/5249) [`895b5d81`](https://github.com/xyflow/xyflow/commit/895b5d81c8ee5236009820ecd0ed6806c6e59e29) Thanks [@moklick](https://github.com/moklick)! - Call `onNodesChange` for uncontrolled flows that use `updateNode`
|
||||||
|
|
||||||
|
- [#5247](https://github.com/xyflow/xyflow/pull/5247) [`67e1cb68`](https://github.com/xyflow/xyflow/commit/67e1cb6891078dbcb9e1d06b9f9fdbfc79860ab6) Thanks [@moklick](https://github.com/moklick)! - Cleanup TSDoc annotations for ReactFlow
|
||||||
|
|
||||||
|
- Updated dependencies [[`2a03213b`](https://github.com/xyflow/xyflow/commit/2a03213b0695d504f831579ec9df3f9de2d3e0bd)]:
|
||||||
|
- @xyflow/system@0.0.58
|
||||||
|
|
||||||
|
## 12.6.0
|
||||||
|
|
||||||
|
### Minor Changes
|
||||||
|
|
||||||
|
- [#5219](https://github.com/xyflow/xyflow/pull/5219) [`4236adbc`](https://github.com/xyflow/xyflow/commit/4236adbc462b3f65ee41869ef426491ed3fa8ba0) Thanks [@moklick](https://github.com/moklick)! - Add initialMinZoom, initialMaxZoom and initialFitViewOptions to ReactFlowProvider
|
||||||
|
|
||||||
|
- [#5227](https://github.com/xyflow/xyflow/pull/5227) [`a7d10ffc`](https://github.com/xyflow/xyflow/commit/a7d10ffce5a0195471681980f97b1b5f6c448f35) Thanks [@moklick](https://github.com/moklick)! - Add `resizeDirection` prop for the `NodeResizeControl` component
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- [#5217](https://github.com/xyflow/xyflow/pull/5217) [`bce74e88`](https://github.com/xyflow/xyflow/commit/bce74e8811a98c967b5bd06c3e5aecde24c8b679) Thanks [@moklick](https://github.com/moklick)! - Keep node seleciton on pane click if elementsSelectable=false
|
||||||
|
|
||||||
|
- Updated dependencies [[`a7d10ffc`](https://github.com/xyflow/xyflow/commit/a7d10ffce5a0195471681980f97b1b5f6c448f35), [`4e681f9c`](https://github.com/xyflow/xyflow/commit/4e681f9c529c3f4f8b2ac5d25b4db7878c197e14)]:
|
||||||
|
- @xyflow/system@0.0.57
|
||||||
|
|
||||||
|
## 12.5.6
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- [#5212](https://github.com/xyflow/xyflow/pull/5212) [`0f43b8ea`](https://github.com/xyflow/xyflow/commit/0f43b8ea45bd293e50e4a86d83868074bb323f13) Thanks [@moklick](https://github.com/moklick)! - Add polyfill for `Promise.withResolvers`
|
||||||
|
|
||||||
|
- [#5192](https://github.com/xyflow/xyflow/pull/5192) [`fc241253`](https://github.com/xyflow/xyflow/commit/fc241253d5dba35f5febf411e77dbc5acb91d5d7) Thanks [@peterkogo](https://github.com/peterkogo)! - Optimize performance of nodesInitialized
|
||||||
|
|
||||||
|
- [#5196](https://github.com/xyflow/xyflow/pull/5196) [`7f902db4`](https://github.com/xyflow/xyflow/commit/7f902db46b6f1ea4adc94390db8d5db47f8c5903) Thanks [@moklick](https://github.com/moklick)! - Hide edge marker and attribution for screenreaders
|
||||||
|
|
||||||
|
- [#5213](https://github.com/xyflow/xyflow/pull/5213) [`78782c16`](https://github.com/xyflow/xyflow/commit/78782c1696323ee9be0d38bbd807bd3fde5f549d) Thanks [@moklick](https://github.com/moklick)! - Do not trigger selection events if elementsSelectable=false
|
||||||
|
|
||||||
|
- [#5191](https://github.com/xyflow/xyflow/pull/5191) [`e5735b51`](https://github.com/xyflow/xyflow/commit/e5735b514a54d86ba0ca7bd725e8bfead89fc08e) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix legacy padding being slightly larger than before
|
||||||
|
|
||||||
|
- Updated dependencies [[`0f43b8ea`](https://github.com/xyflow/xyflow/commit/0f43b8ea45bd293e50e4a86d83868074bb323f13), [`fc241253`](https://github.com/xyflow/xyflow/commit/fc241253d5dba35f5febf411e77dbc5acb91d5d7), [`98fe23c7`](https://github.com/xyflow/xyflow/commit/98fe23c7c2b12972f1b7def866215ce82a86e2c0), [`e5735b51`](https://github.com/xyflow/xyflow/commit/e5735b514a54d86ba0ca7bd725e8bfead89fc08e)]:
|
||||||
|
- @xyflow/system@0.0.56
|
||||||
|
|
||||||
## 12.5.5
|
## 12.5.5
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@xyflow/react",
|
"name": "@xyflow/react",
|
||||||
"version": "12.5.5",
|
"version": "12.6.1",
|
||||||
"description": "React Flow - A highly customizable React library for building node-based editors and interactive flow charts.",
|
"description": "React Flow - A highly customizable React library for building node-based editors and interactive flow charts.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"react",
|
"react",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ function ResizeControl({
|
|||||||
maxWidth = Number.MAX_VALUE,
|
maxWidth = Number.MAX_VALUE,
|
||||||
maxHeight = Number.MAX_VALUE,
|
maxHeight = Number.MAX_VALUE,
|
||||||
keepAspectRatio = false,
|
keepAspectRatio = false,
|
||||||
|
resizeDirection,
|
||||||
shouldResize,
|
shouldResize,
|
||||||
onResizeStart,
|
onResizeStart,
|
||||||
onResize,
|
onResize,
|
||||||
@@ -117,11 +118,12 @@ function ResizeControl({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (change.width !== undefined && change.height !== undefined) {
|
if (change.width !== undefined && change.height !== undefined) {
|
||||||
|
const setAttributes = !resizeDirection ? true : resizeDirection === 'horizontal' ? 'width' : 'height';
|
||||||
const dimensionChange: NodeDimensionChange = {
|
const dimensionChange: NodeDimensionChange = {
|
||||||
id,
|
id,
|
||||||
type: 'dimensions',
|
type: 'dimensions',
|
||||||
resizing: true,
|
resizing: true,
|
||||||
setAttributes: true,
|
setAttributes,
|
||||||
dimensions: {
|
dimensions: {
|
||||||
width: change.width,
|
width: change.width,
|
||||||
height: change.height,
|
height: change.height,
|
||||||
@@ -166,6 +168,7 @@ function ResizeControl({
|
|||||||
maxHeight,
|
maxHeight,
|
||||||
},
|
},
|
||||||
keepAspectRatio,
|
keepAspectRatio,
|
||||||
|
resizeDirection,
|
||||||
onResizeStart,
|
onResizeStart,
|
||||||
onResize,
|
onResize,
|
||||||
onResizeEnd,
|
onResizeEnd,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type {
|
|||||||
ControlPosition,
|
ControlPosition,
|
||||||
ControlLinePosition,
|
ControlLinePosition,
|
||||||
ResizeControlVariant,
|
ResizeControlVariant,
|
||||||
|
ResizeControlDirection,
|
||||||
ShouldResize,
|
ShouldResize,
|
||||||
OnResizeStart,
|
OnResizeStart,
|
||||||
OnResize,
|
OnResize,
|
||||||
@@ -97,6 +98,11 @@ export type ResizeControlProps = Pick<
|
|||||||
* @example ResizeControlVariant.Handle, ResizeControlVariant.Line
|
* @example ResizeControlVariant.Handle, ResizeControlVariant.Line
|
||||||
*/
|
*/
|
||||||
variant?: ResizeControlVariant;
|
variant?: ResizeControlVariant;
|
||||||
|
/**
|
||||||
|
* The direction the user can resize the node.
|
||||||
|
* If not provided, the user can resize in any direction.
|
||||||
|
*/
|
||||||
|
resizeDirection?: ResizeControlDirection;
|
||||||
className?: string;
|
className?: string;
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
@@ -105,6 +111,6 @@ export type ResizeControlProps = Pick<
|
|||||||
/**
|
/**
|
||||||
* @expand
|
* @expand
|
||||||
*/
|
*/
|
||||||
export type ResizeControlLineProps = ResizeControlProps & {
|
export type ResizeControlLineProps = Omit<ResizeControlProps, 'resizeDirection'> & {
|
||||||
position?: ControlLinePosition;
|
position?: ControlLinePosition;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,28 +40,27 @@ export function BatchProvider<NodeType extends Node = Node, EdgeType extends Edg
|
|||||||
next = typeof payload === 'function' ? payload(next) : payload;
|
next = typeof payload === 'function' ? payload(next) : payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const changes = getElementsDiffChanges({
|
||||||
|
items: next,
|
||||||
|
lookup: nodeLookup,
|
||||||
|
}) as NodeChange<NodeType>[];
|
||||||
|
|
||||||
if (hasDefaultNodes) {
|
if (hasDefaultNodes) {
|
||||||
setNodes(next);
|
setNodes(next);
|
||||||
} else {
|
}
|
||||||
// When a controlled flow is used we need to collect the changes
|
|
||||||
const changes = getElementsDiffChanges({
|
|
||||||
items: next,
|
|
||||||
lookup: nodeLookup,
|
|
||||||
}) as NodeChange<NodeType>[];
|
|
||||||
|
|
||||||
// We only want to fire onNodesChange if there are changes to the nodes
|
// We only want to fire onNodesChange if there are changes to the nodes
|
||||||
if (changes.length > 0) {
|
if (changes.length > 0) {
|
||||||
onNodesChange?.(changes);
|
onNodesChange?.(changes);
|
||||||
} else if (fitViewQueued) {
|
} else if (fitViewQueued) {
|
||||||
// If there are no changes to the nodes, we still need to call setNodes
|
// If there are no changes to the nodes, we still need to call setNodes
|
||||||
// to trigger a re-render and fitView.
|
// to trigger a re-render and fitView.
|
||||||
window.requestAnimationFrame(() => {
|
window.requestAnimationFrame(() => {
|
||||||
const { fitViewQueued, nodes, setNodes } = store.getState();
|
const { fitViewQueued, nodes, setNodes } = store.getState();
|
||||||
if (fitViewQueued) {
|
if (fitViewQueued) {
|
||||||
setNodes(nodes);
|
setNodes(nodes);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|||||||
@@ -3,18 +3,49 @@ import { useState, type ReactNode } from 'react';
|
|||||||
import { Provider } from '../../contexts/StoreContext';
|
import { Provider } from '../../contexts/StoreContext';
|
||||||
import { createStore } from '../../store';
|
import { createStore } from '../../store';
|
||||||
import { BatchProvider } from '../BatchProvider';
|
import { BatchProvider } from '../BatchProvider';
|
||||||
import type { Node, Edge } from '../../types';
|
import type { Node, Edge, FitViewOptions } from '../../types';
|
||||||
import { CoordinateExtent, NodeOrigin } from '@xyflow/system';
|
import { CoordinateExtent, NodeOrigin } from '@xyflow/system';
|
||||||
|
|
||||||
export type ReactFlowProviderProps = {
|
export type ReactFlowProviderProps = {
|
||||||
|
/** These nodes are used to initialize the flow. They are not dynamic. */
|
||||||
initialNodes?: Node[];
|
initialNodes?: Node[];
|
||||||
|
/** These edges are used to initialize the flow. They are not dynamic. */
|
||||||
initialEdges?: Edge[];
|
initialEdges?: Edge[];
|
||||||
|
/** These nodes are used to initialize the flow. They are not dynamic. */
|
||||||
defaultNodes?: Node[];
|
defaultNodes?: Node[];
|
||||||
|
/** These edges are used to initialize the flow. They are not dynamic. */
|
||||||
defaultEdges?: Edge[];
|
defaultEdges?: Edge[];
|
||||||
|
/** The initial width is necessary to be able to use fitView on the server */
|
||||||
initialWidth?: number;
|
initialWidth?: number;
|
||||||
|
/** The initial height is necessary to be able to use fitView on the server */
|
||||||
initialHeight?: number;
|
initialHeight?: number;
|
||||||
|
/** When `true`, the flow will be zoomed and panned to fit all the nodes initially provided. */
|
||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
|
/**
|
||||||
|
* You can provide an object of options to customize the initial fitView behavior.
|
||||||
|
*/
|
||||||
|
initialFitViewOptions?: FitViewOptions;
|
||||||
|
/** Initial minimum zoom level */
|
||||||
|
initialMinZoom?: number;
|
||||||
|
/** Initial maximum zoom level */
|
||||||
|
initialMaxZoom?: number;
|
||||||
|
/**
|
||||||
|
* The origin of the node to use when placing it in the flow or looking up its `x` and `y`
|
||||||
|
* position. An origin of `[0, 0]` means that a node's top left corner will be placed at the `x`
|
||||||
|
* and `y` position.
|
||||||
|
* @default [0, 0]
|
||||||
|
* @example
|
||||||
|
* [0, 0] // default, top left
|
||||||
|
* [0.5, 0.5] // center
|
||||||
|
* [1, 1] // bottom right
|
||||||
|
*/
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
|
/**
|
||||||
|
* By default, nodes can be placed on an infinite flow. You can use this prop to set a boundary.
|
||||||
|
*
|
||||||
|
* The first pair of coordinates is the top left boundary and the second pair is the bottom right.
|
||||||
|
* @example [[-1000, -10000], [1000, 1000]]
|
||||||
|
*/
|
||||||
nodeExtent?: CoordinateExtent;
|
nodeExtent?: CoordinateExtent;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
};
|
};
|
||||||
@@ -60,6 +91,9 @@ export function ReactFlowProvider({
|
|||||||
defaultEdges,
|
defaultEdges,
|
||||||
initialWidth: width,
|
initialWidth: width,
|
||||||
initialHeight: height,
|
initialHeight: height,
|
||||||
|
initialMinZoom: minZoom,
|
||||||
|
initialMaxZoom: maxZoom,
|
||||||
|
initialFitViewOptions: fitViewOptions,
|
||||||
fitView,
|
fitView,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
nodeExtent,
|
nodeExtent,
|
||||||
@@ -74,6 +108,9 @@ export function ReactFlowProvider({
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fitView,
|
fitView,
|
||||||
|
minZoom,
|
||||||
|
maxZoom,
|
||||||
|
fitViewOptions,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
nodeExtent,
|
nodeExtent,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ const MarkerDefinitions = ({ defaultColor, rfId }: MarkerDefinitionsProps) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg className="react-flow__marker">
|
<svg className="react-flow__marker" aria-hidden="true">
|
||||||
<defs>
|
<defs>
|
||||||
{markers.map((marker: MarkerProps) => (
|
{markers.map((marker: MarkerProps) => (
|
||||||
<Marker
|
<Marker
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useContext, type ReactNode } from 'react';
|
|||||||
|
|
||||||
import StoreContext from '../../contexts/StoreContext';
|
import StoreContext from '../../contexts/StoreContext';
|
||||||
import { ReactFlowProvider } from '../../components/ReactFlowProvider';
|
import { ReactFlowProvider } from '../../components/ReactFlowProvider';
|
||||||
import type { Node, Edge } from '../../types';
|
import type { Node, Edge, FitViewOptions } from '../../types';
|
||||||
import { CoordinateExtent, NodeOrigin } from '@xyflow/system';
|
import { CoordinateExtent, NodeOrigin } from '@xyflow/system';
|
||||||
|
|
||||||
export function Wrapper({
|
export function Wrapper({
|
||||||
@@ -14,6 +14,9 @@ export function Wrapper({
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fitView,
|
fitView,
|
||||||
|
fitViewOptions,
|
||||||
|
minZoom,
|
||||||
|
maxZoom,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
nodeExtent,
|
nodeExtent,
|
||||||
}: {
|
}: {
|
||||||
@@ -25,6 +28,9 @@ export function Wrapper({
|
|||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
|
fitViewOptions?: FitViewOptions;
|
||||||
|
minZoom?: number;
|
||||||
|
maxZoom?: number;
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
nodeExtent?: CoordinateExtent;
|
nodeExtent?: CoordinateExtent;
|
||||||
}) {
|
}) {
|
||||||
@@ -47,6 +53,9 @@ export function Wrapper({
|
|||||||
initialWidth={width}
|
initialWidth={width}
|
||||||
initialHeight={height}
|
initialHeight={height}
|
||||||
fitView={fitView}
|
fitView={fitView}
|
||||||
|
initialFitViewOptions={fitViewOptions}
|
||||||
|
initialMinZoom={minZoom}
|
||||||
|
initialMaxZoom={maxZoom}
|
||||||
nodeOrigin={nodeOrigin}
|
nodeOrigin={nodeOrigin}
|
||||||
nodeExtent={nodeExtent}
|
nodeExtent={nodeExtent}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -177,6 +177,9 @@ function ReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
|
|||||||
width={width}
|
width={width}
|
||||||
height={height}
|
height={height}
|
||||||
fitView={fitView}
|
fitView={fitView}
|
||||||
|
fitViewOptions={fitViewOptions}
|
||||||
|
minZoom={minZoom}
|
||||||
|
maxZoom={maxZoom}
|
||||||
nodeOrigin={nodeOrigin}
|
nodeOrigin={nodeOrigin}
|
||||||
nodeExtent={nodeExtent}
|
nodeExtent={nodeExtent}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -9,15 +9,17 @@ export type UseNodesInitializedOptions = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const selector = (options: UseNodesInitializedOptions) => (s: ReactFlowState) => {
|
const selector = (options: UseNodesInitializedOptions) => (s: ReactFlowState) => {
|
||||||
|
if (!options.includeHiddenNodes) {
|
||||||
|
return s.nodesInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
if (s.nodeLookup.size === 0) {
|
if (s.nodeLookup.size === 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [, { hidden, internals }] of s.nodeLookup) {
|
for (const [, { internals }] of s.nodeLookup) {
|
||||||
if (options.includeHiddenNodes || !hidden) {
|
if (internals.handleBounds === undefined || !nodeHasDimensions(internals.userNode)) {
|
||||||
if (internals.handleBounds === undefined || !nodeHasDimensions(internals.userNode)) {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
isRectObject,
|
isRectObject,
|
||||||
NodeRemoveChange,
|
NodeRemoveChange,
|
||||||
nodeToRect,
|
nodeToRect,
|
||||||
|
withResolvers,
|
||||||
type Rect,
|
type Rect,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
@@ -280,7 +281,7 @@ export function useReactFlow<NodeType extends Node = Node, EdgeType extends Edge
|
|||||||
fitView: async (options: FitViewOptions<NodeType> | undefined) => {
|
fitView: async (options: FitViewOptions<NodeType> | undefined) => {
|
||||||
// We either create a new Promise or reuse the existing one
|
// We either create a new Promise or reuse the existing one
|
||||||
// Even if fitView is called multiple times in a row, we only end up with a single Promise
|
// Even if fitView is called multiple times in a row, we only end up with a single Promise
|
||||||
const fitViewResolver = store.getState().fitViewResolver ?? Promise.withResolvers<boolean>();
|
const fitViewResolver = store.getState().fitViewResolver ?? withResolvers<boolean>();
|
||||||
|
|
||||||
// We schedule a fitView by setting fitViewQueued and triggering a setNodes
|
// We schedule a fitView by setting fitViewQueued and triggering a setNodes
|
||||||
store.setState({ fitViewQueued: true, fitViewOptions: options, fitViewResolver });
|
store.setState({ fitViewQueued: true, fitViewOptions: options, fitViewResolver });
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import {
|
|||||||
|
|
||||||
import { applyEdgeChanges, applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes';
|
import { applyEdgeChanges, applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes';
|
||||||
import getInitialState from './initialState';
|
import getInitialState from './initialState';
|
||||||
import type { ReactFlowState, Node, Edge, UnselectNodesAndEdgesParams } from '../types';
|
import type { ReactFlowState, Node, Edge, UnselectNodesAndEdgesParams, FitViewOptions } from '../types';
|
||||||
|
|
||||||
const createStore = ({
|
const createStore = ({
|
||||||
nodes,
|
nodes,
|
||||||
@@ -28,6 +28,9 @@ const createStore = ({
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fitView,
|
fitView,
|
||||||
|
fitViewOptions,
|
||||||
|
minZoom,
|
||||||
|
maxZoom,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
nodeExtent,
|
nodeExtent,
|
||||||
}: {
|
}: {
|
||||||
@@ -38,6 +41,9 @@ const createStore = ({
|
|||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
|
fitViewOptions?: FitViewOptions;
|
||||||
|
minZoom?: number;
|
||||||
|
maxZoom?: number;
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
nodeExtent?: CoordinateExtent;
|
nodeExtent?: CoordinateExtent;
|
||||||
}) =>
|
}) =>
|
||||||
@@ -70,7 +76,20 @@ const createStore = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...getInitialState({ nodes, edges, width, height, fitView, nodeOrigin, nodeExtent, defaultNodes, defaultEdges }),
|
...getInitialState({
|
||||||
|
nodes,
|
||||||
|
edges,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
fitView,
|
||||||
|
fitViewOptions,
|
||||||
|
minZoom,
|
||||||
|
maxZoom,
|
||||||
|
nodeOrigin,
|
||||||
|
nodeExtent,
|
||||||
|
defaultNodes,
|
||||||
|
defaultEdges,
|
||||||
|
}),
|
||||||
setNodes: (nodes: Node[]) => {
|
setNodes: (nodes: Node[]) => {
|
||||||
const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect, fitViewQueued } = get();
|
const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect, fitViewQueued } = get();
|
||||||
/*
|
/*
|
||||||
@@ -91,9 +110,9 @@ const createStore = ({
|
|||||||
|
|
||||||
if (fitViewQueued && nodesInitialized) {
|
if (fitViewQueued && nodesInitialized) {
|
||||||
resolveFitView();
|
resolveFitView();
|
||||||
set({ nodes, fitViewQueued: false, fitViewOptions: undefined });
|
set({ nodes, nodesInitialized, fitViewQueued: false, fitViewOptions: undefined });
|
||||||
} else {
|
} else {
|
||||||
set({ nodes });
|
set({ nodes, nodesInitialized });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setEdges: (edges: Edge[]) => {
|
setEdges: (edges: Edge[]) => {
|
||||||
@@ -297,7 +316,11 @@ const createStore = ({
|
|||||||
get().panZoom?.setClickDistance(clickDistance);
|
get().panZoom?.setClickDistance(clickDistance);
|
||||||
},
|
},
|
||||||
resetSelectedElements: () => {
|
resetSelectedElements: () => {
|
||||||
const { edges, nodes, triggerNodeChanges, triggerEdgeChanges } = get();
|
const { edges, nodes, triggerNodeChanges, triggerEdgeChanges, elementsSelectable } = get();
|
||||||
|
|
||||||
|
if (!elementsSelectable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const nodeChanges = nodes.reduce<NodeSelectionChange[]>(
|
const nodeChanges = nodes.reduce<NodeSelectionChange[]>(
|
||||||
(res, node) => (node.selected ? [...res, createSelectionChange(node.id, false)] : res),
|
(res, node) => (node.selected ? [...res, createSelectionChange(node.id, false)] : res),
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
CoordinateExtent,
|
CoordinateExtent,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import type { Edge, InternalNode, Node, ReactFlowStore } from '../types';
|
import type { Edge, FitViewOptions, InternalNode, Node, ReactFlowStore } from '../types';
|
||||||
|
|
||||||
const getInitialState = ({
|
const getInitialState = ({
|
||||||
nodes,
|
nodes,
|
||||||
@@ -22,6 +22,9 @@ const getInitialState = ({
|
|||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fitView,
|
fitView,
|
||||||
|
fitViewOptions,
|
||||||
|
minZoom = 0.5,
|
||||||
|
maxZoom = 2,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
nodeExtent,
|
nodeExtent,
|
||||||
}: {
|
}: {
|
||||||
@@ -32,6 +35,9 @@ const getInitialState = ({
|
|||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
|
fitViewOptions?: FitViewOptions;
|
||||||
|
minZoom?: number;
|
||||||
|
maxZoom?: number;
|
||||||
nodeOrigin?: NodeOrigin;
|
nodeOrigin?: NodeOrigin;
|
||||||
nodeExtent?: CoordinateExtent;
|
nodeExtent?: CoordinateExtent;
|
||||||
} = {}): ReactFlowStore => {
|
} = {}): ReactFlowStore => {
|
||||||
@@ -46,7 +52,7 @@ const getInitialState = ({
|
|||||||
const storeNodeExtent = nodeExtent ?? infiniteExtent;
|
const storeNodeExtent = nodeExtent ?? infiniteExtent;
|
||||||
|
|
||||||
updateConnectionLookup(connectionLookup, edgeLookup, storeEdges);
|
updateConnectionLookup(connectionLookup, edgeLookup, storeEdges);
|
||||||
adoptUserNodes(storeNodes, nodeLookup, parentLookup, {
|
const nodesInitialized = adoptUserNodes(storeNodes, nodeLookup, parentLookup, {
|
||||||
nodeOrigin: storeNodeOrigin,
|
nodeOrigin: storeNodeOrigin,
|
||||||
nodeExtent: storeNodeExtent,
|
nodeExtent: storeNodeExtent,
|
||||||
elevateNodesOnSelect: false,
|
elevateNodesOnSelect: false,
|
||||||
@@ -59,7 +65,14 @@ const getInitialState = ({
|
|||||||
filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight)),
|
filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight)),
|
||||||
});
|
});
|
||||||
|
|
||||||
const { x, y, zoom } = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1);
|
const { x, y, zoom } = getViewportForBounds(
|
||||||
|
bounds,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
minZoom,
|
||||||
|
maxZoom,
|
||||||
|
fitViewOptions?.padding ?? 0.1
|
||||||
|
);
|
||||||
transform = [x, y, zoom];
|
transform = [x, y, zoom];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,6 +82,7 @@ const getInitialState = ({
|
|||||||
height: 0,
|
height: 0,
|
||||||
transform,
|
transform,
|
||||||
nodes: storeNodes,
|
nodes: storeNodes,
|
||||||
|
nodesInitialized,
|
||||||
nodeLookup,
|
nodeLookup,
|
||||||
parentLookup,
|
parentLookup,
|
||||||
edges: storeEdges,
|
edges: storeEdges,
|
||||||
@@ -79,8 +93,8 @@ const getInitialState = ({
|
|||||||
hasDefaultNodes: defaultNodes !== undefined,
|
hasDefaultNodes: defaultNodes !== undefined,
|
||||||
hasDefaultEdges: defaultEdges !== undefined,
|
hasDefaultEdges: defaultEdges !== undefined,
|
||||||
panZoom: null,
|
panZoom: null,
|
||||||
minZoom: 0.5,
|
minZoom,
|
||||||
maxZoom: 2,
|
maxZoom,
|
||||||
translateExtent: infiniteExtent,
|
translateExtent: infiniteExtent,
|
||||||
nodeExtent: storeNodeExtent,
|
nodeExtent: storeNodeExtent,
|
||||||
nodesSelectionActive: false,
|
nodesSelectionActive: false,
|
||||||
@@ -109,7 +123,7 @@ const getInitialState = ({
|
|||||||
multiSelectionActive: false,
|
multiSelectionActive: false,
|
||||||
|
|
||||||
fitViewQueued: fitView ?? false,
|
fitViewQueued: fitView ?? false,
|
||||||
fitViewOptions: undefined,
|
fitViewOptions,
|
||||||
fitViewResolver: null,
|
fitViewResolver: null,
|
||||||
|
|
||||||
connection: { ...initialConnection },
|
connection: { ...initialConnection },
|
||||||
|
|||||||
@@ -85,22 +85,11 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
|||||||
defaultEdges?: EdgeType[];
|
defaultEdges?: EdgeType[];
|
||||||
/**
|
/**
|
||||||
* Defaults to be applied to all new edges that are added to the flow.
|
* Defaults to be applied to all new edges that are added to the flow.
|
||||||
*
|
|
||||||
* Properties on a new edge will override these defaults if they exist.
|
* Properties on a new edge will override these defaults if they exist.
|
||||||
* @example
|
* @example
|
||||||
* const defaultEdgeOptions = {
|
* const defaultEdgeOptions = {
|
||||||
* type: 'customEdgeType',
|
* type: 'customEdgeType',
|
||||||
* animated: true,
|
* animated: true
|
||||||
* interactionWidth: 10,
|
|
||||||
* data: { label: 'custom label' },
|
|
||||||
* hidden: false,
|
|
||||||
* deletable: true,
|
|
||||||
* selected: false,
|
|
||||||
* focusable: true,
|
|
||||||
* markerStart: EdgeMarker.ArrowClosed,
|
|
||||||
* markerEnd: EdgeMarker.ArrowClosed,
|
|
||||||
* zIndex: 12,
|
|
||||||
* ariaLabel: 'custom aria label'
|
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
defaultEdgeOptions?: DefaultEdgeOptions;
|
defaultEdgeOptions?: DefaultEdgeOptions;
|
||||||
@@ -137,7 +126,6 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
|||||||
/**
|
/**
|
||||||
* This handler is called when the source or target of a reconnectable edge is dragged from the
|
* This handler is called when the source or target of a reconnectable edge is dragged from the
|
||||||
* current node. It will fire even if the edge's source or target do not end up changing.
|
* current node. It will fire even if the edge's source or target do not end up changing.
|
||||||
*
|
|
||||||
* You can use the `reconnectEdge` utility to convert the connection to a new edge.
|
* You can use the `reconnectEdge` utility to convert the connection to a new edge.
|
||||||
*/
|
*/
|
||||||
onReconnect?: OnReconnect<EdgeType>;
|
onReconnect?: OnReconnect<EdgeType>;
|
||||||
@@ -148,7 +136,6 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
|||||||
/**
|
/**
|
||||||
* This event fires when the user releases the source or target of an editable edge. It is called
|
* This event fires when the user releases the source or target of an editable edge. It is called
|
||||||
* even if an edge update does not occur.
|
* even if an edge update does not occur.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
onReconnectEnd?: (event: MouseEvent | TouchEvent, edge: EdgeType, handleType: HandleType) => void;
|
onReconnectEnd?: (event: MouseEvent | TouchEvent, edge: EdgeType, handleType: HandleType) => void;
|
||||||
/**
|
/**
|
||||||
@@ -209,7 +196,6 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
|||||||
onSelectionContextMenu?: (event: ReactMouseEvent, nodes: NodeType[]) => void;
|
onSelectionContextMenu?: (event: ReactMouseEvent, nodes: NodeType[]) => void;
|
||||||
/**
|
/**
|
||||||
* When a connection line is completed and two nodes are connected by the user, this event fires with the new connection.
|
* When a connection line is completed and two nodes are connected by the user, this event fires with the new connection.
|
||||||
*
|
|
||||||
* You can use the `addEdge` utility to convert the connection to a complete edge.
|
* You can use the `addEdge` utility to convert the connection to a complete edge.
|
||||||
* @example // Use helper function to update edges onConnect
|
* @example // Use helper function to update edges onConnect
|
||||||
* import ReactFlow, { addEdge } from '@xyflow/react';
|
* import ReactFlow, { addEdge } from '@xyflow/react';
|
||||||
@@ -277,9 +263,7 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
|||||||
onBeforeDelete?: OnBeforeDelete<NodeType, EdgeType>;
|
onBeforeDelete?: OnBeforeDelete<NodeType, EdgeType>;
|
||||||
/**
|
/**
|
||||||
* Custom node types to be available in a flow.
|
* Custom node types to be available in a flow.
|
||||||
*
|
|
||||||
* React Flow matches a node's type to a component in the `nodeTypes` object.
|
* React Flow matches a node's type to a component in the `nodeTypes` object.
|
||||||
* @TODO check if @default is correct
|
|
||||||
* @default {
|
* @default {
|
||||||
* input: InputNode,
|
* input: InputNode,
|
||||||
* default: DefaultNode,
|
* default: DefaultNode,
|
||||||
@@ -294,9 +278,7 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
|||||||
nodeTypes?: NodeTypes;
|
nodeTypes?: NodeTypes;
|
||||||
/**
|
/**
|
||||||
* Custom edge types to be available in a flow.
|
* Custom edge types to be available in a flow.
|
||||||
*
|
|
||||||
* React Flow matches an edge's type to a component in the `edgeTypes` object.
|
* React Flow matches an edge's type to a component in the `edgeTypes` object.
|
||||||
* @TODO check if @default is correct
|
|
||||||
* @default {
|
* @default {
|
||||||
* default: BezierEdge,
|
* default: BezierEdge,
|
||||||
* straight: StraightEdge,
|
* straight: StraightEdge,
|
||||||
@@ -312,7 +294,6 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
|||||||
edgeTypes?: EdgeTypes;
|
edgeTypes?: EdgeTypes;
|
||||||
/**
|
/**
|
||||||
* The type of edge path to use for connection lines.
|
* The type of edge path to use for connection lines.
|
||||||
*
|
|
||||||
* Although created edges can be of any type, React Flow needs to know what type of path to render for the connection line before the edge is created!
|
* Although created edges can be of any type, React Flow needs to know what type of path to render for the connection line before the edge is created!
|
||||||
* @default ConnectionLineType.Bezier
|
* @default ConnectionLineType.Bezier
|
||||||
*/
|
*/
|
||||||
@@ -333,7 +314,7 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
|||||||
/**
|
/**
|
||||||
* If set, pressing the key or chord will delete any selected nodes and edges. Passing an array
|
* If set, pressing the key or chord will delete any selected nodes and edges. Passing an array
|
||||||
* represents multiple keys that can be pressed.
|
* represents multiple keys that can be pressed.
|
||||||
*
|
|
||||||
* For example, `["Delete", "Backspace"]` will delete selected elements when either key is pressed.
|
* For example, `["Delete", "Backspace"]` will delete selected elements when either key is pressed.
|
||||||
* @default 'Backspace'
|
* @default 'Backspace'
|
||||||
*/
|
*/
|
||||||
@@ -450,7 +431,6 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
|||||||
selectNodesOnDrag?: boolean;
|
selectNodesOnDrag?: boolean;
|
||||||
/**
|
/**
|
||||||
* Enabling this prop allows users to pan the viewport by clicking and dragging.
|
* Enabling this prop allows users to pan the viewport by clicking and dragging.
|
||||||
*
|
|
||||||
* You can also set this prop to an array of numbers to limit which mouse buttons can activate panning.
|
* You can also set this prop to an array of numbers to limit which mouse buttons can activate panning.
|
||||||
* @default true
|
* @default true
|
||||||
* @example [0, 2] // allows panning with the left and right mouse buttons
|
* @example [0, 2] // allows panning with the left and right mouse buttons
|
||||||
@@ -489,7 +469,6 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
|||||||
onViewportChange?: (viewport: Viewport) => void;
|
onViewportChange?: (viewport: Viewport) => void;
|
||||||
/**
|
/**
|
||||||
* By default, the viewport extends infinitely. You can use this prop to set a boundary.
|
* By default, the viewport extends infinitely. You can use this prop to set a boundary.
|
||||||
*
|
|
||||||
* The first pair of coordinates is the top left boundary and the second pair is the bottom right.
|
* The first pair of coordinates is the top left boundary and the second pair is the bottom right.
|
||||||
* @default [[-∞, -∞], [+∞, +∞]]
|
* @default [[-∞, -∞], [+∞, +∞]]
|
||||||
* @example [[-1000, -10000], [1000, 1000]]
|
* @example [[-1000, -10000], [1000, 1000]]
|
||||||
@@ -502,7 +481,6 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
|||||||
preventScrolling?: boolean;
|
preventScrolling?: boolean;
|
||||||
/**
|
/**
|
||||||
* By default, nodes can be placed on an infinite flow. You can use this prop to set a boundary.
|
* By default, nodes can be placed on an infinite flow. You can use this prop to set a boundary.
|
||||||
*
|
|
||||||
* The first pair of coordinates is the top left boundary and the second pair is the bottom right.
|
* The first pair of coordinates is the top left boundary and the second pair is the bottom right.
|
||||||
* @example [[-1000, -10000], [1000, 1000]]
|
* @example [[-1000, -10000], [1000, 1000]]
|
||||||
*/
|
*/
|
||||||
@@ -524,21 +502,18 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
|||||||
zoomOnPinch?: boolean;
|
zoomOnPinch?: boolean;
|
||||||
/**
|
/**
|
||||||
* Controls if the viewport should pan by scrolling inside the container.
|
* Controls if the viewport should pan by scrolling inside the container.
|
||||||
*
|
|
||||||
* Can be limited to a specific direction with `panOnScrollMode`.
|
* Can be limited to a specific direction with `panOnScrollMode`.
|
||||||
* @default false
|
* @default false
|
||||||
*/
|
*/
|
||||||
panOnScroll?: boolean;
|
panOnScroll?: boolean;
|
||||||
/**
|
/**
|
||||||
* Controls how fast viewport should be panned on scroll.
|
* Controls how fast viewport should be panned on scroll.
|
||||||
*
|
|
||||||
* Use together with `panOnScroll` prop.
|
* Use together with `panOnScroll` prop.
|
||||||
* @default 0.5
|
* @default 0.5
|
||||||
*/
|
*/
|
||||||
panOnScrollSpeed?: number;
|
panOnScrollSpeed?: number;
|
||||||
/**
|
/**
|
||||||
* This prop is used to limit the direction of panning when `panOnScroll` is enabled.
|
* This prop is used to limit the direction of panning when `panOnScroll` is enabled.
|
||||||
*
|
|
||||||
* The `"free"` option allows panning in any direction.
|
* The `"free"` option allows panning in any direction.
|
||||||
* @default "free"
|
* @default "free"
|
||||||
* @example "horizontal" | "vertical"
|
* @example "horizontal" | "vertical"
|
||||||
@@ -671,10 +646,8 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
|||||||
isValidConnection?: IsValidConnection<EdgeType>;
|
isValidConnection?: IsValidConnection<EdgeType>;
|
||||||
/**
|
/**
|
||||||
* With a threshold greater than zero you can delay node drag events.
|
* With a threshold greater than zero you can delay node drag events.
|
||||||
*
|
|
||||||
* If threshold equals 1, you need to drag the node 1 pixel before a drag event is fired.
|
* If threshold equals 1, you need to drag the node 1 pixel before a drag event is fired.
|
||||||
*
|
* 1 is the default value, so that clicks don't trigger drag events.
|
||||||
* 1 is the default value, so clicks don't trigger drag events.
|
|
||||||
* @default 1
|
* @default 1
|
||||||
*/
|
*/
|
||||||
nodeDragThreshold?: number;
|
nodeDragThreshold?: number;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
ConnectionMode,
|
ConnectionMode,
|
||||||
|
withResolvers,
|
||||||
type ConnectionState,
|
type ConnectionState,
|
||||||
type CoordinateExtent,
|
type CoordinateExtent,
|
||||||
type InternalNodeUpdate,
|
type InternalNodeUpdate,
|
||||||
@@ -53,6 +54,7 @@ export type ReactFlowStore<NodeType extends Node = Node, EdgeType extends Edge =
|
|||||||
height: number;
|
height: number;
|
||||||
transform: Transform;
|
transform: Transform;
|
||||||
nodes: NodeType[];
|
nodes: NodeType[];
|
||||||
|
nodesInitialized: boolean;
|
||||||
nodeLookup: NodeLookup<InternalNode<NodeType>>;
|
nodeLookup: NodeLookup<InternalNode<NodeType>>;
|
||||||
parentLookup: ParentLookup<InternalNode<NodeType>>;
|
parentLookup: ParentLookup<InternalNode<NodeType>>;
|
||||||
edges: EdgeType[];
|
edges: EdgeType[];
|
||||||
@@ -121,7 +123,7 @@ export type ReactFlowStore<NodeType extends Node = Node, EdgeType extends Edge =
|
|||||||
|
|
||||||
fitViewQueued: boolean;
|
fitViewQueued: boolean;
|
||||||
fitViewOptions: FitViewOptions | undefined;
|
fitViewOptions: FitViewOptions | undefined;
|
||||||
fitViewResolver: PromiseWithResolvers<boolean> | null;
|
fitViewResolver: ReturnType<typeof withResolvers<boolean>> | null;
|
||||||
|
|
||||||
onNodesDelete?: OnNodesDelete<NodeType>;
|
onNodesDelete?: OnNodesDelete<NodeType>;
|
||||||
onEdgesDelete?: OnEdgesDelete<EdgeType>;
|
onEdgesDelete?: OnEdgesDelete<EdgeType>;
|
||||||
|
|||||||
@@ -130,8 +130,12 @@ function applyChange(change: any, element: any): any {
|
|||||||
element.measured.height = change.dimensions.height;
|
element.measured.height = change.dimensions.height;
|
||||||
|
|
||||||
if (change.setAttributes) {
|
if (change.setAttributes) {
|
||||||
element.width = change.dimensions.width;
|
if (change.setAttributes === true || change.setAttributes === 'width') {
|
||||||
element.height = change.dimensions.height;
|
element.width = change.dimensions.width;
|
||||||
|
}
|
||||||
|
if (change.setAttributes === true || change.setAttributes === 'height') {
|
||||||
|
element.height = change.dimensions.height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,30 @@
|
|||||||
# @xyflow/svelte
|
# @xyflow/svelte
|
||||||
|
|
||||||
|
## 0.1.38
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [[`2a03213b`](https://github.com/xyflow/xyflow/commit/2a03213b0695d504f831579ec9df3f9de2d3e0bd)]:
|
||||||
|
- @xyflow/system@0.0.58
|
||||||
|
|
||||||
|
## 0.1.37
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [[`a7d10ffc`](https://github.com/xyflow/xyflow/commit/a7d10ffce5a0195471681980f97b1b5f6c448f35), [`4e681f9c`](https://github.com/xyflow/xyflow/commit/4e681f9c529c3f4f8b2ac5d25b4db7878c197e14)]:
|
||||||
|
- @xyflow/system@0.0.57
|
||||||
|
|
||||||
|
## 0.1.36
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- [#5212](https://github.com/xyflow/xyflow/pull/5212) [`0f43b8ea`](https://github.com/xyflow/xyflow/commit/0f43b8ea45bd293e50e4a86d83868074bb323f13) Thanks [@moklick](https://github.com/moklick)! - Add polyfill for `Promise.withResolvers`
|
||||||
|
|
||||||
|
- [#5191](https://github.com/xyflow/xyflow/pull/5191) [`e5735b51`](https://github.com/xyflow/xyflow/commit/e5735b514a54d86ba0ca7bd725e8bfead89fc08e) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix legacy padding being slightly larger than before
|
||||||
|
|
||||||
|
- Updated dependencies [[`0f43b8ea`](https://github.com/xyflow/xyflow/commit/0f43b8ea45bd293e50e4a86d83868074bb323f13), [`fc241253`](https://github.com/xyflow/xyflow/commit/fc241253d5dba35f5febf411e77dbc5acb91d5d7), [`98fe23c7`](https://github.com/xyflow/xyflow/commit/98fe23c7c2b12972f1b7def866215ce82a86e2c0), [`e5735b51`](https://github.com/xyflow/xyflow/commit/e5735b514a54d86ba0ca7bd725e8bfead89fc08e)]:
|
||||||
|
- @xyflow/system@0.0.56
|
||||||
|
|
||||||
## 0.1.35
|
## 0.1.35
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,5 +1,31 @@
|
|||||||
# @xyflow/system
|
# @xyflow/system
|
||||||
|
|
||||||
|
## 0.0.58
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- [#5252](https://github.com/xyflow/xyflow/pull/5252) [`2a03213b`](https://github.com/xyflow/xyflow/commit/2a03213b0695d504f831579ec9df3f9de2d3e0bd) Thanks [@moklick](https://github.com/moklick)! - Center panel correctly when bottom-center or top-center position is used
|
||||||
|
|
||||||
|
## 0.0.57
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- [#5227](https://github.com/xyflow/xyflow/pull/5227) [`a7d10ffc`](https://github.com/xyflow/xyflow/commit/a7d10ffce5a0195471681980f97b1b5f6c448f35) Thanks [@moklick](https://github.com/moklick)! - add resizeDirection for XYResizer
|
||||||
|
|
||||||
|
- [#5221](https://github.com/xyflow/xyflow/pull/5221) [`4e681f9c`](https://github.com/xyflow/xyflow/commit/4e681f9c529c3f4f8b2ac5d25b4db7878c197e14) Thanks [@moklick](https://github.com/moklick)! - Allow zero as a valid node width/height value
|
||||||
|
|
||||||
|
## 0.0.56
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- [#5212](https://github.com/xyflow/xyflow/pull/5212) [`0f43b8ea`](https://github.com/xyflow/xyflow/commit/0f43b8ea45bd293e50e4a86d83868074bb323f13) Thanks [@moklick](https://github.com/moklick)! - Add polyfill for `Promise.withResolvers`
|
||||||
|
|
||||||
|
- [#5192](https://github.com/xyflow/xyflow/pull/5192) [`fc241253`](https://github.com/xyflow/xyflow/commit/fc241253d5dba35f5febf411e77dbc5acb91d5d7) Thanks [@peterkogo](https://github.com/peterkogo)! - Optimize performance of nodesInitialized
|
||||||
|
|
||||||
|
- [#5153](https://github.com/xyflow/xyflow/pull/5153) [`98fe23c7`](https://github.com/xyflow/xyflow/commit/98fe23c7c2b12972f1b7def866215ce82a86e2c0) Thanks [@nick-lawrence-ctm](https://github.com/nick-lawrence-ctm)! - Add separators to horizontal control buttons
|
||||||
|
|
||||||
|
- [#5191](https://github.com/xyflow/xyflow/pull/5191) [`e5735b51`](https://github.com/xyflow/xyflow/commit/e5735b514a54d86ba0ca7bd725e8bfead89fc08e) Thanks [@peterkogo](https://github.com/peterkogo)! - Fix legacy padding being slightly larger than before
|
||||||
|
|
||||||
## 0.0.55
|
## 0.0.55
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@xyflow/system",
|
"name": "@xyflow/system",
|
||||||
"version": "0.0.55",
|
"version": "0.0.58",
|
||||||
"description": "xyflow core system that powers React Flow and Svelte Flow.",
|
"description": "xyflow core system that powers React Flow and Svelte Flow.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"node-based UI",
|
"node-based UI",
|
||||||
|
|||||||
@@ -282,7 +282,7 @@ svg.xy-flow__connectionline {
|
|||||||
&.bottom {
|
&.bottom {
|
||||||
&.center {
|
&.center {
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-15px) translateX(-50%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,7 +298,7 @@ svg.xy-flow__connectionline {
|
|||||||
&.right {
|
&.right {
|
||||||
&.center {
|
&.center {
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-15px) translateY(-50%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,4 +158,17 @@
|
|||||||
&-button:last-child {
|
&-button:last-child {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.horizontal &-button {
|
||||||
|
border-bottom: none;
|
||||||
|
border-right: 1px solid
|
||||||
|
var(
|
||||||
|
--xy-controls-button-border-color-props,
|
||||||
|
var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.horizontal &-button:last-child {
|
||||||
|
border-right: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export type NodeDimensionChange = {
|
|||||||
/* if this is true, the node is currently being resized via the NodeResizer */
|
/* if this is true, the node is currently being resized via the NodeResizer */
|
||||||
resizing?: boolean;
|
resizing?: boolean;
|
||||||
/* if this is true, we will set width and height of the node and not just the measured dimensions */
|
/* if this is true, we will set width and height of the node and not just the measured dimensions */
|
||||||
setAttributes?: boolean;
|
setAttributes?: boolean | 'width' | 'height';
|
||||||
};
|
};
|
||||||
|
|
||||||
export type NodePositionChange = {
|
export type NodePositionChange = {
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ export const rendererPointToPoint = ({ x, y }: XYPosition, [tx, ty, tScale]: Tra
|
|||||||
*/
|
*/
|
||||||
function parsePadding(padding: PaddingWithUnit, viewport: number): number {
|
function parsePadding(padding: PaddingWithUnit, viewport: number): number {
|
||||||
if (typeof padding === 'number') {
|
if (typeof padding === 'number') {
|
||||||
return Math.floor(viewport - viewport / (1 + padding));
|
return Math.floor((viewport - viewport / (1 + padding)) * 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof padding === 'string' && padding.endsWith('px')) {
|
if (typeof padding === 'string' && padding.endsWith('px')) {
|
||||||
@@ -399,3 +399,22 @@ export function areSetsEqual(a: Set<string>, b: Set<string>) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Polyfill for Promise.withResolvers until we can use it in all browsers
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
export function withResolvers<T>(): {
|
||||||
|
promise: Promise<T>;
|
||||||
|
resolve: (value: T | PromiseLike<T>) => void;
|
||||||
|
reject: (reason?: unknown) => void;
|
||||||
|
} {
|
||||||
|
let resolve!: (value: T | PromiseLike<T>) => void;
|
||||||
|
let reject!: (reason?: unknown) => void;
|
||||||
|
|
||||||
|
const promise = new Promise<T>((res, rej) => {
|
||||||
|
resolve = res;
|
||||||
|
reject = rej;
|
||||||
|
});
|
||||||
|
return { promise, resolve, reject };
|
||||||
|
}
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export function adoptUserNodes<NodeType extends NodeBase>(
|
|||||||
): boolean {
|
): boolean {
|
||||||
const _options = mergeObjects(adoptUserNodesDefaultOptions, options);
|
const _options = mergeObjects(adoptUserNodesDefaultOptions, options);
|
||||||
|
|
||||||
let nodesInitialized = true;
|
let nodesInitialized = nodes.length > 0;
|
||||||
const tmpLookup = new Map(nodeLookup);
|
const tmpLookup = new Map(nodeLookup);
|
||||||
const selectedNodeZ: number = _options?.elevateNodesOnSelect ? 1000 : 0;
|
const selectedNodeZ: number = _options?.elevateNodesOnSelect ? 1000 : 0;
|
||||||
|
|
||||||
@@ -125,7 +125,9 @@ export function adoptUserNodes<NodeType extends NodeBase>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(!internalNode.measured || !internalNode.measured.width || !internalNode.measured.height) &&
|
(internalNode.measured === undefined ||
|
||||||
|
internalNode.measured.width === undefined ||
|
||||||
|
internalNode.measured.height === undefined) &&
|
||||||
!internalNode.hidden
|
!internalNode.hidden
|
||||||
) {
|
) {
|
||||||
nodesInitialized = false;
|
nodesInitialized = false;
|
||||||
|
|||||||
@@ -12,7 +12,15 @@ import type {
|
|||||||
Transform,
|
Transform,
|
||||||
XYPosition,
|
XYPosition,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import type { OnResize, OnResizeEnd, OnResizeStart, ResizeDragEvent, ShouldResize, ControlPosition } from './types';
|
import type {
|
||||||
|
OnResize,
|
||||||
|
OnResizeEnd,
|
||||||
|
OnResizeStart,
|
||||||
|
ResizeDragEvent,
|
||||||
|
ShouldResize,
|
||||||
|
ControlPosition,
|
||||||
|
ResizeControlDirection,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
const initPrevValues = { width: 0, height: 0, x: 0, y: 0 };
|
const initPrevValues = { width: 0, height: 0, x: 0, y: 0 };
|
||||||
|
|
||||||
@@ -60,6 +68,7 @@ type XYResizerUpdateParams = {
|
|||||||
maxHeight: number;
|
maxHeight: number;
|
||||||
};
|
};
|
||||||
keepAspectRatio: boolean;
|
keepAspectRatio: boolean;
|
||||||
|
resizeDirection?: ResizeControlDirection;
|
||||||
onResizeStart: OnResizeStart | undefined;
|
onResizeStart: OnResizeStart | undefined;
|
||||||
onResize: OnResize | undefined;
|
onResize: OnResize | undefined;
|
||||||
onResizeEnd: OnResizeEnd | undefined;
|
onResizeEnd: OnResizeEnd | undefined;
|
||||||
@@ -99,6 +108,7 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X
|
|||||||
controlPosition,
|
controlPosition,
|
||||||
boundaries,
|
boundaries,
|
||||||
keepAspectRatio,
|
keepAspectRatio,
|
||||||
|
resizeDirection,
|
||||||
onResizeStart,
|
onResizeStart,
|
||||||
onResize,
|
onResize,
|
||||||
onResizeEnd,
|
onResizeEnd,
|
||||||
@@ -251,8 +261,10 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }: X
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isWidthChange || isHeightChange) {
|
if (isWidthChange || isHeightChange) {
|
||||||
change.width = isWidthChange ? width : prevValues.width;
|
change.width =
|
||||||
change.height = isHeightChange ? height : prevValues.height;
|
isWidthChange && (!resizeDirection || resizeDirection === 'horizontal') ? width : prevValues.width;
|
||||||
|
change.height =
|
||||||
|
isHeightChange && (!resizeDirection || resizeDirection === 'vertical') ? height : prevValues.height;
|
||||||
prevValues.width = change.width;
|
prevValues.width = change.width;
|
||||||
prevValues.height = change.height;
|
prevValues.height = change.height;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ export enum ResizeControlVariant {
|
|||||||
Handle = 'handle',
|
Handle = 'handle',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The direction the user can resize the node.
|
||||||
|
* @public
|
||||||
|
*/
|
||||||
|
export type ResizeControlDirection = 'horizontal' | 'vertical';
|
||||||
|
|
||||||
export const XY_RESIZER_HANDLE_POSITIONS: ControlPosition[] = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];
|
export const XY_RESIZER_HANDLE_POSITIONS: ControlPosition[] = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];
|
||||||
export const XY_RESIZER_LINE_POSITIONS: ControlLinePosition[] = ['top', 'right', 'bottom', 'left'];
|
export const XY_RESIZER_LINE_POSITIONS: ControlLinePosition[] = ['top', 'right', 'bottom', 'left'];
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user