pict-section-flow 1.4.0 → 2.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/package.json +7 -2
- package/source/Pict-Section-Flow.js +20 -14
- package/source/providers/PictProvider-Flow-Background.js +303 -0
- package/source/providers/PictProvider-Flow-CSS.js +73 -7
- package/source/providers/PictProvider-Flow-Geometry.js +11 -421
- package/source/providers/PictProvider-Flow-Icons.js +12 -0
- package/source/providers/PictProvider-Flow-Layouts.js +107 -0
- package/source/services/PictService-Flow-ConnectionRenderer.js +1 -1
- package/source/services/PictService-Flow-CursorManager.js +113 -0
- package/source/services/PictService-Flow-InteractionManager.js +439 -59
- package/source/services/PictService-Flow-Layout.js +21 -16
- package/source/services/PictService-Flow-PathGenerator.js +30 -417
- package/source/services/PictService-Flow-RenderManager.js +9 -1
- package/source/services/PictService-Flow-ViewportManager.js +102 -0
- package/source/views/PictView-Flow-FloatingToolbar.js +5 -1
- package/source/views/PictView-Flow-Node.js +29 -0
- package/source/views/PictView-Flow-Toolbar.js +50 -3
- package/source/views/PictView-Flow.js +591 -2
- package/.claude/launch.json +0 -11
- package/docs/.nojekyll +0 -0
- package/docs/Architecture.md +0 -163
- package/docs/Custom-Styling.md +0 -275
- package/docs/Data_Model.md +0 -149
- package/docs/Event_System.md +0 -156
- package/docs/Getting_Started.md +0 -237
- package/docs/Implementation_Reference.md +0 -528
- package/docs/Layout_Persistence.md +0 -117
- package/docs/README.md +0 -103
- package/docs/Theme_Integration.md +0 -150
- package/docs/_brand.json +0 -18
- package/docs/_cover.md +0 -17
- package/docs/_playground.json +0 -24
- package/docs/_sidebar.md +0 -57
- package/docs/_topbar.md +0 -8
- package/docs/_version.json +0 -7
- package/docs/api/PictFlowCard.md +0 -216
- package/docs/api/PictFlowCardPropertiesPanel.md +0 -235
- package/docs/api/addConnection.md +0 -101
- package/docs/api/addNode.md +0 -137
- package/docs/api/autoLayout.md +0 -77
- package/docs/api/getFlowData.md +0 -112
- package/docs/api/marshalToView.md +0 -95
- package/docs/api/openPanel.md +0 -128
- package/docs/api/registerHandler.md +0 -174
- package/docs/api/registerNodeType.md +0 -142
- package/docs/api/removeConnection.md +0 -57
- package/docs/api/removeNode.md +0 -80
- package/docs/api/saveLayout.md +0 -152
- package/docs/api/screenToSVGCoords.md +0 -68
- package/docs/api/selectNode.md +0 -116
- package/docs/api/setTheme.md +0 -168
- package/docs/api/setZoom.md +0 -97
- package/docs/api/toggleFullscreen.md +0 -68
- package/docs/card-help/EACH.md +0 -19
- package/docs/card-help/FREAD.md +0 -24
- package/docs/card-help/FWRITE.md +0 -24
- package/docs/card-help/GET.md +0 -22
- package/docs/card-help/ITE.md +0 -23
- package/docs/card-help/LOG.md +0 -23
- package/docs/card-help/NOTE.md +0 -17
- package/docs/card-help/PREV.md +0 -18
- package/docs/card-help/SET.md +0 -27
- package/docs/card-help/SPKL.md +0 -22
- package/docs/card-help/STAT.md +0 -23
- package/docs/card-help/SW.md +0 -25
- package/docs/diagrams/architecture-at-a-glance.excalidraw +0 -4270
- package/docs/diagrams/architecture-at-a-glance.mmd +0 -30
- package/docs/diagrams/architecture-at-a-glance.svg +0 -2
- package/docs/diagrams/data-flow.excalidraw +0 -1451
- package/docs/diagrams/data-flow.mmd +0 -17
- package/docs/diagrams/data-flow.svg +0 -2
- package/docs/diagrams/high-level-design.excalidraw +0 -5767
- package/docs/diagrams/high-level-design.mmd +0 -86
- package/docs/diagrams/high-level-design.svg +0 -2
- package/docs/diagrams/relationships.excalidraw +0 -3852
- package/docs/diagrams/relationships.mmd +0 -9
- package/docs/diagrams/relationships.svg +0 -2
- package/docs/diagrams/service-initialization-sequence.excalidraw +0 -1466
- package/docs/diagrams/service-initialization-sequence.mmd +0 -19
- package/docs/diagrams/service-initialization-sequence.svg +0 -2
- package/docs/diagrams/svg-layer-structure.excalidraw +0 -1060
- package/docs/diagrams/svg-layer-structure.mmd +0 -18
- package/docs/diagrams/svg-layer-structure.svg +0 -2
- package/docs/examples/README.md +0 -9
- package/docs/examples/simple_cards/README.md +0 -677
- package/docs/examples/simple_cards/css/flowexample.css +0 -65
- package/docs/examples/simple_cards/index.html +0 -32
- package/docs/examples/simple_cards/js/pict.min.js +0 -12
- package/docs/examples/simple_cards/pict-section-flow-example-simple-cards.compatible.min.js +0 -1
- package/docs/index.html +0 -38
- package/docs/playground/app.json +0 -6
- package/docs/playground/appdata.json +0 -85
- package/docs/playground/application.js +0 -23
- package/docs/playground/pict.json +0 -17
- package/docs/playground/runtime/pict-application.min.js +0 -2
- package/docs/playground/runtime/pict-section-flow.min.js +0 -2
- package/docs/playground/runtime/pict-section-modal.min.js +0 -2
- package/docs/playground/runtime/pict.min.js +0 -12
- package/docs/retold-catalog.json +0 -244
- package/docs/retold-keyword-index.json +0 -26028
- package/example_applications/simple_cards/css/flowexample.css +0 -65
- package/example_applications/simple_cards/html/index.html +0 -32
- package/example_applications/simple_cards/package.json +0 -52
- package/example_applications/simple_cards/source/Pict-Application-FlowExample-Configuration.json +0 -15
- package/example_applications/simple_cards/source/Pict-Application-FlowExample.js +0 -539
- package/example_applications/simple_cards/source/card-help-content.js +0 -16
- package/example_applications/simple_cards/source/cards/FlowCard-Comment.js +0 -38
- package/example_applications/simple_cards/source/cards/FlowCard-DataPreview.js +0 -44
- package/example_applications/simple_cards/source/cards/FlowCard-Each.js +0 -38
- package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +0 -56
- package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +0 -50
- package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +0 -37
- package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +0 -49
- package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +0 -55
- package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +0 -97
- package/example_applications/simple_cards/source/cards/FlowCard-Sparkline.js +0 -100
- package/example_applications/simple_cards/source/cards/FlowCard-StatusMonitor.js +0 -46
- package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +0 -39
- package/example_applications/simple_cards/source/providers/PictRouter-FlowExample-Configuration.json +0 -22
- package/example_applications/simple_cards/source/sample-flows.js +0 -410
- package/example_applications/simple_cards/source/views/PictView-FlowExample-About.js +0 -184
- package/example_applications/simple_cards/source/views/PictView-FlowExample-BottomBar.js +0 -77
- package/example_applications/simple_cards/source/views/PictView-FlowExample-Documentation.js +0 -325
- package/example_applications/simple_cards/source/views/PictView-FlowExample-FileWriteInfo.js +0 -59
- package/example_applications/simple_cards/source/views/PictView-FlowExample-Layout.js +0 -90
- package/example_applications/simple_cards/source/views/PictView-FlowExample-MainWorkspace.js +0 -453
- package/example_applications/simple_cards/source/views/PictView-FlowExample-TopBar.js +0 -95
- package/scripts/generate-card-help.js +0 -214
- package/source/providers/edges/Edge-Bezier.js +0 -41
- package/source/providers/edges/Edge-Orthogonal.js +0 -37
- package/source/providers/edges/Edge-OrthogonalSnap.js +0 -72
- package/source/providers/edges/Edge-Perimeter-Linear.js +0 -31
- package/source/providers/edges/Edge-Perimeter-Orthogonal.js +0 -39
- package/source/providers/edges/Edge-Perimeter.js +0 -48
- package/source/providers/edges/Edge-PerimeterMath.js +0 -92
- package/source/providers/edges/Edge-Straight.js +0 -24
- package/source/providers/layouts/Layout-Circular.js +0 -203
- package/source/providers/layouts/Layout-Coerce.js +0 -40
- package/source/providers/layouts/Layout-Columnar.js +0 -134
- package/source/providers/layouts/Layout-Custom.js +0 -27
- package/source/providers/layouts/Layout-ForcedFromCenter.js +0 -256
- package/source/providers/layouts/Layout-Grid.js +0 -134
- package/source/providers/layouts/Layout-Layered.js +0 -155
- package/source/providers/layouts/Layout-Rank.js +0 -141
- package/source/providers/layouts/Layout-Staggered.js +0 -131
- package/source/providers/layouts/Layout-Tabular.js +0 -94
- package/test/CardPalette_tests.js +0 -43
- package/test/ConnectionHandleManager_tests.js +0 -717
- package/test/ConnectionRenderer_tests.js +0 -591
- package/test/ConnectionStyle_tests.js +0 -90
- package/test/DataManager_tests.js +0 -859
- package/test/Geometry_tests.js +0 -767
- package/test/InteractionManager_tests.js +0 -279
- package/test/Layout_tests.js +0 -1604
- package/test/NodeView_tests.js +0 -66
- package/test/PanelManager_tests.js +0 -172
- package/test/PathGenerator_tests.js +0 -978
- package/test/PortRenderer_tests.js +0 -376
- package/test/RenderManager_tests.js +0 -756
- package/test/Renderer_tests.js +0 -133
- package/test/SelectionManager_tests.js +0 -185
- package/test/StylePresets_tests.js +0 -153
- package/test/ToolbarExtraButtons_tests.js +0 -138
- package/test/UndirectedConnections_tests.js +0 -70
|
@@ -12,6 +12,7 @@ const libPictServiceFlowDataManager = require('../services/PictService-Flow-Data
|
|
|
12
12
|
const libPictServiceFlowConnectionHandleManager = require('../services/PictService-Flow-ConnectionHandleManager.js');
|
|
13
13
|
const libPictServiceFlowRenderManager = require('../services/PictService-Flow-RenderManager.js');
|
|
14
14
|
const libPictServiceFlowPortRenderer = require('../services/PictService-Flow-PortRenderer.js');
|
|
15
|
+
const libPictServiceFlowCursorManager = require('../services/PictService-Flow-CursorManager.js');
|
|
15
16
|
|
|
16
17
|
const libPictProviderFlowNodeTypes = require('../providers/PictProvider-Flow-NodeTypes.js');
|
|
17
18
|
const libPictProviderFlowEventHandler = require('../providers/PictProvider-Flow-EventHandler.js');
|
|
@@ -26,6 +27,7 @@ const libPictProviderFlowTheme = require('../providers/PictProvider-Flow-Theme.j
|
|
|
26
27
|
const libPictProviderFlowRenderer = require('../providers/PictProvider-Flow-Renderer.js');
|
|
27
28
|
const libPictProviderFlowStylePresets = require('../providers/PictProvider-Flow-StylePresets.js');
|
|
28
29
|
const libPictProviderFlowNoise = require('../providers/PictProvider-Flow-Noise.js');
|
|
30
|
+
const libPictProviderFlowBackground = require('../providers/PictProvider-Flow-Background.js');
|
|
29
31
|
|
|
30
32
|
const libPictViewFlowNode = require('./PictView-Flow-Node.js');
|
|
31
33
|
const libPictViewFlowToolbar = require('./PictView-Flow-Toolbar.js');
|
|
@@ -51,6 +53,11 @@ const _DefaultConfiguration =
|
|
|
51
53
|
|
|
52
54
|
TargetElementAddress: '#Flow-SVG-Container',
|
|
53
55
|
|
|
56
|
+
// Config profile: expands into a coherent bundle of the options below before
|
|
57
|
+
// your explicit options apply. null/'graph' = the historical behavior; also
|
|
58
|
+
// 'whiteboard', 'moodboard', 'presentation', 'erd'.
|
|
59
|
+
Profile: null,
|
|
60
|
+
|
|
54
61
|
EnableToolbar: true,
|
|
55
62
|
EnableAddNode: true,
|
|
56
63
|
EnableCardPalette: true,
|
|
@@ -67,6 +74,10 @@ const _DefaultConfiguration =
|
|
|
67
74
|
// When on, the selected node shows a bottom-right grip that resizes it by drag. Off by default
|
|
68
75
|
// so existing diagrams are unaffected; free-form canvases (moodboards) turn it on.
|
|
69
76
|
EnableNodeResizing: false,
|
|
77
|
+
// When on, the selected node shows a rotate grip on an arm above it; drag it to set the node's
|
|
78
|
+
// Rotation (degrees), Shift to snap to 15. Off by default; free-form canvases turn it on. The
|
|
79
|
+
// properties panel can still set Rotation directly, so both ways work.
|
|
80
|
+
EnableNodeRotation: false,
|
|
70
81
|
EnableGridSnap: false,
|
|
71
82
|
GridSnapSize: 20,
|
|
72
83
|
// When on, several nodes can be selected at once: shift-click a node to toggle it, drag on the
|
|
@@ -78,6 +89,14 @@ const _DefaultConfiguration =
|
|
|
78
89
|
EnableAlignmentGuides: false,
|
|
79
90
|
EnableLayoutMenu: true,
|
|
80
91
|
|
|
92
|
+
// Read-only / presentation mode. When true, editing interactions are inert
|
|
93
|
+
// (no drag, resize, connect, delete, panel edit); panning, zoom, selection,
|
|
94
|
+
// and node activation still work, and an onNodeActivate event fires on a node
|
|
95
|
+
// click so a host can show a read-only inspector. Toggle at runtime with
|
|
96
|
+
// setReadOnly(). The container also gets a 'pict-flow-readonly' class so chrome
|
|
97
|
+
// (ports, delete affordance, resize grips) can be hidden via CSS.
|
|
98
|
+
ReadOnly: false,
|
|
99
|
+
|
|
81
100
|
// Host-supplied toolbar buttons. Each entry is { Hash, Icon, Label?, Tooltip?, Active? } where Icon
|
|
82
101
|
// is a flow icon-provider key (edit, check, background, ...). They render in BOTH the docked and the
|
|
83
102
|
// floating toolbar (so they survive every toolbar mode) and, on click, fire onToolbarButton below.
|
|
@@ -91,6 +110,41 @@ const _DefaultConfiguration =
|
|
|
91
110
|
MaxZoom: 5.0,
|
|
92
111
|
ZoomStep: 0.1,
|
|
93
112
|
|
|
113
|
+
// Mouse-wheel behavior. 'zoom' (default) keeps the historical wheel-zoom; set
|
|
114
|
+
// 'pan' so the wheel scrolls the canvas (ctrl/cmd+wheel still zooms), or
|
|
115
|
+
// 'none' to ignore the wheel. WheelZoomRequiresModifier makes a plain wheel
|
|
116
|
+
// pan and only ctrl/cmd+wheel zoom while in 'zoom' mode (tames the
|
|
117
|
+
// "scrolling zooms too fast" feel). Sensitivities scale the per-tick amount.
|
|
118
|
+
WheelMode: 'zoom',
|
|
119
|
+
WheelZoomRequiresModifier: false,
|
|
120
|
+
WheelZoomSensitivity: 1,
|
|
121
|
+
WheelPanSensitivity: 1,
|
|
122
|
+
|
|
123
|
+
// Canvas background. false (default) keeps the built-in grid from the
|
|
124
|
+
// container template. Set an object to control it natively:
|
|
125
|
+
// { Style: 'grid'|'dots'|'solid'|'image'|'none', Color?, Image?, GridSize?, DotSize? }
|
|
126
|
+
// This replaces hand-painting the SVG from a consumer.
|
|
127
|
+
Background: false,
|
|
128
|
+
|
|
129
|
+
// Content frame: a dashed "page" rectangle in content space marking the
|
|
130
|
+
// intended bounds (origin + width + preferred end). false (default) draws
|
|
131
|
+
// nothing. Set { Enabled?, X, Y, Width, Height, FitOnLoad? }; fitToFrame()
|
|
132
|
+
// fits it to the viewport (handy for read-only / presentation). Drag-to-resize
|
|
133
|
+
// handles are a later addition.
|
|
134
|
+
Frame: false,
|
|
135
|
+
|
|
136
|
+
// Presentation fit. 'contain' (default) keeps the legacy fitToFrame/FitOnLoad
|
|
137
|
+
// behavior. 'width' fits the frame's WIDTH to the container (anchored top-left
|
|
138
|
+
// + FitTopMargin) and keeps it fit as the container resizes — the jumbotron /
|
|
139
|
+
// background / fullscreen presentation. Content outside the frame bleeds.
|
|
140
|
+
FitMode: 'contain',
|
|
141
|
+
FitTopMargin: 0,
|
|
142
|
+
|
|
143
|
+
// When true (and not read-only), the content frame gets drag handles: four edge
|
|
144
|
+
// handles to set top/width/height and a move handle to reposition the view-area box.
|
|
145
|
+
EnableFrameEditing: false,
|
|
146
|
+
MinimumFrameSize: 40,
|
|
147
|
+
|
|
94
148
|
DefaultNodeType: 'default',
|
|
95
149
|
DefaultNodeWidth: 180,
|
|
96
150
|
DefaultNodeHeight: 80,
|
|
@@ -107,6 +161,15 @@ const _DefaultConfiguration =
|
|
|
107
161
|
DefaultLayoutParameters: {},
|
|
108
162
|
DefaultLayoutAutoApply: false,
|
|
109
163
|
|
|
164
|
+
// Saved layouts (named position snapshots). LayoutStorage lets a host wire a
|
|
165
|
+
// server / IndexedDB backend by config instead of overriding the layout
|
|
166
|
+
// provider's methods: { read(cb), write(layouts, cb), delete(cb) } with
|
|
167
|
+
// Node-style cb(err, result). false (default) uses localStorage. When
|
|
168
|
+
// ApplyDefaultLayoutOnLoad is on, the layout marked default is applied once
|
|
169
|
+
// the graph first renders.
|
|
170
|
+
LayoutStorage: false,
|
|
171
|
+
ApplyDefaultLayoutOnLoad: false,
|
|
172
|
+
|
|
110
173
|
// Edge-theme subsystem defaults — null = inherit from active layout's
|
|
111
174
|
// `DefaultEdgeTheme` field, with hard fallback to 'Bezier'.
|
|
112
175
|
DefaultEdgeTheme: null,
|
|
@@ -175,11 +238,88 @@ const _DefaultConfiguration =
|
|
|
175
238
|
]
|
|
176
239
|
};
|
|
177
240
|
|
|
241
|
+
// Config profiles. Each is a partial option bundle that expands under the
|
|
242
|
+
// defaults and beneath the host's explicit options, so a single Profile knob
|
|
243
|
+
// gives a coherent setup that the host can still override field by field.
|
|
244
|
+
const _PROFILES =
|
|
245
|
+
{
|
|
246
|
+
// Directed graph editor (the historical default). Empty so the defaults stand.
|
|
247
|
+
graph: {},
|
|
248
|
+
|
|
249
|
+
// Free-form whiteboard: undirected links, resize, multi-select + alignment
|
|
250
|
+
// guides, wheel pans (ctrl/cmd zooms), dotted background, no layout menu.
|
|
251
|
+
whiteboard:
|
|
252
|
+
{
|
|
253
|
+
EnableUndirectedConnections: true,
|
|
254
|
+
EnableNodeResizing: true,
|
|
255
|
+
EnableNodeRotation: true,
|
|
256
|
+
EnableMultiSelect: true,
|
|
257
|
+
EnableAlignmentGuides: true,
|
|
258
|
+
EnableLayoutMenu: false,
|
|
259
|
+
WheelMode: 'pan',
|
|
260
|
+
WheelZoomRequiresModifier: true,
|
|
261
|
+
Background: { Style: 'dots' }
|
|
262
|
+
},
|
|
263
|
+
|
|
264
|
+
// Moodboard: free-form content cards, undirected links, no default node types
|
|
265
|
+
// or add-node button, edge-to-edge cards (no title bar), wheel pans.
|
|
266
|
+
moodboard:
|
|
267
|
+
{
|
|
268
|
+
EnableUndirectedConnections: true,
|
|
269
|
+
EnableNodeResizing: true,
|
|
270
|
+
EnableNodeRotation: true,
|
|
271
|
+
EnableAlignmentGuides: true,
|
|
272
|
+
EnableAddNode: false,
|
|
273
|
+
EnableLayoutMenu: false,
|
|
274
|
+
IncludeDefaultNodeTypes: false,
|
|
275
|
+
NodeTitleBarHeight: 0,
|
|
276
|
+
WheelMode: 'pan',
|
|
277
|
+
WheelZoomRequiresModifier: true,
|
|
278
|
+
// Flat, light canvas (no grid) by default; a host can recolor it via setBackground.
|
|
279
|
+
Background: { Style: 'solid', Color: '#f4f6f9' }
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
// Read-only presentation surface: no editing, no toolbar; plain wheel pans,
|
|
283
|
+
// ctrl/cmd wheel zooms.
|
|
284
|
+
presentation:
|
|
285
|
+
{
|
|
286
|
+
ReadOnly: true,
|
|
287
|
+
EnableToolbar: false,
|
|
288
|
+
WheelMode: 'pan',
|
|
289
|
+
WheelZoomRequiresModifier: true
|
|
290
|
+
},
|
|
291
|
+
|
|
292
|
+
// Entity-relationship diagrams (fleshed out in a later phase). Orthogonal
|
|
293
|
+
// edges read best for ERDs.
|
|
294
|
+
erd:
|
|
295
|
+
{
|
|
296
|
+
DefaultEdgeTheme: 'Orthogonal'
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
|
|
178
300
|
class PictViewFlow extends libPictView
|
|
179
301
|
{
|
|
302
|
+
/**
|
|
303
|
+
* Merge a Profile's option bundle between the defaults and the host's
|
|
304
|
+
* explicit options: defaults < profile < pOptions. Pure (no side effects),
|
|
305
|
+
* so it is unit testable without constructing the view.
|
|
306
|
+
* @param {Object} pOptions - the host-supplied options (may name a Profile)
|
|
307
|
+
* @returns {Object} the fully merged option set
|
|
308
|
+
*/
|
|
309
|
+
static mergeProfileOptions(pOptions)
|
|
310
|
+
{
|
|
311
|
+
let tmpProfileName = (pOptions && pOptions.Profile) ? pOptions.Profile : (_DefaultConfiguration.Profile || 'graph');
|
|
312
|
+
let tmpProfile = _PROFILES[tmpProfileName] || {};
|
|
313
|
+
return Object.assign(
|
|
314
|
+
{},
|
|
315
|
+
JSON.parse(JSON.stringify(_DefaultConfiguration)),
|
|
316
|
+
JSON.parse(JSON.stringify(tmpProfile)),
|
|
317
|
+
pOptions || {});
|
|
318
|
+
}
|
|
319
|
+
|
|
180
320
|
constructor(pFable, pOptions, pServiceHash)
|
|
181
321
|
{
|
|
182
|
-
let tmpOptions =
|
|
322
|
+
let tmpOptions = PictViewFlow.mergeProfileOptions(pOptions);
|
|
183
323
|
super(pFable, tmpOptions, pServiceHash);
|
|
184
324
|
|
|
185
325
|
this.serviceType = 'PictSectionFlow';
|
|
@@ -211,6 +351,7 @@ class PictViewFlow extends libPictView
|
|
|
211
351
|
{ ServiceType: 'PictProviderFlowNodeTypes', Library: libPictProviderFlowNodeTypes, Property: '_NodeTypeProvider', ExtraOptions: () => ({ AdditionalNodeTypes: this.options.NodeTypes, IncludeDefaultNodeTypes: this.options.IncludeDefaultNodeTypes }) },
|
|
212
352
|
{ ServiceType: 'PictProviderFlowEventHandler', Library: libPictProviderFlowEventHandler, Property: '_EventHandlerProvider' },
|
|
213
353
|
{ ServiceType: 'PictProviderFlowLayouts', Library: libPictProviderFlowLayouts, Property: '_LayoutProvider', PostInit: 'loadPersistedLayouts' },
|
|
354
|
+
{ ServiceType: 'PictProviderFlowBackground', Library: libPictProviderFlowBackground, Property: '_BackgroundProvider' },
|
|
214
355
|
|
|
215
356
|
// Services
|
|
216
357
|
{ ServiceType: 'PictServiceFlowPathGenerator', Library: libPictServiceFlowPathGenerator, Property: '_PathGenerator' },
|
|
@@ -219,6 +360,7 @@ class PictViewFlow extends libPictView
|
|
|
219
360
|
{ ServiceType: 'PictServiceFlowRenderManager', Library: libPictServiceFlowRenderManager, Property: '_RenderManager' },
|
|
220
361
|
{ ServiceType: 'PictServiceFlowPortRenderer', Library: libPictServiceFlowPortRenderer, Property: '_PortRenderer' },
|
|
221
362
|
{ ServiceType: 'PictServiceFlowInteractionManager', Library: libPictServiceFlowInteractionManager, Property: '_InteractionManager' },
|
|
363
|
+
{ ServiceType: 'PictServiceFlowCursorManager', Library: libPictServiceFlowCursorManager, Property: '_CursorManager' },
|
|
222
364
|
{ ServiceType: 'PictServiceFlowConnectionRenderer', Library: libPictServiceFlowConnectionRenderer, Property: '_ConnectionRenderer' },
|
|
223
365
|
{ ServiceType: 'PictServiceFlowTether', Library: libPictServiceFlowTether, Property: '_TetherService' },
|
|
224
366
|
{ ServiceType: 'PictServiceFlowLayout', Library: libPictServiceFlowLayout, Property: '_LayoutService' },
|
|
@@ -285,6 +427,7 @@ class PictViewFlow extends libPictView
|
|
|
285
427
|
this._PortRenderer = null;
|
|
286
428
|
|
|
287
429
|
this._InteractionManager = null;
|
|
430
|
+
this._CursorManager = null;
|
|
288
431
|
this._ConnectionRenderer = null;
|
|
289
432
|
this._TetherService = null;
|
|
290
433
|
this._LayoutService = null;
|
|
@@ -304,11 +447,26 @@ class PictViewFlow extends libPictView
|
|
|
304
447
|
this._PanelChromeProvider = null;
|
|
305
448
|
this._NodeTypeProvider = null;
|
|
306
449
|
this._LayoutProvider = null;
|
|
450
|
+
this._BackgroundProvider = null;
|
|
307
451
|
this._EventHandlerProvider = null;
|
|
308
452
|
this._NodeView = null;
|
|
309
453
|
this._ToolbarView = null;
|
|
310
454
|
this._PropertiesPanelView = null;
|
|
311
455
|
|
|
456
|
+
// Registry of renderable renderers keyed by RenderableType. The default
|
|
457
|
+
// 'card' renderer (the node view) is registered once it exists; consumers
|
|
458
|
+
// register additional renderers (shape, sticky, text, image, category,
|
|
459
|
+
// connector) to draw renderables that are not cards.
|
|
460
|
+
this._RenderableRenderers = {};
|
|
461
|
+
|
|
462
|
+
// Read-only / presentation flag (runtime; defaults to the option).
|
|
463
|
+
this._ReadOnly = !!this.options.ReadOnly;
|
|
464
|
+
|
|
465
|
+
// In read-only, canvas pan + wheel-zoom are OFF by default (the wheel scrolls the page and the
|
|
466
|
+
// canvas reads as static content). A host flips it on with a "hand" toggle via
|
|
467
|
+
// setReadOnlyNavigation. No effect in edit mode.
|
|
468
|
+
this._ReadOnlyNavigation = false;
|
|
469
|
+
|
|
312
470
|
this.initialRenderComplete = false;
|
|
313
471
|
}
|
|
314
472
|
|
|
@@ -324,6 +482,377 @@ class PictViewFlow extends libPictView
|
|
|
324
482
|
}
|
|
325
483
|
}
|
|
326
484
|
|
|
485
|
+
/**
|
|
486
|
+
* Register a renderable renderer under a key. The default 'card' renderer is
|
|
487
|
+
* the node view; consumers register additional renderers (shape, sticky,
|
|
488
|
+
* text, image, category, connector) to draw non-card renderables. A renderer
|
|
489
|
+
* must expose renderNode(node, layerElement, isSelected, typeConfig).
|
|
490
|
+
* @param {string} pKey
|
|
491
|
+
* @param {Object} pRenderer
|
|
492
|
+
* @returns {boolean}
|
|
493
|
+
*/
|
|
494
|
+
registerRenderableRenderer(pKey, pRenderer)
|
|
495
|
+
{
|
|
496
|
+
if (!pKey || !pRenderer)
|
|
497
|
+
{
|
|
498
|
+
return false;
|
|
499
|
+
}
|
|
500
|
+
this._RenderableRenderers[pKey] = pRenderer;
|
|
501
|
+
return true;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* Resolve the renderable renderer for a node. A node type may name its
|
|
506
|
+
* renderer via the RenderableType field (default 'card'); unknown keys fall
|
|
507
|
+
* back to the card renderer so existing diagrams are unaffected.
|
|
508
|
+
* @param {Object} pNode
|
|
509
|
+
* @param {Object} pNodeTypeConfig
|
|
510
|
+
* @returns {Object} a renderer exposing renderNode(...)
|
|
511
|
+
*/
|
|
512
|
+
resolveRenderableRenderer(pNode, pNodeTypeConfig)
|
|
513
|
+
{
|
|
514
|
+
// A node may name its renderer directly via pNode.RenderableType (wins), else
|
|
515
|
+
// the node type config's RenderableType, else the default 'card'. The node-level
|
|
516
|
+
// override lets a free-form canvas mint shape/sticky/text renderables without
|
|
517
|
+
// registering a node type for each. Unknown keys fall back to card so existing
|
|
518
|
+
// diagrams are unaffected.
|
|
519
|
+
let tmpKey = 'card';
|
|
520
|
+
if (pNode && typeof pNode.RenderableType === 'string' && pNode.RenderableType)
|
|
521
|
+
{
|
|
522
|
+
tmpKey = pNode.RenderableType;
|
|
523
|
+
}
|
|
524
|
+
else if (pNodeTypeConfig && pNodeTypeConfig.RenderableType)
|
|
525
|
+
{
|
|
526
|
+
tmpKey = pNodeTypeConfig.RenderableType;
|
|
527
|
+
}
|
|
528
|
+
return this._RenderableRenderers[tmpKey] || this._RenderableRenderers['card'] || this._NodeView;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Apply the configured canvas background to the live SVG. No-op when no
|
|
533
|
+
* Background is configured (the template grid stands). Safe to call on every
|
|
534
|
+
* render; idempotent.
|
|
535
|
+
*/
|
|
536
|
+
_applyBackground()
|
|
537
|
+
{
|
|
538
|
+
if (this._BackgroundProvider)
|
|
539
|
+
{
|
|
540
|
+
return this._BackgroundProvider.apply(this);
|
|
541
|
+
}
|
|
542
|
+
return false;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Set the canvas background and repaint it. Pass an object
|
|
547
|
+
* { Style, Color?, Image?, GridSize?, DotSize? } or false to clear it back to
|
|
548
|
+
* the template grid. Stored on ViewState so it travels with the flow data.
|
|
549
|
+
* @param {Object|false} pBackground
|
|
550
|
+
*/
|
|
551
|
+
setBackground(pBackground)
|
|
552
|
+
{
|
|
553
|
+
this._FlowData.ViewState.Background = pBackground || null;
|
|
554
|
+
return this._applyBackground();
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/**
|
|
558
|
+
* Whether the flow is in read-only / presentation mode.
|
|
559
|
+
* @returns {boolean}
|
|
560
|
+
*/
|
|
561
|
+
isReadOnly()
|
|
562
|
+
{
|
|
563
|
+
return !!this._ReadOnly;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Toggle read-only / presentation mode at runtime. Editing interactions go
|
|
568
|
+
* inert; the container gets the 'pict-flow-readonly' class so chrome can be
|
|
569
|
+
* hidden via CSS. Repaints so port/grip visibility updates.
|
|
570
|
+
* @param {boolean} pReadOnly
|
|
571
|
+
* @returns {boolean} the new read-only state
|
|
572
|
+
*/
|
|
573
|
+
setReadOnly(pReadOnly)
|
|
574
|
+
{
|
|
575
|
+
this._ReadOnly = !!pReadOnly;
|
|
576
|
+
// Navigation is a read-only concept; leaving read-only clears it.
|
|
577
|
+
if (!this._ReadOnly) { this._ReadOnlyNavigation = false; }
|
|
578
|
+
this._applyReadOnlyClass();
|
|
579
|
+
if (this._CursorManager) { this._CursorManager.update(); }
|
|
580
|
+
if (this.initialRenderComplete)
|
|
581
|
+
{
|
|
582
|
+
this.renderFlow();
|
|
583
|
+
}
|
|
584
|
+
return this._ReadOnly;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Whether read-only canvas navigation (pan + wheel-zoom) is enabled. When off
|
|
589
|
+
* (the default in read-only), the wheel scrolls the page and the canvas does
|
|
590
|
+
* not pan, so it reads as static content.
|
|
591
|
+
* @returns {boolean}
|
|
592
|
+
*/
|
|
593
|
+
isReadOnlyNavigation()
|
|
594
|
+
{
|
|
595
|
+
return !!this._ReadOnlyNavigation;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Turn read-only canvas navigation (pan + wheel-zoom) on or off. This is the
|
|
600
|
+
* "hand" toggle for a presentation surface; it has no effect in edit mode.
|
|
601
|
+
* @param {boolean} pOn
|
|
602
|
+
* @returns {boolean} the new navigation state
|
|
603
|
+
*/
|
|
604
|
+
setReadOnlyNavigation(pOn)
|
|
605
|
+
{
|
|
606
|
+
this._ReadOnlyNavigation = !!pOn;
|
|
607
|
+
this._applyReadOnlyClass();
|
|
608
|
+
if (this._CursorManager) { this._CursorManager.update(); }
|
|
609
|
+
if (this._EventHandlerProvider)
|
|
610
|
+
{
|
|
611
|
+
this._EventHandlerProvider.fireEvent('onReadOnlyNavigationChanged', this._ReadOnlyNavigation);
|
|
612
|
+
}
|
|
613
|
+
return this._ReadOnlyNavigation;
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Reflect the read-only flag as a class on the flow container so CSS can hide
|
|
618
|
+
* editing chrome (ports, delete affordance, resize grips). Internal.
|
|
619
|
+
*/
|
|
620
|
+
_applyReadOnlyClass()
|
|
621
|
+
{
|
|
622
|
+
let tmpWrapper = this.pict.ContentAssignment.getElement(`#Flow-Wrapper-${this.options.ViewIdentifier}`);
|
|
623
|
+
if (tmpWrapper && tmpWrapper.length > 0 && tmpWrapper[0].classList)
|
|
624
|
+
{
|
|
625
|
+
if (this._ReadOnly)
|
|
626
|
+
{
|
|
627
|
+
tmpWrapper[0].classList.add('pict-flow-readonly');
|
|
628
|
+
}
|
|
629
|
+
else
|
|
630
|
+
{
|
|
631
|
+
tmpWrapper[0].classList.remove('pict-flow-readonly');
|
|
632
|
+
}
|
|
633
|
+
// 'navigating' = read-only with pan/zoom enabled; drives the grab cursor.
|
|
634
|
+
if (this._ReadOnly && this._ReadOnlyNavigation)
|
|
635
|
+
{
|
|
636
|
+
tmpWrapper[0].classList.add('pict-flow-navigating');
|
|
637
|
+
}
|
|
638
|
+
else
|
|
639
|
+
{
|
|
640
|
+
tmpWrapper[0].classList.remove('pict-flow-navigating');
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Resolve the effective content frame: ViewState wins, then the option.
|
|
647
|
+
* @returns {Object|null}
|
|
648
|
+
*/
|
|
649
|
+
_resolveFrame()
|
|
650
|
+
{
|
|
651
|
+
let tmpFrame = (this._FlowData && this._FlowData.ViewState && this._FlowData.ViewState.Frame)
|
|
652
|
+
? this._FlowData.ViewState.Frame
|
|
653
|
+
: this.options.Frame;
|
|
654
|
+
return (tmpFrame && typeof tmpFrame === 'object') ? tmpFrame : null;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
/**
|
|
658
|
+
* Set (or clear, with false) the content frame and repaint it. Stored on
|
|
659
|
+
* ViewState so it travels with the flow data.
|
|
660
|
+
* @param {Object|false} pFrame
|
|
661
|
+
*/
|
|
662
|
+
setFrame(pFrame)
|
|
663
|
+
{
|
|
664
|
+
this._FlowData.ViewState.Frame = (pFrame && typeof pFrame === 'object') ? pFrame : null;
|
|
665
|
+
this._applyFrame();
|
|
666
|
+
return this._FlowData.ViewState.Frame;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Read the effective content frame (ViewState wins, then the option), or null
|
|
671
|
+
* when none is set. The public read counterpart to setFrame.
|
|
672
|
+
* @returns {Object|null}
|
|
673
|
+
*/
|
|
674
|
+
getFrame()
|
|
675
|
+
{
|
|
676
|
+
return this._resolveFrame();
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Toggle the content frame's drag handles at runtime (e.g. an editor turning on
|
|
681
|
+
* "set the view area"). Repaints the frame so the handles appear / disappear.
|
|
682
|
+
* @param {boolean} pEnabled
|
|
683
|
+
* @returns {boolean}
|
|
684
|
+
*/
|
|
685
|
+
setFrameEditing(pEnabled)
|
|
686
|
+
{
|
|
687
|
+
this.options.EnableFrameEditing = !!pEnabled;
|
|
688
|
+
this._applyFrame();
|
|
689
|
+
return this.options.EnableFrameEditing;
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Draw, update, or remove the content-frame rectangle inside the viewport
|
|
694
|
+
* group (behind the content layers). No-op until the viewport exists.
|
|
695
|
+
*/
|
|
696
|
+
_applyFrame()
|
|
697
|
+
{
|
|
698
|
+
if (!this._ViewportElement) return;
|
|
699
|
+
|
|
700
|
+
let tmpId = `Flow-Frame-${this.options.ViewIdentifier}`;
|
|
701
|
+
let tmpExisting = this._ViewportElement.querySelector(`#${tmpId}`);
|
|
702
|
+
let tmpFrame = this._resolveFrame();
|
|
703
|
+
|
|
704
|
+
if (!tmpFrame || tmpFrame.Enabled === false || !tmpFrame.Width || !tmpFrame.Height)
|
|
705
|
+
{
|
|
706
|
+
if (tmpExisting) tmpExisting.remove();
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
let tmpRect = tmpExisting;
|
|
711
|
+
if (!tmpRect)
|
|
712
|
+
{
|
|
713
|
+
tmpRect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
714
|
+
tmpRect.setAttribute('id', tmpId);
|
|
715
|
+
tmpRect.setAttribute('class', 'pict-flow-frame');
|
|
716
|
+
// First child of the viewport group: behind connections and nodes.
|
|
717
|
+
this._ViewportElement.insertBefore(tmpRect, this._ViewportElement.firstChild);
|
|
718
|
+
}
|
|
719
|
+
tmpRect.setAttribute('x', tmpFrame.X || 0);
|
|
720
|
+
tmpRect.setAttribute('y', tmpFrame.Y || 0);
|
|
721
|
+
tmpRect.setAttribute('width', tmpFrame.Width);
|
|
722
|
+
tmpRect.setAttribute('height', tmpFrame.Height);
|
|
723
|
+
|
|
724
|
+
this._applyFrameHandles(tmpFrame);
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
/**
|
|
728
|
+
* Draw, refresh, or remove the content frame's drag handles. Shown only when
|
|
729
|
+
* EnableFrameEditing is on and the view is editable: four edge handles (set top /
|
|
730
|
+
* width / height) and a move handle at the top-left. Rendered on top of the content
|
|
731
|
+
* (appended last in the viewport) so they are grabbable; their data-element-type routes
|
|
732
|
+
* pointer-down to the InteractionManager's frame edit paths.
|
|
733
|
+
* @param {Object} pFrame - the resolved frame ({X,Y,Width,Height})
|
|
734
|
+
*/
|
|
735
|
+
_applyFrameHandles(pFrame)
|
|
736
|
+
{
|
|
737
|
+
if (!this._ViewportElement) return;
|
|
738
|
+
|
|
739
|
+
let tmpId = `Flow-FrameHandles-${this.options.ViewIdentifier}`;
|
|
740
|
+
let tmpExisting = this._ViewportElement.querySelector(`#${tmpId}`);
|
|
741
|
+
if (tmpExisting) tmpExisting.remove();
|
|
742
|
+
|
|
743
|
+
let tmpEditable = this.options.EnableFrameEditing
|
|
744
|
+
&& !(typeof this.isReadOnly === 'function' && this.isReadOnly())
|
|
745
|
+
&& pFrame && pFrame.Width && pFrame.Height;
|
|
746
|
+
if (!tmpEditable) return;
|
|
747
|
+
|
|
748
|
+
let tmpX = pFrame.X || 0;
|
|
749
|
+
let tmpY = pFrame.Y || 0;
|
|
750
|
+
let tmpW = pFrame.Width;
|
|
751
|
+
let tmpH = pFrame.Height;
|
|
752
|
+
let tmpSize = 12;
|
|
753
|
+
|
|
754
|
+
let tmpGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
755
|
+
tmpGroup.setAttribute('id', tmpId);
|
|
756
|
+
tmpGroup.setAttribute('class', 'pict-flow-frame-handles');
|
|
757
|
+
|
|
758
|
+
let tmpHandles =
|
|
759
|
+
[
|
|
760
|
+
{ Edge: 'n', Type: 'frame-resize', CX: tmpX + tmpW / 2, CY: tmpY },
|
|
761
|
+
{ Edge: 's', Type: 'frame-resize', CX: tmpX + tmpW / 2, CY: tmpY + tmpH },
|
|
762
|
+
{ Edge: 'e', Type: 'frame-resize', CX: tmpX + tmpW, CY: tmpY + tmpH / 2 },
|
|
763
|
+
{ Edge: 'w', Type: 'frame-resize', CX: tmpX, CY: tmpY + tmpH / 2 },
|
|
764
|
+
{ Edge: 'move', Type: 'frame-move', CX: tmpX, CY: tmpY }
|
|
765
|
+
];
|
|
766
|
+
for (let i = 0; i < tmpHandles.length; i++)
|
|
767
|
+
{
|
|
768
|
+
let tmpHandle = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
769
|
+
tmpHandle.setAttribute('class', 'pict-flow-frame-handle pict-flow-frame-handle-' + tmpHandles[i].Edge);
|
|
770
|
+
tmpHandle.setAttribute('x', String(tmpHandles[i].CX - tmpSize / 2));
|
|
771
|
+
tmpHandle.setAttribute('y', String(tmpHandles[i].CY - tmpSize / 2));
|
|
772
|
+
tmpHandle.setAttribute('width', String(tmpSize));
|
|
773
|
+
tmpHandle.setAttribute('height', String(tmpSize));
|
|
774
|
+
tmpHandle.setAttribute('rx', '2');
|
|
775
|
+
tmpHandle.setAttribute('data-element-type', tmpHandles[i].Type);
|
|
776
|
+
if (tmpHandles[i].Edge !== 'move')
|
|
777
|
+
{
|
|
778
|
+
tmpHandle.setAttribute('data-frame-edge', tmpHandles[i].Edge);
|
|
779
|
+
}
|
|
780
|
+
tmpGroup.appendChild(tmpHandle);
|
|
781
|
+
}
|
|
782
|
+
this._ViewportElement.appendChild(tmpGroup);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Fit the viewport to the content frame (zoom + pan so it fills the view).
|
|
787
|
+
* @returns {boolean}
|
|
788
|
+
*/
|
|
789
|
+
fitToFrame()
|
|
790
|
+
{
|
|
791
|
+
return this._ViewportManager ? this._ViewportManager.fitToFrame(this._resolveFrame()) : false;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Fit the viewport so the content frame's WIDTH fills the view (anchored top-left + an
|
|
796
|
+
* optional top margin), letting content bleed past and vertical overflow scroll. This is
|
|
797
|
+
* the presentation fit a jumbotron / background board uses; fitToFrame() contains instead.
|
|
798
|
+
* @param {Object} [pOptions] - { TopMargin }
|
|
799
|
+
* @returns {boolean}
|
|
800
|
+
*/
|
|
801
|
+
fitToWidth(pOptions)
|
|
802
|
+
{
|
|
803
|
+
if (!this._ViewportManager) return false;
|
|
804
|
+
let tmpOptions = pOptions || {};
|
|
805
|
+
if (typeof tmpOptions.TopMargin !== 'number')
|
|
806
|
+
{
|
|
807
|
+
tmpOptions = { TopMargin: this.options.FitTopMargin || 0 };
|
|
808
|
+
}
|
|
809
|
+
return this._ViewportManager.fitToFrameWidth(this._resolveFrame(), tmpOptions);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* When FitMode is 'width', fit the frame to the container width now and keep it fit as the
|
|
814
|
+
* container resizes (a ResizeObserver, the documented exception to the no-listener rule).
|
|
815
|
+
* No-op without a frame or when FitMode is not 'width'.
|
|
816
|
+
*/
|
|
817
|
+
_setupFitObserver()
|
|
818
|
+
{
|
|
819
|
+
if (this.options.FitMode !== 'width' || !this._resolveFrame())
|
|
820
|
+
{
|
|
821
|
+
// Not width-fit (e.g. switched back to 'contain'): drop any observer we
|
|
822
|
+
// installed so a stale ResizeObserver does not keep re-fitting the canvas.
|
|
823
|
+
this._teardownFitObserver();
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
this.fitToWidth();
|
|
827
|
+
if ((typeof ResizeObserver !== 'undefined') && this._SVGElement)
|
|
828
|
+
{
|
|
829
|
+
// (Re)observe the CURRENT SVG element. A re-render (e.g. a host toggling presentation mode)
|
|
830
|
+
// can replace the SVG, which would leave a previously-installed observer watching a stale,
|
|
831
|
+
// detached node — so re-point it whenever the observed element has changed.
|
|
832
|
+
if (this._FitObserver && this._FitObservedElement === this._SVGElement) { return; }
|
|
833
|
+
this._teardownFitObserver();
|
|
834
|
+
let tmpSelf = this;
|
|
835
|
+
this._FitObserver = new ResizeObserver(function () { tmpSelf.fitToWidth(); });
|
|
836
|
+
this._FitObserver.observe(this._SVGElement);
|
|
837
|
+
this._FitObservedElement = this._SVGElement;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/**
|
|
842
|
+
* Disconnect the width-fit ResizeObserver, if one is installed. Called when a
|
|
843
|
+
* consumer leaves 'width' FitMode (e.g. a moodboard switching back to canvas)
|
|
844
|
+
* so the observer stops firing. Safe to call when no observer exists.
|
|
845
|
+
*/
|
|
846
|
+
_teardownFitObserver()
|
|
847
|
+
{
|
|
848
|
+
if (this._FitObserver)
|
|
849
|
+
{
|
|
850
|
+
this._FitObserver.disconnect();
|
|
851
|
+
this._FitObserver = null;
|
|
852
|
+
}
|
|
853
|
+
this._FitObservedElement = null;
|
|
854
|
+
}
|
|
855
|
+
|
|
327
856
|
_instantiateServices()
|
|
328
857
|
{
|
|
329
858
|
for (let i = 0; i < this._ServiceRegistry.length; i++)
|
|
@@ -502,12 +1031,24 @@ class PictViewFlow extends libPictView
|
|
|
502
1031
|
}
|
|
503
1032
|
this._SVGElement = tmpSVGElements[0];
|
|
504
1033
|
|
|
1034
|
+
// Paint a configured background (no-op when none is set; template grid stands).
|
|
1035
|
+
this._applyBackground();
|
|
1036
|
+
|
|
1037
|
+
// Reflect read-only mode on the container (drives chrome-hiding CSS).
|
|
1038
|
+
this._applyReadOnlyClass();
|
|
1039
|
+
|
|
1040
|
+
// Set the initial canvas cursor from the (idle) state + current mode.
|
|
1041
|
+
if (this._CursorManager) { this._CursorManager.update(); }
|
|
1042
|
+
|
|
505
1043
|
let tmpViewportElements = this.pict.ContentAssignment.getElement(`#Flow-Viewport-${tmpViewIdentifier}`);
|
|
506
1044
|
if (tmpViewportElements.length > 0)
|
|
507
1045
|
{
|
|
508
1046
|
this._ViewportElement = tmpViewportElements[0];
|
|
509
1047
|
}
|
|
510
1048
|
|
|
1049
|
+
// Draw the content frame, if configured (needs the viewport <g>).
|
|
1050
|
+
this._applyFrame();
|
|
1051
|
+
|
|
511
1052
|
let tmpNodesElements = this.pict.ContentAssignment.getElement(`#Flow-Nodes-${tmpViewIdentifier}`);
|
|
512
1053
|
if (tmpNodesElements.length > 0)
|
|
513
1054
|
{
|
|
@@ -596,6 +1137,8 @@ class PictViewFlow extends libPictView
|
|
|
596
1137
|
this._NodeView = this.fable.instantiateServiceProviderWithoutRegistration('PictViewFlowNode',
|
|
597
1138
|
Object.assign({}, libPictViewFlowNode.default_configuration, tmpNodeViewOptions));
|
|
598
1139
|
this._NodeView._FlowView = this;
|
|
1140
|
+
// The default renderable renderer. Non-card renderers register alongside it.
|
|
1141
|
+
this._RenderableRenderers['card'] = this._NodeView;
|
|
599
1142
|
|
|
600
1143
|
// Setup the properties panel renderer
|
|
601
1144
|
this._PropertiesPanelView = this.fable.instantiateServiceProviderWithoutRegistration('PictViewFlowPropertiesPanel',
|
|
@@ -622,8 +1165,45 @@ class PictViewFlow extends libPictView
|
|
|
622
1165
|
|
|
623
1166
|
// Render the initial flow
|
|
624
1167
|
this.renderFlow();
|
|
1168
|
+
|
|
1169
|
+
// Optionally snap to the default saved layout once the graph is drawn. A host with an async
|
|
1170
|
+
// LayoutStorage that resolves later should call applyDefaultLayout() from its load callback.
|
|
1171
|
+
if (this.options.ApplyDefaultLayoutOnLoad && this._LayoutProvider && this._LayoutProvider.getDefaultLayout())
|
|
1172
|
+
{
|
|
1173
|
+
this._LayoutProvider.applyDefaultLayout();
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
// Presentation fit. FitMode 'width' fits the frame to the container width now and on
|
|
1177
|
+
// resize; otherwise honor a frame's legacy FitOnLoad (contain). Both need the SVG sized,
|
|
1178
|
+
// so this runs after the initial render.
|
|
1179
|
+
if (this.options.FitMode === 'width')
|
|
1180
|
+
{
|
|
1181
|
+
this._setupFitObserver();
|
|
1182
|
+
}
|
|
1183
|
+
else
|
|
1184
|
+
{
|
|
1185
|
+
let tmpFrame = this._resolveFrame();
|
|
1186
|
+
if (tmpFrame && tmpFrame.FitOnLoad)
|
|
1187
|
+
{
|
|
1188
|
+
this.fitToFrame();
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
625
1191
|
}
|
|
626
1192
|
|
|
1193
|
+
// ---- Saved-layout delegations ----
|
|
1194
|
+
// A clean view-level API over the layout provider for saving, restoring, and
|
|
1195
|
+
// choosing a default arrangement of the same graph. The toolbar uses the
|
|
1196
|
+
// provider directly; these are additive.
|
|
1197
|
+
|
|
1198
|
+
saveLayout(pName) { return this._LayoutProvider ? this._LayoutProvider.saveLayout(pName) : null; }
|
|
1199
|
+
restoreLayout(pHash) { return this._LayoutProvider ? this._LayoutProvider.restoreLayout(pHash) : false; }
|
|
1200
|
+
applyLayout(pHash) { return this.restoreLayout(pHash); }
|
|
1201
|
+
deleteLayout(pHash) { return this._LayoutProvider ? this._LayoutProvider.deleteLayout(pHash) : false; }
|
|
1202
|
+
getLayouts() { return this._LayoutProvider ? this._LayoutProvider.getLayouts() : []; }
|
|
1203
|
+
setDefaultLayout(pHash) { return this._LayoutProvider ? this._LayoutProvider.setDefaultLayout(pHash) : false; }
|
|
1204
|
+
getDefaultLayout() { return this._LayoutProvider ? this._LayoutProvider.getDefaultLayout() : null; }
|
|
1205
|
+
applyDefaultLayout() { return this._LayoutProvider ? this._LayoutProvider.applyDefaultLayout() : false; }
|
|
1206
|
+
|
|
627
1207
|
// ---- Data Manager Delegations ----
|
|
628
1208
|
|
|
629
1209
|
marshalToView() { return this._DataManager.marshalToView(); }
|
|
@@ -1587,4 +2167,13 @@ class PictViewFlow extends libPictView
|
|
|
1587
2167
|
|
|
1588
2168
|
module.exports = PictViewFlow;
|
|
1589
2169
|
|
|
1590
|
-
|
|
2170
|
+
// default_configuration is intentionally NOT exported. Pict's addView merges a
|
|
2171
|
+
// view's default_configuration UNDER the host's options, which would make those
|
|
2172
|
+
// now-explicit defaults override Profile values. Defaults are applied inside the
|
|
2173
|
+
// constructor via mergeProfileOptions (defaults < profile < host) so Profile
|
|
2174
|
+
// works as a real config knob. Inspect resolved defaults with
|
|
2175
|
+
// PictViewFlow.mergeProfileOptions({}).
|
|
2176
|
+
|
|
2177
|
+
// Config profiles (graph/whiteboard/moodboard/presentation/erd), exposed so
|
|
2178
|
+
// consumers can inspect or extend them.
|
|
2179
|
+
module.exports.Profiles = _PROFILES;
|