Skip to content

Commit f74f720

Browse files
committed
drafting react flow docs
1 parent 8478ff8 commit f74f720

File tree

9 files changed

+637
-2
lines changed

9 files changed

+637
-2
lines changed

assets/enterprise/flow-simple.gif

5.95 MB
Loading
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Flow Components
2+
3+
This page documents the main components provided by the `rxe.flow` library.
4+
5+
## rxe.flow.provider
6+
7+
The `FlowProvider` component is a context provider that makes it possible to access a flow’s internal state outside of the `<ReactFlow />` component. Many of the hooks we provide rely on this component to work.
8+
9+
**Props:**
10+
11+
- `initial_nodes`: `Sequence[Node]` - These nodes are used to initialize the flow. They are not dynamic.
12+
- `initial_edges`: `Sequence[Edge]` - These edges are used to initialize the flow. They are not dynamic.
13+
- `default_nodes`: `Sequence[Node]` - These nodes are used to initialize the flow. They are not dynamic.
14+
- `default_edges`: `Sequence[Edge]` - These edges are used to initialize the flow. They are not dynamic.
15+
- `initial_width`: `float` - The initial width is necessary to be able to use fitView on the server.
16+
- `initial_height`: `float` - The initial height is necessary to be able to use fitView on the server.
17+
- `fit_view`: `bool` - When true, the flow will be zoomed and panned to fit all the nodes initially provided.
18+
- `initial_fit_view_options`: `FitViewOptions` - You can provide an object of options to customize the initial fitView behavior.
19+
- `initial_min_zoom`: `float` - Initial minimum zoom level.
20+
- `initial_max_zoom`: `float` - Initial maximum zoom level.
21+
- `node_origin`: `NodeOrigin` - The origin of the node to use when placing it in the flow or looking up its x and y position.
22+
- `node_extent`: `CoordinateExtent` - The boundary a node can be moved in.
23+
24+
## rxe.flow
25+
26+
The `Flow` component is the main component that renders the flow. It takes in nodes and edges, and provides event handlers for user interactions.
27+
28+
**Props:**
29+
30+
*Many of the props are documented in the official [React Flow documentation](https://reactflow.dev/api-reference/react-flow). Below are some of the most common props.*
31+
32+
- `nodes`: `Sequence[Node]` - An array of nodes to render in a controlled flow.
33+
- `edges`: `Sequence[Edge]` - An array of edges to render in a controlled flow.
34+
- `default_nodes`: `Sequence[Node]` - The initial nodes to render in an uncontrolled flow.
35+
- `default_edges`: `Sequence[Edge]` - The initial edges to render in an uncontrolled flow.
36+
- `node_types`: `Mapping[str, Any]` - Custom node types.
37+
- `edge_types`: `Mapping[str, Any]` - Custom edge types.
38+
- `on_nodes_change`: Event handler for when nodes change.
39+
- `on_edges_change`: Event handler for when edges change.
40+
- `on_connect`: Event handler for when a connection is made.
41+
- `fit_view`: `bool` - When true, the flow will be zoomed and panned to fit all the nodes initially provided.
42+
- `fit_view_options`: `FitViewOptions` - Options for `fit_view`.
43+
- `style`: The style of the component.
44+
45+
## rxe.flow.background
46+
47+
The `Background` component renders a background for the flow. It can be a pattern of lines, dots, or a cross.
48+
49+
**Props:**
50+
51+
- `color`: `str` - Color of the pattern.
52+
- `bg_color`: `str` - Color of the background.
53+
- `variant`: `Literal["lines", "dots", "cross"]` - The type of pattern to render.
54+
- `gap`: `float | tuple[float, float]` - The gap between patterns.
55+
- `size`: `float` - The size of the pattern elements.
56+
57+
**Example:**
58+
59+
```python
60+
rxe.flow.background(variant="dots", gap=20, size=1)
61+
```
62+
63+
## rxe.flow.controls
64+
65+
The `Controls` component renders a panel with buttons to zoom in, zoom out, fit the view, and lock the viewport.
66+
67+
**Props:**
68+
69+
- `show_zoom`: `bool` - Whether to show the zoom buttons.
70+
- `show_fit_view`: `bool` - Whether to show the fit view button.
71+
- `show_interactive`: `bool` - Whether to show the lock button.
72+
- `position`: `PanelPosition` - The position of the controls on the pane.
73+
74+
**Example:**
75+
76+
```python
77+
rxe.flow.controls()
78+
```
79+
80+
## rxe.flow.mini_map
81+
82+
The `MiniMap` component renders a small overview of your flow.
83+
84+
**Props:**
85+
86+
- `node_color`: `str | Any` - Color of nodes on minimap.
87+
- `node_stroke_color`: `str | Any` - Stroke color of nodes on minimap.
88+
- `pannable`: `bool` - Determines whether you can pan the viewport by dragging inside the minimap.
89+
- `zoomable`: `bool` - Determines whether you can zoom the viewport by scrolling inside the minimap.
90+
- `position`: `PanelPosition` - Position of minimap on pane.
91+
92+
**Example:**
93+
94+
```python
95+
rxe.flow.mini_map(pannable=True, zoomable=True)
96+
```
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Edges
2+
3+
Edges connect nodes together. This page explains how to define and customize edges.
4+
5+
## The `Edge` Type
6+
7+
An edge is represented by a `TypedDict` with the following fields:
8+
9+
- `id`: `str` - Unique id of an edge.
10+
- `source`: `str` - Id of source node.
11+
- `target`: `str` - Id of target node.
12+
- `type`: `EdgeType | str` - Type of edge defined in `edge_types`.
13+
- `sourceHandle`: `str | None` - Id of source handle.
14+
- `targetHandle`: `str | None` - Id of target handle.
15+
- `animated`: `bool` - Whether the edge should be animated.
16+
- `hidden`: `bool` - Whether the edge should be hidden.
17+
- `deletable`: `bool` - Whether the edge can be deleted.
18+
- `selectable`: `bool` - Whether the edge can be selected.
19+
- `data`: `dict[str, Any]` - Arbitrary data passed to an edge.
20+
- `label`: `Any` - The label to render along the edge.
21+
- `style`: `Any` - Custom styles for the edge.
22+
- `className`: `str` - A class name for the edge.
23+
24+
## Custom Edges
25+
26+
You can create custom edges by passing a dictionary of `edge_types` to the `rxe.flow` component. The keys of the dictionary are the names of your custom edge types, and the values are the components that render them.
27+
28+
Here is an example of a custom edge with a button to delete it, based on the `overview.py` demo:
29+
30+
```python
31+
import reflex as rx
32+
import reflex_enterprise as rxe
33+
from reflex_enterprise.components.flow.types import Edge, Position
34+
35+
@rx.memo
36+
def button_edge(
37+
id: rx.Var[str],
38+
sourceX: rx.Var[float],
39+
sourceY: rx.Var[float],
40+
targetX: rx.Var[float],
41+
targetY: rx.Var[float],
42+
sourcePosition: rx.Var[Position],
43+
targetPosition: rx.Var[Position],
44+
markerEnd: rx.Var[str],
45+
):
46+
bezier_path = rxe.components.flow.util.get_bezier_path(
47+
source_x=sourceX,
48+
source_y=sourceY,
49+
target_x=targetX,
50+
target_y=targetY,
51+
source_position=sourcePosition,
52+
target_position=targetPosition,
53+
)
54+
return rx.fragment(
55+
rxe.flow.base_edge(path=bezier_path.path, markerEnd=markerEnd),
56+
rxe.flow.edge_label_renderer(
57+
rx.el.div(
58+
rx.el.button(
59+
"×",
60+
class_name="button-edge__button",
61+
on_click=rx.run_script(
62+
rxe.flow.api.set_edges(
63+
rx.vars.FunctionStringVar.create(
64+
"Array.prototype.filter.call"
65+
).call(
66+
rxe.flow.api.get_edges(),
67+
rx.Var(f"((edge) => edge.id !== {id})"),
68+
),
69+
)
70+
),
71+
),
72+
class_name="button-edge__label nodrag nopan",
73+
transform=f"translate(-50%, -50%) translate({bezier_path.label_x}px,{bezier_path.label_y}px)",
74+
)
75+
),
76+
)
77+
78+
def custom_edge_example():
79+
return rxe.flow(
80+
# ... other props
81+
edge_types={
82+
"button": rx.vars.function.ArgsFunctionOperation.create(
83+
(
84+
rx.vars.function.DestructuredArg(
85+
fields=(
86+
"id",
87+
"sourceX",
88+
"sourceY",
89+
"targetX",
90+
"targetY",
91+
"sourcePosition",
92+
"targetPosition",
93+
"markerEnd",
94+
),
95+
),
96+
),
97+
button_edge(
98+
id=rx.Var("id"),
99+
sourceX=rx.Var("sourceX"),
100+
sourceY=rx.Var("sourceY"),
101+
targetX=rx.Var("targetX"),
102+
targetY=rx.Var("targetY"),
103+
sourcePosition=rx.Var("sourcePosition"),
104+
targetPosition=rx.Var("targetPosition"),
105+
markerEnd=rx.Var("markerEnd"),
106+
),
107+
)
108+
},
109+
)
110+
```
111+
112+
In this example:
113+
114+
1. We define a component `button_edge` that will render our custom edge.
115+
2. This component receives props like `id`, `sourceX`, `sourceY`, etc. which describe the edge's geometry.
116+
3. We use `rxe.flow.util.get_bezier_path` to calculate the SVG path for the edge.
117+
4. We use `rxe.flow.base_edge` to render the path, and `rxe.flow.edge_label_renderer` to render a custom label (in this case, a button).
118+
5. The button's `on_click` handler uses `rxe.flow.api.set_edges` to remove the edge from the flow.
119+
6. We pass our custom edge component to the `edge_types` prop of `rxe.flow`.
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Hooks (API)
2+
3+
The `rxe.flow.api` module provides hooks to interact with the Flow instance. These hooks are wrappers around the `useReactFlow` hook from React Flow.
4+
5+
## Node Hooks
6+
7+
- `get_nodes()`: Returns an array of all nodes in the flow.
8+
- `set_nodes(nodes)`: Sets the nodes in the flow.
9+
- `add_nodes(nodes)`: Adds nodes to the flow.
10+
- `get_node(id)`: Returns a node by its ID.
11+
- `update_node(id, node_update, replace=False)`: Updates a node in the flow.
12+
- `update_node_data(id, data_update, replace=False)`: Updates a node's data in the flow.
13+
14+
## Edge Hooks
15+
16+
- `get_edges()`: Returns an array of all edges in the flow.
17+
- `set_edges(edges)`: Sets the edges in the flow.
18+
- `add_edges(edges)`: Adds edges to the flow.
19+
- `get_edge(id)`: Returns an edge by its ID.
20+
- `update_edge(id, edge_update, replace=False)`: Updates an edge in the flow.
21+
- `update_edge_data(id, data_update, replace=False)`: Updates an edge's data in the flow.
22+
23+
## Viewport Hooks
24+
25+
- `screen_to_flow_position(x, y, snap_to_grid=False)`: Translates a screen pixel position to a flow position.
26+
- `flow_to_screen_position(x, y)`: Translates a position inside the flow’s canvas to a screen pixel position.
27+
28+
## Other Hooks
29+
30+
- `to_object()`: Converts the React Flow state to a JSON object.
31+
- `get_intersecting_nodes(node, partially=True, nodes=None)`: Find all the nodes currently intersecting with a given node or rectangle.
32+
- `get_node_connections(id=None, handle_type=None, handle_id=None)`: This hook returns an array of connections on a specific node, handle type ("source", "target") or handle ID.
33+
- `get_connection()`: Returns the current connection state when there is an active connection interaction.
34+
35+
## Example
36+
37+
Here is an example of how to use the `screen_to_flow_position` hook to add a node at the position of a drop event.
38+
39+
```python
40+
import reflex as rx
41+
import reflex_enterprise as rxe
42+
from reflex_enterprise.components.flow.types import Node, XYPosition
43+
44+
class AddNodeOnDropState(rx.State):
45+
nodes: list[Node] = []
46+
47+
def handle_drop(self, event: rx.event.Event, flow_position: XYPosition):
48+
# Logic to add a new node at flow_position
49+
new_node = {
50+
"id": f"node_{len(self.nodes) + 1}",
51+
"position": flow_position,
52+
"data": {"label": "New Node"},
53+
}
54+
self.nodes.append(new_node)
55+
56+
def add_node_on_drop_example():
57+
return rxe.flow.provider(
58+
rxe.flow(
59+
nodes=AddNodeOnDropState.nodes,
60+
on_drop=lambda event: AddNodeOnDropState.handle_drop(
61+
event,
62+
rxe.flow.api.screen_to_flow_position(
63+
x=event.client_x,
64+
y=event.client_y,
65+
),
66+
),
67+
# ... other props
68+
)
69+
)
70+
```
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Nodes
2+
3+
Nodes are the fundamental building blocks of a flow. This page explains how to define and customize nodes.
4+
5+
## The `Node` Type
6+
7+
A node is represented by a `TypedDict` with the following fields:
8+
9+
- `id`: `str` - Unique id of a node.
10+
- `position`: `XYPosition` - Position of a node on the pane.
11+
- `data`: `dict[str, Any]` - Arbitrary data passed to a node.
12+
- `type`: `str | NodeType` - Type of node defined in `node_types`.
13+
- `sourcePosition`: `Position` - Controls source position.
14+
- `targetPosition`: `Position` - Controls target position.
15+
- `hidden`: `bool` - Whether or not the node should be visible on the canvas.
16+
- `selected`: `bool` - Whether or not the node is currently selected.
17+
- `dragging`: `bool` - Whether or not the node is currently being dragged.
18+
- `draggable`: `bool` - Whether or not the node is able to be dragged.
19+
- `selectable`: `bool` - Whether or not the node can be selected.
20+
- `connectable`: `bool` - Whether or not the node can be connected.
21+
- `deletable`: `bool` - Whether or not the node can be deleted.
22+
- `dragHandle`: `str` - A class name that can be applied to elements inside the node that allows those elements to act as drag handles.
23+
- `width`: `float` - The width of the node.
24+
- `height`: `float` - The height of the node.
25+
- `parentId`: `str` - Parent node id, used for creating sub-flows.
26+
- `style`: `Any` - Custom styles for the node.
27+
- `className`: `str` - A class name for the node.
28+
29+
## Custom Nodes
30+
31+
You can create custom nodes by passing a dictionary of `node_types` to the `rxe.flow` component. The keys of the dictionary are the names of your custom node types, and the values are the components that render them.
32+
33+
Here is an example of a custom node that displays a color picker, based on the `custom_node.py` demo:
34+
35+
```python
36+
import reflex as rx
37+
import reflex_enterprise as rxe
38+
from reflex_enterprise.components.flow.types import Node
39+
from typing import Any
40+
41+
class CustomNodeState(rx.State):
42+
nodes: list[Node] = [
43+
{
44+
"id": "2",
45+
"type": "selectorNode",
46+
"data": {
47+
"color": "#c9f1dd",
48+
},
49+
"position": {"x": 300, "y": 50},
50+
},
51+
]
52+
# ... other state variables and event handlers
53+
54+
@rx.memo
55+
def color_selector_node(data: rx.Var[dict[str, Any]], isConnectable: rx.Var[bool]):
56+
data = data.to(dict)
57+
return rx.fragment(
58+
rxe.flow.handle(
59+
type="target",
60+
position="left",
61+
is_connectable=isConnectable,
62+
),
63+
rx.el.div(
64+
"Custom Color Picker Node: ",
65+
rx.el.strong(data["color"]),
66+
),
67+
rx.el.input(
68+
class_name="nodrag",
69+
type="color",
70+
on_change=CustomNodeState.on_change_color,
71+
default_value=data["color"],
72+
),
73+
rxe.flow.handle(
74+
type="source",
75+
position="right",
76+
is_connectable=isConnectable,
77+
),
78+
)
79+
80+
def custom_node_example():
81+
return rxe.flow(
82+
# ... other props
83+
nodes=CustomNodeState.nodes,
84+
node_types={
85+
"selectorNode": rx.vars.function.ArgsFunctionOperation.create(
86+
(
87+
rx.vars.function.DestructuredArg(
88+
fields=("data", "isConnectable")
89+
),
90+
),
91+
color_selector_node(
92+
data=rx.Var("data"), isConnectable=rx.Var("isConnectable")
93+
),
94+
)
95+
},
96+
)
97+
98+
```
99+
100+
In this example:
101+
102+
1. We define a component `color_selector_node` that will render our custom node.
103+
2. This component receives `data` and `isConnectable` as props.
104+
3. We use `rxe.flow.handle` to define the connection points for the node.
105+
4. We pass our custom node component to the `node_types` prop of `rxe.flow`.
106+
5. We set the `type` of a node to `"selectorNode"` to use our custom component.

0 commit comments

Comments
 (0)