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.
Files changed (38) hide show
  1. package/.claude/launch.json +11 -0
  2. package/docs/README.md +51 -0
  3. package/example_applications/simple_cards/source/Pict-Application-FlowExample.js +105 -0
  4. package/example_applications/simple_cards/source/cards/FlowCard-Comment.js +36 -0
  5. package/example_applications/simple_cards/source/cards/FlowCard-DataPreview.js +42 -0
  6. package/example_applications/simple_cards/source/cards/FlowCard-Each.js +1 -1
  7. package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +1 -1
  8. package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +1 -1
  9. package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +1 -1
  10. package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +1 -1
  11. package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +1 -1
  12. package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +1 -1
  13. package/example_applications/simple_cards/source/cards/FlowCard-Sparkline.js +98 -0
  14. package/example_applications/simple_cards/source/cards/FlowCard-StatusMonitor.js +44 -0
  15. package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +1 -1
  16. package/example_applications/simple_cards/source/views/PictView-FlowExample-MainWorkspace.js +9 -1
  17. package/package.json +2 -2
  18. package/source/Pict-Section-Flow.js +8 -1
  19. package/source/PictFlowCard.js +49 -1
  20. package/source/providers/PictProvider-Flow-CSS.js +1440 -0
  21. package/source/providers/PictProvider-Flow-ConnectorShapes.js +413 -0
  22. package/source/providers/PictProvider-Flow-Geometry.js +43 -0
  23. package/source/providers/PictProvider-Flow-Icons.js +335 -0
  24. package/source/providers/PictProvider-Flow-Layouts.js +214 -2
  25. package/source/providers/PictProvider-Flow-NodeTypes.js +30 -7
  26. package/source/providers/PictProvider-Flow-Noise.js +241 -0
  27. package/source/providers/PictProvider-Flow-PanelChrome.js +19 -0
  28. package/source/providers/PictProvider-Flow-Theme.js +755 -0
  29. package/source/services/PictService-Flow-ConnectionRenderer.js +95 -32
  30. package/source/services/PictService-Flow-PanelManager.js +188 -0
  31. package/source/services/PictService-Flow-SelectionManager.js +109 -0
  32. package/source/services/PictService-Flow-Tether.js +52 -25
  33. package/source/services/PictService-Flow-ViewportManager.js +176 -0
  34. package/source/views/PictView-Flow-FloatingToolbar.js +352 -0
  35. package/source/views/PictView-Flow-Node.js +654 -169
  36. package/source/views/PictView-Flow-PropertiesPanel.js +176 -1
  37. package/source/views/PictView-Flow-Toolbar.js +846 -379
  38. 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 tmpHitArea = this._FlowView._SVGHelperProvider.createSVGElement('path');
57
- tmpHitArea.setAttribute('class', 'pict-flow-connection-hitarea');
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
- tmpPathElement.setAttribute('marker-end', `url(#flow-arrowhead-selected-${tmpViewIdentifier})`);
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
- tmpPathElement.setAttribute('marker-end', `url(#flow-arrowhead-${tmpViewIdentifier})`);
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
- pConnectionsLayer.appendChild(tmpPathElement);
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 tmpCircle = this._FlowView._SVGHelperProvider.createSVGElement('circle');
438
- tmpCircle.setAttribute('class', pClassName);
439
- tmpCircle.setAttribute('cx', String(pX));
440
- tmpCircle.setAttribute('cy', String(pY));
441
- tmpCircle.setAttribute('r', '6');
442
- tmpCircle.setAttribute('data-element-type', 'connection-handle');
443
- tmpCircle.setAttribute('data-connection-hash', pConnectionHash);
444
- tmpCircle.setAttribute('data-handle-type', pHandleType);
445
- pLayer.appendChild(tmpCircle);
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 tmpPathElement = this._FlowView._SVGHelperProvider.createSVGElement('path');
480
- tmpPathElement.setAttribute('class', 'pict-flow-drag-connection');
481
- tmpPathElement.setAttribute('d', tmpPath);
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 (wider invisible path for easier click targeting)
450
- let tmpHitArea = this._FlowView._SVGHelperProvider.createSVGElement('path');
451
- tmpHitArea.setAttribute('class', 'pict-flow-tether-hitarea');
452
- tmpHitArea.setAttribute('d', tmpPath);
453
- tmpHitArea.setAttribute('data-element-type', 'tether-hitarea');
454
- tmpHitArea.setAttribute('data-panel-hash', pPanelData.Hash);
455
- pTethersLayer.appendChild(tmpHitArea);
456
-
457
- // Visible tether path
458
- let tmpPathElement = this._FlowView._SVGHelperProvider.createSVGElement('path');
459
- tmpPathElement.setAttribute('class', `pict-flow-tether-line${pIsSelected ? ' selected' : ''}`);
460
- tmpPathElement.setAttribute('d', tmpPath);
461
- tmpPathElement.setAttribute('marker-end', `url(#flow-tether-arrowhead-${pViewIdentifier})`);
462
- tmpPathElement.setAttribute('data-element-type', 'tether');
463
- tmpPathElement.setAttribute('data-panel-hash', pPanelData.Hash);
464
- pTethersLayer.appendChild(tmpPathElement);
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 tmpCircle = this._FlowView._SVGHelperProvider.createSVGElement('circle');
533
- tmpCircle.setAttribute('class', pClassName);
534
- tmpCircle.setAttribute('cx', String(pX));
535
- tmpCircle.setAttribute('cy', String(pY));
536
- tmpCircle.setAttribute('r', '6');
537
- tmpCircle.setAttribute('data-element-type', 'tether-handle');
538
- tmpCircle.setAttribute('data-panel-hash', pPanelHash);
539
- tmpCircle.setAttribute('data-handle-type', pHandleType);
540
- pLayer.appendChild(tmpCircle);
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