pict-section-flow 0.0.2 → 0.0.3
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 +11 -0
- package/docs/README.md +51 -0
- package/example_applications/simple_cards/source/Pict-Application-FlowExample.js +105 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Comment.js +36 -0
- package/example_applications/simple_cards/source/cards/FlowCard-DataPreview.js +42 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Each.js +1 -1
- package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +1 -1
- package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +1 -1
- package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +1 -1
- package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +1 -1
- package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +1 -1
- package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +1 -1
- package/example_applications/simple_cards/source/cards/FlowCard-Sparkline.js +98 -0
- package/example_applications/simple_cards/source/cards/FlowCard-StatusMonitor.js +44 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +1 -1
- package/example_applications/simple_cards/source/views/PictView-FlowExample-MainWorkspace.js +9 -1
- package/package.json +2 -2
- package/source/Pict-Section-Flow.js +8 -1
- package/source/PictFlowCard.js +49 -1
- package/source/providers/PictProvider-Flow-CSS.js +1440 -0
- package/source/providers/PictProvider-Flow-ConnectorShapes.js +413 -0
- package/source/providers/PictProvider-Flow-Geometry.js +43 -0
- package/source/providers/PictProvider-Flow-Icons.js +335 -0
- package/source/providers/PictProvider-Flow-Layouts.js +214 -2
- package/source/providers/PictProvider-Flow-NodeTypes.js +30 -7
- package/source/providers/PictProvider-Flow-Noise.js +241 -0
- package/source/providers/PictProvider-Flow-PanelChrome.js +19 -0
- package/source/providers/PictProvider-Flow-Theme.js +755 -0
- package/source/services/PictService-Flow-ConnectionRenderer.js +95 -32
- package/source/services/PictService-Flow-PanelManager.js +188 -0
- package/source/services/PictService-Flow-SelectionManager.js +109 -0
- package/source/services/PictService-Flow-Tether.js +52 -25
- package/source/services/PictService-Flow-ViewportManager.js +176 -0
- package/source/views/PictView-Flow-FloatingToolbar.js +352 -0
- package/source/views/PictView-Flow-Node.js +654 -169
- package/source/views/PictView-Flow-PropertiesPanel.js +176 -1
- package/source/views/PictView-Flow-Toolbar.js +846 -379
- package/source/views/PictView-Flow.js +279 -671
package/docs/README.md
CHANGED
|
@@ -14,6 +14,57 @@ npm install pict-section-flow
|
|
|
14
14
|
npx quack build
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
+
## Layout Persistence
|
|
18
|
+
|
|
19
|
+
Saved layouts are persisted to `localStorage` by default, keyed by the flow view identifier (e.g. `pict-flow-layouts-MyFlowDiagram`). Layouts survive page refreshes without any configuration.
|
|
20
|
+
|
|
21
|
+
### Overriding Storage (e.g. REST API)
|
|
22
|
+
|
|
23
|
+
The `LayoutProvider` exposes three hookable storage methods that follow the `fCallback(pError, pResult)` convention. Replace them on the instance to use any backend:
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
// After your flow view is initialized:
|
|
27
|
+
let layoutProvider = myFlowView._LayoutProvider;
|
|
28
|
+
|
|
29
|
+
// Persist layouts to a server
|
|
30
|
+
layoutProvider.storageWrite = function(pLayouts, fCallback)
|
|
31
|
+
{
|
|
32
|
+
fetch('/api/my-flow/layouts',
|
|
33
|
+
{
|
|
34
|
+
method: 'PUT',
|
|
35
|
+
headers: { 'Content-Type': 'application/json' },
|
|
36
|
+
body: JSON.stringify(pLayouts)
|
|
37
|
+
})
|
|
38
|
+
.then(() => fCallback(null))
|
|
39
|
+
.catch((pError) => fCallback(pError));
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Load layouts from a server
|
|
43
|
+
layoutProvider.storageRead = function(fCallback)
|
|
44
|
+
{
|
|
45
|
+
fetch('/api/my-flow/layouts')
|
|
46
|
+
.then((pResponse) => pResponse.json())
|
|
47
|
+
.then((pLayouts) => fCallback(null, pLayouts))
|
|
48
|
+
.catch((pError) => fCallback(pError, []));
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Delete all layouts on the server
|
|
52
|
+
layoutProvider.storageDelete = function(fCallback)
|
|
53
|
+
{
|
|
54
|
+
fetch('/api/my-flow/layouts', { method: 'DELETE' })
|
|
55
|
+
.then(() => fCallback(null))
|
|
56
|
+
.catch((pError) => fCallback(pError));
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Load from the new backend now that hooks are set
|
|
60
|
+
layoutProvider.loadPersistedLayouts();
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Configuration Options
|
|
64
|
+
|
|
65
|
+
- **`StorageKey`** (string) -- Override the localStorage key. Passed via options when instantiating the provider.
|
|
66
|
+
- **`StorageKey: false`** -- Disable localStorage persistence entirely (useful when using only a remote backend).
|
|
67
|
+
|
|
17
68
|
## License
|
|
18
69
|
|
|
19
70
|
MIT
|
|
@@ -242,6 +242,83 @@ class FlowExampleApplication extends libPictApplication
|
|
|
242
242
|
{ Hash: 'port-logerr-out', Direction: 'output', Side: 'right', Label: 'Pass' }
|
|
243
243
|
],
|
|
244
244
|
Data: {}
|
|
245
|
+
},
|
|
246
|
+
// ── Halt: premature termination on write error ────
|
|
247
|
+
{
|
|
248
|
+
Hash: 'node-halt',
|
|
249
|
+
Type: 'halt',
|
|
250
|
+
X: 1870,
|
|
251
|
+
Y: 380,
|
|
252
|
+
Width: 140,
|
|
253
|
+
Height: 80,
|
|
254
|
+
Title: 'Halt',
|
|
255
|
+
Ports:
|
|
256
|
+
[
|
|
257
|
+
{ Hash: 'port-halt-in', Direction: 'input', Side: 'left', Label: 'In' }
|
|
258
|
+
],
|
|
259
|
+
Data: {}
|
|
260
|
+
},
|
|
261
|
+
// ── Body Content Examples ──────────────────────────
|
|
262
|
+
// SVG body: Status Monitor
|
|
263
|
+
{
|
|
264
|
+
Hash: 'node-status',
|
|
265
|
+
Type: 'STAT',
|
|
266
|
+
X: 50,
|
|
267
|
+
Y: 530,
|
|
268
|
+
Width: 200,
|
|
269
|
+
Height: 110,
|
|
270
|
+
Title: 'Service Health',
|
|
271
|
+
Ports:
|
|
272
|
+
[
|
|
273
|
+
{ Hash: 'port-stat-in', Direction: 'input', Side: 'left', Label: 'Check' },
|
|
274
|
+
{ Hash: 'port-stat-ok', Direction: 'output', Side: 'right', Label: 'Healthy' },
|
|
275
|
+
{ Hash: 'port-stat-warn', Direction: 'output', Side: 'bottom', Label: 'Degraded' }
|
|
276
|
+
],
|
|
277
|
+
Data: {}
|
|
278
|
+
},
|
|
279
|
+
// HTML body: Data Preview
|
|
280
|
+
{
|
|
281
|
+
Hash: 'node-preview',
|
|
282
|
+
Type: 'PREV',
|
|
283
|
+
X: 340,
|
|
284
|
+
Y: 530,
|
|
285
|
+
Width: 200,
|
|
286
|
+
Height: 120,
|
|
287
|
+
Title: 'Inspect Payload',
|
|
288
|
+
Ports:
|
|
289
|
+
[
|
|
290
|
+
{ Hash: 'port-prev-in', Direction: 'input', Side: 'left', Label: 'Data' },
|
|
291
|
+
{ Hash: 'port-prev-out', Direction: 'output', Side: 'right', Label: 'Pass' }
|
|
292
|
+
],
|
|
293
|
+
Data: {}
|
|
294
|
+
},
|
|
295
|
+
// Canvas body: Sparkline
|
|
296
|
+
{
|
|
297
|
+
Hash: 'node-spark',
|
|
298
|
+
Type: 'SPKL',
|
|
299
|
+
X: 630,
|
|
300
|
+
Y: 530,
|
|
301
|
+
Width: 220,
|
|
302
|
+
Height: 110,
|
|
303
|
+
Title: 'Throughput',
|
|
304
|
+
Ports:
|
|
305
|
+
[
|
|
306
|
+
{ Hash: 'port-spark-in', Direction: 'input', Side: 'left', Label: 'Values' },
|
|
307
|
+
{ Hash: 'port-spark-out', Direction: 'output', Side: 'right', Label: 'Stats' }
|
|
308
|
+
],
|
|
309
|
+
Data: {}
|
|
310
|
+
},
|
|
311
|
+
// HTML body (template): Comment
|
|
312
|
+
{
|
|
313
|
+
Hash: 'node-comment',
|
|
314
|
+
Type: 'NOTE',
|
|
315
|
+
X: 940,
|
|
316
|
+
Y: 540,
|
|
317
|
+
Width: 180,
|
|
318
|
+
Height: 100,
|
|
319
|
+
Title: 'Note',
|
|
320
|
+
Ports: [],
|
|
321
|
+
Data: { NoteText: 'These four cards demonstrate the three BodyContent renderer types: SVG, HTML, and Canvas.' }
|
|
245
322
|
}
|
|
246
323
|
],
|
|
247
324
|
Connections:
|
|
@@ -380,6 +457,34 @@ class FlowExampleApplication extends libPictApplication
|
|
|
380
457
|
TargetNodeHash: 'node-end',
|
|
381
458
|
TargetPortHash: 'port-end-in',
|
|
382
459
|
Data: {}
|
|
460
|
+
},
|
|
461
|
+
// Write error → Halt
|
|
462
|
+
{
|
|
463
|
+
Hash: 'conn-16',
|
|
464
|
+
SourceNodeHash: 'node-fwrite',
|
|
465
|
+
SourcePortHash: 'port-fwrite-err',
|
|
466
|
+
TargetNodeHash: 'node-halt',
|
|
467
|
+
TargetPortHash: 'port-halt-in',
|
|
468
|
+
Data: {}
|
|
469
|
+
},
|
|
470
|
+
// ── Body Content example connections ────────────────
|
|
471
|
+
// Service Health → Inspect Payload
|
|
472
|
+
{
|
|
473
|
+
Hash: 'conn-17',
|
|
474
|
+
SourceNodeHash: 'node-status',
|
|
475
|
+
SourcePortHash: 'port-stat-ok',
|
|
476
|
+
TargetNodeHash: 'node-preview',
|
|
477
|
+
TargetPortHash: 'port-prev-in',
|
|
478
|
+
Data: {}
|
|
479
|
+
},
|
|
480
|
+
// Inspect Payload → Throughput
|
|
481
|
+
{
|
|
482
|
+
Hash: 'conn-18',
|
|
483
|
+
SourceNodeHash: 'node-preview',
|
|
484
|
+
SourcePortHash: 'port-prev-out',
|
|
485
|
+
TargetNodeHash: 'node-spark',
|
|
486
|
+
TargetPortHash: 'port-spark-in',
|
|
487
|
+
Data: {}
|
|
383
488
|
}
|
|
384
489
|
],
|
|
385
490
|
ViewState:
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const libPictFlowCard = require('pict-section-flow').PictFlowCard;
|
|
2
|
+
|
|
3
|
+
class FlowCardComment extends libPictFlowCard
|
|
4
|
+
{
|
|
5
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
6
|
+
{
|
|
7
|
+
super(pFable, Object.assign(
|
|
8
|
+
{},
|
|
9
|
+
{
|
|
10
|
+
Title: 'Comment',
|
|
11
|
+
Name: 'Annotation',
|
|
12
|
+
Code: 'NOTE',
|
|
13
|
+
Description: 'A floating annotation or comment node for documenting flow logic.',
|
|
14
|
+
Icon: 'NOTE',
|
|
15
|
+
Tooltip: 'Comment: Add a note to the diagram',
|
|
16
|
+
Category: 'Documentation',
|
|
17
|
+
TitleBarColor: '#f39c12',
|
|
18
|
+
BodyStyle: { fill: '#fef9e7', stroke: '#f39c12' },
|
|
19
|
+
Width: 180,
|
|
20
|
+
Height: 100,
|
|
21
|
+
Inputs: [],
|
|
22
|
+
Outputs: [],
|
|
23
|
+
ShowTypeLabel: false,
|
|
24
|
+
LabelsInFront: false,
|
|
25
|
+
BodyContent:
|
|
26
|
+
{
|
|
27
|
+
ContentType: 'html',
|
|
28
|
+
Template: '<div style="padding:6px 8px;font-size:10px;line-height:1.5;color:#7d6608;font-style:italic">{~D:Record.Data.NoteText~}</div>'
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
pOptions),
|
|
32
|
+
pServiceHash);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = FlowCardComment;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
const libPictFlowCard = require('pict-section-flow').PictFlowCard;
|
|
2
|
+
|
|
3
|
+
class FlowCardDataPreview extends libPictFlowCard
|
|
4
|
+
{
|
|
5
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
6
|
+
{
|
|
7
|
+
super(pFable, Object.assign(
|
|
8
|
+
{},
|
|
9
|
+
{
|
|
10
|
+
Title: 'Data Preview',
|
|
11
|
+
Name: 'Inspect Data',
|
|
12
|
+
Code: 'PREV',
|
|
13
|
+
Description: 'Displays a preview of the data flowing through this node.',
|
|
14
|
+
Icon: 'PREV',
|
|
15
|
+
Tooltip: 'Data Preview: View data summary',
|
|
16
|
+
Category: 'Debug',
|
|
17
|
+
TitleBarColor: '#2980b9',
|
|
18
|
+
BodyStyle: { fill: '#ebf5fb', stroke: '#2980b9' },
|
|
19
|
+
Width: 200,
|
|
20
|
+
Height: 120,
|
|
21
|
+
Inputs:
|
|
22
|
+
[
|
|
23
|
+
{ Name: 'Data', Side: 'left', MinimumInputCount: 1, MaximumInputCount: 1 }
|
|
24
|
+
],
|
|
25
|
+
Outputs:
|
|
26
|
+
[
|
|
27
|
+
{ Name: 'Pass', Side: 'right' }
|
|
28
|
+
],
|
|
29
|
+
ShowTypeLabel: false,
|
|
30
|
+
PortLabelPadding: true,
|
|
31
|
+
BodyContent:
|
|
32
|
+
{
|
|
33
|
+
ContentType: 'html',
|
|
34
|
+
Template: '<table style="width:100%;border-collapse:collapse;font-size:9px;color:#2c3e50"><tr style="background:#d6eaf8"><td style="padding:3px 5px;font-weight:600">Field</td><td style="padding:3px 5px;font-weight:600">Type</td><td style="padding:3px 5px;font-weight:600">Value</td></tr><tr><td style="padding:2px 5px;border-top:1px solid #d5dbdb">name</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:#8e44ad">str</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:#7f8c8d">"config"</td></tr><tr><td style="padding:2px 5px;border-top:1px solid #d5dbdb">count</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:#8e44ad">num</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:#7f8c8d">42</td></tr><tr><td style="padding:2px 5px;border-top:1px solid #d5dbdb">active</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:#8e44ad">bool</td><td style="padding:2px 5px;border-top:1px solid #d5dbdb;color:#27ae60">true</td></tr></table>'
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
pOptions),
|
|
38
|
+
pServiceHash);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = FlowCardDataPreview;
|
|
@@ -11,7 +11,7 @@ class FlowCardEach extends libPictFlowCard
|
|
|
11
11
|
Name: 'Loop Iterator',
|
|
12
12
|
Code: 'EACH',
|
|
13
13
|
Description: 'Iterates over a collection, executing the body for each item.',
|
|
14
|
-
Icon: '
|
|
14
|
+
Icon: 'EACH',
|
|
15
15
|
Tooltip: 'Each: Iterate over items in a collection',
|
|
16
16
|
Category: 'Control Flow',
|
|
17
17
|
TitleBarColor: '#8e44ad',
|
|
@@ -11,7 +11,7 @@ class FlowCardFileRead extends libPictFlowCard
|
|
|
11
11
|
Name: 'Read File',
|
|
12
12
|
Code: 'FREAD',
|
|
13
13
|
Description: 'Reads the contents of a file from the filesystem.',
|
|
14
|
-
Icon: '
|
|
14
|
+
Icon: 'FREAD',
|
|
15
15
|
Tooltip: 'File Read: Read data from a file',
|
|
16
16
|
Category: 'I/O',
|
|
17
17
|
TitleBarColor: '#2980b9',
|
|
@@ -11,7 +11,7 @@ class FlowCardFileWrite extends libPictFlowCard
|
|
|
11
11
|
Name: 'Write File',
|
|
12
12
|
Code: 'FWRITE',
|
|
13
13
|
Description: 'Writes data to a file on the filesystem.',
|
|
14
|
-
Icon: '
|
|
14
|
+
Icon: 'FWRITE',
|
|
15
15
|
Tooltip: 'File Write: Write data to a file',
|
|
16
16
|
Category: 'I/O',
|
|
17
17
|
TitleBarColor: '#27ae60',
|
|
@@ -11,7 +11,7 @@ class FlowCardGetValue extends libPictFlowCard
|
|
|
11
11
|
Name: 'Read Value',
|
|
12
12
|
Code: 'GET',
|
|
13
13
|
Description: 'Retrieves a named value from the flow context.',
|
|
14
|
-
Icon: '
|
|
14
|
+
Icon: 'GET',
|
|
15
15
|
Tooltip: 'Get Value: Read a value from the context',
|
|
16
16
|
Category: 'Data',
|
|
17
17
|
TitleBarColor: '#2c3e50',
|
|
@@ -11,7 +11,7 @@ class FlowCardIfThenElse extends libPictFlowCard
|
|
|
11
11
|
Name: 'Conditional Branch',
|
|
12
12
|
Code: 'ITE',
|
|
13
13
|
Description: 'Evaluates a condition and routes to the Then or Else branch.',
|
|
14
|
-
Icon: '
|
|
14
|
+
Icon: 'ITE',
|
|
15
15
|
Tooltip: 'If-Then-Else: Routes flow based on a boolean condition',
|
|
16
16
|
Category: 'Control Flow',
|
|
17
17
|
TitleBarColor: '#e67e22',
|
|
@@ -11,7 +11,7 @@ class FlowCardLogValues extends libPictFlowCard
|
|
|
11
11
|
Name: 'Log to Console',
|
|
12
12
|
Code: 'LOG',
|
|
13
13
|
Description: 'Logs input values to the console or log output.',
|
|
14
|
-
Icon: '
|
|
14
|
+
Icon: 'LOG',
|
|
15
15
|
Tooltip: 'Log Values: Output values to the log',
|
|
16
16
|
Category: 'Debug',
|
|
17
17
|
TitleBarColor: '#7f8c8d',
|
|
@@ -11,7 +11,7 @@ class FlowCardSetValue extends libPictFlowCard
|
|
|
11
11
|
Name: 'Assign Value',
|
|
12
12
|
Code: 'SET',
|
|
13
13
|
Description: 'Sets a named value in the flow context.',
|
|
14
|
-
Icon: '
|
|
14
|
+
Icon: 'SET',
|
|
15
15
|
Tooltip: 'Set Value: Assign a value in the context',
|
|
16
16
|
Category: 'Data',
|
|
17
17
|
TitleBarColor: '#16a085',
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const libPictFlowCard = require('pict-section-flow').PictFlowCard;
|
|
2
|
+
|
|
3
|
+
class FlowCardSparkline extends libPictFlowCard
|
|
4
|
+
{
|
|
5
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
6
|
+
{
|
|
7
|
+
super(pFable, Object.assign(
|
|
8
|
+
{},
|
|
9
|
+
{
|
|
10
|
+
Title: 'Sparkline',
|
|
11
|
+
Name: 'Metric Chart',
|
|
12
|
+
Code: 'SPKL',
|
|
13
|
+
Description: 'Renders a live sparkline visualization of numeric throughput data.',
|
|
14
|
+
Icon: 'SPKL',
|
|
15
|
+
Tooltip: 'Sparkline: Visualize numeric throughput',
|
|
16
|
+
Category: 'Visualization',
|
|
17
|
+
TitleBarColor: '#c0392b',
|
|
18
|
+
BodyStyle: { fill: '#fdedec', stroke: '#c0392b' },
|
|
19
|
+
Width: 220,
|
|
20
|
+
Height: 110,
|
|
21
|
+
Inputs:
|
|
22
|
+
[
|
|
23
|
+
{ Name: 'Values', Side: 'left', MinimumInputCount: 1, MaximumInputCount: 1 }
|
|
24
|
+
],
|
|
25
|
+
Outputs:
|
|
26
|
+
[
|
|
27
|
+
{ Name: 'Stats', Side: 'right' }
|
|
28
|
+
],
|
|
29
|
+
ShowTypeLabel: false,
|
|
30
|
+
PortLabelsVertical: true,
|
|
31
|
+
PortLabelPadding: true,
|
|
32
|
+
LabelsInFront: true,
|
|
33
|
+
BodyContent:
|
|
34
|
+
{
|
|
35
|
+
ContentType: 'canvas',
|
|
36
|
+
RenderCallback: function (pCanvas, pNodeData, pNodeTypeConfig, pBounds)
|
|
37
|
+
{
|
|
38
|
+
let tmpCtx = pCanvas.getContext('2d');
|
|
39
|
+
if (!tmpCtx) return;
|
|
40
|
+
|
|
41
|
+
let tmpW = pCanvas.width;
|
|
42
|
+
let tmpH = pCanvas.height;
|
|
43
|
+
|
|
44
|
+
// Sample data points (simulated throughput)
|
|
45
|
+
let tmpData = [12, 19, 8, 25, 15, 30, 22, 18, 35, 28, 14, 32, 20, 26, 10, 24, 33, 17, 29, 21];
|
|
46
|
+
let tmpMax = Math.max.apply(null, tmpData);
|
|
47
|
+
let tmpMin = Math.min.apply(null, tmpData);
|
|
48
|
+
let tmpRange = tmpMax - tmpMin || 1;
|
|
49
|
+
let tmpPadding = 6;
|
|
50
|
+
let tmpChartW = tmpW - (tmpPadding * 2);
|
|
51
|
+
let tmpChartH = tmpH - (tmpPadding * 2);
|
|
52
|
+
let tmpStep = tmpChartW / (tmpData.length - 1);
|
|
53
|
+
|
|
54
|
+
// Draw filled area
|
|
55
|
+
tmpCtx.beginPath();
|
|
56
|
+
tmpCtx.moveTo(tmpPadding, tmpH - tmpPadding);
|
|
57
|
+
for (let i = 0; i < tmpData.length; i++)
|
|
58
|
+
{
|
|
59
|
+
let tmpX = tmpPadding + (i * tmpStep);
|
|
60
|
+
let tmpY = tmpPadding + tmpChartH - ((tmpData[i] - tmpMin) / tmpRange) * tmpChartH;
|
|
61
|
+
tmpCtx.lineTo(tmpX, tmpY);
|
|
62
|
+
}
|
|
63
|
+
tmpCtx.lineTo(tmpPadding + tmpChartW, tmpH - tmpPadding);
|
|
64
|
+
tmpCtx.closePath();
|
|
65
|
+
tmpCtx.fillStyle = 'rgba(192, 57, 43, 0.12)';
|
|
66
|
+
tmpCtx.fill();
|
|
67
|
+
|
|
68
|
+
// Draw line
|
|
69
|
+
tmpCtx.beginPath();
|
|
70
|
+
for (let i = 0; i < tmpData.length; i++)
|
|
71
|
+
{
|
|
72
|
+
let tmpX = tmpPadding + (i * tmpStep);
|
|
73
|
+
let tmpY = tmpPadding + tmpChartH - ((tmpData[i] - tmpMin) / tmpRange) * tmpChartH;
|
|
74
|
+
if (i === 0) tmpCtx.moveTo(tmpX, tmpY);
|
|
75
|
+
else tmpCtx.lineTo(tmpX, tmpY);
|
|
76
|
+
}
|
|
77
|
+
tmpCtx.strokeStyle = '#c0392b';
|
|
78
|
+
tmpCtx.lineWidth = 1.5;
|
|
79
|
+
tmpCtx.lineJoin = 'round';
|
|
80
|
+
tmpCtx.lineCap = 'round';
|
|
81
|
+
tmpCtx.stroke();
|
|
82
|
+
|
|
83
|
+
// Draw end dot
|
|
84
|
+
let tmpLastX = tmpPadding + ((tmpData.length - 1) * tmpStep);
|
|
85
|
+
let tmpLastY = tmpPadding + tmpChartH - ((tmpData[tmpData.length - 1] - tmpMin) / tmpRange) * tmpChartH;
|
|
86
|
+
tmpCtx.beginPath();
|
|
87
|
+
tmpCtx.arc(tmpLastX, tmpLastY, 3, 0, Math.PI * 2);
|
|
88
|
+
tmpCtx.fillStyle = '#c0392b';
|
|
89
|
+
tmpCtx.fill();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
pOptions),
|
|
94
|
+
pServiceHash);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = FlowCardSparkline;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
const libPictFlowCard = require('pict-section-flow').PictFlowCard;
|
|
2
|
+
|
|
3
|
+
class FlowCardStatusMonitor extends libPictFlowCard
|
|
4
|
+
{
|
|
5
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
6
|
+
{
|
|
7
|
+
super(pFable, Object.assign(
|
|
8
|
+
{},
|
|
9
|
+
{
|
|
10
|
+
Title: 'Status Monitor',
|
|
11
|
+
Name: 'Health Check',
|
|
12
|
+
Code: 'STAT',
|
|
13
|
+
Description: 'Monitors the health status of upstream services and reports availability.',
|
|
14
|
+
Icon: 'STAT',
|
|
15
|
+
Tooltip: 'Status Monitor: Check service health',
|
|
16
|
+
Category: 'Monitoring',
|
|
17
|
+
TitleBarColor: '#27ae60',
|
|
18
|
+
BodyStyle: { fill: '#eafaf1', stroke: '#27ae60' },
|
|
19
|
+
Width: 200,
|
|
20
|
+
Height: 110,
|
|
21
|
+
Inputs:
|
|
22
|
+
[
|
|
23
|
+
{ Name: 'Check', Side: 'left', MinimumInputCount: 1, MaximumInputCount: -1 }
|
|
24
|
+
],
|
|
25
|
+
Outputs:
|
|
26
|
+
[
|
|
27
|
+
{ Name: 'Healthy', Side: 'right' },
|
|
28
|
+
{ Name: 'Degraded', Side: 'bottom' }
|
|
29
|
+
],
|
|
30
|
+
ShowTypeLabel: false,
|
|
31
|
+
PortLabelsOnHover: true,
|
|
32
|
+
LabelsInFront: false,
|
|
33
|
+
BodyContent:
|
|
34
|
+
{
|
|
35
|
+
ContentType: 'svg',
|
|
36
|
+
Template: '<circle cx="30" cy="28" r="6" fill="#27ae60" opacity="0.9"/><text x="42" y="32" font-size="9" fill="#2c3e50">API</text><circle cx="100" cy="28" r="6" fill="#27ae60" opacity="0.9"/><text x="112" y="32" font-size="9" fill="#2c3e50">DB</text><circle cx="30" cy="54" r="6" fill="#f39c12" opacity="0.9"/><text x="42" y="58" font-size="9" fill="#2c3e50">Cache</text><circle cx="100" cy="54" r="6" fill="#27ae60" opacity="0.9"/><text x="112" y="58" font-size="9" fill="#2c3e50">Queue</text>'
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
pOptions),
|
|
40
|
+
pServiceHash);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = FlowCardStatusMonitor;
|
|
@@ -11,7 +11,7 @@ class FlowCardSwitch extends libPictFlowCard
|
|
|
11
11
|
Name: 'Multi-way Branch',
|
|
12
12
|
Code: 'SW',
|
|
13
13
|
Description: 'Routes flow to one of multiple cases based on a value.',
|
|
14
|
-
Icon: '
|
|
14
|
+
Icon: 'SW',
|
|
15
15
|
Tooltip: 'Switch: Multi-way branch on a value',
|
|
16
16
|
Category: 'Control Flow',
|
|
17
17
|
TitleBarColor: '#d35400',
|
package/example_applications/simple_cards/source/views/PictView-FlowExample-MainWorkspace.js
CHANGED
|
@@ -10,6 +10,10 @@ const libFlowCardFileWrite = require('../cards/FlowCard-FileWrite.js');
|
|
|
10
10
|
const libFlowCardLogValues = require('../cards/FlowCard-LogValues.js');
|
|
11
11
|
const libFlowCardSetValue = require('../cards/FlowCard-SetValue.js');
|
|
12
12
|
const libFlowCardGetValue = require('../cards/FlowCard-GetValue.js');
|
|
13
|
+
const libFlowCardStatusMonitor = require('../cards/FlowCard-StatusMonitor.js');
|
|
14
|
+
const libFlowCardDataPreview = require('../cards/FlowCard-DataPreview.js');
|
|
15
|
+
const libFlowCardSparkline = require('../cards/FlowCard-Sparkline.js');
|
|
16
|
+
const libFlowCardComment = require('../cards/FlowCard-Comment.js');
|
|
13
17
|
|
|
14
18
|
const _ViewConfiguration =
|
|
15
19
|
{
|
|
@@ -219,7 +223,11 @@ class FlowExampleMainWorkspaceView extends libPictView
|
|
|
219
223
|
libFlowCardFileWrite,
|
|
220
224
|
libFlowCardLogValues,
|
|
221
225
|
libFlowCardSetValue,
|
|
222
|
-
libFlowCardGetValue
|
|
226
|
+
libFlowCardGetValue,
|
|
227
|
+
libFlowCardStatusMonitor,
|
|
228
|
+
libFlowCardDataPreview,
|
|
229
|
+
libFlowCardSparkline,
|
|
230
|
+
libFlowCardComment
|
|
223
231
|
];
|
|
224
232
|
|
|
225
233
|
let tmpNodeTypes = {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pict-section-flow",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Pict Section Flow Diagram",
|
|
5
5
|
"main": "source/Pict-Section-Flow.js",
|
|
6
6
|
"scripts": {
|
|
@@ -17,6 +17,6 @@
|
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"pict": "^1.0.354",
|
|
20
|
-
"quackage": "^1.0.
|
|
20
|
+
"quackage": "^1.0.59"
|
|
21
21
|
}
|
|
22
22
|
}
|
|
@@ -6,8 +6,9 @@ module.exports = require('./views/PictView-Flow.js');
|
|
|
6
6
|
// Node rendering view
|
|
7
7
|
module.exports.PictViewFlowNode = require('./views/PictView-Flow-Node.js');
|
|
8
8
|
|
|
9
|
-
// Toolbar
|
|
9
|
+
// Toolbar views
|
|
10
10
|
module.exports.PictViewFlowToolbar = require('./views/PictView-Flow-Toolbar.js');
|
|
11
|
+
module.exports.PictViewFlowFloatingToolbar = require('./views/PictView-Flow-FloatingToolbar.js');
|
|
11
12
|
|
|
12
13
|
// Services
|
|
13
14
|
module.exports.PictServiceFlowInteractionManager = require('./services/PictService-Flow-InteractionManager.js');
|
|
@@ -15,6 +16,9 @@ module.exports.PictServiceFlowConnectionRenderer = require('./services/PictServi
|
|
|
15
16
|
module.exports.PictServiceFlowTether = require('./services/PictService-Flow-Tether.js');
|
|
16
17
|
module.exports.PictServiceFlowLayout = require('./services/PictService-Flow-Layout.js');
|
|
17
18
|
module.exports.PictServiceFlowPathGenerator = require('./services/PictService-Flow-PathGenerator.js');
|
|
19
|
+
module.exports.PictServiceFlowViewportManager = require('./services/PictService-Flow-ViewportManager.js');
|
|
20
|
+
module.exports.PictServiceFlowSelectionManager = require('./services/PictService-Flow-SelectionManager.js');
|
|
21
|
+
module.exports.PictServiceFlowPanelManager = require('./services/PictService-Flow-PanelManager.js');
|
|
18
22
|
|
|
19
23
|
// Providers
|
|
20
24
|
module.exports.PictProviderFlowNodeTypes = require('./providers/PictProvider-Flow-NodeTypes.js');
|
|
@@ -23,6 +27,9 @@ module.exports.PictProviderFlowLayouts = require('./providers/PictProvider-Flow-
|
|
|
23
27
|
module.exports.PictProviderFlowSVGHelpers = require('./providers/PictProvider-Flow-SVGHelpers.js');
|
|
24
28
|
module.exports.PictProviderFlowGeometry = require('./providers/PictProvider-Flow-Geometry.js');
|
|
25
29
|
module.exports.PictProviderFlowPanelChrome = require('./providers/PictProvider-Flow-PanelChrome.js');
|
|
30
|
+
module.exports.PictProviderFlowCSS = require('./providers/PictProvider-Flow-CSS.js');
|
|
31
|
+
module.exports.PictProviderFlowIcons = require('./providers/PictProvider-Flow-Icons.js');
|
|
32
|
+
module.exports.PictProviderFlowConnectorShapes = require('./providers/PictProvider-Flow-ConnectorShapes.js');
|
|
26
33
|
|
|
27
34
|
// FlowCard base class
|
|
28
35
|
module.exports.PictFlowCard = require('./PictFlowCard.js');
|
package/source/PictFlowCard.js
CHANGED
|
@@ -31,6 +31,18 @@ const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
|
31
31
|
* - DefaultHeight (number) - Panel height (default 200)
|
|
32
32
|
* - Title (string) - Panel title bar text
|
|
33
33
|
* - Configuration (object) - Panel-type-specific configuration
|
|
34
|
+
* - BodyContent (object, optional) - Custom content rendered in the node body area
|
|
35
|
+
* - ContentType (string) - 'svg', 'html', or 'canvas'
|
|
36
|
+
* - Template (string) - Pict template string (html/svg only)
|
|
37
|
+
* - TemplateHash (string) - Registered template hash (takes precedence over Template)
|
|
38
|
+
* - Templates (array) - Template definitions to register [{Hash, Template}]
|
|
39
|
+
* - RenderCallback (function)- Imperative render callback (required for canvas)
|
|
40
|
+
* - Padding (number) - Inner padding in pixels (default 2)
|
|
41
|
+
* - ShowTypeLabel (boolean, default true) - Whether to show the type label/code badge on hover
|
|
42
|
+
* - PortLabelsOnHover (boolean, default false) - When true, port labels only appear on hover
|
|
43
|
+
* - PortLabelsVertical (boolean, default false) - When true, port labels render vertically
|
|
44
|
+
* - PortLabelPadding (boolean, default false) - When true, port labels have extra padding to avoid overlapping body content
|
|
45
|
+
* - LabelsInFront (boolean, default true) - When true, labels render in front of body content; when false, behind
|
|
34
46
|
*
|
|
35
47
|
* Usage:
|
|
36
48
|
* class MyCard extends PictFlowCard {
|
|
@@ -82,6 +94,18 @@ class PictFlowCard extends libFableServiceProviderBase
|
|
|
82
94
|
this.cardPropertiesPanel = (tmpOptions.PropertiesPanel && typeof tmpOptions.PropertiesPanel === 'object')
|
|
83
95
|
? tmpOptions.PropertiesPanel
|
|
84
96
|
: null;
|
|
97
|
+
|
|
98
|
+
// --- Body content configuration ---
|
|
99
|
+
this.cardBodyContent = (tmpOptions.BodyContent && typeof tmpOptions.BodyContent === 'object')
|
|
100
|
+
? tmpOptions.BodyContent
|
|
101
|
+
: null;
|
|
102
|
+
|
|
103
|
+
// --- Label display booleans ---
|
|
104
|
+
this.cardShowTypeLabel = (typeof tmpOptions.ShowTypeLabel === 'boolean') ? tmpOptions.ShowTypeLabel : true;
|
|
105
|
+
this.cardPortLabelsOnHover = (typeof tmpOptions.PortLabelsOnHover === 'boolean') ? tmpOptions.PortLabelsOnHover : false;
|
|
106
|
+
this.cardPortLabelsVertical = (typeof tmpOptions.PortLabelsVertical === 'boolean') ? tmpOptions.PortLabelsVertical : false;
|
|
107
|
+
this.cardPortLabelPadding = (typeof tmpOptions.PortLabelPadding === 'boolean') ? tmpOptions.PortLabelPadding : false;
|
|
108
|
+
this.cardLabelsInFront = (typeof tmpOptions.LabelsInFront === 'boolean') ? tmpOptions.LabelsInFront : true;
|
|
85
109
|
}
|
|
86
110
|
|
|
87
111
|
/**
|
|
@@ -155,12 +179,30 @@ class PictFlowCard extends libFableServiceProviderBase
|
|
|
155
179
|
}
|
|
156
180
|
};
|
|
157
181
|
|
|
182
|
+
// Include label display booleans
|
|
183
|
+
tmpResult.ShowTypeLabel = this.cardShowTypeLabel;
|
|
184
|
+
tmpResult.PortLabelsOnHover = this.cardPortLabelsOnHover;
|
|
185
|
+
tmpResult.PortLabelsVertical = this.cardPortLabelsVertical;
|
|
186
|
+
tmpResult.PortLabelPadding = this.cardPortLabelPadding;
|
|
187
|
+
tmpResult.LabelsInFront = this.cardLabelsInFront;
|
|
188
|
+
|
|
158
189
|
// Include properties panel config if defined
|
|
159
190
|
if (this.cardPropertiesPanel)
|
|
160
191
|
{
|
|
161
192
|
tmpResult.PropertiesPanel = JSON.parse(JSON.stringify(this.cardPropertiesPanel));
|
|
162
193
|
}
|
|
163
194
|
|
|
195
|
+
// Include body content config if defined
|
|
196
|
+
if (this.cardBodyContent)
|
|
197
|
+
{
|
|
198
|
+
tmpResult.BodyContent = JSON.parse(JSON.stringify(this.cardBodyContent));
|
|
199
|
+
// RenderCallback cannot be serialized — preserve the original function reference
|
|
200
|
+
if (typeof this.options.BodyContent.RenderCallback === 'function')
|
|
201
|
+
{
|
|
202
|
+
tmpResult.BodyContent.RenderCallback = this.options.BodyContent.RenderCallback;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
164
206
|
return tmpResult;
|
|
165
207
|
}
|
|
166
208
|
|
|
@@ -203,5 +245,11 @@ module.exports.default_configuration =
|
|
|
203
245
|
Width: 180,
|
|
204
246
|
Height: 80,
|
|
205
247
|
Category: 'General',
|
|
206
|
-
PropertiesPanel: null
|
|
248
|
+
PropertiesPanel: null,
|
|
249
|
+
BodyContent: null,
|
|
250
|
+
ShowTypeLabel: true,
|
|
251
|
+
PortLabelsOnHover: false,
|
|
252
|
+
PortLabelsVertical: false,
|
|
253
|
+
PortLabelPadding: false,
|
|
254
|
+
LabelsInFront: true
|
|
207
255
|
};
|