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
|
@@ -50,34 +50,71 @@ class PictServiceFlowConnectionRenderer extends libFableServiceProviderBase
|
|
|
50
50
|
tmpPath = this._generateBezierPathWithHandle(tmpSourcePos, tmpTargetPos, tmpHandleX, tmpHandleY);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
// Apply theme noise post-processing to the path
|
|
54
|
+
if (this._FlowView._ThemeProvider)
|
|
55
|
+
{
|
|
56
|
+
tmpPath = this._FlowView._ThemeProvider.processPathString(tmpPath, pConnection.Hash);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Apply stroke-dasharray from theme's ConnectionConfig
|
|
60
|
+
let tmpStrokeDashArray = null;
|
|
61
|
+
if (this._FlowView._ThemeProvider)
|
|
62
|
+
{
|
|
63
|
+
let tmpActiveTheme = this._FlowView._ThemeProvider.getActiveTheme();
|
|
64
|
+
if (tmpActiveTheme && tmpActiveTheme.ConnectionConfig && tmpActiveTheme.ConnectionConfig.StrokeDashArray)
|
|
65
|
+
{
|
|
66
|
+
tmpStrokeDashArray = tmpActiveTheme.ConnectionConfig.StrokeDashArray;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
53
70
|
let tmpViewIdentifier = this._FlowView.options.ViewIdentifier;
|
|
54
71
|
|
|
55
72
|
// Hit area (wider invisible path for easier selection)
|
|
56
|
-
let
|
|
57
|
-
|
|
58
|
-
tmpHitArea.setAttribute('d', tmpPath);
|
|
59
|
-
tmpHitArea.setAttribute('data-connection-hash', pConnection.Hash);
|
|
60
|
-
tmpHitArea.setAttribute('data-element-type', 'connection-hitarea');
|
|
61
|
-
pConnectionsLayer.appendChild(tmpHitArea);
|
|
62
|
-
|
|
63
|
-
// Visible connection path
|
|
64
|
-
let tmpPathElement = this._FlowView._SVGHelperProvider.createSVGElement('path');
|
|
65
|
-
tmpPathElement.setAttribute('class', `pict-flow-connection ${pIsSelected ? 'selected' : ''}`);
|
|
66
|
-
tmpPathElement.setAttribute('d', tmpPath);
|
|
67
|
-
tmpPathElement.setAttribute('data-connection-hash', pConnection.Hash);
|
|
68
|
-
tmpPathElement.setAttribute('data-element-type', 'connection');
|
|
69
|
-
|
|
70
|
-
// Arrow marker
|
|
71
|
-
if (pIsSelected)
|
|
73
|
+
let tmpShapeProvider = this._FlowView._ConnectorShapesProvider;
|
|
74
|
+
if (tmpShapeProvider)
|
|
72
75
|
{
|
|
73
|
-
|
|
76
|
+
let tmpHitArea = tmpShapeProvider.createConnectionHitAreaElement(tmpPath, pConnection.Hash);
|
|
77
|
+
pConnectionsLayer.appendChild(tmpHitArea);
|
|
78
|
+
|
|
79
|
+
let tmpPathElement = tmpShapeProvider.createConnectionPathElement(
|
|
80
|
+
tmpPath, pConnection.Hash, pIsSelected, tmpViewIdentifier);
|
|
81
|
+
if (tmpStrokeDashArray)
|
|
82
|
+
{
|
|
83
|
+
tmpPathElement.setAttribute('stroke-dasharray', tmpStrokeDashArray);
|
|
84
|
+
}
|
|
85
|
+
pConnectionsLayer.appendChild(tmpPathElement);
|
|
74
86
|
}
|
|
75
87
|
else
|
|
76
88
|
{
|
|
77
|
-
|
|
78
|
-
|
|
89
|
+
let tmpHitArea = this._FlowView._SVGHelperProvider.createSVGElement('path');
|
|
90
|
+
tmpHitArea.setAttribute('class', 'pict-flow-connection-hitarea');
|
|
91
|
+
tmpHitArea.setAttribute('d', tmpPath);
|
|
92
|
+
tmpHitArea.setAttribute('data-connection-hash', pConnection.Hash);
|
|
93
|
+
tmpHitArea.setAttribute('data-element-type', 'connection-hitarea');
|
|
94
|
+
pConnectionsLayer.appendChild(tmpHitArea);
|
|
95
|
+
|
|
96
|
+
let tmpPathElement = this._FlowView._SVGHelperProvider.createSVGElement('path');
|
|
97
|
+
tmpPathElement.setAttribute('class', `pict-flow-connection ${pIsSelected ? 'selected' : ''}`);
|
|
98
|
+
tmpPathElement.setAttribute('d', tmpPath);
|
|
99
|
+
tmpPathElement.setAttribute('data-connection-hash', pConnection.Hash);
|
|
100
|
+
tmpPathElement.setAttribute('data-element-type', 'connection');
|
|
101
|
+
|
|
102
|
+
if (pIsSelected)
|
|
103
|
+
{
|
|
104
|
+
tmpPathElement.setAttribute('marker-end', `url(#flow-arrowhead-selected-${tmpViewIdentifier})`);
|
|
105
|
+
}
|
|
106
|
+
else
|
|
107
|
+
{
|
|
108
|
+
tmpPathElement.setAttribute('marker-end', `url(#flow-arrowhead-${tmpViewIdentifier})`);
|
|
109
|
+
}
|
|
79
110
|
|
|
80
|
-
|
|
111
|
+
if (tmpStrokeDashArray)
|
|
112
|
+
{
|
|
113
|
+
tmpPathElement.setAttribute('stroke-dasharray', tmpStrokeDashArray);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
pConnectionsLayer.appendChild(tmpPathElement);
|
|
117
|
+
}
|
|
81
118
|
|
|
82
119
|
// Render drag handles when selected
|
|
83
120
|
if (pIsSelected)
|
|
@@ -434,15 +471,30 @@ class PictServiceFlowConnectionRenderer extends libFableServiceProviderBase
|
|
|
434
471
|
*/
|
|
435
472
|
_createHandle(pLayer, pConnectionHash, pHandleType, pX, pY, pClassName)
|
|
436
473
|
{
|
|
437
|
-
let
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
474
|
+
let tmpShapeProvider = this._FlowView._ConnectorShapesProvider;
|
|
475
|
+
let tmpShapeKey = (pClassName === 'pict-flow-connection-handle-midpoint')
|
|
476
|
+
? 'connection-handle-midpoint' : 'connection-handle';
|
|
477
|
+
|
|
478
|
+
if (tmpShapeProvider)
|
|
479
|
+
{
|
|
480
|
+
let tmpHandle = tmpShapeProvider.createHandleElement(
|
|
481
|
+
pConnectionHash, pHandleType, pX, pY, tmpShapeKey);
|
|
482
|
+
tmpHandle.setAttribute('data-element-type', 'connection-handle');
|
|
483
|
+
tmpHandle.setAttribute('data-connection-hash', pConnectionHash);
|
|
484
|
+
pLayer.appendChild(tmpHandle);
|
|
485
|
+
}
|
|
486
|
+
else
|
|
487
|
+
{
|
|
488
|
+
let tmpCircle = this._FlowView._SVGHelperProvider.createSVGElement('circle');
|
|
489
|
+
tmpCircle.setAttribute('class', pClassName);
|
|
490
|
+
tmpCircle.setAttribute('cx', String(pX));
|
|
491
|
+
tmpCircle.setAttribute('cy', String(pY));
|
|
492
|
+
tmpCircle.setAttribute('r', '6');
|
|
493
|
+
tmpCircle.setAttribute('data-element-type', 'connection-handle');
|
|
494
|
+
tmpCircle.setAttribute('data-connection-hash', pConnectionHash);
|
|
495
|
+
tmpCircle.setAttribute('data-handle-type', pHandleType);
|
|
496
|
+
pLayer.appendChild(tmpCircle);
|
|
497
|
+
}
|
|
446
498
|
}
|
|
447
499
|
|
|
448
500
|
/**
|
|
@@ -476,9 +528,20 @@ class PictServiceFlowConnectionRenderer extends libFableServiceProviderBase
|
|
|
476
528
|
{ x: pEndX, y: pEndY, side: 'left' }
|
|
477
529
|
);
|
|
478
530
|
|
|
479
|
-
let
|
|
480
|
-
tmpPathElement
|
|
481
|
-
|
|
531
|
+
let tmpShapeProvider = this._FlowView._ConnectorShapesProvider;
|
|
532
|
+
let tmpPathElement;
|
|
533
|
+
|
|
534
|
+
if (tmpShapeProvider)
|
|
535
|
+
{
|
|
536
|
+
tmpPathElement = tmpShapeProvider.createDragConnectionElement(tmpPath);
|
|
537
|
+
}
|
|
538
|
+
else
|
|
539
|
+
{
|
|
540
|
+
tmpPathElement = this._FlowView._SVGHelperProvider.createSVGElement('path');
|
|
541
|
+
tmpPathElement.setAttribute('class', 'pict-flow-drag-connection');
|
|
542
|
+
tmpPathElement.setAttribute('d', tmpPath);
|
|
543
|
+
}
|
|
544
|
+
|
|
482
545
|
pLayer.appendChild(tmpPathElement);
|
|
483
546
|
|
|
484
547
|
return tmpPathElement;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PictService-Flow-PanelManager
|
|
5
|
+
*
|
|
6
|
+
* Manages the lifecycle of properties panels in the flow diagram:
|
|
7
|
+
* opening, closing, toggling, and position updates for panels
|
|
8
|
+
* associated with flow nodes.
|
|
9
|
+
*/
|
|
10
|
+
class PictServiceFlowPanelManager extends libFableServiceProviderBase
|
|
11
|
+
{
|
|
12
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
13
|
+
{
|
|
14
|
+
super(pFable, pOptions, pServiceHash);
|
|
15
|
+
|
|
16
|
+
this.serviceType = 'PictServiceFlowPanelManager';
|
|
17
|
+
|
|
18
|
+
this._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Open a properties panel for a node.
|
|
23
|
+
* @param {string} pNodeHash - The hash of the node to open a panel for
|
|
24
|
+
* @returns {Object|false} The panel data, or false if the node was not found
|
|
25
|
+
*/
|
|
26
|
+
openPanel(pNodeHash)
|
|
27
|
+
{
|
|
28
|
+
let tmpNode = this._FlowView.getNode(pNodeHash);
|
|
29
|
+
if (!tmpNode) return false;
|
|
30
|
+
|
|
31
|
+
let tmpNodeTypeConfig = this._FlowView._NodeTypeProvider.getNodeType(tmpNode.Type);
|
|
32
|
+
if (!tmpNodeTypeConfig) return false;
|
|
33
|
+
|
|
34
|
+
// Check if a panel is already open for this node
|
|
35
|
+
let tmpExisting = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.NodeHash === pNodeHash);
|
|
36
|
+
if (tmpExisting) return tmpExisting;
|
|
37
|
+
|
|
38
|
+
let tmpPanelConfig = tmpNodeTypeConfig.PropertiesPanel;
|
|
39
|
+
let tmpPanelHash = `panel-${this.fable.getUUID()}`;
|
|
40
|
+
let tmpWidth, tmpHeight, tmpPanelType, tmpTitle;
|
|
41
|
+
|
|
42
|
+
if (tmpPanelConfig)
|
|
43
|
+
{
|
|
44
|
+
tmpWidth = tmpPanelConfig.DefaultWidth || 300;
|
|
45
|
+
tmpHeight = tmpPanelConfig.DefaultHeight || 200;
|
|
46
|
+
tmpPanelType = tmpPanelConfig.PanelType || 'Base';
|
|
47
|
+
tmpTitle = tmpPanelConfig.Title || tmpNodeTypeConfig.Label || 'Properties';
|
|
48
|
+
}
|
|
49
|
+
else
|
|
50
|
+
{
|
|
51
|
+
// No PropertiesPanel configured — open an auto-generated info panel
|
|
52
|
+
tmpWidth = 240;
|
|
53
|
+
tmpHeight = 180;
|
|
54
|
+
tmpPanelType = 'Info';
|
|
55
|
+
tmpTitle = tmpNodeTypeConfig.Label || tmpNode.Title || 'Node Info';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let tmpPanelData =
|
|
59
|
+
{
|
|
60
|
+
Hash: tmpPanelHash,
|
|
61
|
+
NodeHash: pNodeHash,
|
|
62
|
+
PanelType: tmpPanelType,
|
|
63
|
+
Title: tmpTitle,
|
|
64
|
+
X: tmpNode.X + tmpNode.Width + 30,
|
|
65
|
+
Y: tmpNode.Y,
|
|
66
|
+
Width: tmpWidth,
|
|
67
|
+
Height: tmpHeight
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
this._FlowView._FlowData.OpenPanels.push(tmpPanelData);
|
|
71
|
+
this._FlowView.renderFlow();
|
|
72
|
+
this._FlowView.marshalFromView();
|
|
73
|
+
|
|
74
|
+
if (this._FlowView._EventHandlerProvider)
|
|
75
|
+
{
|
|
76
|
+
this._FlowView._EventHandlerProvider.fireEvent('onPanelOpened', tmpPanelData);
|
|
77
|
+
this._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return tmpPanelData;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Close a properties panel by panel hash.
|
|
85
|
+
* @param {string} pPanelHash
|
|
86
|
+
* @returns {boolean}
|
|
87
|
+
*/
|
|
88
|
+
closePanel(pPanelHash)
|
|
89
|
+
{
|
|
90
|
+
let tmpIndex = this._FlowView._FlowData.OpenPanels.findIndex((pPanel) => pPanel.Hash === pPanelHash);
|
|
91
|
+
if (tmpIndex < 0) return false;
|
|
92
|
+
|
|
93
|
+
let tmpRemovedPanel = this._FlowView._FlowData.OpenPanels.splice(tmpIndex, 1)[0];
|
|
94
|
+
|
|
95
|
+
// Clean up the panel instance
|
|
96
|
+
if (this._FlowView._PropertiesPanelView)
|
|
97
|
+
{
|
|
98
|
+
this._FlowView._PropertiesPanelView.destroyPanel(pPanelHash);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
this._FlowView.renderFlow();
|
|
102
|
+
this._FlowView.marshalFromView();
|
|
103
|
+
|
|
104
|
+
if (this._FlowView._EventHandlerProvider)
|
|
105
|
+
{
|
|
106
|
+
this._FlowView._EventHandlerProvider.fireEvent('onPanelClosed', tmpRemovedPanel);
|
|
107
|
+
this._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView._FlowData);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Close all panels for a given node.
|
|
115
|
+
* @param {string} pNodeHash
|
|
116
|
+
* @returns {boolean}
|
|
117
|
+
*/
|
|
118
|
+
closePanelForNode(pNodeHash)
|
|
119
|
+
{
|
|
120
|
+
let tmpPanelsToClose = this._FlowView._FlowData.OpenPanels.filter((pPanel) => pPanel.NodeHash === pNodeHash);
|
|
121
|
+
if (tmpPanelsToClose.length === 0) return false;
|
|
122
|
+
|
|
123
|
+
for (let i = 0; i < tmpPanelsToClose.length; i++)
|
|
124
|
+
{
|
|
125
|
+
let tmpIndex = this._FlowView._FlowData.OpenPanels.indexOf(tmpPanelsToClose[i]);
|
|
126
|
+
if (tmpIndex >= 0)
|
|
127
|
+
{
|
|
128
|
+
this._FlowView._FlowData.OpenPanels.splice(tmpIndex, 1);
|
|
129
|
+
}
|
|
130
|
+
if (this._FlowView._PropertiesPanelView)
|
|
131
|
+
{
|
|
132
|
+
this._FlowView._PropertiesPanelView.destroyPanel(tmpPanelsToClose[i].Hash);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Toggle a properties panel for a node (open if closed, close if open).
|
|
141
|
+
* @param {string} pNodeHash
|
|
142
|
+
* @returns {Object|false}
|
|
143
|
+
*/
|
|
144
|
+
togglePanel(pNodeHash)
|
|
145
|
+
{
|
|
146
|
+
let tmpExisting = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.NodeHash === pNodeHash);
|
|
147
|
+
if (tmpExisting)
|
|
148
|
+
{
|
|
149
|
+
this.closePanel(tmpExisting.Hash);
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
return this.openPanel(pNodeHash);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Update a panel's position (for drag).
|
|
157
|
+
* @param {string} pPanelHash
|
|
158
|
+
* @param {number} pX
|
|
159
|
+
* @param {number} pY
|
|
160
|
+
*/
|
|
161
|
+
updatePanelPosition(pPanelHash, pX, pY)
|
|
162
|
+
{
|
|
163
|
+
let tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);
|
|
164
|
+
if (!tmpPanel) return;
|
|
165
|
+
|
|
166
|
+
tmpPanel.X = pX;
|
|
167
|
+
tmpPanel.Y = pY;
|
|
168
|
+
|
|
169
|
+
// Reset tether handle positions when panel moves
|
|
170
|
+
this._FlowView._resetHandlesForPanel(pPanelHash);
|
|
171
|
+
|
|
172
|
+
// Update the foreignObject position directly for smooth dragging
|
|
173
|
+
if (this._FlowView._PanelsLayer)
|
|
174
|
+
{
|
|
175
|
+
let tmpFO = this._FlowView._PanelsLayer.querySelector(`[data-panel-hash="${pPanelHash}"]`);
|
|
176
|
+
if (tmpFO)
|
|
177
|
+
{
|
|
178
|
+
tmpFO.setAttribute('x', String(pX));
|
|
179
|
+
tmpFO.setAttribute('y', String(pY));
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Update the tether for this panel
|
|
184
|
+
this._FlowView._renderTethersForNode(tmpPanel.NodeHash);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
module.exports = PictServiceFlowPanelManager;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PictService-Flow-SelectionManager
|
|
5
|
+
*
|
|
6
|
+
* Manages selection state for nodes, connections, and tethers in the flow diagram.
|
|
7
|
+
* Handles selecting, deselecting, and deleting selected elements.
|
|
8
|
+
*/
|
|
9
|
+
class PictServiceFlowSelectionManager extends libFableServiceProviderBase
|
|
10
|
+
{
|
|
11
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
12
|
+
{
|
|
13
|
+
super(pFable, pOptions, pServiceHash);
|
|
14
|
+
|
|
15
|
+
this.serviceType = 'PictServiceFlowSelectionManager';
|
|
16
|
+
|
|
17
|
+
this._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Select a node
|
|
22
|
+
* @param {string|null} pNodeHash - Hash of the node to select, or null to deselect
|
|
23
|
+
*/
|
|
24
|
+
selectNode(pNodeHash)
|
|
25
|
+
{
|
|
26
|
+
let tmpPreviousSelection = this._FlowView._FlowData.ViewState.SelectedNodeHash;
|
|
27
|
+
this._FlowView._FlowData.ViewState.SelectedNodeHash = pNodeHash;
|
|
28
|
+
this._FlowView._FlowData.ViewState.SelectedConnectionHash = null;
|
|
29
|
+
this._FlowView._FlowData.ViewState.SelectedTetherHash = null;
|
|
30
|
+
|
|
31
|
+
this._FlowView.renderFlow();
|
|
32
|
+
|
|
33
|
+
if (this._FlowView._EventHandlerProvider && pNodeHash !== tmpPreviousSelection)
|
|
34
|
+
{
|
|
35
|
+
let tmpNode = pNodeHash ? this._FlowView._FlowData.Nodes.find((pNode) => pNode.Hash === pNodeHash) : null;
|
|
36
|
+
this._FlowView._EventHandlerProvider.fireEvent('onNodeSelected', tmpNode);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Select a connection
|
|
42
|
+
* @param {string|null} pConnectionHash - Hash of the connection to select, or null to deselect
|
|
43
|
+
*/
|
|
44
|
+
selectConnection(pConnectionHash)
|
|
45
|
+
{
|
|
46
|
+
let tmpPreviousSelection = this._FlowView._FlowData.ViewState.SelectedConnectionHash;
|
|
47
|
+
this._FlowView._FlowData.ViewState.SelectedConnectionHash = pConnectionHash;
|
|
48
|
+
this._FlowView._FlowData.ViewState.SelectedNodeHash = null;
|
|
49
|
+
this._FlowView._FlowData.ViewState.SelectedTetherHash = null;
|
|
50
|
+
|
|
51
|
+
this._FlowView.renderFlow();
|
|
52
|
+
|
|
53
|
+
if (this._FlowView._EventHandlerProvider && pConnectionHash !== tmpPreviousSelection)
|
|
54
|
+
{
|
|
55
|
+
let tmpConnection = pConnectionHash ? this._FlowView._FlowData.Connections.find((pConn) => pConn.Hash === pConnectionHash) : null;
|
|
56
|
+
this._FlowView._EventHandlerProvider.fireEvent('onConnectionSelected', tmpConnection);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Select a tether by its panel hash.
|
|
62
|
+
* @param {string|null} pPanelHash - Hash of the panel whose tether to select, or null to deselect
|
|
63
|
+
*/
|
|
64
|
+
selectTether(pPanelHash)
|
|
65
|
+
{
|
|
66
|
+
let tmpPreviousSelection = this._FlowView._FlowData.ViewState.SelectedTetherHash;
|
|
67
|
+
this._FlowView._FlowData.ViewState.SelectedTetherHash = pPanelHash;
|
|
68
|
+
this._FlowView._FlowData.ViewState.SelectedNodeHash = null;
|
|
69
|
+
this._FlowView._FlowData.ViewState.SelectedConnectionHash = null;
|
|
70
|
+
|
|
71
|
+
this._FlowView.renderFlow();
|
|
72
|
+
|
|
73
|
+
if (this._FlowView._EventHandlerProvider && pPanelHash !== tmpPreviousSelection)
|
|
74
|
+
{
|
|
75
|
+
let tmpPanel = pPanelHash ? this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash) : null;
|
|
76
|
+
this._FlowView._EventHandlerProvider.fireEvent('onTetherSelected', tmpPanel);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Deselect all nodes and connections
|
|
82
|
+
*/
|
|
83
|
+
deselectAll()
|
|
84
|
+
{
|
|
85
|
+
this._FlowView._FlowData.ViewState.SelectedNodeHash = null;
|
|
86
|
+
this._FlowView._FlowData.ViewState.SelectedConnectionHash = null;
|
|
87
|
+
this._FlowView._FlowData.ViewState.SelectedTetherHash = null;
|
|
88
|
+
this._FlowView.renderFlow();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Delete the currently selected node or connection
|
|
93
|
+
* @returns {boolean}
|
|
94
|
+
*/
|
|
95
|
+
deleteSelected()
|
|
96
|
+
{
|
|
97
|
+
if (this._FlowView._FlowData.ViewState.SelectedNodeHash)
|
|
98
|
+
{
|
|
99
|
+
return this._FlowView.removeNode(this._FlowView._FlowData.ViewState.SelectedNodeHash);
|
|
100
|
+
}
|
|
101
|
+
if (this._FlowView._FlowData.ViewState.SelectedConnectionHash)
|
|
102
|
+
{
|
|
103
|
+
return this._FlowView.removeConnection(this._FlowView._FlowData.ViewState.SelectedConnectionHash);
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports = PictServiceFlowSelectionManager;
|
|
@@ -446,22 +446,34 @@ class PictServiceFlowTether extends libFableServiceProviderBase
|
|
|
446
446
|
|
|
447
447
|
let tmpPath = this.generatePath(pPanelData, tmpFrom, tmpTo);
|
|
448
448
|
|
|
449
|
-
// Hit area
|
|
450
|
-
let
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
449
|
+
// Hit area and visible tether path
|
|
450
|
+
let tmpShapeProvider = this._FlowView._ConnectorShapesProvider;
|
|
451
|
+
if (tmpShapeProvider)
|
|
452
|
+
{
|
|
453
|
+
let tmpHitArea = tmpShapeProvider.createTetherHitAreaElement(tmpPath, pPanelData.Hash);
|
|
454
|
+
pTethersLayer.appendChild(tmpHitArea);
|
|
455
|
+
|
|
456
|
+
let tmpPathElement = tmpShapeProvider.createTetherPathElement(
|
|
457
|
+
tmpPath, pPanelData.Hash, pIsSelected, pViewIdentifier);
|
|
458
|
+
pTethersLayer.appendChild(tmpPathElement);
|
|
459
|
+
}
|
|
460
|
+
else
|
|
461
|
+
{
|
|
462
|
+
let tmpHitArea = this._FlowView._SVGHelperProvider.createSVGElement('path');
|
|
463
|
+
tmpHitArea.setAttribute('class', 'pict-flow-tether-hitarea');
|
|
464
|
+
tmpHitArea.setAttribute('d', tmpPath);
|
|
465
|
+
tmpHitArea.setAttribute('data-element-type', 'tether-hitarea');
|
|
466
|
+
tmpHitArea.setAttribute('data-panel-hash', pPanelData.Hash);
|
|
467
|
+
pTethersLayer.appendChild(tmpHitArea);
|
|
468
|
+
|
|
469
|
+
let tmpPathElement = this._FlowView._SVGHelperProvider.createSVGElement('path');
|
|
470
|
+
tmpPathElement.setAttribute('class', `pict-flow-tether-line${pIsSelected ? ' selected' : ''}`);
|
|
471
|
+
tmpPathElement.setAttribute('d', tmpPath);
|
|
472
|
+
tmpPathElement.setAttribute('marker-end', `url(#flow-tether-arrowhead-${pViewIdentifier})`);
|
|
473
|
+
tmpPathElement.setAttribute('data-element-type', 'tether');
|
|
474
|
+
tmpPathElement.setAttribute('data-panel-hash', pPanelData.Hash);
|
|
475
|
+
pTethersLayer.appendChild(tmpPathElement);
|
|
476
|
+
}
|
|
465
477
|
|
|
466
478
|
// Render drag handles when selected
|
|
467
479
|
if (pIsSelected)
|
|
@@ -529,15 +541,30 @@ class PictServiceFlowTether extends libFableServiceProviderBase
|
|
|
529
541
|
*/
|
|
530
542
|
_createHandle(pLayer, pPanelHash, pHandleType, pX, pY, pClassName)
|
|
531
543
|
{
|
|
532
|
-
let
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
544
|
+
let tmpShapeProvider = this._FlowView._ConnectorShapesProvider;
|
|
545
|
+
let tmpShapeKey = (pClassName === 'pict-flow-tether-handle-midpoint')
|
|
546
|
+
? 'tether-handle-midpoint' : 'tether-handle';
|
|
547
|
+
|
|
548
|
+
if (tmpShapeProvider)
|
|
549
|
+
{
|
|
550
|
+
let tmpHandle = tmpShapeProvider.createHandleElement(
|
|
551
|
+
pPanelHash, pHandleType, pX, pY, tmpShapeKey);
|
|
552
|
+
tmpHandle.setAttribute('data-element-type', 'tether-handle');
|
|
553
|
+
tmpHandle.setAttribute('data-panel-hash', pPanelHash);
|
|
554
|
+
pLayer.appendChild(tmpHandle);
|
|
555
|
+
}
|
|
556
|
+
else
|
|
557
|
+
{
|
|
558
|
+
let tmpCircle = this._FlowView._SVGHelperProvider.createSVGElement('circle');
|
|
559
|
+
tmpCircle.setAttribute('class', pClassName);
|
|
560
|
+
tmpCircle.setAttribute('cx', String(pX));
|
|
561
|
+
tmpCircle.setAttribute('cy', String(pY));
|
|
562
|
+
tmpCircle.setAttribute('r', '6');
|
|
563
|
+
tmpCircle.setAttribute('data-element-type', 'tether-handle');
|
|
564
|
+
tmpCircle.setAttribute('data-panel-hash', pPanelHash);
|
|
565
|
+
tmpCircle.setAttribute('data-handle-type', pHandleType);
|
|
566
|
+
pLayer.appendChild(tmpCircle);
|
|
567
|
+
}
|
|
541
568
|
}
|
|
542
569
|
}
|
|
543
570
|
|