pict-section-formeditor 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +118 -0
- package/docs/.nojekyll +0 -0
- package/docs/README.md +162 -0
- package/docs/_sidebar.md +23 -0
- package/docs/_topbar.md +5 -0
- package/docs/cover.md +12 -0
- package/docs/css/docuserve.css +73 -0
- package/docs/index.html +39 -0
- package/docs/retold-catalog.json +224 -0
- package/docs/retold-keyword-index.json +46846 -0
- package/example_applications/form_editor/.quackage.json +10 -0
- package/example_applications/form_editor/FormEditor-Example-Application.js +226 -0
- package/example_applications/form_editor/html/icon-chooser.html +375 -0
- package/example_applications/form_editor/html/index.html +54 -0
- package/example_applications/form_editor/package.json +50 -0
- package/package.json +55 -0
- package/sample_manifests/Complex-Table.json +974 -0
- package/sample_manifests/Distill-Example.json +200 -0
- package/sample_manifests/Gradebook-Assignment.json +38 -0
- package/sample_manifests/Gradebook-Student.json +40 -0
- package/sample_manifests/Manyfest-Editor.json +347 -0
- package/sample_manifests/Simple-Form.json +232 -0
- package/sample_manifests/Simple-Table.json +79 -0
- package/source/Pict-Section-FormEditor-DefaultConfiguration.js +3321 -0
- package/source/Pict-Section-FormEditor.js +35 -0
- package/source/providers/Pict-Provider-ChildPictManager-Application.js +40 -0
- package/source/providers/Pict-Provider-ChildPictManager.js +238 -0
- package/source/providers/Pict-Provider-FormEditorDocumentation.js +356 -0
- package/source/providers/Pict-Provider-FormEditorDragDrop.js +535 -0
- package/source/providers/Pict-Provider-FormEditorIconography.js +1002 -0
- package/source/providers/Pict-Provider-FormEditorManifestOps.js +1443 -0
- package/source/providers/Pict-Provider-FormEditorRendering.js +730 -0
- package/source/providers/Pict-Provider-FormEditorUtilities.js +862 -0
- package/source/providers/Pict-Provider-PreviewCSS.js +42 -0
- package/source/views/PictView-FormEditor-InlineEditing.js +309 -0
- package/source/views/PictView-FormEditor-InputTypePicker.js +532 -0
- package/source/views/PictView-FormEditor-PropertiesPanel.js +7730 -0
- package/source/views/PictView-FormEditor.js +681 -0
- package/test/Pict-Section-FormEditor_tests.js +4102 -0
- package/user-documentation/.pict_documentation_topics.json +695 -0
- package/user-documentation/Getting-Started.md +32 -0
- package/user-documentation/Groups.md +52 -0
- package/user-documentation/Inputs.md +98 -0
- package/user-documentation/Sections.md +36 -0
- package/user-documentation/Shortcuts.md +44 -0
- package/user-documentation/Solver-Expression-Walkthrough.md +176 -0
- package/user-documentation/Solver-Expressions-Advanced.md +344 -0
- package/user-documentation/Solver-Functions.md +213 -0
- package/user-documentation/Solvers.md +81 -0
- package/user-documentation/ToC.md +18 -0
- package/user-documentation/solverfunctions/abs.md +84 -0
- package/user-documentation/solverfunctions/aggregationhistogram.md +83 -0
- package/user-documentation/solverfunctions/aggregationhistogrambyobject.md +64 -0
- package/user-documentation/solverfunctions/arrayconcat.md +64 -0
- package/user-documentation/solverfunctions/avg.md +81 -0
- package/user-documentation/solverfunctions/bucketset.md +69 -0
- package/user-documentation/solverfunctions/ceil.md +70 -0
- package/user-documentation/solverfunctions/cleanvaluearray.md +66 -0
- package/user-documentation/solverfunctions/cleanvalueobject.md +68 -0
- package/user-documentation/solverfunctions/colorgroupbackground.md +60 -0
- package/user-documentation/solverfunctions/colorinputbackground.md +62 -0
- package/user-documentation/solverfunctions/colorinputbackgroundtabular.md +64 -0
- package/user-documentation/solverfunctions/colorsectionbackground.md +59 -0
- package/user-documentation/solverfunctions/compare.md +72 -0
- package/user-documentation/solverfunctions/concat.md +73 -0
- package/user-documentation/solverfunctions/concatraw.md +73 -0
- package/user-documentation/solverfunctions/cos.md +75 -0
- package/user-documentation/solverfunctions/count.md +73 -0
- package/user-documentation/solverfunctions/countset.md +65 -0
- package/user-documentation/solverfunctions/countsetelements.md +63 -0
- package/user-documentation/solverfunctions/createarrayfromabsolutevalues.md +63 -0
- package/user-documentation/solverfunctions/createvalueobjectbyhashes.md +69 -0
- package/user-documentation/solverfunctions/cumulativesummation.md +96 -0
- package/user-documentation/solverfunctions/dateadddays.md +79 -0
- package/user-documentation/solverfunctions/dateaddhours.md +74 -0
- package/user-documentation/solverfunctions/dateaddmilliseconds.md +65 -0
- package/user-documentation/solverfunctions/dateaddminutes.md +72 -0
- package/user-documentation/solverfunctions/dateaddmonths.md +74 -0
- package/user-documentation/solverfunctions/dateaddseconds.md +66 -0
- package/user-documentation/solverfunctions/dateaddweeks.md +73 -0
- package/user-documentation/solverfunctions/dateaddyears.md +74 -0
- package/user-documentation/solverfunctions/datedaydifference.md +84 -0
- package/user-documentation/solverfunctions/datefromparts.md +81 -0
- package/user-documentation/solverfunctions/datehourdifference.md +64 -0
- package/user-documentation/solverfunctions/datemathadd.md +72 -0
- package/user-documentation/solverfunctions/datemilliseconddifference.md +64 -0
- package/user-documentation/solverfunctions/dateminutedifference.md +64 -0
- package/user-documentation/solverfunctions/datemonthdifference.md +66 -0
- package/user-documentation/solverfunctions/dateseconddifference.md +64 -0
- package/user-documentation/solverfunctions/dateweekdifference.md +65 -0
- package/user-documentation/solverfunctions/dateyeardifference.md +64 -0
- package/user-documentation/solverfunctions/differencearrays.md +59 -0
- package/user-documentation/solverfunctions/disablesolverordinal.md +58 -0
- package/user-documentation/solverfunctions/distributionhistogram.md +96 -0
- package/user-documentation/solverfunctions/distributionhistogrambyobject.md +64 -0
- package/user-documentation/solverfunctions/enablesolverordinal.md +57 -0
- package/user-documentation/solverfunctions/entryinset.md +72 -0
- package/user-documentation/solverfunctions/euler.md +77 -0
- package/user-documentation/solverfunctions/exp.md +74 -0
- package/user-documentation/solverfunctions/findfirstvaluebyexactmatch.md +67 -0
- package/user-documentation/solverfunctions/findfirstvaluebystringincludes.md +67 -0
- package/user-documentation/solverfunctions/flatten.md +76 -0
- package/user-documentation/solverfunctions/floor.md +70 -0
- package/user-documentation/solverfunctions/gaussianelimination.md +75 -0
- package/user-documentation/solverfunctions/generatearrayofobjectsfromsets.md +70 -0
- package/user-documentation/solverfunctions/generatehtmlhexcolor.md +67 -0
- package/user-documentation/solverfunctions/getvalue.md +90 -0
- package/user-documentation/solverfunctions/getvaluearray.md +64 -0
- package/user-documentation/solverfunctions/getvalueobject.md +67 -0
- package/user-documentation/solverfunctions/hidesections.md +58 -0
- package/user-documentation/solverfunctions/if.md +109 -0
- package/user-documentation/solverfunctions/iterativeseries.md +107 -0
- package/user-documentation/solverfunctions/join.md +75 -0
- package/user-documentation/solverfunctions/joinraw.md +64 -0
- package/user-documentation/solverfunctions/largestinset.md +63 -0
- package/user-documentation/solverfunctions/leastsquares.md +66 -0
- package/user-documentation/solverfunctions/linest.md +58 -0
- package/user-documentation/solverfunctions/log.md +74 -0
- package/user-documentation/solverfunctions/logvalues.md +65 -0
- package/user-documentation/solverfunctions/match.md +71 -0
- package/user-documentation/solverfunctions/matrixinverse.md +67 -0
- package/user-documentation/solverfunctions/matrixmultiply.md +71 -0
- package/user-documentation/solverfunctions/matrixtranspose.md +72 -0
- package/user-documentation/solverfunctions/matrixvectormultiply.md +69 -0
- package/user-documentation/solverfunctions/max.md +73 -0
- package/user-documentation/solverfunctions/mean.md +63 -0
- package/user-documentation/solverfunctions/median.md +79 -0
- package/user-documentation/solverfunctions/min.md +73 -0
- package/user-documentation/solverfunctions/mode.md +66 -0
- package/user-documentation/solverfunctions/objectkeystoarray.md +66 -0
- package/user-documentation/solverfunctions/objectvaluessortbyexternalobjectarray.md +65 -0
- package/user-documentation/solverfunctions/objectvaluestoarray.md +67 -0
- package/user-documentation/solverfunctions/percent.md +75 -0
- package/user-documentation/solverfunctions/pi.md +77 -0
- package/user-documentation/solverfunctions/polynomialregression.md +69 -0
- package/user-documentation/solverfunctions/predict.md +71 -0
- package/user-documentation/solverfunctions/rad.md +85 -0
- package/user-documentation/solverfunctions/randomfloat.md +63 -0
- package/user-documentation/solverfunctions/randomfloatbetween.md +72 -0
- package/user-documentation/solverfunctions/randomfloatupto.md +65 -0
- package/user-documentation/solverfunctions/randominteger.md +56 -0
- package/user-documentation/solverfunctions/randomintegerbetween.md +72 -0
- package/user-documentation/solverfunctions/randomintegerupto.md +64 -0
- package/user-documentation/solverfunctions/refreshtabularsection.md +57 -0
- package/user-documentation/solverfunctions/resolvehtmlentities.md +64 -0
- package/user-documentation/solverfunctions/round.md +111 -0
- package/user-documentation/solverfunctions/runsolvers.md +49 -0
- package/user-documentation/solverfunctions/setconcatenate.md +64 -0
- package/user-documentation/solverfunctions/setgroupvisibility.md +60 -0
- package/user-documentation/solverfunctions/setsectionvisibility.md +59 -0
- package/user-documentation/solverfunctions/setsolverordinalenabled.md +59 -0
- package/user-documentation/solverfunctions/settabularrowlength.md +57 -0
- package/user-documentation/solverfunctions/setvalue.md +65 -0
- package/user-documentation/solverfunctions/showsections.md +58 -0
- package/user-documentation/solverfunctions/sin.md +83 -0
- package/user-documentation/solverfunctions/slice.md +80 -0
- package/user-documentation/solverfunctions/smallestinset.md +63 -0
- package/user-documentation/solverfunctions/sortarray.md +58 -0
- package/user-documentation/solverfunctions/sorthistogram.md +70 -0
- package/user-documentation/solverfunctions/sorthistogrambykeys.md +69 -0
- package/user-documentation/solverfunctions/sortset.md +75 -0
- package/user-documentation/solverfunctions/sqrt.md +85 -0
- package/user-documentation/solverfunctions/stdev.md +81 -0
- package/user-documentation/solverfunctions/stdeva.md +58 -0
- package/user-documentation/solverfunctions/stdevp.md +83 -0
- package/user-documentation/solverfunctions/stringcountsegments.md +66 -0
- package/user-documentation/solverfunctions/stringgetsegments.md +74 -0
- package/user-documentation/solverfunctions/subtractingsummation.md +66 -0
- package/user-documentation/solverfunctions/sum.md +78 -0
- package/user-documentation/solverfunctions/tan.md +78 -0
- package/user-documentation/solverfunctions/tofixed.md +75 -0
- package/user-documentation/solverfunctions/unionarrays.md +59 -0
- package/user-documentation/solverfunctions/uniquearray.md +58 -0
- package/user-documentation/solverfunctions/var.md +67 -0
- package/user-documentation/solverfunctions/vara.md +58 -0
- package/user-documentation/solverfunctions/varp.md +66 -0
- package/user-documentation/solverfunctions/when.md +98 -0
|
@@ -0,0 +1,730 @@
|
|
|
1
|
+
const libPictProvider = require('pict-provider');
|
|
2
|
+
|
|
3
|
+
class FormEditorRendering extends libPictProvider
|
|
4
|
+
{
|
|
5
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
6
|
+
{
|
|
7
|
+
super(pFable, pOptions, pServiceHash);
|
|
8
|
+
|
|
9
|
+
this.serviceType = 'PictProvider';
|
|
10
|
+
|
|
11
|
+
// Back-reference to the parent FormEditor view (set after construction)
|
|
12
|
+
this._ParentFormEditor = null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
_renderTabShell()
|
|
16
|
+
{
|
|
17
|
+
let tmpParent = this._ParentFormEditor;
|
|
18
|
+
let tmpHash = tmpParent.Hash;
|
|
19
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
20
|
+
|
|
21
|
+
let tmpHTML = '';
|
|
22
|
+
|
|
23
|
+
// Tab bar
|
|
24
|
+
tmpHTML += '<div class="pict-fe-tabbar">';
|
|
25
|
+
tmpHTML += `<button class="pict-fe-tab pict-fe-tab-active" id="FormEditor-Tab-Visual-${tmpHash}" onclick="${tmpViewRef}.switchTab('visual')">Visual Editor</button>`;
|
|
26
|
+
tmpHTML += `<button class="pict-fe-tab" id="FormEditor-Tab-SolverEditor-${tmpHash}" onclick="${tmpViewRef}.switchTab('solvereditor')">Solver Editor</button>`;
|
|
27
|
+
tmpHTML += `<button class="pict-fe-tab" id="FormEditor-Tab-Solvers-${tmpHash}" onclick="${tmpViewRef}.switchTab('solvers')">Solvers</button>`;
|
|
28
|
+
tmpHTML += `<button class="pict-fe-tab" id="FormEditor-Tab-ListData-${tmpHash}" onclick="${tmpViewRef}.switchTab('listdata')">List Data</button>`;
|
|
29
|
+
tmpHTML += `<button class="pict-fe-tab" id="FormEditor-Tab-EntityData-${tmpHash}" onclick="${tmpViewRef}.switchTab('entitydata')">Providers</button>`;
|
|
30
|
+
tmpHTML += `<button class="pict-fe-tab" id="FormEditor-Tab-ObjectEditor-${tmpHash}" onclick="${tmpViewRef}.switchTab('objecteditor')">Object Editor</button>`;
|
|
31
|
+
tmpHTML += `<button class="pict-fe-tab" id="FormEditor-Tab-JSON-${tmpHash}" onclick="${tmpViewRef}.switchTab('json')">JSON</button>`;
|
|
32
|
+
tmpHTML += `<button class="pict-fe-tab" id="FormEditor-Tab-Preview-${tmpHash}" onclick="${tmpViewRef}.switchTab('preview')">Preview</button>`;
|
|
33
|
+
tmpHTML += '</div>';
|
|
34
|
+
|
|
35
|
+
// Editor layout: tab content panels + resize handle + properties panel
|
|
36
|
+
tmpHTML += '<div class="pict-fe-editor-layout">';
|
|
37
|
+
|
|
38
|
+
// Tab content panels (stacked, only one active at a time)
|
|
39
|
+
tmpHTML += '<div class="pict-fe-editor-content">';
|
|
40
|
+
|
|
41
|
+
// Visual editor panel
|
|
42
|
+
tmpHTML += `<div class="pict-fe-tabcontent pict-fe-tabcontent-active" id="FormEditor-Panel-Visual-${tmpHash}"></div>`;
|
|
43
|
+
|
|
44
|
+
// Solver editor tab panel
|
|
45
|
+
tmpHTML += `<div class="pict-fe-tabcontent" id="FormEditor-Panel-SolverEditor-${tmpHash}">`;
|
|
46
|
+
tmpHTML += `<div id="FormEditor-SolverEditorTab-Container-${tmpHash}"></div>`;
|
|
47
|
+
tmpHTML += '</div>';
|
|
48
|
+
|
|
49
|
+
// Solvers tab panel
|
|
50
|
+
tmpHTML += `<div class="pict-fe-tabcontent" id="FormEditor-Panel-Solvers-${tmpHash}">`;
|
|
51
|
+
tmpHTML += `<div id="FormEditor-SolversTab-Container-${tmpHash}"></div>`;
|
|
52
|
+
tmpHTML += '</div>';
|
|
53
|
+
|
|
54
|
+
// List Data tab panel
|
|
55
|
+
tmpHTML += `<div class="pict-fe-tabcontent" id="FormEditor-Panel-ListData-${tmpHash}">`;
|
|
56
|
+
tmpHTML += `<div id="FormEditor-ListDataTab-Container-${tmpHash}"></div>`;
|
|
57
|
+
tmpHTML += '</div>';
|
|
58
|
+
|
|
59
|
+
// Entity Data tab panel
|
|
60
|
+
tmpHTML += `<div class="pict-fe-tabcontent" id="FormEditor-Panel-EntityData-${tmpHash}">`;
|
|
61
|
+
tmpHTML += `<div id="FormEditor-EntityDataTab-Container-${tmpHash}"></div>`;
|
|
62
|
+
tmpHTML += '</div>';
|
|
63
|
+
|
|
64
|
+
// Object editor panel
|
|
65
|
+
tmpHTML += `<div class="pict-fe-tabcontent" id="FormEditor-Panel-ObjectEditor-${tmpHash}">`;
|
|
66
|
+
tmpHTML += `<div id="FormEditor-ObjectEditor-Container-${tmpHash}"></div>`;
|
|
67
|
+
tmpHTML += '</div>';
|
|
68
|
+
|
|
69
|
+
// JSON panel
|
|
70
|
+
tmpHTML += `<div class="pict-fe-tabcontent" id="FormEditor-Panel-JSON-${tmpHash}">`;
|
|
71
|
+
tmpHTML += `<div id="FormEditor-CodeEditor-Container-${tmpHash}"></div>`;
|
|
72
|
+
tmpHTML += '</div>';
|
|
73
|
+
|
|
74
|
+
// Preview panel
|
|
75
|
+
tmpHTML += `<div class="pict-fe-tabcontent" id="FormEditor-Panel-Preview-${tmpHash}">`;
|
|
76
|
+
tmpHTML += `<div id="FormEditor-PreviewTab-Container-${tmpHash}"></div>`;
|
|
77
|
+
tmpHTML += '</div>';
|
|
78
|
+
|
|
79
|
+
tmpHTML += '</div>'; // pict-fe-editor-content
|
|
80
|
+
|
|
81
|
+
// Resize handle / collapse toggle (double-click to toggle)
|
|
82
|
+
tmpHTML += `<div class="pict-fe-panel-toggle" onmousedown="${tmpViewRef}._UtilitiesProvider.onPanelResizeStart(event)" ondblclick="${tmpViewRef}._UtilitiesProvider.togglePropertiesPanel()">`;
|
|
83
|
+
tmpHTML += '<div class="pict-fe-panel-toggle-grip"></div>';
|
|
84
|
+
tmpHTML += '</div>';
|
|
85
|
+
|
|
86
|
+
// Properties panel container
|
|
87
|
+
let tmpPanelOpenClass = tmpParent._PanelCollapsed ? '' : ' pict-fe-properties-panel-open';
|
|
88
|
+
let tmpPanelStyle = tmpParent._PanelCollapsed ? '' : ` style="width: ${tmpParent._PanelWidth}px;"`;
|
|
89
|
+
tmpHTML += `<div class="pict-fe-properties-panel${tmpPanelOpenClass}"${tmpPanelStyle} id="FormEditor-PropertiesPanel-${tmpHash}"></div>`;
|
|
90
|
+
|
|
91
|
+
tmpHTML += '</div>'; // pict-fe-editor-layout
|
|
92
|
+
|
|
93
|
+
this.pict.ContentAssignment.assignContent(`#FormEditor-Wrap-${tmpHash}`, tmpHTML);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
renderVisualEditor()
|
|
97
|
+
{
|
|
98
|
+
let tmpParent = this._ParentFormEditor;
|
|
99
|
+
let tmpManifest = tmpParent._resolveManifestData();
|
|
100
|
+
if (!tmpManifest)
|
|
101
|
+
{
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Ensure Rows/Inputs arrays are populated from Descriptors
|
|
106
|
+
tmpParent._ManifestOpsProvider._reconcileManifestStructure();
|
|
107
|
+
|
|
108
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
109
|
+
let tmpHTML = '';
|
|
110
|
+
|
|
111
|
+
// Header with toolbar buttons
|
|
112
|
+
tmpHTML += '<div class="pict-fe-visual-header">';
|
|
113
|
+
tmpHTML += '<h3>Form Sections</h3>';
|
|
114
|
+
tmpHTML += '<div style="display:flex;gap:6px;align-items:center;">';
|
|
115
|
+
|
|
116
|
+
// Drag-and-drop toggle
|
|
117
|
+
let tmpDragActive = tmpParent._DragAndDropEnabled;
|
|
118
|
+
let tmpDragClass = tmpDragActive ? 'pict-fe-btn pict-fe-btn-sm pict-fe-btn-primary' : 'pict-fe-btn pict-fe-btn-sm';
|
|
119
|
+
tmpHTML += `<button class="${tmpDragClass}" onclick="${tmpViewRef}._DragDropProvider.setDragAndDropEnabled(${!tmpDragActive})" title="${tmpDragActive ? 'Disable' : 'Enable'} drag & drop">${tmpParent._IconographyProvider.getIcon('Action', 'DragHandle', 12)} ${tmpDragActive ? 'Drag On' : 'Drag Off'}</button>`;
|
|
120
|
+
|
|
121
|
+
// Input display mode toggle (name vs hash)
|
|
122
|
+
let tmpShowHash = (tmpParent._InputDisplayMode === 'hash');
|
|
123
|
+
let tmpHashClass = tmpShowHash ? 'pict-fe-btn pict-fe-btn-sm pict-fe-btn-primary' : 'pict-fe-btn pict-fe-btn-sm';
|
|
124
|
+
let tmpNextMode = tmpShowHash ? 'name' : 'hash';
|
|
125
|
+
tmpHTML += `<button class="${tmpHashClass}" onclick="${tmpViewRef}._UtilitiesProvider.setInputDisplayMode('${tmpNextMode}')" title="${tmpShowHash ? 'Show input names' : 'Show input hashes'}">${tmpShowHash ? 'Hashes' : 'Names'}</button>`;
|
|
126
|
+
|
|
127
|
+
// Add Section button
|
|
128
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-primary" onclick="${tmpViewRef}._ManifestOpsProvider.addSection()"><span class="pict-fe-icon pict-fe-icon-add">${tmpParent._IconographyProvider.getIcon('Action', 'Add', 12)}</span> Add Section</button>`;
|
|
129
|
+
tmpHTML += '</div>';
|
|
130
|
+
tmpHTML += '</div>';
|
|
131
|
+
|
|
132
|
+
let tmpSections = tmpManifest.Sections;
|
|
133
|
+
if (!tmpSections || !Array.isArray(tmpSections) || tmpSections.length === 0)
|
|
134
|
+
{
|
|
135
|
+
tmpHTML += '<div class="pict-fe-empty">No sections defined. Click "Add Section" to create one.</div>';
|
|
136
|
+
}
|
|
137
|
+
else
|
|
138
|
+
{
|
|
139
|
+
tmpHTML += '<div class="pict-fe-sections-list">';
|
|
140
|
+
for (let i = 0; i < tmpSections.length; i++)
|
|
141
|
+
{
|
|
142
|
+
tmpHTML += this._renderSectionCard(tmpSections[i], i);
|
|
143
|
+
}
|
|
144
|
+
tmpHTML += '</div>';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Preserve the main content scroll position across the DOM replacement
|
|
148
|
+
let tmpPreviousMainScroll = 0;
|
|
149
|
+
if (typeof document !== 'undefined')
|
|
150
|
+
{
|
|
151
|
+
let tmpVisualPanel = document.getElementById(`FormEditor-Panel-Visual-${tmpParent.Hash}`);
|
|
152
|
+
if (tmpVisualPanel)
|
|
153
|
+
{
|
|
154
|
+
tmpPreviousMainScroll = tmpVisualPanel.scrollTop;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
this.pict.ContentAssignment.assignContent(`#FormEditor-Panel-Visual-${tmpParent.Hash}`, tmpHTML);
|
|
159
|
+
|
|
160
|
+
// Restore the main content scroll position after DOM replacement
|
|
161
|
+
if (typeof document !== 'undefined' && tmpPreviousMainScroll > 0)
|
|
162
|
+
{
|
|
163
|
+
let tmpVisualPanel = document.getElementById(`FormEditor-Panel-Visual-${tmpParent.Hash}`);
|
|
164
|
+
if (tmpVisualPanel)
|
|
165
|
+
{
|
|
166
|
+
tmpVisualPanel.scrollTop = tmpPreviousMainScroll;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Always render the properties panel content
|
|
171
|
+
if (tmpParent._PropertiesPanelView)
|
|
172
|
+
{
|
|
173
|
+
tmpParent._PropertiesPanelView.renderPanel();
|
|
174
|
+
|
|
175
|
+
// Keep the top-level data tab content in sync
|
|
176
|
+
if (tmpParent._ActiveTab === 'listdata')
|
|
177
|
+
{
|
|
178
|
+
tmpParent._PropertiesPanelView.renderListDataTabPanel();
|
|
179
|
+
}
|
|
180
|
+
else if (tmpParent._ActiveTab === 'entitydata')
|
|
181
|
+
{
|
|
182
|
+
tmpParent._PropertiesPanelView.renderEntityDataTabPanel();
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
_renderSectionCard(pSection, pIndex)
|
|
188
|
+
{
|
|
189
|
+
let tmpParent = this._ParentFormEditor;
|
|
190
|
+
let tmpHash = tmpParent.Hash;
|
|
191
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
192
|
+
|
|
193
|
+
let tmpName = pSection.Name || 'Untitled Section';
|
|
194
|
+
let tmpSectionHash = pSection.Hash || '';
|
|
195
|
+
|
|
196
|
+
let tmpHTML = '';
|
|
197
|
+
tmpHTML += `<div class="pict-fe-section-card"${tmpParent._DragDropProvider._buildDragAttributes('section', [pIndex])}>`;
|
|
198
|
+
|
|
199
|
+
// Section header
|
|
200
|
+
tmpHTML += `<div class="pict-fe-section-header" style="cursor:pointer;" onclick="${tmpViewRef}._UtilitiesProvider.selectSection(${pIndex})">`;
|
|
201
|
+
tmpHTML += '<div class="pict-fe-section-header-labels">';
|
|
202
|
+
tmpHTML += tmpParent._DragDropProvider._buildDragHandleHTML(14);
|
|
203
|
+
tmpHTML += `<span class="pict-fe-icon pict-fe-icon-section">${tmpParent._IconographyProvider.getIcon('Section', 'Default', 14)}</span>`;
|
|
204
|
+
tmpHTML += `<span class="pict-fe-section-title" id="FormEditor-SectionName-${tmpHash}-${pIndex}" title="Section Name: ${tmpParent._UtilitiesProvider._escapeAttr(tmpName)}" onclick="event.stopPropagation(); ${tmpViewRef}._InlineEditingView.beginEditProperty('Section', ${pIndex}, -1, 'Name')">${tmpParent._UtilitiesProvider._escapeHTML(tmpName)}</span>`;
|
|
205
|
+
tmpHTML += `<span class="pict-fe-section-hash" id="FormEditor-SectionHash-${tmpHash}-${pIndex}" title="Section Hash: ${tmpParent._UtilitiesProvider._escapeAttr(tmpSectionHash)}" onclick="event.stopPropagation(); ${tmpViewRef}._InlineEditingView.beginEditProperty('Section', ${pIndex}, -1, 'Hash')">${tmpParent._UtilitiesProvider._escapeHTML(tmpSectionHash)}</span>`;
|
|
206
|
+
tmpHTML += '</div>';
|
|
207
|
+
tmpHTML += '<div class="pict-fe-section-actions" onclick="event.stopPropagation()">';
|
|
208
|
+
if (pIndex > 0)
|
|
209
|
+
{
|
|
210
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm" onclick="${tmpViewRef}._ManifestOpsProvider.moveSectionUp(${pIndex})" title="Move up">\u25B2</button>`;
|
|
211
|
+
}
|
|
212
|
+
if (pIndex < (tmpParent._resolveManifestData().Sections.length - 1))
|
|
213
|
+
{
|
|
214
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm" onclick="${tmpViewRef}._ManifestOpsProvider.moveSectionDown(${pIndex})" title="Move down">\u25BC</button>`;
|
|
215
|
+
}
|
|
216
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm pict-fe-btn-danger" onclick="${tmpViewRef}._ManifestOpsProvider.removeSection(${pIndex})" title="Remove section">\u00D7</button>`;
|
|
217
|
+
tmpHTML += '</div>';
|
|
218
|
+
tmpHTML += '</div>';
|
|
219
|
+
|
|
220
|
+
// Section body
|
|
221
|
+
tmpHTML += '<div class="pict-fe-section-body">';
|
|
222
|
+
|
|
223
|
+
// Groups
|
|
224
|
+
tmpHTML += '<div class="pict-fe-groups-header">';
|
|
225
|
+
tmpHTML += '<h4>Groups</h4>';
|
|
226
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm" onclick="${tmpViewRef}._ManifestOpsProvider.addGroup(${pIndex})"><span class="pict-fe-icon pict-fe-icon-add">${tmpParent._IconographyProvider.getIcon('Action', 'Add', 10)}</span> Add Group</button>`;
|
|
227
|
+
tmpHTML += '</div>';
|
|
228
|
+
|
|
229
|
+
let tmpGroups = pSection.Groups;
|
|
230
|
+
if (!tmpGroups || !Array.isArray(tmpGroups) || tmpGroups.length === 0)
|
|
231
|
+
{
|
|
232
|
+
tmpHTML += `<div class="pict-fe-empty"${tmpParent._DragDropProvider._buildContainerDropAttributes('group', [pIndex])}>No groups in this section.</div>`;
|
|
233
|
+
}
|
|
234
|
+
else
|
|
235
|
+
{
|
|
236
|
+
tmpHTML += `<div class="pict-fe-groups-list"${tmpParent._DragDropProvider._buildContainerDropAttributes('group', [pIndex])}>`;
|
|
237
|
+
for (let j = 0; j < tmpGroups.length; j++)
|
|
238
|
+
{
|
|
239
|
+
tmpHTML += this._renderGroupCard(tmpGroups[j], pIndex, j);
|
|
240
|
+
}
|
|
241
|
+
tmpHTML += '</div>';
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
tmpHTML += '</div>'; // section-body
|
|
245
|
+
tmpHTML += '</div>'; // section-card
|
|
246
|
+
|
|
247
|
+
return tmpHTML;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
_renderGroupCard(pGroup, pSectionIndex, pGroupIndex)
|
|
251
|
+
{
|
|
252
|
+
let tmpParent = this._ParentFormEditor;
|
|
253
|
+
let tmpHash = tmpParent.Hash;
|
|
254
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
255
|
+
let tmpSection = tmpParent._resolveManifestData().Sections[pSectionIndex];
|
|
256
|
+
let tmpGroupCount = tmpSection.Groups ? tmpSection.Groups.length : 0;
|
|
257
|
+
|
|
258
|
+
let tmpGroupName = pGroup.Name || 'Untitled Group';
|
|
259
|
+
let tmpGroupHash = pGroup.Hash || '';
|
|
260
|
+
|
|
261
|
+
let tmpHTML = '';
|
|
262
|
+
tmpHTML += `<div class="pict-fe-group-card"${tmpParent._DragDropProvider._buildDragAttributes('group', [pSectionIndex, pGroupIndex])}>`;
|
|
263
|
+
|
|
264
|
+
// Group header
|
|
265
|
+
tmpHTML += `<div class="pict-fe-group-header" style="cursor:pointer;" onclick="${tmpViewRef}._UtilitiesProvider.selectGroup(${pSectionIndex}, ${pGroupIndex})">`;
|
|
266
|
+
tmpHTML += '<div class="pict-fe-group-header-labels">';
|
|
267
|
+
tmpHTML += tmpParent._DragDropProvider._buildDragHandleHTML(12);
|
|
268
|
+
tmpHTML += `<span class="pict-fe-icon pict-fe-icon-group">${tmpParent._IconographyProvider.getIcon('Group', 'Default', 14)}</span>`;
|
|
269
|
+
tmpHTML += `<span class="pict-fe-group-title" id="FormEditor-GroupName-${tmpHash}-${pSectionIndex}-${pGroupIndex}" title="Group Name: ${tmpParent._UtilitiesProvider._escapeAttr(tmpGroupName)}" onclick="event.stopPropagation(); ${tmpViewRef}._InlineEditingView.beginEditProperty('Group', ${pSectionIndex}, ${pGroupIndex}, 'Name')">${tmpParent._UtilitiesProvider._escapeHTML(tmpGroupName)}</span>`;
|
|
270
|
+
tmpHTML += `<span class="pict-fe-group-hash" id="FormEditor-GroupHash-${tmpHash}-${pSectionIndex}-${pGroupIndex}" title="Group Hash: ${tmpParent._UtilitiesProvider._escapeAttr(tmpGroupHash)}" onclick="event.stopPropagation(); ${tmpViewRef}._InlineEditingView.beginEditProperty('Group', ${pSectionIndex}, ${pGroupIndex}, 'Hash')">${tmpParent._UtilitiesProvider._escapeHTML(tmpGroupHash)}</span>`;
|
|
271
|
+
let tmpCurrentLayout = pGroup.Layout || 'Record';
|
|
272
|
+
tmpHTML += `<span class="pict-fe-group-layout" id="FormEditor-GroupLayout-${tmpHash}-${pSectionIndex}-${pGroupIndex}" title="Group Layout: ${tmpParent._UtilitiesProvider._escapeAttr(tmpCurrentLayout)}" onclick="event.stopPropagation(); ${tmpViewRef}._InlineEditingView.beginEditProperty('Group', ${pSectionIndex}, ${pGroupIndex}, 'Layout')">${tmpParent._UtilitiesProvider._escapeHTML(tmpCurrentLayout)}</span>`;
|
|
273
|
+
tmpHTML += '</div>';
|
|
274
|
+
tmpHTML += '<div class="pict-fe-group-actions" onclick="event.stopPropagation()">';
|
|
275
|
+
if (pGroupIndex > 0)
|
|
276
|
+
{
|
|
277
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm" onclick="${tmpViewRef}._ManifestOpsProvider.moveGroupUp(${pSectionIndex}, ${pGroupIndex})" title="Move up">\u25B2</button>`;
|
|
278
|
+
}
|
|
279
|
+
if (pGroupIndex < (tmpGroupCount - 1))
|
|
280
|
+
{
|
|
281
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm" onclick="${tmpViewRef}._ManifestOpsProvider.moveGroupDown(${pSectionIndex}, ${pGroupIndex})" title="Move down">\u25BC</button>`;
|
|
282
|
+
}
|
|
283
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm pict-fe-btn-danger" onclick="${tmpViewRef}._ManifestOpsProvider.removeGroup(${pSectionIndex}, ${pGroupIndex})" title="Remove group">\u00D7</button>`;
|
|
284
|
+
tmpHTML += '</div>';
|
|
285
|
+
tmpHTML += '</div>';
|
|
286
|
+
|
|
287
|
+
// Group body — render based on layout type
|
|
288
|
+
if (tmpCurrentLayout === 'Record')
|
|
289
|
+
{
|
|
290
|
+
tmpHTML += this._renderGroupBody(pGroup, pSectionIndex, pGroupIndex);
|
|
291
|
+
}
|
|
292
|
+
else if (tmpCurrentLayout === 'Tabular' || tmpCurrentLayout === 'RecordSet')
|
|
293
|
+
{
|
|
294
|
+
tmpHTML += this._renderSubmanifestGroupBody(pGroup, pSectionIndex, pGroupIndex);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
tmpHTML += '</div>'; // group-card
|
|
298
|
+
|
|
299
|
+
return tmpHTML;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
_renderGroupBody(pGroup, pSectionIndex, pGroupIndex)
|
|
303
|
+
{
|
|
304
|
+
let tmpParent = this._ParentFormEditor;
|
|
305
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
306
|
+
let tmpRows = pGroup.Rows;
|
|
307
|
+
|
|
308
|
+
let tmpHTML = '';
|
|
309
|
+
tmpHTML += `<div class="pict-fe-group-body"${tmpParent._DragDropProvider._buildContainerDropAttributes('row', [pSectionIndex, pGroupIndex])}>`;
|
|
310
|
+
|
|
311
|
+
if (Array.isArray(tmpRows) && tmpRows.length > 0)
|
|
312
|
+
{
|
|
313
|
+
for (let k = 0; k < tmpRows.length; k++)
|
|
314
|
+
{
|
|
315
|
+
tmpHTML += this._renderRow(tmpRows[k], pSectionIndex, pGroupIndex, k);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm pict-fe-add-row" onclick="${tmpViewRef}._ManifestOpsProvider.addRow(${pSectionIndex}, ${pGroupIndex})"><span class="pict-fe-icon pict-fe-icon-add">${tmpParent._IconographyProvider.getIcon('Action', 'Add', 10)}</span> Add Row</button>`;
|
|
320
|
+
tmpHTML += '</div>';
|
|
321
|
+
|
|
322
|
+
return tmpHTML;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/* ---------- Submanifest (Tabular / RecordSet) body rendering ---------- */
|
|
326
|
+
|
|
327
|
+
_renderSubmanifestGroupBody(pGroup, pSectionIndex, pGroupIndex)
|
|
328
|
+
{
|
|
329
|
+
let tmpParent = this._ParentFormEditor;
|
|
330
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
331
|
+
let tmpHash = tmpParent.Hash;
|
|
332
|
+
|
|
333
|
+
let tmpRecordSetAddress = pGroup.RecordSetAddress || '';
|
|
334
|
+
let tmpRecordManifestName = pGroup.RecordManifest || '';
|
|
335
|
+
let tmpCurrentLayout = pGroup.Layout || 'Tabular';
|
|
336
|
+
|
|
337
|
+
let tmpHTML = '';
|
|
338
|
+
tmpHTML += '<div class="pict-fe-group-body pict-fe-tabular-body">';
|
|
339
|
+
|
|
340
|
+
// Tabular configuration fields
|
|
341
|
+
tmpHTML += '<div class="pict-fe-tabular-fields">';
|
|
342
|
+
|
|
343
|
+
// RecordSetAddress field
|
|
344
|
+
tmpHTML += '<div class="pict-fe-tabular-field">';
|
|
345
|
+
tmpHTML += '<div class="pict-fe-field-label">RecordSetAddress</div>';
|
|
346
|
+
tmpHTML += `<input class="pict-fe-field-input" type="text" value="${tmpParent._UtilitiesProvider._escapeAttr(tmpRecordSetAddress)}" onchange="${tmpViewRef}._ManifestOpsProvider.updateGroupProperty(${pSectionIndex}, ${pGroupIndex}, 'RecordSetAddress', this.value); ${tmpViewRef}.renderVisualEditor();" placeholder="e.g. FruitData.FruityVice" />`;
|
|
347
|
+
tmpHTML += '</div>';
|
|
348
|
+
|
|
349
|
+
// RecordManifest selector
|
|
350
|
+
tmpHTML += '<div class="pict-fe-tabular-field">';
|
|
351
|
+
tmpHTML += '<div class="pict-fe-field-label">RecordManifest</div>';
|
|
352
|
+
tmpHTML += this._renderReferenceManifestSelector(pSectionIndex, pGroupIndex, tmpRecordManifestName);
|
|
353
|
+
tmpHTML += '</div>';
|
|
354
|
+
|
|
355
|
+
tmpHTML += '</div>'; // tabular-fields
|
|
356
|
+
|
|
357
|
+
// Column editing area (only if a ReferenceManifest is bound)
|
|
358
|
+
if (tmpRecordManifestName)
|
|
359
|
+
{
|
|
360
|
+
let tmpRefManifest = tmpParent._ManifestOpsProvider._resolveReferenceManifest(tmpRecordManifestName);
|
|
361
|
+
if (tmpRefManifest)
|
|
362
|
+
{
|
|
363
|
+
let tmpDescriptors = tmpRefManifest.Descriptors;
|
|
364
|
+
let tmpHasColumns = tmpDescriptors && typeof tmpDescriptors === 'object' && Object.keys(tmpDescriptors).length > 0;
|
|
365
|
+
|
|
366
|
+
if (tmpCurrentLayout === 'RecordSet')
|
|
367
|
+
{
|
|
368
|
+
tmpHTML += this._renderSubmanifestRowsView(tmpRefManifest, pSectionIndex, pGroupIndex);
|
|
369
|
+
}
|
|
370
|
+
else
|
|
371
|
+
{
|
|
372
|
+
// Tabular: flat column list
|
|
373
|
+
tmpHTML += this._renderSubmanifestColumnList(tmpRefManifest, pSectionIndex, pGroupIndex);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
else
|
|
377
|
+
{
|
|
378
|
+
tmpHTML += `<div class="pict-fe-empty">ReferenceManifest "${tmpParent._UtilitiesProvider._escapeHTML(tmpRecordManifestName)}" not found. Create it or select an existing one.</div>`;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
tmpHTML += '</div>'; // tabular-body
|
|
383
|
+
|
|
384
|
+
return tmpHTML;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
_renderReferenceManifestSelector(pSectionIndex, pGroupIndex, pCurrentManifestName)
|
|
388
|
+
{
|
|
389
|
+
let tmpParent = this._ParentFormEditor;
|
|
390
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
391
|
+
let tmpNames = tmpParent._ManifestOpsProvider.getReferenceManifestNames();
|
|
392
|
+
|
|
393
|
+
let tmpHTML = '';
|
|
394
|
+
tmpHTML += '<div class="pict-fe-refmanifest-selector">';
|
|
395
|
+
|
|
396
|
+
// Select dropdown for existing ReferenceManifests
|
|
397
|
+
tmpHTML += `<select class="pict-fe-field-select" onchange="${tmpViewRef}._ManifestOpsProvider.bindReferenceManifest(${pSectionIndex}, ${pGroupIndex}, this.value)">`;
|
|
398
|
+
tmpHTML += '<option value="">-- Select or Create --</option>';
|
|
399
|
+
for (let i = 0; i < tmpNames.length; i++)
|
|
400
|
+
{
|
|
401
|
+
let tmpSelected = (tmpNames[i] === pCurrentManifestName) ? ' selected' : '';
|
|
402
|
+
tmpHTML += `<option value="${tmpParent._UtilitiesProvider._escapeAttr(tmpNames[i])}"${tmpSelected}>${tmpParent._UtilitiesProvider._escapeHTML(tmpNames[i])}</option>`;
|
|
403
|
+
}
|
|
404
|
+
tmpHTML += '</select>';
|
|
405
|
+
|
|
406
|
+
// Create New button
|
|
407
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm" onclick="${tmpViewRef}._ManifestOpsProvider.createAndBindReferenceManifest(${pSectionIndex}, ${pGroupIndex})" title="Create a new ReferenceManifest"><span class="pict-fe-icon pict-fe-icon-add">${tmpParent._IconographyProvider.getIcon('Action', 'Add', 10)}</span> New</button>`;
|
|
408
|
+
|
|
409
|
+
// Unbind button (only show if currently bound)
|
|
410
|
+
if (pCurrentManifestName)
|
|
411
|
+
{
|
|
412
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm pict-fe-btn-danger" onclick="${tmpViewRef}._ManifestOpsProvider.unbindReferenceManifest(${pSectionIndex}, ${pGroupIndex})" title="Unbind ReferenceManifest">\u00D7</button>`;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
tmpHTML += '</div>';
|
|
416
|
+
|
|
417
|
+
return tmpHTML;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
_renderSubmanifestColumnList(pRefManifest, pSectionIndex, pGroupIndex)
|
|
421
|
+
{
|
|
422
|
+
let tmpParent = this._ParentFormEditor;
|
|
423
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
424
|
+
|
|
425
|
+
let tmpHTML = '';
|
|
426
|
+
|
|
427
|
+
// Header
|
|
428
|
+
tmpHTML += '<div class="pict-fe-tabular-columns-header">';
|
|
429
|
+
tmpHTML += `<span class="pict-fe-row-label">Columns</span>`;
|
|
430
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm pict-fe-add-input" onclick="${tmpViewRef}._ManifestOpsProvider.addSubmanifestColumn(${pSectionIndex}, ${pGroupIndex})"><span class="pict-fe-icon pict-fe-icon-add">${tmpParent._IconographyProvider.getIcon('Action', 'Add', 10)}</span> Add Column</button>`;
|
|
431
|
+
tmpHTML += '</div>';
|
|
432
|
+
|
|
433
|
+
let tmpDescriptors = pRefManifest.Descriptors;
|
|
434
|
+
if (tmpDescriptors && typeof tmpDescriptors === 'object')
|
|
435
|
+
{
|
|
436
|
+
let tmpColumnKeys = Object.keys(tmpDescriptors);
|
|
437
|
+
if (tmpColumnKeys.length > 0)
|
|
438
|
+
{
|
|
439
|
+
tmpHTML += '<div class="pict-fe-tabular-columns-list">';
|
|
440
|
+
for (let c = 0; c < tmpColumnKeys.length; c++)
|
|
441
|
+
{
|
|
442
|
+
tmpHTML += this._renderSubmanifestColumn(
|
|
443
|
+
tmpColumnKeys[c],
|
|
444
|
+
tmpDescriptors[tmpColumnKeys[c]],
|
|
445
|
+
pSectionIndex,
|
|
446
|
+
pGroupIndex,
|
|
447
|
+
c,
|
|
448
|
+
tmpColumnKeys.length
|
|
449
|
+
);
|
|
450
|
+
}
|
|
451
|
+
tmpHTML += '</div>';
|
|
452
|
+
}
|
|
453
|
+
else
|
|
454
|
+
{
|
|
455
|
+
tmpHTML += '<div class="pict-fe-empty">No columns defined. Click "Add Column" to create one.</div>';
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return tmpHTML;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
_renderSubmanifestRowsView(pRefManifest, pSectionIndex, pGroupIndex)
|
|
463
|
+
{
|
|
464
|
+
let tmpParent = this._ParentFormEditor;
|
|
465
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
466
|
+
|
|
467
|
+
let tmpHTML = '';
|
|
468
|
+
|
|
469
|
+
let tmpRows = tmpParent._ManifestOpsProvider._getSubmanifestRows(pRefManifest);
|
|
470
|
+
|
|
471
|
+
if (tmpRows.length === 0)
|
|
472
|
+
{
|
|
473
|
+
// Header with Add Row button
|
|
474
|
+
tmpHTML += '<div class="pict-fe-tabular-columns-header">';
|
|
475
|
+
tmpHTML += `<span class="pict-fe-row-label">Rows & Columns</span>`;
|
|
476
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm pict-fe-add-row" onclick="${tmpViewRef}._ManifestOpsProvider.addSubmanifestRow(${pSectionIndex}, ${pGroupIndex})"><span class="pict-fe-icon pict-fe-icon-add">${tmpParent._IconographyProvider.getIcon('Action', 'Add', 10)}</span> Add Row</button>`;
|
|
477
|
+
tmpHTML += '</div>';
|
|
478
|
+
tmpHTML += '<div class="pict-fe-empty">No rows or columns defined. Click "Add Row" to create one.</div>';
|
|
479
|
+
}
|
|
480
|
+
else
|
|
481
|
+
{
|
|
482
|
+
// Header
|
|
483
|
+
tmpHTML += '<div class="pict-fe-tabular-columns-header">';
|
|
484
|
+
tmpHTML += `<span class="pict-fe-row-label">Rows & Columns</span>`;
|
|
485
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm pict-fe-add-row" onclick="${tmpViewRef}._ManifestOpsProvider.addSubmanifestRow(${pSectionIndex}, ${pGroupIndex})"><span class="pict-fe-icon pict-fe-icon-add">${tmpParent._IconographyProvider.getIcon('Action', 'Add', 10)}</span> Add Row</button>`;
|
|
486
|
+
tmpHTML += '</div>';
|
|
487
|
+
|
|
488
|
+
for (let r = 0; r < tmpRows.length; r++)
|
|
489
|
+
{
|
|
490
|
+
let tmpRowData = tmpRows[r];
|
|
491
|
+
let tmpRowNum = tmpRowData.Row;
|
|
492
|
+
let tmpColumns = tmpRowData.Columns;
|
|
493
|
+
|
|
494
|
+
tmpHTML += '<div class="pict-fe-row">';
|
|
495
|
+
|
|
496
|
+
// Row header
|
|
497
|
+
tmpHTML += '<div class="pict-fe-row-header">';
|
|
498
|
+
tmpHTML += `<span class="pict-fe-icon pict-fe-icon-row">${tmpParent._IconographyProvider.getIcon('Row', 'Default', 12)}</span>`;
|
|
499
|
+
tmpHTML += `<span class="pict-fe-row-label">Row ${tmpRowNum}</span>`;
|
|
500
|
+
tmpHTML += '</div>';
|
|
501
|
+
|
|
502
|
+
// Columns as input cards
|
|
503
|
+
tmpHTML += '<div class="pict-fe-row-inputs">';
|
|
504
|
+
for (let c = 0; c < tmpColumns.length; c++)
|
|
505
|
+
{
|
|
506
|
+
tmpHTML += this._renderSubmanifestColumn(
|
|
507
|
+
tmpColumns[c].Address,
|
|
508
|
+
tmpColumns[c].Descriptor,
|
|
509
|
+
pSectionIndex,
|
|
510
|
+
pGroupIndex,
|
|
511
|
+
c,
|
|
512
|
+
tmpColumns.length
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm pict-fe-add-input" onclick="${tmpViewRef}._ManifestOpsProvider.addSubmanifestColumn(${pSectionIndex}, ${pGroupIndex}, ${tmpRowNum})"><span class="pict-fe-icon pict-fe-icon-add">${tmpParent._IconographyProvider.getIcon('Action', 'Add', 10)}</span> Add Column</button>`;
|
|
516
|
+
tmpHTML += '</div>';
|
|
517
|
+
|
|
518
|
+
tmpHTML += '</div>'; // row
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
return tmpHTML;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
_renderSubmanifestColumn(pColumnAddress, pDescriptor, pSectionIndex, pGroupIndex, pColumnIndex, pColumnCount)
|
|
526
|
+
{
|
|
527
|
+
let tmpParent = this._ParentFormEditor;
|
|
528
|
+
let tmpHash = tmpParent.Hash;
|
|
529
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
530
|
+
|
|
531
|
+
let tmpName = pDescriptor ? (pDescriptor.Name || '') : '';
|
|
532
|
+
let tmpColumnHash = pDescriptor ? (pDescriptor.Hash || pColumnAddress) : pColumnAddress;
|
|
533
|
+
let tmpType = pDescriptor ? (pDescriptor.DataType || 'String') : 'String';
|
|
534
|
+
let tmpOrdinal = pColumnIndex + 1;
|
|
535
|
+
|
|
536
|
+
// Build tooltip
|
|
537
|
+
let tmpTooltipParts = [];
|
|
538
|
+
tmpTooltipParts.push('Address: ' + pColumnAddress);
|
|
539
|
+
tmpTooltipParts.push('Hash: ' + tmpColumnHash);
|
|
540
|
+
if (tmpName)
|
|
541
|
+
{
|
|
542
|
+
tmpTooltipParts.push('Name: ' + tmpName);
|
|
543
|
+
}
|
|
544
|
+
tmpTooltipParts.push('DataType: ' + tmpType);
|
|
545
|
+
let tmpTooltip = tmpTooltipParts.join(' ');
|
|
546
|
+
|
|
547
|
+
// Display text
|
|
548
|
+
let tmpDisplayText = '';
|
|
549
|
+
if (tmpParent._InputDisplayMode === 'hash')
|
|
550
|
+
{
|
|
551
|
+
tmpDisplayText = tmpColumnHash;
|
|
552
|
+
}
|
|
553
|
+
else
|
|
554
|
+
{
|
|
555
|
+
tmpDisplayText = tmpName || tmpColumnHash;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Selected state
|
|
559
|
+
let tmpIsSelected = false;
|
|
560
|
+
if (tmpParent._SelectedTabularColumn &&
|
|
561
|
+
tmpParent._SelectedTabularColumn.SectionIndex === pSectionIndex &&
|
|
562
|
+
tmpParent._SelectedTabularColumn.GroupIndex === pGroupIndex &&
|
|
563
|
+
tmpParent._SelectedTabularColumn.ColumnAddress === pColumnAddress)
|
|
564
|
+
{
|
|
565
|
+
tmpIsSelected = true;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
let tmpSelectedClass = tmpIsSelected ? ' pict-fe-input-selected' : '';
|
|
569
|
+
|
|
570
|
+
// DataType icon
|
|
571
|
+
let tmpDataTypeIconHTML = tmpParent._IconographyProvider.getDataTypeIcon(tmpType, 12);
|
|
572
|
+
if (!tmpDataTypeIconHTML)
|
|
573
|
+
{
|
|
574
|
+
tmpDataTypeIconHTML = tmpParent._IconographyProvider.getIcon('Input', 'Default', 12);
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Escape the column address for use in onclick
|
|
578
|
+
let tmpEscapedAddress = tmpParent._UtilitiesProvider._escapeAttr(pColumnAddress).replace(/'/g, "\\'");
|
|
579
|
+
|
|
580
|
+
let tmpHTML = '';
|
|
581
|
+
tmpHTML += `<div class="pict-fe-input${tmpSelectedClass}" id="FormEditor-SubCol-${tmpHash}-${pSectionIndex}-${pGroupIndex}-${pColumnIndex}" title="${tmpTooltip}" onclick="${tmpViewRef}._ManifestOpsProvider.selectSubmanifestColumn(${pSectionIndex}, ${pGroupIndex}, '${tmpEscapedAddress}')">`;
|
|
582
|
+
tmpHTML += `<span class="pict-fe-icon pict-fe-icon-datatype">${tmpDataTypeIconHTML}</span>`;
|
|
583
|
+
tmpHTML += `<span class="pict-fe-input-ordinal">${tmpOrdinal}</span>`;
|
|
584
|
+
tmpHTML += `<span class="pict-fe-input-name">${tmpParent._UtilitiesProvider._escapeHTML(tmpParent._UtilitiesProvider._truncateMiddle(tmpDisplayText, 20))}</span>`;
|
|
585
|
+
|
|
586
|
+
// Remove button
|
|
587
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm pict-fe-btn-danger pict-fe-input-remove" onclick="event.stopPropagation(); ${tmpViewRef}._ManifestOpsProvider.removeSubmanifestColumn(${pSectionIndex}, ${pGroupIndex}, '${tmpEscapedAddress}')" title="Remove column">\u00D7</button>`;
|
|
588
|
+
tmpHTML += '</div>';
|
|
589
|
+
|
|
590
|
+
return tmpHTML;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
_renderRow(pRow, pSectionIndex, pGroupIndex, pRowIndex)
|
|
594
|
+
{
|
|
595
|
+
let tmpParent = this._ParentFormEditor;
|
|
596
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
597
|
+
|
|
598
|
+
let tmpHTML = '';
|
|
599
|
+
tmpHTML += `<div class="pict-fe-row"${tmpParent._DragDropProvider._buildDragAttributes('row', [pSectionIndex, pGroupIndex, pRowIndex])}>`;
|
|
600
|
+
|
|
601
|
+
// Row header with index and actions
|
|
602
|
+
tmpHTML += '<div class="pict-fe-row-header">';
|
|
603
|
+
tmpHTML += tmpParent._DragDropProvider._buildDragHandleHTML(10);
|
|
604
|
+
tmpHTML += `<span class="pict-fe-icon pict-fe-icon-row">${tmpParent._IconographyProvider.getIcon('Row', 'Default', 12)}</span>`;
|
|
605
|
+
tmpHTML += `<span class="pict-fe-row-label">Row ${pRowIndex + 1}</span>`;
|
|
606
|
+
tmpHTML += '<div class="pict-fe-row-actions">';
|
|
607
|
+
|
|
608
|
+
let tmpManifest = tmpParent._resolveManifestData();
|
|
609
|
+
let tmpGroup = tmpManifest.Sections[pSectionIndex].Groups[pGroupIndex];
|
|
610
|
+
let tmpRowCount = tmpGroup.Rows ? tmpGroup.Rows.length : 0;
|
|
611
|
+
|
|
612
|
+
if (pRowIndex > 0)
|
|
613
|
+
{
|
|
614
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm" onclick="${tmpViewRef}._ManifestOpsProvider.moveRowUp(${pSectionIndex}, ${pGroupIndex}, ${pRowIndex})" title="Move row up">\u25B2</button>`;
|
|
615
|
+
}
|
|
616
|
+
if (pRowIndex < (tmpRowCount - 1))
|
|
617
|
+
{
|
|
618
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm" onclick="${tmpViewRef}._ManifestOpsProvider.moveRowDown(${pSectionIndex}, ${pGroupIndex}, ${pRowIndex})" title="Move row down">\u25BC</button>`;
|
|
619
|
+
}
|
|
620
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm pict-fe-btn-danger" onclick="${tmpViewRef}._ManifestOpsProvider.removeRow(${pSectionIndex}, ${pGroupIndex}, ${pRowIndex})" title="Remove row">\u00D7</button>`;
|
|
621
|
+
tmpHTML += '</div>';
|
|
622
|
+
tmpHTML += '</div>';
|
|
623
|
+
|
|
624
|
+
// Row inputs
|
|
625
|
+
tmpHTML += `<div class="pict-fe-row-inputs"${tmpParent._DragDropProvider._buildContainerDropAttributes('input', [pSectionIndex, pGroupIndex, pRowIndex])}>`;
|
|
626
|
+
let tmpInputs = pRow.Inputs;
|
|
627
|
+
if (Array.isArray(tmpInputs) && tmpInputs.length > 0)
|
|
628
|
+
{
|
|
629
|
+
for (let m = 0; m < tmpInputs.length; m++)
|
|
630
|
+
{
|
|
631
|
+
tmpHTML += this._renderInput(tmpInputs[m], pSectionIndex, pGroupIndex, pRowIndex, m);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm pict-fe-add-input" onclick="${tmpViewRef}._ManifestOpsProvider.addInput(${pSectionIndex}, ${pGroupIndex}, ${pRowIndex})"><span class="pict-fe-icon pict-fe-icon-add">${tmpParent._IconographyProvider.getIcon('Action', 'Add', 10)}</span> Add Input</button>`;
|
|
635
|
+
tmpHTML += '</div>';
|
|
636
|
+
|
|
637
|
+
tmpHTML += '</div>';
|
|
638
|
+
|
|
639
|
+
return tmpHTML;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
_renderInput(pInputAddress, pSectionIndex, pGroupIndex, pRowIndex, pInputIndex)
|
|
643
|
+
{
|
|
644
|
+
let tmpParent = this._ParentFormEditor;
|
|
645
|
+
let tmpHash = tmpParent.Hash;
|
|
646
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
647
|
+
let tmpManifest = tmpParent._resolveManifestData();
|
|
648
|
+
|
|
649
|
+
// Look up the Descriptor by address
|
|
650
|
+
let tmpDescriptor = null;
|
|
651
|
+
if (typeof pInputAddress === 'string' && tmpManifest && tmpManifest.Descriptors)
|
|
652
|
+
{
|
|
653
|
+
tmpDescriptor = tmpManifest.Descriptors[pInputAddress];
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
let tmpInputHash = tmpDescriptor ? (tmpDescriptor.Hash || pInputAddress) : (typeof pInputAddress === 'string' ? pInputAddress : 'input');
|
|
657
|
+
let tmpName = tmpDescriptor ? (tmpDescriptor.Name || '') : '';
|
|
658
|
+
let tmpType = tmpDescriptor ? (tmpDescriptor.DataType || 'String') : 'String';
|
|
659
|
+
let tmpInputType = (tmpDescriptor && tmpDescriptor.PictForm && tmpDescriptor.PictForm.InputType) ? tmpDescriptor.PictForm.InputType : '';
|
|
660
|
+
let tmpWidth = (tmpDescriptor && tmpDescriptor.PictForm && tmpDescriptor.PictForm.Width) ? tmpDescriptor.PictForm.Width : '';
|
|
661
|
+
|
|
662
|
+
// Ordinal number (1-based position in the row)
|
|
663
|
+
let tmpOrdinal = pInputIndex + 1;
|
|
664
|
+
|
|
665
|
+
// Build the rich tooltip
|
|
666
|
+
let tmpTooltipParts = [];
|
|
667
|
+
tmpTooltipParts.push('Hash: ' + tmpInputHash);
|
|
668
|
+
if (tmpName)
|
|
669
|
+
{
|
|
670
|
+
tmpTooltipParts.push('Name: ' + tmpName);
|
|
671
|
+
}
|
|
672
|
+
tmpTooltipParts.push('DataType: ' + tmpType);
|
|
673
|
+
if (tmpInputType)
|
|
674
|
+
{
|
|
675
|
+
tmpTooltipParts.push('InputType: ' + tmpInputType);
|
|
676
|
+
}
|
|
677
|
+
if (tmpWidth)
|
|
678
|
+
{
|
|
679
|
+
tmpTooltipParts.push('Width: ' + tmpWidth);
|
|
680
|
+
}
|
|
681
|
+
let tmpTooltip = tmpTooltipParts.join(' ');
|
|
682
|
+
|
|
683
|
+
// Determine display text based on _InputDisplayMode
|
|
684
|
+
let tmpDisplayText = '';
|
|
685
|
+
if (tmpParent._InputDisplayMode === 'hash')
|
|
686
|
+
{
|
|
687
|
+
tmpDisplayText = tmpInputHash;
|
|
688
|
+
}
|
|
689
|
+
else
|
|
690
|
+
{
|
|
691
|
+
tmpDisplayText = tmpName || tmpInputHash;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// Check if this input is currently selected
|
|
695
|
+
let tmpIsSelected = false;
|
|
696
|
+
if (tmpParent._SelectedInputIndices &&
|
|
697
|
+
tmpParent._SelectedInputIndices[0] === pSectionIndex &&
|
|
698
|
+
tmpParent._SelectedInputIndices[1] === pGroupIndex &&
|
|
699
|
+
tmpParent._SelectedInputIndices[2] === pRowIndex &&
|
|
700
|
+
tmpParent._SelectedInputIndices[3] === pInputIndex)
|
|
701
|
+
{
|
|
702
|
+
tmpIsSelected = true;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
let tmpSelectedClass = tmpIsSelected ? ' pict-fe-input-selected' : '';
|
|
706
|
+
|
|
707
|
+
// DataType icon
|
|
708
|
+
let tmpDataTypeIconHTML = tmpParent._IconographyProvider.getDataTypeIcon(tmpType, 12);
|
|
709
|
+
if (!tmpDataTypeIconHTML)
|
|
710
|
+
{
|
|
711
|
+
// Fallback to generic Input icon if no DataType icon
|
|
712
|
+
tmpDataTypeIconHTML = tmpParent._IconographyProvider.getIcon('Input', 'Default', 12);
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
let tmpHTML = '';
|
|
716
|
+
tmpHTML += `<div class="pict-fe-input${tmpSelectedClass}" id="FormEditor-Input-${tmpHash}-${pSectionIndex}-${pGroupIndex}-${pRowIndex}-${pInputIndex}" title="${tmpTooltip}"${tmpParent._DragDropProvider._buildDragAttributes('input', [pSectionIndex, pGroupIndex, pRowIndex, pInputIndex])} onclick="${tmpViewRef}._UtilitiesProvider.selectInput(${pSectionIndex}, ${pGroupIndex}, ${pRowIndex}, ${pInputIndex})">`;
|
|
717
|
+
tmpHTML += tmpParent._DragDropProvider._buildDragHandleHTML(10);
|
|
718
|
+
tmpHTML += `<span class="pict-fe-icon pict-fe-icon-datatype">${tmpDataTypeIconHTML}</span>`;
|
|
719
|
+
tmpHTML += `<span class="pict-fe-input-ordinal">${tmpOrdinal}</span>`;
|
|
720
|
+
tmpHTML += `<span class="pict-fe-input-name">${tmpParent._UtilitiesProvider._escapeHTML(tmpParent._UtilitiesProvider._truncateMiddle(tmpDisplayText, 20))}</span>`;
|
|
721
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm pict-fe-btn-danger pict-fe-input-remove" onclick="event.stopPropagation(); ${tmpViewRef}._ManifestOpsProvider.removeInput(${pSectionIndex}, ${pGroupIndex}, ${pRowIndex}, ${pInputIndex})" title="Remove input">\u00D7</button>`;
|
|
722
|
+
tmpHTML += '</div>';
|
|
723
|
+
|
|
724
|
+
return tmpHTML;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
module.exports = FormEditorRendering;
|
|
729
|
+
|
|
730
|
+
module.exports.default_configuration = {};
|