pict-section-flow 0.0.10 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/launch.json +1 -1
- package/README.md +176 -0
- package/docs/.nojekyll +0 -0
- package/docs/Architecture.md +303 -0
- package/docs/Custom-Styling.md +275 -0
- package/docs/Data_Model.md +158 -0
- package/docs/Event_System.md +156 -0
- package/docs/Getting_Started.md +237 -0
- package/docs/Implementation_Reference.md +528 -0
- package/docs/Layout_Persistence.md +117 -0
- package/docs/README.md +115 -52
- package/docs/_cover.md +11 -0
- package/docs/_sidebar.md +52 -0
- package/docs/_topbar.md +8 -0
- package/docs/api/PictFlowCard.md +216 -0
- package/docs/api/PictFlowCardPropertiesPanel.md +235 -0
- package/docs/api/addConnection.md +101 -0
- package/docs/api/addNode.md +137 -0
- package/docs/api/autoLayout.md +77 -0
- package/docs/api/getFlowData.md +112 -0
- package/docs/api/marshalToView.md +95 -0
- package/docs/api/openPanel.md +128 -0
- package/docs/api/registerHandler.md +174 -0
- package/docs/api/registerNodeType.md +142 -0
- package/docs/api/removeConnection.md +57 -0
- package/docs/api/removeNode.md +80 -0
- package/docs/api/saveLayout.md +152 -0
- package/docs/api/screenToSVGCoords.md +68 -0
- package/docs/api/selectNode.md +116 -0
- package/docs/api/setTheme.md +168 -0
- package/docs/api/setZoom.md +97 -0
- package/docs/api/toggleFullscreen.md +68 -0
- package/docs/card-help/EACH.md +19 -0
- package/docs/card-help/FREAD.md +24 -0
- package/docs/card-help/FWRITE.md +24 -0
- package/docs/card-help/GET.md +22 -0
- package/docs/card-help/ITE.md +23 -0
- package/docs/card-help/LOG.md +23 -0
- package/docs/card-help/NOTE.md +17 -0
- package/docs/card-help/PREV.md +18 -0
- package/docs/card-help/SET.md +27 -0
- package/docs/card-help/SPKL.md +22 -0
- package/docs/card-help/STAT.md +23 -0
- package/docs/card-help/SW.md +25 -0
- package/docs/css/docuserve.css +73 -0
- package/docs/index.html +39 -0
- package/docs/retold-catalog.json +169 -0
- package/docs/retold-keyword-index.json +13942 -0
- package/example_applications/simple_cards/package.json +1 -0
- package/example_applications/simple_cards/source/card-help-content.js +16 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Comment.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-DataPreview.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Each.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Sparkline.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-StatusMonitor.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +2 -0
- package/package.json +11 -7
- package/scripts/generate-card-help.js +214 -0
- package/source/Pict-Section-Flow.js +4 -0
- package/source/PictFlowCard.js +3 -1
- package/source/providers/PictProvider-Flow-CSS.js +245 -152
- package/source/providers/PictProvider-Flow-ConnectorShapes.js +24 -0
- package/source/providers/PictProvider-Flow-Geometry.js +195 -38
- package/source/providers/PictProvider-Flow-PanelChrome.js +14 -12
- package/source/services/PictService-Flow-ConnectionHandleManager.js +263 -0
- package/source/services/PictService-Flow-ConnectionRenderer.js +134 -183
- package/source/services/PictService-Flow-DataManager.js +338 -0
- package/source/services/PictService-Flow-InteractionManager.js +165 -7
- package/source/services/PictService-Flow-PathGenerator.js +282 -0
- package/source/services/PictService-Flow-PortRenderer.js +269 -0
- package/source/services/PictService-Flow-RenderManager.js +281 -0
- package/source/services/PictService-Flow-Tether.js +6 -42
- package/source/views/PictView-Flow-Node.js +2 -220
- package/source/views/PictView-Flow-PropertiesPanel.js +89 -44
- package/source/views/PictView-Flow.js +130 -882
- package/test/ConnectionHandleManager_tests.js +717 -0
- package/test/ConnectionRenderer_tests.js +591 -0
- package/test/DataManager_tests.js +859 -0
- package/test/Geometry_tests.js +767 -0
- package/test/PathGenerator_tests.js +978 -0
- package/test/PortRenderer_tests.js +367 -0
- package/test/RenderManager_tests.js +756 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# PictFlowCardPropertiesPanel
|
|
2
|
+
|
|
3
|
+
Base class for flow card property panels. Extend this class to create custom panel types beyond the four built-in types (Template, Markdown, Form, View).
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
const libPictFlowCardPropertiesPanel = require('pict-section-flow').PictFlowCardPropertiesPanel;
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Constructor
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
class MyPanel extends libPictFlowCardPropertiesPanel
|
|
15
|
+
{
|
|
16
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
17
|
+
{
|
|
18
|
+
super(pFable, Object.assign({},
|
|
19
|
+
{
|
|
20
|
+
PanelType: 'Custom',
|
|
21
|
+
Title: 'My Panel',
|
|
22
|
+
Width: 300,
|
|
23
|
+
Height: 200,
|
|
24
|
+
Configuration: {}
|
|
25
|
+
}, pOptions), pServiceHash);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Configuration Options
|
|
31
|
+
|
|
32
|
+
| Property | Type | Default | Description |
|
|
33
|
+
|----------|------|---------|-------------|
|
|
34
|
+
| `PanelType` | string | `'Base'` | Panel type identifier |
|
|
35
|
+
| `Title` | string | `'Properties'` | Panel title bar text |
|
|
36
|
+
| `Width` | number | `300` | Default width in pixels |
|
|
37
|
+
| `Height` | number | `200` | Default height in pixels |
|
|
38
|
+
| `Configuration` | object | `{}` | Panel-type-specific config |
|
|
39
|
+
|
|
40
|
+
## Methods to Override
|
|
41
|
+
|
|
42
|
+
### render(pContainer, pNodeData)
|
|
43
|
+
|
|
44
|
+
Render the panel's content into a DOM container element. Subclasses **must** override this.
|
|
45
|
+
|
|
46
|
+
| Parameter | Type | Description |
|
|
47
|
+
|-----------|------|-------------|
|
|
48
|
+
| `pContainer` | HTMLElement | The DOM element to render into |
|
|
49
|
+
| `pNodeData` | object | The node data object (has `.Data` property) |
|
|
50
|
+
|
|
51
|
+
### marshalToPanel(pNodeData)
|
|
52
|
+
|
|
53
|
+
Marshal data from the node's Data object into the panel UI. Called when the panel opens or when data changes externally.
|
|
54
|
+
|
|
55
|
+
| Parameter | Type | Description |
|
|
56
|
+
|-----------|------|-------------|
|
|
57
|
+
| `pNodeData` | object | The node data object |
|
|
58
|
+
|
|
59
|
+
### marshalFromPanel(pNodeData)
|
|
60
|
+
|
|
61
|
+
Marshal data from the panel UI into the node's Data object. Called before saving or when the panel is about to close.
|
|
62
|
+
|
|
63
|
+
| Parameter | Type | Description |
|
|
64
|
+
|-----------|------|-------------|
|
|
65
|
+
| `pNodeData` | object | The node data object to update |
|
|
66
|
+
|
|
67
|
+
### destroy()
|
|
68
|
+
|
|
69
|
+
Called when the panel is being closed. Clean up resources, event listeners, etc.
|
|
70
|
+
|
|
71
|
+
## Internal Properties
|
|
72
|
+
|
|
73
|
+
| Property | Type | Description |
|
|
74
|
+
|----------|------|-------------|
|
|
75
|
+
| `this._FlowView` | PictViewFlow | Reference to the flow view (set when panel is activated) |
|
|
76
|
+
| `this._NodeData` | object | The node data this panel is operating on |
|
|
77
|
+
| `this._ContentContainer` | HTMLElement | The DOM container element |
|
|
78
|
+
| `this._Configuration` | object | Panel-type-specific configuration |
|
|
79
|
+
|
|
80
|
+
## Built-in Panel Types
|
|
81
|
+
|
|
82
|
+
### Template
|
|
83
|
+
|
|
84
|
+
Renders Pict templates inside the panel.
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
{
|
|
88
|
+
PanelType: 'Template',
|
|
89
|
+
Configuration:
|
|
90
|
+
{
|
|
91
|
+
Templates:
|
|
92
|
+
[
|
|
93
|
+
{
|
|
94
|
+
Hash: 'my-template',
|
|
95
|
+
Template: '<div class="info">{~D:Record.Data.Label~}</div>'
|
|
96
|
+
}
|
|
97
|
+
],
|
|
98
|
+
TemplateHash: 'my-template'
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Markdown
|
|
104
|
+
|
|
105
|
+
Renders markdown content via `pict-section-content`.
|
|
106
|
+
|
|
107
|
+
```javascript
|
|
108
|
+
{
|
|
109
|
+
PanelType: 'Markdown',
|
|
110
|
+
Configuration:
|
|
111
|
+
{
|
|
112
|
+
Markdown: '## Help\nThis node reads a file from disk.\n\n**Inputs:** File path\n**Outputs:** File contents or error'
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Form
|
|
118
|
+
|
|
119
|
+
Creates an ephemeral `pict-section-form` section.
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
{
|
|
123
|
+
PanelType: 'Form',
|
|
124
|
+
Configuration:
|
|
125
|
+
{
|
|
126
|
+
Fields:
|
|
127
|
+
[
|
|
128
|
+
{ Name: 'FilePath', DataType: 'String' },
|
|
129
|
+
{ Name: 'Encoding', DataType: 'String' },
|
|
130
|
+
{ Name: 'MaxSize', DataType: 'Number' }
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### View
|
|
137
|
+
|
|
138
|
+
Renders an existing registered Pict view inside the panel.
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
{
|
|
142
|
+
PanelType: 'View',
|
|
143
|
+
Configuration:
|
|
144
|
+
{
|
|
145
|
+
ViewHash: 'my-custom-view'
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Examples
|
|
151
|
+
|
|
152
|
+
### Custom chart panel
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
class ChartPanel extends libPictFlowCardPropertiesPanel
|
|
156
|
+
{
|
|
157
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
158
|
+
{
|
|
159
|
+
super(pFable, Object.assign({},
|
|
160
|
+
{
|
|
161
|
+
PanelType: 'Chart',
|
|
162
|
+
Title: 'Data Preview',
|
|
163
|
+
Width: 400,
|
|
164
|
+
Height: 300
|
|
165
|
+
}, pOptions), pServiceHash);
|
|
166
|
+
|
|
167
|
+
this._ChartInstance = null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
render(pContainer, pNodeData)
|
|
171
|
+
{
|
|
172
|
+
super.render(pContainer, pNodeData);
|
|
173
|
+
|
|
174
|
+
let tmpCanvas = document.createElement('canvas');
|
|
175
|
+
tmpCanvas.width = 380;
|
|
176
|
+
tmpCanvas.height = 260;
|
|
177
|
+
pContainer.appendChild(tmpCanvas);
|
|
178
|
+
|
|
179
|
+
// Create chart from node data
|
|
180
|
+
this._ChartInstance = new Chart(tmpCanvas,
|
|
181
|
+
{
|
|
182
|
+
type: 'line',
|
|
183
|
+
data: pNodeData.Data.ChartData || { labels: [], datasets: [] }
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
marshalToPanel(pNodeData)
|
|
188
|
+
{
|
|
189
|
+
super.marshalToPanel(pNodeData);
|
|
190
|
+
if (this._ChartInstance && pNodeData.Data.ChartData)
|
|
191
|
+
{
|
|
192
|
+
this._ChartInstance.data = pNodeData.Data.ChartData;
|
|
193
|
+
this._ChartInstance.update();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
destroy()
|
|
198
|
+
{
|
|
199
|
+
if (this._ChartInstance)
|
|
200
|
+
{
|
|
201
|
+
this._ChartInstance.destroy();
|
|
202
|
+
this._ChartInstance = null;
|
|
203
|
+
}
|
|
204
|
+
super.destroy();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Using a custom panel in a card
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
class DataPreviewCard extends libPictFlowCard
|
|
213
|
+
{
|
|
214
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
215
|
+
{
|
|
216
|
+
super(pFable, Object.assign({},
|
|
217
|
+
{
|
|
218
|
+
Title: 'Data Preview',
|
|
219
|
+
Code: 'DPRV',
|
|
220
|
+
PropertiesPanel:
|
|
221
|
+
{
|
|
222
|
+
PanelType: 'Chart',
|
|
223
|
+
Title: 'Preview',
|
|
224
|
+
DefaultWidth: 400,
|
|
225
|
+
DefaultHeight: 300
|
|
226
|
+
}
|
|
227
|
+
}, pOptions), pServiceHash);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## See Also
|
|
233
|
+
|
|
234
|
+
- [PictFlowCard](PictFlowCard.md) — Define cards that use panels
|
|
235
|
+
- [openPanel / closePanel](openPanel.md) — Panel lifecycle methods
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# addConnection
|
|
2
|
+
|
|
3
|
+
Connect two ports on the flow canvas. Creates a visual line between the source port (typically an output) and the target port (typically an input). The connection is validated before creation.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
flowView.addConnection(pSourceNode, pSourcePort, pTargetNode, pTargetPort, pData)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Parameters
|
|
12
|
+
|
|
13
|
+
| Parameter | Type | Required | Description |
|
|
14
|
+
|-----------|------|----------|-------------|
|
|
15
|
+
| `pSourceNode` | string | Yes | Hash of the source node |
|
|
16
|
+
| `pSourcePort` | string | Yes | Hash of the source port |
|
|
17
|
+
| `pTargetNode` | string | Yes | Hash of the target node |
|
|
18
|
+
| `pTargetPort` | string | Yes | Hash of the target port |
|
|
19
|
+
| `pData` | object | No | Optional connection metadata |
|
|
20
|
+
|
|
21
|
+
### Connection Data Options
|
|
22
|
+
|
|
23
|
+
| Property | Type | Default | Description |
|
|
24
|
+
|----------|------|---------|-------------|
|
|
25
|
+
| `LineMode` | string | `'bezier'` | Path style: `'bezier'` or `'orthogonal'` |
|
|
26
|
+
|
|
27
|
+
## Returns
|
|
28
|
+
|
|
29
|
+
The connection object on success, or `false` if validation failed (e.g. duplicate connection, self-connection, or port not found).
|
|
30
|
+
|
|
31
|
+
```javascript
|
|
32
|
+
{
|
|
33
|
+
Hash: 'conn-x1y2z3',
|
|
34
|
+
SourceNodeHash: 'node-a1b2',
|
|
35
|
+
SourcePortHash: 'port-c3d4',
|
|
36
|
+
TargetNodeHash: 'node-e5f6',
|
|
37
|
+
TargetPortHash: 'port-g7h8',
|
|
38
|
+
Data:
|
|
39
|
+
{
|
|
40
|
+
LineMode: 'bezier',
|
|
41
|
+
HandleCustomized: false
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Events Fired
|
|
47
|
+
|
|
48
|
+
- `onConnectionCreated` — with the new connection object
|
|
49
|
+
- `onFlowChanged` — with the complete flow data
|
|
50
|
+
|
|
51
|
+
## Examples
|
|
52
|
+
|
|
53
|
+
### Basic connection
|
|
54
|
+
|
|
55
|
+
```javascript
|
|
56
|
+
let tmpStart = flowView.addNode('start', 50, 150, 'Begin');
|
|
57
|
+
let tmpEnd = flowView.addNode('end', 400, 150, 'Done');
|
|
58
|
+
|
|
59
|
+
let tmpOutPort = tmpStart.Ports.find((pPort) => pPort.Direction === 'output');
|
|
60
|
+
let tmpInPort = tmpEnd.Ports.find((pPort) => pPort.Direction === 'input');
|
|
61
|
+
|
|
62
|
+
let tmpConn = flowView.addConnection(
|
|
63
|
+
tmpStart.Hash, tmpOutPort.Hash,
|
|
64
|
+
tmpEnd.Hash, tmpInPort.Hash
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
console.log(tmpConn.Hash); // 'conn-...'
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Orthogonal connection
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
let tmpConn = flowView.addConnection(
|
|
74
|
+
tmpA.Hash, tmpOutPort.Hash,
|
|
75
|
+
tmpB.Hash, tmpInPort.Hash,
|
|
76
|
+
{ LineMode: 'orthogonal' }
|
|
77
|
+
);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Decision node branching
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
let tmpDecision = flowView.addNode('decision', 200, 150, 'Check Status');
|
|
84
|
+
let tmpSuccess = flowView.addNode('default', 400, 50, 'Handle Success');
|
|
85
|
+
let tmpFailure = flowView.addNode('halt', 400, 250, 'Handle Error');
|
|
86
|
+
|
|
87
|
+
// Find the Yes and No output ports on the decision node
|
|
88
|
+
let tmpYesPort = tmpDecision.Ports.find((pPort) => pPort.Label === 'Yes');
|
|
89
|
+
let tmpNoPort = tmpDecision.Ports.find((pPort) => pPort.Label === 'No');
|
|
90
|
+
|
|
91
|
+
let tmpSuccessIn = tmpSuccess.Ports.find((pPort) => pPort.Direction === 'input');
|
|
92
|
+
let tmpFailureIn = tmpFailure.Ports.find((pPort) => pPort.Direction === 'input');
|
|
93
|
+
|
|
94
|
+
flowView.addConnection(tmpDecision.Hash, tmpYesPort.Hash, tmpSuccess.Hash, tmpSuccessIn.Hash);
|
|
95
|
+
flowView.addConnection(tmpDecision.Hash, tmpNoPort.Hash, tmpFailure.Hash, tmpFailureIn.Hash);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## See Also
|
|
99
|
+
|
|
100
|
+
- [removeConnection](removeConnection.md) — Delete a connection
|
|
101
|
+
- [addNode](addNode.md) — Create nodes to connect
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# addNode
|
|
2
|
+
|
|
3
|
+
Create a new node on the flow canvas. The node is added to the flow data, assigned a UUID hash, populated with default ports from the node type definition, and the canvas is re-rendered.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
flowView.addNode(pType, pX, pY, pTitle, pData)
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Parameters
|
|
12
|
+
|
|
13
|
+
| Parameter | Type | Required | Description |
|
|
14
|
+
|-----------|------|----------|-------------|
|
|
15
|
+
| `pType` | string | Yes | Node type key. Built-in types: `'start'`, `'end'`, `'halt'`, `'decision'`, `'default'`. Custom types use the `Code` from a registered `PictFlowCard`. |
|
|
16
|
+
| `pX` | number | Yes | X coordinate in SVG space |
|
|
17
|
+
| `pY` | number | Yes | Y coordinate in SVG space |
|
|
18
|
+
| `pTitle` | string | Yes | Display title shown on the node's title bar |
|
|
19
|
+
| `pData` | object | No | Optional custom data object attached to the node's `Data` property |
|
|
20
|
+
|
|
21
|
+
## Returns
|
|
22
|
+
|
|
23
|
+
The newly created node object:
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
{
|
|
27
|
+
Hash: 'node-a1b2c3d4',
|
|
28
|
+
Type: 'start',
|
|
29
|
+
X: 50,
|
|
30
|
+
Y: 150,
|
|
31
|
+
Width: 140,
|
|
32
|
+
Height: 80,
|
|
33
|
+
Title: 'Begin',
|
|
34
|
+
Ports:
|
|
35
|
+
[
|
|
36
|
+
{
|
|
37
|
+
Hash: 'port-e5f6g7h8',
|
|
38
|
+
Direction: 'output',
|
|
39
|
+
Side: 'right',
|
|
40
|
+
Label: 'Out'
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
Data: {}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Events Fired
|
|
48
|
+
|
|
49
|
+
- `onNodeAdded` — with the new node object
|
|
50
|
+
- `onFlowChanged` — with the complete flow data
|
|
51
|
+
|
|
52
|
+
## Examples
|
|
53
|
+
|
|
54
|
+
### Basic usage
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
// Add a start node at position (50, 150)
|
|
58
|
+
let tmpStart = flowView.addNode('start', 50, 150, 'Begin');
|
|
59
|
+
|
|
60
|
+
// Add a processing node
|
|
61
|
+
let tmpProcess = flowView.addNode('default', 250, 150, 'Transform Data');
|
|
62
|
+
|
|
63
|
+
// Add an end node
|
|
64
|
+
let tmpEnd = flowView.addNode('end', 450, 150, 'Done');
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### With custom data
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
let tmpFileRead = flowView.addNode('FREAD', 100, 200, 'Read Config',
|
|
71
|
+
{
|
|
72
|
+
FilePath: '/etc/app/config.json',
|
|
73
|
+
Encoding: 'utf-8'
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
console.log(tmpFileRead.Data.FilePath); // '/etc/app/config.json'
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Adding a decision node
|
|
80
|
+
|
|
81
|
+
```javascript
|
|
82
|
+
let tmpDecision = flowView.addNode('decision', 300, 200, 'Is Valid?');
|
|
83
|
+
|
|
84
|
+
// The decision node has three ports by default: In, Yes, No
|
|
85
|
+
console.log(tmpDecision.Ports.length); // 3
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Programmatic flow construction
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
function buildPipeline(pFlowView, pSteps)
|
|
92
|
+
{
|
|
93
|
+
let tmpPreviousNode = null;
|
|
94
|
+
|
|
95
|
+
for (let i = 0; i < pSteps.length; i++)
|
|
96
|
+
{
|
|
97
|
+
let tmpNode = pFlowView.addNode(
|
|
98
|
+
pSteps[i].Type,
|
|
99
|
+
100 + (i * 200),
|
|
100
|
+
150,
|
|
101
|
+
pSteps[i].Title,
|
|
102
|
+
pSteps[i].Data
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
if (tmpPreviousNode)
|
|
106
|
+
{
|
|
107
|
+
let tmpOutPort = tmpPreviousNode.Ports.find((pPort) => pPort.Direction === 'output');
|
|
108
|
+
let tmpInPort = tmpNode.Ports.find((pPort) => pPort.Direction === 'input');
|
|
109
|
+
|
|
110
|
+
if (tmpOutPort && tmpInPort)
|
|
111
|
+
{
|
|
112
|
+
pFlowView.addConnection(
|
|
113
|
+
tmpPreviousNode.Hash, tmpOutPort.Hash,
|
|
114
|
+
tmpNode.Hash, tmpInPort.Hash
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
tmpPreviousNode = tmpNode;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
buildPipeline(flowView,
|
|
124
|
+
[
|
|
125
|
+
{ Type: 'start', Title: 'Begin' },
|
|
126
|
+
{ Type: 'FREAD', Title: 'Load Data', Data: { FilePath: '/data/input.csv' } },
|
|
127
|
+
{ Type: 'default', Title: 'Process' },
|
|
128
|
+
{ Type: 'end', Title: 'Complete' }
|
|
129
|
+
]);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## See Also
|
|
133
|
+
|
|
134
|
+
- [removeNode](removeNode.md) — Delete a node
|
|
135
|
+
- [getFlowData](getFlowData.md) — Retrieve the full flow state
|
|
136
|
+
- [PictFlowCard](PictFlowCard.md) — Define custom node types
|
|
137
|
+
- [registerNodeType](registerNodeType.md) — Register node types directly
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# autoLayout
|
|
2
|
+
|
|
3
|
+
Automatically arrange all nodes in the flow using a topological sort algorithm. Nodes are positioned in columns based on their dependency order, with even vertical spacing within each column. This is useful for organizing complex flows or providing a clean starting arrangement.
|
|
4
|
+
|
|
5
|
+
## Signature
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
flowView.autoLayout();
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Parameters
|
|
12
|
+
|
|
13
|
+
None.
|
|
14
|
+
|
|
15
|
+
## Behavior
|
|
16
|
+
|
|
17
|
+
1. Performs a topological sort of the node graph based on connections
|
|
18
|
+
2. Assigns nodes to columns (layers) based on their depth from root nodes
|
|
19
|
+
3. Distributes nodes evenly within each column
|
|
20
|
+
4. Re-renders the entire canvas
|
|
21
|
+
5. Optionally snaps to grid if `EnableGridSnap` is `true`
|
|
22
|
+
|
|
23
|
+
Nodes with no connections are placed in a separate area to the side.
|
|
24
|
+
|
|
25
|
+
## Examples
|
|
26
|
+
|
|
27
|
+
### Basic usage
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
// After constructing a complex flow programmatically
|
|
31
|
+
flowView.autoLayout();
|
|
32
|
+
flowView.zoomToFit();
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Auto-layout after data load
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
fetch('/api/flows/my-flow')
|
|
39
|
+
.then((pResponse) => pResponse.json())
|
|
40
|
+
.then((pFlowData) =>
|
|
41
|
+
{
|
|
42
|
+
flowView.setFlowData(pFlowData);
|
|
43
|
+
flowView.autoLayout();
|
|
44
|
+
flowView.zoomToFit();
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Layout button
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
document.getElementById('auto-layout-btn').addEventListener('click', () =>
|
|
52
|
+
{
|
|
53
|
+
flowView.autoLayout();
|
|
54
|
+
flowView.zoomToFit();
|
|
55
|
+
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Grid Snap
|
|
59
|
+
|
|
60
|
+
When `EnableGridSnap` is `true`, the auto-layout algorithm snaps node positions to the nearest grid point based on `GridSnapSize`:
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
_Pict.addView('MyFlow',
|
|
64
|
+
{
|
|
65
|
+
EnableGridSnap: true,
|
|
66
|
+
GridSnapSize: 20
|
|
67
|
+
},
|
|
68
|
+
libPictSectionFlow);
|
|
69
|
+
|
|
70
|
+
// Auto-layout will snap to 20px grid
|
|
71
|
+
flowView.autoLayout();
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## See Also
|
|
75
|
+
|
|
76
|
+
- [setZoom / zoomToFit](setZoom.md) — Fit the result in the viewport
|
|
77
|
+
- [saveLayout / restoreLayout](saveLayout.md) — Persist spatial arrangements
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# getFlowData / setFlowData
|
|
2
|
+
|
|
3
|
+
Get or replace the entire flow state. `getFlowData` returns a deep clone so mutations do not affect the live state. `setFlowData` replaces the flow data and triggers a full re-render.
|
|
4
|
+
|
|
5
|
+
## Signatures
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
let tmpFlowData = flowView.getFlowData();
|
|
9
|
+
flowView.setFlowData(pFlowData);
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## getFlowData
|
|
13
|
+
|
|
14
|
+
### Parameters
|
|
15
|
+
|
|
16
|
+
None.
|
|
17
|
+
|
|
18
|
+
### Returns
|
|
19
|
+
|
|
20
|
+
A deep clone of the complete flow state:
|
|
21
|
+
|
|
22
|
+
```javascript
|
|
23
|
+
{
|
|
24
|
+
Nodes: [ /* ... */ ],
|
|
25
|
+
Connections: [ /* ... */ ],
|
|
26
|
+
OpenPanels: [ /* ... */ ],
|
|
27
|
+
SavedLayouts: [ /* ... */ ],
|
|
28
|
+
ViewState:
|
|
29
|
+
{
|
|
30
|
+
PanX: 0,
|
|
31
|
+
PanY: 0,
|
|
32
|
+
Zoom: 1,
|
|
33
|
+
SelectedNodeHash: null,
|
|
34
|
+
SelectedConnectionHash: null,
|
|
35
|
+
SelectedTetherHash: null
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## setFlowData
|
|
41
|
+
|
|
42
|
+
### Parameters
|
|
43
|
+
|
|
44
|
+
| Parameter | Type | Required | Description |
|
|
45
|
+
|-----------|------|----------|-------------|
|
|
46
|
+
| `pFlowData` | object | Yes | Complete flow data structure |
|
|
47
|
+
|
|
48
|
+
### Events Fired
|
|
49
|
+
|
|
50
|
+
- `onFlowChanged` — with the new flow data
|
|
51
|
+
|
|
52
|
+
## Examples
|
|
53
|
+
|
|
54
|
+
### Save flow state to a server
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
let tmpFlowData = flowView.getFlowData();
|
|
58
|
+
|
|
59
|
+
fetch('/api/flows/my-flow',
|
|
60
|
+
{
|
|
61
|
+
method: 'PUT',
|
|
62
|
+
headers: { 'Content-Type': 'application/json' },
|
|
63
|
+
body: JSON.stringify(tmpFlowData)
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Load flow state from a server
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
fetch('/api/flows/my-flow')
|
|
71
|
+
.then((pResponse) => pResponse.json())
|
|
72
|
+
.then((pFlowData) =>
|
|
73
|
+
{
|
|
74
|
+
flowView.setFlowData(pFlowData);
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Clone a flow
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
let tmpOriginal = flowView.getFlowData();
|
|
82
|
+
|
|
83
|
+
// Modify the clone without affecting the original
|
|
84
|
+
tmpOriginal.Nodes.forEach((pNode) =>
|
|
85
|
+
{
|
|
86
|
+
pNode.X += 200;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
flowView.setFlowData(tmpOriginal);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Inspect flow statistics
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
let tmpData = flowView.getFlowData();
|
|
96
|
+
|
|
97
|
+
console.log('Nodes:', tmpData.Nodes.length);
|
|
98
|
+
console.log('Connections:', tmpData.Connections.length);
|
|
99
|
+
console.log('Open Panels:', tmpData.OpenPanels.length);
|
|
100
|
+
console.log('Saved Layouts:', tmpData.SavedLayouts.length);
|
|
101
|
+
console.log('Zoom:', tmpData.ViewState.Zoom);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Related Methods
|
|
105
|
+
|
|
106
|
+
- `getNode(pNodeHash)` — Retrieve a single node by hash
|
|
107
|
+
- `getConnection(pConnectionHash)` — Retrieve a single connection by hash
|
|
108
|
+
|
|
109
|
+
## See Also
|
|
110
|
+
|
|
111
|
+
- [marshalToView / marshalFromView](marshalToView.md) — AppData two-way binding
|
|
112
|
+
- [addNode](addNode.md) — Add individual nodes
|