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,207 @@
|
|
|
1
|
+
const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PictFlowCard - Base class for flow diagram cards.
|
|
5
|
+
*
|
|
6
|
+
* Developers create subclasses of PictFlowCard to define reusable node types
|
|
7
|
+
* that appear in the flow palette. Each card describes a discrete operation
|
|
8
|
+
* (e.g. "If-Then-Else", "File Read") with configurable inputs, outputs, and
|
|
9
|
+
* metadata. Pict-Section-Flow uses registered cards to build a palette for
|
|
10
|
+
* the user to drag onto the graph.
|
|
11
|
+
*
|
|
12
|
+
* Configurable properties:
|
|
13
|
+
* - Title (string, required) - Display name shown on the node
|
|
14
|
+
* - Name (string, optional) - Longer descriptive name
|
|
15
|
+
* - Code (string, required) - Short identifier (e.g. "ITE", "SW")
|
|
16
|
+
* - Description (string, optional) - Brief explanation of what the card does
|
|
17
|
+
* - Icon (string, optional) - Icon identifier or emoji
|
|
18
|
+
* - PreviewImage (string, optional) - URL to a preview/thumbnail image
|
|
19
|
+
* - Documentation (string, optional) - URL or inline documentation text
|
|
20
|
+
* - Tooltip (string, optional) - Hover tooltip text
|
|
21
|
+
* - Inputs (array) - Named input ports, each with:
|
|
22
|
+
* - Name (string) - Port label
|
|
23
|
+
* - Side (string) - Port side ('left', 'top', etc.)
|
|
24
|
+
* - MinimumInputCount (number) - Minimum connections accepted (default 0)
|
|
25
|
+
* - MaximumInputCount (number) - Maximum connections accepted (default -1, unlimited)
|
|
26
|
+
* - Outputs (array) - Named output ports
|
|
27
|
+
* - Enabled (boolean) - Whether this card is available in the palette
|
|
28
|
+
* - PropertiesPanel (object, optional) - Configuration for the on-graph properties panel
|
|
29
|
+
* - PanelType (string) - 'Template', 'Markdown', 'Form', or 'View'
|
|
30
|
+
* - DefaultWidth (number) - Panel width (default 300)
|
|
31
|
+
* - DefaultHeight (number) - Panel height (default 200)
|
|
32
|
+
* - Title (string) - Panel title bar text
|
|
33
|
+
* - Configuration (object) - Panel-type-specific configuration
|
|
34
|
+
*
|
|
35
|
+
* Usage:
|
|
36
|
+
* class MyCard extends PictFlowCard {
|
|
37
|
+
* constructor(pFable, pOptions, pServiceHash) {
|
|
38
|
+
* super(pFable, pOptions, pServiceHash);
|
|
39
|
+
* this.cardTitle = 'My Card';
|
|
40
|
+
* this.cardCode = 'MC';
|
|
41
|
+
* this.cardInputs = [{ Name: 'Data', Side: 'left', MinimumInputCount: 1, MaximumInputCount: 1 }];
|
|
42
|
+
* this.cardOutputs = [{ Name: 'Result', Side: 'right' }];
|
|
43
|
+
* }
|
|
44
|
+
* }
|
|
45
|
+
*/
|
|
46
|
+
class PictFlowCard extends libFableServiceProviderBase
|
|
47
|
+
{
|
|
48
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
49
|
+
{
|
|
50
|
+
let tmpOptions = Object.assign({}, PictFlowCard.default_configuration, pOptions);
|
|
51
|
+
super(pFable, tmpOptions, pServiceHash);
|
|
52
|
+
|
|
53
|
+
this.serviceType = 'PictFlowCard';
|
|
54
|
+
|
|
55
|
+
// --- Card metadata ---
|
|
56
|
+
this.cardTitle = (tmpOptions.Title) ? tmpOptions.Title : 'Card';
|
|
57
|
+
this.cardName = (tmpOptions.Name) ? tmpOptions.Name : false;
|
|
58
|
+
this.cardCode = (tmpOptions.Code) ? tmpOptions.Code : '';
|
|
59
|
+
this.cardDescription = (tmpOptions.Description) ? tmpOptions.Description : false;
|
|
60
|
+
this.cardIcon = (tmpOptions.Icon) ? tmpOptions.Icon : false;
|
|
61
|
+
this.cardPreviewImage = (tmpOptions.PreviewImage) ? tmpOptions.PreviewImage : false;
|
|
62
|
+
this.cardDocumentation = (tmpOptions.Documentation) ? tmpOptions.Documentation : false;
|
|
63
|
+
this.cardTooltip = (tmpOptions.Tooltip) ? tmpOptions.Tooltip : false;
|
|
64
|
+
|
|
65
|
+
// --- Card enabled state ---
|
|
66
|
+
this.cardEnabled = (typeof tmpOptions.Enabled === 'boolean') ? tmpOptions.Enabled : true;
|
|
67
|
+
|
|
68
|
+
// --- Card appearance ---
|
|
69
|
+
this.cardTitleBarColor = (tmpOptions.TitleBarColor) ? tmpOptions.TitleBarColor : '#2c3e50';
|
|
70
|
+
this.cardBodyStyle = (tmpOptions.BodyStyle) ? tmpOptions.BodyStyle : {};
|
|
71
|
+
this.cardWidth = (typeof tmpOptions.Width === 'number') ? tmpOptions.Width : 180;
|
|
72
|
+
this.cardHeight = (typeof tmpOptions.Height === 'number') ? tmpOptions.Height : 80;
|
|
73
|
+
this.cardCategory = (tmpOptions.Category) ? tmpOptions.Category : 'General';
|
|
74
|
+
|
|
75
|
+
// --- Input and Output port definitions ---
|
|
76
|
+
// Inputs: [{ Name: 'In', Side: 'left', MinimumInputCount: 0, MaximumInputCount: -1 }]
|
|
77
|
+
// Outputs: [{ Name: 'Out', Side: 'right' }]
|
|
78
|
+
this.cardInputs = Array.isArray(tmpOptions.Inputs) ? tmpOptions.Inputs : [];
|
|
79
|
+
this.cardOutputs = Array.isArray(tmpOptions.Outputs) ? tmpOptions.Outputs : [];
|
|
80
|
+
|
|
81
|
+
// --- Properties panel configuration ---
|
|
82
|
+
this.cardPropertiesPanel = (tmpOptions.PropertiesPanel && typeof tmpOptions.PropertiesPanel === 'object')
|
|
83
|
+
? tmpOptions.PropertiesPanel
|
|
84
|
+
: null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Generate the node type configuration object for the NodeTypes provider.
|
|
89
|
+
* This converts the card's properties into the format expected by
|
|
90
|
+
* PictProviderFlowNodeTypes.registerNodeType().
|
|
91
|
+
*
|
|
92
|
+
* @returns {Object} Node type configuration
|
|
93
|
+
*/
|
|
94
|
+
getNodeTypeConfiguration()
|
|
95
|
+
{
|
|
96
|
+
let tmpPorts = [];
|
|
97
|
+
|
|
98
|
+
// Build input ports
|
|
99
|
+
for (let i = 0; i < this.cardInputs.length; i++)
|
|
100
|
+
{
|
|
101
|
+
let tmpInput = this.cardInputs[i];
|
|
102
|
+
let tmpPort =
|
|
103
|
+
{
|
|
104
|
+
Hash: null,
|
|
105
|
+
Direction: 'input',
|
|
106
|
+
Side: tmpInput.Side || 'left',
|
|
107
|
+
Label: tmpInput.Name || `In ${i + 1}`,
|
|
108
|
+
MinimumInputCount: (typeof tmpInput.MinimumInputCount === 'number') ? tmpInput.MinimumInputCount : 0,
|
|
109
|
+
MaximumInputCount: (typeof tmpInput.MaximumInputCount === 'number') ? tmpInput.MaximumInputCount : -1
|
|
110
|
+
};
|
|
111
|
+
tmpPorts.push(tmpPort);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Build output ports
|
|
115
|
+
for (let i = 0; i < this.cardOutputs.length; i++)
|
|
116
|
+
{
|
|
117
|
+
let tmpOutput = this.cardOutputs[i];
|
|
118
|
+
tmpPorts.push(
|
|
119
|
+
{
|
|
120
|
+
Hash: null,
|
|
121
|
+
Direction: 'output',
|
|
122
|
+
Side: tmpOutput.Side || 'right',
|
|
123
|
+
Label: tmpOutput.Name || `Out ${i + 1}`
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// If no ports were defined, provide sensible defaults
|
|
128
|
+
if (tmpPorts.length === 0)
|
|
129
|
+
{
|
|
130
|
+
tmpPorts.push({ Hash: null, Direction: 'input', Side: 'left', Label: 'In' });
|
|
131
|
+
tmpPorts.push({ Hash: null, Direction: 'output', Side: 'right', Label: 'Out' });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
let tmpResult =
|
|
135
|
+
{
|
|
136
|
+
Hash: this.cardCode,
|
|
137
|
+
Label: this.cardTitle,
|
|
138
|
+
DefaultWidth: this.cardWidth,
|
|
139
|
+
DefaultHeight: this.cardHeight,
|
|
140
|
+
DefaultPorts: tmpPorts,
|
|
141
|
+
TitleBarColor: this.cardTitleBarColor,
|
|
142
|
+
BodyStyle: JSON.parse(JSON.stringify(this.cardBodyStyle)),
|
|
143
|
+
// Extended FlowCard metadata stored alongside the type
|
|
144
|
+
CardMetadata:
|
|
145
|
+
{
|
|
146
|
+
Name: this.cardName,
|
|
147
|
+
Code: this.cardCode,
|
|
148
|
+
Description: this.cardDescription,
|
|
149
|
+
Icon: this.cardIcon,
|
|
150
|
+
PreviewImage: this.cardPreviewImage,
|
|
151
|
+
Documentation: this.cardDocumentation,
|
|
152
|
+
Tooltip: this.cardTooltip,
|
|
153
|
+
Enabled: this.cardEnabled,
|
|
154
|
+
Category: this.cardCategory
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// Include properties panel config if defined
|
|
159
|
+
if (this.cardPropertiesPanel)
|
|
160
|
+
{
|
|
161
|
+
tmpResult.PropertiesPanel = JSON.parse(JSON.stringify(this.cardPropertiesPanel));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return tmpResult;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Register this card with a FlowView's node type provider.
|
|
169
|
+
*
|
|
170
|
+
* @param {Object} pFlowView - The PictViewFlow instance
|
|
171
|
+
* @returns {boolean} Whether registration succeeded
|
|
172
|
+
*/
|
|
173
|
+
registerWithFlowView(pFlowView)
|
|
174
|
+
{
|
|
175
|
+
if (!pFlowView || !pFlowView._NodeTypeProvider)
|
|
176
|
+
{
|
|
177
|
+
this.log.warn('PictFlowCard registerWithFlowView: no valid FlowView or NodeTypeProvider');
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
let tmpConfig = this.getNodeTypeConfiguration();
|
|
182
|
+
return pFlowView._NodeTypeProvider.registerNodeType(tmpConfig);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
module.exports = PictFlowCard;
|
|
187
|
+
|
|
188
|
+
module.exports.default_configuration =
|
|
189
|
+
{
|
|
190
|
+
Title: 'Card',
|
|
191
|
+
Name: false,
|
|
192
|
+
Code: '',
|
|
193
|
+
Description: false,
|
|
194
|
+
Icon: false,
|
|
195
|
+
PreviewImage: false,
|
|
196
|
+
Documentation: false,
|
|
197
|
+
Tooltip: false,
|
|
198
|
+
Inputs: [],
|
|
199
|
+
Outputs: [],
|
|
200
|
+
Enabled: true,
|
|
201
|
+
TitleBarColor: '#2c3e50',
|
|
202
|
+
BodyStyle: {},
|
|
203
|
+
Width: 180,
|
|
204
|
+
Height: 80,
|
|
205
|
+
Category: 'General',
|
|
206
|
+
PropertiesPanel: null
|
|
207
|
+
};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const libFableServiceProviderBase = require('fable-serviceproviderbase');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* PictFlowCardPropertiesPanel - Base class for flow card property panels.
|
|
5
|
+
*
|
|
6
|
+
* Developers create subclasses to define what UI appears when a user opens
|
|
7
|
+
* the properties panel for a node on the flow graph. Four built-in panel
|
|
8
|
+
* types are provided:
|
|
9
|
+
*
|
|
10
|
+
* - Template - Renders pict templates
|
|
11
|
+
* - Markdown - Renders markdown via pict-section-content
|
|
12
|
+
* - Form - Creates an ephemeral pict-section-form section
|
|
13
|
+
* - View - Renders an existing registered pict-view
|
|
14
|
+
*
|
|
15
|
+
* Configurable properties:
|
|
16
|
+
* - PanelType (string) - Panel type identifier
|
|
17
|
+
* - Title (string) - Panel title bar text
|
|
18
|
+
* - Width (number) - Default panel width in pixels
|
|
19
|
+
* - Height (number) - Default panel height in pixels
|
|
20
|
+
* - Configuration (object) - Panel-type-specific configuration
|
|
21
|
+
*/
|
|
22
|
+
class PictFlowCardPropertiesPanel extends libFableServiceProviderBase
|
|
23
|
+
{
|
|
24
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
25
|
+
{
|
|
26
|
+
let tmpOptions = Object.assign({}, PictFlowCardPropertiesPanel.default_configuration, pOptions);
|
|
27
|
+
super(pFable, tmpOptions, pServiceHash);
|
|
28
|
+
|
|
29
|
+
this.serviceType = 'PictFlowCardPropertiesPanel';
|
|
30
|
+
|
|
31
|
+
// Panel metadata
|
|
32
|
+
this.panelType = tmpOptions.PanelType || 'Base';
|
|
33
|
+
this.panelTitle = tmpOptions.Title || 'Properties';
|
|
34
|
+
this.panelWidth = (typeof tmpOptions.Width === 'number') ? tmpOptions.Width : 300;
|
|
35
|
+
this.panelHeight = (typeof tmpOptions.Height === 'number') ? tmpOptions.Height : 200;
|
|
36
|
+
|
|
37
|
+
// Reference to the flow view (set when panel is activated)
|
|
38
|
+
this._FlowView = null;
|
|
39
|
+
|
|
40
|
+
// The node data this panel is operating on (set when panel is opened)
|
|
41
|
+
this._NodeData = null;
|
|
42
|
+
|
|
43
|
+
// The DOM container element for panel content (set during render)
|
|
44
|
+
this._ContentContainer = null;
|
|
45
|
+
|
|
46
|
+
// The panel configuration (panel-type-specific)
|
|
47
|
+
this._Configuration = tmpOptions.Configuration || {};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Render the panel's content into a DOM container element.
|
|
52
|
+
* Subclasses MUST override this.
|
|
53
|
+
*
|
|
54
|
+
* @param {HTMLElement} pContainer - The DOM element to render into
|
|
55
|
+
* @param {Object} pNodeData - The node data object (has .Data property)
|
|
56
|
+
*/
|
|
57
|
+
render(pContainer, pNodeData)
|
|
58
|
+
{
|
|
59
|
+
this._ContentContainer = pContainer;
|
|
60
|
+
this._NodeData = pNodeData;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Marshal data FROM the node's Data object INTO the panel UI.
|
|
65
|
+
* Called when the panel opens or when data changes externally.
|
|
66
|
+
*
|
|
67
|
+
* @param {Object} pNodeData
|
|
68
|
+
*/
|
|
69
|
+
marshalToPanel(pNodeData)
|
|
70
|
+
{
|
|
71
|
+
this._NodeData = pNodeData;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Marshal data FROM the panel UI INTO the node's Data object.
|
|
76
|
+
* Called before saving or when the panel is about to close.
|
|
77
|
+
*
|
|
78
|
+
* @param {Object} pNodeData
|
|
79
|
+
*/
|
|
80
|
+
marshalFromPanel(pNodeData)
|
|
81
|
+
{
|
|
82
|
+
// Subclasses override
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Called when the panel is being destroyed (closed).
|
|
87
|
+
* Subclasses should clean up resources.
|
|
88
|
+
*/
|
|
89
|
+
destroy()
|
|
90
|
+
{
|
|
91
|
+
this._ContentContainer = null;
|
|
92
|
+
this._NodeData = null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
module.exports = PictFlowCardPropertiesPanel;
|
|
97
|
+
|
|
98
|
+
module.exports.default_configuration =
|
|
99
|
+
{
|
|
100
|
+
PanelType: 'Base',
|
|
101
|
+
Title: 'Properties',
|
|
102
|
+
Width: 300,
|
|
103
|
+
Height: 200,
|
|
104
|
+
Configuration: {}
|
|
105
|
+
};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
const libPictFlowCardPropertiesPanel = require('../PictFlowCardPropertiesPanel.js');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* FlowCardPropertiesPanel-Form
|
|
5
|
+
*
|
|
6
|
+
* Creates an ephemeral pict-section-form section to edit the node's Data object.
|
|
7
|
+
* Uses PictViewFormMetacontroller.injectManifest() to dynamically create form
|
|
8
|
+
* sections at runtime.
|
|
9
|
+
*
|
|
10
|
+
* Note: pict-section-form must be available in the consuming application
|
|
11
|
+
* (it is an optional/peer dependency, not bundled with pict-section-flow).
|
|
12
|
+
*
|
|
13
|
+
* Configuration:
|
|
14
|
+
* {
|
|
15
|
+
* PanelType: 'Form',
|
|
16
|
+
* Configuration: {
|
|
17
|
+
* Manifest: {
|
|
18
|
+
* Sections: [...],
|
|
19
|
+
* Descriptors: {...}
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
* }
|
|
23
|
+
*/
|
|
24
|
+
class FlowCardPropertiesPanelForm extends libPictFlowCardPropertiesPanel
|
|
25
|
+
{
|
|
26
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
27
|
+
{
|
|
28
|
+
super(pFable, pOptions, pServiceHash);
|
|
29
|
+
|
|
30
|
+
this.serviceType = 'PictFlowCardPropertiesPanel-Form';
|
|
31
|
+
|
|
32
|
+
this._Metacontroller = null;
|
|
33
|
+
this._InjectedSectionHash = null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Render the form into the panel body.
|
|
38
|
+
*/
|
|
39
|
+
render(pContainer, pNodeData)
|
|
40
|
+
{
|
|
41
|
+
super.render(pContainer, pNodeData);
|
|
42
|
+
|
|
43
|
+
if (!this._Configuration || !this._Configuration.Manifest)
|
|
44
|
+
{
|
|
45
|
+
pContainer.innerHTML = '<em>No form manifest configured</em>';
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Create a unique container ID for the form section
|
|
50
|
+
let tmpContainerID = `pict-flow-panel-form-${pNodeData.Hash}`;
|
|
51
|
+
pContainer.innerHTML = `<div id="${tmpContainerID}"></div>`;
|
|
52
|
+
|
|
53
|
+
try
|
|
54
|
+
{
|
|
55
|
+
// Look for an existing metacontroller or create one
|
|
56
|
+
if (!this._Metacontroller)
|
|
57
|
+
{
|
|
58
|
+
// Try to find PictFormMetacontroller service type (check both common names)
|
|
59
|
+
let tmpServiceType = null;
|
|
60
|
+
if (this.fable.servicesMap.hasOwnProperty('PictFormMetacontroller'))
|
|
61
|
+
{
|
|
62
|
+
tmpServiceType = 'PictFormMetacontroller';
|
|
63
|
+
}
|
|
64
|
+
else if (this.fable.servicesMap.hasOwnProperty('PictViewFormMetacontroller'))
|
|
65
|
+
{
|
|
66
|
+
tmpServiceType = 'PictViewFormMetacontroller';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (tmpServiceType)
|
|
70
|
+
{
|
|
71
|
+
this._Metacontroller = this.fable.instantiateServiceProviderWithoutRegistration(tmpServiceType,
|
|
72
|
+
{
|
|
73
|
+
ViewIdentifier: `FlowPanelForm-${pNodeData.Hash}`,
|
|
74
|
+
DefaultDestinationAddress: `#${tmpContainerID}`,
|
|
75
|
+
AutoRender: false,
|
|
76
|
+
AutoPopulateAfterRender: true,
|
|
77
|
+
AutoSolveBeforeRender: false
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (this._Metacontroller && typeof this._Metacontroller.injectManifestAndRender === 'function')
|
|
83
|
+
{
|
|
84
|
+
// Create the FormContainer div that the metacontroller's
|
|
85
|
+
// updateMetatemplateInDOM() expects when adding section wrappers.
|
|
86
|
+
// Normally this is created when the metacontroller renders its own
|
|
87
|
+
// metatemplate, but we skip that step since we only need the
|
|
88
|
+
// injected section views, not the metacontroller's own renderable.
|
|
89
|
+
let tmpFormContainerID = `Pict-${this._Metacontroller.UUID}-FormContainer`;
|
|
90
|
+
let tmpContainerEl = pContainer.querySelector(`#${tmpContainerID}`);
|
|
91
|
+
if (tmpContainerEl)
|
|
92
|
+
{
|
|
93
|
+
tmpContainerEl.innerHTML = `<div id="${tmpFormContainerID}" class="pict-form"></div>`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Deep clone the manifest so each panel gets its own copy
|
|
97
|
+
let tmpManifest = JSON.parse(JSON.stringify(this._Configuration.Manifest));
|
|
98
|
+
this._InjectedSectionHash = tmpManifest.Hash || null;
|
|
99
|
+
|
|
100
|
+
// Use injectManifestAndRender which properly creates section views,
|
|
101
|
+
// updates the metatemplate in the DOM, and renders each section view
|
|
102
|
+
this._Metacontroller.injectManifestAndRender(tmpManifest);
|
|
103
|
+
}
|
|
104
|
+
else if (this._Metacontroller && typeof this._Metacontroller.injectManifest === 'function')
|
|
105
|
+
{
|
|
106
|
+
// Fallback for older pict-section-form versions: inject the
|
|
107
|
+
// manifest and render each section view individually
|
|
108
|
+
let tmpManifest = JSON.parse(JSON.stringify(this._Configuration.Manifest));
|
|
109
|
+
let tmpViewsToRender = this._Metacontroller.injectManifest(tmpManifest);
|
|
110
|
+
this._InjectedSectionHash = tmpManifest.Hash || null;
|
|
111
|
+
|
|
112
|
+
// Create container divs for each section view and render them
|
|
113
|
+
let tmpContainerEl = pContainer.querySelector(`#${tmpContainerID}`);
|
|
114
|
+
if (tmpContainerEl && tmpViewsToRender.length > 0)
|
|
115
|
+
{
|
|
116
|
+
let tmpInnerHTML = '';
|
|
117
|
+
for (let i = 0; i < tmpViewsToRender.length; i++)
|
|
118
|
+
{
|
|
119
|
+
let tmpDestID = tmpViewsToRender[i].options.DefaultDestinationAddress;
|
|
120
|
+
if (tmpDestID && tmpDestID.charAt(0) === '#')
|
|
121
|
+
{
|
|
122
|
+
tmpDestID = tmpDestID.substring(1);
|
|
123
|
+
}
|
|
124
|
+
tmpInnerHTML += `<div id="${tmpDestID}" class="pict-form-view"></div>`;
|
|
125
|
+
}
|
|
126
|
+
tmpContainerEl.innerHTML = tmpInnerHTML;
|
|
127
|
+
|
|
128
|
+
for (let i = 0; i < tmpViewsToRender.length; i++)
|
|
129
|
+
{
|
|
130
|
+
tmpViewsToRender[i].render();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else
|
|
135
|
+
{
|
|
136
|
+
pContainer.innerHTML = '<em>pict-section-form is not available. Install it in your application to use Form panels.</em>';
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (pError)
|
|
140
|
+
{
|
|
141
|
+
this.log.warn(`FlowCardPropertiesPanel-Form render error: ${pError.message}`);
|
|
142
|
+
pContainer.innerHTML = `<em>Form render error: ${pError.message}</em>`;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
marshalFromPanel(pNodeData)
|
|
147
|
+
{
|
|
148
|
+
if (this._Metacontroller && typeof this._Metacontroller.marshalFromView === 'function')
|
|
149
|
+
{
|
|
150
|
+
this._Metacontroller.marshalFromView();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
destroy()
|
|
155
|
+
{
|
|
156
|
+
this._Metacontroller = null;
|
|
157
|
+
this._InjectedSectionHash = null;
|
|
158
|
+
super.destroy();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = FlowCardPropertiesPanelForm;
|
|
163
|
+
|
|
164
|
+
module.exports.default_configuration = Object.assign(
|
|
165
|
+
{},
|
|
166
|
+
libPictFlowCardPropertiesPanel.default_configuration,
|
|
167
|
+
{
|
|
168
|
+
PanelType: 'Form',
|
|
169
|
+
Configuration:
|
|
170
|
+
{
|
|
171
|
+
Manifest: null
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
);
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
const libPictFlowCardPropertiesPanel = require('../PictFlowCardPropertiesPanel.js');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* FlowCardPropertiesPanel-Markdown
|
|
5
|
+
*
|
|
6
|
+
* Renders markdown content into the panel body using pict-section-content's
|
|
7
|
+
* PictContentProvider service. When pict-section-content is installed and its
|
|
8
|
+
* PictContentProvider service type has been registered with the pict instance,
|
|
9
|
+
* full markdown rendering is available (headings, lists, tables, code blocks
|
|
10
|
+
* with syntax highlighting, KaTeX equations, Mermaid diagrams, etc.).
|
|
11
|
+
*
|
|
12
|
+
* If PictContentProvider is not available, the panel falls back to displaying
|
|
13
|
+
* the raw markdown as pre-formatted text.
|
|
14
|
+
*
|
|
15
|
+
* Configuration:
|
|
16
|
+
* {
|
|
17
|
+
* PanelType: 'Markdown',
|
|
18
|
+
* Configuration: {
|
|
19
|
+
* Markdown: '# Title\nSome **markdown** content'
|
|
20
|
+
* }
|
|
21
|
+
* }
|
|
22
|
+
*
|
|
23
|
+
* Or use an address to pull markdown from the node's data:
|
|
24
|
+
* {
|
|
25
|
+
* PanelType: 'Markdown',
|
|
26
|
+
* Configuration: {
|
|
27
|
+
* MarkdownAddress: 'Data.MarkdownContent'
|
|
28
|
+
* }
|
|
29
|
+
* }
|
|
30
|
+
*/
|
|
31
|
+
class FlowCardPropertiesPanelMarkdown extends libPictFlowCardPropertiesPanel
|
|
32
|
+
{
|
|
33
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
34
|
+
{
|
|
35
|
+
super(pFable, pOptions, pServiceHash);
|
|
36
|
+
|
|
37
|
+
this.serviceType = 'PictFlowCardPropertiesPanel-Markdown';
|
|
38
|
+
|
|
39
|
+
this._ContentProvider = null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Render markdown into the panel.
|
|
44
|
+
*/
|
|
45
|
+
render(pContainer, pNodeData)
|
|
46
|
+
{
|
|
47
|
+
super.render(pContainer, pNodeData);
|
|
48
|
+
|
|
49
|
+
this._renderMarkdown();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
marshalToPanel(pNodeData)
|
|
53
|
+
{
|
|
54
|
+
super.marshalToPanel(pNodeData);
|
|
55
|
+
this._renderMarkdown();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_renderMarkdown()
|
|
59
|
+
{
|
|
60
|
+
if (!this._ContentContainer || !this._Configuration) return;
|
|
61
|
+
|
|
62
|
+
let tmpMarkdown = '';
|
|
63
|
+
|
|
64
|
+
if (this._Configuration.MarkdownAddress && this._NodeData)
|
|
65
|
+
{
|
|
66
|
+
// Resolve markdown from node data using manyfest
|
|
67
|
+
tmpMarkdown = this.fable.manifest.getValueByHash(this._NodeData, this._Configuration.MarkdownAddress) || '';
|
|
68
|
+
}
|
|
69
|
+
else if (this._Configuration.Markdown)
|
|
70
|
+
{
|
|
71
|
+
tmpMarkdown = this._Configuration.Markdown;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!tmpMarkdown)
|
|
75
|
+
{
|
|
76
|
+
this._ContentContainer.innerHTML = '<em>No content</em>';
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Use pict-section-content's PictContentProvider for markdown rendering.
|
|
81
|
+
// The consuming application must register the PictContentProvider service
|
|
82
|
+
// type (from pict-section-content) for this to work.
|
|
83
|
+
try
|
|
84
|
+
{
|
|
85
|
+
if (!this._ContentProvider)
|
|
86
|
+
{
|
|
87
|
+
if (this.fable.servicesMap.hasOwnProperty('PictContentProvider'))
|
|
88
|
+
{
|
|
89
|
+
this._ContentProvider = this.fable.instantiateServiceProviderWithoutRegistration('PictContentProvider', {});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (this._ContentProvider && typeof this._ContentProvider.parseMarkdown === 'function')
|
|
94
|
+
{
|
|
95
|
+
let tmpHTML = this._ContentProvider.parseMarkdown(tmpMarkdown);
|
|
96
|
+
this._ContentContainer.innerHTML = tmpHTML;
|
|
97
|
+
|
|
98
|
+
// Post-render hooks for equations and diagrams
|
|
99
|
+
if (typeof this._ContentProvider.renderKaTeXEquations === 'function')
|
|
100
|
+
{
|
|
101
|
+
this._ContentProvider.renderKaTeXEquations(this._ContentContainer);
|
|
102
|
+
}
|
|
103
|
+
if (typeof this._ContentProvider.renderMermaidDiagrams === 'function')
|
|
104
|
+
{
|
|
105
|
+
this._ContentProvider.renderMermaidDiagrams(this._ContentContainer);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else
|
|
109
|
+
{
|
|
110
|
+
// PictContentProvider not registered — render as pre-formatted text
|
|
111
|
+
this._ContentContainer.innerHTML = `<pre style="white-space: pre-wrap; font-family: inherit;">${this._escapeHTML(tmpMarkdown)}</pre>`;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
catch (pError)
|
|
115
|
+
{
|
|
116
|
+
this.log.warn(`FlowCardPropertiesPanel-Markdown render error: ${pError.message}`);
|
|
117
|
+
this._ContentContainer.innerHTML = `<pre style="white-space: pre-wrap; font-family: inherit;">${this._escapeHTML(tmpMarkdown)}</pre>`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
_escapeHTML(pText)
|
|
122
|
+
{
|
|
123
|
+
let tmpDiv = document.createElement('div');
|
|
124
|
+
tmpDiv.textContent = pText;
|
|
125
|
+
return tmpDiv.innerHTML;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
destroy()
|
|
129
|
+
{
|
|
130
|
+
this._ContentProvider = null;
|
|
131
|
+
super.destroy();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = FlowCardPropertiesPanelMarkdown;
|
|
136
|
+
|
|
137
|
+
module.exports.default_configuration = Object.assign(
|
|
138
|
+
{},
|
|
139
|
+
libPictFlowCardPropertiesPanel.default_configuration,
|
|
140
|
+
{
|
|
141
|
+
PanelType: 'Markdown',
|
|
142
|
+
Configuration:
|
|
143
|
+
{
|
|
144
|
+
Markdown: '',
|
|
145
|
+
MarkdownAddress: ''
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
);
|