pict-section-flow 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/.claude/launch.json +11 -0
  2. package/docs/README.md +51 -0
  3. package/example_applications/simple_cards/source/Pict-Application-FlowExample.js +105 -0
  4. package/example_applications/simple_cards/source/cards/FlowCard-Comment.js +36 -0
  5. package/example_applications/simple_cards/source/cards/FlowCard-DataPreview.js +42 -0
  6. package/example_applications/simple_cards/source/cards/FlowCard-Each.js +1 -1
  7. package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +1 -1
  8. package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +1 -1
  9. package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +1 -1
  10. package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +1 -1
  11. package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +1 -1
  12. package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +1 -1
  13. package/example_applications/simple_cards/source/cards/FlowCard-Sparkline.js +98 -0
  14. package/example_applications/simple_cards/source/cards/FlowCard-StatusMonitor.js +44 -0
  15. package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +1 -1
  16. package/example_applications/simple_cards/source/views/PictView-FlowExample-MainWorkspace.js +9 -1
  17. package/package.json +2 -2
  18. package/source/Pict-Section-Flow.js +8 -1
  19. package/source/PictFlowCard.js +49 -1
  20. package/source/providers/PictProvider-Flow-CSS.js +1440 -0
  21. package/source/providers/PictProvider-Flow-ConnectorShapes.js +413 -0
  22. package/source/providers/PictProvider-Flow-Geometry.js +43 -0
  23. package/source/providers/PictProvider-Flow-Icons.js +335 -0
  24. package/source/providers/PictProvider-Flow-Layouts.js +214 -2
  25. package/source/providers/PictProvider-Flow-NodeTypes.js +30 -7
  26. package/source/providers/PictProvider-Flow-Noise.js +241 -0
  27. package/source/providers/PictProvider-Flow-PanelChrome.js +19 -0
  28. package/source/providers/PictProvider-Flow-Theme.js +755 -0
  29. package/source/services/PictService-Flow-ConnectionRenderer.js +95 -32
  30. package/source/services/PictService-Flow-PanelManager.js +188 -0
  31. package/source/services/PictService-Flow-SelectionManager.js +109 -0
  32. package/source/services/PictService-Flow-Tether.js +52 -25
  33. package/source/services/PictService-Flow-ViewportManager.js +176 -0
  34. package/source/views/PictView-Flow-FloatingToolbar.js +352 -0
  35. package/source/views/PictView-Flow-Node.js +654 -169
  36. package/source/views/PictView-Flow-PropertiesPanel.js +176 -1
  37. package/source/views/PictView-Flow-Toolbar.js +846 -379
  38. package/source/views/PictView-Flow.js +279 -671
@@ -0,0 +1,176 @@
1
+ const libFableServiceProviderBase = require('fable-serviceproviderbase');
2
+
3
+ /**
4
+ * PictService-Flow-ViewportManager
5
+ *
6
+ * Manages viewport transforms (pan/zoom), coordinate conversion between
7
+ * screen and SVG space, zoom-to-fit calculations, and fullscreen toggling
8
+ * for the flow diagram.
9
+ */
10
+ class PictServiceFlowViewportManager extends libFableServiceProviderBase
11
+ {
12
+ constructor(pFable, pOptions, pServiceHash)
13
+ {
14
+ super(pFable, pOptions, pServiceHash);
15
+
16
+ this.serviceType = 'PictServiceFlowViewportManager';
17
+
18
+ this._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;
19
+
20
+ this._IsFullscreen = false;
21
+ }
22
+
23
+ /**
24
+ * Update the viewport transform (pan and zoom)
25
+ */
26
+ updateViewportTransform()
27
+ {
28
+ if (!this._FlowView._ViewportElement) return;
29
+ let tmpVS = this._FlowView._FlowData.ViewState;
30
+ this._FlowView._ViewportElement.setAttribute('transform',
31
+ `translate(${tmpVS.PanX}, ${tmpVS.PanY}) scale(${tmpVS.Zoom})`
32
+ );
33
+ }
34
+
35
+ /**
36
+ * Set zoom level
37
+ * @param {number} pZoom - The zoom level
38
+ * @param {number} [pFocusX] - X coordinate to zoom toward (SVG space)
39
+ * @param {number} [pFocusY] - Y coordinate to zoom toward (SVG space)
40
+ */
41
+ setZoom(pZoom, pFocusX, pFocusY)
42
+ {
43
+ let tmpNewZoom = Math.max(this._FlowView.options.MinZoom, Math.min(this._FlowView.options.MaxZoom, pZoom));
44
+ let tmpOldZoom = this._FlowView._FlowData.ViewState.Zoom;
45
+
46
+ if (typeof pFocusX === 'number' && typeof pFocusY === 'number')
47
+ {
48
+ // Zoom toward focus point
49
+ let tmpVS = this._FlowView._FlowData.ViewState;
50
+ tmpVS.PanX = pFocusX - (pFocusX - tmpVS.PanX) * (tmpNewZoom / tmpOldZoom);
51
+ tmpVS.PanY = pFocusY - (pFocusY - tmpVS.PanY) * (tmpNewZoom / tmpOldZoom);
52
+ }
53
+
54
+ this._FlowView._FlowData.ViewState.Zoom = tmpNewZoom;
55
+ this.updateViewportTransform();
56
+ }
57
+
58
+ /**
59
+ * Zoom to fit all nodes in the viewport
60
+ */
61
+ zoomToFit()
62
+ {
63
+ if (this._FlowView._FlowData.Nodes.length === 0) return;
64
+ if (!this._FlowView._SVGElement) return;
65
+
66
+ let tmpMinX = Infinity, tmpMinY = Infinity;
67
+ let tmpMaxX = -Infinity, tmpMaxY = -Infinity;
68
+
69
+ for (let i = 0; i < this._FlowView._FlowData.Nodes.length; i++)
70
+ {
71
+ let tmpNode = this._FlowView._FlowData.Nodes[i];
72
+ tmpMinX = Math.min(tmpMinX, tmpNode.X);
73
+ tmpMinY = Math.min(tmpMinY, tmpNode.Y);
74
+ tmpMaxX = Math.max(tmpMaxX, tmpNode.X + tmpNode.Width);
75
+ tmpMaxY = Math.max(tmpMaxY, tmpNode.Y + tmpNode.Height);
76
+ }
77
+
78
+ let tmpPadding = 50;
79
+ let tmpFlowWidth = tmpMaxX - tmpMinX + tmpPadding * 2;
80
+ let tmpFlowHeight = tmpMaxY - tmpMinY + tmpPadding * 2;
81
+
82
+ let tmpSVGRect = this._FlowView._SVGElement.getBoundingClientRect();
83
+ let tmpScaleX = tmpSVGRect.width / tmpFlowWidth;
84
+ let tmpScaleY = tmpSVGRect.height / tmpFlowHeight;
85
+ let tmpZoom = Math.min(tmpScaleX, tmpScaleY, 1.0); // Don't zoom in past 1.0
86
+ tmpZoom = Math.max(this._FlowView.options.MinZoom, Math.min(this._FlowView.options.MaxZoom, tmpZoom));
87
+
88
+ let tmpCenterX = (tmpMinX + tmpMaxX) / 2;
89
+ let tmpCenterY = (tmpMinY + tmpMaxY) / 2;
90
+
91
+ this._FlowView._FlowData.ViewState.Zoom = tmpZoom;
92
+ this._FlowView._FlowData.ViewState.PanX = (tmpSVGRect.width / 2) - (tmpCenterX * tmpZoom);
93
+ this._FlowView._FlowData.ViewState.PanY = (tmpSVGRect.height / 2) - (tmpCenterY * tmpZoom);
94
+
95
+ this.updateViewportTransform();
96
+ }
97
+
98
+ /**
99
+ * Convert screen coordinates to SVG viewport coordinates
100
+ * @param {number} pScreenX
101
+ * @param {number} pScreenY
102
+ * @returns {{x: number, y: number}}
103
+ */
104
+ screenToSVGCoords(pScreenX, pScreenY)
105
+ {
106
+ if (!this._FlowView._SVGElement)
107
+ {
108
+ return { x: pScreenX, y: pScreenY };
109
+ }
110
+
111
+ let tmpPoint = this._FlowView._SVGElement.createSVGPoint();
112
+ tmpPoint.x = pScreenX;
113
+ tmpPoint.y = pScreenY;
114
+
115
+ let tmpCTM = this._FlowView._SVGElement.getScreenCTM();
116
+ if (tmpCTM)
117
+ {
118
+ let tmpInverse = tmpCTM.inverse();
119
+ let tmpTransformed = tmpPoint.matrixTransform(tmpInverse);
120
+ // Account for viewport pan/zoom
121
+ let tmpVS = this._FlowView._FlowData.ViewState;
122
+ return {
123
+ x: (tmpTransformed.x - tmpVS.PanX) / tmpVS.Zoom,
124
+ y: (tmpTransformed.y - tmpVS.PanY) / tmpVS.Zoom
125
+ };
126
+ }
127
+
128
+ return { x: pScreenX, y: pScreenY };
129
+ }
130
+
131
+ /**
132
+ * Toggle fullscreen mode on the flow editor container.
133
+ * Uses a CSS fixed-position overlay instead of the Fullscreen API.
134
+ * @returns {boolean} The new fullscreen state
135
+ */
136
+ toggleFullscreen()
137
+ {
138
+ let tmpViewIdentifier = this._FlowView.options.ViewIdentifier;
139
+ let tmpContainerElements = this._FlowView.pict.ContentAssignment.getElement(`#Flow-Wrapper-${tmpViewIdentifier}`);
140
+ if (tmpContainerElements.length < 1) return this._IsFullscreen;
141
+
142
+ let tmpContainer = tmpContainerElements[0];
143
+
144
+ this._IsFullscreen = !this._IsFullscreen;
145
+
146
+ if (this._IsFullscreen)
147
+ {
148
+ tmpContainer.classList.add('pict-flow-fullscreen');
149
+ }
150
+ else
151
+ {
152
+ tmpContainer.classList.remove('pict-flow-fullscreen');
153
+ }
154
+
155
+ return this._IsFullscreen;
156
+ }
157
+
158
+ /**
159
+ * Exit fullscreen mode if currently active.
160
+ */
161
+ exitFullscreen()
162
+ {
163
+ if (!this._IsFullscreen) return;
164
+
165
+ let tmpViewIdentifier = this._FlowView.options.ViewIdentifier;
166
+ let tmpContainerElements = this._FlowView.pict.ContentAssignment.getElement(`#Flow-Wrapper-${tmpViewIdentifier}`);
167
+ if (tmpContainerElements.length > 0)
168
+ {
169
+ tmpContainerElements[0].classList.remove('pict-flow-fullscreen');
170
+ }
171
+
172
+ this._IsFullscreen = false;
173
+ }
174
+ }
175
+
176
+ module.exports = PictServiceFlowViewportManager;
@@ -0,0 +1,352 @@
1
+ const libPictView = require('pict-view');
2
+
3
+ const _DefaultConfiguration =
4
+ {
5
+ ViewIdentifier: 'Flow-FloatingToolbar',
6
+
7
+ DefaultRenderable: 'Flow-FloatingToolbar-Content',
8
+ DefaultDestinationAddress: '#Flow-FloatingToolbar-Container',
9
+
10
+ AutoRender: false,
11
+
12
+ FlowViewIdentifier: 'Pict-Flow',
13
+
14
+ CSS: false,
15
+
16
+ Templates:
17
+ [
18
+ {
19
+ Hash: 'Flow-FloatingToolbar-Template',
20
+ Template: /*html*/`
21
+ <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">
23
+ <span id="Flow-FloatingIcon-grip-{~D:Record.FlowViewIdentifier~}"></span>
24
+ </div>
25
+ <button class="pict-flow-floating-btn" data-flow-action="add-node" title="Add Node">
26
+ <span id="Flow-FloatingIcon-plus-{~D:Record.FlowViewIdentifier~}"></span>
27
+ </button>
28
+ <button class="pict-flow-floating-btn danger" data-flow-action="delete-selected" title="Delete Selected">
29
+ <span id="Flow-FloatingIcon-trash-{~D:Record.FlowViewIdentifier~}"></span>
30
+ </button>
31
+ <div class="pict-flow-floating-separator"></div>
32
+ <button class="pict-flow-floating-btn" data-flow-action="zoom-in" title="Zoom In">
33
+ <span id="Flow-FloatingIcon-zoom-in-{~D:Record.FlowViewIdentifier~}"></span>
34
+ </button>
35
+ <button class="pict-flow-floating-btn" data-flow-action="zoom-out" title="Zoom Out">
36
+ <span id="Flow-FloatingIcon-zoom-out-{~D:Record.FlowViewIdentifier~}"></span>
37
+ </button>
38
+ <button class="pict-flow-floating-btn" data-flow-action="zoom-fit" title="Fit to View">
39
+ <span id="Flow-FloatingIcon-zoom-fit-{~D:Record.FlowViewIdentifier~}"></span>
40
+ </button>
41
+ <div class="pict-flow-floating-separator"></div>
42
+ <button class="pict-flow-floating-btn" data-flow-action="auto-layout" title="Auto Layout">
43
+ <span id="Flow-FloatingIcon-auto-layout-{~D:Record.FlowViewIdentifier~}"></span>
44
+ </button>
45
+ <button class="pict-flow-floating-btn" data-flow-action="cards-popup" title="Cards">
46
+ <span id="Flow-FloatingIcon-cards-{~D:Record.FlowViewIdentifier~}"></span>
47
+ </button>
48
+ <button class="pict-flow-floating-btn" data-flow-action="layout-popup" title="Layout">
49
+ <span id="Flow-FloatingIcon-layout-{~D:Record.FlowViewIdentifier~}"></span>
50
+ </button>
51
+ <button class="pict-flow-floating-btn" data-flow-action="fullscreen" title="Toggle Fullscreen">
52
+ <span id="Flow-FloatingIcon-fullscreen-{~D:Record.FlowViewIdentifier~}"></span>
53
+ </button>
54
+ <div class="pict-flow-floating-separator"></div>
55
+ <button class="pict-flow-floating-btn" data-flow-action="dock-toolbar" title="Dock Toolbar">
56
+ <span id="Flow-FloatingIcon-dock-{~D:Record.FlowViewIdentifier~}"></span>
57
+ </button>
58
+ </div>
59
+ `
60
+ }
61
+ ],
62
+
63
+ Renderables:
64
+ [
65
+ {
66
+ RenderableHash: 'Flow-FloatingToolbar-Content',
67
+ TemplateHash: 'Flow-FloatingToolbar-Template',
68
+ DestinationAddress: '#Flow-FloatingToolbar-Container',
69
+ RenderMethod: 'replace'
70
+ }
71
+ ]
72
+ };
73
+
74
+ class PictViewFlowFloatingToolbar extends libPictView
75
+ {
76
+ constructor(pFable, pOptions, pServiceHash)
77
+ {
78
+ let tmpOptions = Object.assign({}, JSON.parse(JSON.stringify(_DefaultConfiguration)), pOptions);
79
+ super(pFable, tmpOptions, pServiceHash);
80
+
81
+ this.serviceType = 'PictViewFlowFloatingToolbar';
82
+
83
+ this._ToolbarView = null;
84
+ this._FlowView = null;
85
+
86
+ this._IsCollapsed = false;
87
+
88
+ this._IsDragging = false;
89
+ this._DragStartX = 0;
90
+ this._DragStartY = 0;
91
+ this._DragStartLeft = 0;
92
+ this._DragStartTop = 0;
93
+
94
+ this._BoundMouseMove = null;
95
+ this._BoundMouseUp = null;
96
+ }
97
+
98
+ render(pRenderableHash, pRenderDestinationAddress, pTemplateRecordAddress)
99
+ {
100
+ return super.render(pRenderableHash, pRenderDestinationAddress, this.options);
101
+ }
102
+
103
+ onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent)
104
+ {
105
+ let tmpFlowViewIdentifier = this.options.FlowViewIdentifier;
106
+
107
+ // Bind click delegation for action buttons
108
+ let tmpFloatingToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);
109
+ if (tmpFloatingToolbar.length > 0)
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
156
+ this._populateIcons();
157
+
158
+ return super.onAfterRender(pRenderable, pRenderDestinationAddress, pRecord, pContent);
159
+ }
160
+
161
+ /**
162
+ * Populate SVG icons into all floating toolbar button spans.
163
+ */
164
+ _populateIcons()
165
+ {
166
+ let tmpIconProvider = this._FlowView ? this._FlowView._IconProvider : null;
167
+ if (!tmpIconProvider) return;
168
+
169
+ let tmpFlowViewIdentifier = this.options.FlowViewIdentifier;
170
+
171
+ let tmpIconMap =
172
+ {
173
+ 'grip': 'grip',
174
+ 'plus': 'plus',
175
+ 'trash': 'trash',
176
+ 'zoom-in': 'zoom-in',
177
+ 'zoom-out': 'zoom-out',
178
+ 'zoom-fit': 'zoom-fit',
179
+ 'auto-layout': 'auto-layout',
180
+ 'cards': 'cards',
181
+ 'layout': 'layout',
182
+ 'fullscreen': 'fullscreen',
183
+ 'dock': 'dock'
184
+ };
185
+
186
+ let tmpKeys = Object.keys(tmpIconMap);
187
+ for (let i = 0; i < tmpKeys.length; i++)
188
+ {
189
+ let tmpElementId = `Flow-FloatingIcon-${tmpKeys[i]}-${tmpFlowViewIdentifier}`;
190
+ let tmpElements = this.pict.ContentAssignment.getElement(`#${tmpElementId}`);
191
+ if (tmpElements.length > 0)
192
+ {
193
+ tmpElements[0].innerHTML = tmpIconProvider.getIconSVGMarkup(tmpIconMap[tmpKeys[i]], 16);
194
+ }
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Toggle the floating toolbar between expanded and collapsed (grip-only square) states.
200
+ */
201
+ _toggleCollapse()
202
+ {
203
+ let tmpFlowViewIdentifier = this.options.FlowViewIdentifier;
204
+ let tmpToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);
205
+ if (tmpToolbar.length < 1) return;
206
+
207
+ this._IsCollapsed = !this._IsCollapsed;
208
+
209
+ if (this._IsCollapsed)
210
+ {
211
+ tmpToolbar[0].classList.add('collapsed');
212
+ }
213
+ else
214
+ {
215
+ tmpToolbar[0].classList.remove('collapsed');
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Start dragging the floating toolbar.
221
+ * @param {MouseEvent} pEvent
222
+ */
223
+ _startDrag(pEvent)
224
+ {
225
+ pEvent.preventDefault();
226
+
227
+ let tmpFlowViewIdentifier = this.options.FlowViewIdentifier;
228
+ let tmpToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);
229
+ if (tmpToolbar.length < 1) return;
230
+
231
+ let tmpEl = tmpToolbar[0];
232
+ this._IsDragging = true;
233
+ this._DragStartX = pEvent.clientX;
234
+ this._DragStartY = pEvent.clientY;
235
+ this._DragStartLeft = tmpEl.offsetLeft;
236
+ this._DragStartTop = tmpEl.offsetTop;
237
+
238
+ this._BoundMouseMove = (pMoveEvent) => { this._onDragMove(pMoveEvent); };
239
+ this._BoundMouseUp = () => { this._onDragEnd(); };
240
+
241
+ document.addEventListener('mousemove', this._BoundMouseMove);
242
+ document.addEventListener('mouseup', this._BoundMouseUp);
243
+ }
244
+
245
+ /**
246
+ * Handle drag movement.
247
+ * @param {MouseEvent} pEvent
248
+ */
249
+ _onDragMove(pEvent)
250
+ {
251
+ if (!this._IsDragging) return;
252
+
253
+ let tmpFlowViewIdentifier = this.options.FlowViewIdentifier;
254
+ let tmpToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);
255
+ if (tmpToolbar.length < 1) return;
256
+
257
+ let tmpEl = tmpToolbar[0];
258
+ let tmpDeltaX = pEvent.clientX - this._DragStartX;
259
+ let tmpDeltaY = pEvent.clientY - this._DragStartY;
260
+
261
+ let tmpNewLeft = this._DragStartLeft + tmpDeltaX;
262
+ let tmpNewTop = this._DragStartTop + tmpDeltaY;
263
+
264
+ // Clamp to parent bounds
265
+ let tmpParent = tmpEl.parentElement;
266
+ if (tmpParent)
267
+ {
268
+ let tmpMaxLeft = tmpParent.clientWidth - tmpEl.offsetWidth;
269
+ let tmpMaxTop = tmpParent.clientHeight - tmpEl.offsetHeight;
270
+ tmpNewLeft = Math.max(0, Math.min(tmpNewLeft, tmpMaxLeft));
271
+ tmpNewTop = Math.max(0, Math.min(tmpNewTop, tmpMaxTop));
272
+ }
273
+
274
+ tmpEl.style.left = tmpNewLeft + 'px';
275
+ tmpEl.style.top = tmpNewTop + 'px';
276
+ }
277
+
278
+ /**
279
+ * End dragging.
280
+ */
281
+ _onDragEnd()
282
+ {
283
+ this._IsDragging = false;
284
+
285
+ if (this._BoundMouseMove)
286
+ {
287
+ document.removeEventListener('mousemove', this._BoundMouseMove);
288
+ this._BoundMouseMove = null;
289
+ }
290
+ if (this._BoundMouseUp)
291
+ {
292
+ document.removeEventListener('mouseup', this._BoundMouseUp);
293
+ this._BoundMouseUp = null;
294
+ }
295
+
296
+ // Save position
297
+ if (this._ToolbarView)
298
+ {
299
+ let tmpFlowViewIdentifier = this.options.FlowViewIdentifier;
300
+ let tmpToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);
301
+ if (tmpToolbar.length > 0)
302
+ {
303
+ this._ToolbarView._FloatingPosition.X = tmpToolbar[0].offsetLeft;
304
+ this._ToolbarView._FloatingPosition.Y = tmpToolbar[0].offsetTop;
305
+ }
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Show the floating toolbar at the saved position.
311
+ */
312
+ show()
313
+ {
314
+ let tmpFlowViewIdentifier = this.options.FlowViewIdentifier;
315
+ let tmpContainer = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-Container-${tmpFlowViewIdentifier}`);
316
+ if (tmpContainer.length > 0)
317
+ {
318
+ tmpContainer[0].style.display = 'block';
319
+ }
320
+
321
+ let tmpToolbar = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-${tmpFlowViewIdentifier}`);
322
+ if (tmpToolbar.length > 0 && this._ToolbarView)
323
+ {
324
+ tmpToolbar[0].style.left = this._ToolbarView._FloatingPosition.X + 'px';
325
+ tmpToolbar[0].style.top = this._ToolbarView._FloatingPosition.Y + 'px';
326
+ }
327
+
328
+ // Restore expanded state when showing
329
+ if (this._IsCollapsed && tmpToolbar.length > 0)
330
+ {
331
+ this._IsCollapsed = false;
332
+ tmpToolbar[0].classList.remove('collapsed');
333
+ }
334
+ }
335
+
336
+ /**
337
+ * Hide the floating toolbar.
338
+ */
339
+ hide()
340
+ {
341
+ let tmpFlowViewIdentifier = this.options.FlowViewIdentifier;
342
+ let tmpContainer = this.pict.ContentAssignment.getElement(`#Flow-FloatingToolbar-Container-${tmpFlowViewIdentifier}`);
343
+ if (tmpContainer.length > 0)
344
+ {
345
+ tmpContainer[0].style.display = 'none';
346
+ }
347
+ }
348
+ }
349
+
350
+ module.exports = PictViewFlowFloatingToolbar;
351
+
352
+ module.exports.default_configuration = _DefaultConfiguration;