pict-section-flow 0.0.10 → 0.0.13
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 +1 -1
- package/README.md +176 -0
- package/docs/.nojekyll +0 -0
- package/docs/Architecture.md +303 -0
- package/docs/Custom-Styling.md +275 -0
- package/docs/Data_Model.md +158 -0
- package/docs/Event_System.md +156 -0
- package/docs/Getting_Started.md +237 -0
- package/docs/Implementation_Reference.md +528 -0
- package/docs/Layout_Persistence.md +117 -0
- package/docs/README.md +115 -52
- package/docs/_cover.md +11 -0
- package/docs/_sidebar.md +52 -0
- package/docs/_topbar.md +8 -0
- package/docs/api/PictFlowCard.md +216 -0
- package/docs/api/PictFlowCardPropertiesPanel.md +235 -0
- package/docs/api/addConnection.md +101 -0
- package/docs/api/addNode.md +137 -0
- package/docs/api/autoLayout.md +77 -0
- package/docs/api/getFlowData.md +112 -0
- package/docs/api/marshalToView.md +95 -0
- package/docs/api/openPanel.md +128 -0
- package/docs/api/registerHandler.md +174 -0
- package/docs/api/registerNodeType.md +142 -0
- package/docs/api/removeConnection.md +57 -0
- package/docs/api/removeNode.md +80 -0
- package/docs/api/saveLayout.md +152 -0
- package/docs/api/screenToSVGCoords.md +68 -0
- package/docs/api/selectNode.md +116 -0
- package/docs/api/setTheme.md +168 -0
- package/docs/api/setZoom.md +97 -0
- package/docs/api/toggleFullscreen.md +68 -0
- package/docs/card-help/EACH.md +19 -0
- package/docs/card-help/FREAD.md +24 -0
- package/docs/card-help/FWRITE.md +24 -0
- package/docs/card-help/GET.md +22 -0
- package/docs/card-help/ITE.md +23 -0
- package/docs/card-help/LOG.md +23 -0
- package/docs/card-help/NOTE.md +17 -0
- package/docs/card-help/PREV.md +18 -0
- package/docs/card-help/SET.md +27 -0
- package/docs/card-help/SPKL.md +22 -0
- package/docs/card-help/STAT.md +23 -0
- package/docs/card-help/SW.md +25 -0
- package/docs/css/docuserve.css +73 -0
- package/docs/index.html +39 -0
- package/docs/retold-catalog.json +169 -0
- package/docs/retold-keyword-index.json +13942 -0
- package/example_applications/simple_cards/package.json +1 -0
- package/example_applications/simple_cards/source/card-help-content.js +16 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Comment.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-DataPreview.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Each.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Sparkline.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-StatusMonitor.js +2 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +2 -0
- package/package.json +11 -7
- package/scripts/generate-card-help.js +214 -0
- package/source/Pict-Section-Flow.js +4 -0
- package/source/PictFlowCard.js +3 -1
- package/source/providers/PictProvider-Flow-CSS.js +245 -152
- package/source/providers/PictProvider-Flow-ConnectorShapes.js +24 -0
- package/source/providers/PictProvider-Flow-Geometry.js +195 -38
- package/source/providers/PictProvider-Flow-PanelChrome.js +14 -12
- package/source/services/PictService-Flow-ConnectionHandleManager.js +263 -0
- package/source/services/PictService-Flow-ConnectionRenderer.js +134 -183
- package/source/services/PictService-Flow-DataManager.js +338 -0
- package/source/services/PictService-Flow-InteractionManager.js +165 -7
- package/source/services/PictService-Flow-PathGenerator.js +282 -0
- package/source/services/PictService-Flow-PortRenderer.js +269 -0
- package/source/services/PictService-Flow-RenderManager.js +281 -0
- package/source/services/PictService-Flow-Tether.js +6 -42
- package/source/views/PictView-Flow-Node.js +2 -220
- package/source/views/PictView-Flow-PropertiesPanel.js +89 -44
- package/source/views/PictView-Flow.js +130 -882
- package/test/ConnectionHandleManager_tests.js +717 -0
- package/test/ConnectionRenderer_tests.js +591 -0
- package/test/DataManager_tests.js +859 -0
- package/test/Geometry_tests.js +767 -0
- package/test/PathGenerator_tests.js +978 -0
- package/test/PortRenderer_tests.js +367 -0
- package/test/RenderManager_tests.js +756 -0
|
@@ -8,6 +8,10 @@ const libPictServiceFlowPathGenerator = require('../services/PictService-Flow-Pa
|
|
|
8
8
|
const libPictServiceFlowViewportManager = require('../services/PictService-Flow-ViewportManager.js');
|
|
9
9
|
const libPictServiceFlowSelectionManager = require('../services/PictService-Flow-SelectionManager.js');
|
|
10
10
|
const libPictServiceFlowPanelManager = require('../services/PictService-Flow-PanelManager.js');
|
|
11
|
+
const libPictServiceFlowDataManager = require('../services/PictService-Flow-DataManager.js');
|
|
12
|
+
const libPictServiceFlowConnectionHandleManager = require('../services/PictService-Flow-ConnectionHandleManager.js');
|
|
13
|
+
const libPictServiceFlowRenderManager = require('../services/PictService-Flow-RenderManager.js');
|
|
14
|
+
const libPictServiceFlowPortRenderer = require('../services/PictService-Flow-PortRenderer.js');
|
|
11
15
|
|
|
12
16
|
const libPictProviderFlowNodeTypes = require('../providers/PictProvider-Flow-NodeTypes.js');
|
|
13
17
|
const libPictProviderFlowEventHandler = require('../providers/PictProvider-Flow-EventHandler.js');
|
|
@@ -67,7 +71,7 @@ const _DefaultConfiguration =
|
|
|
67
71
|
[
|
|
68
72
|
{
|
|
69
73
|
Hash: 'Flow-PanelChrome-Template',
|
|
70
|
-
Template: /*html*/`<div class="pict-flow-panel" xmlns="http://www.w3.org/1999/xhtml"><div class="pict-flow-panel-titlebar" data-element-type="panel-titlebar" data-panel-hash="{~D:Record.Hash~}"><span class="pict-flow-panel-title-text">{~D:Record.Title~}</span><span class="pict-flow-panel-close-btn" data-element-type="panel-close" data-panel-hash="{~D:Record.Hash~}"><span class="pict-flow-panel-close-icon"></span></span></div><div class="pict-flow-panel-
|
|
74
|
+
Template: /*html*/`<div class="pict-flow-panel" xmlns="http://www.w3.org/1999/xhtml"><div class="pict-flow-panel-titlebar" data-element-type="panel-titlebar" data-panel-hash="{~D:Record.Hash~}"><span class="pict-flow-panel-title-text">{~D:Record.Title~}</span><span class="pict-flow-panel-close-btn" data-element-type="panel-close" data-panel-hash="{~D:Record.Hash~}"><span class="pict-flow-panel-close-icon"></span></span></div><div class="pict-flow-panel-content" data-panel-hash="{~D:Record.Hash~}"><div class="pict-flow-panel-tab-pane active" data-tab="properties" data-panel-hash="{~D:Record.Hash~}"></div><div class="pict-flow-panel-tab-pane" data-tab="help" data-panel-hash="{~D:Record.Hash~}" style="display:none;"></div><div class="pict-flow-panel-tab-pane" data-tab="appearance" data-panel-hash="{~D:Record.Hash~}" style="display:none;"></div></div><div class="pict-flow-panel-resize-handle" data-element-type="panel-resize" data-panel-hash="{~D:Record.Hash~}"></div><div class="pict-flow-panel-tabbar" data-panel-hash="{~D:Record.Hash~}"><div class="pict-flow-panel-tab active" data-tab-target="properties" data-panel-hash="{~D:Record.Hash~}">Properties</div><div class="pict-flow-panel-tab" data-tab-target="help" data-panel-hash="{~D:Record.Hash~}" style="display:none;">Help</div><div class="pict-flow-panel-tab" data-tab-target="appearance" data-panel-hash="{~D:Record.Hash~}">Appearance</div></div></div>`
|
|
71
75
|
},
|
|
72
76
|
{
|
|
73
77
|
Hash: 'Flow-Container-Template',
|
|
@@ -122,119 +126,58 @@ class PictViewFlow extends libPictView
|
|
|
122
126
|
|
|
123
127
|
this.serviceType = 'PictSectionFlow';
|
|
124
128
|
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
if (!this.fable.servicesMap.hasOwnProperty('PictProviderFlowConnectorShapes'))
|
|
179
|
-
{
|
|
180
|
-
this.fable.addServiceType('PictProviderFlowConnectorShapes', libPictProviderFlowConnectorShapes);
|
|
181
|
-
}
|
|
182
|
-
if (!this.fable.servicesMap.hasOwnProperty('PictProviderFlowTheme'))
|
|
183
|
-
{
|
|
184
|
-
this.fable.addServiceType('PictProviderFlowTheme', libPictProviderFlowTheme);
|
|
185
|
-
}
|
|
186
|
-
if (!this.fable.servicesMap.hasOwnProperty('PictProviderFlowNoise'))
|
|
187
|
-
{
|
|
188
|
-
this.fable.addServiceType('PictProviderFlowNoise', libPictProviderFlowNoise);
|
|
189
|
-
}
|
|
190
|
-
if (!this.fable.servicesMap.hasOwnProperty('PictProviderFlowNodeTypes'))
|
|
191
|
-
{
|
|
192
|
-
this.fable.addServiceType('PictProviderFlowNodeTypes', libPictProviderFlowNodeTypes);
|
|
193
|
-
}
|
|
194
|
-
if (!this.fable.servicesMap.hasOwnProperty('PictProviderFlowEventHandler'))
|
|
195
|
-
{
|
|
196
|
-
this.fable.addServiceType('PictProviderFlowEventHandler', libPictProviderFlowEventHandler);
|
|
197
|
-
}
|
|
198
|
-
if (!this.fable.servicesMap.hasOwnProperty('PictProviderFlowLayouts'))
|
|
199
|
-
{
|
|
200
|
-
this.fable.addServiceType('PictProviderFlowLayouts', libPictProviderFlowLayouts);
|
|
201
|
-
}
|
|
202
|
-
if (!this.fable.servicesMap.hasOwnProperty('PictViewFlowNode'))
|
|
203
|
-
{
|
|
204
|
-
this.fable.addServiceType('PictViewFlowNode', libPictViewFlowNode);
|
|
205
|
-
}
|
|
206
|
-
if (!this.fable.servicesMap.hasOwnProperty('PictViewFlowToolbar'))
|
|
207
|
-
{
|
|
208
|
-
this.fable.addServiceType('PictViewFlowToolbar', libPictViewFlowToolbar);
|
|
209
|
-
}
|
|
210
|
-
if (!this.fable.servicesMap.hasOwnProperty('PictViewFlowFloatingToolbar'))
|
|
211
|
-
{
|
|
212
|
-
this.fable.addServiceType('PictViewFlowFloatingToolbar', libPictViewFlowFloatingToolbar);
|
|
213
|
-
}
|
|
214
|
-
if (!this.fable.servicesMap.hasOwnProperty('PictViewFlowPropertiesPanel'))
|
|
215
|
-
{
|
|
216
|
-
this.fable.addServiceType('PictViewFlowPropertiesPanel', libPictViewFlowPropertiesPanel);
|
|
217
|
-
}
|
|
218
|
-
if (!this.fable.servicesMap.hasOwnProperty('PictFlowCardPropertiesPanel'))
|
|
219
|
-
{
|
|
220
|
-
this.fable.addServiceType('PictFlowCardPropertiesPanel', libPictFlowCardPropertiesPanel);
|
|
221
|
-
}
|
|
222
|
-
if (!this.fable.servicesMap.hasOwnProperty('PictFlowCardPropertiesPanel-Template'))
|
|
223
|
-
{
|
|
224
|
-
this.fable.addServiceType('PictFlowCardPropertiesPanel-Template', libPanelTemplate);
|
|
225
|
-
}
|
|
226
|
-
if (!this.fable.servicesMap.hasOwnProperty('PictFlowCardPropertiesPanel-Markdown'))
|
|
227
|
-
{
|
|
228
|
-
this.fable.addServiceType('PictFlowCardPropertiesPanel-Markdown', libPanelMarkdown);
|
|
229
|
-
}
|
|
230
|
-
if (!this.fable.servicesMap.hasOwnProperty('PictFlowCardPropertiesPanel-Form'))
|
|
231
|
-
{
|
|
232
|
-
this.fable.addServiceType('PictFlowCardPropertiesPanel-Form', libPanelForm);
|
|
233
|
-
}
|
|
234
|
-
if (!this.fable.servicesMap.hasOwnProperty('PictFlowCardPropertiesPanel-View'))
|
|
235
|
-
{
|
|
236
|
-
this.fable.addServiceType('PictFlowCardPropertiesPanel-View', libPanelView);
|
|
237
|
-
}
|
|
129
|
+
// ---- Declarative service registry ----
|
|
130
|
+
// Each entry defines a service to register, instantiate, and guard.
|
|
131
|
+
// Optional flags:
|
|
132
|
+
// NoFlowView — instantiate without { FlowView: this }
|
|
133
|
+
// PostInit — method name to call on the instance after creation
|
|
134
|
+
// RegisterOnly — only register the type; do not bulk-instantiate
|
|
135
|
+
this._ServiceRegistry =
|
|
136
|
+
[
|
|
137
|
+
// Providers (stateless or config-only — no FlowView needed)
|
|
138
|
+
{ ServiceType: 'PictProviderFlowSVGHelpers', Library: libPictProviderFlowSVGHelpers, Property: '_SVGHelperProvider', NoFlowView: true },
|
|
139
|
+
{ ServiceType: 'PictProviderFlowGeometry', Library: libPictProviderFlowGeometry, Property: '_GeometryProvider', NoFlowView: true },
|
|
140
|
+
{ ServiceType: 'PictProviderFlowNoise', Library: libPictProviderFlowNoise, Property: '_NoiseProvider', NoFlowView: true },
|
|
141
|
+
|
|
142
|
+
// Providers (need FlowView)
|
|
143
|
+
{ ServiceType: 'PictProviderFlowTheme', Library: libPictProviderFlowTheme, Property: '_ThemeProvider' },
|
|
144
|
+
{ ServiceType: 'PictProviderFlowCSS', Library: libPictProviderFlowCSS, Property: '_CSSProvider', PostInit: 'registerCSS' },
|
|
145
|
+
{ ServiceType: 'PictProviderFlowIcons', Library: libPictProviderFlowIcons, Property: '_IconProvider', PostInit: 'registerIconTemplates' },
|
|
146
|
+
{ ServiceType: 'PictProviderFlowConnectorShapes', Library: libPictProviderFlowConnectorShapes, Property: '_ConnectorShapesProvider' },
|
|
147
|
+
{ ServiceType: 'PictProviderFlowPanelChrome', Library: libPictProviderFlowPanelChrome, Property: '_PanelChromeProvider' },
|
|
148
|
+
{ ServiceType: 'PictProviderFlowNodeTypes', Library: libPictProviderFlowNodeTypes, Property: '_NodeTypeProvider', ExtraOptions: () => ({ AdditionalNodeTypes: this.options.NodeTypes }) },
|
|
149
|
+
{ ServiceType: 'PictProviderFlowEventHandler', Library: libPictProviderFlowEventHandler, Property: '_EventHandlerProvider' },
|
|
150
|
+
{ ServiceType: 'PictProviderFlowLayouts', Library: libPictProviderFlowLayouts, Property: '_LayoutProvider', PostInit: 'loadPersistedLayouts' },
|
|
151
|
+
|
|
152
|
+
// Services
|
|
153
|
+
{ ServiceType: 'PictServiceFlowPathGenerator', Library: libPictServiceFlowPathGenerator, Property: '_PathGenerator' },
|
|
154
|
+
{ ServiceType: 'PictServiceFlowDataManager', Library: libPictServiceFlowDataManager, Property: '_DataManager' },
|
|
155
|
+
{ ServiceType: 'PictServiceFlowConnectionHandleManager', Library: libPictServiceFlowConnectionHandleManager, Property: '_ConnectionHandleManager' },
|
|
156
|
+
{ ServiceType: 'PictServiceFlowRenderManager', Library: libPictServiceFlowRenderManager, Property: '_RenderManager' },
|
|
157
|
+
{ ServiceType: 'PictServiceFlowPortRenderer', Library: libPictServiceFlowPortRenderer, Property: '_PortRenderer' },
|
|
158
|
+
{ ServiceType: 'PictServiceFlowInteractionManager', Library: libPictServiceFlowInteractionManager, Property: '_InteractionManager' },
|
|
159
|
+
{ ServiceType: 'PictServiceFlowConnectionRenderer', Library: libPictServiceFlowConnectionRenderer, Property: '_ConnectionRenderer' },
|
|
160
|
+
{ ServiceType: 'PictServiceFlowTether', Library: libPictServiceFlowTether, Property: '_TetherService' },
|
|
161
|
+
{ ServiceType: 'PictServiceFlowLayout', Library: libPictServiceFlowLayout, Property: '_LayoutService' },
|
|
162
|
+
{ ServiceType: 'PictServiceFlowViewportManager', Library: libPictServiceFlowViewportManager, Property: '_ViewportManager' },
|
|
163
|
+
{ ServiceType: 'PictServiceFlowSelectionManager', Library: libPictServiceFlowSelectionManager, Property: '_SelectionManager' },
|
|
164
|
+
{ ServiceType: 'PictServiceFlowPanelManager', Library: libPictServiceFlowPanelManager, Property: '_PanelManager' },
|
|
165
|
+
|
|
166
|
+
// View types (register only — instantiated with custom config in onAfterInitialRender)
|
|
167
|
+
{ ServiceType: 'PictViewFlowNode', Library: libPictViewFlowNode, RegisterOnly: true },
|
|
168
|
+
{ ServiceType: 'PictViewFlowToolbar', Library: libPictViewFlowToolbar, RegisterOnly: true },
|
|
169
|
+
{ ServiceType: 'PictViewFlowFloatingToolbar', Library: libPictViewFlowFloatingToolbar, RegisterOnly: true },
|
|
170
|
+
{ ServiceType: 'PictViewFlowPropertiesPanel', Library: libPictViewFlowPropertiesPanel, RegisterOnly: true },
|
|
171
|
+
|
|
172
|
+
// Panel types (register only)
|
|
173
|
+
{ ServiceType: 'PictFlowCardPropertiesPanel', Library: libPictFlowCardPropertiesPanel, RegisterOnly: true },
|
|
174
|
+
{ ServiceType: 'PictFlowCardPropertiesPanel-Template', Library: libPanelTemplate, RegisterOnly: true },
|
|
175
|
+
{ ServiceType: 'PictFlowCardPropertiesPanel-Markdown', Library: libPanelMarkdown, RegisterOnly: true },
|
|
176
|
+
{ ServiceType: 'PictFlowCardPropertiesPanel-Form', Library: libPanelForm, RegisterOnly: true },
|
|
177
|
+
{ ServiceType: 'PictFlowCardPropertiesPanel-View', Library: libPanelView, RegisterOnly: true }
|
|
178
|
+
];
|
|
179
|
+
|
|
180
|
+
this._registerServiceTypes();
|
|
238
181
|
|
|
239
182
|
// Internal state
|
|
240
183
|
this._FlowData = {
|
|
@@ -259,6 +202,11 @@ class PictViewFlow extends libPictView
|
|
|
259
202
|
this._TethersLayer = null;
|
|
260
203
|
this._PanelsLayer = null;
|
|
261
204
|
|
|
205
|
+
this._DataManager = null;
|
|
206
|
+
this._ConnectionHandleManager = null;
|
|
207
|
+
this._RenderManager = null;
|
|
208
|
+
this._PortRenderer = null;
|
|
209
|
+
|
|
262
210
|
this._InteractionManager = null;
|
|
263
211
|
this._ConnectionRenderer = null;
|
|
264
212
|
this._TetherService = null;
|
|
@@ -285,6 +233,41 @@ class PictViewFlow extends libPictView
|
|
|
285
233
|
this.initialRenderComplete = false;
|
|
286
234
|
}
|
|
287
235
|
|
|
236
|
+
_registerServiceTypes()
|
|
237
|
+
{
|
|
238
|
+
for (let i = 0; i < this._ServiceRegistry.length; i++)
|
|
239
|
+
{
|
|
240
|
+
let tmpEntry = this._ServiceRegistry[i];
|
|
241
|
+
if (!this.fable.servicesMap.hasOwnProperty(tmpEntry.ServiceType))
|
|
242
|
+
{
|
|
243
|
+
this.fable.addServiceType(tmpEntry.ServiceType, tmpEntry.Library);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
_instantiateServices()
|
|
249
|
+
{
|
|
250
|
+
for (let i = 0; i < this._ServiceRegistry.length; i++)
|
|
251
|
+
{
|
|
252
|
+
let tmpEntry = this._ServiceRegistry[i];
|
|
253
|
+
if (tmpEntry.RegisterOnly) continue;
|
|
254
|
+
if (this[tmpEntry.Property]) continue;
|
|
255
|
+
|
|
256
|
+
let tmpOptions = tmpEntry.NoFlowView ? {} : { FlowView: this };
|
|
257
|
+
if (typeof tmpEntry.ExtraOptions === 'function')
|
|
258
|
+
{
|
|
259
|
+
Object.assign(tmpOptions, tmpEntry.ExtraOptions());
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
this[tmpEntry.Property] = this.fable.instantiateServiceProviderWithoutRegistration(tmpEntry.ServiceType, tmpOptions);
|
|
263
|
+
|
|
264
|
+
if (tmpEntry.PostInit && typeof this[tmpEntry.Property][tmpEntry.PostInit] === 'function')
|
|
265
|
+
{
|
|
266
|
+
this[tmpEntry.Property][tmpEntry.PostInit]();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
288
271
|
get flowData()
|
|
289
272
|
{
|
|
290
273
|
return this._FlowData;
|
|
@@ -324,7 +307,7 @@ class PictViewFlow extends libPictView
|
|
|
324
307
|
{
|
|
325
308
|
super.onBeforeInitialize();
|
|
326
309
|
|
|
327
|
-
//
|
|
310
|
+
// Theme + Noise must be created first (CSS PostInit depends on theme state)
|
|
328
311
|
this._ThemeProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowTheme', { FlowView: this });
|
|
329
312
|
this._NoiseProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowNoise');
|
|
330
313
|
|
|
@@ -338,37 +321,8 @@ class PictViewFlow extends libPictView
|
|
|
338
321
|
this._ThemeProvider.setNoiseLevel(this.options.NoiseLevel);
|
|
339
322
|
}
|
|
340
323
|
|
|
341
|
-
// Instantiate
|
|
342
|
-
this.
|
|
343
|
-
this._CSSProvider.registerCSS();
|
|
344
|
-
|
|
345
|
-
// Instantiate the SVG icon provider
|
|
346
|
-
this._IconProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowIcons', { FlowView: this });
|
|
347
|
-
this._IconProvider.registerIconTemplates();
|
|
348
|
-
|
|
349
|
-
// Instantiate the connector shapes provider
|
|
350
|
-
this._ConnectorShapesProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowConnectorShapes', { FlowView: this });
|
|
351
|
-
|
|
352
|
-
// Instantiate shared utility providers first (used by services below)
|
|
353
|
-
this._SVGHelperProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowSVGHelpers');
|
|
354
|
-
this._GeometryProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowGeometry');
|
|
355
|
-
this._PathGenerator = this.fable.instantiateServiceProviderWithoutRegistration('PictServiceFlowPathGenerator', { FlowView: this });
|
|
356
|
-
this._PanelChromeProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowPanelChrome', { FlowView: this });
|
|
357
|
-
|
|
358
|
-
// Instantiate services
|
|
359
|
-
this._InteractionManager = this.fable.instantiateServiceProviderWithoutRegistration('PictServiceFlowInteractionManager', { FlowView: this });
|
|
360
|
-
this._ConnectionRenderer = this.fable.instantiateServiceProviderWithoutRegistration('PictServiceFlowConnectionRenderer', { FlowView: this });
|
|
361
|
-
this._TetherService = this.fable.instantiateServiceProviderWithoutRegistration('PictServiceFlowTether', { FlowView: this });
|
|
362
|
-
this._LayoutService = this.fable.instantiateServiceProviderWithoutRegistration('PictServiceFlowLayout', { FlowView: this });
|
|
363
|
-
this._ViewportManager = this.fable.instantiateServiceProviderWithoutRegistration('PictServiceFlowViewportManager', { FlowView: this });
|
|
364
|
-
this._SelectionManager = this.fable.instantiateServiceProviderWithoutRegistration('PictServiceFlowSelectionManager', { FlowView: this });
|
|
365
|
-
this._PanelManager = this.fable.instantiateServiceProviderWithoutRegistration('PictServiceFlowPanelManager', { FlowView: this });
|
|
366
|
-
|
|
367
|
-
// Instantiate providers, passing any additional node types from view options
|
|
368
|
-
this._NodeTypeProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowNodeTypes', { FlowView: this, AdditionalNodeTypes: this.options.NodeTypes });
|
|
369
|
-
this._EventHandlerProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowEventHandler', { FlowView: this });
|
|
370
|
-
this._LayoutProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowLayouts', { FlowView: this });
|
|
371
|
-
this._LayoutProvider.loadPersistedLayouts();
|
|
324
|
+
// Instantiate all remaining services (skips Theme + Noise since already set)
|
|
325
|
+
this._instantiateServices();
|
|
372
326
|
|
|
373
327
|
return super.onBeforeInitialize();
|
|
374
328
|
}
|
|
@@ -426,96 +380,8 @@ class PictViewFlow extends libPictView
|
|
|
426
380
|
this._PanelsLayer = tmpPanelsElements[0];
|
|
427
381
|
}
|
|
428
382
|
|
|
429
|
-
//
|
|
430
|
-
|
|
431
|
-
{
|
|
432
|
-
this._ThemeProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowTheme', { FlowView: this });
|
|
433
|
-
}
|
|
434
|
-
if (!this._NoiseProvider)
|
|
435
|
-
{
|
|
436
|
-
this._NoiseProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowNoise');
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
// Initialize CSS provider (fallback if not already created)
|
|
440
|
-
if (!this._CSSProvider)
|
|
441
|
-
{
|
|
442
|
-
this._CSSProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowCSS', { FlowView: this });
|
|
443
|
-
this._CSSProvider.registerCSS();
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// Initialize icon provider (fallback if not already created)
|
|
447
|
-
if (!this._IconProvider)
|
|
448
|
-
{
|
|
449
|
-
this._IconProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowIcons', { FlowView: this });
|
|
450
|
-
this._IconProvider.registerIconTemplates();
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
// Initialize connector shapes provider (fallback if not already created)
|
|
454
|
-
if (!this._ConnectorShapesProvider)
|
|
455
|
-
{
|
|
456
|
-
this._ConnectorShapesProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowConnectorShapes', { FlowView: this });
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// Initialize shared utility providers (used by services below)
|
|
460
|
-
if (!this._SVGHelperProvider)
|
|
461
|
-
{
|
|
462
|
-
this._SVGHelperProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowSVGHelpers');
|
|
463
|
-
}
|
|
464
|
-
if (!this._GeometryProvider)
|
|
465
|
-
{
|
|
466
|
-
this._GeometryProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowGeometry');
|
|
467
|
-
}
|
|
468
|
-
if (!this._PathGenerator)
|
|
469
|
-
{
|
|
470
|
-
this._PathGenerator = this.fable.instantiateServiceProviderWithoutRegistration('PictServiceFlowPathGenerator', { FlowView: this });
|
|
471
|
-
}
|
|
472
|
-
if (!this._PanelChromeProvider)
|
|
473
|
-
{
|
|
474
|
-
this._PanelChromeProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowPanelChrome', { FlowView: this });
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
// Initialize services with references
|
|
478
|
-
if (!this._InteractionManager)
|
|
479
|
-
{
|
|
480
|
-
this._InteractionManager = this.fable.instantiateServiceProviderWithoutRegistration('PictServiceFlowInteractionManager', { FlowView: this });
|
|
481
|
-
}
|
|
482
|
-
if (!this._ConnectionRenderer)
|
|
483
|
-
{
|
|
484
|
-
this._ConnectionRenderer = this.fable.instantiateServiceProviderWithoutRegistration('PictServiceFlowConnectionRenderer', { FlowView: this });
|
|
485
|
-
}
|
|
486
|
-
if (!this._TetherService)
|
|
487
|
-
{
|
|
488
|
-
this._TetherService = this.fable.instantiateServiceProviderWithoutRegistration('PictServiceFlowTether', { FlowView: this });
|
|
489
|
-
}
|
|
490
|
-
if (!this._LayoutService)
|
|
491
|
-
{
|
|
492
|
-
this._LayoutService = this.fable.instantiateServiceProviderWithoutRegistration('PictServiceFlowLayout', { FlowView: this });
|
|
493
|
-
}
|
|
494
|
-
if (!this._ViewportManager)
|
|
495
|
-
{
|
|
496
|
-
this._ViewportManager = this.fable.instantiateServiceProviderWithoutRegistration('PictServiceFlowViewportManager', { FlowView: this });
|
|
497
|
-
}
|
|
498
|
-
if (!this._SelectionManager)
|
|
499
|
-
{
|
|
500
|
-
this._SelectionManager = this.fable.instantiateServiceProviderWithoutRegistration('PictServiceFlowSelectionManager', { FlowView: this });
|
|
501
|
-
}
|
|
502
|
-
if (!this._PanelManager)
|
|
503
|
-
{
|
|
504
|
-
this._PanelManager = this.fable.instantiateServiceProviderWithoutRegistration('PictServiceFlowPanelManager', { FlowView: this });
|
|
505
|
-
}
|
|
506
|
-
if (!this._NodeTypeProvider)
|
|
507
|
-
{
|
|
508
|
-
this._NodeTypeProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowNodeTypes', { FlowView: this, AdditionalNodeTypes: this.options.NodeTypes });
|
|
509
|
-
}
|
|
510
|
-
if (!this._EventHandlerProvider)
|
|
511
|
-
{
|
|
512
|
-
this._EventHandlerProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowEventHandler', { FlowView: this });
|
|
513
|
-
}
|
|
514
|
-
if (!this._LayoutProvider)
|
|
515
|
-
{
|
|
516
|
-
this._LayoutProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictProviderFlowLayouts', { FlowView: this });
|
|
517
|
-
this._LayoutProvider.loadPersistedLayouts();
|
|
518
|
-
}
|
|
383
|
+
// Ensure all services are initialized (fallback if onBeforeInitialize was skipped)
|
|
384
|
+
this._instantiateServices();
|
|
519
385
|
|
|
520
386
|
// Inject marker defs via the connector shapes provider
|
|
521
387
|
// Note: insertAdjacentHTML does not work on SVG elements (wrong namespace),
|
|
@@ -590,296 +456,16 @@ class PictViewFlow extends libPictView
|
|
|
590
456
|
this.renderFlow();
|
|
591
457
|
}
|
|
592
458
|
|
|
593
|
-
|
|
594
|
-
* Marshal data from AppData into the flow view
|
|
595
|
-
*/
|
|
596
|
-
marshalToView()
|
|
597
|
-
{
|
|
598
|
-
if (this.options.FlowDataAddress)
|
|
599
|
-
{
|
|
600
|
-
const tmpAddressSpace =
|
|
601
|
-
{
|
|
602
|
-
Fable: this.fable,
|
|
603
|
-
Pict: this.pict || this.fable,
|
|
604
|
-
AppData: this.pict ? this.pict.AppData : this.fable.AppData,
|
|
605
|
-
Bundle: this.Bundle,
|
|
606
|
-
Options: this.options
|
|
607
|
-
};
|
|
608
|
-
let tmpData = this.fable.manifest.getValueByHash(tmpAddressSpace, this.options.FlowDataAddress);
|
|
609
|
-
if (typeof tmpData === 'object' && tmpData !== null)
|
|
610
|
-
{
|
|
611
|
-
this.setFlowData(tmpData);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
/**
|
|
617
|
-
* Marshal data from the flow view back to AppData
|
|
618
|
-
*/
|
|
619
|
-
marshalFromView()
|
|
620
|
-
{
|
|
621
|
-
if (this.options.FlowDataAddress)
|
|
622
|
-
{
|
|
623
|
-
const tmpAddressSpace =
|
|
624
|
-
{
|
|
625
|
-
Fable: this.fable,
|
|
626
|
-
Pict: this.pict || this.fable,
|
|
627
|
-
AppData: this.pict ? this.pict.AppData : this.fable.AppData,
|
|
628
|
-
Bundle: this.Bundle,
|
|
629
|
-
Options: this.options
|
|
630
|
-
};
|
|
631
|
-
this.fable.manifest.setValueByHash(tmpAddressSpace, this.options.FlowDataAddress, JSON.parse(JSON.stringify(this._FlowData)));
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
/**
|
|
636
|
-
* Get the complete flow data object
|
|
637
|
-
* @returns {Object} The flow data including nodes, connections, and view state
|
|
638
|
-
*/
|
|
639
|
-
getFlowData()
|
|
640
|
-
{
|
|
641
|
-
return JSON.parse(JSON.stringify(this._FlowData));
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
/**
|
|
645
|
-
* Set the complete flow data object and re-render
|
|
646
|
-
* @param {Object} pFlowData - The flow data to set
|
|
647
|
-
*/
|
|
648
|
-
setFlowData(pFlowData)
|
|
649
|
-
{
|
|
650
|
-
if (typeof pFlowData !== 'object' || pFlowData === null)
|
|
651
|
-
{
|
|
652
|
-
this.log.warn('PictSectionFlow setFlowData received invalid data');
|
|
653
|
-
return;
|
|
654
|
-
}
|
|
655
|
-
|
|
656
|
-
this._FlowData = {
|
|
657
|
-
Nodes: Array.isArray(pFlowData.Nodes) ? pFlowData.Nodes : [],
|
|
658
|
-
Connections: Array.isArray(pFlowData.Connections) ? pFlowData.Connections : [],
|
|
659
|
-
OpenPanels: Array.isArray(pFlowData.OpenPanels) ? pFlowData.OpenPanels : [],
|
|
660
|
-
SavedLayouts: Array.isArray(pFlowData.SavedLayouts) ? pFlowData.SavedLayouts : [],
|
|
661
|
-
ViewState: Object.assign(
|
|
662
|
-
{ PanX: 0, PanY: 0, Zoom: 1, SelectedNodeHash: null, SelectedConnectionHash: null, SelectedTetherHash: null },
|
|
663
|
-
pFlowData.ViewState || {}
|
|
664
|
-
)
|
|
665
|
-
};
|
|
666
|
-
|
|
667
|
-
// Merge any browser-persisted layouts into the newly loaded data
|
|
668
|
-
if (this._LayoutProvider)
|
|
669
|
-
{
|
|
670
|
-
this._LayoutProvider.loadPersistedLayouts();
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
if (this.initialRenderComplete)
|
|
674
|
-
{
|
|
675
|
-
this.renderFlow();
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
/**
|
|
680
|
-
* Add a new node to the flow
|
|
681
|
-
* @param {string} pType - The node type hash
|
|
682
|
-
* @param {number} pX - X position
|
|
683
|
-
* @param {number} pY - Y position
|
|
684
|
-
* @param {string} [pTitle] - Optional title
|
|
685
|
-
* @param {Object} [pData] - Optional additional data
|
|
686
|
-
* @returns {Object} The created node
|
|
687
|
-
*/
|
|
688
|
-
addNode(pType, pX, pY, pTitle, pData)
|
|
689
|
-
{
|
|
690
|
-
let tmpType = pType || this.options.DefaultNodeType;
|
|
691
|
-
let tmpNodeTypeConfig = this._NodeTypeProvider.getNodeType(tmpType);
|
|
692
|
-
|
|
693
|
-
let tmpNodeHash = `node-${this.fable.getUUID()}`;
|
|
694
|
-
let tmpNode =
|
|
695
|
-
{
|
|
696
|
-
Hash: tmpNodeHash,
|
|
697
|
-
Type: tmpType,
|
|
698
|
-
X: pX || 100,
|
|
699
|
-
Y: pY || 100,
|
|
700
|
-
Width: (tmpNodeTypeConfig && tmpNodeTypeConfig.DefaultWidth) || this.options.DefaultNodeWidth,
|
|
701
|
-
Height: (tmpNodeTypeConfig && tmpNodeTypeConfig.DefaultHeight) || this.options.DefaultNodeHeight,
|
|
702
|
-
Title: pTitle || (tmpNodeTypeConfig && tmpNodeTypeConfig.Label) || 'New Node',
|
|
703
|
-
Ports: (tmpNodeTypeConfig && tmpNodeTypeConfig.DefaultPorts)
|
|
704
|
-
? JSON.parse(JSON.stringify(tmpNodeTypeConfig.DefaultPorts))
|
|
705
|
-
: [
|
|
706
|
-
{ Hash: `port-in-${this.fable.getUUID()}`, Direction: 'input', Side: 'left', Label: 'In' },
|
|
707
|
-
{ Hash: `port-out-${this.fable.getUUID()}`, Direction: 'output', Side: 'right', Label: 'Out' }
|
|
708
|
-
],
|
|
709
|
-
Data: pData || {}
|
|
710
|
-
};
|
|
711
|
-
|
|
712
|
-
// Ensure each port has a unique hash
|
|
713
|
-
for (let i = 0; i < tmpNode.Ports.length; i++)
|
|
714
|
-
{
|
|
715
|
-
if (!tmpNode.Ports[i].Hash)
|
|
716
|
-
{
|
|
717
|
-
tmpNode.Ports[i].Hash = `port-${tmpNode.Ports[i].Direction}-${this.fable.getUUID()}`;
|
|
718
|
-
}
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
this._FlowData.Nodes.push(tmpNode);
|
|
722
|
-
this.renderFlow();
|
|
723
|
-
this.marshalFromView();
|
|
724
|
-
|
|
725
|
-
if (this._EventHandlerProvider)
|
|
726
|
-
{
|
|
727
|
-
this._EventHandlerProvider.fireEvent('onNodeAdded', tmpNode);
|
|
728
|
-
this._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowData);
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
return tmpNode;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
/**
|
|
735
|
-
* Remove a node and all its connections
|
|
736
|
-
* @param {string} pNodeHash - The hash of the node to remove
|
|
737
|
-
* @returns {boolean} Whether the node was removed
|
|
738
|
-
*/
|
|
739
|
-
removeNode(pNodeHash)
|
|
740
|
-
{
|
|
741
|
-
let tmpNodeIndex = this._FlowData.Nodes.findIndex((pNode) => pNode.Hash === pNodeHash);
|
|
742
|
-
if (tmpNodeIndex < 0)
|
|
743
|
-
{
|
|
744
|
-
this.log.warn(`PictSectionFlow removeNode: node ${pNodeHash} not found`);
|
|
745
|
-
return false;
|
|
746
|
-
}
|
|
747
|
-
|
|
748
|
-
let tmpRemovedNode = this._FlowData.Nodes.splice(tmpNodeIndex, 1)[0];
|
|
749
|
-
|
|
750
|
-
// Remove all connections involving this node
|
|
751
|
-
this._FlowData.Connections = this._FlowData.Connections.filter((pConnection) =>
|
|
752
|
-
{
|
|
753
|
-
return pConnection.SourceNodeHash !== pNodeHash && pConnection.TargetNodeHash !== pNodeHash;
|
|
754
|
-
});
|
|
755
|
-
|
|
756
|
-
// Close any open panels for this node
|
|
757
|
-
this.closePanelForNode(pNodeHash);
|
|
459
|
+
// ---- Data Manager Delegations ----
|
|
758
460
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
if (this._EventHandlerProvider)
|
|
769
|
-
{
|
|
770
|
-
this._EventHandlerProvider.fireEvent('onNodeRemoved', tmpRemovedNode);
|
|
771
|
-
this._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowData);
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
return true;
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
/**
|
|
778
|
-
* Add a connection between two ports
|
|
779
|
-
* @param {string} pSourceNodeHash
|
|
780
|
-
* @param {string} pSourcePortHash
|
|
781
|
-
* @param {string} pTargetNodeHash
|
|
782
|
-
* @param {string} pTargetPortHash
|
|
783
|
-
* @param {Object} [pData] - Optional additional data
|
|
784
|
-
* @returns {Object|false} The created connection, or false if invalid
|
|
785
|
-
*/
|
|
786
|
-
addConnection(pSourceNodeHash, pSourcePortHash, pTargetNodeHash, pTargetPortHash, pData)
|
|
787
|
-
{
|
|
788
|
-
// Validate that both nodes and ports exist
|
|
789
|
-
let tmpSourceNode = this._FlowData.Nodes.find((pNode) => pNode.Hash === pSourceNodeHash);
|
|
790
|
-
let tmpTargetNode = this._FlowData.Nodes.find((pNode) => pNode.Hash === pTargetNodeHash);
|
|
791
|
-
|
|
792
|
-
if (!tmpSourceNode || !tmpTargetNode)
|
|
793
|
-
{
|
|
794
|
-
this.log.warn('PictSectionFlow addConnection: source or target node not found');
|
|
795
|
-
return false;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
let tmpSourcePort = tmpSourceNode.Ports.find((pPort) => pPort.Hash === pSourcePortHash);
|
|
799
|
-
let tmpTargetPort = tmpTargetNode.Ports.find((pPort) => pPort.Hash === pTargetPortHash);
|
|
800
|
-
|
|
801
|
-
if (!tmpSourcePort || !tmpTargetPort)
|
|
802
|
-
{
|
|
803
|
-
this.log.warn('PictSectionFlow addConnection: source or target port not found');
|
|
804
|
-
return false;
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
// Prevent self-connections
|
|
808
|
-
if (pSourceNodeHash === pTargetNodeHash)
|
|
809
|
-
{
|
|
810
|
-
this.log.warn('PictSectionFlow addConnection: cannot connect a node to itself');
|
|
811
|
-
return false;
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
// Check for duplicate connections
|
|
815
|
-
let tmpDuplicate = this._FlowData.Connections.find((pConn) =>
|
|
816
|
-
{
|
|
817
|
-
return pConn.SourceNodeHash === pSourceNodeHash
|
|
818
|
-
&& pConn.SourcePortHash === pSourcePortHash
|
|
819
|
-
&& pConn.TargetNodeHash === pTargetNodeHash
|
|
820
|
-
&& pConn.TargetPortHash === pTargetPortHash;
|
|
821
|
-
});
|
|
822
|
-
if (tmpDuplicate)
|
|
823
|
-
{
|
|
824
|
-
this.log.warn('PictSectionFlow addConnection: duplicate connection');
|
|
825
|
-
return false;
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
let tmpConnection =
|
|
829
|
-
{
|
|
830
|
-
Hash: `conn-${this.fable.getUUID()}`,
|
|
831
|
-
SourceNodeHash: pSourceNodeHash,
|
|
832
|
-
SourcePortHash: pSourcePortHash,
|
|
833
|
-
TargetNodeHash: pTargetNodeHash,
|
|
834
|
-
TargetPortHash: pTargetPortHash,
|
|
835
|
-
Data: pData || {}
|
|
836
|
-
};
|
|
837
|
-
|
|
838
|
-
this._FlowData.Connections.push(tmpConnection);
|
|
839
|
-
this.renderFlow();
|
|
840
|
-
this.marshalFromView();
|
|
841
|
-
|
|
842
|
-
if (this._EventHandlerProvider)
|
|
843
|
-
{
|
|
844
|
-
this._EventHandlerProvider.fireEvent('onConnectionCreated', tmpConnection);
|
|
845
|
-
this._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowData);
|
|
846
|
-
}
|
|
847
|
-
|
|
848
|
-
return tmpConnection;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
/**
|
|
852
|
-
* Remove a connection
|
|
853
|
-
* @param {string} pConnectionHash - The hash of the connection to remove
|
|
854
|
-
* @returns {boolean} Whether the connection was removed
|
|
855
|
-
*/
|
|
856
|
-
removeConnection(pConnectionHash)
|
|
857
|
-
{
|
|
858
|
-
let tmpConnectionIndex = this._FlowData.Connections.findIndex((pConn) => pConn.Hash === pConnectionHash);
|
|
859
|
-
if (tmpConnectionIndex < 0)
|
|
860
|
-
{
|
|
861
|
-
this.log.warn(`PictSectionFlow removeConnection: connection ${pConnectionHash} not found`);
|
|
862
|
-
return false;
|
|
863
|
-
}
|
|
864
|
-
|
|
865
|
-
let tmpRemovedConnection = this._FlowData.Connections.splice(tmpConnectionIndex, 1)[0];
|
|
866
|
-
|
|
867
|
-
if (this._FlowData.ViewState.SelectedConnectionHash === pConnectionHash)
|
|
868
|
-
{
|
|
869
|
-
this._FlowData.ViewState.SelectedConnectionHash = null;
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
this.renderFlow();
|
|
873
|
-
this.marshalFromView();
|
|
874
|
-
|
|
875
|
-
if (this._EventHandlerProvider)
|
|
876
|
-
{
|
|
877
|
-
this._EventHandlerProvider.fireEvent('onConnectionRemoved', tmpRemovedConnection);
|
|
878
|
-
this._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowData);
|
|
879
|
-
}
|
|
880
|
-
|
|
881
|
-
return true;
|
|
882
|
-
}
|
|
461
|
+
marshalToView() { return this._DataManager.marshalToView(); }
|
|
462
|
+
marshalFromView() { return this._DataManager.marshalFromView(); }
|
|
463
|
+
getFlowData() { return this._DataManager.getFlowData(); }
|
|
464
|
+
setFlowData(pFlowData) { return this._DataManager.setFlowData(pFlowData); }
|
|
465
|
+
addNode(pType, pX, pY, pTitle, pData) { return this._DataManager.addNode(pType, pX, pY, pTitle, pData); }
|
|
466
|
+
removeNode(pNodeHash) { return this._DataManager.removeNode(pNodeHash); }
|
|
467
|
+
addConnection(pSourceNodeHash, pSourcePortHash, pTargetNodeHash, pTargetPortHash, pData) { return this._DataManager.addConnection(pSourceNodeHash, pSourcePortHash, pTargetNodeHash, pTargetPortHash, pData); }
|
|
468
|
+
removeConnection(pConnectionHash) { return this._DataManager.removeConnection(pConnectionHash); }
|
|
883
469
|
|
|
884
470
|
/**
|
|
885
471
|
* Select a node
|
|
@@ -1064,34 +650,7 @@ class PictViewFlow extends libPictView
|
|
|
1064
650
|
return 'default';
|
|
1065
651
|
}
|
|
1066
652
|
|
|
1067
|
-
|
|
1068
|
-
* Re-inject SVG marker definitions (arrowheads).
|
|
1069
|
-
* Called after a theme switch to update arrowhead colors.
|
|
1070
|
-
*/
|
|
1071
|
-
_reinjectMarkerDefs()
|
|
1072
|
-
{
|
|
1073
|
-
if (!this._ConnectorShapesProvider || !this._SVGElement) return;
|
|
1074
|
-
|
|
1075
|
-
let tmpViewIdentifier = this.options.ViewIdentifier;
|
|
1076
|
-
let tmpDefs = this._SVGElement.querySelector('defs');
|
|
1077
|
-
if (!tmpDefs) return;
|
|
1078
|
-
|
|
1079
|
-
// Remove existing marker elements
|
|
1080
|
-
let tmpExistingMarkers = tmpDefs.querySelectorAll('marker');
|
|
1081
|
-
for (let i = 0; i < tmpExistingMarkers.length; i++)
|
|
1082
|
-
{
|
|
1083
|
-
tmpExistingMarkers[i].remove();
|
|
1084
|
-
}
|
|
1085
|
-
|
|
1086
|
-
// Re-generate and inject
|
|
1087
|
-
let tmpMarkerMarkup = this._ConnectorShapesProvider.generateMarkerDefs(tmpViewIdentifier);
|
|
1088
|
-
let tmpTempSVG = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
|
|
1089
|
-
tmpTempSVG.innerHTML = tmpMarkerMarkup;
|
|
1090
|
-
while (tmpTempSVG.firstChild)
|
|
1091
|
-
{
|
|
1092
|
-
tmpDefs.appendChild(tmpTempSVG.firstChild);
|
|
1093
|
-
}
|
|
1094
|
-
}
|
|
653
|
+
_reinjectMarkerDefs() { return this._RenderManager.reinjectMarkerDefs(); }
|
|
1095
654
|
|
|
1096
655
|
/**
|
|
1097
656
|
* Get a node by hash
|
|
@@ -1122,74 +681,19 @@ class PictViewFlow extends libPictView
|
|
|
1122
681
|
return this._SelectionManager.selectTether(pPanelHash);
|
|
1123
682
|
}
|
|
1124
683
|
|
|
1125
|
-
|
|
1126
|
-
* Update a connection handle position during drag (for real-time feedback).
|
|
1127
|
-
* @param {string} pConnectionHash
|
|
1128
|
-
* @param {string} pHandleType - 'bezier-midpoint', 'ortho-corner1', 'ortho-corner2', 'ortho-midpoint'
|
|
1129
|
-
* @param {number} pX
|
|
1130
|
-
* @param {number} pY
|
|
1131
|
-
*/
|
|
1132
|
-
updateConnectionHandle(pConnectionHash, pHandleType, pX, pY)
|
|
1133
|
-
{
|
|
1134
|
-
let tmpConnection = this.getConnection(pConnectionHash);
|
|
1135
|
-
if (!tmpConnection) return;
|
|
1136
|
-
|
|
1137
|
-
if (!tmpConnection.Data) tmpConnection.Data = {};
|
|
1138
|
-
tmpConnection.Data.HandleCustomized = true;
|
|
1139
|
-
|
|
1140
|
-
switch (pHandleType)
|
|
1141
|
-
{
|
|
1142
|
-
case 'bezier-midpoint':
|
|
1143
|
-
tmpConnection.Data.BezierHandleX = pX;
|
|
1144
|
-
tmpConnection.Data.BezierHandleY = pY;
|
|
1145
|
-
break;
|
|
1146
|
-
|
|
1147
|
-
case 'ortho-corner1':
|
|
1148
|
-
tmpConnection.Data.OrthoCorner1X = pX;
|
|
1149
|
-
tmpConnection.Data.OrthoCorner1Y = pY;
|
|
1150
|
-
break;
|
|
1151
|
-
|
|
1152
|
-
case 'ortho-corner2':
|
|
1153
|
-
tmpConnection.Data.OrthoCorner2X = pX;
|
|
1154
|
-
tmpConnection.Data.OrthoCorner2Y = pY;
|
|
1155
|
-
break;
|
|
1156
|
-
|
|
1157
|
-
case 'ortho-midpoint':
|
|
1158
|
-
{
|
|
1159
|
-
// Midpoint drag shifts the corridor offset
|
|
1160
|
-
let tmpSourcePos = this.getPortPosition(tmpConnection.SourceNodeHash, tmpConnection.SourcePortHash);
|
|
1161
|
-
let tmpTargetPos = this.getPortPosition(tmpConnection.TargetNodeHash, tmpConnection.TargetPortHash);
|
|
1162
|
-
if (tmpSourcePos && tmpTargetPos)
|
|
1163
|
-
{
|
|
1164
|
-
let tmpGeom = this._ConnectionRenderer._computeDirectionalGeometry(tmpSourcePos, tmpTargetPos);
|
|
1165
|
-
let tmpStartDir = tmpGeom.startDir;
|
|
1166
|
-
|
|
1167
|
-
// Compute offset along the corridor axis
|
|
1168
|
-
if (Math.abs(tmpStartDir.dx) > Math.abs(tmpStartDir.dy))
|
|
1169
|
-
{
|
|
1170
|
-
// Horizontal departure — corridor is vertical, shift is along X
|
|
1171
|
-
let tmpAutoMidX = (tmpGeom.departX + tmpGeom.approachX) / 2;
|
|
1172
|
-
tmpConnection.Data.OrthoMidOffset = pX - tmpAutoMidX;
|
|
1173
|
-
}
|
|
1174
|
-
else
|
|
1175
|
-
{
|
|
1176
|
-
// Vertical departure — corridor is horizontal, shift is along Y
|
|
1177
|
-
let tmpAutoMidY = (tmpGeom.departY + tmpGeom.approachY) / 2;
|
|
1178
|
-
tmpConnection.Data.OrthoMidOffset = pY - tmpAutoMidY;
|
|
1179
|
-
}
|
|
1180
|
-
}
|
|
1181
|
-
break;
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
684
|
+
// ---- Connection Handle Manager Delegations ----
|
|
1184
685
|
|
|
1185
|
-
|
|
1186
|
-
}
|
|
686
|
+
updateConnectionHandle(pConnectionHash, pHandleType, pX, pY) { return this._ConnectionHandleManager.updateConnectionHandle(pConnectionHash, pHandleType, pX, pY); }
|
|
687
|
+
addConnectionHandle(pConnectionHash, pX, pY) { return this._ConnectionHandleManager.addConnectionHandle(pConnectionHash, pX, pY); }
|
|
688
|
+
removeConnectionHandle(pConnectionHash, pIndex) { return this._ConnectionHandleManager.removeConnectionHandle(pConnectionHash, pIndex); }
|
|
689
|
+
_resetHandlesForNode(pNodeHash) { return this._ConnectionHandleManager.resetHandlesForNode(pNodeHash); }
|
|
690
|
+
_resetHandlesForPanel(pPanelHash) { return this._ConnectionHandleManager.resetHandlesForPanel(pPanelHash); }
|
|
1187
691
|
|
|
1188
692
|
/**
|
|
1189
693
|
* Update a tether handle position during drag (for real-time feedback).
|
|
1190
694
|
* Delegates state update to the TetherService.
|
|
1191
695
|
* @param {string} pPanelHash
|
|
1192
|
-
* @param {string} pHandleType
|
|
696
|
+
* @param {string} pHandleType
|
|
1193
697
|
* @param {number} pX
|
|
1194
698
|
* @param {number} pY
|
|
1195
699
|
*/
|
|
@@ -1206,103 +710,6 @@ class PictViewFlow extends libPictView
|
|
|
1206
710
|
this._renderSingleTether(pPanelHash);
|
|
1207
711
|
}
|
|
1208
712
|
|
|
1209
|
-
/**
|
|
1210
|
-
* Re-render a single connection (remove and re-add) for smooth drag performance.
|
|
1211
|
-
* @param {string} pConnectionHash
|
|
1212
|
-
*/
|
|
1213
|
-
_renderSingleConnection(pConnectionHash)
|
|
1214
|
-
{
|
|
1215
|
-
if (!this._ConnectionsLayer) return;
|
|
1216
|
-
|
|
1217
|
-
// Remove existing elements for this connection
|
|
1218
|
-
let tmpExisting = this._ConnectionsLayer.querySelectorAll(`[data-connection-hash="${pConnectionHash}"]`);
|
|
1219
|
-
for (let i = 0; i < tmpExisting.length; i++)
|
|
1220
|
-
{
|
|
1221
|
-
tmpExisting[i].remove();
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
let tmpConnection = this.getConnection(pConnectionHash);
|
|
1225
|
-
if (!tmpConnection) return;
|
|
1226
|
-
|
|
1227
|
-
let tmpIsSelected = (this._FlowData.ViewState.SelectedConnectionHash === pConnectionHash);
|
|
1228
|
-
this._ConnectionRenderer.renderConnection(tmpConnection, this._ConnectionsLayer, tmpIsSelected);
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
/**
|
|
1232
|
-
* Re-render a single tether (remove and re-add) for smooth drag performance.
|
|
1233
|
-
* @param {string} pPanelHash
|
|
1234
|
-
*/
|
|
1235
|
-
_renderSingleTether(pPanelHash)
|
|
1236
|
-
{
|
|
1237
|
-
if (!this._TethersLayer || !this._TetherService) return;
|
|
1238
|
-
|
|
1239
|
-
// Remove existing tether elements for this panel
|
|
1240
|
-
let tmpExisting = this._TethersLayer.querySelectorAll(`[data-panel-hash="${pPanelHash}"]`);
|
|
1241
|
-
for (let i = 0; i < tmpExisting.length; i++)
|
|
1242
|
-
{
|
|
1243
|
-
tmpExisting[i].remove();
|
|
1244
|
-
}
|
|
1245
|
-
|
|
1246
|
-
let tmpPanel = this._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);
|
|
1247
|
-
if (!tmpPanel) return;
|
|
1248
|
-
|
|
1249
|
-
let tmpNodeData = this.getNode(tmpPanel.NodeHash);
|
|
1250
|
-
if (!tmpNodeData) return;
|
|
1251
|
-
|
|
1252
|
-
let tmpIsSelected = (this._FlowData.ViewState.SelectedTetherHash === pPanelHash);
|
|
1253
|
-
this._TetherService.renderTether(tmpPanel, tmpNodeData, this._TethersLayer, tmpIsSelected, this.options.ViewIdentifier);
|
|
1254
|
-
}
|
|
1255
|
-
|
|
1256
|
-
/**
|
|
1257
|
-
* Reset handle positions for all connections/tethers involving a node.
|
|
1258
|
-
* Called when a node moves. Preserves LineMode but resets handle coordinates to auto.
|
|
1259
|
-
* @param {string} pNodeHash
|
|
1260
|
-
*/
|
|
1261
|
-
_resetHandlesForNode(pNodeHash)
|
|
1262
|
-
{
|
|
1263
|
-
// Reset connection handles
|
|
1264
|
-
for (let i = 0; i < this._FlowData.Connections.length; i++)
|
|
1265
|
-
{
|
|
1266
|
-
let tmpConn = this._FlowData.Connections[i];
|
|
1267
|
-
if (tmpConn.SourceNodeHash === pNodeHash || tmpConn.TargetNodeHash === pNodeHash)
|
|
1268
|
-
{
|
|
1269
|
-
if (tmpConn.Data && tmpConn.Data.HandleCustomized)
|
|
1270
|
-
{
|
|
1271
|
-
tmpConn.Data.HandleCustomized = false;
|
|
1272
|
-
tmpConn.Data.BezierHandleX = null;
|
|
1273
|
-
tmpConn.Data.BezierHandleY = null;
|
|
1274
|
-
tmpConn.Data.OrthoCorner1X = null;
|
|
1275
|
-
tmpConn.Data.OrthoCorner1Y = null;
|
|
1276
|
-
tmpConn.Data.OrthoCorner2X = null;
|
|
1277
|
-
tmpConn.Data.OrthoCorner2Y = null;
|
|
1278
|
-
tmpConn.Data.OrthoMidOffset = 0;
|
|
1279
|
-
}
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
// Reset tether handles for panels attached to this node
|
|
1284
|
-
if (this._TetherService)
|
|
1285
|
-
{
|
|
1286
|
-
this._TetherService.resetHandlesForNode(this._FlowData.OpenPanels, pNodeHash);
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
|
-
|
|
1290
|
-
/**
|
|
1291
|
-
* Reset tether handle positions for a specific panel.
|
|
1292
|
-
* Called when a panel is dragged.
|
|
1293
|
-
* @param {string} pPanelHash
|
|
1294
|
-
*/
|
|
1295
|
-
_resetHandlesForPanel(pPanelHash)
|
|
1296
|
-
{
|
|
1297
|
-
let tmpPanel = this._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);
|
|
1298
|
-
if (!tmpPanel) return;
|
|
1299
|
-
|
|
1300
|
-
if (this._TetherService)
|
|
1301
|
-
{
|
|
1302
|
-
this._TetherService.resetHandlePositions(tmpPanel);
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
713
|
/**
|
|
1307
714
|
* Get a port's absolute position in SVG coordinates.
|
|
1308
715
|
*
|
|
@@ -1341,7 +748,10 @@ class PictViewFlow extends libPictView
|
|
|
1341
748
|
}
|
|
1342
749
|
}
|
|
1343
750
|
|
|
1344
|
-
|
|
751
|
+
// Build port counts map for adaptive zone sizing
|
|
752
|
+
let tmpPortCountsBySide = this._GeometryProvider.buildPortCountsBySide(tmpNode.Ports);
|
|
753
|
+
|
|
754
|
+
let tmpLocal = this._GeometryProvider.getPortLocalPosition(tmpPort.Side, tmpPortIndex, tmpPortCount, tmpNode.Width, tmpHeight, tmpTitleBarHeight, tmpPortCountsBySide);
|
|
1345
755
|
|
|
1346
756
|
return { x: tmpNode.X + tmpLocal.x, y: tmpNode.Y + tmpLocal.y, side: tmpPort.Side || 'right' };
|
|
1347
757
|
}
|
|
@@ -1357,176 +767,14 @@ class PictViewFlow extends libPictView
|
|
|
1357
767
|
return this._ViewportManager.screenToSVGCoords(pScreenX, pScreenY);
|
|
1358
768
|
}
|
|
1359
769
|
|
|
1360
|
-
|
|
1361
|
-
* Render the complete flow diagram
|
|
1362
|
-
*/
|
|
1363
|
-
renderFlow()
|
|
1364
|
-
{
|
|
1365
|
-
if (!this._NodesLayer || !this._ConnectionsLayer) return;
|
|
1366
|
-
|
|
1367
|
-
// Clear existing SVG content
|
|
1368
|
-
while (this._NodesLayer.firstChild)
|
|
1369
|
-
{
|
|
1370
|
-
this._NodesLayer.removeChild(this._NodesLayer.firstChild);
|
|
1371
|
-
}
|
|
1372
|
-
while (this._ConnectionsLayer.firstChild)
|
|
1373
|
-
{
|
|
1374
|
-
this._ConnectionsLayer.removeChild(this._ConnectionsLayer.firstChild);
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
// Render connections first (behind nodes)
|
|
1378
|
-
for (let i = 0; i < this._FlowData.Connections.length; i++)
|
|
1379
|
-
{
|
|
1380
|
-
let tmpConnection = this._FlowData.Connections[i];
|
|
1381
|
-
let tmpIsSelected = (this._FlowData.ViewState.SelectedConnectionHash === tmpConnection.Hash);
|
|
1382
|
-
|
|
1383
|
-
this._ConnectionRenderer.renderConnection(
|
|
1384
|
-
tmpConnection,
|
|
1385
|
-
this._ConnectionsLayer,
|
|
1386
|
-
tmpIsSelected
|
|
1387
|
-
);
|
|
1388
|
-
}
|
|
1389
|
-
|
|
1390
|
-
// Render nodes
|
|
1391
|
-
for (let i = 0; i < this._FlowData.Nodes.length; i++)
|
|
1392
|
-
{
|
|
1393
|
-
let tmpNode = this._FlowData.Nodes[i];
|
|
1394
|
-
let tmpIsSelected = (this._FlowData.ViewState.SelectedNodeHash === tmpNode.Hash);
|
|
1395
|
-
let tmpNodeTypeConfig = this._NodeTypeProvider.getNodeType(tmpNode.Type);
|
|
1396
|
-
|
|
1397
|
-
// Enrich saved port data with metadata from the node type's DefaultPorts.
|
|
1398
|
-
// Saved flow data may not include PortType or may have stale Side values,
|
|
1399
|
-
// so we match each port to its DefaultPort counterpart by Label and Direction,
|
|
1400
|
-
// then copy over PortType and Side from the authoritative node type definition.
|
|
1401
|
-
if (tmpNodeTypeConfig && tmpNodeTypeConfig.DefaultPorts && tmpNode.Ports)
|
|
1402
|
-
{
|
|
1403
|
-
for (let p = 0; p < tmpNode.Ports.length; p++)
|
|
1404
|
-
{
|
|
1405
|
-
let tmpPort = tmpNode.Ports[p];
|
|
1406
|
-
for (let d = 0; d < tmpNodeTypeConfig.DefaultPorts.length; d++)
|
|
1407
|
-
{
|
|
1408
|
-
let tmpDefault = tmpNodeTypeConfig.DefaultPorts[d];
|
|
1409
|
-
if (tmpDefault.Label === tmpPort.Label && tmpDefault.Direction === tmpPort.Direction)
|
|
1410
|
-
{
|
|
1411
|
-
if (tmpDefault.PortType)
|
|
1412
|
-
{
|
|
1413
|
-
tmpPort.PortType = tmpDefault.PortType;
|
|
1414
|
-
}
|
|
1415
|
-
if (tmpDefault.Side)
|
|
1416
|
-
{
|
|
1417
|
-
tmpPort.Side = tmpDefault.Side;
|
|
1418
|
-
}
|
|
1419
|
-
break;
|
|
1420
|
-
}
|
|
1421
|
-
}
|
|
1422
|
-
}
|
|
1423
|
-
}
|
|
1424
|
-
|
|
1425
|
-
this._NodeView.renderNode(tmpNode, this._NodesLayer, tmpIsSelected, tmpNodeTypeConfig);
|
|
1426
|
-
}
|
|
770
|
+
// ---- Render Manager Delegations ----
|
|
1427
771
|
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
// Update viewport transform
|
|
1435
|
-
this.updateViewportTransform();
|
|
1436
|
-
}
|
|
1437
|
-
|
|
1438
|
-
/**
|
|
1439
|
-
* Update a single node's position in the SVG without full re-render (for drag performance)
|
|
1440
|
-
* @param {string} pNodeHash
|
|
1441
|
-
* @param {number} pX
|
|
1442
|
-
* @param {number} pY
|
|
1443
|
-
*/
|
|
1444
|
-
updateNodePosition(pNodeHash, pX, pY)
|
|
1445
|
-
{
|
|
1446
|
-
let tmpNode = this.getNode(pNodeHash);
|
|
1447
|
-
if (!tmpNode) return;
|
|
1448
|
-
|
|
1449
|
-
if (this.options.EnableGridSnap)
|
|
1450
|
-
{
|
|
1451
|
-
pX = this._LayoutService.snapToGrid(pX, this.options.GridSnapSize);
|
|
1452
|
-
pY = this._LayoutService.snapToGrid(pY, this.options.GridSnapSize);
|
|
1453
|
-
}
|
|
1454
|
-
|
|
1455
|
-
tmpNode.X = pX;
|
|
1456
|
-
tmpNode.Y = pY;
|
|
1457
|
-
|
|
1458
|
-
// Reset customized handle positions for connections/tethers involving this node
|
|
1459
|
-
this._resetHandlesForNode(pNodeHash);
|
|
1460
|
-
|
|
1461
|
-
// Update the node's SVG group transform for smooth dragging
|
|
1462
|
-
let tmpNodeGroup = this._NodesLayer.querySelector(`[data-node-hash="${pNodeHash}"]`);
|
|
1463
|
-
if (tmpNodeGroup)
|
|
1464
|
-
{
|
|
1465
|
-
tmpNodeGroup.setAttribute('transform', `translate(${pX}, ${pY})`);
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
// Re-render connections that involve this node
|
|
1469
|
-
this._renderConnectionsForNode(pNodeHash);
|
|
1470
|
-
|
|
1471
|
-
// Update tethers for any panels attached to this node
|
|
1472
|
-
this._renderTethersForNode(pNodeHash);
|
|
1473
|
-
}
|
|
1474
|
-
|
|
1475
|
-
/**
|
|
1476
|
-
* Re-render only connections that involve a specific node (for drag performance)
|
|
1477
|
-
* @param {string} pNodeHash
|
|
1478
|
-
*/
|
|
1479
|
-
_renderConnectionsForNode(pNodeHash)
|
|
1480
|
-
{
|
|
1481
|
-
let tmpAffectedConnections = this._FlowData.Connections.filter((pConn) =>
|
|
1482
|
-
{
|
|
1483
|
-
return pConn.SourceNodeHash === pNodeHash || pConn.TargetNodeHash === pNodeHash;
|
|
1484
|
-
});
|
|
1485
|
-
|
|
1486
|
-
for (let i = 0; i < tmpAffectedConnections.length; i++)
|
|
1487
|
-
{
|
|
1488
|
-
let tmpConn = tmpAffectedConnections[i];
|
|
1489
|
-
let tmpIsSelected = (this._FlowData.ViewState.SelectedConnectionHash === tmpConn.Hash);
|
|
1490
|
-
|
|
1491
|
-
// Remove existing connection SVG elements
|
|
1492
|
-
let tmpExisting = this._ConnectionsLayer.querySelectorAll(`[data-connection-hash="${tmpConn.Hash}"]`);
|
|
1493
|
-
for (let j = 0; j < tmpExisting.length; j++)
|
|
1494
|
-
{
|
|
1495
|
-
tmpExisting[j].remove();
|
|
1496
|
-
}
|
|
1497
|
-
|
|
1498
|
-
// Re-render this connection
|
|
1499
|
-
this._ConnectionRenderer.renderConnection(tmpConn, this._ConnectionsLayer, tmpIsSelected);
|
|
1500
|
-
}
|
|
1501
|
-
}
|
|
1502
|
-
|
|
1503
|
-
/**
|
|
1504
|
-
* Re-render tethers for panels attached to a specific node (for drag performance).
|
|
1505
|
-
* @param {string} pNodeHash
|
|
1506
|
-
*/
|
|
1507
|
-
_renderTethersForNode(pNodeHash)
|
|
1508
|
-
{
|
|
1509
|
-
if (!this._TethersLayer || !this._TetherService) return;
|
|
1510
|
-
|
|
1511
|
-
let tmpAffectedPanels = this._FlowData.OpenPanels.filter((pPanel) => pPanel.NodeHash === pNodeHash);
|
|
1512
|
-
if (tmpAffectedPanels.length === 0) return;
|
|
1513
|
-
|
|
1514
|
-
// Remove existing tethers for these panels and re-render via TetherService
|
|
1515
|
-
for (let i = 0; i < tmpAffectedPanels.length; i++)
|
|
1516
|
-
{
|
|
1517
|
-
let tmpExisting = this._TethersLayer.querySelectorAll(`[data-panel-hash="${tmpAffectedPanels[i].Hash}"]`);
|
|
1518
|
-
for (let j = 0; j < tmpExisting.length; j++)
|
|
1519
|
-
{
|
|
1520
|
-
tmpExisting[j].remove();
|
|
1521
|
-
}
|
|
1522
|
-
|
|
1523
|
-
let tmpNodeData = this.getNode(tmpAffectedPanels[i].NodeHash);
|
|
1524
|
-
if (!tmpNodeData) continue;
|
|
1525
|
-
|
|
1526
|
-
let tmpIsSelected = (this._FlowData.ViewState.SelectedTetherHash === tmpAffectedPanels[i].Hash);
|
|
1527
|
-
this._TetherService.renderTether(tmpAffectedPanels[i], tmpNodeData, this._TethersLayer, tmpIsSelected, this.options.ViewIdentifier);
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
772
|
+
renderFlow() { return this._RenderManager.renderFlow(); }
|
|
773
|
+
_renderSingleConnection(pConnectionHash) { return this._RenderManager.renderSingleConnection(pConnectionHash); }
|
|
774
|
+
_renderSingleTether(pPanelHash) { return this._RenderManager.renderSingleTether(pPanelHash); }
|
|
775
|
+
updateNodePosition(pNodeHash, pX, pY) { return this._RenderManager.updateNodePosition(pNodeHash, pX, pY); }
|
|
776
|
+
_renderConnectionsForNode(pNodeHash) { return this._RenderManager.renderConnectionsForNode(pNodeHash); }
|
|
777
|
+
_renderTethersForNode(pNodeHash) { return this._RenderManager.renderTethersForNode(pNodeHash); }
|
|
1530
778
|
|
|
1531
779
|
// ---- Properties Panel Management ----
|
|
1532
780
|
|