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,275 @@
|
|
|
1
|
+
# Custom Styling
|
|
2
|
+
|
|
3
|
+
The flow diagram exposes a comprehensive set of CSS custom properties (design tokens) on the `.pict-flow-container` element. Override any of these variables to theme the flow diagram without modifying source code.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
Add a `<style>` block after the flow diagram initializes, or use a theme provider:
|
|
8
|
+
|
|
9
|
+
```css
|
|
10
|
+
/* Override via CSS */
|
|
11
|
+
.pict-flow-container {
|
|
12
|
+
--pf-canvas-bg: #1a1a2e;
|
|
13
|
+
--pf-node-body-fill: #16213e;
|
|
14
|
+
--pf-node-body-stroke: #0f3460;
|
|
15
|
+
--pf-text-primary: #e8e8e8;
|
|
16
|
+
--pf-node-selected-stroke: #e94560;
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
// Override via the Theme Provider API
|
|
22
|
+
flowView._ThemeProvider.registerTheme('dark',
|
|
23
|
+
{
|
|
24
|
+
Key: 'dark',
|
|
25
|
+
Label: 'Dark Mode',
|
|
26
|
+
CSSVariables:
|
|
27
|
+
{
|
|
28
|
+
'--pf-canvas-bg': '#1a1a2e',
|
|
29
|
+
'--pf-node-body-fill': '#16213e',
|
|
30
|
+
'--pf-node-body-stroke': '#0f3460',
|
|
31
|
+
'--pf-text-primary': '#e8e8e8',
|
|
32
|
+
'--pf-node-selected-stroke': '#e94560'
|
|
33
|
+
},
|
|
34
|
+
NodeBodyMode: 'rect'
|
|
35
|
+
});
|
|
36
|
+
flowView.setTheme('dark');
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Design Token Reference
|
|
40
|
+
|
|
41
|
+
All variables are prefixed `--pf-` (short for *pict-flow*) and defined on `.pict-flow-container`. Default values shown below are for the built-in Modern theme.
|
|
42
|
+
|
|
43
|
+
### Text
|
|
44
|
+
|
|
45
|
+
| Variable | Default | Description |
|
|
46
|
+
|----------|---------|-------------|
|
|
47
|
+
| `--pf-text-primary` | `#2c3e50` | Primary text color (labels, body text, form values) |
|
|
48
|
+
| `--pf-text-heading` | `#1a252f` | Heading text (info panel headers) |
|
|
49
|
+
| `--pf-text-secondary` | `#7f8c8d` | Secondary text (descriptions, field labels) |
|
|
50
|
+
| `--pf-text-tertiary` | `#8e99a4` | Tertiary text (section titles, constraints) |
|
|
51
|
+
| `--pf-text-placeholder` | `#95a5a6` | Placeholder/muted text (category labels, code badges) |
|
|
52
|
+
|
|
53
|
+
### Node
|
|
54
|
+
|
|
55
|
+
| Variable | Default | Description |
|
|
56
|
+
|----------|---------|-------------|
|
|
57
|
+
| `--pf-node-body-fill` | `#ffffff` | Node body background |
|
|
58
|
+
| `--pf-node-body-stroke` | `#d0d4d8` | Node body border |
|
|
59
|
+
| `--pf-node-body-stroke-hover` | `#b0b8c0` | Node body border on hover |
|
|
60
|
+
| `--pf-node-body-stroke-width` | `1` | Node body border width |
|
|
61
|
+
| `--pf-node-body-radius` | `8px` | Node body corner radius |
|
|
62
|
+
| `--pf-node-shadow` | `drop-shadow(0 1px 3px rgba(0,0,0,0.10))` | Default node shadow |
|
|
63
|
+
| `--pf-node-shadow-hover` | `drop-shadow(0 2px 6px rgba(0,0,0,0.15))` | Shadow on hover |
|
|
64
|
+
| `--pf-node-shadow-selected` | `drop-shadow(0 2px 8px rgba(52,152,219,0.25))` | Shadow when selected |
|
|
65
|
+
| `--pf-node-shadow-dragging` | `drop-shadow(0 4px 12px rgba(0,0,0,0.20))` | Shadow while dragging |
|
|
66
|
+
| `--pf-node-title-fill` | `#ffffff` | Title text color (on the title bar) |
|
|
67
|
+
| `--pf-node-title-size` | `11.5px` | Title font size |
|
|
68
|
+
| `--pf-node-title-weight` | `600` | Title font weight |
|
|
69
|
+
| `--pf-node-title-bar-color` | `#2c3e50` | Default title bar background color |
|
|
70
|
+
| `--pf-node-type-label-fill` | `#a0a8b0` | Node type label color (appears on hover) |
|
|
71
|
+
| `--pf-node-selected-stroke` | `#3498db` | Primary accent / selection stroke color |
|
|
72
|
+
|
|
73
|
+
### Node Variants
|
|
74
|
+
|
|
75
|
+
Built-in node types override the node body fill and stroke. Use these tokens to customize variant colors globally.
|
|
76
|
+
|
|
77
|
+
| Variable | Default | Description |
|
|
78
|
+
|----------|---------|-------------|
|
|
79
|
+
| `--pf-node-start-fill` | `#eafaf1` | Start node body fill |
|
|
80
|
+
| `--pf-node-start-stroke` | `#27ae60` | Start node body stroke |
|
|
81
|
+
| `--pf-node-end-fill` | `#e8f8f5` | End node body fill |
|
|
82
|
+
| `--pf-node-end-stroke` | `#1abc9c` | End node body stroke |
|
|
83
|
+
| `--pf-node-halt-fill` | `#fdedec` | Halt/error node body fill |
|
|
84
|
+
| `--pf-node-halt-stroke` | `#e74c3c` | Halt/error node body stroke |
|
|
85
|
+
| `--pf-node-decision-fill` | `#fff9e6` | Decision node body fill |
|
|
86
|
+
| `--pf-node-decision-stroke` | `#f39c12` | Decision node body stroke |
|
|
87
|
+
|
|
88
|
+
### Ports
|
|
89
|
+
|
|
90
|
+
| Variable | Default | Description |
|
|
91
|
+
|----------|---------|-------------|
|
|
92
|
+
| `--pf-port-input-fill` | `#3498db` | Input port circle fill |
|
|
93
|
+
| `--pf-port-output-fill` | `#2ecc71` | Output port circle fill |
|
|
94
|
+
| `--pf-port-stroke` | `#ffffff` | Port circle border color |
|
|
95
|
+
| `--pf-port-stroke-width` | `2` | Port circle border width |
|
|
96
|
+
| `--pf-port-label-bg` | `rgba(255,253,240,0.5)` | Port label badge background |
|
|
97
|
+
| `--pf-port-label-text` | `#2c3e50` | Port label text color |
|
|
98
|
+
|
|
99
|
+
### Port Type Colors
|
|
100
|
+
|
|
101
|
+
When ports have a `PortType` set, these colors override the default input/output fills.
|
|
102
|
+
|
|
103
|
+
| Variable | Default | Description |
|
|
104
|
+
|----------|---------|-------------|
|
|
105
|
+
| `--pf-port-event-in-fill` | `#3498db` | Event input port |
|
|
106
|
+
| `--pf-port-event-out-fill` | `#2ecc71` | Event output port |
|
|
107
|
+
| `--pf-port-setting-fill` | `#e67e22` | Setting port |
|
|
108
|
+
| `--pf-port-value-fill` | `#f1c40f` | Value port |
|
|
109
|
+
| `--pf-port-error-fill` | `#e74c3c` | Error port |
|
|
110
|
+
|
|
111
|
+
### Connection Type Colors
|
|
112
|
+
|
|
113
|
+
Connections inherit color from their source port type.
|
|
114
|
+
|
|
115
|
+
| Variable | Default | Description |
|
|
116
|
+
|----------|---------|-------------|
|
|
117
|
+
| `--pf-connection-event-in-stroke` | `#3498db` | Event-in connection stroke |
|
|
118
|
+
| `--pf-connection-event-out-stroke` | `#2ecc71` | Event-out connection stroke |
|
|
119
|
+
| `--pf-connection-setting-stroke` | `#e67e22` | Setting connection stroke |
|
|
120
|
+
| `--pf-connection-value-stroke` | `#f1c40f` | Value connection stroke |
|
|
121
|
+
| `--pf-connection-error-stroke` | `#e74c3c` | Error connection stroke |
|
|
122
|
+
|
|
123
|
+
### Connections
|
|
124
|
+
|
|
125
|
+
| Variable | Default | Description |
|
|
126
|
+
|----------|---------|-------------|
|
|
127
|
+
| `--pf-connection-stroke` | `#95a5a6` | Default connection line color |
|
|
128
|
+
| `--pf-connection-stroke-hover` | `#7f8c8d` | Connection line color on hover |
|
|
129
|
+
| `--pf-connection-selected-stroke` | `#3498db` | Connection line color when selected |
|
|
130
|
+
|
|
131
|
+
### Panels
|
|
132
|
+
|
|
133
|
+
| Variable | Default | Description |
|
|
134
|
+
|----------|---------|-------------|
|
|
135
|
+
| `--pf-panel-bg` | `#ffffff` | Panel background |
|
|
136
|
+
| `--pf-panel-border` | `#d0d4d8` | Panel border color |
|
|
137
|
+
| `--pf-panel-radius` | `8px` | Panel corner radius |
|
|
138
|
+
| `--pf-panel-shadow` | `0 4px 12px rgba(0,0,0,0.10), ...` | Panel box shadow |
|
|
139
|
+
| `--pf-panel-titlebar-bg` | `#f7f8fa` | Panel title bar background |
|
|
140
|
+
| `--pf-panel-titlebar-border` | `#e8eaed` | Title bar bottom border |
|
|
141
|
+
| `--pf-panel-title-color` | `#2c3e50` | Panel title text color |
|
|
142
|
+
|
|
143
|
+
### Tabs
|
|
144
|
+
|
|
145
|
+
| Variable | Default | Description |
|
|
146
|
+
|----------|---------|-------------|
|
|
147
|
+
| `--pf-tab-text` | `#8e99a4` | Inactive tab text color |
|
|
148
|
+
| `--pf-tab-text-hover` | `#5a6a7a` | Tab text color on hover |
|
|
149
|
+
| `--pf-tab-active-border` | `var(--pf-node-selected-stroke)` | Active tab top border color |
|
|
150
|
+
| `--pf-resize-handle-hover` | `#e0e3e6` | Panel resize handle hover color |
|
|
151
|
+
|
|
152
|
+
### Forms & Inputs
|
|
153
|
+
|
|
154
|
+
| Variable | Default | Description |
|
|
155
|
+
|----------|---------|-------------|
|
|
156
|
+
| `--pf-input-border` | `#d5d8dc` | Input/select border color |
|
|
157
|
+
| `--pf-input-border-focus` | `#3498db` | Input border color on focus |
|
|
158
|
+
| `--pf-divider-light` | `#ecf0f1` | Light divider/separator color |
|
|
159
|
+
| `--pf-divider-medium` | `#e8eaed` | Medium divider/separator color |
|
|
160
|
+
|
|
161
|
+
### Buttons
|
|
162
|
+
|
|
163
|
+
| Variable | Default | Description |
|
|
164
|
+
|----------|---------|-------------|
|
|
165
|
+
| `--pf-button-border` | `#bdc3c7` | Button border color |
|
|
166
|
+
| `--pf-button-hover-border` | `#95a5a6` | Button border color on hover |
|
|
167
|
+
| `--pf-button-hover-bg` | `#ecf0f1` | Button background on hover |
|
|
168
|
+
| `--pf-button-active-bg` | `#d5dbdb` | Button background on active/press |
|
|
169
|
+
| `--pf-button-danger-text` | `#e74c3c` | Danger button text color |
|
|
170
|
+
| `--pf-button-danger-hover-bg` | `#fdedec` | Danger button hover background |
|
|
171
|
+
| `--pf-button-close-color` | `#b0b8c0` | Panel close button color |
|
|
172
|
+
|
|
173
|
+
### Badges
|
|
174
|
+
|
|
175
|
+
| Variable | Default | Description |
|
|
176
|
+
|----------|---------|-------------|
|
|
177
|
+
| `--pf-badge-category-bg` | `#f0f2f4` | Category badge background |
|
|
178
|
+
| `--pf-badge-category-text` | `#6b7b8d` | Category badge text color |
|
|
179
|
+
| `--pf-badge-code-bg` | `#eaf2f8` | Code badge background |
|
|
180
|
+
| `--pf-badge-code-text` | `#2980b9` | Code badge text color |
|
|
181
|
+
|
|
182
|
+
### Info Panel
|
|
183
|
+
|
|
184
|
+
| Variable | Default | Description |
|
|
185
|
+
|----------|---------|-------------|
|
|
186
|
+
| `--pf-port-item-bg` | `#f8f9fa` | Port list item background |
|
|
187
|
+
|
|
188
|
+
### Toolbar
|
|
189
|
+
|
|
190
|
+
| Variable | Default | Description |
|
|
191
|
+
|----------|---------|-------------|
|
|
192
|
+
| `--pf-toolbar-bg` | `#ffffff` | Toolbar background |
|
|
193
|
+
| `--pf-toolbar-border` | `#e0e0e0` | Toolbar border and group separators |
|
|
194
|
+
|
|
195
|
+
### Palette Cards
|
|
196
|
+
|
|
197
|
+
| Variable | Default | Description |
|
|
198
|
+
|----------|---------|-------------|
|
|
199
|
+
| `--pf-card-border` | `#d5d8dc` | Palette card border |
|
|
200
|
+
| `--pf-card-hover-bg` | `#eaf2f8` | Palette card hover background |
|
|
201
|
+
| `--pf-card-hover-shadow` | `0 1px 3px rgba(52,152,219,0.15)` | Palette card hover shadow |
|
|
202
|
+
|
|
203
|
+
### Canvas
|
|
204
|
+
|
|
205
|
+
| Variable | Default | Description |
|
|
206
|
+
|----------|---------|-------------|
|
|
207
|
+
| `--pf-canvas-bg` | `#fafafa` | Background color of the SVG canvas |
|
|
208
|
+
| `--pf-grid-stroke` | `#e8e8e8` | Grid line color |
|
|
209
|
+
|
|
210
|
+
## Per-Node-Type Overrides
|
|
211
|
+
|
|
212
|
+
Individual node types can scope-override any variable. The flow renderer adds a CSS class `.pict-flow-node-{type}` to each node group. Target these classes to customize specific node types without affecting others:
|
|
213
|
+
|
|
214
|
+
```css
|
|
215
|
+
/* Make "FileRead" nodes stand out */
|
|
216
|
+
.pict-flow-node-FileRead .pict-flow-node-body {
|
|
217
|
+
fill: #fef9e7;
|
|
218
|
+
stroke: #f4d03f;
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Theme Provider API
|
|
223
|
+
|
|
224
|
+
The `PictProviderFlowTheme` service manages named themes and applies them via CSS variable overrides.
|
|
225
|
+
|
|
226
|
+
### Registering a Theme
|
|
227
|
+
|
|
228
|
+
```javascript
|
|
229
|
+
flowView._ThemeProvider.registerTheme('corporate',
|
|
230
|
+
{
|
|
231
|
+
Key: 'corporate',
|
|
232
|
+
Label: 'Corporate',
|
|
233
|
+
CSSVariables:
|
|
234
|
+
{
|
|
235
|
+
'--pf-canvas-bg': '#f5f6fa',
|
|
236
|
+
'--pf-node-selected-stroke': '#0066cc',
|
|
237
|
+
'--pf-node-body-stroke': '#c8d6e5',
|
|
238
|
+
'--pf-toolbar-bg': '#ffffff',
|
|
239
|
+
'--pf-panel-titlebar-bg': '#f0f2f5'
|
|
240
|
+
},
|
|
241
|
+
NodeBodyMode: 'rect',
|
|
242
|
+
NoiseConfig: { Enabled: false }
|
|
243
|
+
});
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Switching Themes
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
flowView.setTheme('corporate'); // Apply the corporate theme
|
|
250
|
+
flowView.setTheme('default'); // Restore the default Modern theme
|
|
251
|
+
flowView.setTheme('sketch'); // Hand-drawn style (built-in)
|
|
252
|
+
flowView.setTheme('blueprint'); // Blueprint style (built-in)
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Theme Properties
|
|
256
|
+
|
|
257
|
+
| Property | Type | Description |
|
|
258
|
+
|----------|------|-------------|
|
|
259
|
+
| `Key` | `string` | Unique identifier for the theme |
|
|
260
|
+
| `Label` | `string` | Display name |
|
|
261
|
+
| `CSSVariables` | `object` | Map of `--pf-*` variable names to values |
|
|
262
|
+
| `AdditionalCSS` | `string` | Extra CSS appended after variable overrides |
|
|
263
|
+
| `NodeBodyMode` | `string` | Node rendering mode: `'rect'` or `'bracket'` |
|
|
264
|
+
| `NoiseConfig` | `object` | Hand-drawn effect configuration |
|
|
265
|
+
|
|
266
|
+
### How It Works
|
|
267
|
+
|
|
268
|
+
When a theme is activated:
|
|
269
|
+
|
|
270
|
+
1. The CSS provider calls `generateCSS()` which aggregates all domain CSS
|
|
271
|
+
2. If an active theme defines `CSSVariables`, they are emitted as a `.pict-flow-container` override block appended after the base CSS
|
|
272
|
+
3. If `AdditionalCSS` is defined, it is appended after the variable overrides
|
|
273
|
+
4. The combined CSS is re-injected via the Pict `CSSMap` service
|
|
274
|
+
|
|
275
|
+
This means theme variable overrides have higher specificity than the base defaults (same selector, later in source order), and `AdditionalCSS` can add entirely new rules or increase specificity where needed.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# Data Model
|
|
2
|
+
|
|
3
|
+
The entire flow graph state is represented by a single JSON object called **FlowData**. Every mutation goes through the `DataManager` service and triggers a re-render. This structure is what gets persisted when you call `marshalFromView()` and restored with `marshalToView()`.
|
|
4
|
+
|
|
5
|
+
## FlowData Structure
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
{
|
|
9
|
+
Nodes: [], // Array of node objects
|
|
10
|
+
Connections: [], // Array of connection objects
|
|
11
|
+
OpenPanels: [], // Array of panel objects
|
|
12
|
+
SavedLayouts: [], // Array of layout snapshots
|
|
13
|
+
ViewState:
|
|
14
|
+
{
|
|
15
|
+
PanX: 0, // Viewport horizontal offset
|
|
16
|
+
PanY: 0, // Viewport vertical offset
|
|
17
|
+
Zoom: 1, // Viewport zoom level
|
|
18
|
+
SelectedNodeHash: null,
|
|
19
|
+
SelectedConnectionHash: null,
|
|
20
|
+
SelectedTetherHash: null
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Node
|
|
26
|
+
|
|
27
|
+
Each node represents a discrete operation in the flow graph.
|
|
28
|
+
|
|
29
|
+
```javascript
|
|
30
|
+
{
|
|
31
|
+
Hash: 'abc123', // Unique identifier (auto-generated)
|
|
32
|
+
Type: 'ITE', // Card type code (references NodeTypes)
|
|
33
|
+
X: 200, // Horizontal position in SVG space
|
|
34
|
+
Y: 150, // Vertical position in SVG space
|
|
35
|
+
Width: 200, // Node width in pixels
|
|
36
|
+
Height: 100, // Node height in pixels
|
|
37
|
+
Title: 'If-Then-Else', // Display title on the node
|
|
38
|
+
Ports: // Array of port definitions
|
|
39
|
+
[
|
|
40
|
+
{
|
|
41
|
+
Hash: 'port-1',
|
|
42
|
+
Direction: 'input', // 'input' or 'output'
|
|
43
|
+
Side: 'left', // Port position on node edge
|
|
44
|
+
Label: 'In', // Port label text
|
|
45
|
+
PortType: 'event-in', // Optional type for coloring
|
|
46
|
+
DataType: 'boolean', // Optional data type hint
|
|
47
|
+
MinimumInputCount: 1, // Minimum connections (0 = optional)
|
|
48
|
+
MaximumInputCount: 1 // Maximum connections (-1 = unlimited)
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
Style: // Optional per-node style overrides
|
|
52
|
+
{
|
|
53
|
+
BodyFill: '#fef5e7',
|
|
54
|
+
BodyStroke: '#e67e22',
|
|
55
|
+
BodyStrokeWidth: 1,
|
|
56
|
+
TitleBarColor: '#e67e22'
|
|
57
|
+
},
|
|
58
|
+
Data: {} // User-defined payload (card-specific)
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Port Side Positions
|
|
63
|
+
|
|
64
|
+
Ports can be placed at 12 positions around the node perimeter using a zone system:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
top-left top top-right
|
|
68
|
+
left-top (body) right-top
|
|
69
|
+
left right
|
|
70
|
+
left-bottom (body) right-bottom
|
|
71
|
+
bottom-left bottom bottom-right
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The legacy four-value sides (`left`, `right`, `top`, `bottom`) map to the middle zone of each edge.
|
|
75
|
+
|
|
76
|
+
## Connection
|
|
77
|
+
|
|
78
|
+
A connection links an output port on one node to an input port on another.
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
{
|
|
82
|
+
Hash: 'conn-1',
|
|
83
|
+
SourceNodeHash: 'node-a',
|
|
84
|
+
SourcePortHash: 'port-out',
|
|
85
|
+
TargetNodeHash: 'node-b',
|
|
86
|
+
TargetPortHash: 'port-in',
|
|
87
|
+
Data:
|
|
88
|
+
{
|
|
89
|
+
LineMode: 'bezier', // 'bezier' or 'orthogonal'
|
|
90
|
+
HandleCustomized: false, // True if handles have been manually moved
|
|
91
|
+
|
|
92
|
+
// Bezier handles (multi-point curve control)
|
|
93
|
+
BezierHandles: [{ x: 300, y: 175 }],
|
|
94
|
+
|
|
95
|
+
// Orthogonal handles (right-angle path corners)
|
|
96
|
+
OrthoCorner1X: 250,
|
|
97
|
+
OrthoCorner1Y: 150,
|
|
98
|
+
OrthoCorner2X: 350,
|
|
99
|
+
OrthoCorner2Y: 200,
|
|
100
|
+
OrthoMidOffset: 0
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Connection Routing
|
|
106
|
+
|
|
107
|
+
Connections support two path modes:
|
|
108
|
+
|
|
109
|
+
- **Bezier** (default) — Smooth curves with one or more control handles. Right-click a connection to add a handle; drag handles to reshape the curve.
|
|
110
|
+
- **Orthogonal** — Right-angle paths with two corner points. Double-click a connection handle to toggle between bezier and orthogonal modes.
|
|
111
|
+
|
|
112
|
+
## Panel
|
|
113
|
+
|
|
114
|
+
An open properties panel associated with a node.
|
|
115
|
+
|
|
116
|
+
```javascript
|
|
117
|
+
{
|
|
118
|
+
Hash: 'panel-1',
|
|
119
|
+
NodeHash: 'node-a', // The node this panel belongs to
|
|
120
|
+
PanelType: 'Form', // 'Template', 'Markdown', 'Form', 'View', or 'Info'
|
|
121
|
+
Title: 'Set Value Properties',
|
|
122
|
+
X: 450, // Panel position in SVG space
|
|
123
|
+
Y: 100,
|
|
124
|
+
Width: 320,
|
|
125
|
+
Height: 200
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Each panel is rendered as an HTML `foreignObject` inside the SVG, with a tether line connecting it to its parent node. Panels can be dragged, resized, and have tabbed views for Properties, Help, and Appearance.
|
|
130
|
+
|
|
131
|
+
## ViewState
|
|
132
|
+
|
|
133
|
+
Viewport and selection state, persisted with the flow data.
|
|
134
|
+
|
|
135
|
+
```javascript
|
|
136
|
+
{
|
|
137
|
+
PanX: -120, // Horizontal pan offset
|
|
138
|
+
PanY: -50, // Vertical pan offset
|
|
139
|
+
Zoom: 0.85, // Zoom level (0.1 to 5.0)
|
|
140
|
+
SelectedNodeHash: 'node-a', // Currently selected node (null if none)
|
|
141
|
+
SelectedConnectionHash: null, // Currently selected connection
|
|
142
|
+
SelectedTetherHash: null // Currently selected tether line
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Relationships
|
|
147
|
+
|
|
148
|
+
```mermaid
|
|
149
|
+
erDiagram
|
|
150
|
+
FlowData ||--o{ Node : contains
|
|
151
|
+
FlowData ||--o{ Connection : contains
|
|
152
|
+
FlowData ||--o{ Panel : contains
|
|
153
|
+
FlowData ||--|| ViewState : has
|
|
154
|
+
Node ||--o{ Port : has
|
|
155
|
+
Connection }o--|| Port : "source port"
|
|
156
|
+
Connection }o--|| Port : "target port"
|
|
157
|
+
Panel }o--|| Node : "attached to"
|
|
158
|
+
```
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Event System
|
|
2
|
+
|
|
3
|
+
Pict-Section-Flow exposes a rich event system through the `EventHandlerProvider`. Register handlers to react to user interactions, data changes, and lifecycle events without modifying core code.
|
|
4
|
+
|
|
5
|
+
## Registering Handlers
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
let tmpFlowView = _Pict.views.MyFlow;
|
|
9
|
+
|
|
10
|
+
// Register a named handler
|
|
11
|
+
tmpFlowView._EventHandlerProvider.registerHandler(
|
|
12
|
+
'onNodeAdded',
|
|
13
|
+
(pNode, pFlowView) =>
|
|
14
|
+
{
|
|
15
|
+
console.log('Node added:', pNode.Title);
|
|
16
|
+
},
|
|
17
|
+
'my-node-handler'
|
|
18
|
+
);
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The third parameter is an optional handler hash for later removal. If omitted, a unique hash is generated and returned.
|
|
22
|
+
|
|
23
|
+
## Removing Handlers
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
// Remove a specific handler
|
|
27
|
+
tmpFlowView._EventHandlerProvider.removeHandler('onNodeAdded', 'my-node-handler');
|
|
28
|
+
|
|
29
|
+
// Remove all handlers for an event
|
|
30
|
+
tmpFlowView._EventHandlerProvider.removeAllHandlers('onNodeAdded');
|
|
31
|
+
|
|
32
|
+
// Remove all handlers for all events
|
|
33
|
+
tmpFlowView._EventHandlerProvider.removeAllHandlers();
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Querying Handlers
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
// Check if any handlers are registered
|
|
40
|
+
tmpFlowView._EventHandlerProvider.hasHandlers('onNodeAdded'); // true or false
|
|
41
|
+
|
|
42
|
+
// Count registered handlers
|
|
43
|
+
tmpFlowView._EventHandlerProvider.getHandlerCount('onNodeAdded'); // number
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Available Events
|
|
47
|
+
|
|
48
|
+
### Node Events
|
|
49
|
+
|
|
50
|
+
| Event | Payload | Description |
|
|
51
|
+
|-------|---------|-------------|
|
|
52
|
+
| `onNodeSelected` | `node` or `null` | A node was selected or deselected |
|
|
53
|
+
| `onNodeAdded` | `node` | A new node was created |
|
|
54
|
+
| `onNodeRemoved` | `node` | A node was deleted |
|
|
55
|
+
| `onNodeMoved` | `node` | A node was dragged to a new position |
|
|
56
|
+
|
|
57
|
+
### Connection Events
|
|
58
|
+
|
|
59
|
+
| Event | Payload | Description |
|
|
60
|
+
|-------|---------|-------------|
|
|
61
|
+
| `onConnectionSelected` | `connection` | A connection was selected |
|
|
62
|
+
| `onConnectionCreated` | `connection` | A new connection was created between ports |
|
|
63
|
+
| `onConnectionRemoved` | `connection` | A connection was deleted |
|
|
64
|
+
| `onConnectionHandleMoved` | `connection` | A bezier or orthogonal handle was dragged |
|
|
65
|
+
| `onConnectionModeChanged` | `connection` | A connection toggled between bezier and orthogonal |
|
|
66
|
+
|
|
67
|
+
### Panel Events
|
|
68
|
+
|
|
69
|
+
| Event | Payload | Description |
|
|
70
|
+
|-------|---------|-------------|
|
|
71
|
+
| `onPanelOpened` | `panelData` | A properties panel was opened |
|
|
72
|
+
| `onPanelClosed` | `panelData` | A properties panel was closed |
|
|
73
|
+
| `onPanelMoved` | `panelData` | A properties panel was dragged |
|
|
74
|
+
|
|
75
|
+
### Tether Events
|
|
76
|
+
|
|
77
|
+
| Event | Payload | Description |
|
|
78
|
+
|-------|---------|-------------|
|
|
79
|
+
| `onTetherSelected` | `panelData` | A tether line was selected |
|
|
80
|
+
| `onTetherHandleMoved` | `panelData` | A tether handle was dragged |
|
|
81
|
+
| `onTetherModeChanged` | `panelData` | A tether toggled between bezier and orthogonal |
|
|
82
|
+
|
|
83
|
+
### Layout and Meta Events
|
|
84
|
+
|
|
85
|
+
| Event | Payload | Description |
|
|
86
|
+
|-------|---------|-------------|
|
|
87
|
+
| `onLayoutSaved` | `layoutData` | A layout snapshot was saved |
|
|
88
|
+
| `onLayoutRestored` | `layoutData` | A saved layout was restored |
|
|
89
|
+
| `onLayoutDeleted` | `layoutData` | A saved layout was deleted |
|
|
90
|
+
| `onFlowChanged` | `flowData` | Catch-all fired after any data mutation |
|
|
91
|
+
| `onThemeChanged` | `themeKey` | The active theme was switched |
|
|
92
|
+
|
|
93
|
+
## Common Patterns
|
|
94
|
+
|
|
95
|
+
### Undo/Redo Stack
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
let tmpUndoStack = [];
|
|
99
|
+
let tmpRedoStack = [];
|
|
100
|
+
|
|
101
|
+
tmpFlowView._EventHandlerProvider.registerHandler(
|
|
102
|
+
'onFlowChanged',
|
|
103
|
+
(pFlowData) =>
|
|
104
|
+
{
|
|
105
|
+
tmpUndoStack.push(JSON.parse(JSON.stringify(pFlowData)));
|
|
106
|
+
tmpRedoStack = [];
|
|
107
|
+
},
|
|
108
|
+
'undo-tracker'
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
function undo()
|
|
112
|
+
{
|
|
113
|
+
if (tmpUndoStack.length < 2) return;
|
|
114
|
+
tmpRedoStack.push(tmpUndoStack.pop());
|
|
115
|
+
tmpFlowView.setFlowData(tmpUndoStack[tmpUndoStack.length - 1]);
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Server Sync
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
tmpFlowView._EventHandlerProvider.registerHandler(
|
|
123
|
+
'onFlowChanged',
|
|
124
|
+
(pFlowData) =>
|
|
125
|
+
{
|
|
126
|
+
fetch('/api/flows/save',
|
|
127
|
+
{
|
|
128
|
+
method: 'POST',
|
|
129
|
+
headers: { 'Content-Type': 'application/json' },
|
|
130
|
+
body: JSON.stringify(pFlowData)
|
|
131
|
+
});
|
|
132
|
+
},
|
|
133
|
+
'server-sync'
|
|
134
|
+
);
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Selection Sidebar
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
tmpFlowView._EventHandlerProvider.registerHandler(
|
|
141
|
+
'onNodeSelected',
|
|
142
|
+
(pNode) =>
|
|
143
|
+
{
|
|
144
|
+
if (pNode)
|
|
145
|
+
{
|
|
146
|
+
document.getElementById('sidebar-title').textContent = pNode.Title;
|
|
147
|
+
document.getElementById('sidebar-type').textContent = pNode.Type;
|
|
148
|
+
}
|
|
149
|
+
else
|
|
150
|
+
{
|
|
151
|
+
document.getElementById('sidebar-title').textContent = 'No selection';
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
'sidebar-updater'
|
|
155
|
+
);
|
|
156
|
+
```
|