chore(hooks): tsdoc update
This commit is contained in:
@@ -57,7 +57,7 @@
|
||||
"build": "rollup --config node:@xyflow/rollup-config --environment NODE_ENV:production && npm run css",
|
||||
"css": "postcss src/styles/{base,style}.css --config ./../../tooling/postcss-config/ --dir dist ",
|
||||
"css-watch": "pnpm css --watch",
|
||||
"lint": "eslint --ext .js,.jsx,.ts,.tsx src",
|
||||
"lint": "eslint --ext .js,.jsx,.ts,.tsx src --fix",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -90,4 +90,57 @@ function BackgroundComponent({
|
||||
|
||||
BackgroundComponent.displayName = 'Background';
|
||||
|
||||
/**
|
||||
* The `<Background />` component makes it convenient to render different types of backgrounds common in node-based UIs. It comes with three variants: lines, dots and cross.
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* A simple example of how to use the Background component.
|
||||
*
|
||||
* ```tsx
|
||||
* import { useState } from 'react';
|
||||
* import { ReactFlow, Background, BackgroundVariant } from '@xyflow/react';
|
||||
*
|
||||
* export default function Flow() {
|
||||
* return (
|
||||
* <ReactFlow defaultNodes={[...]} defaultEdges={[...]}>
|
||||
* <Background color="#ccc" variant={BackgroundVariant.Dots} />
|
||||
* </ReactFlow>
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* In this example you can see how to combine multiple backgrounds
|
||||
*
|
||||
* ```tsx
|
||||
* import { ReactFlow, Background, BackgroundVariant } from '@xyflow/react';
|
||||
* import '@xyflow/react/dist/style.css';
|
||||
*
|
||||
* export default function Flow() {
|
||||
* return (
|
||||
* <ReactFlow defaultNodes={[...]} defaultEdges={[...]}>
|
||||
* <Background
|
||||
* id="1"
|
||||
* gap={10}
|
||||
* color="#f1f1f1"
|
||||
* variant={BackgroundVariant.Lines}
|
||||
* />
|
||||
* <Background
|
||||
* id="2"
|
||||
* gap={100}
|
||||
* color="#ccc"
|
||||
* variant={BackgroundVariant.Lines}
|
||||
* />
|
||||
* </ReactFlow>
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* When combining multiple <Background /> components it’s important to give each of them a unique id prop!
|
||||
*
|
||||
*/
|
||||
export const Background = memo(BackgroundComponent);
|
||||
|
||||
@@ -24,7 +24,8 @@ export type BackgroundProps = {
|
||||
offset?: number | [number, number];
|
||||
/** Line width of the Line pattern */
|
||||
lineWidth?: number;
|
||||
/** Variant of the pattern
|
||||
/**
|
||||
* Variant of the pattern
|
||||
* @example BackgroundVariant.Lines, BackgroundVariant.Dots, BackgroundVariant.Cross
|
||||
* 'lines', 'dots', 'cross'
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,8 @@ export type ControlProps = {
|
||||
onFitView?: () => void;
|
||||
/** Callback when interactivity is toggled */
|
||||
onInteractiveChange?: (interactiveStatus: boolean) => void;
|
||||
/** Position of the controls on the pane
|
||||
/**
|
||||
* Position of the controls on the pane
|
||||
* @example PanelPosition.TopLeft, PanelPosition.TopRight,
|
||||
* PanelPosition.BottomLeft, PanelPosition.BottomRight
|
||||
*/
|
||||
|
||||
@@ -44,8 +44,10 @@ function MiniMapComponent<NodeType extends Node = Node>({
|
||||
nodeClassName = '',
|
||||
nodeBorderRadius = 5,
|
||||
nodeStrokeWidth,
|
||||
// We need to rename the prop to be `CapitalCase` so that JSX will render it as
|
||||
// a component properly.
|
||||
/*
|
||||
* We need to rename the prop to be `CapitalCase` so that JSX will render it as
|
||||
* a component properly.
|
||||
*/
|
||||
nodeComponent,
|
||||
bgColor,
|
||||
maskColor,
|
||||
@@ -111,16 +113,16 @@ function MiniMapComponent<NodeType extends Node = Node>({
|
||||
|
||||
const onSvgClick = onClick
|
||||
? (event: MouseEvent) => {
|
||||
const [x, y] = minimapInstance.current?.pointer(event) || [0, 0];
|
||||
onClick(event, { x, y });
|
||||
}
|
||||
const [x, y] = minimapInstance.current?.pointer(event) || [0, 0];
|
||||
onClick(event, { x, y });
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const onSvgNodeClick = onNodeClick
|
||||
? useCallback((event: MouseEvent, nodeId: string) => {
|
||||
const node = store.getState().nodeLookup.get(nodeId)!;
|
||||
onNodeClick(event, node);
|
||||
}, [])
|
||||
const node = store.getState().nodeLookup.get(nodeId)!;
|
||||
onNodeClick(event, node);
|
||||
}, [])
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
|
||||
@@ -21,8 +21,10 @@ function MiniMapNodes<NodeType extends Node>({
|
||||
nodeClassName = '',
|
||||
nodeBorderRadius = 5,
|
||||
nodeStrokeWidth,
|
||||
// We need to rename the prop to be `CapitalCase` so that JSX will render it as
|
||||
// a component properly.
|
||||
/*
|
||||
* We need to rename the prop to be `CapitalCase` so that JSX will render it as
|
||||
* a component properly.
|
||||
*/
|
||||
nodeComponent: NodeComponent = MiniMapNode,
|
||||
onClick,
|
||||
}: MiniMapNodesProps<NodeType>) {
|
||||
@@ -36,11 +38,13 @@ function MiniMapNodes<NodeType extends Node>({
|
||||
return (
|
||||
<>
|
||||
{nodeIds.map((nodeId) => (
|
||||
// The split of responsibilities between MiniMapNodes and
|
||||
// NodeComponentWrapper may appear weird. However, it’s designed to
|
||||
// minimize the cost of updates when individual nodes change.
|
||||
//
|
||||
// For more details, see a similar commit in `NodeRenderer/index.tsx`.
|
||||
/*
|
||||
* The split of responsibilities between MiniMapNodes and
|
||||
* NodeComponentWrapper may appear weird. However, it’s designed to
|
||||
* minimize the cost of updates when individual nodes change.
|
||||
*
|
||||
* For more details, see a similar commit in `NodeRenderer/index.tsx`.
|
||||
*/
|
||||
<NodeComponentWrapper<NodeType>
|
||||
key={nodeId}
|
||||
id={nodeId}
|
||||
|
||||
@@ -27,7 +27,8 @@ export type MiniMapProps<NodeType extends Node = Node> = Omit<HTMLAttributes<SVG
|
||||
maskStrokeColor?: string;
|
||||
/** Stroke width of mask representing viewport */
|
||||
maskStrokeWidth?: number;
|
||||
/** Position of minimap on pane
|
||||
/**
|
||||
* Position of minimap on pane
|
||||
* @example PanelPosition.TopLeft, PanelPosition.TopRight,
|
||||
* PanelPosition.BottomLeft, PanelPosition.BottomRight
|
||||
*/
|
||||
|
||||
@@ -99,8 +99,10 @@ function ResizeControl({
|
||||
const parentExpandChanges = handleExpandParent([child], nodeLookup, parentLookup, nodeOrigin);
|
||||
changes.push(...parentExpandChanges);
|
||||
|
||||
// when the parent was expanded by the child node, its position will be clamped at
|
||||
// 0,0 when node origin is 0,0 and to width, height if it's 1,1
|
||||
/*
|
||||
* when the parent was expanded by the child node, its position will be clamped at
|
||||
* 0,0 when node origin is 0,0 and to width, height if it's 1,1
|
||||
*/
|
||||
nextPosition.x = change.x ? Math.max(origin[0] * width, change.x) : undefined;
|
||||
nextPosition.y = change.y ? Math.max(origin[1] * height, change.y) : undefined;
|
||||
}
|
||||
|
||||
@@ -10,7 +10,8 @@ import type {
|
||||
} from '@xyflow/system';
|
||||
|
||||
export type NodeResizerProps = {
|
||||
/** Id of the node it is resizing
|
||||
/**
|
||||
* Id of the node it is resizing
|
||||
* @remarks optional if used inside custom node
|
||||
*/
|
||||
nodeId?: string;
|
||||
@@ -60,12 +61,14 @@ export type ResizeControlProps = Pick<
|
||||
| 'onResize'
|
||||
| 'onResizeEnd'
|
||||
> & {
|
||||
/** Position of the control
|
||||
/**
|
||||
* Position of the control
|
||||
* @example ControlPosition.TopLeft, ControlPosition.TopRight,
|
||||
* ControlPosition.BottomLeft, ControlPosition.BottomRight
|
||||
*/
|
||||
position?: ControlPosition;
|
||||
/** Variant of the control
|
||||
/**
|
||||
* Variant of the control
|
||||
* @example ResizeControlVariant.Handle, ResizeControlVariant.Line
|
||||
*/
|
||||
variant?: ResizeControlVariant;
|
||||
|
||||
@@ -6,14 +6,16 @@ export type NodeToolbarProps = HTMLAttributes<HTMLDivElement> & {
|
||||
nodeId?: string | string[];
|
||||
/** If true, node toolbar is visible even if node is not selected */
|
||||
isVisible?: boolean;
|
||||
/** Position of the toolbar relative to the node
|
||||
/**
|
||||
* Position of the toolbar relative to the node
|
||||
* @example Position.TopLeft, Position.TopRight,
|
||||
* Position.BottomLeft, Position.BottomRight
|
||||
*/
|
||||
position?: Position;
|
||||
/** Offset the toolbar from the node */
|
||||
offset?: number;
|
||||
/** Align the toolbar relative to the node
|
||||
/**
|
||||
* Align the toolbar relative to the node
|
||||
* @example Align.Start, Align.Center, Align.End
|
||||
*/
|
||||
align?: Align;
|
||||
|
||||
@@ -30,9 +30,11 @@ export function BatchProvider<NodeType extends Node = Node, EdgeType extends Edg
|
||||
const nodeQueueHandler = useCallback((queueItems: QueueItem<NodeType>[]) => {
|
||||
const { nodes = [], setNodes, hasDefaultNodes, onNodesChange, nodeLookup } = store.getState();
|
||||
|
||||
// This is essentially an `Array.reduce` in imperative clothing. Processing
|
||||
// this queue is a relatively hot path so we'd like to avoid the overhead of
|
||||
// array methods where we can.
|
||||
/*
|
||||
* This is essentially an `Array.reduce` in imperative clothing. Processing
|
||||
* this queue is a relatively hot path so we'd like to avoid the overhead of
|
||||
* array methods where we can.
|
||||
*/
|
||||
let next = nodes as NodeType[];
|
||||
for (const payload of queueItems) {
|
||||
next = typeof payload === 'function' ? payload(next) : payload;
|
||||
|
||||
@@ -12,21 +12,27 @@ import { Queue, QueueItem } from './types';
|
||||
* @returns a Queue object
|
||||
*/
|
||||
export function useQueue<T>(runQueue: (items: QueueItem<T>[]) => void) {
|
||||
// Because we're using a ref above, we need some way to let React know when to
|
||||
// actually process the queue. We increment this number any time we mutate the
|
||||
// queue, creating a new state to trigger the layout effect below.
|
||||
// Using a boolean dirty flag here instead would lead to issues related to
|
||||
// automatic batching. (https://github.com/xyflow/xyflow/issues/4779)
|
||||
/*
|
||||
* Because we're using a ref above, we need some way to let React know when to
|
||||
* actually process the queue. We increment this number any time we mutate the
|
||||
* queue, creating a new state to trigger the layout effect below.
|
||||
* Using a boolean dirty flag here instead would lead to issues related to
|
||||
* automatic batching. (https://github.com/xyflow/xyflow/issues/4779)
|
||||
*/
|
||||
const [serial, setSerial] = useState(BigInt(0));
|
||||
|
||||
// A reference of all the batched updates to process before the next render. We
|
||||
// want a reference here so multiple synchronous calls to `setNodes` etc can be
|
||||
// batched together.
|
||||
/*
|
||||
* A reference of all the batched updates to process before the next render. We
|
||||
* want a reference here so multiple synchronous calls to `setNodes` etc can be
|
||||
* batched together.
|
||||
*/
|
||||
const [queue] = useState(() => createQueue<T>(() => setSerial(n => n + BigInt(1))));
|
||||
|
||||
// Layout effects are guaranteed to run before the next render which means we
|
||||
// shouldn't run into any issues with stale state or weird issues that come from
|
||||
// rendering things one frame later than expected (we used to use `setTimeout`).
|
||||
/*
|
||||
* Layout effects are guaranteed to run before the next render which means we
|
||||
* shouldn't run into any issues with stale state or weird issues that come from
|
||||
* rendering things one frame later than expected (we used to use `setTimeout`).
|
||||
*/
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
const queueItems = queue.get();
|
||||
|
||||
|
||||
@@ -136,28 +136,28 @@ export function EdgeWrapper<EdgeType extends Edge = Edge>({
|
||||
|
||||
const onEdgeDoubleClick = onDoubleClick
|
||||
? (event: React.MouseEvent) => {
|
||||
onDoubleClick(event, { ...edge });
|
||||
}
|
||||
onDoubleClick(event, { ...edge });
|
||||
}
|
||||
: undefined;
|
||||
const onEdgeContextMenu = onContextMenu
|
||||
? (event: React.MouseEvent) => {
|
||||
onContextMenu(event, { ...edge });
|
||||
}
|
||||
onContextMenu(event, { ...edge });
|
||||
}
|
||||
: undefined;
|
||||
const onEdgeMouseEnter = onMouseEnter
|
||||
? (event: React.MouseEvent) => {
|
||||
onMouseEnter(event, { ...edge });
|
||||
}
|
||||
onMouseEnter(event, { ...edge });
|
||||
}
|
||||
: undefined;
|
||||
const onEdgeMouseMove = onMouseMove
|
||||
? (event: React.MouseEvent) => {
|
||||
onMouseMove(event, { ...edge });
|
||||
}
|
||||
onMouseMove(event, { ...edge });
|
||||
}
|
||||
: undefined;
|
||||
const onEdgeMouseLeave = onMouseLeave
|
||||
? (event: React.MouseEvent) => {
|
||||
onMouseLeave(event, { ...edge });
|
||||
}
|
||||
onMouseLeave(event, { ...edge });
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// We distinguish between internal and exported edges
|
||||
// The internal edges are used directly like custom edges and always get an id, source and target props
|
||||
// If you import an edge from the library, the id is optional and source and target are not used at all
|
||||
/*
|
||||
* We distinguish between internal and exported edges
|
||||
* The internal edges are used directly like custom edges and always get an id, source and target props
|
||||
* If you import an edge from the library, the id is optional and source and target are not used at all
|
||||
*/
|
||||
|
||||
export { SimpleBezierEdge, SimpleBezierEdgeInternal } from './SimpleBezierEdge';
|
||||
export { SmoothStepEdge, SmoothStepEdgeInternal } from './SmoothStepEdge';
|
||||
|
||||
@@ -228,8 +228,10 @@ function HandleComponent(
|
||||
connectingfrom: connectingFrom,
|
||||
connectingto: connectingTo,
|
||||
valid,
|
||||
// shows where you can start a connection from
|
||||
// and where you can end it while connecting
|
||||
/*
|
||||
* shows where you can start a connection from
|
||||
* and where you can end it while connecting
|
||||
*/
|
||||
connectionindicator:
|
||||
isConnectable &&
|
||||
(!connectionInProcess || isPossibleEndHandle) &&
|
||||
|
||||
@@ -108,8 +108,10 @@ export function NodeWrapper<NodeType extends Node>({
|
||||
const { selectNodesOnDrag, nodeDragThreshold } = store.getState();
|
||||
|
||||
if (isSelectable && (!selectNodesOnDrag || !isDraggable || nodeDragThreshold > 0)) {
|
||||
// this handler gets called by XYDrag on drag start when selectNodesOnDrag=true
|
||||
// here we only need to call it when selectNodesOnDrag=false
|
||||
/*
|
||||
* this handler gets called by XYDrag on drag start when selectNodesOnDrag=true
|
||||
* here we only need to call it when selectNodesOnDrag=false
|
||||
*/
|
||||
handleNodeClick({
|
||||
id,
|
||||
store,
|
||||
|
||||
@@ -49,8 +49,10 @@ export function useNodeObserver({
|
||||
|
||||
useEffect(() => {
|
||||
if (nodeRef.current) {
|
||||
// when the user programmatically changes the source or handle position, we need to update the internals
|
||||
// to make sure the edges are updated correctly
|
||||
/*
|
||||
* when the user programmatically changes the source or handle position, we need to update the internals
|
||||
* to make sure the edges are updated correctly
|
||||
*/
|
||||
const typeChanged = prevType.current !== nodeType;
|
||||
const sourcePosChanged = prevSourcePosition.current !== node.sourcePosition;
|
||||
const targetPosChanged = prevTargetPosition.current !== node.targetPosition;
|
||||
|
||||
@@ -23,9 +23,9 @@ export const builtinNodeTypes: NodeTypes = {
|
||||
export function getNodeInlineStyleDimensions<NodeType extends Node = Node>(
|
||||
node: InternalNode<NodeType>
|
||||
): {
|
||||
width: number | string | undefined;
|
||||
height: number | string | undefined;
|
||||
} {
|
||||
width: number | string | undefined;
|
||||
height: number | string | undefined;
|
||||
} {
|
||||
if (node.internals.handleBounds === undefined) {
|
||||
return {
|
||||
width: node.width ?? node.initialWidth ?? node.style?.width,
|
||||
|
||||
@@ -4,10 +4,12 @@ import { errorMessages } from '@xyflow/system';
|
||||
|
||||
import type { ReactFlowState } from '../../types';
|
||||
|
||||
// this handler is called by
|
||||
// 1. the click handler when node is not draggable or selectNodesOnDrag = false
|
||||
// or
|
||||
// 2. the on drag start handler when node is draggable and selectNodesOnDrag = true
|
||||
/*
|
||||
* this handler is called by
|
||||
* 1. the click handler when node is not draggable or selectNodesOnDrag = false
|
||||
* or
|
||||
* 2. the on drag start handler when node is draggable and selectNodesOnDrag = true
|
||||
*/
|
||||
export function handleNodeClick({
|
||||
id,
|
||||
store,
|
||||
|
||||
@@ -61,9 +61,9 @@ export function NodesSelection<NodeType extends Node>({
|
||||
|
||||
const onContextMenu = onSelectionContextMenu
|
||||
? (event: MouseEvent) => {
|
||||
const selectedNodes = store.getState().nodes.filter((n) => n.selected);
|
||||
onSelectionContextMenu(event, selectedNodes);
|
||||
}
|
||||
const selectedNodes = store.getState().nodes.filter((n) => n.selected);
|
||||
onSelectionContextMenu(event, selectedNodes);
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const onKeyDown = (event: KeyboardEvent) => {
|
||||
|
||||
@@ -6,7 +6,8 @@ import { useStore } from '../../hooks/useStore';
|
||||
import type { ReactFlowState } from '../../types';
|
||||
|
||||
export type PanelProps = HTMLAttributes<HTMLDivElement> & {
|
||||
/** Set position of the panel
|
||||
/**
|
||||
* Set position of the panel
|
||||
* @example 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'
|
||||
*/
|
||||
position?: PanelPosition;
|
||||
|
||||
@@ -94,9 +94,11 @@ const selector = (s: ReactFlowState) => ({
|
||||
});
|
||||
|
||||
const initPrevValues = {
|
||||
// these are values that are also passed directly to other components
|
||||
// than the StoreUpdater. We can reduce the number of setStore calls
|
||||
// by setting the same values here as prev fields.
|
||||
/*
|
||||
* these are values that are also passed directly to other components
|
||||
* than the StoreUpdater. We can reduce the number of setStore calls
|
||||
* by setting the same values here as prev fields.
|
||||
*/
|
||||
translateExtent: infiniteExtent,
|
||||
nodeOrigin: defaultNodeOrigin,
|
||||
minZoom: 0.5,
|
||||
|
||||
@@ -42,9 +42,11 @@ const Marker = ({
|
||||
);
|
||||
};
|
||||
|
||||
// when you have multiple flows on a page and you hide the first one, the other ones have no markers anymore
|
||||
// when they do have markers with the same ids. To prevent this the user can pass a unique id to the react flow wrapper
|
||||
// that we can then use for creating our unique marker ids
|
||||
/*
|
||||
* when you have multiple flows on a page and you hide the first one, the other ones have no markers anymore
|
||||
* when they do have markers with the same ids. To prevent this the user can pass a unique id to the react flow wrapper
|
||||
* that we can then use for creating our unique marker ids
|
||||
*/
|
||||
const MarkerDefinitions = ({ defaultColor, rfId }: MarkerDefinitionsProps) => {
|
||||
const edges = useStore((s) => s.edges);
|
||||
const defaultEdgeOptions = useStore((s) => s.defaultEdgeOptions);
|
||||
|
||||
@@ -44,29 +44,31 @@ function NodeRendererComponent<NodeType extends Node>(props: NodeRendererProps<N
|
||||
<div className="react-flow__nodes" style={containerStyle}>
|
||||
{nodeIds.map((nodeId) => {
|
||||
return (
|
||||
// The split of responsibilities between NodeRenderer and
|
||||
// NodeComponentWrapper may appear weird. However, it’s designed to
|
||||
// minimize the cost of updates when individual nodes change.
|
||||
//
|
||||
// For example, when you’re dragging a single node, that node gets
|
||||
// updated multiple times per second. If `NodeRenderer` were to update
|
||||
// every time, it would have to re-run the `nodes.map()` loop every
|
||||
// time. This gets pricey with hundreds of nodes, especially if every
|
||||
// loop cycle does more than just rendering a JSX element!
|
||||
//
|
||||
// As a result of this choice, we took the following implementation
|
||||
// decisions:
|
||||
// - NodeRenderer subscribes *only* to node IDs – and therefore
|
||||
// rerender *only* when visible nodes are added or removed.
|
||||
// - NodeRenderer performs all operations the result of which can be
|
||||
// shared between nodes (such as creating the `ResizeObserver`
|
||||
// instance, or subscribing to `selector`). This means extra prop
|
||||
// drilling into `NodeComponentWrapper`, but it means we need to run
|
||||
// these operations only once – instead of once per node.
|
||||
// - Any operations that you’d normally write inside `nodes.map` are
|
||||
// moved into `NodeComponentWrapper`. This ensures they are
|
||||
// memorized – so if `NodeRenderer` *has* to rerender, it only
|
||||
// needs to regenerate the list of nodes, nothing else.
|
||||
/*
|
||||
* The split of responsibilities between NodeRenderer and
|
||||
* NodeComponentWrapper may appear weird. However, it’s designed to
|
||||
* minimize the cost of updates when individual nodes change.
|
||||
*
|
||||
* For example, when you’re dragging a single node, that node gets
|
||||
* updated multiple times per second. If `NodeRenderer` were to update
|
||||
* every time, it would have to re-run the `nodes.map()` loop every
|
||||
* time. This gets pricey with hundreds of nodes, especially if every
|
||||
* loop cycle does more than just rendering a JSX element!
|
||||
*
|
||||
* As a result of this choice, we took the following implementation
|
||||
* decisions:
|
||||
* - NodeRenderer subscribes *only* to node IDs – and therefore
|
||||
* rerender *only* when visible nodes are added or removed.
|
||||
* - NodeRenderer performs all operations the result of which can be
|
||||
* shared between nodes (such as creating the `ResizeObserver`
|
||||
* instance, or subscribing to `selector`). This means extra prop
|
||||
* drilling into `NodeComponentWrapper`, but it means we need to run
|
||||
* these operations only once – instead of once per node.
|
||||
* - Any operations that you’d normally write inside `nodes.map` are
|
||||
* moved into `NodeComponentWrapper`. This ensures they are
|
||||
* memorized – so if `NodeRenderer` *has* to rerender, it only
|
||||
* needs to regenerate the list of nodes, nothing else.
|
||||
*/
|
||||
<NodeWrapper<NodeType>
|
||||
key={nodeId}
|
||||
id={nodeId}
|
||||
|
||||
@@ -233,8 +233,10 @@ export function Pane({
|
||||
(event.target as Partial<Element>)?.releasePointerCapture?.(event.pointerId);
|
||||
const { userSelectionRect } = store.getState();
|
||||
|
||||
// We only want to trigger click functions when in selection mode if
|
||||
// the user did not move the mouse.
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
@@ -246,8 +248,10 @@ export function Pane({
|
||||
});
|
||||
onSelectionEnd?.(event);
|
||||
|
||||
// If the user kept holding the selectionKey during the selection,
|
||||
// we need to reset the selectionInProgress, so the next click event is not prevented
|
||||
/*
|
||||
* If the user kept holding the selectionKey during the selection,
|
||||
* we need to reset the selectionInProgress, so the next click event is not prevented
|
||||
*/
|
||||
if (selectionKeyPressed || selectionOnDrag) {
|
||||
selectionInProgress.current = false;
|
||||
}
|
||||
|
||||
@@ -31,8 +31,10 @@ export function Wrapper({
|
||||
const isWrapped = useContext(StoreContext);
|
||||
|
||||
if (isWrapped) {
|
||||
// we need to wrap it with a fragment because it's not allowed for children to be a ReactNode
|
||||
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051
|
||||
/*
|
||||
* we need to wrap it with a fragment because it's not allowed for children to be a ReactNode
|
||||
* https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051
|
||||
*/
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,34 @@ export const NodeIdContext = createContext<string | null>(null);
|
||||
export const Provider = NodeIdContext.Provider;
|
||||
export const Consumer = NodeIdContext.Consumer;
|
||||
|
||||
/**
|
||||
* You can use this hook to get the id of the node it is used inside. It is useful
|
||||
*if you need the node's id deeper in the render tree but don't want to manually
|
||||
*drill down the id as a prop.
|
||||
*
|
||||
* @public
|
||||
* @returns id of the node
|
||||
*
|
||||
* @example
|
||||
*```jsx
|
||||
*import { useNodeId } from '@xyflow/react';
|
||||
*
|
||||
*export default function CustomNode() {
|
||||
* return (
|
||||
* <div>
|
||||
* <span>This node has an id of </span>
|
||||
* <NodeIdDisplay />
|
||||
* </div>
|
||||
* );
|
||||
*}
|
||||
*
|
||||
*function NodeIdDisplay() {
|
||||
* const nodeId = useNodeId();
|
||||
*
|
||||
* return <span>{nodeId}</span>;
|
||||
*}
|
||||
*```
|
||||
*/
|
||||
export const useNodeId = (): string | null => {
|
||||
const nodeId = useContext(NodeIdContext);
|
||||
return nodeId;
|
||||
|
||||
@@ -24,9 +24,28 @@ function getSelector<NodeType extends Node = Node, SelectorReturn = ConnectionSt
|
||||
return storeSelector;
|
||||
}
|
||||
/**
|
||||
* Hook for accessing the connection state.
|
||||
* The `useConnection` hook returns the current connection when there is an active
|
||||
*connection interaction. If no connection interaction is active, it returns null
|
||||
*for every property. A typical use case for this hook is to colorize handles
|
||||
*based on a certain condition (e.g. if the connection is valid or not).
|
||||
*
|
||||
* @public
|
||||
* @example
|
||||
*
|
||||
* ```tsx
|
||||
*import { useConnection } from '@xyflow/react';
|
||||
*
|
||||
*function App() {
|
||||
* const connection = useConnection();
|
||||
*
|
||||
* return (
|
||||
* <div> {connection ? `Someone is trying to make a connection from ${connection.fromNode} to this one.` : 'There are currently no incoming connections!'}
|
||||
*
|
||||
* </div>
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* @returns ConnectionState
|
||||
*/
|
||||
export function useConnection<NodeType extends Node = Node, SelectorReturn = ConnectionState<InternalNode<NodeType>>>(
|
||||
|
||||
@@ -6,10 +6,22 @@ import type { Edge, ReactFlowState } from '../types';
|
||||
const edgesSelector = (state: ReactFlowState) => state.edges;
|
||||
|
||||
/**
|
||||
* Hook for getting the current edges from the store.
|
||||
* This hook returns an array of the current edges. Components that use this hook
|
||||
*will re-render **whenever any edge changes**.
|
||||
*
|
||||
* @public
|
||||
* @returns An array of edges
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
*import { useEdges } from '@xyflow/react';
|
||||
*
|
||||
*export default function () {
|
||||
* const edges = useEdges();
|
||||
*
|
||||
* return <div>There are currently {edges.length} edges!</div>;
|
||||
*}
|
||||
*```
|
||||
*/
|
||||
export function useEdges<EdgeType extends Edge = Edge>(): EdgeType[] {
|
||||
const edges = useStore(edgesSelector, shallow) as EdgeType[];
|
||||
|
||||
@@ -5,11 +5,31 @@ import { useStore } from './useStore';
|
||||
import type { InternalNode, Node } from '../types';
|
||||
|
||||
/**
|
||||
* Hook for getting an internal node by id
|
||||
* This hook returns the internal representation of a specific node. Components that use this hook
|
||||
*will re-render **whenever the node changes**, including when a node is selected
|
||||
*or moved.
|
||||
*
|
||||
* @public
|
||||
* @param id - id of the node
|
||||
* @returns array with visible node ids
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
*import { useInternalNode } from '@xyflow/react';
|
||||
*
|
||||
*export default function () {
|
||||
* const internalNode = useInternalNode('node-1');
|
||||
* const absolutePosition = internalNode.internals.positionAbsolute;
|
||||
*
|
||||
* return (
|
||||
* <div>
|
||||
* The absolute position of the node is at:
|
||||
* <p>x: {absolutePosition.x}</p>
|
||||
* <p>y: {absolutePosition.y}</p>
|
||||
* </div>
|
||||
* );
|
||||
*}
|
||||
*```
|
||||
*/
|
||||
export function useInternalNode<NodeType extends Node = Node>(id: string): InternalNode<NodeType> | undefined {
|
||||
const node = useStore(
|
||||
|
||||
@@ -13,18 +13,38 @@ export type UseKeyPressOptions = {
|
||||
const defaultDoc = typeof document !== 'undefined' ? document : null;
|
||||
|
||||
/**
|
||||
* Hook for handling key events.
|
||||
* This hook lets you listen for specific key codes and tells you whether they are
|
||||
*currently pressed or not.
|
||||
*
|
||||
* @public
|
||||
* @param param.keyCode - The key code (string or array of strings) to use
|
||||
* @param param.options - Options
|
||||
* @returns boolean
|
||||
*
|
||||
* @example
|
||||
* ```tsx
|
||||
*import { useKeyPress } from '@xyflow/react';
|
||||
*
|
||||
*export default function () {
|
||||
* const spacePressed = useKeyPress('Space');
|
||||
* const cmdAndSPressed = useKeyPress(['Meta+s', 'Strg+s']);
|
||||
*
|
||||
* return (
|
||||
* <div>
|
||||
* {spacePressed && <p>Space pressed!</p>}
|
||||
* {cmdAndSPressed && <p>Cmd + S pressed!</p>}
|
||||
* </div>
|
||||
* );
|
||||
*}
|
||||
*```
|
||||
*/
|
||||
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'
|
||||
/*
|
||||
* 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 {
|
||||
@@ -36,20 +56,24 @@ export function useKeyPress(
|
||||
// we need to remember the pressed keys in order to support combinations
|
||||
const pressedKeys = useRef<PressedKeys>(new Set([]));
|
||||
|
||||
// keyCodes = array with single keys [['a']] or key combinations [['a', 's']]
|
||||
// keysToWatch = array with all keys flattened ['a', 'd', 'ShiftLeft']
|
||||
// used to check if we store event.code or event.key. When the code is in the list of keysToWatch
|
||||
// we use the code otherwise the key. Explainer: When you press the left "command" key, the code is "MetaLeft"
|
||||
// and the key is "Meta". We want users to be able to pass keys and codes so we assume that the key is meant when
|
||||
// we can't find it in the list of keysToWatch.
|
||||
/*
|
||||
* keyCodes = array with single keys [['a']] or key combinations [['a', 's']]
|
||||
* keysToWatch = array with all keys flattened ['a', 'd', 'ShiftLeft']
|
||||
* used to check if we store event.code or event.key. When the code is in the list of keysToWatch
|
||||
* we use the code otherwise the key. Explainer: When you press the left "command" key, the code is "MetaLeft"
|
||||
* and the key is "Meta". We want users to be able to pass keys and codes so we assume that the key is meant when
|
||||
* we can't find it in the list of keysToWatch.
|
||||
*/
|
||||
const [keyCodes, keysToWatch] = useMemo<[Array<Keys>, Keys]>(() => {
|
||||
if (keyCode !== null) {
|
||||
const keyCodeArr = Array.isArray(keyCode) ? keyCode : [keyCode];
|
||||
const keys = keyCodeArr
|
||||
.filter((kc) => typeof kc === 'string')
|
||||
// we first replace all '+' with '\n' which we will use to split the keys on
|
||||
// then we replace '\n\n' with '\n+', this way we can also support the combination 'key++'
|
||||
// in the end we simply split on '\n' to get the key array
|
||||
/*
|
||||
* we first replace all '+' with '\n' which we will use to split the keys on
|
||||
* then we replace '\n\n' with '\n+', this way we can also support the combination 'key++'
|
||||
* in the end we simply split on '\n' to get the key array
|
||||
*/
|
||||
.map((kc) => kc.replace('+', '\n').replace('\n\n', '\n+').split('\n'));
|
||||
const keysFlat = keys.reduce((res: Keys, item) => res.concat(...item), []);
|
||||
|
||||
@@ -133,12 +157,16 @@ export function useKeyPress(
|
||||
function isMatchingKey(keyCodes: Array<Keys>, pressedKeys: PressedKeys, isUp: boolean): boolean {
|
||||
return (
|
||||
keyCodes
|
||||
// we only want to compare same sizes of keyCode definitions
|
||||
// and pressed keys. When the user specified 'Meta' as a key somewhere
|
||||
// this would also be truthy without this filter when user presses 'Meta' + 'r'
|
||||
/*
|
||||
* we only want to compare same sizes of keyCode definitions
|
||||
* and pressed keys. When the user specified 'Meta' as a key somewhere
|
||||
* this would also be truthy without this filter when user presses 'Meta' + 'r'
|
||||
*/
|
||||
.filter((keys) => isUp || keys.length === pressedKeys.size)
|
||||
// since we want to support multiple possibilities only one of the
|
||||
// combinations need to be part of the pressed keys
|
||||
/*
|
||||
* since we want to support multiple possibilities only one of the
|
||||
* combinations need to be part of the pressed keys
|
||||
*/
|
||||
.some((keys) => keys.every((k) => pressedKeys.has(k)))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,8 +22,10 @@ export function useMoveSelectedNodes() {
|
||||
const nodeUpdates = new Map();
|
||||
const isSelected = selectedAndDraggable(nodesDraggable);
|
||||
|
||||
// by default a node moves 5px on each key press
|
||||
// if snap grid is enabled, we use that for the velocity
|
||||
/*
|
||||
* by default a node moves 5px on each key press
|
||||
* if snap grid is enabled, we use that for the velocity
|
||||
*/
|
||||
const xVelo = snapToGrid ? snapGrid[0] : 5;
|
||||
const yVelo = snapToGrid ? snapGrid[1] : 5;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ type UseNodeConnectionsParams = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to retrieve all edges connected to a node. Can be filtered by handle type and id.
|
||||
* This hook returns an array of connections on a specific node, handle type ('source', 'target') or handle ID.
|
||||
*
|
||||
* @public
|
||||
* @param param.id - node id - optional if called inside a custom node
|
||||
@@ -31,6 +31,22 @@ type UseNodeConnectionsParams = {
|
||||
* @param param.onConnect - gets called when a connection is established
|
||||
* @param param.onDisconnect - gets called when a connection is removed
|
||||
* @returns an array with connections
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
*import { useNodeConnections } from '@xyflow/react';
|
||||
*
|
||||
*export default function () {
|
||||
* const connections = useNodeConnections({
|
||||
* type: 'target',
|
||||
* handleId: 'my-handle',
|
||||
* });
|
||||
*
|
||||
* return (
|
||||
* <div>There are currently {connections.length} incoming connections!</div>
|
||||
* );
|
||||
*}
|
||||
*```
|
||||
*/
|
||||
export function useNodeConnections({
|
||||
id,
|
||||
|
||||
@@ -6,10 +6,23 @@ import type { Node, ReactFlowState } from '../types';
|
||||
const nodesSelector = (state: ReactFlowState) => state.nodes;
|
||||
|
||||
/**
|
||||
* Hook for getting the current nodes from the store.
|
||||
* This hook returns an array of the current nodes. Components that use this hook
|
||||
*will re-render **whenever any node changes**, including when a node is selected
|
||||
*or moved.
|
||||
*
|
||||
* @public
|
||||
* @returns An array of nodes
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
*import { useNodes } from '@xyflow/react';
|
||||
*
|
||||
*export default function() {
|
||||
* const nodes = useNodes();
|
||||
*
|
||||
* return <div>There are currently {nodes.length} nodes!</div>;
|
||||
*}
|
||||
*```
|
||||
*/
|
||||
export function useNodes<NodeType extends Node = Node>(): NodeType[] {
|
||||
const nodes = useStore(nodesSelector, shallow) as NodeType[];
|
||||
|
||||
@@ -5,12 +5,24 @@ import { useStore } from '../hooks/useStore';
|
||||
import type { Node } from '../types';
|
||||
|
||||
/**
|
||||
* Hook for receiving data of one or multiple nodes
|
||||
* This hook lets you subscribe to changes of a specific nodes `data` object.
|
||||
*
|
||||
* @public
|
||||
* @param nodeId - The id (or ids) of the node to get the data from
|
||||
* @param guard - Optional guard function to narrow down the node type
|
||||
* @returns An object (or array of object) with {id, type, data} representing each node
|
||||
*
|
||||
* @example
|
||||
*
|
||||
*```jsx
|
||||
*import { useNodesData } from '@xyflow/react';
|
||||
*
|
||||
*export default function() {
|
||||
* const nodeData = useNodesData('nodeId-1');
|
||||
* const nodesData = useNodesData(['nodeId-1', 'nodeId-2']);
|
||||
*
|
||||
* return null;
|
||||
*}
|
||||
*```
|
||||
*/
|
||||
export function useNodesData<NodeType extends Node = Node>(
|
||||
nodeId: string
|
||||
|
||||
@@ -4,11 +4,41 @@ import { applyNodeChanges, applyEdgeChanges } from '../utils/changes';
|
||||
import type { Node, Edge, OnNodesChange, OnEdgesChange } from '../types';
|
||||
|
||||
/**
|
||||
* Hook for managing the state of nodes - should only be used for prototyping / simple use cases.
|
||||
* This hook makes it easy to prototype a controlled flow where you manage the
|
||||
*state of nodes and edges outside the `ReactFlowInstance`. You can think of it
|
||||
*like React's `useState` hook with an additional helper callback.
|
||||
*
|
||||
* @public
|
||||
* @param initialNodes
|
||||
* @returns an array [nodes, setNodes, onNodesChange]
|
||||
* @example
|
||||
*
|
||||
*```tsx
|
||||
*import { ReactFlow, useNodesState, useEdgesState } from '@xyflow/react';
|
||||
*
|
||||
*const initialNodes = [];
|
||||
*const initialEdges = [];
|
||||
*
|
||||
*export default function () {
|
||||
* const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||
* const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||
*
|
||||
* return (
|
||||
* <ReactFlow
|
||||
* nodes={nodes}
|
||||
* edges={edges}
|
||||
* onNodesChange={onNodesChange}
|
||||
* onEdgesChange={onEdgesChange}
|
||||
* />
|
||||
* );
|
||||
*}
|
||||
*```
|
||||
*
|
||||
*@remarks This hook was created to make prototyping easier and our documentation
|
||||
*examples clearer. Although it is OK to use this hook in production, in
|
||||
*practice you may want to use a more sophisticated state management solution
|
||||
*like Zustand {@link https://reactflow.dev/docs/guides/state-management/} instead.
|
||||
*
|
||||
*/
|
||||
export function useNodesState<NodeType extends Node>(
|
||||
initialNodes: NodeType[]
|
||||
@@ -23,11 +53,41 @@ export function useNodesState<NodeType extends Node>(
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for managing the state of edges - should only be used for prototyping / simple use cases.
|
||||
* This hook makes it easy to prototype a controlled flow where you manage the
|
||||
*state of nodes and edges outside the `ReactFlowInstance`. You can think of it
|
||||
*like React's `useState` hook with an additional helper callback.
|
||||
*
|
||||
* @public
|
||||
* @param initialEdges
|
||||
* @returns an array [edges, setEdges, onEdgesChange]
|
||||
* @example
|
||||
*
|
||||
*```tsx
|
||||
*import { ReactFlow, useNodesState, useEdgesState } from '@xyflow/react';
|
||||
*
|
||||
*const initialNodes = [];
|
||||
*const initialEdges = [];
|
||||
*
|
||||
*export default function () {
|
||||
* const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||
* const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||
*
|
||||
* return (
|
||||
* <ReactFlow
|
||||
* nodes={nodes}
|
||||
* edges={edges}
|
||||
* onNodesChange={onNodesChange}
|
||||
* onEdgesChange={onEdgesChange}
|
||||
* />
|
||||
* );
|
||||
*}
|
||||
*```
|
||||
*
|
||||
* @remarks This hook was created to make prototyping easier and our documentation
|
||||
*examples clearer. Although it is OK to use this hook in production, in
|
||||
*practice you may want to use a more sophisticated state management solution
|
||||
*like Zustand {@link https://reactflow.dev/docs/guides/state-management/} instead.
|
||||
*
|
||||
*/
|
||||
export function useEdgesState<EdgeType extends Edge = Edge>(
|
||||
initialEdges: EdgeType[]
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { nodeHasDimensions } from '@xyflow/system';
|
||||
|
||||
import { useStore } from './useStore';
|
||||
import type { ReactFlowState } from '../types';
|
||||
import { nodeHasDimensions } from '@xyflow/system';
|
||||
|
||||
export type UseNodesInitializedOptions = {
|
||||
includeHiddenNodes?: boolean;
|
||||
@@ -22,18 +23,44 @@ const selector = (options: UseNodesInitializedOptions) => (s: ReactFlowState) =>
|
||||
return true;
|
||||
};
|
||||
|
||||
const defaultOptions = {
|
||||
includeHiddenNodes: false,
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook which returns true when all nodes are initialized.
|
||||
* This hook tells you whether all the nodes in a flow have been measured and given
|
||||
*a width and height. When you add a node to the flow, this hook will return
|
||||
*`false` and then `true` again once the node has been measured.
|
||||
*
|
||||
* @public
|
||||
* @param options.includeHiddenNodes - defaults to false
|
||||
* @returns boolean indicating whether all nodes are initialized
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
*import { useReactFlow, useNodesInitialized } from '@xyflow/react';
|
||||
*import { useEffect, useState } from 'react';
|
||||
*
|
||||
*const options = {
|
||||
* includeHiddenNodes: false,
|
||||
*};
|
||||
*
|
||||
*export default function useLayout() {
|
||||
* const { getNodes } = useReactFlow();
|
||||
* const nodesInitialized = useNodesInitialized(options);
|
||||
* const [layoutedNodes, setLayoutedNodes] = useState(getNodes());
|
||||
*
|
||||
* useEffect(() => {
|
||||
* if (nodesInitialized) {
|
||||
* setLayoutedNodes(yourLayoutingFunction(getNodes()));
|
||||
* }
|
||||
* }, [nodesInitialized]);
|
||||
*
|
||||
* return layoutedNodes;
|
||||
*}
|
||||
*```
|
||||
*/
|
||||
export function useNodesInitialized(options: UseNodesInitializedOptions = defaultOptions): boolean {
|
||||
export function useNodesInitialized(
|
||||
options: UseNodesInitializedOptions = {
|
||||
includeHiddenNodes: false,
|
||||
}
|
||||
): boolean {
|
||||
const initialized = useStore(selector(options));
|
||||
|
||||
return initialized;
|
||||
|
||||
@@ -8,10 +8,42 @@ export type UseOnSelectionChangeOptions = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook for registering an onSelectionChange handler.
|
||||
* This hook lets you listen for changes to both node and edge selection. As the
|
||||
*name implies, the callback you provide will be called whenever the selection of
|
||||
*_either_ nodes or edges changes.
|
||||
*
|
||||
* @public
|
||||
* @param params.onChange - The handler to register
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
*import { useState } from 'react';
|
||||
*import { ReactFlow, useOnSelectionChange } from '@xyflow/react';
|
||||
*
|
||||
*function SelectionDisplay() {
|
||||
* const [selectedNodes, setSelectedNodes] = useState([]);
|
||||
* const [selectedEdges, setSelectedEdges] = useState([]);
|
||||
*
|
||||
* // the passed handler has to be memoized, otherwise the hook will not work correctly
|
||||
* const onChange = useCallback(({ nodes, edges }) => {
|
||||
* setSelectedNodes(nodes.map((node) => node.id));
|
||||
* setSelectedEdges(edges.map((edge) => edge.id));
|
||||
* }, []);
|
||||
*
|
||||
* useOnSelectionChange({
|
||||
* onChange,
|
||||
* });
|
||||
*
|
||||
* return (
|
||||
* <div>
|
||||
* <p>Selected nodes: {selectedNodes.join(', ')}</p>
|
||||
* <p>Selected edges: {selectedEdges.join(', ')}</p>
|
||||
* </div>
|
||||
* );
|
||||
*}
|
||||
*```
|
||||
*
|
||||
* @remarks You need to memoize the passed `onChange` handler, otherwise the hook will not work correctly.
|
||||
*/
|
||||
export function useOnSelectionChange({ onChange }: UseOnSelectionChangeOptions) {
|
||||
const store = useStoreApi();
|
||||
|
||||
@@ -10,12 +10,30 @@ export type UseOnViewportChangeOptions = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook for registering an onViewportChange handler.
|
||||
* The `useOnViewportChange` hook lets you listen for changes to the viewport such
|
||||
*as panning and zooming. You can provide a callback for each phase of a viewport
|
||||
*change: `onStart`, `onChange`, and `onEnd`.
|
||||
*
|
||||
* @public
|
||||
* @param params.onStart - gets called when the viewport starts changing
|
||||
* @param params.onChange - gets called when the viewport changes
|
||||
* @param params.onEnd - gets called when the viewport stops changing
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
*import { useCallback } from 'react';
|
||||
*import { useOnViewportChange } from '@xyflow/react';
|
||||
*
|
||||
*function ViewportChangeLogger() {
|
||||
* useOnViewportChange({
|
||||
* onStart: (viewport: Viewport) => console.log('start', viewport),
|
||||
* onChange: (viewport: Viewport) => console.log('change', viewport),
|
||||
* onEnd: (viewport: Viewport) => console.log('end', viewport),
|
||||
* });
|
||||
*
|
||||
* return null;
|
||||
*}
|
||||
*```
|
||||
*/
|
||||
export function useOnViewportChange({ onStart, onChange, onEnd }: UseOnViewportChangeOptions) {
|
||||
const store = useStoreApi();
|
||||
|
||||
@@ -20,10 +20,33 @@ import type { ReactFlowInstance, Node, Edge, InternalNode, ReactFlowState, Gener
|
||||
const selector = (s: ReactFlowState) => !!s.panZoom;
|
||||
|
||||
/**
|
||||
* Hook for accessing the ReactFlow instance.
|
||||
* This hook returns a ReactFlowInstance that can be used to update nodes and edges, manipulate the viewport, or query the current state of the flow.
|
||||
*
|
||||
* @public
|
||||
* @returns ReactFlowInstance
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
*import { useCallback, useState } from 'react';
|
||||
*import { useReactFlow } from '@xyflow/react';
|
||||
*
|
||||
*export function NodeCounter() {
|
||||
* const reactFlow = useReactFlow();
|
||||
* const [count, setCount] = useState(0);
|
||||
* const countNodes = useCallback(() => {
|
||||
* setCount(reactFlow.getNodes().length);
|
||||
* // you need to pass it as a dependency if you are using it with useEffect or useCallback
|
||||
* // because at the first render, it's not initialized yet and some functions might not work.
|
||||
* }, [reactFlow]);
|
||||
*
|
||||
* return (
|
||||
* <div>
|
||||
* <button onClick={countNodes}>Update count</button>
|
||||
* <p>There are {count} nodes in the flow.</p>
|
||||
* </div>
|
||||
* );
|
||||
*}
|
||||
*```
|
||||
*/
|
||||
export function useReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(): ReactFlowInstance<
|
||||
NodeType,
|
||||
|
||||
@@ -9,7 +9,9 @@ import type { Edge, Node, ReactFlowState } from '../types';
|
||||
const zustandErrorMessage = errorMessages['error001']();
|
||||
|
||||
/**
|
||||
* Hook for accessing the internal store. Should only be used in rare cases.
|
||||
* This hook can be used to subscribe to internal state changes of the React Flow
|
||||
*component. The `useStore` hook is re-exported from the [Zustand](https://github.com/pmndrs/zustand)
|
||||
*state management library, so you should check out their docs for more details.
|
||||
*
|
||||
* @public
|
||||
* @param selector
|
||||
@@ -17,8 +19,13 @@ const zustandErrorMessage = errorMessages['error001']();
|
||||
* @returns The selected state slice
|
||||
*
|
||||
* @example
|
||||
* const nodes = useStore((state: ReactFlowState<MyNodeType>) => state.nodes);
|
||||
* ```ts
|
||||
* const nodes = useStore((state) => state.nodes);
|
||||
* ```
|
||||
*
|
||||
* @remarks This hook should only be used if there is no other way to access the internal
|
||||
*state. For many of the common use cases, there are dedicated hooks available
|
||||
*such as {@link useReactFlow}, {@link useViewport}, etc.
|
||||
*/
|
||||
function useStore<StateSlice = unknown>(
|
||||
selector: (state: ReactFlowState) => StateSlice,
|
||||
@@ -33,6 +40,20 @@ function useStore<StateSlice = unknown>(
|
||||
return useZustandStore(store, selector, equalityFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* In some cases, you might need to access the store directly. This hook returns the store object which can be used on demand to access the state or dispatch actions.
|
||||
*
|
||||
* @returns The store object
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* const store = useStoreApi();
|
||||
* ```
|
||||
*
|
||||
* @remarks This hook should only be used if there is no other way to access the internal
|
||||
*state. For many of the common use cases, there are dedicated hooks available
|
||||
*such as {@link useReactFlow}, {@link useViewport}, etc.
|
||||
*/
|
||||
function useStoreApi<NodeType extends Node = Node, EdgeType extends Edge = Edge>() {
|
||||
const store = useContext(StoreContext) as UseBoundStoreWithEqualityFn<
|
||||
StoreApi<ReactFlowState<NodeType, EdgeType>>
|
||||
|
||||
@@ -4,10 +4,48 @@ import type { UpdateNodeInternals, InternalNodeUpdate } from '@xyflow/system';
|
||||
import { useStoreApi } from '../hooks/useStore';
|
||||
|
||||
/**
|
||||
* Hook for updating node internals.
|
||||
* When you programmatically add or remove handles to a node or update a node's
|
||||
*handle position, you need to let React Flow know about it using this hook. This
|
||||
*will update the internal dimensions of the node and properly reposition handles
|
||||
*on the canvas if necessary.
|
||||
*
|
||||
* @public
|
||||
* @returns function for updating node internals
|
||||
*
|
||||
* @example
|
||||
* ```jsx
|
||||
*import { useCallback, useState } from 'react';
|
||||
*import { Handle, useUpdateNodeInternals } from '@xyflow/react';
|
||||
*
|
||||
*export default function RandomHandleNode({ id }) {
|
||||
* const updateNodeInternals = useUpdateNodeInternals();
|
||||
* const [handleCount, setHandleCount] = useState(0);
|
||||
* const randomizeHandleCount = useCallback(() => {
|
||||
* setHandleCount(Math.floor(Math.random() * 10));
|
||||
* updateNodeInternals(id);
|
||||
* }, [id, updateNodeInternals]);
|
||||
*
|
||||
* return (
|
||||
* <>
|
||||
* {Array.from({ length: handleCount }).map((_, index) => (
|
||||
* <Handle
|
||||
* key={index}
|
||||
* type="target"
|
||||
* position="left"
|
||||
* id={`handle-${index}`}
|
||||
* />
|
||||
* ))}
|
||||
*
|
||||
* <div>
|
||||
* <button onClick={randomizeHandleCount}>Randomize handle count</button>
|
||||
* <p>There are {handleCount} handles on this node.</p>
|
||||
* </div>
|
||||
* </>
|
||||
* );
|
||||
*}
|
||||
*```
|
||||
* @remarks This hook can only be used in a component that is a child of a
|
||||
*{@link ReactFlowProvider} or a {@link ReactFlow} component.
|
||||
*/
|
||||
export function useUpdateNodeInternals(): UpdateNodeInternals {
|
||||
const store = useStoreApi();
|
||||
|
||||
@@ -11,10 +11,33 @@ const viewportSelector = (state: ReactFlowState) => ({
|
||||
});
|
||||
|
||||
/**
|
||||
* Hook for getting the current viewport from the store.
|
||||
* The `useViewport` hook is a convenient way to read the current state of the
|
||||
*{@link Viewport} in a component. Components that use this hook
|
||||
*will re-render **whenever the viewport changes**.
|
||||
*
|
||||
* @public
|
||||
* @returns The current viewport
|
||||
*
|
||||
* @example
|
||||
*
|
||||
*```jsx
|
||||
*import { useViewport } from '@xyflow/react';
|
||||
*
|
||||
*export default function ViewportDisplay() {
|
||||
* const { x, y, zoom } = useViewport();
|
||||
*
|
||||
* return (
|
||||
* <div>
|
||||
* <p>
|
||||
* The viewport is currently at ({x}, {y}) and zoomed to {zoom}.
|
||||
* </p>
|
||||
* </div>
|
||||
* );
|
||||
*}
|
||||
*```
|
||||
*
|
||||
* @remarks This hook can only be used in a component that is a child of a
|
||||
*{@link ReactFlowProvider} or a {@link ReactFlow} component.
|
||||
*/
|
||||
export function useViewport(): Viewport {
|
||||
const viewport = useStore(viewportSelector, shallow);
|
||||
|
||||
@@ -8,8 +8,8 @@ import type { Node, ReactFlowState } from '../types';
|
||||
const selector = (onlyRenderVisible: boolean) => (s: ReactFlowState) => {
|
||||
return onlyRenderVisible
|
||||
? getNodesInside<Node>(s.nodeLookup, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true).map(
|
||||
(node) => node.id
|
||||
)
|
||||
(node) => node.id
|
||||
)
|
||||
: Array.from(s.nodeLookup.keys());
|
||||
};
|
||||
|
||||
|
||||
@@ -47,12 +47,14 @@ const createStore = ({
|
||||
...getInitialState({ nodes, edges, width, height, fitView, nodeOrigin, nodeExtent, defaultNodes, defaultEdges }),
|
||||
setNodes: (nodes: Node[]) => {
|
||||
const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
||||
// setNodes() is called exclusively in response to user actions:
|
||||
// - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
|
||||
// - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup.
|
||||
//
|
||||
// When this happens, we take the note objects passed by the user and extend them with fields
|
||||
// relevant for internal React Flow operations.
|
||||
/*
|
||||
* setNodes() is called exclusively in response to user actions:
|
||||
* - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
|
||||
* - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup.
|
||||
*
|
||||
* When this happens, we take the note objects passed by the user and extend them with fields
|
||||
* relevant for internal React Flow operations.
|
||||
*/
|
||||
adoptUserNodes(nodes, nodeLookup, parentLookup, {
|
||||
nodeOrigin,
|
||||
nodeExtent,
|
||||
@@ -81,9 +83,11 @@ const createStore = ({
|
||||
set({ hasDefaultEdges: true });
|
||||
}
|
||||
},
|
||||
// Every node gets registerd at a ResizeObserver. Whenever a node
|
||||
// changes its dimensions, this function is called to measure the
|
||||
// new dimensions and update the nodes.
|
||||
/*
|
||||
* Every node gets registerd at a ResizeObserver. Whenever a node
|
||||
* changes its dimensions, this function is called to measure the
|
||||
* new dimensions and update the nodes.
|
||||
*/
|
||||
updateNodeInternals: (updates, params = { triggerFitView: true }) => {
|
||||
const {
|
||||
triggerNodeChanges,
|
||||
@@ -125,11 +129,13 @@ const createStore = ({
|
||||
});
|
||||
}
|
||||
|
||||
// here we are cirmumventing the onNodesChange handler
|
||||
// in order to be able to display nodes even if the user
|
||||
// has not provided an onNodesChange handler.
|
||||
// Nodes are only rendered if they have a width and height
|
||||
// attribute which they get from this handler.
|
||||
/*
|
||||
* here we are cirmumventing the onNodesChange handler
|
||||
* in order to be able to display nodes even if the user
|
||||
* has not provided an onNodesChange handler.
|
||||
* Nodes are only rendered if they have a width and height
|
||||
* attribute which they get from this handler.
|
||||
*/
|
||||
set({ fitViewDone: nextFitViewDone });
|
||||
} else {
|
||||
// we always want to trigger useStore calls whenever updateNodeInternals is called
|
||||
@@ -155,9 +161,9 @@ const createStore = ({
|
||||
type: 'position',
|
||||
position: expandParent
|
||||
? {
|
||||
x: Math.max(0, dragItem.position.x),
|
||||
y: Math.max(0, dragItem.position.y),
|
||||
}
|
||||
x: Math.max(0, dragItem.position.x),
|
||||
y: Math.max(0, dragItem.position.y),
|
||||
}
|
||||
: dragItem.position,
|
||||
dragging,
|
||||
};
|
||||
@@ -248,8 +254,10 @@ const createStore = ({
|
||||
const nodeChanges = nodesToUnselect.map((n) => {
|
||||
const internalNode = nodeLookup.get(n.id);
|
||||
if (internalNode) {
|
||||
// we need to unselect the internal node that was selected previously before we
|
||||
// send the change to the user to prevent it to be selected while dragging the new node
|
||||
/*
|
||||
* we need to unselect the internal node that was selected previously before we
|
||||
* send the change to the user to prevent it to be selected while dragging the new node
|
||||
*/
|
||||
internalNode.selected = false;
|
||||
}
|
||||
|
||||
@@ -342,8 +350,10 @@ const createStore = ({
|
||||
options
|
||||
);
|
||||
},
|
||||
// we can't call an asnychronous function in updateNodeInternals
|
||||
// for that we created this sync version of fitView
|
||||
/*
|
||||
* we can't call an asnychronous function in updateNodeInternals
|
||||
* for that we created this sync version of fitView
|
||||
*/
|
||||
fitViewSync: (options?: FitViewOptions): boolean => {
|
||||
const { panZoom, width, height, minZoom, maxZoom, nodeLookup } = get();
|
||||
|
||||
|
||||
@@ -52,7 +52,8 @@ import type {
|
||||
*/
|
||||
export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends Edge = Edge>
|
||||
extends Omit<HTMLAttributes<HTMLDivElement>, 'onError'> {
|
||||
/** An array of nodes to render in a controlled flow.
|
||||
/**
|
||||
* An array of nodes to render in a controlled flow.
|
||||
* @example
|
||||
* const nodes = [
|
||||
* {
|
||||
@@ -64,7 +65,8 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
||||
* ];
|
||||
*/
|
||||
nodes?: NodeType[];
|
||||
/** An array of edges to render in a controlled flow.
|
||||
/**
|
||||
* An array of edges to render in a controlled flow.
|
||||
* @example
|
||||
* const edges = [
|
||||
* {
|
||||
@@ -79,7 +81,8 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
||||
defaultNodes?: NodeType[];
|
||||
/** The initial edges to render in an uncontrolled flow. */
|
||||
defaultEdges?: EdgeType[];
|
||||
/** Defaults to be applied to all new edges that are added to the flow.
|
||||
/**
|
||||
* Defaults to be applied to all new edges that are added to the flow.
|
||||
*
|
||||
* Properties on a new edge will override these defaults if they exist.
|
||||
* @example
|
||||
@@ -132,7 +135,8 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
||||
onReconnect?: OnReconnect<EdgeType>;
|
||||
onReconnectStart?: (event: ReactMouseEvent, edge: EdgeType, handleType: HandleType) => void;
|
||||
onReconnectEnd?: (event: MouseEvent | TouchEvent, edge: EdgeType, handleType: HandleType) => void;
|
||||
/** This event handler is called when a Node is updated
|
||||
/**
|
||||
* This event handler is called when a Node is updated
|
||||
* @example // Use NodesState hook to create edges and get onNodesChange handler
|
||||
* import ReactFlow, { useNodesState } from '@xyflow/react';
|
||||
* const [edges, setNodes, onNodesChange] = useNodesState(initialNodes);
|
||||
@@ -149,7 +153,8 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
||||
* return (<ReactFlow onNodeChange={onNodeChange} {...rest} />)
|
||||
*/
|
||||
onNodesChange?: OnNodesChange<NodeType>;
|
||||
/** This event handler is called when a Edge is updated
|
||||
/**
|
||||
* This event handler is called when a Edge is updated
|
||||
* @example // Use EdgesState hook to create edges and get onEdgesChange handler
|
||||
* import ReactFlow, { useEdgesState } from '@xyflow/react';
|
||||
* const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||
@@ -181,7 +186,8 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
||||
onSelectionStart?: (event: ReactMouseEvent) => void;
|
||||
onSelectionEnd?: (event: ReactMouseEvent) => void;
|
||||
onSelectionContextMenu?: (event: ReactMouseEvent, nodes: NodeType[]) => void;
|
||||
/** When a connection line is completed and two nodes are connected by the user, this event fires with the new connection.
|
||||
/**
|
||||
* When a connection line is completed and two nodes are connected by the user, this event fires with the new connection.
|
||||
*
|
||||
* You can use the addEdge utility to convert the connection to a complete edge.
|
||||
* @example // Use helper function to update edges onConnect
|
||||
@@ -223,17 +229,20 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
||||
onPaneMouseMove?: (event: ReactMouseEvent) => void;
|
||||
/** This event handler gets called when mouse leaves the pane */
|
||||
onPaneMouseLeave?: (event: ReactMouseEvent) => void;
|
||||
/** Distance that the mouse can move between mousedown/up that will trigger a click
|
||||
/**
|
||||
* Distance that the mouse can move between mousedown/up that will trigger a click
|
||||
* @default 0
|
||||
*/
|
||||
paneClickDistance?: number;
|
||||
/** Distance that the mouse can move between mousedown/up that will trigger a click
|
||||
/**
|
||||
* Distance that the mouse can move between mousedown/up that will trigger a click
|
||||
* @default 0
|
||||
*/
|
||||
nodeClickDistance?: number;
|
||||
/** This handler gets called before the user deletes nodes or edges and provides a way to abort the deletion by returning false. */
|
||||
onBeforeDelete?: OnBeforeDelete<NodeType, EdgeType>;
|
||||
/** Custom node types to be available in a flow.
|
||||
/**
|
||||
* Custom node types to be available in a flow.
|
||||
*
|
||||
* React Flow matches a node's type to a component in the nodeTypes object.
|
||||
* @example
|
||||
@@ -242,7 +251,8 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
||||
* const nodeTypes = { nameOfNodeType: CustomNode };
|
||||
*/
|
||||
nodeTypes?: NodeTypes;
|
||||
/** Custom edge types to be available in a flow.
|
||||
/**
|
||||
* Custom edge types to be available in a flow.
|
||||
*
|
||||
* React Flow matches an edge's type to a component in the edgeTypes object.
|
||||
* @example
|
||||
@@ -251,7 +261,8 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
||||
* const edgeTypes = { nameOfEdgeType: CustomEdge };
|
||||
*/
|
||||
edgeTypes?: EdgeTypes;
|
||||
/** The type of edge path to use for connection lines.
|
||||
/**
|
||||
* The type of edge path to use for connection lines.
|
||||
*
|
||||
* Although created edges can be of any type, React Flow needs to know what type of path to render for the connection line before the edge is created!
|
||||
*/
|
||||
@@ -262,17 +273,20 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
||||
connectionLineComponent?: ConnectionLineComponent;
|
||||
/** Styles to be applied to the container of the connection line */
|
||||
connectionLineContainerStyle?: CSSProperties;
|
||||
/** 'strict' connection mode will only allow you to connect source handles to target handles.
|
||||
/**
|
||||
* 'strict' connection mode will only allow you to connect source handles to target handles.
|
||||
*
|
||||
* 'loose' connection mode will allow you to connect handles of any type to one another.
|
||||
* @default 'strict'
|
||||
*/
|
||||
connectionMode?: ConnectionMode;
|
||||
/** Pressing down this key deletes all selected nodes & edges.
|
||||
/**
|
||||
* Pressing down this key deletes all selected nodes & edges.
|
||||
* @default 'Backspace'
|
||||
*/
|
||||
deleteKeyCode?: KeyCode | null;
|
||||
/** If a key is set, you can pan the viewport while that key is held down even if panOnScroll is set to false.
|
||||
/**
|
||||
* If a key is set, you can pan the viewport while that key is held down even if panOnScroll is set to false.
|
||||
*
|
||||
* By setting this prop to null you can disable this functionality.
|
||||
* @default 'Space'
|
||||
@@ -280,91 +294,110 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
||||
selectionKeyCode?: KeyCode | null;
|
||||
/** Select multiple elements with a selection box, without pressing down selectionKey */
|
||||
selectionOnDrag?: boolean;
|
||||
/** When set to "partial", when the user creates a selection box by click and dragging nodes that are only partially in the box are still selected.
|
||||
/**
|
||||
* When set to "partial", when the user creates a selection box by click and dragging nodes that are only partially in the box are still selected.
|
||||
* @default 'full'
|
||||
*/
|
||||
selectionMode?: SelectionMode;
|
||||
/** If a key is set, you can pan the viewport while that key is held down even if panOnScroll is set to false.
|
||||
/**
|
||||
* If a key is set, you can pan the viewport while that key is held down even if panOnScroll is set to false.
|
||||
*
|
||||
* By setting this prop to null you can disable this functionality.
|
||||
* @default 'Space'
|
||||
*/
|
||||
panActivationKeyCode?: KeyCode | null;
|
||||
/** Pressing down this key you can select multiple elements by clicking.
|
||||
/**
|
||||
* Pressing down this key you can select multiple elements by clicking.
|
||||
* @default 'Meta' for macOS, "Ctrl" for other systems
|
||||
*/
|
||||
multiSelectionKeyCode?: KeyCode | null;
|
||||
/** If a key is set, you can zoom the viewport while that key is held down even if panOnScroll is set to false.
|
||||
/**
|
||||
* If a key is set, you can zoom the viewport while that key is held down even if panOnScroll is set to false.
|
||||
*
|
||||
* By setting this prop to null you can disable this functionality.
|
||||
* @default 'Meta' for macOS, "Ctrl" for other systems
|
||||
* */
|
||||
*
|
||||
*/
|
||||
zoomActivationKeyCode?: KeyCode | null;
|
||||
/** Set this prop to make the flow snap to the grid */
|
||||
snapToGrid?: boolean;
|
||||
/** Grid all nodes will snap to
|
||||
/**
|
||||
* Grid all nodes will snap to
|
||||
* @example [20, 20]
|
||||
*/
|
||||
snapGrid?: SnapGrid;
|
||||
/** You can enable this optimisation to instruct Svelte Flow to only render nodes and edges that would be visible in the viewport.
|
||||
/**
|
||||
* You can enable this optimisation to instruct Svelte Flow to only render nodes and edges that would be visible in the viewport.
|
||||
*
|
||||
* This might improve performance when you have a large number of nodes and edges but also adds an overhead.
|
||||
* @default false
|
||||
*/
|
||||
onlyRenderVisibleElements?: boolean;
|
||||
/** Controls if all nodes should be draggable
|
||||
/**
|
||||
* Controls if all nodes should be draggable
|
||||
* @default true
|
||||
*/
|
||||
nodesDraggable?: boolean;
|
||||
/** Controls if all nodes should be connectable to each other
|
||||
/**
|
||||
* Controls if all nodes should be connectable to each other
|
||||
* @default true
|
||||
*/
|
||||
nodesConnectable?: boolean;
|
||||
/** Controls if all nodes should be focusable
|
||||
/**
|
||||
* Controls if all nodes should be focusable
|
||||
* @default true
|
||||
*/
|
||||
nodesFocusable?: boolean;
|
||||
/** Defines nodes relative position to its coordinates
|
||||
/**
|
||||
* Defines nodes relative position to its coordinates
|
||||
* @example
|
||||
* [0, 0] // default, top left
|
||||
* [0.5, 0.5] // center
|
||||
* [1, 1] // bottom right
|
||||
*/
|
||||
nodeOrigin?: NodeOrigin;
|
||||
/** Controls if all edges should be focusable
|
||||
/**
|
||||
* Controls if all edges should be focusable
|
||||
* @default true
|
||||
*/
|
||||
edgesFocusable?: boolean;
|
||||
/** Controls if all edges should be updateable
|
||||
/**
|
||||
* Controls if all edges should be updateable
|
||||
* @default true
|
||||
*/
|
||||
edgesReconnectable?: boolean;
|
||||
/** Controls if all elements should (nodes & edges) be selectable
|
||||
/**
|
||||
* Controls if all elements should (nodes & edges) be selectable
|
||||
* @default true
|
||||
*/
|
||||
elementsSelectable?: boolean;
|
||||
/** If true, nodes get selected on drag
|
||||
/**
|
||||
* If true, nodes get selected on drag
|
||||
* @default true
|
||||
*/
|
||||
selectNodesOnDrag?: boolean;
|
||||
/** Enableing this prop allows users to pan the viewport by clicking and dragging.
|
||||
/**
|
||||
* Enableing this prop allows users to pan the viewport by clicking and dragging.
|
||||
*
|
||||
* You can also set this prop to an array of numbers to limit which mouse buttons can activate panning.
|
||||
* @example [0, 2] // allows panning with the left and right mouse buttons
|
||||
* [0, 1, 2, 3, 4] // allows panning with all mouse buttons
|
||||
*/
|
||||
panOnDrag?: boolean | number[];
|
||||
/** Minimum zoom level
|
||||
/**
|
||||
* Minimum zoom level
|
||||
* @default 0.5
|
||||
*/
|
||||
minZoom?: number;
|
||||
/** Maximum zoom level
|
||||
/**
|
||||
* Maximum zoom level
|
||||
* @default 2
|
||||
*/
|
||||
maxZoom?: number;
|
||||
/** Controlled viewport to be used instead of internal one */
|
||||
viewport?: Viewport;
|
||||
/** Sets the initial position and zoom of the viewport.
|
||||
/**
|
||||
* Sets the initial position and zoom of the viewport.
|
||||
*
|
||||
* If a default viewport is provided but fitView is enabled, the default viewport will be ignored.
|
||||
* @example
|
||||
@@ -378,23 +411,27 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
||||
* Gets called when the viewport changes.
|
||||
*/
|
||||
onViewportChange?: (viewport: Viewport) => void;
|
||||
/** By default the viewport extends infinitely. You can use this prop to set a boundary.
|
||||
/**
|
||||
* By default the viewport extends infinitely. You can use this prop to set a boundary.
|
||||
*
|
||||
* The first pair of coordinates is the top left boundary and the second pair is the bottom right.
|
||||
* @example [[-1000, -10000], [1000, 1000]]
|
||||
*/
|
||||
translateExtent?: CoordinateExtent;
|
||||
/** Disabling this prop will allow the user to scroll the page even when their pointer is over the flow.
|
||||
/**
|
||||
* Disabling this prop will allow the user to scroll the page even when their pointer is over the flow.
|
||||
* @default true
|
||||
*/
|
||||
preventScrolling?: boolean;
|
||||
/** By default nodes can be placed on an infinite flow. You can use this prop to set a boundary.
|
||||
/**
|
||||
* By default nodes can be placed on an infinite flow. You can use this prop to set a boundary.
|
||||
*
|
||||
* The first pair of coordinates is the top left boundary and the second pair is the bottom right.
|
||||
* @example [[-1000, -10000], [1000, 1000]]
|
||||
*/
|
||||
nodeExtent?: CoordinateExtent;
|
||||
/** Color of edge markers
|
||||
/**
|
||||
* Color of edge markers
|
||||
* @example "#b1b1b7"
|
||||
*/
|
||||
defaultMarkerColor?: string;
|
||||
@@ -402,17 +439,20 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
||||
zoomOnScroll?: boolean;
|
||||
/** Controls if the viewport should zoom by pinching on a touch screen */
|
||||
zoomOnPinch?: boolean;
|
||||
/** Controls if the viewport should pan by scrolling inside the container
|
||||
/**
|
||||
* Controls if the viewport should pan by scrolling inside the container
|
||||
*
|
||||
* Can be limited to a specific direction with panOnScrollMode
|
||||
*/
|
||||
panOnScroll?: boolean;
|
||||
/** Controls how fast viewport should be panned on scroll.
|
||||
/**
|
||||
* Controls how fast viewport should be panned on scroll.
|
||||
*
|
||||
* Use togther with panOnScroll prop.
|
||||
*/
|
||||
panOnScrollSpeed?: number;
|
||||
/** This prop is used to limit the direction of panning when panOnScroll is enabled.
|
||||
/**
|
||||
* This prop is used to limit the direction of panning when panOnScroll is enabled.
|
||||
*
|
||||
* The "free" option allows panning in any direction.
|
||||
* @default "free"
|
||||
@@ -427,7 +467,8 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
||||
noPanClassName?: string;
|
||||
/** If set, initial viewport will show all nodes & edges */
|
||||
fitView?: boolean;
|
||||
/** Options to be used in combination with fitView
|
||||
/**
|
||||
* Options to be used in combination with fitView
|
||||
* @example
|
||||
* const fitViewOptions = {
|
||||
* padding: 0.1,
|
||||
@@ -439,30 +480,35 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
||||
* };
|
||||
*/
|
||||
fitViewOptions?: FitViewOptions;
|
||||
/**The connectOnClick option lets you click or tap on a source handle to start a connection
|
||||
/**
|
||||
*The connectOnClick option lets you click or tap on a source handle to start a connection
|
||||
* and then click on a target handle to complete the connection.
|
||||
*
|
||||
* If you set this option to false, users will need to drag the connection line to the target
|
||||
* handle to create a connection.
|
||||
*/
|
||||
connectOnClick?: boolean;
|
||||
/** Set position of the attribution
|
||||
/**
|
||||
* Set position of the attribution
|
||||
* @default 'bottom-right'
|
||||
* @example 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right'
|
||||
*/
|
||||
attributionPosition?: PanelPosition;
|
||||
/** By default, we render a small attribution in the corner of your flows that links back to the project.
|
||||
/**
|
||||
* By default, we render a small attribution in the corner of your flows that links back to the project.
|
||||
*
|
||||
* Anyone is free to remove this attribution whether they're a Pro subscriber or not
|
||||
* but we ask that you take a quick look at our {@link https://reactflow.dev/learn/troubleshooting/remove-attribution | removing attribution guide}
|
||||
* before doing so.
|
||||
*/
|
||||
proOptions?: ProOptions;
|
||||
/** Enabling this option will raise the z-index of nodes when they are selected.
|
||||
/**
|
||||
* Enabling this option will raise the z-index of nodes when they are selected.
|
||||
* @default true
|
||||
*/
|
||||
elevateNodesOnSelect?: boolean;
|
||||
/** Enabling this option will raise the z-index of edges when they are selected.
|
||||
/**
|
||||
* Enabling this option will raise the z-index of edges when they are selected.
|
||||
* @default true
|
||||
*/
|
||||
elevateEdgesOnSelect?: boolean;
|
||||
@@ -471,36 +517,43 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
||||
* @default false
|
||||
*/
|
||||
disableKeyboardA11y?: boolean;
|
||||
/** You can enable this prop to automatically pan the viewport while dragging a node.
|
||||
/**
|
||||
* You can enable this prop to automatically pan the viewport while dragging a node.
|
||||
* @default true
|
||||
*/
|
||||
autoPanOnNodeDrag?: boolean;
|
||||
/** You can enable this prop to automatically pan the viewport while dragging a node.
|
||||
/**
|
||||
* You can enable this prop to automatically pan the viewport while dragging a node.
|
||||
* @default true
|
||||
*/
|
||||
autoPanOnConnect?: boolean;
|
||||
/** The speed at which the viewport pans while dragging a node or a selection box.
|
||||
/**
|
||||
* The speed at which the viewport pans while dragging a node or a selection box.
|
||||
* @default 15
|
||||
*/
|
||||
autoPanSpeed?: number;
|
||||
/** You can enable this prop to automatically pan the viewport while making a new connection.
|
||||
/**
|
||||
* You can enable this prop to automatically pan the viewport while making a new connection.
|
||||
* @default true
|
||||
*/
|
||||
connectionRadius?: number;
|
||||
/** Ocassionally something may happen that causes Svelte Flow to throw an error.
|
||||
/**
|
||||
* Ocassionally something may happen that causes Svelte Flow to throw an error.
|
||||
*
|
||||
* Instead of exploding your application, we log a message to the console and then call this event handler.
|
||||
* You might use it for additional logging or to show a message to the user.
|
||||
*/
|
||||
onError?: OnError;
|
||||
/** This callback can be used to validate a new connection
|
||||
/**
|
||||
* This callback can be used to validate a new connection
|
||||
*
|
||||
* If you return false, the edge will not be added to your flow.
|
||||
* If you have custom connection logic its preferred to use this callback over the isValidConnection prop on the handle component for performance reasons.
|
||||
* @default (connection: Connection) => true
|
||||
*/
|
||||
isValidConnection?: IsValidConnection<EdgeType>;
|
||||
/** With a threshold greater than zero you can control the distinction between node drag and click events.
|
||||
/**
|
||||
* With a threshold greater than zero you can control the distinction between node drag and click events.
|
||||
*
|
||||
* If threshold equals 1, you need to drag the node 1 pixel before a drag event is fired.
|
||||
* @default 1
|
||||
@@ -510,12 +563,14 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
||||
width?: number;
|
||||
/** Sets a fixed height for the flow */
|
||||
height?: number;
|
||||
/** Controls color scheme used for styling the flow
|
||||
/**
|
||||
* Controls color scheme used for styling the flow
|
||||
* @default 'system'
|
||||
* @example 'system' | 'light' | 'dark'
|
||||
*/
|
||||
colorMode?: ColorMode;
|
||||
/** If set true, some debug information will be logged to the console like which events are fired.
|
||||
/**
|
||||
* If set true, some debug information will be logged to the console like which events are fired.
|
||||
*
|
||||
* @default undefined
|
||||
*/
|
||||
|
||||
@@ -11,13 +11,17 @@ import {
|
||||
} from '@xyflow/system';
|
||||
import type { Node, Edge, InternalNode } from '../types';
|
||||
|
||||
// This function applies changes to nodes or edges that are triggered by React Flow internally.
|
||||
// When you drag a node for example, React Flow will send a position change update.
|
||||
// This function then applies the changes and returns the updated elements.
|
||||
/*
|
||||
* This function applies changes to nodes or edges that are triggered by React Flow internally.
|
||||
* When you drag a node for example, React Flow will send a position change update.
|
||||
* This function then applies the changes and returns the updated elements.
|
||||
*/
|
||||
function applyChanges(changes: any[], elements: any[]): any[] {
|
||||
const updatedElements: any[] = [];
|
||||
// By storing a map of changes for each element, we can a quick lookup as we
|
||||
// iterate over the elements array!
|
||||
/*
|
||||
* By storing a map of changes for each element, we can a quick lookup as we
|
||||
* iterate over the elements array!
|
||||
*/
|
||||
const changesMap = new Map<any, any[]>();
|
||||
const addItemChanges: any[] = [];
|
||||
|
||||
@@ -26,15 +30,19 @@ function applyChanges(changes: any[], elements: any[]): any[] {
|
||||
addItemChanges.push(change);
|
||||
continue;
|
||||
} else if (change.type === 'remove' || change.type === 'replace') {
|
||||
// For a 'remove' change we can safely ignore any other changes queued for
|
||||
// the same element, it's going to be removed anyway!
|
||||
/*
|
||||
* For a 'remove' change we can safely ignore any other changes queued for
|
||||
* the same element, it's going to be removed anyway!
|
||||
*/
|
||||
changesMap.set(change.id, [change]);
|
||||
} else {
|
||||
const elementChanges = changesMap.get(change.id);
|
||||
|
||||
if (elementChanges) {
|
||||
// If we have some changes queued already, we can do a mutable update of
|
||||
// that array and save ourselves some copying.
|
||||
/*
|
||||
* If we have some changes queued already, we can do a mutable update of
|
||||
* that array and save ourselves some copying.
|
||||
*/
|
||||
elementChanges.push(change);
|
||||
} else {
|
||||
changesMap.set(change.id, [change]);
|
||||
@@ -45,8 +53,10 @@ function applyChanges(changes: any[], elements: any[]): any[] {
|
||||
for (const element of elements) {
|
||||
const changes = changesMap.get(element.id);
|
||||
|
||||
// When there are no changes for an element we can just push it unmodified,
|
||||
// no need to copy it.
|
||||
/*
|
||||
* When there are no changes for an element we can just push it unmodified,
|
||||
* no need to copy it.
|
||||
*/
|
||||
if (!changes) {
|
||||
updatedElements.push(element);
|
||||
continue;
|
||||
@@ -62,9 +72,11 @@ function applyChanges(changes: any[], elements: any[]): any[] {
|
||||
continue;
|
||||
}
|
||||
|
||||
// For other types of changes, we want to start with a shallow copy of the
|
||||
// object so React knows this element has changed. Sequential changes will
|
||||
/// each _mutate_ this object, so there's only ever one copy.
|
||||
/**
|
||||
* For other types of changes, we want to start with a shallow copy of the
|
||||
* object so React knows this element has changed. Sequential changes will
|
||||
* each _mutate_ this object, so there's only ever one copy.
|
||||
*/
|
||||
const updatedElement = { ...element };
|
||||
|
||||
for (const change of changes) {
|
||||
@@ -74,8 +86,10 @@ function applyChanges(changes: any[], elements: any[]): any[] {
|
||||
updatedElements.push(updatedElement);
|
||||
}
|
||||
|
||||
// we need to wait for all changes to be applied before adding new items
|
||||
// to be able to add them at the correct index
|
||||
/*
|
||||
* we need to wait for all changes to be applied before adding new items
|
||||
* to be able to add them at the correct index
|
||||
*/
|
||||
if (addItemChanges.length) {
|
||||
addItemChanges.forEach((change) => {
|
||||
if (change.index !== undefined) {
|
||||
@@ -134,21 +148,21 @@ function applyChange(change: any, element: any): any {
|
||||
* Drop in function that applies node changes to an array of nodes.
|
||||
* @public
|
||||
* @remarks Various events on the <ReactFlow /> component can produce an {@link NodeChange} that describes how to update the edges of your flow in some way.
|
||||
If you don't need any custom behaviour, this util can be used to take an array of these changes and apply them to your edges.
|
||||
*If you don't need any custom behaviour, this util can be used to take an array of these changes and apply them to your edges.
|
||||
* @param changes - Array of changes to apply
|
||||
* @param nodes - Array of nodes to apply the changes to
|
||||
* @returns Array of updated nodes
|
||||
* @example
|
||||
* const onNodesChange = useCallback(
|
||||
(changes) => {
|
||||
setNodes((oldNodes) => applyNodeChanges(changes, oldNodes));
|
||||
},
|
||||
[setNodes],
|
||||
);
|
||||
|
||||
return (
|
||||
<ReactFLow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />
|
||||
);
|
||||
* (changes) => {
|
||||
* setNodes((oldNodes) => applyNodeChanges(changes, oldNodes));
|
||||
* },
|
||||
* [setNodes],
|
||||
* );
|
||||
*
|
||||
* return (
|
||||
* <ReactFLow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />
|
||||
* );
|
||||
*/
|
||||
export function applyNodeChanges<NodeType extends Node = Node>(
|
||||
changes: NodeChange<NodeType>[],
|
||||
@@ -161,21 +175,21 @@ export function applyNodeChanges<NodeType extends Node = Node>(
|
||||
* Drop in function that applies edge changes to an array of edges.
|
||||
* @public
|
||||
* @remarks Various events on the <ReactFlow /> component can produce an {@link EdgeChange} that describes how to update the edges of your flow in some way.
|
||||
If you don't need any custom behaviour, this util can be used to take an array of these changes and apply them to your edges.
|
||||
*If you don't need any custom behaviour, this util can be used to take an array of these changes and apply them to your edges.
|
||||
* @param changes - Array of changes to apply
|
||||
* @param edges - Array of edge to apply the changes to
|
||||
* @returns Array of updated edges
|
||||
* @example
|
||||
* const onEdgesChange = useCallback(
|
||||
(changes) => {
|
||||
setEdges((oldEdges) => applyEdgeChanges(changes, oldEdges));
|
||||
},
|
||||
[setEdges],
|
||||
);
|
||||
|
||||
return (
|
||||
<ReactFlow nodes={nodes} edges={edges} onEdgesChange={onEdgesChange} />
|
||||
);
|
||||
* (changes) => {
|
||||
* setEdges((oldEdges) => applyEdgeChanges(changes, oldEdges));
|
||||
* },
|
||||
* [setEdges],
|
||||
* );
|
||||
*
|
||||
* return (
|
||||
* <ReactFlow nodes={nodes} edges={edges} onEdgesChange={onEdgesChange} />
|
||||
* );
|
||||
*/
|
||||
export function applyEdgeChanges<EdgeType extends Edge = Edge>(
|
||||
changes: EdgeChange<EdgeType>[],
|
||||
@@ -205,9 +219,11 @@ export function getSelectionChanges(
|
||||
// we don't want to set all items to selected=false on the first selection
|
||||
if (!(item.selected === undefined && !willBeSelected) && item.selected !== willBeSelected) {
|
||||
if (mutateItem) {
|
||||
// this hack is needed for nodes. When the user dragged a node, it's selected.
|
||||
// When another node gets dragged, we need to deselect the previous one,
|
||||
// in order to have only one selected node at a time - the onNodesChange callback comes too late here :/
|
||||
/*
|
||||
* this hack is needed for nodes. When the user dragged a node, it's selected.
|
||||
* When another node gets dragged, we need to deselect the previous one,
|
||||
* in order to have only one selected node at a time - the onNodesChange callback comes too late here :/
|
||||
*/
|
||||
item.selected = willBeSelected;
|
||||
}
|
||||
changes.push(createSelectionChange(item.id, willBeSelected));
|
||||
|
||||
48
pnpm-lock.yaml
generated
48
pnpm-lock.yaml
generated
@@ -428,6 +428,9 @@ importers:
|
||||
|
||||
tooling/eslint-config:
|
||||
devDependencies:
|
||||
'@stylistic/eslint-plugin':
|
||||
specifier: ^3.1.0
|
||||
version: 3.1.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
'@typescript-eslint/eslint-plugin':
|
||||
specifier: ^8.23.0
|
||||
version: 8.23.0(@typescript-eslint/parser@8.23.0(eslint@8.57.0)(typescript@5.4.5))(eslint@8.57.0)(typescript@5.4.5)
|
||||
@@ -1706,6 +1709,12 @@ packages:
|
||||
resolution: {integrity: sha512-rUV5WyJrJLoloD4NDN1V1+LDMDWOa4OTsT4yYJwQNpTU6FWxkxHpL7eu4w+DmiH8x/EAM1otkPE1+LaspIbplw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@stylistic/eslint-plugin@3.1.0':
|
||||
resolution: {integrity: sha512-pA6VOrOqk0+S8toJYhQGv2MWpQQR0QpeUo9AhNkC49Y26nxBQ/nH1rta9bUU1rPw2fJ1zZEMV5oCX5AazT7J2g==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: '>=8.40.0'
|
||||
|
||||
'@svelte-put/shortcut@3.1.1':
|
||||
resolution: {integrity: sha512-2L5EYTZXiaKvbEelVkg5znxqvfZGZai3m97+cAiUBhLZwXnGtviTDpHxOoZBsqz41szlfRMcamW/8o0+fbW3ZQ==}
|
||||
peerDependencies:
|
||||
@@ -2217,6 +2226,11 @@ packages:
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
acorn@8.14.0:
|
||||
resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
aggregate-error@3.1.0:
|
||||
resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -3290,6 +3304,10 @@ packages:
|
||||
esm-env@1.0.0:
|
||||
resolution: {integrity: sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==}
|
||||
|
||||
espree@10.3.0:
|
||||
resolution: {integrity: sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
espree@9.6.1:
|
||||
resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
@@ -4776,6 +4794,10 @@ packages:
|
||||
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||
engines: {node: '>=8.6'}
|
||||
|
||||
picomatch@4.0.2:
|
||||
resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
pify@2.3.0:
|
||||
resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -7959,6 +7981,18 @@ snapshots:
|
||||
|
||||
'@sindresorhus/merge-streams@1.0.0': {}
|
||||
|
||||
'@stylistic/eslint-plugin@3.1.0(eslint@8.57.0)(typescript@5.4.5)':
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 8.23.0(eslint@8.57.0)(typescript@5.4.5)
|
||||
eslint: 8.57.0
|
||||
eslint-visitor-keys: 4.2.0
|
||||
espree: 10.3.0
|
||||
estraverse: 5.3.0
|
||||
picomatch: 4.0.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@svelte-put/shortcut@3.1.1(svelte@4.2.12)':
|
||||
dependencies:
|
||||
svelte: 4.2.12
|
||||
@@ -8613,12 +8647,18 @@ snapshots:
|
||||
dependencies:
|
||||
acorn: 8.10.0
|
||||
|
||||
acorn-jsx@5.3.2(acorn@8.14.0):
|
||||
dependencies:
|
||||
acorn: 8.14.0
|
||||
|
||||
acorn@8.10.0: {}
|
||||
|
||||
acorn@8.11.2: {}
|
||||
|
||||
acorn@8.11.3: {}
|
||||
|
||||
acorn@8.14.0: {}
|
||||
|
||||
aggregate-error@3.1.0:
|
||||
dependencies:
|
||||
clean-stack: 2.2.0
|
||||
@@ -10257,6 +10297,12 @@ snapshots:
|
||||
|
||||
esm-env@1.0.0: {}
|
||||
|
||||
espree@10.3.0:
|
||||
dependencies:
|
||||
acorn: 8.14.0
|
||||
acorn-jsx: 5.3.2(acorn@8.14.0)
|
||||
eslint-visitor-keys: 4.2.0
|
||||
|
||||
espree@9.6.1:
|
||||
dependencies:
|
||||
acorn: 8.10.0
|
||||
@@ -11961,6 +12007,8 @@ snapshots:
|
||||
|
||||
picomatch@2.3.1: {}
|
||||
|
||||
picomatch@4.0.2: {}
|
||||
|
||||
pify@2.3.0: {}
|
||||
|
||||
pify@4.0.1: {}
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
"license": "MIT",
|
||||
"main": "src/index.js",
|
||||
"devDependencies": {
|
||||
"eslint-config-prettier": "^10.0.1",
|
||||
"eslint-config-turbo": "^2.4.0",
|
||||
"eslint-plugin-react": "^7.37.4",
|
||||
"@stylistic/eslint-plugin": "^3.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^8.23.0",
|
||||
"@typescript-eslint/parser": "^8.23.0",
|
||||
"eslint-plugin-prettier": "^4.2.1"
|
||||
"eslint-config-prettier": "^10.0.1",
|
||||
"eslint-config-turbo": "^2.4.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-react": "^7.37.4"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ module.exports = {
|
||||
'turbo',
|
||||
'prettier',
|
||||
],
|
||||
plugins: ['react', '@typescript-eslint'],
|
||||
plugins: ['react', '@typescript-eslint', '@stylistic'],
|
||||
parserOptions: {
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
@@ -28,5 +28,7 @@ module.exports = {
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@stylistic/indent': ['error', 2],
|
||||
'@stylistic/multiline-comment-style': ['error', 'starred-block'],
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user