pict-section-flow 1.4.0 → 2.0.1
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/package.json +7 -2
- package/source/Pict-Section-Flow.js +20 -14
- package/source/providers/PictProvider-Flow-Background.js +303 -0
- package/source/providers/PictProvider-Flow-CSS.js +73 -7
- package/source/providers/PictProvider-Flow-Geometry.js +11 -421
- package/source/providers/PictProvider-Flow-Icons.js +12 -0
- package/source/providers/PictProvider-Flow-Layouts.js +107 -0
- package/source/services/PictService-Flow-ConnectionRenderer.js +1 -1
- package/source/services/PictService-Flow-CursorManager.js +113 -0
- package/source/services/PictService-Flow-InteractionManager.js +439 -59
- package/source/services/PictService-Flow-Layout.js +21 -16
- package/source/services/PictService-Flow-PathGenerator.js +30 -417
- package/source/services/PictService-Flow-RenderManager.js +23 -3
- package/source/services/PictService-Flow-ViewportManager.js +102 -0
- package/source/views/PictView-Flow-FloatingToolbar.js +5 -1
- package/source/views/PictView-Flow-Node.js +29 -0
- package/source/views/PictView-Flow-Toolbar.js +50 -3
- package/source/views/PictView-Flow.js +591 -2
- package/.claude/launch.json +0 -11
- package/docs/.nojekyll +0 -0
- package/docs/Architecture.md +0 -163
- package/docs/Custom-Styling.md +0 -275
- package/docs/Data_Model.md +0 -149
- package/docs/Event_System.md +0 -156
- package/docs/Getting_Started.md +0 -237
- package/docs/Implementation_Reference.md +0 -528
- package/docs/Layout_Persistence.md +0 -117
- package/docs/README.md +0 -103
- package/docs/Theme_Integration.md +0 -150
- package/docs/_brand.json +0 -18
- package/docs/_cover.md +0 -17
- package/docs/_playground.json +0 -24
- package/docs/_sidebar.md +0 -57
- package/docs/_topbar.md +0 -8
- package/docs/_version.json +0 -7
- package/docs/api/PictFlowCard.md +0 -216
- package/docs/api/PictFlowCardPropertiesPanel.md +0 -235
- package/docs/api/addConnection.md +0 -101
- package/docs/api/addNode.md +0 -137
- package/docs/api/autoLayout.md +0 -77
- package/docs/api/getFlowData.md +0 -112
- package/docs/api/marshalToView.md +0 -95
- package/docs/api/openPanel.md +0 -128
- package/docs/api/registerHandler.md +0 -174
- package/docs/api/registerNodeType.md +0 -142
- package/docs/api/removeConnection.md +0 -57
- package/docs/api/removeNode.md +0 -80
- package/docs/api/saveLayout.md +0 -152
- package/docs/api/screenToSVGCoords.md +0 -68
- package/docs/api/selectNode.md +0 -116
- package/docs/api/setTheme.md +0 -168
- package/docs/api/setZoom.md +0 -97
- package/docs/api/toggleFullscreen.md +0 -68
- package/docs/card-help/EACH.md +0 -19
- package/docs/card-help/FREAD.md +0 -24
- package/docs/card-help/FWRITE.md +0 -24
- package/docs/card-help/GET.md +0 -22
- package/docs/card-help/ITE.md +0 -23
- package/docs/card-help/LOG.md +0 -23
- package/docs/card-help/NOTE.md +0 -17
- package/docs/card-help/PREV.md +0 -18
- package/docs/card-help/SET.md +0 -27
- package/docs/card-help/SPKL.md +0 -22
- package/docs/card-help/STAT.md +0 -23
- package/docs/card-help/SW.md +0 -25
- package/docs/diagrams/architecture-at-a-glance.excalidraw +0 -4270
- package/docs/diagrams/architecture-at-a-glance.mmd +0 -30
- package/docs/diagrams/architecture-at-a-glance.svg +0 -2
- package/docs/diagrams/data-flow.excalidraw +0 -1451
- package/docs/diagrams/data-flow.mmd +0 -17
- package/docs/diagrams/data-flow.svg +0 -2
- package/docs/diagrams/high-level-design.excalidraw +0 -5767
- package/docs/diagrams/high-level-design.mmd +0 -86
- package/docs/diagrams/high-level-design.svg +0 -2
- package/docs/diagrams/relationships.excalidraw +0 -3852
- package/docs/diagrams/relationships.mmd +0 -9
- package/docs/diagrams/relationships.svg +0 -2
- package/docs/diagrams/service-initialization-sequence.excalidraw +0 -1466
- package/docs/diagrams/service-initialization-sequence.mmd +0 -19
- package/docs/diagrams/service-initialization-sequence.svg +0 -2
- package/docs/diagrams/svg-layer-structure.excalidraw +0 -1060
- package/docs/diagrams/svg-layer-structure.mmd +0 -18
- package/docs/diagrams/svg-layer-structure.svg +0 -2
- package/docs/examples/README.md +0 -9
- package/docs/examples/simple_cards/README.md +0 -677
- package/docs/examples/simple_cards/css/flowexample.css +0 -65
- package/docs/examples/simple_cards/index.html +0 -32
- package/docs/examples/simple_cards/js/pict.min.js +0 -12
- package/docs/examples/simple_cards/pict-section-flow-example-simple-cards.compatible.min.js +0 -1
- package/docs/index.html +0 -38
- package/docs/playground/app.json +0 -6
- package/docs/playground/appdata.json +0 -85
- package/docs/playground/application.js +0 -23
- package/docs/playground/pict.json +0 -17
- package/docs/playground/runtime/pict-application.min.js +0 -2
- package/docs/playground/runtime/pict-section-flow.min.js +0 -2
- package/docs/playground/runtime/pict-section-modal.min.js +0 -2
- package/docs/playground/runtime/pict.min.js +0 -12
- package/docs/retold-catalog.json +0 -244
- package/docs/retold-keyword-index.json +0 -26028
- package/example_applications/simple_cards/css/flowexample.css +0 -65
- package/example_applications/simple_cards/html/index.html +0 -32
- package/example_applications/simple_cards/package.json +0 -52
- package/example_applications/simple_cards/source/Pict-Application-FlowExample-Configuration.json +0 -15
- package/example_applications/simple_cards/source/Pict-Application-FlowExample.js +0 -539
- package/example_applications/simple_cards/source/card-help-content.js +0 -16
- package/example_applications/simple_cards/source/cards/FlowCard-Comment.js +0 -38
- package/example_applications/simple_cards/source/cards/FlowCard-DataPreview.js +0 -44
- package/example_applications/simple_cards/source/cards/FlowCard-Each.js +0 -38
- package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +0 -56
- package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +0 -50
- package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +0 -37
- package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +0 -49
- package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +0 -55
- package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +0 -97
- package/example_applications/simple_cards/source/cards/FlowCard-Sparkline.js +0 -100
- package/example_applications/simple_cards/source/cards/FlowCard-StatusMonitor.js +0 -46
- package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +0 -39
- package/example_applications/simple_cards/source/providers/PictRouter-FlowExample-Configuration.json +0 -22
- package/example_applications/simple_cards/source/sample-flows.js +0 -410
- package/example_applications/simple_cards/source/views/PictView-FlowExample-About.js +0 -184
- package/example_applications/simple_cards/source/views/PictView-FlowExample-BottomBar.js +0 -77
- package/example_applications/simple_cards/source/views/PictView-FlowExample-Documentation.js +0 -325
- package/example_applications/simple_cards/source/views/PictView-FlowExample-FileWriteInfo.js +0 -59
- package/example_applications/simple_cards/source/views/PictView-FlowExample-Layout.js +0 -90
- package/example_applications/simple_cards/source/views/PictView-FlowExample-MainWorkspace.js +0 -453
- package/example_applications/simple_cards/source/views/PictView-FlowExample-TopBar.js +0 -95
- package/scripts/generate-card-help.js +0 -214
- package/source/providers/edges/Edge-Bezier.js +0 -41
- package/source/providers/edges/Edge-Orthogonal.js +0 -37
- package/source/providers/edges/Edge-OrthogonalSnap.js +0 -72
- package/source/providers/edges/Edge-Perimeter-Linear.js +0 -31
- package/source/providers/edges/Edge-Perimeter-Orthogonal.js +0 -39
- package/source/providers/edges/Edge-Perimeter.js +0 -48
- package/source/providers/edges/Edge-PerimeterMath.js +0 -92
- package/source/providers/edges/Edge-Straight.js +0 -24
- package/source/providers/layouts/Layout-Circular.js +0 -203
- package/source/providers/layouts/Layout-Coerce.js +0 -40
- package/source/providers/layouts/Layout-Columnar.js +0 -134
- package/source/providers/layouts/Layout-Custom.js +0 -27
- package/source/providers/layouts/Layout-ForcedFromCenter.js +0 -256
- package/source/providers/layouts/Layout-Grid.js +0 -134
- package/source/providers/layouts/Layout-Layered.js +0 -155
- package/source/providers/layouts/Layout-Rank.js +0 -141
- package/source/providers/layouts/Layout-Staggered.js +0 -131
- package/source/providers/layouts/Layout-Tabular.js +0 -94
- package/test/CardPalette_tests.js +0 -43
- package/test/ConnectionHandleManager_tests.js +0 -717
- package/test/ConnectionRenderer_tests.js +0 -591
- package/test/ConnectionStyle_tests.js +0 -90
- package/test/DataManager_tests.js +0 -859
- package/test/Geometry_tests.js +0 -767
- package/test/InteractionManager_tests.js +0 -279
- package/test/Layout_tests.js +0 -1604
- package/test/NodeView_tests.js +0 -66
- package/test/PanelManager_tests.js +0 -172
- package/test/PathGenerator_tests.js +0 -978
- package/test/PortRenderer_tests.js +0 -376
- package/test/RenderManager_tests.js +0 -756
- package/test/Renderer_tests.js +0 -133
- package/test/SelectionManager_tests.js +0 -185
- package/test/StylePresets_tests.js +0 -153
- package/test/ToolbarExtraButtons_tests.js +0 -138
- package/test/UndirectedConnections_tests.js +0 -70
|
@@ -1,23 +1,20 @@
|
|
|
1
|
-
const
|
|
1
|
+
const libPictProviderGraphGeometry = require('pict-provider-graphgeometry');
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* PictProvider-Flow-Geometry
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* Backwards-compatible shim. As of the flow 2.0 plan (Phase 1a) the geometry
|
|
7
|
+
* math lives in the standalone, dependency-free pict-provider-graphgeometry
|
|
8
|
+
* module so it can be reused and unit tested on its own. This subclass keeps the
|
|
9
|
+
* historical `PictProviderFlowGeometry` export and serviceType so the flow
|
|
10
|
+
* service registry, every caller (PortRenderer, Node view, ConnectionRenderer,
|
|
11
|
+
* Tether), and the existing Geometry_tests.js are unaffected. All methods are
|
|
12
|
+
* inherited and delegate to the GraphGeometry core.
|
|
9
13
|
*
|
|
10
|
-
* Port Side values (12 positions)
|
|
11
|
-
*
|
|
12
|
-
* Top edge: 'top-left' 'top' 'top-right'
|
|
13
|
-
* Left edge: 'left-top' 'left' 'left-bottom'
|
|
14
|
-
* Right edge: 'right-top' 'right' 'right-bottom'
|
|
15
|
-
* Bottom edge: 'bottom-left' 'bottom' 'bottom-right'
|
|
16
|
-
*
|
|
17
|
-
* The old 4-value sides ('left', 'right', 'top', 'bottom') map to
|
|
18
|
-
* the middle position on each edge for backward compatibility.
|
|
14
|
+
* Port Side values (12 positions) and the full method set are documented in
|
|
15
|
+
* pict-provider-graphgeometry/source/GraphGeometry-Core.js.
|
|
19
16
|
*/
|
|
20
|
-
class PictProviderFlowGeometry extends
|
|
17
|
+
class PictProviderFlowGeometry extends libPictProviderGraphGeometry
|
|
21
18
|
{
|
|
22
19
|
constructor(pFable, pOptions, pServiceHash)
|
|
23
20
|
{
|
|
@@ -25,413 +22,6 @@ class PictProviderFlowGeometry extends libFableServiceProviderBase
|
|
|
25
22
|
|
|
26
23
|
this.serviceType = 'PictProviderFlowGeometry';
|
|
27
24
|
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Extract the edge name from a Side value.
|
|
31
|
-
*
|
|
32
|
-
* Maps all 12 positions (and the 4 legacy values) back to
|
|
33
|
-
* the edge they sit on: 'left', 'right', 'top', or 'bottom'.
|
|
34
|
-
*
|
|
35
|
-
* @param {string} pSide - Any valid Side value
|
|
36
|
-
* @returns {string} The edge: 'left', 'right', 'top', or 'bottom'
|
|
37
|
-
*/
|
|
38
|
-
getEdgeFromSide(pSide)
|
|
39
|
-
{
|
|
40
|
-
switch (pSide)
|
|
41
|
-
{
|
|
42
|
-
case 'left-top':
|
|
43
|
-
case 'left':
|
|
44
|
-
case 'left-bottom':
|
|
45
|
-
return 'left';
|
|
46
|
-
|
|
47
|
-
case 'right-top':
|
|
48
|
-
case 'right':
|
|
49
|
-
case 'right-bottom':
|
|
50
|
-
return 'right';
|
|
51
|
-
|
|
52
|
-
case 'top-left':
|
|
53
|
-
case 'top':
|
|
54
|
-
case 'top-right':
|
|
55
|
-
return 'top';
|
|
56
|
-
|
|
57
|
-
case 'bottom-left':
|
|
58
|
-
case 'bottom':
|
|
59
|
-
case 'bottom-right':
|
|
60
|
-
return 'bottom';
|
|
61
|
-
|
|
62
|
-
default:
|
|
63
|
-
return 'right';
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Get the outward unit direction vector for a given side.
|
|
69
|
-
*
|
|
70
|
-
* All positions on the same edge share the same direction vector.
|
|
71
|
-
*
|
|
72
|
-
* @param {string} pSide - Any valid Side value (12 positions or 4 legacy)
|
|
73
|
-
* @returns {{dx: number, dy: number}}
|
|
74
|
-
*/
|
|
75
|
-
sideDirection(pSide)
|
|
76
|
-
{
|
|
77
|
-
switch (this.getEdgeFromSide(pSide))
|
|
78
|
-
{
|
|
79
|
-
case 'left': return { dx: -1, dy: 0 };
|
|
80
|
-
case 'right': return { dx: 1, dy: 0 };
|
|
81
|
-
case 'top': return { dx: 0, dy: -1 };
|
|
82
|
-
case 'bottom': return { dx: 0, dy: 1 };
|
|
83
|
-
default: return { dx: 1, dy: 0 };
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Get the center point of a rectangle's edge.
|
|
89
|
-
* Works for any object with X, Y, Width, Height properties
|
|
90
|
-
* (nodes, panels, or any rectangular element).
|
|
91
|
-
*
|
|
92
|
-
* @param {Object} pRectData - Object with X, Y, Width, Height
|
|
93
|
-
* @param {string} pSide - 'left', 'right', 'top', 'bottom'
|
|
94
|
-
* @returns {{x: number, y: number}}
|
|
95
|
-
*/
|
|
96
|
-
getEdgeCenter(pRectData, pSide)
|
|
97
|
-
{
|
|
98
|
-
switch (pSide)
|
|
99
|
-
{
|
|
100
|
-
case 'left':
|
|
101
|
-
return { x: pRectData.X, y: pRectData.Y + pRectData.Height / 2 };
|
|
102
|
-
case 'right':
|
|
103
|
-
return { x: pRectData.X + pRectData.Width, y: pRectData.Y + pRectData.Height / 2 };
|
|
104
|
-
case 'top':
|
|
105
|
-
return { x: pRectData.X + pRectData.Width / 2, y: pRectData.Y };
|
|
106
|
-
case 'bottom':
|
|
107
|
-
return { x: pRectData.X + pRectData.Width / 2, y: pRectData.Y + pRectData.Height };
|
|
108
|
-
default:
|
|
109
|
-
return { x: pRectData.X + pRectData.Width, y: pRectData.Y + pRectData.Height / 2 };
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Calculate a port's local position relative to node origin.
|
|
115
|
-
*
|
|
116
|
-
* Supports 12 positions (3 zones per edge). For left/right edges,
|
|
117
|
-
* the body area below the title bar is divided into vertical zones.
|
|
118
|
-
* For top/bottom edges, the full width is divided into horizontal zones.
|
|
119
|
-
*
|
|
120
|
-
* When pPortCountsBySide is provided, zone fractions are computed
|
|
121
|
-
* proportionally based on actual port counts (adaptive zones).
|
|
122
|
-
* Otherwise, fixed 1/3 zones are used for backward compatibility.
|
|
123
|
-
*
|
|
124
|
-
* Multiple ports sharing the same Side value distribute evenly within
|
|
125
|
-
* their zone.
|
|
126
|
-
*
|
|
127
|
-
* @param {string} pSide - Side value (any of 12 positions or 4 legacy)
|
|
128
|
-
* @param {number} pIndex - Index of this port within its Side group
|
|
129
|
-
* @param {number} pTotal - Total ports with this Side value
|
|
130
|
-
* @param {number} pWidth - Node width
|
|
131
|
-
* @param {number} pHeight - Node height
|
|
132
|
-
* @param {number} pTitleBarHeight - Height of the node title bar
|
|
133
|
-
* @param {Object} [pPortCountsBySide] - Optional map of Side → port count
|
|
134
|
-
* for all ports on the node. Enables adaptive zone sizing.
|
|
135
|
-
* @returns {{x: number, y: number}}
|
|
136
|
-
*/
|
|
137
|
-
getPortLocalPosition(pSide, pIndex, pTotal, pWidth, pHeight, pTitleBarHeight, pPortCountsBySide)
|
|
138
|
-
{
|
|
139
|
-
let tmpEdge = this.getEdgeFromSide(pSide);
|
|
140
|
-
let tmpZone = pPortCountsBySide
|
|
141
|
-
? this._computeAdaptiveZone(pSide, pPortCountsBySide)
|
|
142
|
-
: this._getZoneFromSide(pSide);
|
|
143
|
-
|
|
144
|
-
// Use the fixed zone to decide alignment intent (start/center/end)
|
|
145
|
-
// because adaptive zones shift boundaries when neighbouring zones
|
|
146
|
-
// are empty, which would break alignment decisions.
|
|
147
|
-
let tmpFixedZone = this._getZoneFromSide(pSide);
|
|
148
|
-
|
|
149
|
-
// Minimum spacing between port centers (px)
|
|
150
|
-
let tmpMinSpacing = 16;
|
|
151
|
-
|
|
152
|
-
// Reserve space at the bottom of the body so that port badges
|
|
153
|
-
// never overlap the panel-indicator icon (10×10 rect at bottom-right)
|
|
154
|
-
// and always leave a visible gap above the node bottom edge.
|
|
155
|
-
let tmpBottomPad = 16;
|
|
156
|
-
|
|
157
|
-
// Determine alignment from the fixed zone position:
|
|
158
|
-
// start zone (0.000 – 0.333) → start-align (offset 0)
|
|
159
|
-
// middle zone (0.333 – 0.667) → center
|
|
160
|
-
// end zone (0.667 – 1.000) → end-align
|
|
161
|
-
let tmpAlignment = 'start';
|
|
162
|
-
if (tmpFixedZone.start >= 0.5)
|
|
163
|
-
{
|
|
164
|
-
tmpAlignment = 'end';
|
|
165
|
-
}
|
|
166
|
-
else if (tmpFixedZone.start >= 0.17)
|
|
167
|
-
{
|
|
168
|
-
tmpAlignment = 'center';
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (tmpEdge === 'left' || tmpEdge === 'right')
|
|
172
|
-
{
|
|
173
|
-
let tmpX = (tmpEdge === 'left') ? 0 : pWidth;
|
|
174
|
-
let tmpBodyHeight = pHeight - pTitleBarHeight - tmpBottomPad;
|
|
175
|
-
let tmpZoneStart = pTitleBarHeight + tmpBodyHeight * tmpZone.start;
|
|
176
|
-
let tmpZoneHeight = tmpBodyHeight * (tmpZone.end - tmpZone.start);
|
|
177
|
-
|
|
178
|
-
// Use fixed spacing so port gaps stay consistent across cards
|
|
179
|
-
// even when one edge drives the card height beyond what the
|
|
180
|
-
// other needs.
|
|
181
|
-
let tmpSpacing = tmpMinSpacing;
|
|
182
|
-
let tmpGroupHeight = tmpSpacing * (pTotal + 1);
|
|
183
|
-
let tmpSlack = tmpZoneHeight - tmpGroupHeight;
|
|
184
|
-
if (tmpSlack < 0)
|
|
185
|
-
{
|
|
186
|
-
tmpSlack = 0;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
let tmpAlignOffset = 0;
|
|
190
|
-
if (tmpAlignment === 'end')
|
|
191
|
-
{
|
|
192
|
-
tmpAlignOffset = tmpSlack;
|
|
193
|
-
}
|
|
194
|
-
else if (tmpAlignment === 'center')
|
|
195
|
-
{
|
|
196
|
-
tmpAlignOffset = tmpSlack / 2;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
let tmpY = tmpZoneStart + tmpAlignOffset + tmpSpacing * (pIndex + 1);
|
|
200
|
-
return { x: tmpX, y: tmpY };
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// top or bottom
|
|
204
|
-
let tmpY = (tmpEdge === 'top') ? 0 : pHeight;
|
|
205
|
-
let tmpZoneStart = pWidth * tmpZone.start;
|
|
206
|
-
let tmpZoneWidth = pWidth * (tmpZone.end - tmpZone.start);
|
|
207
|
-
|
|
208
|
-
let tmpSpacing = tmpMinSpacing;
|
|
209
|
-
let tmpGroupWidth = tmpSpacing * (pTotal + 1);
|
|
210
|
-
let tmpSlack = tmpZoneWidth - tmpGroupWidth;
|
|
211
|
-
if (tmpSlack < 0)
|
|
212
|
-
{
|
|
213
|
-
tmpSlack = 0;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
let tmpAlignOffset = 0;
|
|
217
|
-
if (tmpAlignment === 'end')
|
|
218
|
-
{
|
|
219
|
-
tmpAlignOffset = tmpSlack;
|
|
220
|
-
}
|
|
221
|
-
else if (tmpAlignment === 'center')
|
|
222
|
-
{
|
|
223
|
-
tmpAlignOffset = tmpSlack / 2;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
let tmpX = tmpZoneStart + tmpAlignOffset + tmpSpacing * (pIndex + 1);
|
|
227
|
-
return { x: tmpX, y: tmpY };
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Get the zone fraction (start, end) for a Side value.
|
|
232
|
-
*
|
|
233
|
-
* Each edge is divided into three zones of equal size:
|
|
234
|
-
* start: 0.0 — 0.333
|
|
235
|
-
* middle: 0.333 — 0.667
|
|
236
|
-
* end: 0.667 — 1.0
|
|
237
|
-
*
|
|
238
|
-
* Used as fallback when adaptive zones are not available.
|
|
239
|
-
*
|
|
240
|
-
* @param {string} pSide
|
|
241
|
-
* @returns {{start: number, end: number}}
|
|
242
|
-
*/
|
|
243
|
-
_getZoneFromSide(pSide)
|
|
244
|
-
{
|
|
245
|
-
switch (pSide)
|
|
246
|
-
{
|
|
247
|
-
// Left edge: top, middle, bottom
|
|
248
|
-
case 'left-top': return { start: 0.0, end: 0.333 };
|
|
249
|
-
case 'left': return { start: 0.333, end: 0.667 };
|
|
250
|
-
case 'left-bottom': return { start: 0.667, end: 1.0 };
|
|
251
|
-
|
|
252
|
-
// Right edge: top, middle, bottom
|
|
253
|
-
case 'right-top': return { start: 0.0, end: 0.333 };
|
|
254
|
-
case 'right': return { start: 0.333, end: 0.667 };
|
|
255
|
-
case 'right-bottom': return { start: 0.667, end: 1.0 };
|
|
256
|
-
|
|
257
|
-
// Top edge: left, middle, right
|
|
258
|
-
case 'top-left': return { start: 0.0, end: 0.333 };
|
|
259
|
-
case 'top': return { start: 0.333, end: 0.667 };
|
|
260
|
-
case 'top-right': return { start: 0.667, end: 1.0 };
|
|
261
|
-
|
|
262
|
-
// Bottom edge: left, middle, right
|
|
263
|
-
case 'bottom-left': return { start: 0.0, end: 0.333 };
|
|
264
|
-
case 'bottom': return { start: 0.333, end: 0.667 };
|
|
265
|
-
case 'bottom-right': return { start: 0.667, end: 1.0 };
|
|
266
|
-
|
|
267
|
-
// Fallback: full range (legacy behavior)
|
|
268
|
-
default: return { start: 0.0, end: 1.0 };
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
/**
|
|
273
|
-
* Get the three zone Side keys for a given edge, in order.
|
|
274
|
-
*
|
|
275
|
-
* @param {string} pEdge - 'left', 'right', 'top', or 'bottom'
|
|
276
|
-
* @returns {Array<string>} Three Side keys in start-to-end order
|
|
277
|
-
*/
|
|
278
|
-
_getZoneKeysForEdge(pEdge)
|
|
279
|
-
{
|
|
280
|
-
switch (pEdge)
|
|
281
|
-
{
|
|
282
|
-
case 'left': return ['left-top', 'left', 'left-bottom'];
|
|
283
|
-
case 'right': return ['right-top', 'right', 'right-bottom'];
|
|
284
|
-
case 'top': return ['top-left', 'top', 'top-right'];
|
|
285
|
-
case 'bottom': return ['bottom-left', 'bottom', 'bottom-right'];
|
|
286
|
-
default: return ['right-top', 'right', 'right-bottom'];
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Compute an adaptive zone fraction for a Side value based on the
|
|
292
|
-
* actual port distribution across all zones on the same edge.
|
|
293
|
-
*
|
|
294
|
-
* Instead of fixed 1/3 splits, zones are sized proportionally to the
|
|
295
|
-
* space each zone needs (minSpacing * (portCount + 1)). Zones with
|
|
296
|
-
* zero ports collapse to zero, giving occupied zones more room.
|
|
297
|
-
*
|
|
298
|
-
* @param {string} pSide - The Side value to compute a zone for
|
|
299
|
-
* @param {Object} pPortCountsBySide - Map of Side → number of ports
|
|
300
|
-
* @returns {{start: number, end: number}}
|
|
301
|
-
*/
|
|
302
|
-
_computeAdaptiveZone(pSide, pPortCountsBySide)
|
|
303
|
-
{
|
|
304
|
-
let tmpEdge = this.getEdgeFromSide(pSide);
|
|
305
|
-
let tmpZoneKeys = this._getZoneKeysForEdge(tmpEdge);
|
|
306
|
-
|
|
307
|
-
let tmpMinSpacing = 16;
|
|
308
|
-
|
|
309
|
-
// Compute the space each zone needs: minSpacing * (count + 1)
|
|
310
|
-
// The +1 provides padding at both ends of the zone.
|
|
311
|
-
let tmpTotalSpace = 0;
|
|
312
|
-
let tmpSpaceByZone = {};
|
|
313
|
-
for (let i = 0; i < tmpZoneKeys.length; i++)
|
|
314
|
-
{
|
|
315
|
-
let tmpKey = tmpZoneKeys[i];
|
|
316
|
-
let tmpCount = pPortCountsBySide[tmpKey] || 0;
|
|
317
|
-
let tmpSpace = (tmpCount > 0) ? (tmpMinSpacing * (tmpCount + 1)) : 0;
|
|
318
|
-
tmpSpaceByZone[tmpKey] = tmpSpace;
|
|
319
|
-
tmpTotalSpace += tmpSpace;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// If no ports on this edge at all, fall back to fixed zones
|
|
323
|
-
if (tmpTotalSpace === 0)
|
|
324
|
-
{
|
|
325
|
-
return this._getZoneFromSide(pSide);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Compute proportional start/end for the requested zone
|
|
329
|
-
let tmpCumulativeStart = 0;
|
|
330
|
-
for (let i = 0; i < tmpZoneKeys.length; i++)
|
|
331
|
-
{
|
|
332
|
-
let tmpKey = tmpZoneKeys[i];
|
|
333
|
-
let tmpFraction = tmpSpaceByZone[tmpKey] / tmpTotalSpace;
|
|
334
|
-
if (tmpKey === pSide)
|
|
335
|
-
{
|
|
336
|
-
return { start: tmpCumulativeStart, end: tmpCumulativeStart + tmpFraction };
|
|
337
|
-
}
|
|
338
|
-
tmpCumulativeStart += tmpFraction;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// Should not reach here; fall back to fixed zones
|
|
342
|
-
return this._getZoneFromSide(pSide);
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Build a map of Side → port count from an array of port objects.
|
|
347
|
-
*
|
|
348
|
-
* Convenience method for callers that need to pass port counts
|
|
349
|
-
* to getPortLocalPosition or computeMinimumNodeHeight.
|
|
350
|
-
*
|
|
351
|
-
* @param {Array} pPorts - Array of port objects with Side, Direction
|
|
352
|
-
* @returns {Object} Map of Side value → count
|
|
353
|
-
*/
|
|
354
|
-
buildPortCountsBySide(pPorts)
|
|
355
|
-
{
|
|
356
|
-
let tmpCounts = {};
|
|
357
|
-
if (!pPorts || !Array.isArray(pPorts))
|
|
358
|
-
{
|
|
359
|
-
return tmpCounts;
|
|
360
|
-
}
|
|
361
|
-
for (let i = 0; i < pPorts.length; i++)
|
|
362
|
-
{
|
|
363
|
-
let tmpSide = pPorts[i].Side || (pPorts[i].Direction === 'input' ? 'left' : 'right');
|
|
364
|
-
if (!tmpCounts[tmpSide])
|
|
365
|
-
{
|
|
366
|
-
tmpCounts[tmpSide] = 0;
|
|
367
|
-
}
|
|
368
|
-
tmpCounts[tmpSide]++;
|
|
369
|
-
}
|
|
370
|
-
return tmpCounts;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
* Compute the minimum node height required so that all ports
|
|
375
|
-
* (with their badges) fit within the node boundary.
|
|
376
|
-
*
|
|
377
|
-
* Uses adaptive zone sizing: instead of assuming each zone gets
|
|
378
|
-
* a fixed 1/3 of the body, sums the space needed by all occupied
|
|
379
|
-
* zones on each left/right edge. This produces compact cards
|
|
380
|
-
* whose height scales linearly with total port count.
|
|
381
|
-
*
|
|
382
|
-
* @param {Array} pPorts - Array of port objects with Side, Direction
|
|
383
|
-
* @param {number} pTitleBarHeight - Height of the title bar
|
|
384
|
-
* @returns {number} Minimum node height in pixels (0 if no ports)
|
|
385
|
-
*/
|
|
386
|
-
computeMinimumNodeHeight(pPorts, pTitleBarHeight)
|
|
387
|
-
{
|
|
388
|
-
if (!pPorts || !Array.isArray(pPorts) || pPorts.length === 0)
|
|
389
|
-
{
|
|
390
|
-
return 0;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
let tmpMinSpacing = 16;
|
|
394
|
-
let tmpBottomPad = 16;
|
|
395
|
-
|
|
396
|
-
// Count ports per Side value
|
|
397
|
-
let tmpCountBySide = this.buildPortCountsBySide(pPorts);
|
|
398
|
-
|
|
399
|
-
// Sum the space needed per edge (left, right) across all zones.
|
|
400
|
-
// Each zone needs minSpacing * (count + 1) pixels.
|
|
401
|
-
let tmpSpacePerEdge = {};
|
|
402
|
-
for (let tmpSide in tmpCountBySide)
|
|
403
|
-
{
|
|
404
|
-
let tmpEdge = this.getEdgeFromSide(tmpSide);
|
|
405
|
-
|
|
406
|
-
// Only left/right edge zones affect required height
|
|
407
|
-
if (tmpEdge !== 'left' && tmpEdge !== 'right')
|
|
408
|
-
{
|
|
409
|
-
continue;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
let tmpCount = tmpCountBySide[tmpSide];
|
|
413
|
-
let tmpZoneSpace = tmpMinSpacing * (tmpCount + 1);
|
|
414
|
-
|
|
415
|
-
if (!tmpSpacePerEdge[tmpEdge])
|
|
416
|
-
{
|
|
417
|
-
tmpSpacePerEdge[tmpEdge] = 0;
|
|
418
|
-
}
|
|
419
|
-
tmpSpacePerEdge[tmpEdge] += tmpZoneSpace;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
// The minimum height is titleBar + bottomPad + max edge space
|
|
423
|
-
let tmpMinHeight = 0;
|
|
424
|
-
for (let tmpEdge in tmpSpacePerEdge)
|
|
425
|
-
{
|
|
426
|
-
let tmpRequired = pTitleBarHeight + tmpBottomPad + tmpSpacePerEdge[tmpEdge];
|
|
427
|
-
if (tmpRequired > tmpMinHeight)
|
|
428
|
-
{
|
|
429
|
-
tmpMinHeight = tmpRequired;
|
|
430
|
-
}
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
return Math.ceil(tmpMinHeight);
|
|
434
|
-
}
|
|
435
25
|
}
|
|
436
26
|
|
|
437
27
|
module.exports = PictProviderFlowGeometry;
|
|
@@ -53,6 +53,18 @@ const _DefaultIcons =
|
|
|
53
53
|
|
|
54
54
|
'connect': '<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="M8 8l8 8" stroke="var(--theme-color-text-primary, #2c3e50)" stroke-width="2"/><circle cx="6" cy="6" r="3" fill="var(--theme-color-background-secondary, #d5e8f7)" stroke="var(--theme-color-text-primary, #2c3e50)" stroke-width="2"/><circle cx="18" cy="18" r="3" fill="var(--theme-color-background-secondary, #d5e8f7)" stroke="var(--theme-color-text-primary, #2c3e50)" stroke-width="2"/></svg>',
|
|
55
55
|
|
|
56
|
+
// Pan / navigate "hand" tool — read-only surfaces use this to toggle pan + zoom on the canvas.
|
|
57
|
+
'pan': '<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="M18 11V6.5a1.5 1.5 0 0 0-3 0M15 6.5V4.5a1.5 1.5 0 0 0-3 0V6M12 6V5a1.5 1.5 0 0 0-3 0v7M9 12V8.5a1.5 1.5 0 0 0-3 0V14a6 6 0 0 0 6 6h1a5 5 0 0 0 5-5v-4a1.5 1.5 0 0 0-3 0" fill="var(--theme-color-background-secondary, #d5e8f7)" stroke="var(--theme-color-text-primary, #2c3e50)" stroke-width="2"/></svg>',
|
|
58
|
+
|
|
59
|
+
// Crop / view-area frame — a host (e.g. a moodboard) uses this to toggle the content-frame drag handles.
|
|
60
|
+
'frame': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke="var(--theme-color-text-primary, #2c3e50)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 2v16a1 1 0 0 0 1 1h16"/><path d="M2 6h16a1 1 0 0 1 1 1v16"/><rect x="9" y="9" width="6" height="5" rx="1" fill="var(--theme-color-background-secondary, #d5e8f7)" stroke="none"/></svg>',
|
|
61
|
+
|
|
62
|
+
// Display-style toggles for a presentation surface (moodboard): canvas (a free board of cards),
|
|
63
|
+
// jumbotron (a hero band across the top), background (a full-width backdrop behind content).
|
|
64
|
+
'display-canvas': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke="var(--theme-color-text-primary, #2c3e50)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="16" rx="2"/><rect x="6" y="7" width="6" height="5" rx="1" fill="var(--theme-color-background-secondary, #d5e8f7)" stroke="none"/><rect x="14" y="11" width="5" height="6" rx="1" fill="var(--theme-color-background-secondary, #d5e8f7)" stroke="none"/></svg>',
|
|
65
|
+
'display-jumbotron': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke="var(--theme-color-text-primary, #2c3e50)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="16" rx="2"/><rect x="3" y="4" width="18" height="7" rx="2" fill="var(--theme-color-background-secondary, #d5e8f7)" stroke="none"/><path d="M6 15h8M6 18h12"/></svg>',
|
|
66
|
+
'display-background': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke="var(--theme-color-text-primary, #2c3e50)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="4" width="18" height="16" rx="2" fill="var(--theme-color-background-secondary, #d5e8f7)"/><rect x="8" y="8" width="8" height="8" rx="1" fill="var(--theme-color-background-panel, #ffffff)"/></svg>',
|
|
67
|
+
|
|
56
68
|
'fullscreen': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke="var(--theme-color-text-primary, #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>',
|
|
57
69
|
|
|
58
70
|
'exit-fullscreen': '<svg xmlns="http://www.w3.org/2000/svg" width="{FlowIconSize}" height="{FlowIconSize}" viewBox="0 0 24 24" fill="none" stroke="var(--theme-color-text-primary, #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>',
|
|
@@ -67,6 +67,20 @@ class PictProviderFlowLayouts extends libPictProvider
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Resolve a host-supplied storage backend, if one was passed as the flow's
|
|
72
|
+
* `LayoutStorage` option: `{ read(cb), write(layouts, cb), delete(cb) }` with
|
|
73
|
+
* Node-style callbacks. Lets a host wire a REST API / IndexedDB by config
|
|
74
|
+
* instead of overriding the three storage hooks. Returns null to fall back to
|
|
75
|
+
* localStorage.
|
|
76
|
+
* @returns {Object|null}
|
|
77
|
+
*/
|
|
78
|
+
_resolveLayoutStorage()
|
|
79
|
+
{
|
|
80
|
+
let tmpStorage = (this._FlowView && this._FlowView.options) ? this._FlowView.options.LayoutStorage : null;
|
|
81
|
+
return (tmpStorage && typeof tmpStorage === 'object') ? tmpStorage : null;
|
|
82
|
+
}
|
|
83
|
+
|
|
70
84
|
// ── Storage Hooks ─────────────────────────────────────────────────────
|
|
71
85
|
// These three methods form the persistence contract. The default
|
|
72
86
|
// implementation uses localStorage. Override them on the instance or
|
|
@@ -84,6 +98,11 @@ class PictProviderFlowLayouts extends libPictProvider
|
|
|
84
98
|
*/
|
|
85
99
|
storageWrite(pLayouts, fCallback)
|
|
86
100
|
{
|
|
101
|
+
let tmpStorage = this._resolveLayoutStorage();
|
|
102
|
+
if (tmpStorage && typeof tmpStorage.write === 'function')
|
|
103
|
+
{
|
|
104
|
+
return tmpStorage.write(pLayouts, fCallback);
|
|
105
|
+
}
|
|
87
106
|
if (this._StorageKey === false)
|
|
88
107
|
{
|
|
89
108
|
return fCallback(null);
|
|
@@ -113,6 +132,11 @@ class PictProviderFlowLayouts extends libPictProvider
|
|
|
113
132
|
*/
|
|
114
133
|
storageRead(fCallback)
|
|
115
134
|
{
|
|
135
|
+
let tmpStorage = this._resolveLayoutStorage();
|
|
136
|
+
if (tmpStorage && typeof tmpStorage.read === 'function')
|
|
137
|
+
{
|
|
138
|
+
return tmpStorage.read(fCallback);
|
|
139
|
+
}
|
|
116
140
|
if (this._StorageKey === false)
|
|
117
141
|
{
|
|
118
142
|
return fCallback(null, []);
|
|
@@ -149,6 +173,11 @@ class PictProviderFlowLayouts extends libPictProvider
|
|
|
149
173
|
*/
|
|
150
174
|
storageDelete(fCallback)
|
|
151
175
|
{
|
|
176
|
+
let tmpStorage = this._resolveLayoutStorage();
|
|
177
|
+
if (tmpStorage && typeof tmpStorage.delete === 'function')
|
|
178
|
+
{
|
|
179
|
+
return tmpStorage.delete(fCallback);
|
|
180
|
+
}
|
|
152
181
|
if (this._StorageKey === false)
|
|
153
182
|
{
|
|
154
183
|
return fCallback(null);
|
|
@@ -489,6 +518,84 @@ class PictProviderFlowLayouts extends libPictProvider
|
|
|
489
518
|
(pLayout) => pLayout.Hash === pLayoutHash
|
|
490
519
|
) || null;
|
|
491
520
|
}
|
|
521
|
+
|
|
522
|
+
// ── Default layout ─────────────────────────────────────────────────────
|
|
523
|
+
// One saved layout can be marked the default arrangement for a graph. The
|
|
524
|
+
// mark is an IsDefault flag on the layout object, so it rides inside
|
|
525
|
+
// SavedLayouts and persists through both the storage hooks and
|
|
526
|
+
// getFlowData/setFlowData with no storage-shape change.
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Mark a saved layout as the default (clearing any previous default). Pass a
|
|
530
|
+
* falsy hash to clear the default entirely. Persists the change.
|
|
531
|
+
* @param {string} pLayoutHash
|
|
532
|
+
* @returns {boolean} true on success; false if a given hash was not found
|
|
533
|
+
*/
|
|
534
|
+
setDefaultLayout(pLayoutHash)
|
|
535
|
+
{
|
|
536
|
+
if (!this._FlowView)
|
|
537
|
+
{
|
|
538
|
+
this.log.warn('PictProviderFlowLayouts setDefaultLayout: no FlowView reference');
|
|
539
|
+
return false;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
let tmpFlowData = this._FlowView._FlowData;
|
|
543
|
+
let tmpFound = false;
|
|
544
|
+
for (let i = 0; i < tmpFlowData.SavedLayouts.length; i++)
|
|
545
|
+
{
|
|
546
|
+
let tmpIsMatch = (!!pLayoutHash && tmpFlowData.SavedLayouts[i].Hash === pLayoutHash);
|
|
547
|
+
tmpFlowData.SavedLayouts[i].IsDefault = tmpIsMatch;
|
|
548
|
+
if (tmpIsMatch) { tmpFound = true; }
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// A falsy hash clears the default (every IsDefault now false); a non-matching hash is an error.
|
|
552
|
+
if (pLayoutHash && !tmpFound)
|
|
553
|
+
{
|
|
554
|
+
this.log.warn(`PictProviderFlowLayouts setDefaultLayout: layout '${pLayoutHash}' not found`);
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
this._FlowView.marshalFromView();
|
|
559
|
+
this.storageWrite(tmpFlowData.SavedLayouts, (pError) =>
|
|
560
|
+
{
|
|
561
|
+
if (pError)
|
|
562
|
+
{
|
|
563
|
+
this.log.warn(`PictProviderFlowLayouts: failed to persist default: ${pError.message}`);
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
if (this._FlowView._EventHandlerProvider)
|
|
568
|
+
{
|
|
569
|
+
this._FlowView._EventHandlerProvider.fireEvent('onLayoutDefaultChanged', this.getDefaultLayout());
|
|
570
|
+
this._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', tmpFlowData);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return true;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* The layout currently marked default, or null if none.
|
|
578
|
+
* @returns {Object|null}
|
|
579
|
+
*/
|
|
580
|
+
getDefaultLayout()
|
|
581
|
+
{
|
|
582
|
+
if (!this._FlowView) return null;
|
|
583
|
+
return this._FlowView._FlowData.SavedLayouts.find((pLayout) => pLayout.IsDefault === true) || null;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Restore the default layout if one is set.
|
|
588
|
+
* @returns {boolean} true if a default existed and was restored
|
|
589
|
+
*/
|
|
590
|
+
applyDefaultLayout()
|
|
591
|
+
{
|
|
592
|
+
let tmpDefault = this.getDefaultLayout();
|
|
593
|
+
if (!tmpDefault)
|
|
594
|
+
{
|
|
595
|
+
return false;
|
|
596
|
+
}
|
|
597
|
+
return this.restoreLayout(tmpDefault.Hash);
|
|
598
|
+
}
|
|
492
599
|
}
|
|
493
600
|
|
|
494
601
|
module.exports = PictProviderFlowLayouts;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
2
|
-
const libPerimeterMath = require('
|
|
2
|
+
const libPerimeterMath = require('pict-provider-graphlayout').Edges.PerimeterMath;
|
|
3
3
|
|
|
4
4
|
// Chip (port-label badge) geometry — must mirror PortRenderer's badge
|
|
5
5
|
// dimensions exactly so hint paths land on the chip's actual outer
|