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
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PictProvider-Flow-Icons
|
|
5
|
+
*
|
|
6
|
+
* Centralized SVG icon provider for the flow diagram.
|
|
7
|
+
* All icons use a duotone style: 2px outline (#2c3e50) with
|
|
8
|
+
* subtle filled accent shapes (#d5e8f7).
|
|
9
|
+
*
|
|
10
|
+
* Each icon is registered as a pict template with hash `Flow-Icon-{key}`,
|
|
11
|
+
* making them individually overridable by consumers.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const _ProviderConfiguration =
|
|
15
|
+
{
|
|
16
|
+
ProviderIdentifier: 'PictProviderFlowIcons'
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// ── Default Icon SVG Markup ────────────────────────────────────────────────
|
|
20
|
+
// All icons: viewBox="0 0 24 24", duotone style
|
|
21
|
+
// Accent fill: #d5e8f7, Stroke: #2c3e50, Stroke-width: 2
|
|
22
|
+
//
|
|
23
|
+
// The {FlowIconSize} placeholder is replaced at render time with the
|
|
24
|
+
// requested pixel size. Each template is a self-contained <svg> element.
|
|
25
|
+
|
|
26
|
+
const _DefaultIcons =
|
|
27
|
+
{
|
|
28
|
+
// ── FlowCard Icons ─────────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
'ITE': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="6" r="3" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><circle cx="6" cy="18" r="2.5" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><circle cx="18" cy="18" r="2.5" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M12 9v2M9.5 12.5L6 15.5M14.5 12.5L18 15.5" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
31
|
+
|
|
32
|
+
'SW': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M3 12h5M16 12h5M14.8 9.2l3.7-5.2M14.8 14.8l3.7 5.2" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
33
|
+
|
|
34
|
+
'EACH': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7" rx="1.5" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><rect x="14" y="3" width="7" height="7" rx="1.5" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><rect x="3" y="14" width="7" height="7" rx="1.5" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><rect x="14" y="14" width="7" height="7" rx="1.5" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
35
|
+
|
|
36
|
+
'FREAD': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9l-7-7z" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M13 2v7h7" stroke="#2c3e50" stroke-width="2"/><path d="M9 13h6M9 17h4" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
37
|
+
|
|
38
|
+
'FWRITE': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9l-7-7z" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M13 2v7h7" stroke="#2c3e50" stroke-width="2"/><path d="M12 13v5M9.5 15.5L12 13l2.5 2.5" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
39
|
+
|
|
40
|
+
'LOG': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="3" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><circle cx="7.5" cy="8" r="1" fill="#2c3e50"/><circle cx="7.5" cy="12" r="1" fill="#2c3e50"/><circle cx="7.5" cy="16" r="1" fill="#2c3e50"/><path d="M11 8h5.5M11 12h5.5M11 16h3.5" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
41
|
+
|
|
42
|
+
'GET': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><circle cx="10.5" cy="10.5" r="6.5" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M21 21l-5.15-5.15" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
43
|
+
|
|
44
|
+
'SET': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4.5 1.5L4 16Z" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M14 6l3 3" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
45
|
+
|
|
46
|
+
// ── UI Icons ───────────────────────────────────────────────────────────
|
|
47
|
+
|
|
48
|
+
'fullscreen': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke="#2c3e50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6"/><path d="M9 21H3v-6"/><path d="M21 3l-7 7"/><path d="M3 21l7-7"/></svg>',
|
|
49
|
+
|
|
50
|
+
'exit-fullscreen': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke="#2c3e50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 14h6v6"/><path d="M20 10h-6V4"/><path d="M14 10l7-7"/><path d="M3 21l7-7"/></svg>',
|
|
51
|
+
|
|
52
|
+
'close': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke="#2c3e50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',
|
|
53
|
+
|
|
54
|
+
'chevron-down': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke="#2c3e50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>',
|
|
55
|
+
|
|
56
|
+
// ── Toolbar & Popup Icons ─────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
'search': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M21 21l-4.35-4.35" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
59
|
+
|
|
60
|
+
'cards': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="7" width="16" height="12" rx="2" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M6 7V5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v10a2 2 0 0 1-2 2h-2" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
61
|
+
|
|
62
|
+
'layout': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="8" height="10" rx="1.5" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><rect x="13" y="3" width="8" height="6" rx="1.5" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><rect x="3" y="15" width="8" height="6" rx="1.5" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><rect x="13" y="11" width="8" height="10" rx="1.5" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
63
|
+
|
|
64
|
+
'collapse': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="3" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M8 12h8" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
65
|
+
|
|
66
|
+
'expand': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="3" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M12 8v8M8 12h8" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
67
|
+
|
|
68
|
+
'grip': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="#2c3e50"><circle cx="9" cy="5" r="1.5"/><circle cx="15" cy="5" r="1.5"/><circle cx="9" cy="12" r="1.5"/><circle cx="15" cy="12" r="1.5"/><circle cx="9" cy="19" r="1.5"/><circle cx="15" cy="19" r="1.5"/></svg>',
|
|
69
|
+
|
|
70
|
+
'settings': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke="#2c3e50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3" fill="#d5e8f7"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>',
|
|
71
|
+
|
|
72
|
+
'plus': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M12 8v8M8 12h8" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
73
|
+
|
|
74
|
+
'trash': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18" stroke="#2c3e50" stroke-width="2"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" stroke="#2c3e50" stroke-width="2"/><rect x="5" y="6" width="14" height="14" rx="2" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M10 11v6M14 11v6" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
75
|
+
|
|
76
|
+
'save': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M17 21v-8H7v8" stroke="#2c3e50" stroke-width="2"/><path d="M7 3v5h8" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
77
|
+
|
|
78
|
+
'auto-layout': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><rect x="8" y="2" width="8" height="6" rx="1.5" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><rect x="2" y="16" width="8" height="6" rx="1.5" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><rect x="14" y="16" width="8" height="6" rx="1.5" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M12 8v4M6 16v-4h12v4" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
79
|
+
|
|
80
|
+
'zoom-in': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M21 21l-4.35-4.35" stroke="#2c3e50" stroke-width="2"/><path d="M11 8v6M8 11h6" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
81
|
+
|
|
82
|
+
'zoom-out': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="7" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M21 21l-4.35-4.35" stroke="#2c3e50" stroke-width="2"/><path d="M8 11h6" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
83
|
+
|
|
84
|
+
'zoom-fit': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke="#2c3e50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 7V3h4"/><path d="M17 3h4v4"/><path d="M21 17v4h-4"/><path d="M7 21H3v-4"/><rect x="7" y="7" width="10" height="10" rx="1.5" fill="#d5e8f7"/></svg>',
|
|
85
|
+
|
|
86
|
+
'dock': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="4" rx="1.5" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M12 20V11M8 14l4-4 4 4" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
87
|
+
|
|
88
|
+
'restore': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke="#2c3e50" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12a9 9 0 1 0 3-6.7L3 8"/><path d="M3 3v5h5"/></svg>',
|
|
89
|
+
|
|
90
|
+
'delete-node': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><path d="M3 6h18" stroke="#2c3e50" stroke-width="2"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" stroke="#2c3e50" stroke-width="2"/><rect x="5" y="6" width="14" height="14" rx="2" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><path d="M10 11v6M14 11v6" stroke="#2c3e50" stroke-width="2"/></svg>',
|
|
91
|
+
|
|
92
|
+
// ── Fallback ───────────────────────────────────────────────────────────
|
|
93
|
+
|
|
94
|
+
'default': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="4" fill="#d5e8f7" stroke="#2c3e50" stroke-width="2"/><circle cx="12" cy="12" r="2.5" fill="#2c3e50"/></svg>'
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
class PictProviderFlowIcons extends libFableServiceProviderBase
|
|
98
|
+
{
|
|
99
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
100
|
+
{
|
|
101
|
+
let tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);
|
|
102
|
+
super(pFable, tmpOptions, pServiceHash);
|
|
103
|
+
|
|
104
|
+
this.serviceType = 'PictProviderFlowIcons';
|
|
105
|
+
|
|
106
|
+
this._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;
|
|
107
|
+
|
|
108
|
+
// Deep copy the default icons
|
|
109
|
+
this._Icons = JSON.parse(JSON.stringify(_DefaultIcons));
|
|
110
|
+
|
|
111
|
+
// Merge any additional icons passed via options
|
|
112
|
+
if (pOptions && pOptions.AdditionalIcons && typeof pOptions.AdditionalIcons === 'object')
|
|
113
|
+
{
|
|
114
|
+
let tmpKeys = Object.keys(pOptions.AdditionalIcons);
|
|
115
|
+
for (let i = 0; i < tmpKeys.length; i++)
|
|
116
|
+
{
|
|
117
|
+
this._Icons[tmpKeys[i]] = pOptions.AdditionalIcons[tmpKeys[i]];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Register all icons as pict templates with hash Flow-Icon-{key}.
|
|
124
|
+
* Consumers can override any icon by registering a template with
|
|
125
|
+
* the same hash before the flow view renders.
|
|
126
|
+
*/
|
|
127
|
+
registerIconTemplates()
|
|
128
|
+
{
|
|
129
|
+
if (!this.fable || !this.fable.TemplateProvider)
|
|
130
|
+
{
|
|
131
|
+
this.log.warn('PictProviderFlowIcons: TemplateProvider not available; icon templates not registered.');
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
let tmpKeys = Object.keys(this._Icons);
|
|
136
|
+
for (let i = 0; i < tmpKeys.length; i++)
|
|
137
|
+
{
|
|
138
|
+
let tmpHash = 'Flow-Icon-' + tmpKeys[i];
|
|
139
|
+
|
|
140
|
+
// Only register if not already present (allow consumer overrides)
|
|
141
|
+
if (!this.fable.TemplateProvider.getTemplate(tmpHash))
|
|
142
|
+
{
|
|
143
|
+
this.fable.TemplateProvider.addTemplate(tmpHash, this._Icons[tmpKeys[i]]);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Determine if the given icon string is an emoji (legacy) or an icon key.
|
|
150
|
+
* Returns true if the string contains characters with code points above U+00FF,
|
|
151
|
+
* indicating emoji or Unicode symbol characters.
|
|
152
|
+
*
|
|
153
|
+
* @param {string} pIconValue - The icon value to check
|
|
154
|
+
* @returns {boolean}
|
|
155
|
+
*/
|
|
156
|
+
isEmojiIcon(pIconValue)
|
|
157
|
+
{
|
|
158
|
+
if (!pIconValue || typeof pIconValue !== 'string')
|
|
159
|
+
{
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
for (let i = 0; i < pIconValue.length; i++)
|
|
164
|
+
{
|
|
165
|
+
if (pIconValue.charCodeAt(i) > 255)
|
|
166
|
+
{
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Resolve the icon key to use for a given CardMetadata object.
|
|
176
|
+
* Tries Icon field first, then Code field, then falls back to 'default'.
|
|
177
|
+
*
|
|
178
|
+
* @param {Object} pCardMetadata - The CardMetadata object
|
|
179
|
+
* @returns {string} The icon key
|
|
180
|
+
*/
|
|
181
|
+
resolveIconKey(pCardMetadata)
|
|
182
|
+
{
|
|
183
|
+
if (!pCardMetadata)
|
|
184
|
+
{
|
|
185
|
+
return 'default';
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// If Icon is a known key, use it
|
|
189
|
+
if (pCardMetadata.Icon && this._Icons.hasOwnProperty(pCardMetadata.Icon))
|
|
190
|
+
{
|
|
191
|
+
return pCardMetadata.Icon;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// If Icon is a non-emoji string, check if it matches a registered template
|
|
195
|
+
if (pCardMetadata.Icon && !this.isEmojiIcon(pCardMetadata.Icon))
|
|
196
|
+
{
|
|
197
|
+
return pCardMetadata.Icon;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Fall back to Code field
|
|
201
|
+
if (pCardMetadata.Code && this._Icons.hasOwnProperty(pCardMetadata.Code))
|
|
202
|
+
{
|
|
203
|
+
return pCardMetadata.Code;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return 'default';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get the raw SVG markup string for a given icon key, with size applied.
|
|
211
|
+
*
|
|
212
|
+
* @param {string} pIconKey - The icon key
|
|
213
|
+
* @param {number} pSize - Pixel size (default 16)
|
|
214
|
+
* @returns {string} The SVG markup string
|
|
215
|
+
*/
|
|
216
|
+
getIconSVGMarkup(pIconKey, pSize)
|
|
217
|
+
{
|
|
218
|
+
let tmpSize = pSize || 16;
|
|
219
|
+
let tmpKey = pIconKey || 'default';
|
|
220
|
+
let tmpMarkup = this._Icons[tmpKey] || this._Icons['default'];
|
|
221
|
+
|
|
222
|
+
// Replace the size placeholder
|
|
223
|
+
return tmpMarkup.replace(/\{FlowIconSize\}/g, String(tmpSize));
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Render an icon into an SVG canvas context using createElementNS.
|
|
228
|
+
* Creates a <g> element containing the icon paths, positioned at (pX, pY)
|
|
229
|
+
* with the given size via a scale transform.
|
|
230
|
+
*
|
|
231
|
+
* @param {string} pIconKey - The icon key
|
|
232
|
+
* @param {SVGElement} pParentGroup - The parent SVG group to append to
|
|
233
|
+
* @param {number} pX - X position (top-left of icon bounding box)
|
|
234
|
+
* @param {number} pY - Y position (top-left of icon bounding box)
|
|
235
|
+
* @param {number} pSize - Pixel size (default 16)
|
|
236
|
+
* @returns {SVGGElement|null} The created group element, or null on failure
|
|
237
|
+
*/
|
|
238
|
+
renderIconIntoSVGGroup(pIconKey, pParentGroup, pX, pY, pSize)
|
|
239
|
+
{
|
|
240
|
+
if (!pParentGroup)
|
|
241
|
+
{
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
let tmpSize = pSize || 16;
|
|
246
|
+
let tmpScale = tmpSize / 24;
|
|
247
|
+
let tmpKey = pIconKey || 'default';
|
|
248
|
+
let tmpMarkup = this._Icons[tmpKey] || this._Icons['default'];
|
|
249
|
+
|
|
250
|
+
// Replace size placeholder (for consistency, though we scale via transform)
|
|
251
|
+
tmpMarkup = tmpMarkup.replace(/\{FlowIconSize\}/g, '24');
|
|
252
|
+
|
|
253
|
+
try
|
|
254
|
+
{
|
|
255
|
+
// Create a temporary SVG element to parse the icon markup
|
|
256
|
+
let tmpTempSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
257
|
+
tmpTempSVG.innerHTML = tmpMarkup;
|
|
258
|
+
|
|
259
|
+
// Find the inner SVG (the icon's root <svg> element)
|
|
260
|
+
let tmpInnerSVG = tmpTempSVG.querySelector('svg');
|
|
261
|
+
if (!tmpInnerSVG)
|
|
262
|
+
{
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Create a group to hold the icon content
|
|
267
|
+
let tmpGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
268
|
+
tmpGroup.setAttribute('transform', 'translate(' + pX + ',' + pY + ') scale(' + tmpScale + ')');
|
|
269
|
+
tmpGroup.setAttribute('pointer-events', 'none');
|
|
270
|
+
tmpGroup.setAttribute('class', 'pict-flow-icon-svg');
|
|
271
|
+
|
|
272
|
+
// Move all children from the parsed SVG into the group
|
|
273
|
+
while (tmpInnerSVG.childNodes.length > 0)
|
|
274
|
+
{
|
|
275
|
+
tmpGroup.appendChild(tmpInnerSVG.childNodes[0]);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
pParentGroup.appendChild(tmpGroup);
|
|
279
|
+
return tmpGroup;
|
|
280
|
+
}
|
|
281
|
+
catch (pError)
|
|
282
|
+
{
|
|
283
|
+
this.log.warn('PictProviderFlowIcons renderIconIntoSVGGroup error: ' + pError.message);
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Get all registered icon keys.
|
|
290
|
+
* @returns {Array<string>}
|
|
291
|
+
*/
|
|
292
|
+
getIconKeys()
|
|
293
|
+
{
|
|
294
|
+
return Object.keys(this._Icons);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Check if a given key has a registered icon.
|
|
299
|
+
* @param {string} pIconKey
|
|
300
|
+
* @returns {boolean}
|
|
301
|
+
*/
|
|
302
|
+
hasIcon(pIconKey)
|
|
303
|
+
{
|
|
304
|
+
return this._Icons.hasOwnProperty(pIconKey);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Register a new icon or override an existing one.
|
|
309
|
+
* @param {string} pIconKey - The icon key
|
|
310
|
+
* @param {string} pSVGMarkup - The SVG markup string (must contain {FlowIconSize} placeholders)
|
|
311
|
+
* @returns {boolean}
|
|
312
|
+
*/
|
|
313
|
+
registerIcon(pIconKey, pSVGMarkup)
|
|
314
|
+
{
|
|
315
|
+
if (!pIconKey || !pSVGMarkup)
|
|
316
|
+
{
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
this._Icons[pIconKey] = pSVGMarkup;
|
|
321
|
+
|
|
322
|
+
// Also update the pict template if TemplateProvider is available
|
|
323
|
+
if (this.fable && this.fable.TemplateProvider)
|
|
324
|
+
{
|
|
325
|
+
this.fable.TemplateProvider.addTemplate('Flow-Icon-' + pIconKey, pSVGMarkup);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
module.exports = PictProviderFlowIcons;
|
|
333
|
+
|
|
334
|
+
module.exports.default_configuration = _ProviderConfiguration;
|
|
335
|
+
module.exports.DefaultIcons = _DefaultIcons;
|
|
@@ -13,6 +13,22 @@ const _ProviderConfiguration =
|
|
|
13
13
|
* still exist are placed at their saved positions and any new nodes are
|
|
14
14
|
* auto-laid-out to the right.
|
|
15
15
|
*
|
|
16
|
+
* ## Persistence
|
|
17
|
+
*
|
|
18
|
+
* By default, layouts are persisted to the browser's `localStorage` using a
|
|
19
|
+
* key derived from the flow view identifier. This means layouts survive page
|
|
20
|
+
* refreshes out of the box.
|
|
21
|
+
*
|
|
22
|
+
* Developers can override the storage backend (e.g., to use a REST API or
|
|
23
|
+
* IndexedDB) by replacing the three storage hook methods on the instance or
|
|
24
|
+
* in a subclass:
|
|
25
|
+
*
|
|
26
|
+
* - `storageWrite(pLayouts, fCallback)` — persist the full layout array
|
|
27
|
+
* - `storageRead(fCallback)` — load the persisted layout array
|
|
28
|
+
* - `storageDelete(fCallback)` — remove all persisted layouts
|
|
29
|
+
*
|
|
30
|
+
* Each callback follows the Node convention: `fCallback(pError, pResult)`.
|
|
31
|
+
*
|
|
16
32
|
* Saved layout data structure:
|
|
17
33
|
* {
|
|
18
34
|
* Hash: "layout-<UUID>",
|
|
@@ -33,8 +49,174 @@ class PictProviderFlowLayouts extends libPictProvider
|
|
|
33
49
|
this.serviceType = 'PictProviderFlowLayouts';
|
|
34
50
|
|
|
35
51
|
this._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;
|
|
52
|
+
|
|
53
|
+
// Storage key for localStorage persistence.
|
|
54
|
+
// Defaults to a key derived from the FlowView identifier, or can be
|
|
55
|
+
// set via options.StorageKey. Pass `false` to disable localStorage.
|
|
56
|
+
if (pOptions && pOptions.StorageKey !== undefined)
|
|
57
|
+
{
|
|
58
|
+
this._StorageKey = pOptions.StorageKey;
|
|
59
|
+
}
|
|
60
|
+
else if (this._FlowView && this._FlowView.options && this._FlowView.options.ViewIdentifier)
|
|
61
|
+
{
|
|
62
|
+
this._StorageKey = `pict-flow-layouts-${this._FlowView.options.ViewIdentifier}`;
|
|
63
|
+
}
|
|
64
|
+
else
|
|
65
|
+
{
|
|
66
|
+
this._StorageKey = 'pict-flow-layouts';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ── Storage Hooks ─────────────────────────────────────────────────────
|
|
71
|
+
// These three methods form the persistence contract. The default
|
|
72
|
+
// implementation uses localStorage. Override them on the instance or
|
|
73
|
+
// subclass to use a REST API, IndexedDB, or any other backend.
|
|
74
|
+
//
|
|
75
|
+
// All callbacks follow `fCallback(pError, pResult)`.
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Persist the full array of layout objects.
|
|
79
|
+
*
|
|
80
|
+
* Default implementation writes JSON to `localStorage`.
|
|
81
|
+
*
|
|
82
|
+
* @param {Array} pLayouts - The array of layout objects to persist
|
|
83
|
+
* @param {Function} fCallback - `function(pError)` called when done
|
|
84
|
+
*/
|
|
85
|
+
storageWrite(pLayouts, fCallback)
|
|
86
|
+
{
|
|
87
|
+
if (this._StorageKey === false)
|
|
88
|
+
{
|
|
89
|
+
return fCallback(null);
|
|
90
|
+
}
|
|
91
|
+
try
|
|
92
|
+
{
|
|
93
|
+
if (typeof localStorage !== 'undefined')
|
|
94
|
+
{
|
|
95
|
+
localStorage.setItem(this._StorageKey, JSON.stringify(pLayouts));
|
|
96
|
+
}
|
|
97
|
+
return fCallback(null);
|
|
98
|
+
}
|
|
99
|
+
catch (pError)
|
|
100
|
+
{
|
|
101
|
+
this.log.warn(`PictProviderFlowLayouts storageWrite error: ${pError.message}`);
|
|
102
|
+
return fCallback(pError);
|
|
103
|
+
}
|
|
36
104
|
}
|
|
37
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Load the persisted array of layout objects.
|
|
108
|
+
*
|
|
109
|
+
* Default implementation reads JSON from `localStorage`.
|
|
110
|
+
*
|
|
111
|
+
* @param {Function} fCallback - `function(pError, pLayouts)` where
|
|
112
|
+
* pLayouts is an Array (or null/empty if nothing stored)
|
|
113
|
+
*/
|
|
114
|
+
storageRead(fCallback)
|
|
115
|
+
{
|
|
116
|
+
if (this._StorageKey === false)
|
|
117
|
+
{
|
|
118
|
+
return fCallback(null, []);
|
|
119
|
+
}
|
|
120
|
+
try
|
|
121
|
+
{
|
|
122
|
+
if (typeof localStorage !== 'undefined')
|
|
123
|
+
{
|
|
124
|
+
let tmpRaw = localStorage.getItem(this._StorageKey);
|
|
125
|
+
if (tmpRaw)
|
|
126
|
+
{
|
|
127
|
+
let tmpParsed = JSON.parse(tmpRaw);
|
|
128
|
+
if (Array.isArray(tmpParsed))
|
|
129
|
+
{
|
|
130
|
+
return fCallback(null, tmpParsed);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return fCallback(null, []);
|
|
135
|
+
}
|
|
136
|
+
catch (pError)
|
|
137
|
+
{
|
|
138
|
+
this.log.warn(`PictProviderFlowLayouts storageRead error: ${pError.message}`);
|
|
139
|
+
return fCallback(pError, []);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Remove all persisted layout data.
|
|
145
|
+
*
|
|
146
|
+
* Default implementation removes the key from `localStorage`.
|
|
147
|
+
*
|
|
148
|
+
* @param {Function} fCallback - `function(pError)` called when done
|
|
149
|
+
*/
|
|
150
|
+
storageDelete(fCallback)
|
|
151
|
+
{
|
|
152
|
+
if (this._StorageKey === false)
|
|
153
|
+
{
|
|
154
|
+
return fCallback(null);
|
|
155
|
+
}
|
|
156
|
+
try
|
|
157
|
+
{
|
|
158
|
+
if (typeof localStorage !== 'undefined')
|
|
159
|
+
{
|
|
160
|
+
localStorage.removeItem(this._StorageKey);
|
|
161
|
+
}
|
|
162
|
+
return fCallback(null);
|
|
163
|
+
}
|
|
164
|
+
catch (pError)
|
|
165
|
+
{
|
|
166
|
+
this.log.warn(`PictProviderFlowLayouts storageDelete error: ${pError.message}`);
|
|
167
|
+
return fCallback(pError);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// ── Initialization ────────────────────────────────────────────────────
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Load persisted layouts and merge them into _FlowData.SavedLayouts.
|
|
175
|
+
* Layouts already present in _FlowData (e.g., from setFlowData) are
|
|
176
|
+
* kept; persisted layouts with new hashes are appended.
|
|
177
|
+
*
|
|
178
|
+
* Call this after _FlowData is populated.
|
|
179
|
+
*/
|
|
180
|
+
loadPersistedLayouts()
|
|
181
|
+
{
|
|
182
|
+
this.storageRead((pError, pLayouts) =>
|
|
183
|
+
{
|
|
184
|
+
if (pError || !Array.isArray(pLayouts) || pLayouts.length === 0)
|
|
185
|
+
{
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (!this._FlowView || !this._FlowView._FlowData)
|
|
190
|
+
{
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
let tmpExisting = this._FlowView._FlowData.SavedLayouts;
|
|
195
|
+
let tmpExistingHashes = {};
|
|
196
|
+
for (let i = 0; i < tmpExisting.length; i++)
|
|
197
|
+
{
|
|
198
|
+
tmpExistingHashes[tmpExisting[i].Hash] = true;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
let tmpAdded = 0;
|
|
202
|
+
for (let i = 0; i < pLayouts.length; i++)
|
|
203
|
+
{
|
|
204
|
+
if (!tmpExistingHashes[pLayouts[i].Hash])
|
|
205
|
+
{
|
|
206
|
+
tmpExisting.push(pLayouts[i]);
|
|
207
|
+
tmpAdded++;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (tmpAdded > 0)
|
|
212
|
+
{
|
|
213
|
+
this.log.trace(`PictProviderFlowLayouts loaded ${tmpAdded} persisted layout(s)`);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ── Public API ────────────────────────────────────────────────────────
|
|
219
|
+
|
|
38
220
|
/**
|
|
39
221
|
* Save the current node and panel positions as a named layout.
|
|
40
222
|
* @param {string} pName - The display name for this layout
|
|
@@ -53,7 +235,7 @@ class PictProviderFlowLayouts extends libPictProvider
|
|
|
53
235
|
let tmpNodePositions = {};
|
|
54
236
|
let tmpPanelPositions = {};
|
|
55
237
|
|
|
56
|
-
// Capture node positions (
|
|
238
|
+
// Capture node positions and per-instance overrides (Title, Style)
|
|
57
239
|
for (let i = 0; i < tmpFlowData.Nodes.length; i++)
|
|
58
240
|
{
|
|
59
241
|
let tmpNode = tmpFlowData.Nodes[i];
|
|
@@ -62,8 +244,15 @@ class PictProviderFlowLayouts extends libPictProvider
|
|
|
62
244
|
X: tmpNode.X,
|
|
63
245
|
Y: tmpNode.Y,
|
|
64
246
|
Width: tmpNode.Width,
|
|
65
|
-
Height: tmpNode.Height
|
|
247
|
+
Height: tmpNode.Height,
|
|
248
|
+
Title: tmpNode.Title
|
|
66
249
|
};
|
|
250
|
+
|
|
251
|
+
// Only include Style if it has been customized
|
|
252
|
+
if (tmpNode.Style && Object.keys(tmpNode.Style).length > 0)
|
|
253
|
+
{
|
|
254
|
+
tmpNodePositions[tmpNode.Hash].Style = JSON.parse(JSON.stringify(tmpNode.Style));
|
|
255
|
+
}
|
|
67
256
|
}
|
|
68
257
|
|
|
69
258
|
// Capture panel positions keyed by NodeHash (panels get new hashes on each open)
|
|
@@ -97,6 +286,15 @@ class PictProviderFlowLayouts extends libPictProvider
|
|
|
97
286
|
tmpFlowData.SavedLayouts.push(tmpLayout);
|
|
98
287
|
this._FlowView.marshalFromView();
|
|
99
288
|
|
|
289
|
+
// Persist to storage
|
|
290
|
+
this.storageWrite(tmpFlowData.SavedLayouts, (pError) =>
|
|
291
|
+
{
|
|
292
|
+
if (pError)
|
|
293
|
+
{
|
|
294
|
+
this.log.warn(`PictProviderFlowLayouts: failed to persist after save: ${pError.message}`);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
100
298
|
if (this._FlowView._EventHandlerProvider)
|
|
101
299
|
{
|
|
102
300
|
this._FlowView._EventHandlerProvider.fireEvent('onLayoutSaved', tmpLayout);
|
|
@@ -150,6 +348,11 @@ class PictProviderFlowLayouts extends libPictProvider
|
|
|
150
348
|
tmpNode.Y = tmpSaved.Y;
|
|
151
349
|
if (typeof tmpSaved.Width === 'number') tmpNode.Width = tmpSaved.Width;
|
|
152
350
|
if (typeof tmpSaved.Height === 'number') tmpNode.Height = tmpSaved.Height;
|
|
351
|
+
if (typeof tmpSaved.Title === 'string') tmpNode.Title = tmpSaved.Title;
|
|
352
|
+
if (tmpSaved.Style && typeof tmpSaved.Style === 'object')
|
|
353
|
+
{
|
|
354
|
+
tmpNode.Style = JSON.parse(JSON.stringify(tmpSaved.Style));
|
|
355
|
+
}
|
|
153
356
|
tmpMatchedNodes.push(tmpNode);
|
|
154
357
|
}
|
|
155
358
|
else
|
|
@@ -244,6 +447,15 @@ class PictProviderFlowLayouts extends libPictProvider
|
|
|
244
447
|
let tmpRemovedLayout = tmpFlowData.SavedLayouts.splice(tmpIndex, 1)[0];
|
|
245
448
|
this._FlowView.marshalFromView();
|
|
246
449
|
|
|
450
|
+
// Persist to storage (with the layout removed)
|
|
451
|
+
this.storageWrite(tmpFlowData.SavedLayouts, (pError) =>
|
|
452
|
+
{
|
|
453
|
+
if (pError)
|
|
454
|
+
{
|
|
455
|
+
this.log.warn(`PictProviderFlowLayouts: failed to persist after delete: ${pError.message}`);
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
|
|
247
459
|
if (this._FlowView._EventHandlerProvider)
|
|
248
460
|
{
|
|
249
461
|
this._FlowView._EventHandlerProvider.fireEvent('onLayoutDeleted', tmpRemovedLayout);
|
|
@@ -30,9 +30,7 @@ const _DefaultNodeTypes =
|
|
|
30
30
|
BodyStyle:
|
|
31
31
|
{
|
|
32
32
|
'fill': '#eafaf1',
|
|
33
|
-
'stroke': '#27ae60'
|
|
34
|
-
'rx': '25',
|
|
35
|
-
'ry': '25'
|
|
33
|
+
'stroke': '#27ae60'
|
|
36
34
|
}
|
|
37
35
|
},
|
|
38
36
|
'end':
|
|
@@ -45,13 +43,28 @@ const _DefaultNodeTypes =
|
|
|
45
43
|
[
|
|
46
44
|
{ Hash: null, Direction: 'input', Side: 'left', Label: 'In' }
|
|
47
45
|
],
|
|
46
|
+
TitleBarColor: '#1abc9c',
|
|
47
|
+
BodyStyle:
|
|
48
|
+
{
|
|
49
|
+
'fill': '#e8f8f5',
|
|
50
|
+
'stroke': '#1abc9c'
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
'halt':
|
|
54
|
+
{
|
|
55
|
+
Hash: 'halt',
|
|
56
|
+
Label: 'Halt',
|
|
57
|
+
DefaultWidth: 140,
|
|
58
|
+
DefaultHeight: 80,
|
|
59
|
+
DefaultPorts:
|
|
60
|
+
[
|
|
61
|
+
{ Hash: null, Direction: 'input', Side: 'left', Label: 'In' }
|
|
62
|
+
],
|
|
48
63
|
TitleBarColor: '#e74c3c',
|
|
49
64
|
BodyStyle:
|
|
50
65
|
{
|
|
51
66
|
'fill': '#fdedec',
|
|
52
|
-
'stroke': '#e74c3c'
|
|
53
|
-
'rx': '25',
|
|
54
|
-
'ry': '25'
|
|
67
|
+
'stroke': '#e74c3c'
|
|
55
68
|
}
|
|
56
69
|
},
|
|
57
70
|
'decision':
|
|
@@ -100,11 +113,21 @@ class PictProviderFlowNodeTypes extends libPictProvider
|
|
|
100
113
|
let tmpAdditionalKeys = Object.keys(pOptions.AdditionalNodeTypes);
|
|
101
114
|
for (let i = 0; i < tmpAdditionalKeys.length; i++)
|
|
102
115
|
{
|
|
116
|
+
let tmpOriginal = pOptions.AdditionalNodeTypes[tmpAdditionalKeys[i]];
|
|
103
117
|
this._NodeTypes[tmpAdditionalKeys[i]] = Object.assign(
|
|
104
118
|
{},
|
|
105
119
|
this._NodeTypes[tmpAdditionalKeys[i]] || {},
|
|
106
|
-
JSON.parse(JSON.stringify(
|
|
120
|
+
JSON.parse(JSON.stringify(tmpOriginal))
|
|
107
121
|
);
|
|
122
|
+
// Preserve BodyContent.RenderCallback (functions are stripped by JSON serialization)
|
|
123
|
+
if (tmpOriginal.BodyContent && typeof tmpOriginal.BodyContent.RenderCallback === 'function')
|
|
124
|
+
{
|
|
125
|
+
if (!this._NodeTypes[tmpAdditionalKeys[i]].BodyContent)
|
|
126
|
+
{
|
|
127
|
+
this._NodeTypes[tmpAdditionalKeys[i]].BodyContent = {};
|
|
128
|
+
}
|
|
129
|
+
this._NodeTypes[tmpAdditionalKeys[i]].BodyContent.RenderCallback = tmpOriginal.BodyContent.RenderCallback;
|
|
130
|
+
}
|
|
108
131
|
}
|
|
109
132
|
}
|
|
110
133
|
}
|