Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified docs/assets/screenshots/declare-types.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/screenshots/define-editors-edge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/screenshots/define-editors-node.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/screenshots/define-nodes-edges.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/screenshots/embed-views-in-nodes.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/screenshots/quickstart.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/screenshots/react-to-events.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/assets/screenshots/style-nodes-edges.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
160 changes: 137 additions & 23 deletions docs/how-to/declare-types.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,142 @@
# Declare Node & Edge Types

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

Types are separate from editors. A type says "a *task* node has a
*status* string and a *priority* integer"; an editor says "render a
dropdown and a number input for those fields." This separation lets you
reuse the same type with different editors, or rely on the auto-generated
form.
- a type name (`type`)
- a display label (`label`)
- node handles (`inputs` / `outputs`)
- a schema for the `data` payload (`schema`)

Types are separate from editors. A type defines structure; an editor defines
the UI used to edit it.

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

---

## Node types
## Complete runnable example

This script is a minimal, working example that produces the visualization
shown above.

```python
import param
import panel as pn

from panel_reactflow import EdgeType, NodeType, ReactFlow

pn.extension("jsoneditor")


class Job(param.Parameterized):
status = param.Selector(objects=["idle", "running", "done"])
retries = param.Integer(default=0)


decision_schema = {
"type": "object",
"properties": {
"question": {"type": "string", "title": "Question"},
"outcome": {
"type": "string",
"enum": ["yes", "no", "maybe"],
"title": "Outcome",
},
},
}

node_types = {
"job": NodeType(type="job", label="Job", schema=Job, inputs=["in"], outputs=["out"]),
"decision": NodeType(
type="decision",
label="Decision",
schema=decision_schema,
inputs=["in"],
outputs=["yes", "no"],
),
}

edge_types = {
"flow": EdgeType(
type="flow",
label="Flow",
schema={
"type": "object",
"properties": {"weight": {"type": "number", "title": "Weight"}},
},
),
}

nodes = [
{
"id": "j1",
"type": "job",
"label": "Fetch Data",
"position": {"x": 0, "y": 0},
"data": {"status": "idle", "retries": 0},
},
{
"id": "d1",
"type": "decision",
"label": "Valid?",
"position": {"x": 300, "y": 250},
"data": {"question": "Is data valid?", "outcome": "yes"},
},
{
"id": "j2",
"type": "job",
"label": "Process",
"position": {"x": 600, "y": 400},
"data": {"status": "running", "retries": 1},
},
]

edges = [
{"id": "e1", "source": "j1", "target": "d1", "type": "flow", "data": {"weight": 1.0}},
{"id": "e2", "source": "d1", "target": "j2", "type": "flow", "data": {"weight": 0.8}},
]

TASK_NODE_CSS = """
.react-flow__node-job {
background-color: white;
border-radius: 8px;
border: 1.5px solid #7c3aed;
}

.react-flow__node-decision {
background-color: white;
border-radius: 8px;
border: 1.5px solid green;
}
"""

flow = ReactFlow(
nodes=nodes,
edges=edges,
node_types=node_types,
edge_types=edge_types,
editor_mode="node",
sizing_mode="stretch_both",
stylesheets=[TASK_NODE_CSS]
)

pn.Column(flow, sizing_mode="stretch_both").servable()
```

## How this code maps to the visualization

- `node_types["job"]` and `node_types["decision"]` define the two node kinds you see.
- `inputs` and `outputs` define the left/right handles rendered on each node.
- `edge_types["flow"]` defines the edge payload schema used by both connections.
- `nodes` controls labels (`Fetch Data`, `Valid?`, `Process`) and positions.
- `editor_mode="side"` makes selection open the schema-driven editor in the right panel.

---

## Node type snippet

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

```python
from panel_reactflow import NodeType
Expand All @@ -42,12 +160,9 @@ node_types = {
}
```

---

## Edge types
## Edge type snippet

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

```python
from panel_reactflow import EdgeType
Expand All @@ -71,8 +186,7 @@ edge_types = {

## Schema sources

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

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

---

## Register on ReactFlow
## Register on `ReactFlow`

Pass types as dictionaries keyed by type name.
Pass `node_types` and `edge_types` as dictionaries keyed by type name:

```python
flow = ReactFlow(
Expand All @@ -121,5 +235,5 @@ flow = ReactFlow(
)
```

Types without a schema still work — the node or edge simply has no
schema-driven validation or auto-generated form.
Types without a schema still work; they just do not get schema-driven
validation or auto-generated forms.
110 changes: 110 additions & 0 deletions docs/how-to/define-editors.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,65 @@ or falls back to a raw JSON editor.

---

## Complete runnable example (node editor screenshot)

This script is a minimal, working example for the screenshot above. Run it,
then click the `Start` node to open the schema-driven editor in the side panel.

```python
import panel as pn

from panel_reactflow import NodeType, ReactFlow

pn.extension("jsoneditor")

task_schema = {
"type": "object",
"properties": {
"status": {"type": "string", "enum": ["idle", "running", "done"], "title": "Status"},
"priority": {"type": "integer", "title": "Priority"},
"notes": {"type": "string", "title": "Notes"},
},
}

nodes = [
{
"id": "start",
"type": "task",
"label": "Start",
"position": {"x": 0, "y": 0},
"data": {"status": "idle", "priority": 1, "notes": ""},
},
{
"id": "finish",
"type": "task",
"label": "Finish",
"position": {"x": 300, "y": 80},
"data": {"status": "done", "priority": 2, "notes": "All clear"},
},
]

edges = [{"id": "e1", "source": "start", "target": "finish"}]

flow = ReactFlow(
nodes=nodes,
edges=edges,
node_types={"task": NodeType(type="task", label="Task", schema=task_schema)},
editor_mode="side",
sizing_mode="stretch_both",
)

pn.Column(flow, sizing_mode="stretch_both").servable()
```

## How this code maps to the node-editor screenshot

- `task_schema` defines fields rendered in the side-panel form.
- `node_types={"task": ...}` binds that schema to both `task` nodes.
- Clicking a node selects it and opens its editor because `editor_mode="side"`.

---

## Editor signature

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

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

### Complete runnable example (edge editor screenshot)

This script reproduces the edge-editor screenshot. Run it, then click the
`pipe` edge to open its schema-driven editor.

```python
import panel as pn

from panel_reactflow import EdgeType, NodeType, ReactFlow

pn.extension("jsoneditor")

pipe_schema = {
"type": "object",
"properties": {
"throughput": {"type": "number", "title": "Throughput"},
"protocol": {
"type": "string",
"enum": ["tcp", "udp", "http"],
"title": "Protocol",
},
},
}

nodes = [
{"id": "src", "type": "device", "label": "Source", "position": {"x": 0, "y": 0}, "data": {}},
{"id": "sink", "type": "device", "label": "Sink", "position": {"x": 400, "y": 0}, "data": {}},
]

edges = [
{
"id": "e1",
"source": "src",
"target": "sink",
"type": "pipe",
"label": "pipe",
"data": {"throughput": 100.0, "protocol": "tcp"},
},
]

flow = ReactFlow(
nodes=nodes,
edges=edges,
node_types={"device": NodeType(type="device", label="Device")},
edge_types={"pipe": EdgeType(type="pipe", label="Pipe", schema=pipe_schema)},
sizing_mode="stretch_both",
)

pn.Column(flow, sizing_mode="stretch_both").servable()
```

### Schema-driven edge editor

If you declare an `EdgeType` with a schema and do not provide an explicit
Expand Down
52 changes: 52 additions & 0 deletions docs/how-to/define-nodes-edges.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,58 @@ and update data after the graph is live.

---

## Complete runnable example

This script is a minimal, working example that produces the visualization
shown above.

```python
import panel as pn

from panel_reactflow import ReactFlow

pn.extension("jsoneditor")

nodes = [
{
"id": "n1",
"type": "panel",
"label": "Start",
"position": {"x": 0, "y": 0},
"data": {"status": "idle"},
"view": pn.pane.Markdown("Optional node body"),
},
{
"id": "n2",
"type": "panel",
"label": "End",
"position": {"x": 300, "y": 80},
"data": {"status": "done"},
},
]

edges = [
{"id": "e1", "source": "n1", "target": "n2", "label": "next"},
]

flow = ReactFlow(
nodes=nodes,
edges=edges,
sizing_mode="stretch_both",
)

pn.Column(flow, sizing_mode="stretch_both").servable()
```

## How this code maps to the visualization

- `nodes` defines the two boxes (`Start`, `End`) and where they appear.
- `edges` defines the single connection labeled `next`.
- `view` on `n1` adds inline content inside that node.
- `ReactFlow(nodes=..., edges=...)` renders the graph from those lists.

---

## Define nodes

A node dict requires `id`, `position`, and `data`. The display label is a
Expand Down
Loading
Loading