Skip to content

Commit 08d6a62

Browse files
authored
Update documentation (#53)
1 parent e9811c5 commit 08d6a62

15 files changed

Lines changed: 370 additions & 33 deletions
46.2 KB
Loading
16.8 KB
Loading
4.89 KB
Loading
6.16 KB
Loading
-32.5 KB
Loading
1 KB
Loading
-27 Bytes
Loading
11 KB
Loading

docs/how-to/declare-types.md

Lines changed: 137 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,142 @@
11
# Declare Node & Edge Types
22

3-
Node and edge types are lightweight descriptors that tell Panel-ReactFlow
4-
**what kind of data a node or edge carries**. A type defines a name, an
5-
optional display label, optional input/output ports (for nodes), and an
6-
optional JSON Schema for its `data` payload.
3+
Node and edge types are lightweight descriptors that define **what data each
4+
kind of node/edge carries**. A type can provide:
75

8-
Types are separate from editors. A type says "a *task* node has a
9-
*status* string and a *priority* integer"; an editor says "render a
10-
dropdown and a number input for those fields." This separation lets you
11-
reuse the same type with different editors, or rely on the auto-generated
12-
form.
6+
- a type name (`type`)
7+
- a display label (`label`)
8+
- node handles (`inputs` / `outputs`)
9+
- a schema for the `data` payload (`schema`)
10+
11+
Types are separate from editors. A type defines structure; an editor defines
12+
the UI used to edit it.
1313

1414
![Screenshot: multiple node types with different schemas](../assets/screenshots/declare-types.png)
1515

1616
---
1717

18-
## Node types
18+
## Complete runnable example
19+
20+
This script is a minimal, working example that produces the visualization
21+
shown above.
22+
23+
```python
24+
import param
25+
import panel as pn
26+
27+
from panel_reactflow import EdgeType, NodeType, ReactFlow
28+
29+
pn.extension("jsoneditor")
30+
31+
32+
class Job(param.Parameterized):
33+
status = param.Selector(objects=["idle", "running", "done"])
34+
retries = param.Integer(default=0)
35+
36+
37+
decision_schema = {
38+
"type": "object",
39+
"properties": {
40+
"question": {"type": "string", "title": "Question"},
41+
"outcome": {
42+
"type": "string",
43+
"enum": ["yes", "no", "maybe"],
44+
"title": "Outcome",
45+
},
46+
},
47+
}
48+
49+
node_types = {
50+
"job": NodeType(type="job", label="Job", schema=Job, inputs=["in"], outputs=["out"]),
51+
"decision": NodeType(
52+
type="decision",
53+
label="Decision",
54+
schema=decision_schema,
55+
inputs=["in"],
56+
outputs=["yes", "no"],
57+
),
58+
}
59+
60+
edge_types = {
61+
"flow": EdgeType(
62+
type="flow",
63+
label="Flow",
64+
schema={
65+
"type": "object",
66+
"properties": {"weight": {"type": "number", "title": "Weight"}},
67+
},
68+
),
69+
}
70+
71+
nodes = [
72+
{
73+
"id": "j1",
74+
"type": "job",
75+
"label": "Fetch Data",
76+
"position": {"x": 0, "y": 0},
77+
"data": {"status": "idle", "retries": 0},
78+
},
79+
{
80+
"id": "d1",
81+
"type": "decision",
82+
"label": "Valid?",
83+
"position": {"x": 300, "y": 250},
84+
"data": {"question": "Is data valid?", "outcome": "yes"},
85+
},
86+
{
87+
"id": "j2",
88+
"type": "job",
89+
"label": "Process",
90+
"position": {"x": 600, "y": 400},
91+
"data": {"status": "running", "retries": 1},
92+
},
93+
]
94+
95+
edges = [
96+
{"id": "e1", "source": "j1", "target": "d1", "type": "flow", "data": {"weight": 1.0}},
97+
{"id": "e2", "source": "d1", "target": "j2", "type": "flow", "data": {"weight": 0.8}},
98+
]
99+
100+
TASK_NODE_CSS = """
101+
.react-flow__node-job {
102+
background-color: white;
103+
border-radius: 8px;
104+
border: 1.5px solid #7c3aed;
105+
}
106+
107+
.react-flow__node-decision {
108+
background-color: white;
109+
border-radius: 8px;
110+
border: 1.5px solid green;
111+
}
112+
"""
113+
114+
flow = ReactFlow(
115+
nodes=nodes,
116+
edges=edges,
117+
node_types=node_types,
118+
edge_types=edge_types,
119+
editor_mode="node",
120+
sizing_mode="stretch_both",
121+
stylesheets=[TASK_NODE_CSS]
122+
)
123+
124+
pn.Column(flow, sizing_mode="stretch_both").servable()
125+
```
126+
127+
## How this code maps to the visualization
128+
129+
- `node_types["job"]` and `node_types["decision"]` define the two node kinds you see.
130+
- `inputs` and `outputs` define the left/right handles rendered on each node.
131+
- `edge_types["flow"]` defines the edge payload schema used by both connections.
132+
- `nodes` controls labels (`Fetch Data`, `Valid?`, `Process`) and positions.
133+
- `editor_mode="side"` makes selection open the schema-driven editor in the right panel.
134+
135+
---
136+
137+
## Node type snippet
19138

20-
Use `NodeType` to describe a node type. Provide `inputs` and `outputs` to
21-
control the handles (ports) shown on each side of the node.
139+
Use `NodeType` to define node handles and payload schema.
22140

23141
```python
24142
from panel_reactflow import NodeType
@@ -42,12 +160,9 @@ node_types = {
42160
}
43161
```
44162

45-
---
46-
47-
## Edge types
163+
## Edge type snippet
48164

49-
Use `EdgeType` to describe an edge type. Edges with a schema get the same
50-
auto-generated editor support as nodes.
165+
Use `EdgeType` for edge payload schema and label.
51166

52167
```python
53168
from panel_reactflow import EdgeType
@@ -71,8 +186,7 @@ edge_types = {
71186

72187
## Schema sources
73188

74-
The `schema` field accepts multiple formats. All are normalized to
75-
JSON Schema before being sent to the frontend or used by editors.
189+
The `schema` field accepts multiple inputs and normalizes them to JSON Schema.
76190

77191
| Source | Example |
78192
|--------|---------|
@@ -108,9 +222,9 @@ node_types = {"config": NodeType(type="config", label="Config", schema=Config)}
108222

109223
---
110224

111-
## Register on ReactFlow
225+
## Register on `ReactFlow`
112226

113-
Pass types as dictionaries keyed by type name.
227+
Pass `node_types` and `edge_types` as dictionaries keyed by type name:
114228

115229
```python
116230
flow = ReactFlow(
@@ -121,5 +235,5 @@ flow = ReactFlow(
121235
)
122236
```
123237

124-
Types without a schema still work — the node or edge simply has no
125-
schema-driven validation or auto-generated form.
238+
Types without a schema still work; they just do not get schema-driven
239+
validation or auto-generated forms.

docs/how-to/define-editors.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,65 @@ or falls back to a raw JSON editor.
1818

1919
---
2020

21+
## Complete runnable example (node editor screenshot)
22+
23+
This script is a minimal, working example for the screenshot above. Run it,
24+
then click the `Start` node to open the schema-driven editor in the side panel.
25+
26+
```python
27+
import panel as pn
28+
29+
from panel_reactflow import NodeType, ReactFlow
30+
31+
pn.extension("jsoneditor")
32+
33+
task_schema = {
34+
"type": "object",
35+
"properties": {
36+
"status": {"type": "string", "enum": ["idle", "running", "done"], "title": "Status"},
37+
"priority": {"type": "integer", "title": "Priority"},
38+
"notes": {"type": "string", "title": "Notes"},
39+
},
40+
}
41+
42+
nodes = [
43+
{
44+
"id": "start",
45+
"type": "task",
46+
"label": "Start",
47+
"position": {"x": 0, "y": 0},
48+
"data": {"status": "idle", "priority": 1, "notes": ""},
49+
},
50+
{
51+
"id": "finish",
52+
"type": "task",
53+
"label": "Finish",
54+
"position": {"x": 300, "y": 80},
55+
"data": {"status": "done", "priority": 2, "notes": "All clear"},
56+
},
57+
]
58+
59+
edges = [{"id": "e1", "source": "start", "target": "finish"}]
60+
61+
flow = ReactFlow(
62+
nodes=nodes,
63+
edges=edges,
64+
node_types={"task": NodeType(type="task", label="Task", schema=task_schema)},
65+
editor_mode="side",
66+
sizing_mode="stretch_both",
67+
)
68+
69+
pn.Column(flow, sizing_mode="stretch_both").servable()
70+
```
71+
72+
## How this code maps to the node-editor screenshot
73+
74+
- `task_schema` defines fields rendered in the side-panel form.
75+
- `node_types={"task": ...}` binds that schema to both `task` nodes.
76+
- Clicking a node selects it and opens its editor because `editor_mode="side"`.
77+
78+
---
79+
2180
## Editor signature
2281

2382
Every editor — whether a simple function, a lambda, or a class — receives
@@ -106,6 +165,57 @@ edge type) or `default_edge_editor` for a blanket default.
106165

107166
![Screenshot: an edge editor open in the side panel](../assets/screenshots/define-editors-edge.png)
108167

168+
### Complete runnable example (edge editor screenshot)
169+
170+
This script reproduces the edge-editor screenshot. Run it, then click the
171+
`pipe` edge to open its schema-driven editor.
172+
173+
```python
174+
import panel as pn
175+
176+
from panel_reactflow import EdgeType, NodeType, ReactFlow
177+
178+
pn.extension("jsoneditor")
179+
180+
pipe_schema = {
181+
"type": "object",
182+
"properties": {
183+
"throughput": {"type": "number", "title": "Throughput"},
184+
"protocol": {
185+
"type": "string",
186+
"enum": ["tcp", "udp", "http"],
187+
"title": "Protocol",
188+
},
189+
},
190+
}
191+
192+
nodes = [
193+
{"id": "src", "type": "device", "label": "Source", "position": {"x": 0, "y": 0}, "data": {}},
194+
{"id": "sink", "type": "device", "label": "Sink", "position": {"x": 400, "y": 0}, "data": {}},
195+
]
196+
197+
edges = [
198+
{
199+
"id": "e1",
200+
"source": "src",
201+
"target": "sink",
202+
"type": "pipe",
203+
"label": "pipe",
204+
"data": {"throughput": 100.0, "protocol": "tcp"},
205+
},
206+
]
207+
208+
flow = ReactFlow(
209+
nodes=nodes,
210+
edges=edges,
211+
node_types={"device": NodeType(type="device", label="Device")},
212+
edge_types={"pipe": EdgeType(type="pipe", label="Pipe", schema=pipe_schema)},
213+
sizing_mode="stretch_both",
214+
)
215+
216+
pn.Column(flow, sizing_mode="stretch_both").servable()
217+
```
218+
109219
### Schema-driven edge editor
110220

111221
If you declare an `EdgeType` with a schema and do not provide an explicit

0 commit comments

Comments
 (0)