Merge branch 'next' into svelte-node-resizer
This commit is contained in:
@@ -11,7 +11,6 @@ import {
|
||||
EdgeChange,
|
||||
Controls,
|
||||
Background,
|
||||
MiniMap,
|
||||
Panel,
|
||||
} from '@xyflow/react';
|
||||
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
# @xyflow/react
|
||||
|
||||
## 12.0.0-next.5
|
||||
|
||||
### Minor changes
|
||||
|
||||
- fix applyChanges: handle multi changes for one node
|
||||
|
||||
## 12.0.0-next.4
|
||||
|
||||
### Minor changes
|
||||
|
||||
- fix applyChanges: handle empty flows + addNodes/addEdges closes
|
||||
- cleanup exports
|
||||
|
||||
## 12.0.0-next.3
|
||||
|
||||
### Minor changes
|
||||
|
||||
- fix edges styles when using base.css
|
||||
|
||||
## 12.0.0-next.2
|
||||
|
||||
### Minor changes
|
||||
|
||||
- fix connection line rendering
|
||||
- fix multi handle
|
||||
|
||||
## 12.0.0-next.1
|
||||
|
||||
### Minor changes
|
||||
@@ -14,54 +40,58 @@ React Flow v12 is coming soon! We worked hard over the past months and tried to
|
||||
2. **Reactive flows**: new hooks and helper functions to simplify data flows
|
||||
3. **Dark mode**: a new base style and easy way to switch between built in color modes
|
||||
|
||||
Svelte Flow had a big impact on this release as well. While combing through each line of React Flow, we created framework agnostic helpers, found bugs, and made some under the hood improvements. All of these changes are baked into the v12 release as a welcome side-effect of that launch. 🙌🏻 We also improved the performance for larger flows with the help of Ivan.
|
||||
Svelte Flow had a big impact on this release as well. While combing through each line of React Flow, we created framework agnostic helpers, found bugs, and made some under the hood improvements. All of these changes are baked into the v12 release as a welcome side-effect of that launch. 🙌🏻 We also improved the performance for larger flows with the help of Ivan.
|
||||
|
||||
### Migrate from 11 to 12
|
||||
|
||||
Before you can try out the new features, you need to do some minor updates:
|
||||
|
||||
- **A new npm package name:** Our name changed from `reactflow` to `@xyflow/react` and the main component is no longer a default, but a named import:
|
||||
- v11: `import ReactFlow from 'reactflow';`
|
||||
- v12: `import { ReactFlow } from '@xyflow/react';`
|
||||
- v11: `import ReactFlow from 'reactflow';`
|
||||
- v12: `import { ReactFlow } from '@xyflow/react';`
|
||||
- **Node attribute “computed”:** All computed node values are now stored in `node.computed`
|
||||
- v11: `node.width`, `node.height` ,`node.positionAbsolute`
|
||||
- v12: `node.computed.width`, `node.computed.height` and `node.computed.positionAbsolute` . (`node.width`/ `node.height` can now be used for SSG)
|
||||
- v11: `node.width`, `node.height` ,`node.positionAbsolute`
|
||||
- v12: `node.computed.width`, `node.computed.height` and `node.computed.positionAbsolute` . (`node.width`/ `node.height` can now be used for SSG)
|
||||
- **Updating nodes:** We are not supporting node updates with object mutations anymore. If you want to update a certain attribute, you need to create a new node.
|
||||
- v11:
|
||||
```js
|
||||
setNodes(nds => nds.map((node) => {
|
||||
node.hidden = true;
|
||||
return node;
|
||||
}));
|
||||
```
|
||||
- v12:
|
||||
```js
|
||||
setNodes(nds => nds.map((node) => ({
|
||||
...node,
|
||||
hidden: true
|
||||
})));
|
||||
```
|
||||
- v11:
|
||||
```js
|
||||
setNodes((nds) =>
|
||||
nds.map((node) => {
|
||||
node.hidden = true;
|
||||
return node;
|
||||
})
|
||||
);
|
||||
```
|
||||
- v12:
|
||||
```js
|
||||
setNodes((nds) =>
|
||||
nds.map((node) => ({
|
||||
...node,
|
||||
hidden: true,
|
||||
}))
|
||||
);
|
||||
```
|
||||
- **NodeProps:** `posX`/`posY` is now called `positionAbsoluteX`/`positionAbsoluteY`
|
||||
- **Typescript only:** We simplified types and fixed issues about functions where users could pass a `NodeData` generic. The new way is to define your own node type for the whole app and then only use that one. The big advantage of this is, that you can have multiple node types with different data structures and always be able to distinguish by checking the `node.type` attribute.
|
||||
- v11: `applyNodeChange<NodeData, NodeType>`
|
||||
- v12: `type MyNodeType = Node<{ value: number }, ‘number’> | Node<{ value: string }, ‘text’>; applyNodeChange<MyNodeType>`
|
||||
- affected functions: `useNodes`, `useNodesState`, `useEdgesState`, `applyNodeChange`, `onInit`, `applyEdgeChanges` , `MiniMapProps`
|
||||
- v11: `applyNodeChange<NodeData, NodeType>`
|
||||
- v12: `type MyNodeType = Node<{ value: number }, ‘number’> | Node<{ value: string }, ‘text’>; applyNodeChange<MyNodeType>`
|
||||
- affected functions: `useNodes`, `useNodesState`, `useEdgesState`, `applyNodeChange`, `onInit`, `applyEdgeChanges` , `MiniMapProps`
|
||||
- **Removal of deprecated functions:**
|
||||
- `getTransformForBounds` (new name: `getViewportForBounds`),
|
||||
- `getRectOfNodes` ****(new name: `getNodesBounds`)
|
||||
- `project` (new name: `screenToFlowPosition`)
|
||||
- `getMarkerEndId`
|
||||
- `getTransformForBounds` (new name: `getViewportForBounds`),
|
||||
- `getRectOfNodes` (new name: `getNodesBounds`)
|
||||
- `project` (new name: `screenToFlowPosition`)
|
||||
- `getMarkerEndId`
|
||||
|
||||
### Main features
|
||||
|
||||
Now that you successfully migrated to v12, you can use all the fancy features. As mentioned above, the biggest updates for v12 are:
|
||||
|
||||
- **SSR / SSG**: you can define `width`, `height` and `handles` for the nodes. This makes it possible to render a flow on the server and hydrate on the client: [codesandbox](https://codesandbox.io/p/devbox/reactflow-v12-next-pr66yh)
|
||||
- Details: In v11, `width` and `height` were set by the library as soon as the nodes got measured. This still happens, but we are now using `computed.width` and `computed.height` to store this information. The `positionAbsolute` attribute also gets stored in `computed` . In the previous versions there was always a lot of confusion about `width` and `height`. It’s hard to understand, that you can’t use it for passing an actual width or height. It’s also not obvious that those attributes get added by the library. We think that the new implementation solves both of the problems: `width` and `height` are optional attributes that can be used to define dimensions and everything that is set by the library, is stored in `computed`.
|
||||
- Details: In v11, `width` and `height` were set by the library as soon as the nodes got measured. This still happens, but we are now using `computed.width` and `computed.height` to store this information. The `positionAbsolute` attribute also gets stored in `computed` . In the previous versions there was always a lot of confusion about `width` and `height`. It’s hard to understand, that you can’t use it for passing an actual width or height. It’s also not obvious that those attributes get added by the library. We think that the new implementation solves both of the problems: `width` and `height` are optional attributes that can be used to define dimensions and everything that is set by the library, is stored in `computed`.
|
||||
- **Reactive Flows:** The new hooks `useHandleConnections` and `useNodesData` and the new `updateNode` and `updateNodeData` functions can be used for managing the data flow between your nodes: [codesandbox](https://codesandbox.io/p/sandbox/reactflow-reactive-flow-sy93yx)
|
||||
- Details: Working with reactive flows is super common. You update node A and want to react on those changes in the connected node B. Until now everyone had to come up with a custom solution. With this version we want to change this and give you performant helpers to handle this. If you are excited about this, you can check out this example:
|
||||
- Details: Working with reactive flows is super common. You update node A and want to react on those changes in the connected node B. Until now everyone had to come up with a custom solution. With this version we want to change this and give you performant helpers to handle this. If you are excited about this, you can check out this example:
|
||||
- **Dark mode and css variables:** React Flow now comes with a built-in dark mode, that can be toggled by using the new `colorMode` prop (”light”, “dark” or “system”): [codesandbox](https://codesandbox.io/p/sandbox/reactflow-dark-mode-256l99)
|
||||
- Details: With this version we want to make it easier to switch between dark and light modes and give you a better starting point for dark flows. If you pass colorMode=”dark”, we add the class name “dark” to the wrapper and use it to adjust the styling. To make the implementation for this new feature easier on our ends, we switched to CSS variables for most of the styles. These variables can also be used in user land to customize a flow.
|
||||
- Details: With this version we want to make it easier to switch between dark and light modes and give you a better starting point for dark flows. If you pass colorMode=”dark”, we add the class name “dark” to the wrapper and use it to adjust the styling. To make the implementation for this new feature easier on our ends, we switched to CSS variables for most of the styles. These variables can also be used in user land to customize a flow.
|
||||
|
||||
### More features and updates
|
||||
|
||||
@@ -86,12 +116,11 @@ There is more! Besides the new main features, we added some minor things that we
|
||||
These changes are not really user-facing, but it could be important for folks who are working with the React Flow store:
|
||||
|
||||
- The biggest internal change is that we created a new package **@xyflow/system with framework agnostic helpers** that can be used be React Flow and Svelte Flow
|
||||
- **XYDrag** for handling dragging node(s) and selection
|
||||
- **XYPanZoom** for controlling the viewport panning and zooming
|
||||
- **XYHandle** for managing new connections
|
||||
- **XYDrag** for handling dragging node(s) and selection
|
||||
- **XYPanZoom** for controlling the viewport panning and zooming
|
||||
- **XYHandle** for managing new connections
|
||||
- We replaced the `nodeInternals` map with a `nodes` array. We added a new `nodeLookup` map that serves as a lookup, but we are not creating a new map object on any change so it’s really only useful as a lookup.
|
||||
- We removed `connectionNodeId`, `connectionHandleId`, `connectionHandleType` from the store and added `connectionStartHandle.nodeId`, `connectionStartHandle.handleId`, …
|
||||
- add `data-id` to edges
|
||||
|
||||
|
||||
__With v12 the `reactflow` package was renamed to `@xyflow/react` - you can find the v11 source and the [`reactflow` changelog](https://github.com/xyflow/xyflow/blob/v11/packages/reactflow/CHANGELOG.md) on the v11 branch.__
|
||||
**With v12 the `reactflow` package was renamed to `@xyflow/react` - you can find the v11 source and the [`reactflow` changelog](https://github.com/xyflow/xyflow/blob/v11/packages/reactflow/CHANGELOG.md) on the v11 branch.**
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@xyflow/react",
|
||||
"version": "12.0.0-next.1",
|
||||
"version": "12.0.0-next.4",
|
||||
"description": "React Flow - A highly customizable React library for building node-based editors and interactive flow charts.",
|
||||
"keywords": [
|
||||
"react",
|
||||
|
||||
@@ -3,10 +3,10 @@ import cc from 'classcat';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { useStore } from '../../hooks/useStore';
|
||||
import { type ReactFlowState } from '../../types';
|
||||
import { BackgroundProps, BackgroundVariant } from './types';
|
||||
import { DotPattern, LinePattern } from './Patterns';
|
||||
import { containerStyle } from '../../styles/utils';
|
||||
import { type BackgroundProps, BackgroundVariant } from './types';
|
||||
import { type ReactFlowState } from '../../types';
|
||||
|
||||
const defaultSize = {
|
||||
[BackgroundVariant.Dots]: 1,
|
||||
@@ -16,7 +16,7 @@ const defaultSize = {
|
||||
|
||||
const selector = (s: ReactFlowState) => ({ transform: s.transform, patternId: `pattern-${s.rfId}` });
|
||||
|
||||
function Background({
|
||||
function BackgroundComponent({
|
||||
id,
|
||||
variant = BackgroundVariant.Dots,
|
||||
// only used for dots and cross
|
||||
@@ -87,6 +87,6 @@ function Background({
|
||||
);
|
||||
}
|
||||
|
||||
Background.displayName = 'Background';
|
||||
BackgroundComponent.displayName = 'Background';
|
||||
|
||||
export default memo(Background);
|
||||
export const Background = memo(BackgroundComponent);
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { default as Background } from './Background';
|
||||
export * from './types';
|
||||
export { Background } from './Background';
|
||||
export { BackgroundVariant, type BackgroundProps } from './types';
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
import type { FC, PropsWithChildren } from 'react';
|
||||
import cc from 'classcat';
|
||||
|
||||
import type { ControlButtonProps } from './types';
|
||||
|
||||
const ControlButton: FC<PropsWithChildren<ControlButtonProps>> = ({ children, className, ...rest }) => (
|
||||
<button type="button" className={cc(['react-flow__controls-button', className])} {...rest}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
|
||||
ControlButton.displayName = 'ControlButton';
|
||||
|
||||
export default ControlButton;
|
||||
export function ControlButton({ children, className, ...rest }: ControlButtonProps) {
|
||||
return (
|
||||
<button type="button" className={cc(['react-flow__controls-button', className])} {...rest}>
|
||||
{children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
import { memo, type FC, type PropsWithChildren } from 'react';
|
||||
import { memo } from 'react';
|
||||
import cc from 'classcat';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||
import useReactFlow from '../../hooks/useReactFlow';
|
||||
import Panel from '../../components/Panel';
|
||||
import { useReactFlow } from '../../hooks/useReactFlow';
|
||||
import { Panel } from '../../components/Panel';
|
||||
import { type ReactFlowState } from '../../types';
|
||||
|
||||
import PlusIcon from './Icons/Plus';
|
||||
import MinusIcon from './Icons/Minus';
|
||||
import FitviewIcon from './Icons/FitView';
|
||||
import LockIcon from './Icons/Lock';
|
||||
import UnlockIcon from './Icons/Unlock';
|
||||
import ControlButton from './ControlButton';
|
||||
|
||||
import { PlusIcon } from './Icons/Plus';
|
||||
import { MinusIcon } from './Icons/Minus';
|
||||
import { FitViewIcon } from './Icons/FitView';
|
||||
import { LockIcon } from './Icons/Lock';
|
||||
import { UnlockIcon } from './Icons/Unlock';
|
||||
import { ControlButton } from './ControlButton';
|
||||
import type { ControlProps } from './types';
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
@@ -22,7 +21,7 @@ const selector = (s: ReactFlowState) => ({
|
||||
maxZoomReached: s.transform[2] >= s.maxZoom,
|
||||
});
|
||||
|
||||
const Controls: FC<PropsWithChildren<ControlProps>> = ({
|
||||
function ControlsComponent({
|
||||
style,
|
||||
showZoom = true,
|
||||
showFitView = true,
|
||||
@@ -35,7 +34,7 @@ const Controls: FC<PropsWithChildren<ControlProps>> = ({
|
||||
className,
|
||||
children,
|
||||
position = 'bottom-left',
|
||||
}) => {
|
||||
}: ControlProps) {
|
||||
const store = useStoreApi();
|
||||
const { isInteractive, minZoomReached, maxZoomReached } = useStore(selector, shallow);
|
||||
const { zoomIn, zoomOut, fitView } = useReactFlow();
|
||||
@@ -101,7 +100,7 @@ const Controls: FC<PropsWithChildren<ControlProps>> = ({
|
||||
title="fit view"
|
||||
aria-label="fit view"
|
||||
>
|
||||
<FitviewIcon />
|
||||
<FitViewIcon />
|
||||
</ControlButton>
|
||||
)}
|
||||
{showInteractive && (
|
||||
@@ -117,8 +116,8 @@ const Controls: FC<PropsWithChildren<ControlProps>> = ({
|
||||
{children}
|
||||
</Panel>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
Controls.displayName = 'Controls';
|
||||
ControlsComponent.displayName = 'Controls';
|
||||
|
||||
export default memo(Controls);
|
||||
export const Controls = memo(ControlsComponent);
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
function FitViewIcon() {
|
||||
export function FitViewIcon() {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 30">
|
||||
<path d="M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default FitViewIcon;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
function LockIcon() {
|
||||
export function LockIcon() {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 32">
|
||||
<path d="M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default LockIcon;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
function MinusIcon() {
|
||||
export function MinusIcon() {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 5">
|
||||
<path d="M0 0h32v4.2H0z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default MinusIcon;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
function PlusIcon() {
|
||||
export function PlusIcon() {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||
<path d="M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default PlusIcon;
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
function UnlockIcon() {
|
||||
export function UnlockIcon() {
|
||||
return (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 32">
|
||||
<path d="M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default UnlockIcon;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
export { default as Controls } from './Controls';
|
||||
export { default as ControlButton } from './ControlButton';
|
||||
export * from './types';
|
||||
export { Controls } from './Controls';
|
||||
export { ControlButton } from './ControlButton';
|
||||
export type { ControlProps, ControlButtonProps } from './types';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ButtonHTMLAttributes, HTMLAttributes } from 'react';
|
||||
import type { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from 'react';
|
||||
import type { PanelPosition } from '@xyflow/system';
|
||||
|
||||
import type { FitViewOptions } from '../../types';
|
||||
@@ -13,6 +13,7 @@ export type ControlProps = HTMLAttributes<HTMLDivElement> & {
|
||||
onFitView?: () => void;
|
||||
onInteractiveChange?: (interactiveStatus: boolean) => void;
|
||||
position?: PanelPosition;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
export type ControlButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;
|
||||
|
||||
@@ -6,11 +6,11 @@ import { shallow } from 'zustand/shallow';
|
||||
import { getNodesBounds, getBoundsOfRects, XYMinimap, type Rect, type XYMinimapInstance } from '@xyflow/system';
|
||||
|
||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||
import Panel from '../../components/Panel';
|
||||
import { Panel } from '../../components/Panel';
|
||||
import type { ReactFlowState } from '../../types';
|
||||
|
||||
import type { MiniMapProps } from './types';
|
||||
import MiniMapNodes from './MiniMapNodes';
|
||||
import type { MiniMapProps } from './types';
|
||||
|
||||
const defaultWidth = 200;
|
||||
const defaultHeight = 150;
|
||||
@@ -37,7 +37,7 @@ const selector = (s: ReactFlowState) => {
|
||||
|
||||
const ARIA_LABEL_KEY = 'react-flow__minimap-desc';
|
||||
|
||||
function MiniMap({
|
||||
function MiniMapComponent({
|
||||
style,
|
||||
className,
|
||||
nodeStrokeColor,
|
||||
@@ -171,6 +171,6 @@ function MiniMap({
|
||||
);
|
||||
}
|
||||
|
||||
MiniMap.displayName = 'MiniMap';
|
||||
MiniMapComponent.displayName = 'MiniMap';
|
||||
|
||||
export default memo(MiniMap);
|
||||
export const MiniMap = memo(MiniMapComponent);
|
||||
|
||||
@@ -3,7 +3,7 @@ import cc from 'classcat';
|
||||
|
||||
import type { MiniMapNodeProps } from './types';
|
||||
|
||||
function MiniMapNode({
|
||||
function MiniMapNodeComponent({
|
||||
id,
|
||||
x,
|
||||
y,
|
||||
@@ -42,4 +42,4 @@ function MiniMapNode({
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(MiniMapNode);
|
||||
export const MiniMapNode = memo(MiniMapNodeComponent);
|
||||
|
||||
@@ -5,8 +5,8 @@ import { NodeOrigin, getNodePositionWithOrigin } from '@xyflow/system';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { useStore } from '../../hooks/useStore';
|
||||
import { MiniMapNode } from './MiniMapNode';
|
||||
import type { ReactFlowState } from '../../types';
|
||||
import MiniMapNode from './MiniMapNode';
|
||||
import type { MiniMapNodes as MiniMapNodesProps, GetMiniMapNodeAttribute, MiniMapNodeProps } from './types';
|
||||
|
||||
declare const window: any;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { default as MiniMap } from './MiniMap';
|
||||
export { MiniMap } from './MiniMap';
|
||||
export * from './types';
|
||||
|
||||
@@ -6,8 +6,8 @@ import { getNodesBounds, Rect, Position, internalsSymbol, getNodeToolbarTransfor
|
||||
import { Node, ReactFlowState } from '../../types';
|
||||
import { useStore } from '../../hooks/useStore';
|
||||
import { useNodeId } from '../../contexts/NodeIdContext';
|
||||
import NodeToolbarPortal from './NodeToolbarPortal';
|
||||
import { NodeToolbarProps } from './types';
|
||||
import { NodeToolbarPortal } from './NodeToolbarPortal';
|
||||
import type { NodeToolbarProps } from './types';
|
||||
|
||||
const nodeEqualityFn = (a?: Node, b?: Node) =>
|
||||
a?.computed?.positionAbsolute?.x !== b?.computed?.positionAbsolute?.x ||
|
||||
@@ -35,7 +35,7 @@ const storeSelector = (state: ReactFlowState) => ({
|
||||
selectedNodesCount: state.nodes.filter((node) => node.selected).length,
|
||||
});
|
||||
|
||||
function NodeToolbar({
|
||||
export function NodeToolbar({
|
||||
nodeId,
|
||||
children,
|
||||
className,
|
||||
@@ -96,5 +96,3 @@ function NodeToolbar({
|
||||
</NodeToolbarPortal>
|
||||
);
|
||||
}
|
||||
|
||||
export default NodeToolbar;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useStore } from '../../hooks/useStore';
|
||||
|
||||
const selector = (state: ReactFlowState) => state.domNode?.querySelector('.react-flow__renderer');
|
||||
|
||||
function NodeToolbarPortal({ children }: { children: ReactNode }) {
|
||||
export function NodeToolbarPortal({ children }: { children: ReactNode }) {
|
||||
const wrapperRef = useStore(selector);
|
||||
|
||||
if (!wrapperRef) {
|
||||
@@ -15,5 +15,3 @@ function NodeToolbarPortal({ children }: { children: ReactNode }) {
|
||||
|
||||
return createPortal(children, wrapperRef);
|
||||
}
|
||||
|
||||
export default NodeToolbarPortal;
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export { default as NodeToolbar } from './NodeToolbar';
|
||||
export * from './types';
|
||||
export { NodeToolbar } from './NodeToolbar';
|
||||
export type { NodeToolbarProps } from './types';
|
||||
|
||||
@@ -32,7 +32,7 @@ function AriaLiveMessage({ rfId }: { rfId: string }) {
|
||||
);
|
||||
}
|
||||
|
||||
function A11yDescriptions({ rfId, disableKeyboardA11y }: { rfId: string; disableKeyboardA11y: boolean }) {
|
||||
export function A11yDescriptions({ rfId, disableKeyboardA11y }: { rfId: string; disableKeyboardA11y: boolean }) {
|
||||
return (
|
||||
<>
|
||||
<div id={`${ARIA_NODE_DESC_KEY}-${rfId}`} style={style}>
|
||||
@@ -47,5 +47,3 @@ function A11yDescriptions({ rfId, disableKeyboardA11y }: { rfId: string; disable
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default A11yDescriptions;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import type { PanelPosition, ProOptions } from '@xyflow/system';
|
||||
|
||||
import Panel from '../Panel';
|
||||
import { Panel } from '../Panel';
|
||||
|
||||
type AttributionProps = {
|
||||
proOptions?: ProOptions;
|
||||
position?: PanelPosition;
|
||||
};
|
||||
|
||||
function Attribution({ proOptions, position = 'bottom-right' }: AttributionProps) {
|
||||
export function Attribution({ proOptions, position = 'bottom-right' }: AttributionProps) {
|
||||
if (proOptions?.hideAttribution) {
|
||||
return null;
|
||||
}
|
||||
@@ -24,5 +24,3 @@ function Attribution({ proOptions, position = 'bottom-right' }: AttributionProps
|
||||
</Panel>
|
||||
);
|
||||
}
|
||||
|
||||
export default Attribution;
|
||||
|
||||
@@ -142,7 +142,7 @@ const selector = (s: ReactFlowState) => ({
|
||||
height: s.height,
|
||||
});
|
||||
|
||||
function ConnectionLineWrapper({ containerStyle, style, type, component }: ConnectionLineWrapperProps) {
|
||||
export function ConnectionLineWrapper({ containerStyle, style, type, component }: ConnectionLineWrapperProps) {
|
||||
const { nodeId, handleType, nodesConnectable, width, height, connectionStatus } = useStore(selector, shallow);
|
||||
const isValid = !!(nodeId && handleType && width && nodesConnectable);
|
||||
|
||||
@@ -155,7 +155,7 @@ function ConnectionLineWrapper({ containerStyle, style, type, component }: Conne
|
||||
style={containerStyle}
|
||||
width={width}
|
||||
height={height}
|
||||
className="react-flow__edges react-flow__connectionline react-flow__container"
|
||||
className="react-flow__connectionline react-flow__container"
|
||||
>
|
||||
<g className={cc(['react-flow__connection', connectionStatus])}>
|
||||
<ConnectionLine
|
||||
@@ -170,5 +170,3 @@ function ConnectionLineWrapper({ containerStyle, style, type, component }: Conne
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export default ConnectionLineWrapper;
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { ReactFlowState } from '../../types';
|
||||
|
||||
const selector = (s: ReactFlowState) => s.domNode?.querySelector('.react-flow__edgelabel-renderer');
|
||||
|
||||
function EdgeLabelRenderer({ children }: { children: ReactNode }) {
|
||||
export function EdgeLabelRenderer({ children }: { children: ReactNode }) {
|
||||
const edgeLabelRenderer = useStore(selector);
|
||||
|
||||
if (!edgeLabelRenderer) {
|
||||
@@ -15,5 +15,3 @@ function EdgeLabelRenderer({ children }: { children: ReactNode }) {
|
||||
|
||||
return createPortal(children, edgeLabelRenderer);
|
||||
}
|
||||
|
||||
export default EdgeLabelRenderer;
|
||||
|
||||
@@ -9,8 +9,8 @@ type EdgeUpdateAnchorsProps = {
|
||||
edge: Edge;
|
||||
isUpdatable: boolean | 'source' | 'target';
|
||||
edgeUpdaterRadius: EdgeWrapperProps['edgeUpdaterRadius'];
|
||||
sourceHandleId: EdgeWrapperProps['sourceHandleId'];
|
||||
targetHandleId: EdgeWrapperProps['targetHandleId'];
|
||||
sourceHandleId: Edge['sourceHandle'];
|
||||
targetHandleId: Edge['targetHandle'];
|
||||
onEdgeUpdate: EdgeWrapperProps['onEdgeUpdate'];
|
||||
onEdgeUpdateStart: EdgeWrapperProps['onEdgeUpdateStart'];
|
||||
onEdgeUpdateEnd: EdgeWrapperProps['onEdgeUpdateEnd'];
|
||||
@@ -18,7 +18,7 @@ type EdgeUpdateAnchorsProps = {
|
||||
setUpdating: (updating: boolean) => void;
|
||||
} & EdgePosition;
|
||||
|
||||
function EdgeUpdateAnchors({
|
||||
export function EdgeUpdateAnchors({
|
||||
isUpdatable,
|
||||
edgeUpdaterRadius,
|
||||
edge,
|
||||
@@ -133,5 +133,3 @@ function EdgeUpdateAnchors({
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default EdgeUpdateAnchors;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { memo, useState, useMemo, useRef, type KeyboardEvent, useCallback } from 'react';
|
||||
import { useState, useMemo, useRef, type KeyboardEvent, useCallback } from 'react';
|
||||
import cc from 'classcat';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import {
|
||||
@@ -11,19 +11,17 @@ import {
|
||||
|
||||
import { useStoreApi, useStore } from '../../hooks/useStore';
|
||||
import { ARIA_EDGE_DESC_KEY } from '../A11yDescriptions';
|
||||
import type { EdgeWrapperProps } from '../../types';
|
||||
import { builtinEdgeTypes, nullPosition } from './utils';
|
||||
import EdgeUpdateAnchors from './EdgeUpdateAnchors';
|
||||
import { EdgeUpdateAnchors } from './EdgeUpdateAnchors';
|
||||
import type { EdgeWrapperProps } from '../../types';
|
||||
|
||||
function EdgeWrapper({
|
||||
export function EdgeWrapper({
|
||||
id,
|
||||
edgesFocusable,
|
||||
edgesUpdatable,
|
||||
elementsSelectable,
|
||||
onClick,
|
||||
onDoubleClick,
|
||||
sourceHandleId,
|
||||
targetHandleId,
|
||||
onContextMenu,
|
||||
onMouseEnter,
|
||||
onMouseMove,
|
||||
@@ -78,8 +76,8 @@ function EdgeWrapper({
|
||||
id,
|
||||
sourceNode,
|
||||
targetNode,
|
||||
sourceHandle: sourceHandleId || null,
|
||||
targetHandle: targetHandleId || null,
|
||||
sourceHandle: edge.sourceHandle || null,
|
||||
targetHandle: edge.targetHandle || null,
|
||||
connectionMode: store.connectionMode,
|
||||
onError,
|
||||
});
|
||||
@@ -97,7 +95,7 @@ function EdgeWrapper({
|
||||
...(edgePosition || nullPosition),
|
||||
};
|
||||
},
|
||||
[edge.source, edge.target, edge.selected, edge.zIndex]
|
||||
[edge.source, edge.target, edge.sourceHandle, edge.targetHandle, edge.selected, edge.zIndex]
|
||||
),
|
||||
shallow
|
||||
);
|
||||
@@ -228,8 +226,8 @@ function EdgeWrapper({
|
||||
targetPosition={targetPosition}
|
||||
data={edge.data}
|
||||
style={edge.style}
|
||||
sourceHandleId={sourceHandleId}
|
||||
targetHandleId={targetHandleId}
|
||||
sourceHandleId={edge.sourceHandle}
|
||||
targetHandleId={edge.targetHandle}
|
||||
markerStart={markerStartUrl}
|
||||
markerEnd={markerEndUrl}
|
||||
pathOptions={'pathOptions' in edge ? edge.pathOptions : undefined}
|
||||
@@ -252,15 +250,11 @@ function EdgeWrapper({
|
||||
targetPosition={targetPosition}
|
||||
setUpdateHover={setUpdateHover}
|
||||
setUpdating={setUpdating}
|
||||
sourceHandleId={sourceHandleId}
|
||||
targetHandleId={targetHandleId}
|
||||
sourceHandleId={edge.sourceHandle}
|
||||
targetHandleId={edge.targetHandle}
|
||||
/>
|
||||
)}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
EdgeWrapper.displayName = 'EdgeWrapper';
|
||||
|
||||
export default memo(EdgeWrapper);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { isNumeric } from '@xyflow/system';
|
||||
import cc from 'classcat';
|
||||
|
||||
import { EdgeText } from './EdgeText';
|
||||
import type { BaseEdgeProps } from '../../types';
|
||||
import EdgeText from './EdgeText';
|
||||
import classcat from 'classcat';
|
||||
|
||||
const BaseEdge = ({
|
||||
export function BaseEdge({
|
||||
id,
|
||||
path,
|
||||
labelX,
|
||||
@@ -20,7 +20,7 @@ const BaseEdge = ({
|
||||
markerStart,
|
||||
className,
|
||||
interactionWidth = 20,
|
||||
}: BaseEdgeProps) => {
|
||||
}: BaseEdgeProps) {
|
||||
return (
|
||||
<>
|
||||
<path
|
||||
@@ -28,7 +28,7 @@ const BaseEdge = ({
|
||||
style={style}
|
||||
d={path}
|
||||
fill="none"
|
||||
className={classcat(['react-flow__edge-path', className])}
|
||||
className={cc(['react-flow__edge-path', className])}
|
||||
markerEnd={markerEnd}
|
||||
markerStart={markerStart}
|
||||
/>
|
||||
@@ -55,8 +55,4 @@ const BaseEdge = ({
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
BaseEdge.displayName = 'BaseEdge';
|
||||
|
||||
export default BaseEdge;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { memo } from 'react';
|
||||
import { Position, getBezierPath } from '@xyflow/system';
|
||||
|
||||
import BaseEdge from './BaseEdge';
|
||||
import { BaseEdge } from './BaseEdge';
|
||||
import type { BezierEdgeProps } from '../../types';
|
||||
|
||||
function createBezierEdge(params: { isInternal: boolean }) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { FC, MouseEvent as ReactMouseEvent, SVGAttributes } from 'react';
|
||||
import type { MouseEvent as ReactMouseEvent, SVGAttributes } from 'react';
|
||||
import cc from 'classcat';
|
||||
import { Position } from '@xyflow/system';
|
||||
|
||||
@@ -27,7 +27,7 @@ export interface EdgeAnchorProps extends SVGAttributes<SVGGElement> {
|
||||
|
||||
const EdgeUpdaterClassName = 'react-flow__edgeupdater';
|
||||
|
||||
export const EdgeAnchor: FC<EdgeAnchorProps> = ({
|
||||
export function EdgeAnchor({
|
||||
position,
|
||||
centerX,
|
||||
centerY,
|
||||
@@ -36,16 +36,18 @@ export const EdgeAnchor: FC<EdgeAnchorProps> = ({
|
||||
onMouseEnter,
|
||||
onMouseOut,
|
||||
type,
|
||||
}: EdgeAnchorProps) => (
|
||||
<circle
|
||||
onMouseDown={onMouseDown}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseOut={onMouseOut}
|
||||
className={cc([EdgeUpdaterClassName, `${EdgeUpdaterClassName}-${type}`])}
|
||||
cx={shiftX(centerX, radius, position)}
|
||||
cy={shiftY(centerY, radius, position)}
|
||||
r={radius}
|
||||
stroke="transparent"
|
||||
fill="transparent"
|
||||
/>
|
||||
);
|
||||
}: EdgeAnchorProps) {
|
||||
return (
|
||||
<circle
|
||||
onMouseDown={onMouseDown}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseOut={onMouseOut}
|
||||
className={cc([EdgeUpdaterClassName, `${EdgeUpdaterClassName}-${type}`])}
|
||||
cx={shiftX(centerX, radius, position)}
|
||||
cy={shiftY(centerY, radius, position)}
|
||||
r={radius}
|
||||
stroke="transparent"
|
||||
fill="transparent"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { memo, useState, type FC, type PropsWithChildren, useCallback } from 'react';
|
||||
import { memo, useState, useCallback } from 'react';
|
||||
import cc from 'classcat';
|
||||
import type { Rect } from '@xyflow/system';
|
||||
|
||||
import type { EdgeTextProps } from '../../types';
|
||||
|
||||
const EdgeText: FC<PropsWithChildren<EdgeTextProps>> = ({
|
||||
function EdgeTextComponent({
|
||||
x,
|
||||
y,
|
||||
label,
|
||||
@@ -16,7 +16,7 @@ const EdgeText: FC<PropsWithChildren<EdgeTextProps>> = ({
|
||||
children,
|
||||
className,
|
||||
...rest
|
||||
}) => {
|
||||
}: EdgeTextProps) {
|
||||
const [edgeTextBbox, setEdgeTextBbox] = useState<Rect>({ x: 1, y: 0, width: 0, height: 0 });
|
||||
const edgeTextClasses = cc(['react-flow__edge-textwrapper', className]);
|
||||
|
||||
@@ -68,5 +68,8 @@ const EdgeText: FC<PropsWithChildren<EdgeTextProps>> = ({
|
||||
{children}
|
||||
</g>
|
||||
);
|
||||
};
|
||||
export default memo(EdgeText);
|
||||
}
|
||||
|
||||
EdgeTextComponent.displayName = 'EdgeText';
|
||||
|
||||
export const EdgeText = memo(EdgeTextComponent);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { memo } from 'react';
|
||||
import { Position, getBezierEdgeCenter } from '@xyflow/system';
|
||||
|
||||
import BaseEdge from './BaseEdge';
|
||||
import { BaseEdge } from './BaseEdge';
|
||||
import type { SimpleBezierEdgeProps } from '../../types';
|
||||
|
||||
export interface GetSimpleBezierPathParams {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { memo } from 'react';
|
||||
import { Position, getSmoothStepPath } from '@xyflow/system';
|
||||
|
||||
import BaseEdge from './BaseEdge';
|
||||
import { BaseEdge } from './BaseEdge';
|
||||
import type { SmoothStepEdgeProps } from '../../types';
|
||||
|
||||
function createSmoothStepEdge(params: { isInternal: boolean }) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { memo } from 'react';
|
||||
import { getStraightPath } from '@xyflow/system';
|
||||
|
||||
import BaseEdge from './BaseEdge';
|
||||
import { BaseEdge } from './BaseEdge';
|
||||
import type { StraightEdgeProps } from '../../types';
|
||||
|
||||
function createStraightEdge(params: { isInternal: boolean }) {
|
||||
|
||||
@@ -47,7 +47,7 @@ const connectingSelector =
|
||||
};
|
||||
};
|
||||
|
||||
const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
const HandleComponent = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
(
|
||||
{
|
||||
type = 'source',
|
||||
@@ -216,6 +216,6 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
|
||||
}
|
||||
);
|
||||
|
||||
Handle.displayName = 'Handle';
|
||||
HandleComponent.displayName = 'Handle';
|
||||
|
||||
export default memo(Handle);
|
||||
export const Handle = memo(HandleComponent);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useEffect, useRef, memo, type MouseEvent, type KeyboardEvent } from 'react';
|
||||
import { useEffect, useRef, type MouseEvent, type KeyboardEvent } from 'react';
|
||||
import cc from 'classcat';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import {
|
||||
clampPosition,
|
||||
elementSelectionKeys,
|
||||
@@ -12,14 +13,13 @@ import {
|
||||
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 { useDrag } from '../../hooks/useDrag';
|
||||
import { useUpdateNodePositions } from '../../hooks/useUpdateNodePositions';
|
||||
import { handleNodeClick } from '../Nodes/utils';
|
||||
import type { NodeWrapperProps } from '../../types';
|
||||
import { arrowKeyDiffs, builtinNodeTypes } from './utils';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import type { NodeWrapperProps } from '../../types';
|
||||
|
||||
const NodeWrapper = ({
|
||||
export function NodeWrapper({
|
||||
id,
|
||||
onClick,
|
||||
onMouseEnter,
|
||||
@@ -40,7 +40,7 @@ const NodeWrapper = ({
|
||||
nodeExtent,
|
||||
nodeOrigin,
|
||||
onError,
|
||||
}: NodeWrapperProps) => {
|
||||
}: NodeWrapperProps) {
|
||||
const { node, positionAbsoluteX, positionAbsoluteY, zIndex, isParent } = useStore((s) => {
|
||||
const node = s.nodeLookup.get(id)!;
|
||||
|
||||
@@ -257,8 +257,4 @@ const NodeWrapper = ({
|
||||
</Provider>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
NodeWrapper.displayName = 'NodeWrapper';
|
||||
|
||||
export default memo(NodeWrapper);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
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 { 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> = {
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { memo } from 'react';
|
||||
import { Position, type NodeProps } from '@xyflow/system';
|
||||
|
||||
import Handle from '../../components/Handle';
|
||||
import { Handle } from '../../components/Handle';
|
||||
|
||||
const DefaultNode = ({
|
||||
export function DefaultNode({
|
||||
data,
|
||||
isConnectable,
|
||||
targetPosition = Position.Top,
|
||||
sourcePosition = Position.Bottom,
|
||||
}: NodeProps) => {
|
||||
}: NodeProps) {
|
||||
return (
|
||||
<>
|
||||
<Handle type="target" position={targetPosition} isConnectable={isConnectable} />
|
||||
@@ -16,8 +15,4 @@ const DefaultNode = ({
|
||||
<Handle type="source" position={sourcePosition} isConnectable={isConnectable} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
DefaultNode.displayName = 'DefaultNode';
|
||||
|
||||
export default memo(DefaultNode);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
const GroupNode = () => null;
|
||||
|
||||
GroupNode.displayName = 'GroupNode';
|
||||
|
||||
export default GroupNode;
|
||||
export function GroupNode() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { memo } from 'react';
|
||||
import { Position, type NodeProps } from '@xyflow/system';
|
||||
|
||||
import Handle from '../../components/Handle';
|
||||
import { Handle } from '../../components/Handle';
|
||||
|
||||
const InputNode = ({ data, isConnectable, sourcePosition = Position.Bottom }: NodeProps) => (
|
||||
<>
|
||||
{data?.label}
|
||||
<Handle type="source" position={sourcePosition} isConnectable={isConnectable} />
|
||||
</>
|
||||
);
|
||||
|
||||
InputNode.displayName = 'InputNode';
|
||||
|
||||
export default memo(InputNode);
|
||||
export function InputNode({ data, isConnectable, sourcePosition = Position.Bottom }: NodeProps) {
|
||||
return (
|
||||
<>
|
||||
{data?.label}
|
||||
<Handle type="source" position={sourcePosition} isConnectable={isConnectable} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { memo } from 'react';
|
||||
import { Position, type NodeProps } from '@xyflow/system';
|
||||
|
||||
import Handle from '../../components/Handle';
|
||||
import { Handle } from '../../components/Handle';
|
||||
|
||||
const OutputNode = ({ data, isConnectable, targetPosition = Position.Top }: NodeProps) => (
|
||||
<>
|
||||
<Handle type="target" position={targetPosition} isConnectable={isConnectable} />
|
||||
{data?.label}
|
||||
</>
|
||||
);
|
||||
|
||||
OutputNode.displayName = 'OutputNode';
|
||||
|
||||
export default memo(OutputNode);
|
||||
export function OutputNode({ data, isConnectable, targetPosition = Position.Top }: NodeProps) {
|
||||
return (
|
||||
<>
|
||||
<Handle type="target" position={targetPosition} isConnectable={isConnectable} />
|
||||
{data?.label}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { RefObject } from 'react';
|
||||
import type { StoreApi } from 'zustand';
|
||||
import { errorMessages } from '@xyflow/system';
|
||||
|
||||
import type { ReactFlowState } from '../../types';
|
||||
import { errorMessages } from '@xyflow/system';
|
||||
|
||||
// this handler is called by
|
||||
// 1. the click handler when node is not draggable or selectNodesOnDrag = false
|
||||
|
||||
@@ -3,16 +3,16 @@
|
||||
* made a selection with on or several nodes
|
||||
*/
|
||||
|
||||
import { memo, useRef, useEffect, type MouseEvent, type KeyboardEvent } from 'react';
|
||||
import { useRef, useEffect, type MouseEvent, type KeyboardEvent } from 'react';
|
||||
import cc from 'classcat';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { getNodesBounds } from '@xyflow/system';
|
||||
|
||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||
import useDrag from '../../hooks/useDrag';
|
||||
import useUpdateNodePositions from '../../hooks/useUpdateNodePositions';
|
||||
import type { Node, ReactFlowState } from '../../types';
|
||||
import { useDrag } from '../../hooks/useDrag';
|
||||
import { useUpdateNodePositions } from '../../hooks/useUpdateNodePositions';
|
||||
import { arrowKeyDiffs } from '../NodeWrapper/utils';
|
||||
import type { Node, ReactFlowState } from '../../types';
|
||||
|
||||
export type NodesSelectionProps = {
|
||||
onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void;
|
||||
@@ -32,7 +32,7 @@ const selector = (s: ReactFlowState) => {
|
||||
};
|
||||
};
|
||||
|
||||
function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboardA11y }: NodesSelectionProps) {
|
||||
export function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboardA11y }: NodesSelectionProps) {
|
||||
const store = useStoreApi();
|
||||
const { width, height, transformString, userSelectionActive } = useStore(selector, shallow);
|
||||
const updatePositions = useUpdateNodePositions();
|
||||
@@ -93,5 +93,3 @@ function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboar
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(NodesSelection);
|
||||
|
||||
@@ -6,13 +6,13 @@ import { useStore } from '../../hooks/useStore';
|
||||
import type { ReactFlowState } from '../../types';
|
||||
|
||||
export type PanelProps = HTMLAttributes<HTMLDivElement> & {
|
||||
position: PanelPosition;
|
||||
position?: PanelPosition;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const selector = (s: ReactFlowState) => (s.userSelectionActive ? 'none' : 'all');
|
||||
|
||||
function Panel({ position, children, className, style, ...rest }: PanelProps) {
|
||||
export function Panel({ position = 'top-left', children, className, style, ...rest }: PanelProps) {
|
||||
const pointerEvents = useStore(selector);
|
||||
const positionClasses = `${position}`.split('-');
|
||||
|
||||
@@ -26,5 +26,3 @@ function Panel({ position, children, className, style, ...rest }: PanelProps) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Panel;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Provider } from '../../contexts/RFStoreContext';
|
||||
import { createRFStore } from '../../store';
|
||||
import type { ReactFlowState, Node, Edge } from '../../types';
|
||||
|
||||
function ReactFlowProvider({
|
||||
export function ReactFlowProvider({
|
||||
children,
|
||||
initialNodes,
|
||||
initialEdges,
|
||||
@@ -35,7 +35,3 @@ function ReactFlowProvider({
|
||||
|
||||
return <Provider value={storeRef.current}>{children}</Provider>;
|
||||
}
|
||||
|
||||
ReactFlowProvider.displayName = 'ReactFlowProvider';
|
||||
|
||||
export default ReactFlowProvider;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* or is using the useOnSelectionChange hook.
|
||||
* @TODO: Now that we have the onNodesChange and on EdgesChange listeners, do we still need this component?
|
||||
*/
|
||||
import { memo, useEffect } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||
@@ -30,7 +30,7 @@ function areEqual(a: SelectorSlice, b: SelectorSlice) {
|
||||
);
|
||||
}
|
||||
|
||||
const SelectionListener = memo(({ onSelectionChange }: SelectionListenerProps) => {
|
||||
function SelectionListenerInner({ onSelectionChange }: SelectionListenerProps) {
|
||||
const store = useStoreApi();
|
||||
const { selectedNodes, selectedEdges } = useStore(selector, areEqual);
|
||||
|
||||
@@ -42,20 +42,16 @@ const SelectionListener = memo(({ onSelectionChange }: SelectionListenerProps) =
|
||||
}, [selectedNodes, selectedEdges, onSelectionChange]);
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
SelectionListener.displayName = 'SelectionListener';
|
||||
}
|
||||
|
||||
const changeSelector = (s: ReactFlowState) => !!s.onSelectionChangeHandlers;
|
||||
|
||||
function Wrapper({ onSelectionChange }: SelectionListenerProps) {
|
||||
export function SelectionListener({ onSelectionChange }: SelectionListenerProps) {
|
||||
const storeHasSelectionChangeHandlers = useStore(changeSelector);
|
||||
|
||||
if (onSelectionChange || storeHasSelectionChangeHandlers) {
|
||||
return <SelectionListener onSelectionChange={onSelectionChange} />;
|
||||
return <SelectionListenerInner onSelectionChange={onSelectionChange} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default Wrapper;
|
||||
|
||||
@@ -84,7 +84,7 @@ const selector = (s: ReactFlowState) => ({
|
||||
reset: s.reset,
|
||||
});
|
||||
|
||||
const StoreUpdater = (props: StoreUpdaterProps) => {
|
||||
export function StoreUpdater(props: StoreUpdaterProps) {
|
||||
const {
|
||||
setNodes,
|
||||
setEdges,
|
||||
@@ -149,6 +149,4 @@ const StoreUpdater = (props: StoreUpdaterProps) => {
|
||||
);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default StoreUpdater;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ const selector = (s: ReactFlowState) => ({
|
||||
userSelectionRect: s.userSelectionRect,
|
||||
});
|
||||
|
||||
function UserSelection() {
|
||||
export function UserSelection() {
|
||||
const { userSelectionActive, userSelectionRect } = useStore(selector, shallow);
|
||||
const isActive = userSelectionActive && userSelectionRect;
|
||||
|
||||
@@ -27,5 +27,3 @@ function UserSelection() {
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default UserSelection;
|
||||
|
||||
@@ -6,7 +6,7 @@ import type { ReactFlowState } from '../../types';
|
||||
|
||||
const selector = (s: ReactFlowState) => s.domNode?.querySelector('.react-flow__viewport-portal');
|
||||
|
||||
function ViewportPortal({ children }: { children: ReactNode }) {
|
||||
export function ViewportPortal({ children }: { children: ReactNode }) {
|
||||
const viewPortalDiv = useStore(selector);
|
||||
|
||||
if (!viewPortalDiv) {
|
||||
@@ -15,5 +15,3 @@ function ViewportPortal({ children }: { children: ReactNode }) {
|
||||
|
||||
return createPortal(children, viewPortalDiv);
|
||||
}
|
||||
|
||||
export default ViewportPortal;
|
||||
|
||||
@@ -57,5 +57,3 @@ export function useMarkerSymbol(type: MarkerType) {
|
||||
|
||||
return symbol;
|
||||
}
|
||||
|
||||
export default MarkerSymbols;
|
||||
|
||||
@@ -2,10 +2,10 @@ import { memo, ReactNode } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import { useStore } from '../../hooks/useStore';
|
||||
import useVisibleEdgeIds from '../../hooks/useVisibleEdgeIds';
|
||||
import { useVisibleEdgeIds } from '../../hooks/useVisibleEdgeIds';
|
||||
import MarkerDefinitions from './MarkerDefinitions';
|
||||
import { GraphViewProps } from '../GraphView';
|
||||
import EdgeWrapper from '../../components/EdgeWrapper';
|
||||
import { EdgeWrapper } from '../../components/EdgeWrapper';
|
||||
import type { ReactFlowState } from '../../types';
|
||||
|
||||
type EdgeRendererProps = Pick<
|
||||
@@ -27,7 +27,7 @@ type EdgeRendererProps = Pick<
|
||||
| 'disableKeyboardA11y'
|
||||
| 'edgeTypes'
|
||||
> & {
|
||||
children: ReactNode;
|
||||
children?: ReactNode;
|
||||
};
|
||||
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
@@ -40,7 +40,7 @@ const selector = (s: ReactFlowState) => ({
|
||||
onError: s.onError,
|
||||
});
|
||||
|
||||
const EdgeRenderer = ({
|
||||
function EdgeRendererComponent({
|
||||
defaultMarkerColor,
|
||||
onlyRenderVisibleElements,
|
||||
rfId,
|
||||
@@ -56,8 +56,7 @@ const EdgeRenderer = ({
|
||||
onEdgeDoubleClick,
|
||||
onEdgeUpdateStart,
|
||||
onEdgeUpdateEnd,
|
||||
children,
|
||||
}: EdgeRendererProps) => {
|
||||
}: EdgeRendererProps) {
|
||||
const { edgesFocusable, edgesUpdatable, elementsSelectable, onError } = useStore(selector, shallow);
|
||||
const edgeIds = useVisibleEdgeIds(onlyRenderVisibleElements);
|
||||
|
||||
@@ -90,11 +89,10 @@ const EdgeRenderer = ({
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
EdgeRenderer.displayName = 'EdgeRenderer';
|
||||
EdgeRendererComponent.displayName = 'EdgeRenderer';
|
||||
|
||||
export default memo(EdgeRenderer);
|
||||
export const EdgeRenderer = memo(EdgeRendererComponent);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { memo, type ReactNode } from 'react';
|
||||
|
||||
import { useStore } from '../../hooks/useStore';
|
||||
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler';
|
||||
import useKeyPress from '../../hooks/useKeyPress';
|
||||
import { useGlobalKeyHandler } from '../../hooks/useGlobalKeyHandler';
|
||||
import { useKeyPress } from '../../hooks/useKeyPress';
|
||||
import { GraphViewProps } from '../GraphView';
|
||||
import ZoomPane from '../ZoomPane';
|
||||
import Pane from '../Pane';
|
||||
import NodesSelection from '../../components/NodesSelection';
|
||||
import { ZoomPane } from '../ZoomPane';
|
||||
import { Pane } from '../Pane';
|
||||
import { NodesSelection } from '../../components/NodesSelection';
|
||||
import type { ReactFlowState } from '../../types';
|
||||
|
||||
export type FlowRendererProps = Omit<
|
||||
@@ -30,7 +30,7 @@ export type FlowRendererProps = Omit<
|
||||
|
||||
const selector = (s: ReactFlowState) => s.nodesSelectionActive;
|
||||
|
||||
const FlowRenderer = ({
|
||||
const FlowRendererComponent = ({
|
||||
children,
|
||||
onPaneClick,
|
||||
onPaneMouseEnter,
|
||||
@@ -125,6 +125,6 @@ const FlowRenderer = ({
|
||||
);
|
||||
};
|
||||
|
||||
FlowRenderer.displayName = 'FlowRenderer';
|
||||
FlowRendererComponent.displayName = 'FlowRenderer';
|
||||
|
||||
export default memo(FlowRenderer);
|
||||
export const FlowRenderer = memo(FlowRendererComponent);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { memo } from 'react';
|
||||
|
||||
import FlowRenderer from '../FlowRenderer';
|
||||
import NodeRenderer from '../NodeRenderer';
|
||||
import EdgeRenderer from '../EdgeRenderer';
|
||||
import ViewportWrapper from '../Viewport';
|
||||
import useOnInitHandler from '../../hooks/useOnInitHandler';
|
||||
import useViewportSync from '../../hooks/useViewportSync';
|
||||
import ConnectionLine from '../../components/ConnectionLine';
|
||||
import { FlowRenderer } from '../FlowRenderer';
|
||||
import { NodeRenderer } from '../NodeRenderer';
|
||||
import { EdgeRenderer } from '../EdgeRenderer';
|
||||
import { Viewport } from '../Viewport';
|
||||
import { useOnInitHandler } from '../../hooks/useOnInitHandler';
|
||||
import { useViewportSync } from '../../hooks/useViewportSync';
|
||||
import { ConnectionLineWrapper } from '../../components/ConnectionLine';
|
||||
import { useNodeOrEdgeTypesWarning } from './useNodeOrEdgeTypesWarning';
|
||||
import type { ReactFlowProps } from '../../types';
|
||||
import useNodeOrEdgeTypesWarning from './useNodeOrEdgeTypesWarning';
|
||||
|
||||
export type GraphViewProps = Omit<
|
||||
ReactFlowProps,
|
||||
@@ -38,7 +38,7 @@ export type GraphViewProps = Omit<
|
||||
rfId: string;
|
||||
};
|
||||
|
||||
const GraphView = ({
|
||||
function GraphViewComponent({
|
||||
nodeTypes,
|
||||
edgeTypes,
|
||||
onInit,
|
||||
@@ -102,7 +102,7 @@ const GraphView = ({
|
||||
rfId,
|
||||
viewport,
|
||||
onViewportChange,
|
||||
}: GraphViewProps) => {
|
||||
}: GraphViewProps) {
|
||||
useNodeOrEdgeTypesWarning(nodeTypes);
|
||||
useNodeOrEdgeTypesWarning(edgeTypes);
|
||||
|
||||
@@ -147,7 +147,7 @@ const GraphView = ({
|
||||
onViewportChange={onViewportChange}
|
||||
isControlledViewport={!!viewport}
|
||||
>
|
||||
<ViewportWrapper>
|
||||
<Viewport>
|
||||
<EdgeRenderer
|
||||
edgeTypes={edgeTypes}
|
||||
onEdgeClick={onEdgeClick}
|
||||
@@ -165,14 +165,13 @@ const GraphView = ({
|
||||
noPanClassName={noPanClassName}
|
||||
disableKeyboardA11y={disableKeyboardA11y}
|
||||
rfId={rfId}
|
||||
>
|
||||
<ConnectionLine
|
||||
style={connectionLineStyle}
|
||||
type={connectionLineType}
|
||||
component={connectionLineComponent}
|
||||
containerStyle={connectionLineContainerStyle}
|
||||
/>
|
||||
</EdgeRenderer>
|
||||
/>
|
||||
<ConnectionLineWrapper
|
||||
style={connectionLineStyle}
|
||||
type={connectionLineType}
|
||||
component={connectionLineComponent}
|
||||
containerStyle={connectionLineContainerStyle}
|
||||
/>
|
||||
<div className="react-flow__edgelabel-renderer" />
|
||||
<div className="react-flow__viewport-portal" />
|
||||
<NodeRenderer
|
||||
@@ -191,11 +190,11 @@ const GraphView = ({
|
||||
nodeExtent={nodeExtent}
|
||||
rfId={rfId}
|
||||
/>
|
||||
</ViewportWrapper>
|
||||
</Viewport>
|
||||
</FlowRenderer>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
GraphView.displayName = 'GraphView';
|
||||
GraphViewComponent.displayName = 'GraphView';
|
||||
|
||||
export default memo(GraphView);
|
||||
export const GraphView = memo(GraphViewComponent);
|
||||
|
||||
@@ -12,7 +12,7 @@ const emptyTypes = {};
|
||||
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 {
|
||||
export function useNodeOrEdgeTypesWarning(nodeOrEdgeTypes: any = emptyTypes): any {
|
||||
const updateCount = useRef(0);
|
||||
const store = useStoreApi();
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { memo } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
|
||||
import useVisibleNodesIds from '../../hooks/useVisibleNodeIds';
|
||||
import { useVisibleNodeIds } from '../../hooks/useVisibleNodeIds';
|
||||
import { useStore } from '../../hooks/useStore';
|
||||
import { containerStyle } from '../../styles/utils';
|
||||
import { GraphViewProps } from '../GraphView';
|
||||
import { useResizeObserver } from './useResizeObserver';
|
||||
import { NodeWrapper } from '../../components/NodeWrapper';
|
||||
import type { ReactFlowState } from '../../types';
|
||||
import useResizeObserver from './useResizeObserver';
|
||||
import NodeWrapper from '../../components/NodeWrapper';
|
||||
|
||||
export type NodeRendererProps = Pick<
|
||||
GraphViewProps,
|
||||
@@ -35,9 +35,9 @@ const selector = (s: ReactFlowState) => ({
|
||||
onError: s.onError,
|
||||
});
|
||||
|
||||
const NodeRenderer = (props: NodeRendererProps) => {
|
||||
const NodeRendererComponent = (props: NodeRendererProps) => {
|
||||
const { nodesDraggable, nodesConnectable, nodesFocusable, elementsSelectable, onError } = useStore(selector, shallow);
|
||||
const nodeIds = useVisibleNodesIds(props.onlyRenderVisibleElements);
|
||||
const nodeIds = useVisibleNodeIds(props.onlyRenderVisibleElements);
|
||||
const resizeObserver = useResizeObserver();
|
||||
|
||||
return (
|
||||
@@ -96,6 +96,6 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
NodeRenderer.displayName = 'NodeRenderer';
|
||||
NodeRendererComponent.displayName = 'NodeRenderer';
|
||||
|
||||
export default memo(NodeRenderer);
|
||||
export const NodeRenderer = memo(NodeRendererComponent);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useStore } from '../../hooks/useStore';
|
||||
|
||||
const selector = (s: ReactFlowState) => s.updateNodeDimensions;
|
||||
|
||||
export default function useResizeObserver() {
|
||||
export function useResizeObserver() {
|
||||
const updateNodeDimensions = useStore(selector);
|
||||
const resizeObserverRef = useRef<ResizeObserver>();
|
||||
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
* The user selection rectangle gets displayed when a user drags the mouse while pressing shift
|
||||
*/
|
||||
|
||||
import { memo, useRef, type MouseEvent as ReactMouseEvent, type ReactNode } from 'react';
|
||||
import { useRef, type MouseEvent as ReactMouseEvent, type ReactNode } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import cc from 'classcat';
|
||||
import { getNodesInside, getEventPosition, SelectionMode } from '@xyflow/system';
|
||||
|
||||
import UserSelection from '../../components/UserSelection';
|
||||
import { UserSelection } from '../../components/UserSelection';
|
||||
import { containerStyle } from '../../styles/utils';
|
||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||
import { getSelectionChanges } from '../../utils';
|
||||
@@ -50,196 +50,190 @@ const selector = (s: ReactFlowState) => ({
|
||||
dragging: s.paneDragging,
|
||||
});
|
||||
|
||||
const Pane = memo(
|
||||
({
|
||||
isSelecting,
|
||||
selectionMode = SelectionMode.Full,
|
||||
panOnDrag,
|
||||
onSelectionStart,
|
||||
onSelectionEnd,
|
||||
onPaneClick,
|
||||
onPaneContextMenu,
|
||||
onPaneScroll,
|
||||
onPaneMouseEnter,
|
||||
onPaneMouseMove,
|
||||
onPaneMouseLeave,
|
||||
children,
|
||||
}: PaneProps) => {
|
||||
const container = useRef<HTMLDivElement | null>(null);
|
||||
const store = useStoreApi();
|
||||
const prevSelectedNodesCount = useRef<number>(0);
|
||||
const prevSelectedEdgesCount = useRef<number>(0);
|
||||
const containerBounds = useRef<DOMRect>();
|
||||
const { userSelectionActive, elementsSelectable, dragging } = useStore(selector, shallow);
|
||||
export function Pane({
|
||||
isSelecting,
|
||||
selectionMode = SelectionMode.Full,
|
||||
panOnDrag,
|
||||
onSelectionStart,
|
||||
onSelectionEnd,
|
||||
onPaneClick,
|
||||
onPaneContextMenu,
|
||||
onPaneScroll,
|
||||
onPaneMouseEnter,
|
||||
onPaneMouseMove,
|
||||
onPaneMouseLeave,
|
||||
children,
|
||||
}: PaneProps) {
|
||||
const container = useRef<HTMLDivElement | null>(null);
|
||||
const store = useStoreApi();
|
||||
const prevSelectedNodesCount = useRef<number>(0);
|
||||
const prevSelectedEdgesCount = useRef<number>(0);
|
||||
const containerBounds = useRef<DOMRect>();
|
||||
const { userSelectionActive, elementsSelectable, dragging } = useStore(selector, shallow);
|
||||
|
||||
const resetUserSelection = () => {
|
||||
store.setState({ userSelectionActive: false, userSelectionRect: null });
|
||||
const resetUserSelection = () => {
|
||||
store.setState({ userSelectionActive: false, userSelectionRect: null });
|
||||
|
||||
prevSelectedNodesCount.current = 0;
|
||||
prevSelectedEdgesCount.current = 0;
|
||||
prevSelectedNodesCount.current = 0;
|
||||
prevSelectedEdgesCount.current = 0;
|
||||
};
|
||||
|
||||
const onClick = (event: ReactMouseEvent) => {
|
||||
onPaneClick?.(event);
|
||||
store.getState().resetSelectedElements();
|
||||
store.setState({ nodesSelectionActive: false });
|
||||
};
|
||||
|
||||
const onContextMenu = (event: ReactMouseEvent) => {
|
||||
if (Array.isArray(panOnDrag) && panOnDrag?.includes(2)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
onPaneContextMenu?.(event);
|
||||
};
|
||||
|
||||
const onWheel = onPaneScroll ? (event: React.WheelEvent) => onPaneScroll(event) : undefined;
|
||||
|
||||
const onMouseDown = (event: ReactMouseEvent): void => {
|
||||
const { resetSelectedElements, domNode } = store.getState();
|
||||
containerBounds.current = domNode?.getBoundingClientRect();
|
||||
|
||||
if (
|
||||
!elementsSelectable ||
|
||||
!isSelecting ||
|
||||
event.button !== 0 ||
|
||||
event.target !== container.current ||
|
||||
!containerBounds.current
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { x, y } = getEventPosition(event.nativeEvent, containerBounds.current);
|
||||
|
||||
resetSelectedElements();
|
||||
|
||||
store.setState({
|
||||
userSelectionRect: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
startX: x,
|
||||
startY: y,
|
||||
x,
|
||||
y,
|
||||
},
|
||||
});
|
||||
|
||||
onSelectionStart?.(event);
|
||||
};
|
||||
|
||||
const onMouseMove = (event: ReactMouseEvent): void => {
|
||||
const { userSelectionRect, edges, transform, nodeOrigin, nodes, onNodesChange, onEdgesChange } = store.getState();
|
||||
if (!isSelecting || !containerBounds.current || !userSelectionRect) {
|
||||
return;
|
||||
}
|
||||
|
||||
store.setState({ userSelectionActive: true, nodesSelectionActive: false });
|
||||
|
||||
const mousePos = getEventPosition(event.nativeEvent, containerBounds.current);
|
||||
const startX = userSelectionRect.startX ?? 0;
|
||||
const startY = userSelectionRect.startY ?? 0;
|
||||
|
||||
const nextUserSelectRect = {
|
||||
...userSelectionRect,
|
||||
x: mousePos.x < startX ? mousePos.x : startX,
|
||||
y: mousePos.y < startY ? mousePos.y : startY,
|
||||
width: Math.abs(mousePos.x - startX),
|
||||
height: Math.abs(mousePos.y - startY),
|
||||
};
|
||||
|
||||
const onClick = (event: ReactMouseEvent) => {
|
||||
onPaneClick?.(event);
|
||||
store.getState().resetSelectedElements();
|
||||
store.setState({ nodesSelectionActive: false });
|
||||
};
|
||||
|
||||
const onContextMenu = (event: ReactMouseEvent) => {
|
||||
if (Array.isArray(panOnDrag) && panOnDrag?.includes(2)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
onPaneContextMenu?.(event);
|
||||
};
|
||||
|
||||
const onWheel = onPaneScroll ? (event: React.WheelEvent) => onPaneScroll(event) : undefined;
|
||||
|
||||
const onMouseDown = (event: ReactMouseEvent): void => {
|
||||
const { resetSelectedElements, domNode } = store.getState();
|
||||
containerBounds.current = domNode?.getBoundingClientRect();
|
||||
|
||||
if (
|
||||
!elementsSelectable ||
|
||||
!isSelecting ||
|
||||
event.button !== 0 ||
|
||||
event.target !== container.current ||
|
||||
!containerBounds.current
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { x, y } = getEventPosition(event.nativeEvent, containerBounds.current);
|
||||
|
||||
resetSelectedElements();
|
||||
|
||||
store.setState({
|
||||
userSelectionRect: {
|
||||
width: 0,
|
||||
height: 0,
|
||||
startX: x,
|
||||
startY: y,
|
||||
x,
|
||||
y,
|
||||
},
|
||||
});
|
||||
|
||||
onSelectionStart?.(event);
|
||||
};
|
||||
|
||||
const onMouseMove = (event: ReactMouseEvent): void => {
|
||||
const { userSelectionRect, edges, transform, nodeOrigin, nodes, onNodesChange, onEdgesChange } = store.getState();
|
||||
if (!isSelecting || !containerBounds.current || !userSelectionRect) {
|
||||
return;
|
||||
}
|
||||
|
||||
store.setState({ userSelectionActive: true, nodesSelectionActive: false });
|
||||
|
||||
const mousePos = getEventPosition(event.nativeEvent, containerBounds.current);
|
||||
const startX = userSelectionRect.startX ?? 0;
|
||||
const startY = userSelectionRect.startY ?? 0;
|
||||
|
||||
const nextUserSelectRect = {
|
||||
...userSelectionRect,
|
||||
x: mousePos.x < startX ? mousePos.x : startX,
|
||||
y: mousePos.y < startY ? mousePos.y : startY,
|
||||
width: Math.abs(mousePos.x - startX),
|
||||
height: Math.abs(mousePos.y - startY),
|
||||
};
|
||||
|
||||
const selectedNodes = getNodesInside(
|
||||
nodes,
|
||||
nextUserSelectRect,
|
||||
transform,
|
||||
selectionMode === SelectionMode.Partial,
|
||||
true,
|
||||
nodeOrigin
|
||||
);
|
||||
|
||||
const selectedEdgeIds = new Set<string>();
|
||||
const selectedNodeIds = new Set<string>();
|
||||
|
||||
for (const selectedNode of selectedNodes) {
|
||||
selectedNodeIds.add(selectedNode.id);
|
||||
|
||||
for (const edge of edges) {
|
||||
if (edge.source === selectedNode.id || edge.target === selectedNode.id) {
|
||||
selectedEdgeIds.add(edge.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prevSelectedNodesCount.current !== selectedNodeIds.size) {
|
||||
prevSelectedNodesCount.current = selectedNodeIds.size;
|
||||
const changes = getSelectionChanges(nodes, selectedNodeIds, true) as NodeChange[];
|
||||
if (changes.length) {
|
||||
onNodesChange?.(changes);
|
||||
}
|
||||
}
|
||||
|
||||
if (prevSelectedEdgesCount.current !== selectedEdgeIds.size) {
|
||||
prevSelectedEdgesCount.current = selectedEdgeIds.size;
|
||||
const changes = getSelectionChanges(edges, selectedEdgeIds) as EdgeChange[];
|
||||
if (changes.length) {
|
||||
onEdgesChange?.(changes);
|
||||
}
|
||||
}
|
||||
|
||||
store.setState({
|
||||
userSelectionRect: nextUserSelectRect,
|
||||
});
|
||||
};
|
||||
|
||||
const onMouseUp = (event: ReactMouseEvent) => {
|
||||
if (event.button !== 0) {
|
||||
return;
|
||||
}
|
||||
const { userSelectionRect } = store.getState();
|
||||
// We only want to trigger click functions when in selection mode if
|
||||
// the user did not move the mouse.
|
||||
if (!userSelectionActive && userSelectionRect && event.target === container.current) {
|
||||
onClick?.(event);
|
||||
}
|
||||
|
||||
store.setState({ nodesSelectionActive: prevSelectedNodesCount.current > 0 });
|
||||
|
||||
resetUserSelection();
|
||||
onSelectionEnd?.(event);
|
||||
};
|
||||
|
||||
const onMouseLeave = (event: ReactMouseEvent) => {
|
||||
if (userSelectionActive) {
|
||||
store.setState({ nodesSelectionActive: prevSelectedNodesCount.current > 0 });
|
||||
onSelectionEnd?.(event);
|
||||
}
|
||||
|
||||
resetUserSelection();
|
||||
};
|
||||
|
||||
const hasActiveSelection = elementsSelectable && (isSelecting || userSelectionActive);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cc(['react-flow__pane', { dragging, selection: isSelecting }])}
|
||||
onClick={hasActiveSelection ? undefined : wrapHandler(onClick, container)}
|
||||
onContextMenu={wrapHandler(onContextMenu, container)}
|
||||
onWheel={wrapHandler(onWheel, container)}
|
||||
onMouseEnter={hasActiveSelection ? undefined : onPaneMouseEnter}
|
||||
onMouseDown={hasActiveSelection ? onMouseDown : undefined}
|
||||
onMouseMove={hasActiveSelection ? onMouseMove : onPaneMouseMove}
|
||||
onMouseUp={hasActiveSelection ? onMouseUp : undefined}
|
||||
onMouseLeave={hasActiveSelection ? onMouseLeave : onPaneMouseLeave}
|
||||
ref={container}
|
||||
style={containerStyle}
|
||||
>
|
||||
{children}
|
||||
<UserSelection />
|
||||
</div>
|
||||
const selectedNodes = getNodesInside(
|
||||
nodes,
|
||||
nextUserSelectRect,
|
||||
transform,
|
||||
selectionMode === SelectionMode.Partial,
|
||||
true,
|
||||
nodeOrigin
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
Pane.displayName = 'Pane';
|
||||
const selectedEdgeIds = new Set<string>();
|
||||
const selectedNodeIds = new Set<string>();
|
||||
|
||||
export default Pane;
|
||||
for (const selectedNode of selectedNodes) {
|
||||
selectedNodeIds.add(selectedNode.id);
|
||||
|
||||
for (const edge of edges) {
|
||||
if (edge.source === selectedNode.id || edge.target === selectedNode.id) {
|
||||
selectedEdgeIds.add(edge.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (prevSelectedNodesCount.current !== selectedNodeIds.size) {
|
||||
prevSelectedNodesCount.current = selectedNodeIds.size;
|
||||
const changes = getSelectionChanges(nodes, selectedNodeIds, true) as NodeChange[];
|
||||
if (changes.length) {
|
||||
onNodesChange?.(changes);
|
||||
}
|
||||
}
|
||||
|
||||
if (prevSelectedEdgesCount.current !== selectedEdgeIds.size) {
|
||||
prevSelectedEdgesCount.current = selectedEdgeIds.size;
|
||||
const changes = getSelectionChanges(edges, selectedEdgeIds) as EdgeChange[];
|
||||
if (changes.length) {
|
||||
onEdgesChange?.(changes);
|
||||
}
|
||||
}
|
||||
|
||||
store.setState({
|
||||
userSelectionRect: nextUserSelectRect,
|
||||
});
|
||||
};
|
||||
|
||||
const onMouseUp = (event: ReactMouseEvent) => {
|
||||
if (event.button !== 0) {
|
||||
return;
|
||||
}
|
||||
const { userSelectionRect } = store.getState();
|
||||
// We only want to trigger click functions when in selection mode if
|
||||
// the user did not move the mouse.
|
||||
if (!userSelectionActive && userSelectionRect && event.target === container.current) {
|
||||
onClick?.(event);
|
||||
}
|
||||
|
||||
store.setState({ nodesSelectionActive: prevSelectedNodesCount.current > 0 });
|
||||
|
||||
resetUserSelection();
|
||||
onSelectionEnd?.(event);
|
||||
};
|
||||
|
||||
const onMouseLeave = (event: ReactMouseEvent) => {
|
||||
if (userSelectionActive) {
|
||||
store.setState({ nodesSelectionActive: prevSelectedNodesCount.current > 0 });
|
||||
onSelectionEnd?.(event);
|
||||
}
|
||||
|
||||
resetUserSelection();
|
||||
};
|
||||
|
||||
const hasActiveSelection = elementsSelectable && (isSelecting || userSelectionActive);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cc(['react-flow__pane', { dragging, selection: isSelecting }])}
|
||||
onClick={hasActiveSelection ? undefined : wrapHandler(onClick, container)}
|
||||
onContextMenu={wrapHandler(onContextMenu, container)}
|
||||
onWheel={wrapHandler(onWheel, container)}
|
||||
onMouseEnter={hasActiveSelection ? undefined : onPaneMouseEnter}
|
||||
onMouseDown={hasActiveSelection ? onMouseDown : undefined}
|
||||
onMouseMove={hasActiveSelection ? onMouseMove : onPaneMouseMove}
|
||||
onMouseUp={hasActiveSelection ? onMouseUp : undefined}
|
||||
onMouseLeave={hasActiveSelection ? onMouseLeave : onPaneMouseLeave}
|
||||
ref={container}
|
||||
style={containerStyle}
|
||||
>
|
||||
{children}
|
||||
<UserSelection />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useContext, type ReactNode } from 'react';
|
||||
|
||||
import StoreContext from '../../contexts/RFStoreContext';
|
||||
import ReactFlowProvider from '../../components/ReactFlowProvider';
|
||||
import { ReactFlowProvider } from '../../components/ReactFlowProvider';
|
||||
import type { Node, Edge } from '../../types';
|
||||
|
||||
function Wrapper({
|
||||
export function Wrapper({
|
||||
children,
|
||||
nodes,
|
||||
edges,
|
||||
@@ -39,7 +39,3 @@ function Wrapper({
|
||||
</ReactFlowProvider>
|
||||
);
|
||||
}
|
||||
|
||||
Wrapper.displayName = 'ReactFlowWrapper';
|
||||
|
||||
export default Wrapper;
|
||||
|
||||
@@ -5,20 +5,19 @@ import {
|
||||
PanOnScrollMode,
|
||||
SelectionMode,
|
||||
infiniteExtent,
|
||||
isMacOs,
|
||||
type NodeOrigin,
|
||||
type Viewport,
|
||||
isMacOs,
|
||||
} from '@xyflow/system';
|
||||
|
||||
import Attribution from '../../components/Attribution';
|
||||
|
||||
import SelectionListener from '../../components/SelectionListener';
|
||||
import StoreUpdater from '../../components/StoreUpdater';
|
||||
import A11yDescriptions from '../../components/A11yDescriptions';
|
||||
import GraphView from '../GraphView';
|
||||
import Wrapper from './Wrapper';
|
||||
import { A11yDescriptions } from '../../components/A11yDescriptions';
|
||||
import { Attribution } from '../../components/Attribution';
|
||||
import { SelectionListener } from '../../components/SelectionListener';
|
||||
import { StoreUpdater } from '../../components/StoreUpdater';
|
||||
import { useColorModeClass } from '../../hooks/useColorModeClass';
|
||||
import { GraphView } from '../GraphView';
|
||||
import { Wrapper } from './Wrapper';
|
||||
import type { ReactFlowProps, ReactFlowRefType } from '../../types';
|
||||
import useColorModeClass from '../../hooks/useColorModeClass';
|
||||
|
||||
export const initNodeOrigin: NodeOrigin = [0, 0];
|
||||
const initDefaultViewport: Viewport = { x: 0, y: 0, zoom: 1 };
|
||||
|
||||
@@ -9,7 +9,7 @@ type ViewportProps = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export default function Viewport({ children }: ViewportProps) {
|
||||
export function Viewport({ children }: ViewportProps) {
|
||||
const transform = useStore(selector);
|
||||
|
||||
return (
|
||||
|
||||
@@ -3,8 +3,8 @@ import { useEffect, useRef } from 'react';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { XYPanZoom, PanOnScrollMode, type Transform, type PanZoomInstance } from '@xyflow/system';
|
||||
|
||||
import useKeyPress from '../../hooks/useKeyPress';
|
||||
import useResizeHandler from '../../hooks/useResizeHandler';
|
||||
import { useKeyPress } from '../../hooks/useKeyPress';
|
||||
import { useResizeHandler } from '../../hooks/useResizeHandler';
|
||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||
import { containerStyle } from '../../styles/utils';
|
||||
import type { FlowRendererProps } from '../FlowRenderer';
|
||||
@@ -27,7 +27,7 @@ const selector = (s: ReactFlowState) => ({
|
||||
lib: s.lib,
|
||||
});
|
||||
|
||||
const ZoomPane = ({
|
||||
export function ZoomPane({
|
||||
onPaneContextMenu,
|
||||
zoomOnScroll = true,
|
||||
zoomOnPinch = true,
|
||||
@@ -47,7 +47,7 @@ const ZoomPane = ({
|
||||
noPanClassName,
|
||||
onViewportChange,
|
||||
isControlledViewport,
|
||||
}: ZoomPaneProps) => {
|
||||
}: ZoomPaneProps) {
|
||||
const store = useStoreApi();
|
||||
const zoomPane = useRef<HTMLDivElement>(null);
|
||||
const { userSelectionActive, lib } = useStore(selector, shallow);
|
||||
@@ -142,6 +142,4 @@ const ZoomPane = ({
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ZoomPane;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ function getMediaQuery() {
|
||||
* @internal
|
||||
* @param colorMode - The color mode to use ('dark', 'light' or 'system')
|
||||
*/
|
||||
export default function useColorModeClass(colorMode: ColorMode): ColorModeClass {
|
||||
export function useColorModeClass(colorMode: ColorMode): ColorModeClass {
|
||||
const [colorModeClass, setColorModeClass] = useState<ColorModeClass | null>(
|
||||
colorMode === 'system' ? null : colorMode
|
||||
);
|
||||
|
||||
@@ -18,7 +18,14 @@ type UseDragParams = {
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function useDrag({ nodeRef, disabled = false, noDragClassName, handleSelector, nodeId, isSelectable }: UseDragParams) {
|
||||
export function useDrag({
|
||||
nodeRef,
|
||||
disabled = false,
|
||||
noDragClassName,
|
||||
handleSelector,
|
||||
nodeId,
|
||||
isSelectable,
|
||||
}: UseDragParams) {
|
||||
const store = useStoreApi();
|
||||
const [dragging, setDragging] = useState<boolean>(false);
|
||||
const xyDrag = useRef<XYDragInstance>();
|
||||
@@ -64,5 +71,3 @@ function useDrag({ nodeRef, disabled = false, noDragClassName, handleSelector, n
|
||||
|
||||
return dragging;
|
||||
}
|
||||
|
||||
export default useDrag;
|
||||
|
||||
@@ -11,10 +11,8 @@ const edgesSelector = (state: ReactFlowState) => state.edges;
|
||||
* @public
|
||||
* @returns An array of edges
|
||||
*/
|
||||
function useEdges<EdgeData>(): Edge<EdgeData>[] {
|
||||
export function useEdges<EdgeData>(): Edge<EdgeData>[] {
|
||||
const edges = useStore(edgesSelector, shallow);
|
||||
|
||||
return edges;
|
||||
}
|
||||
|
||||
export default useEdges;
|
||||
|
||||
@@ -2,8 +2,8 @@ import { useEffect } from 'react';
|
||||
import type { KeyCode } from '@xyflow/system';
|
||||
|
||||
import { useStoreApi } from '../hooks/useStore';
|
||||
import useKeyPress, { UseKeyPressOptions } from './useKeyPress';
|
||||
import useReactFlow from './useReactFlow';
|
||||
import { useKeyPress, UseKeyPressOptions } from './useKeyPress';
|
||||
import { useReactFlow } from './useReactFlow';
|
||||
import { Edge, Node } from '../types';
|
||||
|
||||
const selected = (item: Node | Edge) => item.selected;
|
||||
@@ -15,13 +15,13 @@ const deleteKeyOptions: UseKeyPressOptions = { actInsideInputWithModifier: false
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
export default ({
|
||||
export function useGlobalKeyHandler({
|
||||
deleteKeyCode,
|
||||
multiSelectionKeyCode,
|
||||
}: {
|
||||
deleteKeyCode: KeyCode | null;
|
||||
multiSelectionKeyCode: KeyCode | null;
|
||||
}): void => {
|
||||
}): void {
|
||||
const store = useStoreApi();
|
||||
const { deleteElements } = useReactFlow();
|
||||
|
||||
@@ -39,4 +39,4 @@ export default ({
|
||||
useEffect(() => {
|
||||
store.setState({ multiSelectionActive: multiSelectionKeyPressed });
|
||||
}, [multiSelectionKeyPressed]);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -20,14 +20,14 @@ const defaultDoc = typeof document !== 'undefined' ? document : null;
|
||||
* @param param.options - Options
|
||||
* @returns boolean
|
||||
*/
|
||||
export default (
|
||||
export function useKeyPress(
|
||||
// the keycode can be a string 'a' or an array of strings ['a', 'a+d']
|
||||
// a string means a single key 'a' or a combination when '+' is used 'a+d'
|
||||
// an array means different possibilites. Explainer: ['a', 'd+s'] here the
|
||||
// user can use the single key 'a' or the combination 'd' + 's'
|
||||
keyCode: KeyCode | null = null,
|
||||
options: UseKeyPressOptions = { target: defaultDoc, actInsideInputWithModifier: true }
|
||||
): boolean => {
|
||||
): boolean {
|
||||
const [keyPressed, setKeyPressed] = useState(false);
|
||||
|
||||
// we need to remember if a modifier key is pressed in order to track it
|
||||
@@ -113,7 +113,7 @@ export default (
|
||||
}, [keyCode, setKeyPressed]);
|
||||
|
||||
return keyPressed;
|
||||
};
|
||||
}
|
||||
|
||||
// utils
|
||||
|
||||
|
||||
@@ -11,10 +11,8 @@ const nodesSelector = (state: ReactFlowState) => state.nodes;
|
||||
* @public
|
||||
* @returns An array of nodes
|
||||
*/
|
||||
function useNodes<NodeType extends Node = Node>(): NodeType[] {
|
||||
export function useNodes<NodeType extends Node = Node>(): NodeType[] {
|
||||
const nodes = useStore(nodesSelector, shallow) as NodeType[];
|
||||
|
||||
return nodes;
|
||||
}
|
||||
|
||||
export default useNodes;
|
||||
|
||||
@@ -28,10 +28,8 @@ const defaultOptions = {
|
||||
* @param options.includeHiddenNodes - defaults to false
|
||||
* @returns boolean indicating whether all nodes are initialized
|
||||
*/
|
||||
function useNodesInitialized(options: UseNodesInitializedOptions = defaultOptions): boolean {
|
||||
export function useNodesInitialized(options: UseNodesInitializedOptions = defaultOptions): boolean {
|
||||
const initialized = useStore(selector(options));
|
||||
|
||||
return initialized;
|
||||
}
|
||||
|
||||
export default useNodesInitialized;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
|
||||
import useReactFlow from './useReactFlow';
|
||||
import { useReactFlow } from './useReactFlow';
|
||||
import type { OnInit } from '../types';
|
||||
|
||||
/**
|
||||
@@ -8,7 +8,7 @@ import type { OnInit } from '../types';
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function useOnInitHandler(onInit: OnInit | undefined) {
|
||||
export function useOnInitHandler(onInit: OnInit | undefined) {
|
||||
const rfInstance = useReactFlow();
|
||||
const isInitialized = useRef<boolean>(false);
|
||||
|
||||
@@ -19,5 +19,3 @@ function useOnInitHandler(onInit: OnInit | undefined) {
|
||||
}
|
||||
}, [onInit, rfInstance.viewportInitialized]);
|
||||
}
|
||||
|
||||
export default useOnInitHandler;
|
||||
|
||||
@@ -13,7 +13,7 @@ export type UseOnSelectionChangeOptions = {
|
||||
* @public
|
||||
* @params params.onChange - The handler to register
|
||||
*/
|
||||
function useOnSelectionChange({ onChange }: UseOnSelectionChangeOptions) {
|
||||
export function useOnSelectionChange({ onChange }: UseOnSelectionChangeOptions) {
|
||||
const store = useStoreApi();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -26,5 +26,3 @@ function useOnSelectionChange({ onChange }: UseOnSelectionChangeOptions) {
|
||||
};
|
||||
}, [onChange]);
|
||||
}
|
||||
|
||||
export default useOnSelectionChange;
|
||||
|
||||
@@ -17,7 +17,7 @@ export type UseOnViewportChangeOptions = {
|
||||
* @param params.onChange - gets called when the viewport changes
|
||||
* @param params.onEnd - gets called when the viewport stops changing
|
||||
*/
|
||||
function useOnViewportChange({ onStart, onChange, onEnd }: UseOnViewportChangeOptions) {
|
||||
export function useOnViewportChange({ onStart, onChange, onEnd }: UseOnViewportChangeOptions) {
|
||||
const store = useStoreApi();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -32,5 +32,3 @@ function useOnViewportChange({ onStart, onChange, onEnd }: UseOnViewportChangeOp
|
||||
store.setState({ onViewportChangeEnd: onEnd });
|
||||
}, [onEnd]);
|
||||
}
|
||||
|
||||
export default useOnViewportChange;
|
||||
|
||||
@@ -23,7 +23,7 @@ import { isNode } from '../utils';
|
||||
* @public
|
||||
* @returns ReactFlowInstance
|
||||
*/
|
||||
export default function useReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(): ReactFlowInstance<
|
||||
export function useReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(): ReactFlowInstance<
|
||||
NodeType,
|
||||
EdgeType
|
||||
> {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { useStoreApi } from '../hooks/useStore';
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
function useResizeHandler(domNode: MutableRefObject<HTMLDivElement | null>): void {
|
||||
export function useResizeHandler(domNode: MutableRefObject<HTMLDivElement | null>): void {
|
||||
const store = useStoreApi();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -42,5 +42,3 @@ function useResizeHandler(domNode: MutableRefObject<HTMLDivElement | null>): voi
|
||||
}
|
||||
}, []);
|
||||
}
|
||||
|
||||
export default useResizeHandler;
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useStoreApi } from '../hooks/useStore';
|
||||
* @public
|
||||
* @returns function for updating node internals
|
||||
*/
|
||||
function useUpdateNodeInternals(): UpdateNodeInternals {
|
||||
export function useUpdateNodeInternals(): UpdateNodeInternals {
|
||||
const store = useStoreApi();
|
||||
|
||||
return useCallback<UpdateNodeInternals>((id: string | string[]) => {
|
||||
@@ -28,5 +28,3 @@ function useUpdateNodeInternals(): UpdateNodeInternals {
|
||||
requestAnimationFrame(() => updateNodeDimensions(updates));
|
||||
}, []);
|
||||
}
|
||||
|
||||
export default useUpdateNodeInternals;
|
||||
|
||||
@@ -13,7 +13,7 @@ const selectedAndDraggable = (nodesDraggable: boolean) => (n: Node) =>
|
||||
* @internal
|
||||
* @returns function for updating node positions
|
||||
*/
|
||||
function useUpdateNodePositions() {
|
||||
export function useUpdateNodePositions() {
|
||||
const store = useStoreApi();
|
||||
|
||||
const updatePositions = useCallback((params: { x: number; y: number; isShiftPressed: boolean }) => {
|
||||
@@ -63,5 +63,3 @@ function useUpdateNodePositions() {
|
||||
|
||||
return updatePositions;
|
||||
}
|
||||
|
||||
export default useUpdateNodePositions;
|
||||
|
||||
@@ -16,10 +16,8 @@ const viewportSelector = (state: ReactFlowState) => ({
|
||||
* @public
|
||||
* @returns The current viewport
|
||||
*/
|
||||
function useViewport(): Viewport {
|
||||
export function useViewport(): Viewport {
|
||||
const viewport = useStore(viewportSelector, shallow);
|
||||
|
||||
return viewport;
|
||||
}
|
||||
|
||||
export default useViewport;
|
||||
|
||||
@@ -12,7 +12,7 @@ const selector = (state: ReactFlowState) => state.panZoom?.syncViewport;
|
||||
* @internal
|
||||
* @param viewport
|
||||
*/
|
||||
export default function useViewportSync(viewport?: Viewport) {
|
||||
export function useViewportSync(viewport?: Viewport) {
|
||||
const syncViewport = useStore(selector);
|
||||
const store = useStoreApi();
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { type ReactFlowState } from '../types';
|
||||
* @param onlyRenderVisible
|
||||
* @returns array with visible edge ids
|
||||
*/
|
||||
function useVisibleEdgeIds(onlyRenderVisible: boolean): string[] {
|
||||
export function useVisibleEdgeIds(onlyRenderVisible: boolean): string[] {
|
||||
const edgeIds = useStore(
|
||||
useCallback(
|
||||
(s: ReactFlowState) => {
|
||||
@@ -52,5 +52,3 @@ function useVisibleEdgeIds(onlyRenderVisible: boolean): string[] {
|
||||
|
||||
return edgeIds;
|
||||
}
|
||||
|
||||
export default useVisibleEdgeIds;
|
||||
|
||||
@@ -20,10 +20,8 @@ const selector = (onlyRenderVisible: boolean) => (s: ReactFlowState) => {
|
||||
* @param onlyRenderVisible
|
||||
* @returns array with visible node ids
|
||||
*/
|
||||
function useVisibleNodeIds(onlyRenderVisible: boolean) {
|
||||
export function useVisibleNodeIds(onlyRenderVisible: boolean) {
|
||||
const nodeIds = useStore(useCallback(selector(onlyRenderVisible), [onlyRenderVisible]), shallow);
|
||||
|
||||
return nodeIds;
|
||||
}
|
||||
|
||||
export default useVisibleNodeIds;
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
export { default as ReactFlow } from './container/ReactFlow';
|
||||
export { default as Handle, type HandleComponentProps } from './components/Handle';
|
||||
export { default as EdgeText } from './components/Edges/EdgeText';
|
||||
export { Handle, type HandleComponentProps } from './components/Handle';
|
||||
export { EdgeText } from './components/Edges/EdgeText';
|
||||
export { StraightEdge } from './components/Edges/StraightEdge';
|
||||
export { StepEdge } from './components/Edges/StepEdge';
|
||||
export { BezierEdge } from './components/Edges/BezierEdge';
|
||||
export { SimpleBezierEdge, getSimpleBezierPath } from './components/Edges/SimpleBezierEdge';
|
||||
export { SmoothStepEdge } from './components/Edges/SmoothStepEdge';
|
||||
export { default as BaseEdge } from './components/Edges/BaseEdge';
|
||||
export { default as ReactFlowProvider } from './components/ReactFlowProvider';
|
||||
export { default as Panel, type PanelProps } from './components/Panel';
|
||||
export { default as EdgeLabelRenderer } from './components/EdgeLabelRenderer';
|
||||
export { default as ViewportPortal } from './components/ViewportPortal';
|
||||
export { BaseEdge } from './components/Edges/BaseEdge';
|
||||
export { ReactFlowProvider } from './components/ReactFlowProvider';
|
||||
export { Panel, type PanelProps } from './components/Panel';
|
||||
export { EdgeLabelRenderer } from './components/EdgeLabelRenderer';
|
||||
export { ViewportPortal } from './components/ViewportPortal';
|
||||
|
||||
export { default as useReactFlow } from './hooks/useReactFlow';
|
||||
export { default as useUpdateNodeInternals } from './hooks/useUpdateNodeInternals';
|
||||
export { default as useNodes } from './hooks/useNodes';
|
||||
export { default as useEdges } from './hooks/useEdges';
|
||||
export { default as useViewport } from './hooks/useViewport';
|
||||
export { default as useKeyPress } from './hooks/useKeyPress';
|
||||
export * from './hooks/useNodesEdgesState';
|
||||
export { useReactFlow } from './hooks/useReactFlow';
|
||||
export { useUpdateNodeInternals } from './hooks/useUpdateNodeInternals';
|
||||
export { useNodes } from './hooks/useNodes';
|
||||
export { useEdges } from './hooks/useEdges';
|
||||
export { useViewport } from './hooks/useViewport';
|
||||
export { useKeyPress } from './hooks/useKeyPress';
|
||||
export { useNodesState, useEdgesState } from './hooks/useNodesEdgesState';
|
||||
export { useStore, useStoreApi } from './hooks/useStore';
|
||||
export { default as useOnViewportChange, type UseOnViewportChangeOptions } from './hooks/useOnViewportChange';
|
||||
export { default as useOnSelectionChange, type UseOnSelectionChangeOptions } from './hooks/useOnSelectionChange';
|
||||
export { default as useNodesInitialized, type UseNodesInitializedOptions } from './hooks/useNodesInitialized';
|
||||
export { useOnViewportChange, type UseOnViewportChangeOptions } from './hooks/useOnViewportChange';
|
||||
export { useOnSelectionChange, type UseOnSelectionChangeOptions } from './hooks/useOnSelectionChange';
|
||||
export { useNodesInitialized, type UseNodesInitializedOptions } from './hooks/useNodesInitialized';
|
||||
export { useHandleConnections } from './hooks/useHandleConnections';
|
||||
export { useNodesData } from './hooks/useNodesData';
|
||||
export { useConnection } from './hooks/useConnection';
|
||||
|
||||
@@ -69,8 +69,6 @@ export type EdgeWrapperProps = {
|
||||
noPanClassName: string;
|
||||
onClick?: EdgeMouseHandler;
|
||||
onDoubleClick?: EdgeMouseHandler;
|
||||
sourceHandleId?: string | null;
|
||||
targetHandleId?: string | null;
|
||||
onEdgeUpdate?: OnEdgeUpdateFunc;
|
||||
onContextMenu?: EdgeMouseHandler;
|
||||
onMouseEnter?: EdgeMouseHandler;
|
||||
|
||||
@@ -54,20 +54,26 @@ function applyChanges(changes: any[], elements: any[]): any[] {
|
||||
return changes.filter((c) => c.type === 'reset').map((c) => c.item);
|
||||
}
|
||||
|
||||
let remainingChanges = changes;
|
||||
let remainingChanges = [];
|
||||
const updatedElements: any[] = [];
|
||||
|
||||
for (const change of changes) {
|
||||
if (change.type === 'add') {
|
||||
updatedElements.push(change.item);
|
||||
} else {
|
||||
remainingChanges.push(change);
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of elements) {
|
||||
const nextChanges: any[] = [];
|
||||
const _remainingChanges: any[] = [];
|
||||
|
||||
for (const c of remainingChanges) {
|
||||
if (c.type === 'add') {
|
||||
updatedElements.push(c.item);
|
||||
} else if (c.id === item.id) {
|
||||
nextChanges.push(c);
|
||||
for (const change of remainingChanges) {
|
||||
if (change.id === item.id) {
|
||||
nextChanges.push(change);
|
||||
} else {
|
||||
_remainingChanges.push(c);
|
||||
_remainingChanges.push(change);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,8 +141,9 @@ function applyChanges(changes: any[], elements: any[]): any[] {
|
||||
}
|
||||
}
|
||||
}
|
||||
updatedElements.push(updateItem);
|
||||
}
|
||||
|
||||
updatedElements.push(updateItem);
|
||||
}
|
||||
|
||||
return updatedElements;
|
||||
|
||||
@@ -106,6 +106,12 @@
|
||||
|
||||
.xy-flow__edges {
|
||||
position: absolute;
|
||||
|
||||
svg {
|
||||
overflow: visible;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
.xy-flow__edge {
|
||||
@@ -156,7 +162,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.xy-flow__connectionline {
|
||||
svg.xy-flow__connectionline {
|
||||
z-index: 1001;
|
||||
overflow: visible;
|
||||
position: absolute;
|
||||
|
||||
@@ -43,12 +43,6 @@
|
||||
--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.xy-flow__edges svg {
|
||||
overflow: visible;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.xy-flow__edge {
|
||||
&.updating {
|
||||
.xy-flow__edge-path {
|
||||
|
||||
Reference in New Issue
Block a user