pict-section-flow 0.0.1 → 0.0.2
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/docs/README.md +19 -0
- package/{example_application → example_applications/simple_cards}/html/index.html +2 -2
- package/example_applications/simple_cards/package.json +43 -0
- package/example_applications/simple_cards/source/Pict-Application-FlowExample.js +434 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Each.js +36 -0
- package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +54 -0
- package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +48 -0
- package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +35 -0
- package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +47 -0
- package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +53 -0
- package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +95 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +37 -0
- package/example_applications/simple_cards/source/views/PictView-FlowExample-FileWriteInfo.js +59 -0
- package/{example_application → example_applications/simple_cards}/source/views/PictView-FlowExample-Layout.js +5 -1
- package/example_applications/simple_cards/source/views/PictView-FlowExample-MainWorkspace.js +312 -0
- package/package.json +6 -6
- package/source/Pict-Section-Flow.js +19 -0
- package/source/PictFlowCard.js +207 -0
- package/source/PictFlowCardPropertiesPanel.js +105 -0
- package/source/panels/FlowCardPropertiesPanel-Form.js +174 -0
- package/source/panels/FlowCardPropertiesPanel-Markdown.js +148 -0
- package/source/panels/FlowCardPropertiesPanel-Template.js +88 -0
- package/source/panels/FlowCardPropertiesPanel-View.js +114 -0
- package/source/providers/PictProvider-Flow-EventHandler.js +19 -8
- package/source/providers/PictProvider-Flow-Geometry.js +64 -0
- package/source/providers/PictProvider-Flow-Layouts.js +284 -0
- package/source/providers/PictProvider-Flow-NodeTypes.js +70 -0
- package/source/providers/PictProvider-Flow-PanelChrome.js +72 -0
- package/source/providers/PictProvider-Flow-SVGHelpers.js +30 -0
- package/source/services/PictService-Flow-ConnectionRenderer.js +324 -66
- package/source/services/PictService-Flow-InteractionManager.js +399 -75
- package/source/services/PictService-Flow-Layout.js +159 -0
- package/source/services/PictService-Flow-PathGenerator.js +199 -0
- package/source/services/PictService-Flow-Tether.js +544 -0
- package/source/views/PictView-Flow-Node.js +95 -18
- package/source/views/PictView-Flow-PropertiesPanel.js +435 -0
- package/source/views/PictView-Flow-Toolbar.js +491 -5
- package/source/views/PictView-Flow.js +830 -8
- package/example_application/package.json +0 -41
- package/example_application/source/Pict-Application-FlowExample.js +0 -241
- package/example_application/source/views/PictView-FlowExample-MainWorkspace.js +0 -191
- /package/{example_application → example_applications/simple_cards}/css/flowexample.css +0 -0
- /package/{example_application → example_applications/simple_cards}/source/Pict-Application-FlowExample-Configuration.json +0 -0
- /package/{example_application → example_applications/simple_cards}/source/providers/PictRouter-FlowExample-Configuration.json +0 -0
- /package/{example_application → example_applications/simple_cards}/source/views/PictView-FlowExample-About.js +0 -0
- /package/{example_application → example_applications/simple_cards}/source/views/PictView-FlowExample-BottomBar.js +0 -0
- /package/{example_application → example_applications/simple_cards}/source/views/PictView-FlowExample-Documentation.js +0 -0
- /package/{example_application → example_applications/simple_cards}/source/views/PictView-FlowExample-TopBar.js +0 -0
|
@@ -7,6 +7,8 @@ const INTERACTION_STATES =
|
|
|
7
7
|
{
|
|
8
8
|
IDLE: 'idle',
|
|
9
9
|
DRAGGING_NODE: 'dragging-node',
|
|
10
|
+
DRAGGING_PANEL: 'dragging-panel',
|
|
11
|
+
DRAGGING_HANDLE: 'dragging-handle',
|
|
10
12
|
CONNECTING: 'connecting',
|
|
11
13
|
PANNING: 'panning'
|
|
12
14
|
};
|
|
@@ -34,6 +36,19 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
34
36
|
this._DragNodeStartX = 0;
|
|
35
37
|
this._DragNodeStartY = 0;
|
|
36
38
|
|
|
39
|
+
// Panel drag state
|
|
40
|
+
this._DragPanelHash = null;
|
|
41
|
+
this._DragPanelStartX = 0;
|
|
42
|
+
this._DragPanelStartY = 0;
|
|
43
|
+
this._DragPanelDataStartX = 0;
|
|
44
|
+
this._DragPanelDataStartY = 0;
|
|
45
|
+
|
|
46
|
+
// Handle drag state
|
|
47
|
+
this._DragHandleConnectionHash = null;
|
|
48
|
+
this._DragHandlePanelHash = null;
|
|
49
|
+
this._DragHandleType = null;
|
|
50
|
+
this._DragHandleIsTether = false;
|
|
51
|
+
|
|
37
52
|
// Pan state
|
|
38
53
|
this._PanStartX = 0;
|
|
39
54
|
this._PanStartY = 0;
|
|
@@ -45,6 +60,16 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
45
60
|
this._ConnectSourcePortHash = null;
|
|
46
61
|
this._ConnectDragLine = null;
|
|
47
62
|
|
|
63
|
+
// Double-click detection
|
|
64
|
+
this._LastClickTime = 0;
|
|
65
|
+
this._LastClickNodeHash = null;
|
|
66
|
+
this._DoubleClickThreshold = 400;
|
|
67
|
+
|
|
68
|
+
// Double-click detection for handles
|
|
69
|
+
this._LastHandleClickTime = 0;
|
|
70
|
+
this._LastHandleClickHash = null;
|
|
71
|
+
this._LastHandleClickType = null;
|
|
72
|
+
|
|
48
73
|
// Bound event handlers (for removeEventListener)
|
|
49
74
|
this._boundOnPointerDown = this._onPointerDown.bind(this);
|
|
50
75
|
this._boundOnPointerMove = this._onPointerMove.bind(this);
|
|
@@ -110,6 +135,12 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
110
135
|
let tmpTarget = pEvent.target;
|
|
111
136
|
let tmpElementType = this._getElementType(tmpTarget);
|
|
112
137
|
|
|
138
|
+
// Check if click is inside a panel body — let HTML handle its own events
|
|
139
|
+
if (tmpTarget.closest && tmpTarget.closest('.pict-flow-panel-body'))
|
|
140
|
+
{
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
113
144
|
// Capture pointer for smooth dragging
|
|
114
145
|
this._SVGElement.setPointerCapture(pEvent.pointerId);
|
|
115
146
|
|
|
@@ -121,8 +152,102 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
121
152
|
|
|
122
153
|
case 'node':
|
|
123
154
|
case 'node-body':
|
|
124
|
-
|
|
155
|
+
case 'panel-indicator':
|
|
156
|
+
{
|
|
157
|
+
let tmpNodeHash = this._getNodeHash(tmpTarget);
|
|
158
|
+
let tmpNow = Date.now();
|
|
159
|
+
|
|
160
|
+
// Check for double-click on same node
|
|
161
|
+
if (tmpNodeHash && tmpNodeHash === this._LastClickNodeHash
|
|
162
|
+
&& (tmpNow - this._LastClickTime) < this._DoubleClickThreshold)
|
|
163
|
+
{
|
|
164
|
+
// Double-click: toggle panel
|
|
165
|
+
this._LastClickTime = 0;
|
|
166
|
+
this._LastClickNodeHash = null;
|
|
167
|
+
this._FlowView.togglePanel(tmpNodeHash);
|
|
168
|
+
}
|
|
169
|
+
else
|
|
170
|
+
{
|
|
171
|
+
// Single click: start node drag
|
|
172
|
+
this._LastClickTime = tmpNow;
|
|
173
|
+
this._LastClickNodeHash = tmpNodeHash;
|
|
174
|
+
this._startNodeDrag(pEvent, tmpTarget);
|
|
175
|
+
}
|
|
125
176
|
break;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
case 'panel-titlebar':
|
|
180
|
+
this._startPanelDrag(pEvent, tmpTarget);
|
|
181
|
+
break;
|
|
182
|
+
|
|
183
|
+
case 'panel-close':
|
|
184
|
+
{
|
|
185
|
+
let tmpPanelHash = this._getPanelHash(tmpTarget);
|
|
186
|
+
if (tmpPanelHash)
|
|
187
|
+
{
|
|
188
|
+
this._FlowView.closePanel(tmpPanelHash);
|
|
189
|
+
}
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
case 'connection-handle':
|
|
194
|
+
{
|
|
195
|
+
let tmpConnectionHash = this._getConnectionHash(tmpTarget);
|
|
196
|
+
let tmpHandleType = tmpTarget.getAttribute('data-handle-type');
|
|
197
|
+
let tmpNow = Date.now();
|
|
198
|
+
|
|
199
|
+
// Check for double-click on handle to toggle mode
|
|
200
|
+
if (tmpConnectionHash === this._LastHandleClickHash
|
|
201
|
+
&& tmpHandleType === this._LastHandleClickType
|
|
202
|
+
&& (tmpNow - this._LastHandleClickTime) < this._DoubleClickThreshold)
|
|
203
|
+
{
|
|
204
|
+
this._toggleConnectionLineMode(tmpConnectionHash);
|
|
205
|
+
this._LastHandleClickTime = 0;
|
|
206
|
+
this._LastHandleClickHash = null;
|
|
207
|
+
this._LastHandleClickType = null;
|
|
208
|
+
}
|
|
209
|
+
else
|
|
210
|
+
{
|
|
211
|
+
this._LastHandleClickTime = tmpNow;
|
|
212
|
+
this._LastHandleClickHash = tmpConnectionHash;
|
|
213
|
+
this._LastHandleClickType = tmpHandleType;
|
|
214
|
+
this._startHandleDrag(pEvent, tmpConnectionHash, null, tmpHandleType, false);
|
|
215
|
+
}
|
|
216
|
+
pEvent.stopPropagation();
|
|
217
|
+
break;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
case 'tether':
|
|
221
|
+
case 'tether-hitarea':
|
|
222
|
+
this._selectTether(tmpTarget);
|
|
223
|
+
break;
|
|
224
|
+
|
|
225
|
+
case 'tether-handle':
|
|
226
|
+
{
|
|
227
|
+
let tmpPanelHash = this._getPanelHash(tmpTarget);
|
|
228
|
+
let tmpHandleType = tmpTarget.getAttribute('data-handle-type');
|
|
229
|
+
let tmpNow = Date.now();
|
|
230
|
+
|
|
231
|
+
// Check for double-click on tether handle to toggle mode
|
|
232
|
+
if (tmpPanelHash === this._LastHandleClickHash
|
|
233
|
+
&& tmpHandleType === this._LastHandleClickType
|
|
234
|
+
&& (tmpNow - this._LastHandleClickTime) < this._DoubleClickThreshold)
|
|
235
|
+
{
|
|
236
|
+
this._toggleTetherLineMode(tmpPanelHash);
|
|
237
|
+
this._LastHandleClickTime = 0;
|
|
238
|
+
this._LastHandleClickHash = null;
|
|
239
|
+
this._LastHandleClickType = null;
|
|
240
|
+
}
|
|
241
|
+
else
|
|
242
|
+
{
|
|
243
|
+
this._LastHandleClickTime = tmpNow;
|
|
244
|
+
this._LastHandleClickHash = tmpPanelHash;
|
|
245
|
+
this._LastHandleClickType = tmpHandleType;
|
|
246
|
+
this._startHandleDrag(pEvent, null, tmpPanelHash, tmpHandleType, true);
|
|
247
|
+
}
|
|
248
|
+
pEvent.stopPropagation();
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
126
251
|
|
|
127
252
|
case 'connection':
|
|
128
253
|
case 'connection-hitarea':
|
|
@@ -153,6 +278,14 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
153
278
|
this._onNodeDrag(pEvent);
|
|
154
279
|
break;
|
|
155
280
|
|
|
281
|
+
case INTERACTION_STATES.DRAGGING_PANEL:
|
|
282
|
+
this._onPanelDrag(pEvent);
|
|
283
|
+
break;
|
|
284
|
+
|
|
285
|
+
case INTERACTION_STATES.DRAGGING_HANDLE:
|
|
286
|
+
this._onHandleDrag(pEvent);
|
|
287
|
+
break;
|
|
288
|
+
|
|
156
289
|
case INTERACTION_STATES.CONNECTING:
|
|
157
290
|
this._onConnectionDrag(pEvent);
|
|
158
291
|
break;
|
|
@@ -183,6 +316,14 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
183
316
|
this._endNodeDrag(pEvent);
|
|
184
317
|
break;
|
|
185
318
|
|
|
319
|
+
case INTERACTION_STATES.DRAGGING_PANEL:
|
|
320
|
+
this._endPanelDrag(pEvent);
|
|
321
|
+
break;
|
|
322
|
+
|
|
323
|
+
case INTERACTION_STATES.DRAGGING_HANDLE:
|
|
324
|
+
this._endHandleDrag(pEvent);
|
|
325
|
+
break;
|
|
326
|
+
|
|
186
327
|
case INTERACTION_STATES.CONNECTING:
|
|
187
328
|
this._endConnection(pEvent);
|
|
188
329
|
break;
|
|
@@ -225,11 +366,15 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
225
366
|
// Only handle events when the flow is focused/visible
|
|
226
367
|
if (pEvent.key === 'Delete' || pEvent.key === 'Backspace')
|
|
227
368
|
{
|
|
228
|
-
// Don't delete if user is typing in an input
|
|
369
|
+
// Don't delete if user is typing in an input or inside a panel
|
|
229
370
|
if (pEvent.target && (pEvent.target.tagName === 'INPUT' || pEvent.target.tagName === 'TEXTAREA' || pEvent.target.tagName === 'SELECT'))
|
|
230
371
|
{
|
|
231
372
|
return;
|
|
232
373
|
}
|
|
374
|
+
if (pEvent.target && pEvent.target.closest && pEvent.target.closest('.pict-flow-panel'))
|
|
375
|
+
{
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
233
378
|
|
|
234
379
|
this._FlowView.deleteSelected();
|
|
235
380
|
pEvent.preventDefault();
|
|
@@ -240,17 +385,31 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
240
385
|
{
|
|
241
386
|
this._cancelConnection();
|
|
242
387
|
}
|
|
388
|
+
|
|
389
|
+
// Exit fullscreen if currently in fullscreen mode
|
|
390
|
+
if (this._FlowView._IsFullscreen)
|
|
391
|
+
{
|
|
392
|
+
this._FlowView.exitFullscreen();
|
|
393
|
+
// Update the toolbar button text
|
|
394
|
+
if (this._FlowView._ToolbarView)
|
|
395
|
+
{
|
|
396
|
+
let tmpFlowViewIdentifier = this._FlowView.options.ViewIdentifier;
|
|
397
|
+
let tmpBtnElements = this._FlowView.pict.ContentAssignment.getElement(`#Flow-Toolbar-Fullscreen-${tmpFlowViewIdentifier}`);
|
|
398
|
+
if (tmpBtnElements.length > 0)
|
|
399
|
+
{
|
|
400
|
+
tmpBtnElements[0].innerHTML = '⛶ Fullscreen';
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
pEvent.preventDefault();
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
243
407
|
this._FlowView.deselectAll();
|
|
244
408
|
}
|
|
245
409
|
}
|
|
246
410
|
|
|
247
411
|
// ---- Node Dragging ----
|
|
248
412
|
|
|
249
|
-
/**
|
|
250
|
-
* Start dragging a node
|
|
251
|
-
* @param {PointerEvent} pEvent
|
|
252
|
-
* @param {Element} pTarget
|
|
253
|
-
*/
|
|
254
413
|
_startNodeDrag(pEvent, pTarget)
|
|
255
414
|
{
|
|
256
415
|
if (!this._FlowView.options.EnableNodeDragging) return;
|
|
@@ -258,7 +417,6 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
258
417
|
let tmpNodeHash = this._getNodeHash(pTarget);
|
|
259
418
|
if (!tmpNodeHash) return;
|
|
260
419
|
|
|
261
|
-
// Select the node
|
|
262
420
|
this._FlowView.selectNode(tmpNodeHash);
|
|
263
421
|
|
|
264
422
|
let tmpNode = this._FlowView.getNode(tmpNodeHash);
|
|
@@ -271,7 +429,6 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
271
429
|
this._DragNodeStartX = tmpNode.X;
|
|
272
430
|
this._DragNodeStartY = tmpNode.Y;
|
|
273
431
|
|
|
274
|
-
// Add dragging class
|
|
275
432
|
this._SVGElement.classList.add('panning');
|
|
276
433
|
|
|
277
434
|
let tmpNodeGroup = this._FlowView._NodesLayer.querySelector(`[data-node-hash="${tmpNodeHash}"]`);
|
|
@@ -281,10 +438,6 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
281
438
|
}
|
|
282
439
|
}
|
|
283
440
|
|
|
284
|
-
/**
|
|
285
|
-
* Handle node dragging
|
|
286
|
-
* @param {PointerEvent} pEvent
|
|
287
|
-
*/
|
|
288
441
|
_onNodeDrag(pEvent)
|
|
289
442
|
{
|
|
290
443
|
if (!this._DragNodeHash) return;
|
|
@@ -299,10 +452,6 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
299
452
|
this._FlowView.updateNodePosition(this._DragNodeHash, tmpNewX, tmpNewY);
|
|
300
453
|
}
|
|
301
454
|
|
|
302
|
-
/**
|
|
303
|
-
* End node dragging
|
|
304
|
-
* @param {PointerEvent} pEvent
|
|
305
|
-
*/
|
|
306
455
|
_endNodeDrag(pEvent)
|
|
307
456
|
{
|
|
308
457
|
this._SVGElement.classList.remove('panning');
|
|
@@ -313,7 +462,6 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
313
462
|
tmpNodeGroup.classList.remove('dragging');
|
|
314
463
|
}
|
|
315
464
|
|
|
316
|
-
// Full re-render to finalize positions
|
|
317
465
|
this._FlowView.renderFlow();
|
|
318
466
|
this._FlowView.marshalFromView();
|
|
319
467
|
|
|
@@ -328,13 +476,196 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
328
476
|
this._DragNodeHash = null;
|
|
329
477
|
}
|
|
330
478
|
|
|
479
|
+
// ---- Panel Dragging ----
|
|
480
|
+
|
|
481
|
+
_startPanelDrag(pEvent, pTarget)
|
|
482
|
+
{
|
|
483
|
+
let tmpPanelHash = this._getPanelHash(pTarget);
|
|
484
|
+
if (!tmpPanelHash) return;
|
|
485
|
+
|
|
486
|
+
let tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === tmpPanelHash);
|
|
487
|
+
if (!tmpPanel) return;
|
|
488
|
+
|
|
489
|
+
this._State = INTERACTION_STATES.DRAGGING_PANEL;
|
|
490
|
+
this._DragPanelHash = tmpPanelHash;
|
|
491
|
+
this._DragPanelStartX = pEvent.clientX;
|
|
492
|
+
this._DragPanelStartY = pEvent.clientY;
|
|
493
|
+
this._DragPanelDataStartX = tmpPanel.X;
|
|
494
|
+
this._DragPanelDataStartY = tmpPanel.Y;
|
|
495
|
+
|
|
496
|
+
this._SVGElement.classList.add('panning');
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
_onPanelDrag(pEvent)
|
|
500
|
+
{
|
|
501
|
+
if (!this._DragPanelHash) return;
|
|
502
|
+
|
|
503
|
+
let tmpVS = this._FlowView.viewState;
|
|
504
|
+
let tmpDX = (pEvent.clientX - this._DragPanelStartX) / tmpVS.Zoom;
|
|
505
|
+
let tmpDY = (pEvent.clientY - this._DragPanelStartY) / tmpVS.Zoom;
|
|
506
|
+
|
|
507
|
+
let tmpNewX = this._DragPanelDataStartX + tmpDX;
|
|
508
|
+
let tmpNewY = this._DragPanelDataStartY + tmpDY;
|
|
509
|
+
|
|
510
|
+
this._FlowView.updatePanelPosition(this._DragPanelHash, tmpNewX, tmpNewY);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
_endPanelDrag(pEvent)
|
|
514
|
+
{
|
|
515
|
+
this._SVGElement.classList.remove('panning');
|
|
516
|
+
|
|
517
|
+
this._FlowView.marshalFromView();
|
|
518
|
+
|
|
519
|
+
let tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === this._DragPanelHash);
|
|
520
|
+
if (tmpPanel && this._FlowView._EventHandlerProvider)
|
|
521
|
+
{
|
|
522
|
+
this._FlowView._EventHandlerProvider.fireEvent('onPanelMoved', tmpPanel);
|
|
523
|
+
this._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView.flowData);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
this._State = INTERACTION_STATES.IDLE;
|
|
527
|
+
this._DragPanelHash = null;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// ---- Handle Dragging ----
|
|
531
|
+
|
|
532
|
+
_startHandleDrag(pEvent, pConnectionHash, pPanelHash, pHandleType, pIsTether)
|
|
533
|
+
{
|
|
534
|
+
this._State = INTERACTION_STATES.DRAGGING_HANDLE;
|
|
535
|
+
this._DragHandleConnectionHash = pConnectionHash;
|
|
536
|
+
this._DragHandlePanelHash = pPanelHash;
|
|
537
|
+
this._DragHandleType = pHandleType;
|
|
538
|
+
this._DragHandleIsTether = pIsTether;
|
|
539
|
+
this._DragStartX = pEvent.clientX;
|
|
540
|
+
this._DragStartY = pEvent.clientY;
|
|
541
|
+
this._SVGElement.classList.add('panning');
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
_onHandleDrag(pEvent)
|
|
545
|
+
{
|
|
546
|
+
let tmpCoords = this._FlowView.screenToSVGCoords(pEvent.clientX, pEvent.clientY);
|
|
547
|
+
|
|
548
|
+
if (this._DragHandleIsTether)
|
|
549
|
+
{
|
|
550
|
+
this._FlowView.updateTetherHandle(
|
|
551
|
+
this._DragHandlePanelHash,
|
|
552
|
+
this._DragHandleType,
|
|
553
|
+
tmpCoords.x,
|
|
554
|
+
tmpCoords.y
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
else
|
|
558
|
+
{
|
|
559
|
+
this._FlowView.updateConnectionHandle(
|
|
560
|
+
this._DragHandleConnectionHash,
|
|
561
|
+
this._DragHandleType,
|
|
562
|
+
tmpCoords.x,
|
|
563
|
+
tmpCoords.y
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
_endHandleDrag(pEvent)
|
|
569
|
+
{
|
|
570
|
+
this._SVGElement.classList.remove('panning');
|
|
571
|
+
|
|
572
|
+
this._FlowView.renderFlow();
|
|
573
|
+
this._FlowView.marshalFromView();
|
|
574
|
+
|
|
575
|
+
if (this._FlowView._EventHandlerProvider)
|
|
576
|
+
{
|
|
577
|
+
this._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView.flowData);
|
|
578
|
+
|
|
579
|
+
if (this._DragHandleIsTether)
|
|
580
|
+
{
|
|
581
|
+
let tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === this._DragHandlePanelHash);
|
|
582
|
+
if (tmpPanel)
|
|
583
|
+
{
|
|
584
|
+
this._FlowView._EventHandlerProvider.fireEvent('onTetherHandleMoved', tmpPanel);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
else
|
|
588
|
+
{
|
|
589
|
+
let tmpConnection = this._FlowView.getConnection(this._DragHandleConnectionHash);
|
|
590
|
+
if (tmpConnection)
|
|
591
|
+
{
|
|
592
|
+
this._FlowView._EventHandlerProvider.fireEvent('onConnectionHandleMoved', tmpConnection);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
this._State = INTERACTION_STATES.IDLE;
|
|
598
|
+
this._DragHandleConnectionHash = null;
|
|
599
|
+
this._DragHandlePanelHash = null;
|
|
600
|
+
this._DragHandleType = null;
|
|
601
|
+
this._DragHandleIsTether = false;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// ---- Line Mode Toggling ----
|
|
605
|
+
|
|
606
|
+
_toggleConnectionLineMode(pConnectionHash)
|
|
607
|
+
{
|
|
608
|
+
let tmpConnection = this._FlowView.getConnection(pConnectionHash);
|
|
609
|
+
if (!tmpConnection) return;
|
|
610
|
+
|
|
611
|
+
if (!tmpConnection.Data) tmpConnection.Data = {};
|
|
612
|
+
|
|
613
|
+
let tmpCurrentMode = tmpConnection.Data.LineMode || 'bezier';
|
|
614
|
+
tmpConnection.Data.LineMode = (tmpCurrentMode === 'bezier') ? 'orthogonal' : 'bezier';
|
|
615
|
+
|
|
616
|
+
// Reset handle positions when switching modes
|
|
617
|
+
tmpConnection.Data.HandleCustomized = false;
|
|
618
|
+
tmpConnection.Data.BezierHandleX = null;
|
|
619
|
+
tmpConnection.Data.BezierHandleY = null;
|
|
620
|
+
tmpConnection.Data.OrthoCorner1X = null;
|
|
621
|
+
tmpConnection.Data.OrthoCorner1Y = null;
|
|
622
|
+
tmpConnection.Data.OrthoCorner2X = null;
|
|
623
|
+
tmpConnection.Data.OrthoCorner2Y = null;
|
|
624
|
+
tmpConnection.Data.OrthoMidOffset = 0;
|
|
625
|
+
|
|
626
|
+
this._FlowView.renderFlow();
|
|
627
|
+
this._FlowView.marshalFromView();
|
|
628
|
+
|
|
629
|
+
if (this._FlowView._EventHandlerProvider)
|
|
630
|
+
{
|
|
631
|
+
this._FlowView._EventHandlerProvider.fireEvent('onConnectionModeChanged', tmpConnection);
|
|
632
|
+
this._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView.flowData);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
_toggleTetherLineMode(pPanelHash)
|
|
637
|
+
{
|
|
638
|
+
let tmpPanel = this._FlowView._FlowData.OpenPanels.find((pPanel) => pPanel.Hash === pPanelHash);
|
|
639
|
+
if (!tmpPanel) return;
|
|
640
|
+
|
|
641
|
+
if (this._FlowView._TetherService)
|
|
642
|
+
{
|
|
643
|
+
this._FlowView._TetherService.toggleLineMode(tmpPanel);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
this._FlowView.renderFlow();
|
|
647
|
+
this._FlowView.marshalFromView();
|
|
648
|
+
|
|
649
|
+
if (this._FlowView._EventHandlerProvider)
|
|
650
|
+
{
|
|
651
|
+
this._FlowView._EventHandlerProvider.fireEvent('onTetherModeChanged', tmpPanel);
|
|
652
|
+
this._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', this._FlowView.flowData);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// ---- Tether Selection ----
|
|
657
|
+
|
|
658
|
+
_selectTether(pTarget)
|
|
659
|
+
{
|
|
660
|
+
let tmpPanelHash = this._getPanelHash(pTarget);
|
|
661
|
+
if (tmpPanelHash)
|
|
662
|
+
{
|
|
663
|
+
this._FlowView.selectTether(tmpPanelHash);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
331
667
|
// ---- Connection Creation ----
|
|
332
668
|
|
|
333
|
-
/**
|
|
334
|
-
* Start creating a connection from a port
|
|
335
|
-
* @param {PointerEvent} pEvent
|
|
336
|
-
* @param {Element} pTarget
|
|
337
|
-
*/
|
|
338
669
|
_startConnection(pEvent, pTarget)
|
|
339
670
|
{
|
|
340
671
|
if (!this._FlowView.options.EnableConnectionCreation) return;
|
|
@@ -345,7 +676,6 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
345
676
|
|
|
346
677
|
if (!tmpNodeHash || !tmpPortHash) return;
|
|
347
678
|
|
|
348
|
-
// Only allow starting connections from output ports
|
|
349
679
|
if (tmpPortDirection !== 'output')
|
|
350
680
|
{
|
|
351
681
|
return;
|
|
@@ -357,25 +687,18 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
357
687
|
|
|
358
688
|
this._SVGElement.classList.add('connecting');
|
|
359
689
|
|
|
360
|
-
// Create drag line
|
|
361
690
|
let tmpPortPos = this._FlowView.getPortPosition(tmpNodeHash, tmpPortHash);
|
|
362
691
|
if (tmpPortPos)
|
|
363
692
|
{
|
|
364
693
|
this._ConnectDragLine = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
365
694
|
this._ConnectDragLine.setAttribute('class', 'pict-flow-drag-connection');
|
|
366
695
|
this._ConnectDragLine.setAttribute('d', `M ${tmpPortPos.x} ${tmpPortPos.y} L ${tmpPortPos.x} ${tmpPortPos.y}`);
|
|
367
|
-
|
|
368
|
-
// Add to viewport (so it transforms with pan/zoom)
|
|
369
696
|
this._FlowView._ViewportElement.appendChild(this._ConnectDragLine);
|
|
370
697
|
}
|
|
371
698
|
|
|
372
699
|
pEvent.stopPropagation();
|
|
373
700
|
}
|
|
374
701
|
|
|
375
|
-
/**
|
|
376
|
-
* Handle connection drag
|
|
377
|
-
* @param {PointerEvent} pEvent
|
|
378
|
-
*/
|
|
379
702
|
_onConnectionDrag(pEvent)
|
|
380
703
|
{
|
|
381
704
|
if (!this._ConnectDragLine) return;
|
|
@@ -385,19 +708,13 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
385
708
|
|
|
386
709
|
let tmpEndCoords = this._FlowView.screenToSVGCoords(pEvent.clientX, pEvent.clientY);
|
|
387
710
|
|
|
388
|
-
// Render a bezier curve for the drag line
|
|
389
711
|
let tmpDX = Math.abs(tmpEndCoords.x - tmpSourcePos.x) * 0.5;
|
|
390
712
|
let tmpPath = `M ${tmpSourcePos.x} ${tmpSourcePos.y} C ${tmpSourcePos.x + tmpDX} ${tmpSourcePos.y}, ${tmpEndCoords.x - tmpDX} ${tmpEndCoords.y}, ${tmpEndCoords.x} ${tmpEndCoords.y}`;
|
|
391
713
|
this._ConnectDragLine.setAttribute('d', tmpPath);
|
|
392
714
|
}
|
|
393
715
|
|
|
394
|
-
/**
|
|
395
|
-
* End connection creation
|
|
396
|
-
* @param {PointerEvent} pEvent
|
|
397
|
-
*/
|
|
398
716
|
_endConnection(pEvent)
|
|
399
717
|
{
|
|
400
|
-
// Remove drag line
|
|
401
718
|
if (this._ConnectDragLine && this._ConnectDragLine.parentNode)
|
|
402
719
|
{
|
|
403
720
|
this._ConnectDragLine.parentNode.removeChild(this._ConnectDragLine);
|
|
@@ -406,7 +723,6 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
406
723
|
|
|
407
724
|
this._SVGElement.classList.remove('connecting');
|
|
408
725
|
|
|
409
|
-
// Check if we're over a valid target port
|
|
410
726
|
let tmpTarget = document.elementFromPoint(pEvent.clientX, pEvent.clientY);
|
|
411
727
|
if (tmpTarget)
|
|
412
728
|
{
|
|
@@ -430,9 +746,6 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
430
746
|
this._ConnectSourcePortHash = null;
|
|
431
747
|
}
|
|
432
748
|
|
|
433
|
-
/**
|
|
434
|
-
* Cancel connection creation (e.g., on Escape)
|
|
435
|
-
*/
|
|
436
749
|
_cancelConnection()
|
|
437
750
|
{
|
|
438
751
|
if (this._ConnectDragLine && this._ConnectDragLine.parentNode)
|
|
@@ -450,13 +763,8 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
450
763
|
|
|
451
764
|
// ---- Panning ----
|
|
452
765
|
|
|
453
|
-
/**
|
|
454
|
-
* Start panning the viewport
|
|
455
|
-
* @param {PointerEvent} pEvent
|
|
456
|
-
*/
|
|
457
766
|
_startPanning(pEvent)
|
|
458
767
|
{
|
|
459
|
-
// Deselect if clicking on empty space
|
|
460
768
|
this._FlowView.deselectAll();
|
|
461
769
|
|
|
462
770
|
this._State = INTERACTION_STATES.PANNING;
|
|
@@ -468,10 +776,6 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
468
776
|
this._SVGElement.classList.add('panning');
|
|
469
777
|
}
|
|
470
778
|
|
|
471
|
-
/**
|
|
472
|
-
* Handle panning
|
|
473
|
-
* @param {PointerEvent} pEvent
|
|
474
|
-
*/
|
|
475
779
|
_onPan(pEvent)
|
|
476
780
|
{
|
|
477
781
|
let tmpDX = pEvent.clientX - this._PanStartX;
|
|
@@ -483,10 +787,6 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
483
787
|
this._FlowView.updateViewportTransform();
|
|
484
788
|
}
|
|
485
789
|
|
|
486
|
-
/**
|
|
487
|
-
* End panning
|
|
488
|
-
* @param {PointerEvent} pEvent
|
|
489
|
-
*/
|
|
490
790
|
_endPanning(pEvent)
|
|
491
791
|
{
|
|
492
792
|
this._SVGElement.classList.remove('panning');
|
|
@@ -495,13 +795,9 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
495
795
|
|
|
496
796
|
// ---- Connection Selection ----
|
|
497
797
|
|
|
498
|
-
/**
|
|
499
|
-
* Select a connection
|
|
500
|
-
* @param {Element} pTarget
|
|
501
|
-
*/
|
|
502
798
|
_selectConnection(pTarget)
|
|
503
799
|
{
|
|
504
|
-
let tmpConnectionHash =
|
|
800
|
+
let tmpConnectionHash = this._getConnectionHash(pTarget);
|
|
505
801
|
if (tmpConnectionHash)
|
|
506
802
|
{
|
|
507
803
|
this._FlowView.selectConnection(tmpConnectionHash);
|
|
@@ -510,25 +806,18 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
510
806
|
|
|
511
807
|
// ---- Utilities ----
|
|
512
808
|
|
|
513
|
-
/**
|
|
514
|
-
* Get the element type from a target element (walks up to find data attributes)
|
|
515
|
-
* @param {Element} pTarget
|
|
516
|
-
* @returns {string} The element type
|
|
517
|
-
*/
|
|
518
809
|
_getElementType(pTarget)
|
|
519
810
|
{
|
|
520
811
|
if (!pTarget) return 'background';
|
|
521
812
|
|
|
522
|
-
|
|
523
|
-
let tmpType = pTarget.getAttribute('data-element-type');
|
|
813
|
+
let tmpType = pTarget.getAttribute ? pTarget.getAttribute('data-element-type') : null;
|
|
524
814
|
if (tmpType) return tmpType;
|
|
525
815
|
|
|
526
|
-
// Walk up to find the closest element with a data attribute
|
|
527
816
|
let tmpParent = pTarget.parentElement;
|
|
528
817
|
let tmpDepth = 0;
|
|
529
818
|
while (tmpParent && tmpDepth < 5)
|
|
530
819
|
{
|
|
531
|
-
tmpType = tmpParent.getAttribute('data-element-type');
|
|
820
|
+
tmpType = tmpParent.getAttribute ? tmpParent.getAttribute('data-element-type') : null;
|
|
532
821
|
if (tmpType) return tmpType;
|
|
533
822
|
tmpParent = tmpParent.parentElement;
|
|
534
823
|
tmpDepth++;
|
|
@@ -537,23 +826,58 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
537
826
|
return 'background';
|
|
538
827
|
}
|
|
539
828
|
|
|
540
|
-
/**
|
|
541
|
-
* Get the node hash from a target element (walks up parents)
|
|
542
|
-
* @param {Element} pTarget
|
|
543
|
-
* @returns {string|null}
|
|
544
|
-
*/
|
|
545
829
|
_getNodeHash(pTarget)
|
|
546
830
|
{
|
|
547
831
|
if (!pTarget) return null;
|
|
548
832
|
|
|
549
|
-
let tmpHash = pTarget.getAttribute('data-node-hash');
|
|
833
|
+
let tmpHash = pTarget.getAttribute ? pTarget.getAttribute('data-node-hash') : null;
|
|
834
|
+
if (tmpHash) return tmpHash;
|
|
835
|
+
|
|
836
|
+
let tmpParent = pTarget.parentElement;
|
|
837
|
+
let tmpDepth = 0;
|
|
838
|
+
while (tmpParent && tmpDepth < 5)
|
|
839
|
+
{
|
|
840
|
+
tmpHash = tmpParent.getAttribute ? tmpParent.getAttribute('data-node-hash') : null;
|
|
841
|
+
if (tmpHash) return tmpHash;
|
|
842
|
+
tmpParent = tmpParent.parentElement;
|
|
843
|
+
tmpDepth++;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
return null;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
_getPanelHash(pTarget)
|
|
850
|
+
{
|
|
851
|
+
if (!pTarget) return null;
|
|
852
|
+
|
|
853
|
+
let tmpHash = pTarget.getAttribute ? pTarget.getAttribute('data-panel-hash') : null;
|
|
854
|
+
if (tmpHash) return tmpHash;
|
|
855
|
+
|
|
856
|
+
let tmpParent = pTarget.parentElement;
|
|
857
|
+
let tmpDepth = 0;
|
|
858
|
+
while (tmpParent && tmpDepth < 5)
|
|
859
|
+
{
|
|
860
|
+
tmpHash = tmpParent.getAttribute ? tmpParent.getAttribute('data-panel-hash') : null;
|
|
861
|
+
if (tmpHash) return tmpHash;
|
|
862
|
+
tmpParent = tmpParent.parentElement;
|
|
863
|
+
tmpDepth++;
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
return null;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
_getConnectionHash(pTarget)
|
|
870
|
+
{
|
|
871
|
+
if (!pTarget) return null;
|
|
872
|
+
|
|
873
|
+
let tmpHash = pTarget.getAttribute ? pTarget.getAttribute('data-connection-hash') : null;
|
|
550
874
|
if (tmpHash) return tmpHash;
|
|
551
875
|
|
|
552
876
|
let tmpParent = pTarget.parentElement;
|
|
553
877
|
let tmpDepth = 0;
|
|
554
878
|
while (tmpParent && tmpDepth < 5)
|
|
555
879
|
{
|
|
556
|
-
tmpHash = tmpParent.getAttribute('data-
|
|
880
|
+
tmpHash = tmpParent.getAttribute ? tmpParent.getAttribute('data-connection-hash') : null;
|
|
557
881
|
if (tmpHash) return tmpHash;
|
|
558
882
|
tmpParent = tmpParent.parentElement;
|
|
559
883
|
tmpDepth++;
|