pict-section-flow 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/README.md +19 -0
- package/{example_application → example_applications/simple_cards}/html/index.html +2 -2
- package/example_applications/simple_cards/package.json +43 -0
- package/example_applications/simple_cards/source/Pict-Application-FlowExample.js +434 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Each.js +36 -0
- package/example_applications/simple_cards/source/cards/FlowCard-FileRead.js +54 -0
- package/example_applications/simple_cards/source/cards/FlowCard-FileWrite.js +48 -0
- package/example_applications/simple_cards/source/cards/FlowCard-GetValue.js +35 -0
- package/example_applications/simple_cards/source/cards/FlowCard-IfThenElse.js +47 -0
- package/example_applications/simple_cards/source/cards/FlowCard-LogValues.js +53 -0
- package/example_applications/simple_cards/source/cards/FlowCard-SetValue.js +95 -0
- package/example_applications/simple_cards/source/cards/FlowCard-Switch.js +37 -0
- package/example_applications/simple_cards/source/views/PictView-FlowExample-FileWriteInfo.js +59 -0
- package/{example_application → example_applications/simple_cards}/source/views/PictView-FlowExample-Layout.js +5 -1
- package/example_applications/simple_cards/source/views/PictView-FlowExample-MainWorkspace.js +312 -0
- package/package.json +6 -6
- package/source/Pict-Section-Flow.js +19 -0
- package/source/PictFlowCard.js +207 -0
- package/source/PictFlowCardPropertiesPanel.js +105 -0
- package/source/panels/FlowCardPropertiesPanel-Form.js +174 -0
- package/source/panels/FlowCardPropertiesPanel-Markdown.js +148 -0
- package/source/panels/FlowCardPropertiesPanel-Template.js +88 -0
- package/source/panels/FlowCardPropertiesPanel-View.js +114 -0
- package/source/providers/PictProvider-Flow-EventHandler.js +19 -8
- package/source/providers/PictProvider-Flow-Geometry.js +64 -0
- package/source/providers/PictProvider-Flow-Layouts.js +284 -0
- package/source/providers/PictProvider-Flow-NodeTypes.js +70 -0
- package/source/providers/PictProvider-Flow-PanelChrome.js +72 -0
- package/source/providers/PictProvider-Flow-SVGHelpers.js +30 -0
- package/source/services/PictService-Flow-ConnectionRenderer.js +324 -66
- package/source/services/PictService-Flow-InteractionManager.js +399 -75
- package/source/services/PictService-Flow-Layout.js +159 -0
- package/source/services/PictService-Flow-PathGenerator.js +199 -0
- package/source/services/PictService-Flow-Tether.js +544 -0
- package/source/views/PictView-Flow-Node.js +95 -18
- package/source/views/PictView-Flow-PropertiesPanel.js +435 -0
- package/source/views/PictView-Flow-Toolbar.js +491 -5
- package/source/views/PictView-Flow.js +830 -8
- package/example_application/package.json +0 -41
- package/example_application/source/Pict-Application-FlowExample.js +0 -241
- package/example_application/source/views/PictView-FlowExample-MainWorkspace.js +0 -191
- /package/{example_application → example_applications/simple_cards}/css/flowexample.css +0 -0
- /package/{example_application → example_applications/simple_cards}/source/Pict-Application-FlowExample-Configuration.json +0 -0
- /package/{example_application → example_applications/simple_cards}/source/providers/PictRouter-FlowExample-Configuration.json +0 -0
- /package/{example_application → example_applications/simple_cards}/source/views/PictView-FlowExample-About.js +0 -0
- /package/{example_application → example_applications/simple_cards}/source/views/PictView-FlowExample-BottomBar.js +0 -0
- /package/{example_application → example_applications/simple_cards}/source/views/PictView-FlowExample-Documentation.js +0 -0
- /package/{example_application → example_applications/simple_cards}/source/views/PictView-FlowExample-TopBar.js +0 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const libPictFlowCardPropertiesPanel = require('../PictFlowCardPropertiesPanel.js');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* FlowCardPropertiesPanel-Template
|
|
5
|
+
*
|
|
6
|
+
* Renders pict templates into the panel body.
|
|
7
|
+
*
|
|
8
|
+
* Configuration:
|
|
9
|
+
* {
|
|
10
|
+
* PanelType: 'Template',
|
|
11
|
+
* Configuration: {
|
|
12
|
+
* Templates: [
|
|
13
|
+
* { Hash: 'my-template', Template: '<div>{~D:Record.Data.SomeValue~}</div>' }
|
|
14
|
+
* ],
|
|
15
|
+
* TemplateHash: 'my-template'
|
|
16
|
+
* }
|
|
17
|
+
* }
|
|
18
|
+
*/
|
|
19
|
+
class FlowCardPropertiesPanelTemplate extends libPictFlowCardPropertiesPanel
|
|
20
|
+
{
|
|
21
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
22
|
+
{
|
|
23
|
+
super(pFable, pOptions, pServiceHash);
|
|
24
|
+
|
|
25
|
+
this.serviceType = 'PictFlowCardPropertiesPanel-Template';
|
|
26
|
+
|
|
27
|
+
this._TemplatesRegistered = false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Register templates with the pict template provider and render.
|
|
32
|
+
*/
|
|
33
|
+
render(pContainer, pNodeData)
|
|
34
|
+
{
|
|
35
|
+
super.render(pContainer, pNodeData);
|
|
36
|
+
|
|
37
|
+
if (!this._Configuration || !this._Configuration.Templates) return;
|
|
38
|
+
|
|
39
|
+
// Register templates with pict (only once)
|
|
40
|
+
if (!this._TemplatesRegistered)
|
|
41
|
+
{
|
|
42
|
+
let tmpTemplates = this._Configuration.Templates;
|
|
43
|
+
for (let i = 0; i < tmpTemplates.length; i++)
|
|
44
|
+
{
|
|
45
|
+
if (tmpTemplates[i].Hash && tmpTemplates[i].Template)
|
|
46
|
+
{
|
|
47
|
+
this.fable.TemplateProvider.addTemplate(tmpTemplates[i].Hash, tmpTemplates[i].Template);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
this._TemplatesRegistered = true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this._renderTemplate();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
marshalToPanel(pNodeData)
|
|
57
|
+
{
|
|
58
|
+
super.marshalToPanel(pNodeData);
|
|
59
|
+
this._renderTemplate();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
_renderTemplate()
|
|
63
|
+
{
|
|
64
|
+
if (!this._ContentContainer || !this._NodeData) return;
|
|
65
|
+
|
|
66
|
+
let tmpTemplateHash = this._Configuration.TemplateHash;
|
|
67
|
+
if (!tmpTemplateHash) return;
|
|
68
|
+
|
|
69
|
+
let tmpRecord = this._NodeData;
|
|
70
|
+
let tmpHTML = this.fable.parseTemplate(tmpTemplateHash, tmpRecord, null, [tmpRecord]);
|
|
71
|
+
this._ContentContainer.innerHTML = tmpHTML;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
module.exports = FlowCardPropertiesPanelTemplate;
|
|
76
|
+
|
|
77
|
+
module.exports.default_configuration = Object.assign(
|
|
78
|
+
{},
|
|
79
|
+
libPictFlowCardPropertiesPanel.default_configuration,
|
|
80
|
+
{
|
|
81
|
+
PanelType: 'Template',
|
|
82
|
+
Configuration:
|
|
83
|
+
{
|
|
84
|
+
Templates: [],
|
|
85
|
+
TemplateHash: ''
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
);
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const libPictFlowCardPropertiesPanel = require('../PictFlowCardPropertiesPanel.js');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* FlowCardPropertiesPanel-View
|
|
5
|
+
*
|
|
6
|
+
* Renders an existing registered pict-view into the panel body.
|
|
7
|
+
* The view's destination is temporarily overridden to render inside
|
|
8
|
+
* the panel container.
|
|
9
|
+
*
|
|
10
|
+
* Configuration:
|
|
11
|
+
* {
|
|
12
|
+
* PanelType: 'View',
|
|
13
|
+
* Configuration: {
|
|
14
|
+
* ViewHash: 'MyCustomViewIdentifier'
|
|
15
|
+
* }
|
|
16
|
+
* }
|
|
17
|
+
*/
|
|
18
|
+
class FlowCardPropertiesPanelView extends libPictFlowCardPropertiesPanel
|
|
19
|
+
{
|
|
20
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
21
|
+
{
|
|
22
|
+
super(pFable, pOptions, pServiceHash);
|
|
23
|
+
|
|
24
|
+
this.serviceType = 'PictFlowCardPropertiesPanel-View';
|
|
25
|
+
|
|
26
|
+
this._OriginalDestination = null;
|
|
27
|
+
this._ViewInstance = null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Render the referenced pict-view into the panel body.
|
|
32
|
+
*/
|
|
33
|
+
render(pContainer, pNodeData)
|
|
34
|
+
{
|
|
35
|
+
super.render(pContainer, pNodeData);
|
|
36
|
+
|
|
37
|
+
if (!this._Configuration || !this._Configuration.ViewHash)
|
|
38
|
+
{
|
|
39
|
+
pContainer.innerHTML = '<em>No ViewHash configured</em>';
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let tmpViewHash = this._Configuration.ViewHash;
|
|
44
|
+
|
|
45
|
+
// Create a unique container ID
|
|
46
|
+
let tmpContainerID = `pict-flow-panel-view-${pNodeData.Hash}`;
|
|
47
|
+
pContainer.innerHTML = `<div id="${tmpContainerID}"></div>`;
|
|
48
|
+
|
|
49
|
+
try
|
|
50
|
+
{
|
|
51
|
+
// Look up the view in the pict instance
|
|
52
|
+
let tmpPict = this.pict || this.fable;
|
|
53
|
+
if (tmpPict.views && tmpPict.views[tmpViewHash])
|
|
54
|
+
{
|
|
55
|
+
this._ViewInstance = tmpPict.views[tmpViewHash];
|
|
56
|
+
|
|
57
|
+
// Save original destination
|
|
58
|
+
this._OriginalDestination = this._ViewInstance.options.DefaultDestinationAddress;
|
|
59
|
+
|
|
60
|
+
// Override destination to our panel container
|
|
61
|
+
this._ViewInstance.options.DefaultDestinationAddress = `#${tmpContainerID}`;
|
|
62
|
+
|
|
63
|
+
if (typeof this._ViewInstance.render === 'function')
|
|
64
|
+
{
|
|
65
|
+
this._ViewInstance.render();
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else
|
|
69
|
+
{
|
|
70
|
+
pContainer.innerHTML = `<em>View "${tmpViewHash}" not found</em>`;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch (pError)
|
|
74
|
+
{
|
|
75
|
+
this.log.warn(`FlowCardPropertiesPanel-View render error: ${pError.message}`);
|
|
76
|
+
pContainer.innerHTML = `<em>View render error: ${pError.message}</em>`;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
marshalFromPanel(pNodeData)
|
|
81
|
+
{
|
|
82
|
+
if (this._ViewInstance && typeof this._ViewInstance.marshalFromView === 'function')
|
|
83
|
+
{
|
|
84
|
+
this._ViewInstance.marshalFromView();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
destroy()
|
|
89
|
+
{
|
|
90
|
+
// Restore original destination
|
|
91
|
+
if (this._ViewInstance && this._OriginalDestination)
|
|
92
|
+
{
|
|
93
|
+
this._ViewInstance.options.DefaultDestinationAddress = this._OriginalDestination;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this._ViewInstance = null;
|
|
97
|
+
this._OriginalDestination = null;
|
|
98
|
+
super.destroy();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = FlowCardPropertiesPanelView;
|
|
103
|
+
|
|
104
|
+
module.exports.default_configuration = Object.assign(
|
|
105
|
+
{},
|
|
106
|
+
libPictFlowCardPropertiesPanel.default_configuration,
|
|
107
|
+
{
|
|
108
|
+
PanelType: 'View',
|
|
109
|
+
Configuration:
|
|
110
|
+
{
|
|
111
|
+
ViewHash: ''
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
);
|
|
@@ -11,14 +11,25 @@ const _ProviderConfiguration =
|
|
|
11
11
|
* for flow events like node selection, movement, connection creation, etc.
|
|
12
12
|
*
|
|
13
13
|
* Supported events:
|
|
14
|
-
* - onNodeSelected(node)
|
|
15
|
-
* - onNodeAdded(node)
|
|
16
|
-
* - onNodeRemoved(node)
|
|
17
|
-
* - onNodeMoved(node)
|
|
18
|
-
* - onConnectionSelected(conn)
|
|
19
|
-
* - onConnectionCreated(conn)
|
|
20
|
-
* - onConnectionRemoved(conn)
|
|
21
|
-
* -
|
|
14
|
+
* - onNodeSelected(node) - A node was selected (or null for deselection)
|
|
15
|
+
* - onNodeAdded(node) - A new node was added
|
|
16
|
+
* - onNodeRemoved(node) - A node was removed
|
|
17
|
+
* - onNodeMoved(node) - A node was moved to a new position
|
|
18
|
+
* - onConnectionSelected(conn) - A connection was selected
|
|
19
|
+
* - onConnectionCreated(conn) - A new connection was created
|
|
20
|
+
* - onConnectionRemoved(conn) - A connection was removed
|
|
21
|
+
* - onConnectionHandleMoved(conn) - A connection's drag handle was repositioned
|
|
22
|
+
* - onConnectionModeChanged(conn) - A connection's line mode was toggled (bezier/orthogonal)
|
|
23
|
+
* - onPanelOpened(panelData) - A properties panel was opened
|
|
24
|
+
* - onPanelClosed(panelData) - A properties panel was closed
|
|
25
|
+
* - onPanelMoved(panelData) - A properties panel was moved
|
|
26
|
+
* - onTetherSelected(panelData) - A tether line was selected (or null for deselection)
|
|
27
|
+
* - onTetherHandleMoved(panelData) - A tether's drag handle was repositioned
|
|
28
|
+
* - onTetherModeChanged(panelData) - A tether's line mode was toggled (bezier/orthogonal)
|
|
29
|
+
* - onLayoutSaved(layoutData) - A layout snapshot was saved
|
|
30
|
+
* - onLayoutRestored(layoutData) - A saved layout was restored
|
|
31
|
+
* - onLayoutDeleted(layoutData) - A saved layout was deleted
|
|
32
|
+
* - onFlowChanged(flowData) - The flow data changed (catch-all)
|
|
22
33
|
*/
|
|
23
34
|
class PictProviderFlowEventHandler extends libPictProvider
|
|
24
35
|
{
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PictProvider-Flow-Geometry
|
|
5
|
+
*
|
|
6
|
+
* Shared geometry utilities for the flow diagram.
|
|
7
|
+
* Provides direction vectors and edge center calculations used by
|
|
8
|
+
* connections, tethers, and other flow components.
|
|
9
|
+
*/
|
|
10
|
+
class PictProviderFlowGeometry extends libFableServiceProviderBase
|
|
11
|
+
{
|
|
12
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
13
|
+
{
|
|
14
|
+
super(pFable, pOptions, pServiceHash);
|
|
15
|
+
|
|
16
|
+
this.serviceType = 'PictProviderFlowGeometry';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get the outward unit direction vector for a given side.
|
|
21
|
+
*
|
|
22
|
+
* @param {string} pSide - 'left', 'right', 'top', or 'bottom'
|
|
23
|
+
* @returns {{dx: number, dy: number}}
|
|
24
|
+
*/
|
|
25
|
+
sideDirection(pSide)
|
|
26
|
+
{
|
|
27
|
+
switch (pSide)
|
|
28
|
+
{
|
|
29
|
+
case 'left': return { dx: -1, dy: 0 };
|
|
30
|
+
case 'right': return { dx: 1, dy: 0 };
|
|
31
|
+
case 'top': return { dx: 0, dy: -1 };
|
|
32
|
+
case 'bottom': return { dx: 0, dy: 1 };
|
|
33
|
+
default: return { dx: 1, dy: 0 };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Get the center point of a rectangle's edge.
|
|
39
|
+
* Works for any object with X, Y, Width, Height properties
|
|
40
|
+
* (nodes, panels, or any rectangular element).
|
|
41
|
+
*
|
|
42
|
+
* @param {Object} pRectData - Object with X, Y, Width, Height
|
|
43
|
+
* @param {string} pSide - 'left', 'right', 'top', 'bottom'
|
|
44
|
+
* @returns {{x: number, y: number}}
|
|
45
|
+
*/
|
|
46
|
+
getEdgeCenter(pRectData, pSide)
|
|
47
|
+
{
|
|
48
|
+
switch (pSide)
|
|
49
|
+
{
|
|
50
|
+
case 'left':
|
|
51
|
+
return { x: pRectData.X, y: pRectData.Y + pRectData.Height / 2 };
|
|
52
|
+
case 'right':
|
|
53
|
+
return { x: pRectData.X + pRectData.Width, y: pRectData.Y + pRectData.Height / 2 };
|
|
54
|
+
case 'top':
|
|
55
|
+
return { x: pRectData.X + pRectData.Width / 2, y: pRectData.Y };
|
|
56
|
+
case 'bottom':
|
|
57
|
+
return { x: pRectData.X + pRectData.Width / 2, y: pRectData.Y + pRectData.Height };
|
|
58
|
+
default:
|
|
59
|
+
return { x: pRectData.X + pRectData.Width, y: pRectData.Y + pRectData.Height / 2 };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = PictProviderFlowGeometry;
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
const libPictProvider = require('pict-provider');
|
|
2
|
+
|
|
3
|
+
const _ProviderConfiguration =
|
|
4
|
+
{
|
|
5
|
+
ProviderIdentifier: 'PictProviderFlowLayouts'
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Provider for managing saved flow diagram layouts.
|
|
10
|
+
*
|
|
11
|
+
* Layouts capture the spatial arrangement of nodes and panels on the canvas
|
|
12
|
+
* without storing any card content. When a layout is restored, nodes that
|
|
13
|
+
* still exist are placed at their saved positions and any new nodes are
|
|
14
|
+
* auto-laid-out to the right.
|
|
15
|
+
*
|
|
16
|
+
* Saved layout data structure:
|
|
17
|
+
* {
|
|
18
|
+
* Hash: "layout-<UUID>",
|
|
19
|
+
* Name: "My Layout",
|
|
20
|
+
* CreatedAt: "2026-02-26T12:00:00.000Z",
|
|
21
|
+
* NodePositions: { "node-hash": { X, Y, Width, Height } },
|
|
22
|
+
* PanelPositions: { "node-hash": { X, Y, Width, Height } },
|
|
23
|
+
* ViewState: { PanX, PanY, Zoom }
|
|
24
|
+
* }
|
|
25
|
+
*/
|
|
26
|
+
class PictProviderFlowLayouts extends libPictProvider
|
|
27
|
+
{
|
|
28
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
29
|
+
{
|
|
30
|
+
let tmpOptions = Object.assign({}, _ProviderConfiguration, pOptions);
|
|
31
|
+
super(pFable, tmpOptions, pServiceHash);
|
|
32
|
+
|
|
33
|
+
this.serviceType = 'PictProviderFlowLayouts';
|
|
34
|
+
|
|
35
|
+
this._FlowView = (pOptions && pOptions.FlowView) ? pOptions.FlowView : null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Save the current node and panel positions as a named layout.
|
|
40
|
+
* @param {string} pName - The display name for this layout
|
|
41
|
+
* @returns {Object} The saved layout entry
|
|
42
|
+
*/
|
|
43
|
+
saveLayout(pName)
|
|
44
|
+
{
|
|
45
|
+
if (!this._FlowView)
|
|
46
|
+
{
|
|
47
|
+
this.log.warn('PictProviderFlowLayouts saveLayout: no FlowView reference');
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
let tmpFlowData = this._FlowView._FlowData;
|
|
52
|
+
let tmpLayoutHash = `layout-${this.fable.getUUID()}`;
|
|
53
|
+
let tmpNodePositions = {};
|
|
54
|
+
let tmpPanelPositions = {};
|
|
55
|
+
|
|
56
|
+
// Capture node positions (arrangement only, no content)
|
|
57
|
+
for (let i = 0; i < tmpFlowData.Nodes.length; i++)
|
|
58
|
+
{
|
|
59
|
+
let tmpNode = tmpFlowData.Nodes[i];
|
|
60
|
+
tmpNodePositions[tmpNode.Hash] =
|
|
61
|
+
{
|
|
62
|
+
X: tmpNode.X,
|
|
63
|
+
Y: tmpNode.Y,
|
|
64
|
+
Width: tmpNode.Width,
|
|
65
|
+
Height: tmpNode.Height
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Capture panel positions keyed by NodeHash (panels get new hashes on each open)
|
|
70
|
+
for (let i = 0; i < tmpFlowData.OpenPanels.length; i++)
|
|
71
|
+
{
|
|
72
|
+
let tmpPanel = tmpFlowData.OpenPanels[i];
|
|
73
|
+
tmpPanelPositions[tmpPanel.NodeHash] =
|
|
74
|
+
{
|
|
75
|
+
X: tmpPanel.X,
|
|
76
|
+
Y: tmpPanel.Y,
|
|
77
|
+
Width: tmpPanel.Width,
|
|
78
|
+
Height: tmpPanel.Height
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let tmpLayout =
|
|
83
|
+
{
|
|
84
|
+
Hash: tmpLayoutHash,
|
|
85
|
+
Name: pName || 'Untitled Layout',
|
|
86
|
+
CreatedAt: new Date().toISOString(),
|
|
87
|
+
NodePositions: tmpNodePositions,
|
|
88
|
+
PanelPositions: tmpPanelPositions,
|
|
89
|
+
ViewState:
|
|
90
|
+
{
|
|
91
|
+
PanX: tmpFlowData.ViewState.PanX,
|
|
92
|
+
PanY: tmpFlowData.ViewState.PanY,
|
|
93
|
+
Zoom: tmpFlowData.ViewState.Zoom
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
tmpFlowData.SavedLayouts.push(tmpLayout);
|
|
98
|
+
this._FlowView.marshalFromView();
|
|
99
|
+
|
|
100
|
+
if (this._FlowView._EventHandlerProvider)
|
|
101
|
+
{
|
|
102
|
+
this._FlowView._EventHandlerProvider.fireEvent('onLayoutSaved', tmpLayout);
|
|
103
|
+
this._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', tmpFlowData);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this.log.trace(`PictProviderFlowLayouts saved layout '${tmpLayout.Name}' (${tmpLayout.Hash})`);
|
|
107
|
+
|
|
108
|
+
return tmpLayout;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Restore a saved layout by hash.
|
|
113
|
+
* Nodes present in the saved layout are placed at their saved positions.
|
|
114
|
+
* Nodes not in the saved layout are auto-laid-out to the right of the
|
|
115
|
+
* positioned nodes.
|
|
116
|
+
* @param {string} pLayoutHash - The hash of the layout to restore
|
|
117
|
+
* @returns {boolean} Whether the layout was restored
|
|
118
|
+
*/
|
|
119
|
+
restoreLayout(pLayoutHash)
|
|
120
|
+
{
|
|
121
|
+
if (!this._FlowView)
|
|
122
|
+
{
|
|
123
|
+
this.log.warn('PictProviderFlowLayouts restoreLayout: no FlowView reference');
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let tmpFlowData = this._FlowView._FlowData;
|
|
128
|
+
let tmpLayout = tmpFlowData.SavedLayouts.find(
|
|
129
|
+
(pLayout) => pLayout.Hash === pLayoutHash
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
if (!tmpLayout)
|
|
133
|
+
{
|
|
134
|
+
this.log.warn(`PictProviderFlowLayouts restoreLayout: layout '${pLayoutHash}' not found`);
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
let tmpMatchedNodes = [];
|
|
139
|
+
let tmpUnmatchedNodes = [];
|
|
140
|
+
|
|
141
|
+
// Apply saved positions to matched nodes; collect unmatched ones
|
|
142
|
+
for (let i = 0; i < tmpFlowData.Nodes.length; i++)
|
|
143
|
+
{
|
|
144
|
+
let tmpNode = tmpFlowData.Nodes[i];
|
|
145
|
+
let tmpSaved = tmpLayout.NodePositions[tmpNode.Hash];
|
|
146
|
+
|
|
147
|
+
if (tmpSaved)
|
|
148
|
+
{
|
|
149
|
+
tmpNode.X = tmpSaved.X;
|
|
150
|
+
tmpNode.Y = tmpSaved.Y;
|
|
151
|
+
if (typeof tmpSaved.Width === 'number') tmpNode.Width = tmpSaved.Width;
|
|
152
|
+
if (typeof tmpSaved.Height === 'number') tmpNode.Height = tmpSaved.Height;
|
|
153
|
+
tmpMatchedNodes.push(tmpNode);
|
|
154
|
+
}
|
|
155
|
+
else
|
|
156
|
+
{
|
|
157
|
+
tmpUnmatchedNodes.push(tmpNode);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Apply saved panel positions (keyed by NodeHash)
|
|
162
|
+
if (tmpLayout.PanelPositions)
|
|
163
|
+
{
|
|
164
|
+
for (let i = 0; i < tmpFlowData.OpenPanels.length; i++)
|
|
165
|
+
{
|
|
166
|
+
let tmpPanel = tmpFlowData.OpenPanels[i];
|
|
167
|
+
let tmpSavedPanel = tmpLayout.PanelPositions[tmpPanel.NodeHash];
|
|
168
|
+
|
|
169
|
+
if (tmpSavedPanel)
|
|
170
|
+
{
|
|
171
|
+
tmpPanel.X = tmpSavedPanel.X;
|
|
172
|
+
tmpPanel.Y = tmpSavedPanel.Y;
|
|
173
|
+
if (typeof tmpSavedPanel.Width === 'number') tmpPanel.Width = tmpSavedPanel.Width;
|
|
174
|
+
if (typeof tmpSavedPanel.Height === 'number') tmpPanel.Height = tmpSavedPanel.Height;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Auto-layout unmatched nodes to the right of positioned nodes
|
|
180
|
+
if (tmpUnmatchedNodes.length > 0 && this._FlowView._LayoutService)
|
|
181
|
+
{
|
|
182
|
+
this._FlowView._LayoutService.autoLayoutSubset(
|
|
183
|
+
tmpUnmatchedNodes,
|
|
184
|
+
tmpMatchedNodes,
|
|
185
|
+
tmpFlowData.Connections
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Restore view state (camera position)
|
|
190
|
+
if (tmpLayout.ViewState)
|
|
191
|
+
{
|
|
192
|
+
if (typeof tmpLayout.ViewState.PanX === 'number')
|
|
193
|
+
{
|
|
194
|
+
tmpFlowData.ViewState.PanX = tmpLayout.ViewState.PanX;
|
|
195
|
+
}
|
|
196
|
+
if (typeof tmpLayout.ViewState.PanY === 'number')
|
|
197
|
+
{
|
|
198
|
+
tmpFlowData.ViewState.PanY = tmpLayout.ViewState.PanY;
|
|
199
|
+
}
|
|
200
|
+
if (typeof tmpLayout.ViewState.Zoom === 'number')
|
|
201
|
+
{
|
|
202
|
+
tmpFlowData.ViewState.Zoom = tmpLayout.ViewState.Zoom;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
this._FlowView.renderFlow();
|
|
207
|
+
this._FlowView.marshalFromView();
|
|
208
|
+
|
|
209
|
+
if (this._FlowView._EventHandlerProvider)
|
|
210
|
+
{
|
|
211
|
+
this._FlowView._EventHandlerProvider.fireEvent('onLayoutRestored', tmpLayout);
|
|
212
|
+
this._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', tmpFlowData);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
this.log.trace(`PictProviderFlowLayouts restored layout '${tmpLayout.Name}' (${tmpLayout.Hash})`);
|
|
216
|
+
|
|
217
|
+
return true;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Delete a saved layout by hash.
|
|
222
|
+
* @param {string} pLayoutHash - The hash of the layout to delete
|
|
223
|
+
* @returns {boolean} Whether the layout was deleted
|
|
224
|
+
*/
|
|
225
|
+
deleteLayout(pLayoutHash)
|
|
226
|
+
{
|
|
227
|
+
if (!this._FlowView)
|
|
228
|
+
{
|
|
229
|
+
this.log.warn('PictProviderFlowLayouts deleteLayout: no FlowView reference');
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
let tmpFlowData = this._FlowView._FlowData;
|
|
234
|
+
let tmpIndex = tmpFlowData.SavedLayouts.findIndex(
|
|
235
|
+
(pLayout) => pLayout.Hash === pLayoutHash
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
if (tmpIndex < 0)
|
|
239
|
+
{
|
|
240
|
+
this.log.warn(`PictProviderFlowLayouts deleteLayout: layout '${pLayoutHash}' not found`);
|
|
241
|
+
return false;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
let tmpRemovedLayout = tmpFlowData.SavedLayouts.splice(tmpIndex, 1)[0];
|
|
245
|
+
this._FlowView.marshalFromView();
|
|
246
|
+
|
|
247
|
+
if (this._FlowView._EventHandlerProvider)
|
|
248
|
+
{
|
|
249
|
+
this._FlowView._EventHandlerProvider.fireEvent('onLayoutDeleted', tmpRemovedLayout);
|
|
250
|
+
this._FlowView._EventHandlerProvider.fireEvent('onFlowChanged', tmpFlowData);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
this.log.trace(`PictProviderFlowLayouts deleted layout '${tmpRemovedLayout.Name}' (${tmpRemovedLayout.Hash})`);
|
|
254
|
+
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Get the list of saved layouts.
|
|
260
|
+
* @returns {Array} Array of saved layout objects
|
|
261
|
+
*/
|
|
262
|
+
getLayouts()
|
|
263
|
+
{
|
|
264
|
+
if (!this._FlowView) return [];
|
|
265
|
+
return this._FlowView._FlowData.SavedLayouts;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Get a specific saved layout by hash.
|
|
270
|
+
* @param {string} pLayoutHash
|
|
271
|
+
* @returns {Object|null}
|
|
272
|
+
*/
|
|
273
|
+
getLayout(pLayoutHash)
|
|
274
|
+
{
|
|
275
|
+
if (!this._FlowView) return null;
|
|
276
|
+
return this._FlowView._FlowData.SavedLayouts.find(
|
|
277
|
+
(pLayout) => pLayout.Hash === pLayoutHash
|
|
278
|
+
) || null;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
module.exports = PictProviderFlowLayouts;
|
|
283
|
+
|
|
284
|
+
module.exports.default_configuration = _ProviderConfiguration;
|
|
@@ -93,6 +93,20 @@ class PictProviderFlowNodeTypes extends libPictProvider
|
|
|
93
93
|
|
|
94
94
|
// Initialize with default node types
|
|
95
95
|
this._NodeTypes = JSON.parse(JSON.stringify(_DefaultNodeTypes));
|
|
96
|
+
|
|
97
|
+
// Merge any additional node types passed in via options
|
|
98
|
+
if (pOptions && pOptions.AdditionalNodeTypes && typeof pOptions.AdditionalNodeTypes === 'object')
|
|
99
|
+
{
|
|
100
|
+
let tmpAdditionalKeys = Object.keys(pOptions.AdditionalNodeTypes);
|
|
101
|
+
for (let i = 0; i < tmpAdditionalKeys.length; i++)
|
|
102
|
+
{
|
|
103
|
+
this._NodeTypes[tmpAdditionalKeys[i]] = Object.assign(
|
|
104
|
+
{},
|
|
105
|
+
this._NodeTypes[tmpAdditionalKeys[i]] || {},
|
|
106
|
+
JSON.parse(JSON.stringify(pOptions.AdditionalNodeTypes[tmpAdditionalKeys[i]]))
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
96
110
|
}
|
|
97
111
|
|
|
98
112
|
/**
|
|
@@ -166,6 +180,62 @@ class PictProviderFlowNodeTypes extends libPictProvider
|
|
|
166
180
|
{
|
|
167
181
|
return Object.keys(this._NodeTypes);
|
|
168
182
|
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Get all enabled node types that have FlowCard metadata.
|
|
186
|
+
* Returns only types that are cards and whose Enabled flag is true.
|
|
187
|
+
* @returns {Array<Object>} Array of node type configurations
|
|
188
|
+
*/
|
|
189
|
+
getEnabledCards()
|
|
190
|
+
{
|
|
191
|
+
let tmpCards = [];
|
|
192
|
+
let tmpKeys = Object.keys(this._NodeTypes);
|
|
193
|
+
for (let i = 0; i < tmpKeys.length; i++)
|
|
194
|
+
{
|
|
195
|
+
let tmpType = this._NodeTypes[tmpKeys[i]];
|
|
196
|
+
if (tmpType.CardMetadata)
|
|
197
|
+
{
|
|
198
|
+
if (tmpType.CardMetadata.Enabled !== false)
|
|
199
|
+
{
|
|
200
|
+
tmpCards.push(JSON.parse(JSON.stringify(tmpType)));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return tmpCards;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Get all enabled cards grouped by category.
|
|
209
|
+
* @returns {Object} Map of category name to array of node type configurations
|
|
210
|
+
*/
|
|
211
|
+
getCardsByCategory()
|
|
212
|
+
{
|
|
213
|
+
let tmpCards = this.getEnabledCards();
|
|
214
|
+
let tmpCategories = {};
|
|
215
|
+
for (let i = 0; i < tmpCards.length; i++)
|
|
216
|
+
{
|
|
217
|
+
let tmpCategory = (tmpCards[i].CardMetadata && tmpCards[i].CardMetadata.Category)
|
|
218
|
+
? tmpCards[i].CardMetadata.Category
|
|
219
|
+
: 'General';
|
|
220
|
+
if (!tmpCategories[tmpCategory])
|
|
221
|
+
{
|
|
222
|
+
tmpCategories[tmpCategory] = [];
|
|
223
|
+
}
|
|
224
|
+
tmpCategories[tmpCategory].push(tmpCards[i]);
|
|
225
|
+
}
|
|
226
|
+
return tmpCategories;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Check whether a node type hash refers to a FlowCard (has CardMetadata).
|
|
231
|
+
* @param {string} pTypeHash
|
|
232
|
+
* @returns {boolean}
|
|
233
|
+
*/
|
|
234
|
+
isFlowCard(pTypeHash)
|
|
235
|
+
{
|
|
236
|
+
let tmpType = this._NodeTypes[pTypeHash];
|
|
237
|
+
return !!(tmpType && tmpType.CardMetadata);
|
|
238
|
+
}
|
|
169
239
|
}
|
|
170
240
|
|
|
171
241
|
module.exports = PictProviderFlowNodeTypes;
|