pict-section-flow 0.0.18 → 1.0.0
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/Theme_Integration.md +150 -0
- package/docs/_brand.json +18 -0
- package/docs/_sidebar.md +1 -0
- package/package.json +8 -8
- package/source/providers/PictProvider-Flow-CSS.js +197 -47
- package/source/providers/PictProvider-Flow-ConnectorShapes.js +9 -5
- package/source/providers/PictProvider-Flow-NodeTypes.js +10 -0
- package/source/providers/PictProvider-Flow-PanelChrome.js +7 -17
- package/source/services/PictService-Flow-InteractionManager.js +39 -42
- package/source/services/PictService-Flow-PortRenderer.js +10 -24
- package/source/views/PictView-Flow-FloatingToolbar.js +69 -61
- package/source/views/PictView-Flow-Node.js +19 -6
- package/source/views/PictView-Flow-PropertiesPanel.js +46 -53
- package/source/views/PictView-Flow-Toolbar.js +664 -789
- package/source/views/PictView-Flow.js +183 -2
|
@@ -50,7 +50,11 @@ class PictProviderFlowPanelChrome extends libFableServiceProviderBase
|
|
|
50
50
|
let tmpPict = this._FlowView.pict || this._FlowView.fable;
|
|
51
51
|
let tmpTitle = pPanelData.Title || 'Properties';
|
|
52
52
|
let tmpChromeHTML = tmpPict.parseTemplateByHash('Flow-PanelChrome-Template',
|
|
53
|
-
{
|
|
53
|
+
{
|
|
54
|
+
Hash: pPanelData.Hash,
|
|
55
|
+
Title: tmpTitle,
|
|
56
|
+
FlowViewIdentifier: this._FlowView.options.ViewIdentifier
|
|
57
|
+
});
|
|
54
58
|
|
|
55
59
|
tmpFO.innerHTML = tmpChromeHTML;
|
|
56
60
|
|
|
@@ -65,22 +69,8 @@ class PictProviderFlowPanelChrome extends libFableServiceProviderBase
|
|
|
65
69
|
tmpCloseIcon.textContent = '\u2715';
|
|
66
70
|
}
|
|
67
71
|
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
let tmpContent = tmpFO.querySelector('.pict-flow-panel-content');
|
|
71
|
-
if (tmpContent)
|
|
72
|
-
{
|
|
73
|
-
tmpContent.addEventListener('pointerdown', (pEvent) => { pEvent.stopPropagation(); });
|
|
74
|
-
tmpContent.addEventListener('wheel', (pEvent) => { pEvent.stopPropagation(); });
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Isolate events on the tab bar
|
|
78
|
-
let tmpTabbar = tmpFO.querySelector('.pict-flow-panel-tabbar');
|
|
79
|
-
if (tmpTabbar)
|
|
80
|
-
{
|
|
81
|
-
tmpTabbar.addEventListener('pointerdown', (pEvent) => { pEvent.stopPropagation(); });
|
|
82
|
-
tmpTabbar.addEventListener('wheel', (pEvent) => { pEvent.stopPropagation(); });
|
|
83
|
-
}
|
|
72
|
+
// Pointer/wheel isolation lives on the inline onpointerdown/onwheel
|
|
73
|
+
// attributes in Flow-PanelChrome-Template — see PictView-Flow.js.
|
|
84
74
|
|
|
85
75
|
pPanelsLayer.appendChild(tmpFO);
|
|
86
76
|
|
|
@@ -104,61 +104,58 @@ class PictServiceFlowInteractionManager extends libFableServiceProviderBase
|
|
|
104
104
|
|
|
105
105
|
if (!this._SVGElement) return;
|
|
106
106
|
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
this._SVGElement.addEventListener('wheel', this._boundOnWheel, { passive: false });
|
|
113
|
-
|
|
114
|
-
// Keyboard events for delete
|
|
107
|
+
// Pointer/wheel/contextmenu live as inline attributes on the SVG
|
|
108
|
+
// template (see Flow-Container-Template in PictView-Flow.js); they
|
|
109
|
+
// route to InteractionManager via the FlowView's bridges. The
|
|
110
|
+
// keydown listener stays document-level because keyboard input
|
|
111
|
+
// has no element-level inline equivalent for the canvas-as-a-whole.
|
|
115
112
|
document.addEventListener('keydown', this._boundOnKeyDown);
|
|
113
|
+
}
|
|
116
114
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Inline-handler bridge — invoked from the SVG template's
|
|
117
|
+
* `oncontextmenu` attribute. Prevents the browser context menu and
|
|
118
|
+
* dispatches to the right bezier-handle action based on what was
|
|
119
|
+
* right-clicked.
|
|
120
|
+
*
|
|
121
|
+
* @param {Event} pEvent
|
|
122
|
+
*/
|
|
123
|
+
handleContextMenu(pEvent)
|
|
124
|
+
{
|
|
125
|
+
pEvent.preventDefault();
|
|
121
126
|
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
let tmpTarget = pEvent.target;
|
|
128
|
+
let tmpElementType = this._getElementType(tmpTarget);
|
|
124
129
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
switch (tmpElementType)
|
|
131
|
+
{
|
|
132
|
+
case 'connection':
|
|
133
|
+
case 'connection-hitarea':
|
|
134
|
+
this._addBezierHandle(tmpTarget, pEvent);
|
|
135
|
+
break;
|
|
131
136
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
137
|
+
case 'connection-handle':
|
|
138
|
+
this._removeBezierHandle(tmpTarget);
|
|
139
|
+
break;
|
|
135
140
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
141
|
+
case 'tether':
|
|
142
|
+
case 'tether-hitarea':
|
|
143
|
+
this._addTetherBezierHandle(tmpTarget, pEvent);
|
|
144
|
+
break;
|
|
140
145
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
});
|
|
146
|
+
case 'tether-handle':
|
|
147
|
+
this._removeTetherBezierHandle(tmpTarget);
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
146
150
|
}
|
|
147
151
|
|
|
148
152
|
/**
|
|
149
|
-
* Remove all event listeners
|
|
153
|
+
* Remove all event listeners. Pointer/wheel/contextmenu listeners live
|
|
154
|
+
* as inline template attributes and don't need explicit teardown — they
|
|
155
|
+
* disappear with the SVG element when the FlowView is destroyed.
|
|
150
156
|
*/
|
|
151
157
|
destroy()
|
|
152
158
|
{
|
|
153
|
-
if (this._SVGElement)
|
|
154
|
-
{
|
|
155
|
-
this._SVGElement.removeEventListener('pointerdown', this._boundOnPointerDown);
|
|
156
|
-
this._SVGElement.removeEventListener('pointermove', this._boundOnPointerMove);
|
|
157
|
-
this._SVGElement.removeEventListener('pointerup', this._boundOnPointerUp);
|
|
158
|
-
this._SVGElement.removeEventListener('pointerleave', this._boundOnPointerUp);
|
|
159
|
-
this._SVGElement.removeEventListener('wheel', this._boundOnWheel);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
159
|
document.removeEventListener('keydown', this._boundOnKeyDown);
|
|
163
160
|
}
|
|
164
161
|
|
|
@@ -283,31 +283,17 @@ class PictServiceFlowPortRenderer extends libFableServiceProviderBase
|
|
|
283
283
|
*/
|
|
284
284
|
_wirePortHintHover(pHoverEl, pPortHash, pNodeHash)
|
|
285
285
|
{
|
|
286
|
-
if (!pHoverEl || typeof pHoverEl.
|
|
286
|
+
if (!pHoverEl || typeof pHoverEl.setAttribute !== 'function') return;
|
|
287
|
+
if (!this._FlowView || !this._FlowView.options) return;
|
|
287
288
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
let tmpHints = tmpScope.querySelectorAll(tmpSelector);
|
|
297
|
-
for (let i = 0; i < tmpHints.length; i++)
|
|
298
|
-
{
|
|
299
|
-
tmpHints[i].setAttribute('data-active', 'true');
|
|
300
|
-
}
|
|
301
|
-
});
|
|
302
|
-
pHoverEl.addEventListener('mouseleave', function ()
|
|
303
|
-
{
|
|
304
|
-
let tmpScope = tmpFlowView._SVGElement || document;
|
|
305
|
-
let tmpHints = tmpScope.querySelectorAll(tmpSelector);
|
|
306
|
-
for (let i = 0; i < tmpHints.length; i++)
|
|
307
|
-
{
|
|
308
|
-
tmpHints[i].removeAttribute('data-active');
|
|
309
|
-
}
|
|
310
|
-
});
|
|
289
|
+
// Inline attribute handlers — survive the connection-layer rebuild
|
|
290
|
+
// without leaving stale listeners on detached DOM nodes.
|
|
291
|
+
let tmpFlowViewIdentifier = this._FlowView.options.ViewIdentifier;
|
|
292
|
+
let tmpHashArg = pPortHash ? ('"' + pPortHash + '", null') : ('null, "' + pNodeHash + '"');
|
|
293
|
+
pHoverEl.setAttribute('onmouseenter',
|
|
294
|
+
"_Pict.views['" + tmpFlowViewIdentifier + "']._activatePortHints(" + tmpHashArg + ")");
|
|
295
|
+
pHoverEl.setAttribute('onmouseleave',
|
|
296
|
+
"_Pict.views['" + tmpFlowViewIdentifier + "']._deactivatePortHints(" + tmpHashArg + ")");
|
|
311
297
|
}
|
|
312
298
|
|
|
313
299
|
/**
|
|
@@ -17,42 +17,56 @@ const _DefaultConfiguration =
|
|
|
17
17
|
[
|
|
18
18
|
{
|
|
19
19
|
Hash: 'Flow-FloatingToolbar-Template',
|
|
20
|
+
// Inline handlers route to the floating toolbar view via _Pict.views
|
|
21
|
+
// (see _handleButtonClick / _startDrag / _toggleCollapse).
|
|
20
22
|
Template: /*html*/`
|
|
21
23
|
<div class="pict-flow-floating-toolbar" id="Flow-FloatingToolbar-{~D:Record.FlowViewIdentifier~}">
|
|
22
|
-
<div class="pict-flow-floating-grip" id="Flow-FloatingGrip-{~D:Record.FlowViewIdentifier~}" title="Drag to move · Double-click to collapse"
|
|
24
|
+
<div class="pict-flow-floating-grip" id="Flow-FloatingGrip-{~D:Record.FlowViewIdentifier~}" title="Drag to move · Double-click to collapse"
|
|
25
|
+
onmousedown="_Pict.views['{~D:Record.FlowViewIdentifier~}']._ToolbarView._FloatingToolbarView._startDrag(event)"
|
|
26
|
+
ondblclick="_Pict.views['{~D:Record.FlowViewIdentifier~}']._ToolbarView._FloatingToolbarView._handleGripDoubleClick(event)">
|
|
23
27
|
<span id="Flow-FloatingIcon-grip-{~D:Record.FlowViewIdentifier~}"></span>
|
|
24
28
|
</div>
|
|
25
|
-
<button class="pict-flow-floating-btn" data-flow-action="add-node" title="Add Node"
|
|
29
|
+
<button class="pict-flow-floating-btn" data-flow-action="add-node" title="Add Node"
|
|
30
|
+
onclick="_Pict.views['{~D:Record.FlowViewIdentifier~}']._ToolbarView._FloatingToolbarView._handleButtonClick('add-node')">
|
|
26
31
|
<span id="Flow-FloatingIcon-plus-{~D:Record.FlowViewIdentifier~}"></span>
|
|
27
32
|
</button>
|
|
28
|
-
<button class="pict-flow-floating-btn" data-flow-action="cards-popup" title="Cards"
|
|
33
|
+
<button class="pict-flow-floating-btn" data-flow-action="cards-popup" title="Cards"
|
|
34
|
+
onclick="_Pict.views['{~D:Record.FlowViewIdentifier~}']._ToolbarView._FloatingToolbarView._handleButtonClick('cards-popup')">
|
|
29
35
|
<span id="Flow-FloatingIcon-cards-{~D:Record.FlowViewIdentifier~}"></span>
|
|
30
36
|
</button>
|
|
31
|
-
<button class="pict-flow-floating-btn" data-flow-action="delete-selected" title="Delete Selected"
|
|
37
|
+
<button class="pict-flow-floating-btn" data-flow-action="delete-selected" title="Delete Selected"
|
|
38
|
+
onclick="_Pict.views['{~D:Record.FlowViewIdentifier~}']._ToolbarView._FloatingToolbarView._handleButtonClick('delete-selected')">
|
|
32
39
|
<span id="Flow-FloatingIcon-trash-{~D:Record.FlowViewIdentifier~}"></span>
|
|
33
40
|
</button>
|
|
34
41
|
<div class="pict-flow-floating-separator"></div>
|
|
35
|
-
<button class="pict-flow-floating-btn" data-flow-action="zoom-in" title="Zoom In"
|
|
42
|
+
<button class="pict-flow-floating-btn" data-flow-action="zoom-in" title="Zoom In"
|
|
43
|
+
onclick="_Pict.views['{~D:Record.FlowViewIdentifier~}']._ToolbarView._FloatingToolbarView._handleButtonClick('zoom-in')">
|
|
36
44
|
<span id="Flow-FloatingIcon-zoom-in-{~D:Record.FlowViewIdentifier~}"></span>
|
|
37
45
|
</button>
|
|
38
|
-
<button class="pict-flow-floating-btn" data-flow-action="zoom-out" title="Zoom Out"
|
|
46
|
+
<button class="pict-flow-floating-btn" data-flow-action="zoom-out" title="Zoom Out"
|
|
47
|
+
onclick="_Pict.views['{~D:Record.FlowViewIdentifier~}']._ToolbarView._FloatingToolbarView._handleButtonClick('zoom-out')">
|
|
39
48
|
<span id="Flow-FloatingIcon-zoom-out-{~D:Record.FlowViewIdentifier~}"></span>
|
|
40
49
|
</button>
|
|
41
|
-
<button class="pict-flow-floating-btn" data-flow-action="zoom-fit" title="Fit to View"
|
|
50
|
+
<button class="pict-flow-floating-btn" data-flow-action="zoom-fit" title="Fit to View"
|
|
51
|
+
onclick="_Pict.views['{~D:Record.FlowViewIdentifier~}']._ToolbarView._FloatingToolbarView._handleButtonClick('zoom-fit')">
|
|
42
52
|
<span id="Flow-FloatingIcon-zoom-fit-{~D:Record.FlowViewIdentifier~}"></span>
|
|
43
53
|
</button>
|
|
44
54
|
<div class="pict-flow-floating-separator"></div>
|
|
45
|
-
<button class="pict-flow-floating-btn" data-flow-action="auto-layout" title="Auto Layout"
|
|
55
|
+
<button class="pict-flow-floating-btn" data-flow-action="auto-layout" title="Auto Layout"
|
|
56
|
+
onclick="_Pict.views['{~D:Record.FlowViewIdentifier~}']._ToolbarView._FloatingToolbarView._handleButtonClick('auto-layout')">
|
|
46
57
|
<span id="Flow-FloatingIcon-auto-layout-{~D:Record.FlowViewIdentifier~}"></span>
|
|
47
58
|
</button>
|
|
48
|
-
<button class="pict-flow-floating-btn" data-flow-action="layout-popup" title="Layout"
|
|
59
|
+
<button class="pict-flow-floating-btn" data-flow-action="layout-popup" title="Layout"
|
|
60
|
+
onclick="_Pict.views['{~D:Record.FlowViewIdentifier~}']._ToolbarView._FloatingToolbarView._handleButtonClick('layout-popup')">
|
|
49
61
|
<span id="Flow-FloatingIcon-layout-{~D:Record.FlowViewIdentifier~}"></span>
|
|
50
62
|
</button>
|
|
51
|
-
<button class="pict-flow-floating-btn" data-flow-action="fullscreen" title="Toggle Fullscreen"
|
|
63
|
+
<button class="pict-flow-floating-btn" data-flow-action="fullscreen" title="Toggle Fullscreen"
|
|
64
|
+
onclick="_Pict.views['{~D:Record.FlowViewIdentifier~}']._ToolbarView._FloatingToolbarView._handleButtonClick('fullscreen')">
|
|
52
65
|
<span id="Flow-FloatingIcon-fullscreen-{~D:Record.FlowViewIdentifier~}"></span>
|
|
53
66
|
</button>
|
|
54
67
|
<div class="pict-flow-floating-separator"></div>
|
|
55
|
-
<button class="pict-flow-floating-btn" data-flow-action="dock-toolbar" title="Dock Toolbar"
|
|
68
|
+
<button class="pict-flow-floating-btn" data-flow-action="dock-toolbar" title="Dock Toolbar"
|
|
69
|
+
onclick="_Pict.views['{~D:Record.FlowViewIdentifier~}']._ToolbarView._FloatingToolbarView._handleButtonClick('dock-toolbar')">
|
|
56
70
|
<span id="Flow-FloatingIcon-dock-{~D:Record.FlowViewIdentifier~}"></span>
|
|
57
71
|
</button>
|
|
58
72
|
</div>
|
|
@@ -104,58 +118,13 @@ class PictViewFlowFloatingToolbar extends libPictView
|
|
|
104
118
|
{
|
|
105
119
|
let tmpFlowViewIdentifier = this.options.FlowViewIdentifier;
|
|
106
120
|
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
tmpFloatingToolbar[0].addEventListener('click', (pEvent) =>
|
|
112
|
-
{
|
|
113
|
-
let tmpTarget = pEvent.target;
|
|
114
|
-
if (!tmpTarget) return;
|
|
115
|
-
|
|
116
|
-
let tmpButton = tmpTarget.closest('[data-flow-action]');
|
|
117
|
-
if (!tmpButton) return;
|
|
118
|
-
|
|
119
|
-
let tmpAction = tmpButton.getAttribute('data-flow-action');
|
|
120
|
-
if (tmpAction === 'dock-toolbar')
|
|
121
|
-
{
|
|
122
|
-
if (this._ToolbarView)
|
|
123
|
-
{
|
|
124
|
-
this._ToolbarView._setToolbarMode('docked');
|
|
125
|
-
}
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Delegate all other actions to the docked toolbar
|
|
130
|
-
if (this._ToolbarView)
|
|
131
|
-
{
|
|
132
|
-
this._ToolbarView._handleToolbarAction(tmpAction);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Bind drag behavior on the grip
|
|
138
|
-
let tmpGrip = this.pict.ContentAssignment.getElement(`#Flow-FloatingGrip-${tmpFlowViewIdentifier}`);
|
|
139
|
-
if (tmpGrip.length > 0)
|
|
140
|
-
{
|
|
141
|
-
tmpGrip[0].addEventListener('mousedown', (pEvent) =>
|
|
142
|
-
{
|
|
143
|
-
this._startDrag(pEvent);
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
// Double-click grip to toggle collapsed state
|
|
147
|
-
tmpGrip[0].addEventListener('dblclick', (pEvent) =>
|
|
148
|
-
{
|
|
149
|
-
pEvent.preventDefault();
|
|
150
|
-
pEvent.stopPropagation();
|
|
151
|
-
this._toggleCollapse();
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
// Populate icons
|
|
121
|
+
// Button clicks and grip drag/dblclick are wired inline in
|
|
122
|
+
// Flow-FloatingToolbar-Template — see _handleButtonClick / _startDrag /
|
|
123
|
+
// _handleGripDoubleClick. Only icon population and option-based
|
|
124
|
+
// pruning happen here.
|
|
156
125
|
this._populateIcons();
|
|
157
126
|
|
|
158
|
-
|
|
127
|
+
let tmpFloatingToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);
|
|
159
128
|
if (tmpFloatingToolbar.length > 0)
|
|
160
129
|
{
|
|
161
130
|
if (this.options.EnableAddNode === false)
|
|
@@ -179,6 +148,45 @@ class PictViewFlowFloatingToolbar extends libPictView
|
|
|
179
148
|
return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
|
|
180
149
|
}
|
|
181
150
|
|
|
151
|
+
/**
|
|
152
|
+
* Handle a click on a floating toolbar action button. Called from the
|
|
153
|
+
* inline `onclick` handler on each button.
|
|
154
|
+
*
|
|
155
|
+
* @param {string} pAction - Value of the button's data-flow-action attr
|
|
156
|
+
*/
|
|
157
|
+
_handleButtonClick(pAction)
|
|
158
|
+
{
|
|
159
|
+
if (pAction === 'dock-toolbar')
|
|
160
|
+
{
|
|
161
|
+
if (this._ToolbarView)
|
|
162
|
+
{
|
|
163
|
+
this._ToolbarView._setToolbarMode('docked');
|
|
164
|
+
}
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (this._ToolbarView)
|
|
168
|
+
{
|
|
169
|
+
this._ToolbarView._handleToolbarAction(pAction);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Handle a double-click on the grip. Toggles collapsed state and
|
|
175
|
+
* prevents the event from reaching the surrounding canvas. Called
|
|
176
|
+
* from the inline `ondblclick` handler.
|
|
177
|
+
*
|
|
178
|
+
* @param {Event} pEvent
|
|
179
|
+
*/
|
|
180
|
+
_handleGripDoubleClick(pEvent)
|
|
181
|
+
{
|
|
182
|
+
if (pEvent && typeof pEvent.preventDefault === 'function')
|
|
183
|
+
{
|
|
184
|
+
pEvent.preventDefault();
|
|
185
|
+
pEvent.stopPropagation();
|
|
186
|
+
}
|
|
187
|
+
this._toggleCollapse();
|
|
188
|
+
}
|
|
189
|
+
|
|
182
190
|
/**
|
|
183
191
|
* Populate SVG icons into all floating toolbar button spans.
|
|
184
192
|
*/
|
|
@@ -40,6 +40,18 @@ class PictViewFlowNode extends libPictView
|
|
|
40
40
|
if (pNodeTypeConfig.PortLabelsOnHover) tmpClassList += ' pict-flow-node-port-labels-hover';
|
|
41
41
|
if (pNodeTypeConfig.PortLabelsVertical) tmpClassList += ' pict-flow-node-port-labels-vertical';
|
|
42
42
|
}
|
|
43
|
+
|
|
44
|
+
// Resolve theme-aware ColorRole. Per-node override wins; falls back
|
|
45
|
+
// to the node type's role. A role of 'none' (or empty string)
|
|
46
|
+
// explicitly opts out — useful when a card wants to keep its
|
|
47
|
+
// hex BodyStyle/TitleBarColor regardless of the host theme.
|
|
48
|
+
let tmpColorRole = (typeof pNodeData.ColorRole !== 'undefined')
|
|
49
|
+
? pNodeData.ColorRole
|
|
50
|
+
: (pNodeTypeConfig && pNodeTypeConfig.ColorRole);
|
|
51
|
+
if (tmpColorRole && tmpColorRole !== 'none')
|
|
52
|
+
{
|
|
53
|
+
tmpClassList += ' pict-flow-node-color-' + tmpColorRole;
|
|
54
|
+
}
|
|
43
55
|
tmpGroup.setAttribute('class', tmpClassList);
|
|
44
56
|
tmpGroup.setAttribute('transform', `translate(${pNodeData.X}, ${pNodeData.Y})`);
|
|
45
57
|
tmpGroup.setAttribute('data-node-hash', pNodeData.Hash);
|
|
@@ -430,9 +442,10 @@ class PictViewFlowNode extends libPictView
|
|
|
430
442
|
tmpDiv.setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
|
|
431
443
|
tmpDiv.setAttribute('class', 'pict-flow-node-body-content-html');
|
|
432
444
|
|
|
433
|
-
// Pointer event isolation — prevent node drag/canvas pan
|
|
434
|
-
|
|
435
|
-
tmpDiv.
|
|
445
|
+
// Pointer event isolation — prevent node drag/canvas pan.
|
|
446
|
+
// Inline attribute handlers (visible in DOM, no closure leak).
|
|
447
|
+
tmpDiv.setAttribute('onpointerdown', 'event.stopPropagation()');
|
|
448
|
+
tmpDiv.setAttribute('onwheel', 'event.stopPropagation()');
|
|
436
449
|
|
|
437
450
|
// Render template content
|
|
438
451
|
let tmpRenderedContent = this._resolveBodyTemplate(pBodyContent, pNodeData, pPict);
|
|
@@ -471,9 +484,9 @@ class PictViewFlowNode extends libPictView
|
|
|
471
484
|
tmpCanvas.style.width = '100%';
|
|
472
485
|
tmpCanvas.style.height = '100%';
|
|
473
486
|
|
|
474
|
-
// Pointer event isolation
|
|
475
|
-
tmpCanvas.
|
|
476
|
-
tmpCanvas.
|
|
487
|
+
// Pointer event isolation — inline attribute handlers.
|
|
488
|
+
tmpCanvas.setAttribute('onpointerdown', 'event.stopPropagation()');
|
|
489
|
+
tmpCanvas.setAttribute('onwheel', 'event.stopPropagation()');
|
|
477
490
|
|
|
478
491
|
// Invoke render callback (the primary rendering path for canvas)
|
|
479
492
|
if (typeof pBodyContent.RenderCallback === 'function')
|
|
@@ -74,7 +74,17 @@ const _DefaultConfiguration =
|
|
|
74
74
|
},
|
|
75
75
|
{
|
|
76
76
|
Hash: 'Flow-NodeProps-Editor',
|
|
77
|
-
|
|
77
|
+
// Inline oninput/onpointerdown wired to the FlowView's helper —
|
|
78
|
+
// see PictViewFlow._applyNodePropChange / _stopEvent.
|
|
79
|
+
Template: '<div class="pict-flow-node-props-fields" data-flow-view="{~D:Record.FlowViewIdentifier~}" data-node-hash="{~D:Record.NodeHash~}">'
|
|
80
|
+
+ '<div class="pict-flow-node-props-field"><label class="pict-flow-node-props-label">Title</label><input type="text" class="pict-flow-node-props-input" data-prop="Title" value="{~D:Record.Title~}" oninput="_Pict.views[\'{~D:Record.FlowViewIdentifier~}\']._applyNodePropChange(\'{~D:Record.NodeHash~}\', this.getAttribute(\'data-prop\'), this.value, this.type, event)" onpointerdown="event.stopPropagation()" /></div>'
|
|
81
|
+
+ '<div class="pict-flow-node-props-field"><label class="pict-flow-node-props-label">Width</label><input type="number" class="pict-flow-node-props-input" data-prop="Width" value="{~D:Record.Width~}" min="60" step="10" oninput="_Pict.views[\'{~D:Record.FlowViewIdentifier~}\']._applyNodePropChange(\'{~D:Record.NodeHash~}\', this.getAttribute(\'data-prop\'), this.value, this.type, event)" onpointerdown="event.stopPropagation()" /></div>'
|
|
82
|
+
+ '<div class="pict-flow-node-props-field"><label class="pict-flow-node-props-label">Height</label><input type="number" class="pict-flow-node-props-input" data-prop="Height" value="{~D:Record.Height~}" min="40" step="10" oninput="_Pict.views[\'{~D:Record.FlowViewIdentifier~}\']._applyNodePropChange(\'{~D:Record.NodeHash~}\', this.getAttribute(\'data-prop\'), this.value, this.type, event)" onpointerdown="event.stopPropagation()" /></div>'
|
|
83
|
+
+ '<div class="pict-flow-node-props-field"><label class="pict-flow-node-props-label">Body Fill</label><input type="color" class="pict-flow-node-props-input pict-flow-node-props-color" data-prop="Style.BodyFill" value="{~D:Record.BodyFillValue~}" oninput="_Pict.views[\'{~D:Record.FlowViewIdentifier~}\']._applyNodePropChange(\'{~D:Record.NodeHash~}\', this.getAttribute(\'data-prop\'), this.value, this.type, event)" onpointerdown="event.stopPropagation()" /></div>'
|
|
84
|
+
+ '<div class="pict-flow-node-props-field"><label class="pict-flow-node-props-label">Body Stroke</label><input type="color" class="pict-flow-node-props-input pict-flow-node-props-color" data-prop="Style.BodyStroke" value="{~D:Record.BodyStrokeValue~}" oninput="_Pict.views[\'{~D:Record.FlowViewIdentifier~}\']._applyNodePropChange(\'{~D:Record.NodeHash~}\', this.getAttribute(\'data-prop\'), this.value, this.type, event)" onpointerdown="event.stopPropagation()" /></div>'
|
|
85
|
+
+ '<div class="pict-flow-node-props-field"><label class="pict-flow-node-props-label">Stroke Width</label><input type="number" class="pict-flow-node-props-input" data-prop="Style.BodyStrokeWidth" value="{~D:Record.BodyStrokeWidthValue~}" min="0" max="10" step="0.5" oninput="_Pict.views[\'{~D:Record.FlowViewIdentifier~}\']._applyNodePropChange(\'{~D:Record.NodeHash~}\', this.getAttribute(\'data-prop\'), this.value, this.type, event)" onpointerdown="event.stopPropagation()" /></div>'
|
|
86
|
+
+ '<div class="pict-flow-node-props-field"><label class="pict-flow-node-props-label">Title Bar</label><input type="color" class="pict-flow-node-props-input pict-flow-node-props-color" data-prop="Style.TitleBarColor" value="{~D:Record.TitleBarColorValue~}" oninput="_Pict.views[\'{~D:Record.FlowViewIdentifier~}\']._applyNodePropChange(\'{~D:Record.NodeHash~}\', this.getAttribute(\'data-prop\'), this.value, this.type, event)" onpointerdown="event.stopPropagation()" /></div>'
|
|
87
|
+
+ '</div>'
|
|
78
88
|
}
|
|
79
89
|
]
|
|
80
90
|
};
|
|
@@ -216,12 +226,11 @@ class PictViewFlowPropertiesPanel extends libPictView
|
|
|
216
226
|
let tmpFO = pPanelsLayer.querySelector(`[data-panel-hash="${pPanelData.Hash}"]`);
|
|
217
227
|
if (tmpFO)
|
|
218
228
|
{
|
|
219
|
-
// Render appearance and help tabs
|
|
229
|
+
// Render appearance and help tabs. Tab-switching click handlers
|
|
230
|
+
// are inline `onclick=` attributes in Flow-PanelChrome-Template
|
|
231
|
+
// that call FlowView._handlePanelTabClick → switchPanelTab.
|
|
220
232
|
this._renderAppearanceTab(pPanelData, tmpFO);
|
|
221
233
|
this._renderHelpTab(pPanelData, tmpFO);
|
|
222
|
-
|
|
223
|
-
// Wire up tab switching
|
|
224
|
-
this._wireTabSwitching(tmpFO);
|
|
225
234
|
}
|
|
226
235
|
}
|
|
227
236
|
|
|
@@ -559,27 +568,16 @@ class PictViewFlowPropertiesPanel extends libPictView
|
|
|
559
568
|
BodyFillValue: tmpStyle.BodyFill || tmpDefaultBodyFill,
|
|
560
569
|
BodyStrokeValue: tmpStyle.BodyStroke || tmpDefaultBodyStroke,
|
|
561
570
|
BodyStrokeWidthValue: tmpStyle.BodyStrokeWidth || 1,
|
|
562
|
-
TitleBarColorValue: tmpStyle.TitleBarColor || tmpDefaultTitleBarColor
|
|
571
|
+
TitleBarColorValue: tmpStyle.TitleBarColor || tmpDefaultTitleBarColor,
|
|
572
|
+
NodeHash: pPanelData.NodeHash,
|
|
573
|
+
FlowViewIdentifier: this._FlowView.options.ViewIdentifier
|
|
563
574
|
};
|
|
564
575
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
//
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
{
|
|
571
|
-
let tmpInput = tmpInputs[i];
|
|
572
|
-
let tmpProp = tmpInput.getAttribute('data-prop');
|
|
573
|
-
|
|
574
|
-
tmpInput.addEventListener('input', (pEvent) =>
|
|
575
|
-
{
|
|
576
|
-
pEvent.stopPropagation();
|
|
577
|
-
this._applyNodePropChange(pPanelData.NodeHash, tmpProp, tmpInput.value, tmpInput.type);
|
|
578
|
-
});
|
|
579
|
-
|
|
580
|
-
// Prevent pointer events from propagating to SVG drag handler
|
|
581
|
-
tmpInput.addEventListener('pointerdown', (pEvent) => { pEvent.stopPropagation(); });
|
|
582
|
-
}
|
|
576
|
+
// Inputs in the rendered template carry inline oninput/onpointerdown
|
|
577
|
+
// handlers that call the FlowView's _applyNodePropChange — no
|
|
578
|
+
// addEventListener wiring needed here.
|
|
579
|
+
this.pict.ContentAssignment.assignContent(tmpAppearancePane,
|
|
580
|
+
this.pict.parseTemplateByHash('Flow-NodeProps-Editor', tmpRecord));
|
|
583
581
|
}
|
|
584
582
|
|
|
585
583
|
/**
|
|
@@ -619,42 +617,37 @@ class PictViewFlowPropertiesPanel extends libPictView
|
|
|
619
617
|
}
|
|
620
618
|
|
|
621
619
|
/**
|
|
622
|
-
*
|
|
620
|
+
* Switch the visible tab pane within a panel foreignObject. Invoked
|
|
621
|
+
* from the inline `onclick` handler on tab buttons in
|
|
622
|
+
* Flow-PanelChrome-Template via FlowView._handlePanelTabClick.
|
|
623
623
|
*
|
|
624
|
-
* @param {Element}
|
|
624
|
+
* @param {Element} pTabElement - The clicked tab button
|
|
625
625
|
*/
|
|
626
|
-
|
|
626
|
+
switchPanelTab(pTabElement)
|
|
627
627
|
{
|
|
628
|
-
let
|
|
629
|
-
|
|
628
|
+
let tmpForeignObject = pTabElement.closest('foreignObject');
|
|
629
|
+
if (!tmpForeignObject) return;
|
|
630
|
+
|
|
631
|
+
let tmpTarget = pTabElement.getAttribute('data-tab-target');
|
|
632
|
+
let tmpTabs = tmpForeignObject.querySelectorAll('.pict-flow-panel-tab');
|
|
633
|
+
let tmpPanes = tmpForeignObject.querySelectorAll('.pict-flow-panel-tab-pane');
|
|
630
634
|
|
|
631
635
|
for (let i = 0; i < tmpTabs.length; i++)
|
|
632
636
|
{
|
|
633
|
-
tmpTabs[i].
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
{
|
|
641
|
-
tmpTabs[j].classList.remove('active');
|
|
642
|
-
}
|
|
643
|
-
for (let j = 0; j < tmpPanes.length; j++)
|
|
644
|
-
{
|
|
645
|
-
tmpPanes[j].classList.remove('active');
|
|
646
|
-
tmpPanes[j].style.display = 'none';
|
|
647
|
-
}
|
|
637
|
+
tmpTabs[i].classList.remove('active');
|
|
638
|
+
}
|
|
639
|
+
for (let i = 0; i < tmpPanes.length; i++)
|
|
640
|
+
{
|
|
641
|
+
tmpPanes[i].classList.remove('active');
|
|
642
|
+
tmpPanes[i].style.display = 'none';
|
|
643
|
+
}
|
|
648
644
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
tmpTargetPane.style.display = 'block';
|
|
656
|
-
}
|
|
657
|
-
});
|
|
645
|
+
pTabElement.classList.add('active');
|
|
646
|
+
let tmpTargetPane = tmpForeignObject.querySelector('.pict-flow-panel-tab-pane[data-tab="' + tmpTarget + '"]');
|
|
647
|
+
if (tmpTargetPane)
|
|
648
|
+
{
|
|
649
|
+
tmpTargetPane.classList.add('active');
|
|
650
|
+
tmpTargetPane.style.display = 'block';
|
|
658
651
|
}
|
|
659
652
|
}
|
|
660
653
|
|