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.
Files changed (178) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +118 -0
  3. package/docs/.nojekyll +0 -0
  4. package/docs/README.md +162 -0
  5. package/docs/_sidebar.md +23 -0
  6. package/docs/_topbar.md +5 -0
  7. package/docs/cover.md +12 -0
  8. package/docs/css/docuserve.css +73 -0
  9. package/docs/index.html +39 -0
  10. package/docs/retold-catalog.json +224 -0
  11. package/docs/retold-keyword-index.json +46846 -0
  12. package/example_applications/form_editor/.quackage.json +10 -0
  13. package/example_applications/form_editor/FormEditor-Example-Application.js +226 -0
  14. package/example_applications/form_editor/html/icon-chooser.html +375 -0
  15. package/example_applications/form_editor/html/index.html +54 -0
  16. package/example_applications/form_editor/package.json +50 -0
  17. package/package.json +55 -0
  18. package/sample_manifests/Complex-Table.json +974 -0
  19. package/sample_manifests/Distill-Example.json +200 -0
  20. package/sample_manifests/Gradebook-Assignment.json +38 -0
  21. package/sample_manifests/Gradebook-Student.json +40 -0
  22. package/sample_manifests/Manyfest-Editor.json +347 -0
  23. package/sample_manifests/Simple-Form.json +232 -0
  24. package/sample_manifests/Simple-Table.json +79 -0
  25. package/source/Pict-Section-FormEditor-DefaultConfiguration.js +3321 -0
  26. package/source/Pict-Section-FormEditor.js +35 -0
  27. package/source/providers/Pict-Provider-ChildPictManager-Application.js +40 -0
  28. package/source/providers/Pict-Provider-ChildPictManager.js +238 -0
  29. package/source/providers/Pict-Provider-FormEditorDocumentation.js +356 -0
  30. package/source/providers/Pict-Provider-FormEditorDragDrop.js +535 -0
  31. package/source/providers/Pict-Provider-FormEditorIconography.js +1002 -0
  32. package/source/providers/Pict-Provider-FormEditorManifestOps.js +1443 -0
  33. package/source/providers/Pict-Provider-FormEditorRendering.js +730 -0
  34. package/source/providers/Pict-Provider-FormEditorUtilities.js +862 -0
  35. package/source/providers/Pict-Provider-PreviewCSS.js +42 -0
  36. package/source/views/PictView-FormEditor-InlineEditing.js +309 -0
  37. package/source/views/PictView-FormEditor-InputTypePicker.js +532 -0
  38. package/source/views/PictView-FormEditor-PropertiesPanel.js +7730 -0
  39. package/source/views/PictView-FormEditor.js +681 -0
  40. package/test/Pict-Section-FormEditor_tests.js +4102 -0
  41. package/user-documentation/.pict_documentation_topics.json +695 -0
  42. package/user-documentation/Getting-Started.md +32 -0
  43. package/user-documentation/Groups.md +52 -0
  44. package/user-documentation/Inputs.md +98 -0
  45. package/user-documentation/Sections.md +36 -0
  46. package/user-documentation/Shortcuts.md +44 -0
  47. package/user-documentation/Solver-Expression-Walkthrough.md +176 -0
  48. package/user-documentation/Solver-Expressions-Advanced.md +344 -0
  49. package/user-documentation/Solver-Functions.md +213 -0
  50. package/user-documentation/Solvers.md +81 -0
  51. package/user-documentation/ToC.md +18 -0
  52. package/user-documentation/solverfunctions/abs.md +84 -0
  53. package/user-documentation/solverfunctions/aggregationhistogram.md +83 -0
  54. package/user-documentation/solverfunctions/aggregationhistogrambyobject.md +64 -0
  55. package/user-documentation/solverfunctions/arrayconcat.md +64 -0
  56. package/user-documentation/solverfunctions/avg.md +81 -0
  57. package/user-documentation/solverfunctions/bucketset.md +69 -0
  58. package/user-documentation/solverfunctions/ceil.md +70 -0
  59. package/user-documentation/solverfunctions/cleanvaluearray.md +66 -0
  60. package/user-documentation/solverfunctions/cleanvalueobject.md +68 -0
  61. package/user-documentation/solverfunctions/colorgroupbackground.md +60 -0
  62. package/user-documentation/solverfunctions/colorinputbackground.md +62 -0
  63. package/user-documentation/solverfunctions/colorinputbackgroundtabular.md +64 -0
  64. package/user-documentation/solverfunctions/colorsectionbackground.md +59 -0
  65. package/user-documentation/solverfunctions/compare.md +72 -0
  66. package/user-documentation/solverfunctions/concat.md +73 -0
  67. package/user-documentation/solverfunctions/concatraw.md +73 -0
  68. package/user-documentation/solverfunctions/cos.md +75 -0
  69. package/user-documentation/solverfunctions/count.md +73 -0
  70. package/user-documentation/solverfunctions/countset.md +65 -0
  71. package/user-documentation/solverfunctions/countsetelements.md +63 -0
  72. package/user-documentation/solverfunctions/createarrayfromabsolutevalues.md +63 -0
  73. package/user-documentation/solverfunctions/createvalueobjectbyhashes.md +69 -0
  74. package/user-documentation/solverfunctions/cumulativesummation.md +96 -0
  75. package/user-documentation/solverfunctions/dateadddays.md +79 -0
  76. package/user-documentation/solverfunctions/dateaddhours.md +74 -0
  77. package/user-documentation/solverfunctions/dateaddmilliseconds.md +65 -0
  78. package/user-documentation/solverfunctions/dateaddminutes.md +72 -0
  79. package/user-documentation/solverfunctions/dateaddmonths.md +74 -0
  80. package/user-documentation/solverfunctions/dateaddseconds.md +66 -0
  81. package/user-documentation/solverfunctions/dateaddweeks.md +73 -0
  82. package/user-documentation/solverfunctions/dateaddyears.md +74 -0
  83. package/user-documentation/solverfunctions/datedaydifference.md +84 -0
  84. package/user-documentation/solverfunctions/datefromparts.md +81 -0
  85. package/user-documentation/solverfunctions/datehourdifference.md +64 -0
  86. package/user-documentation/solverfunctions/datemathadd.md +72 -0
  87. package/user-documentation/solverfunctions/datemilliseconddifference.md +64 -0
  88. package/user-documentation/solverfunctions/dateminutedifference.md +64 -0
  89. package/user-documentation/solverfunctions/datemonthdifference.md +66 -0
  90. package/user-documentation/solverfunctions/dateseconddifference.md +64 -0
  91. package/user-documentation/solverfunctions/dateweekdifference.md +65 -0
  92. package/user-documentation/solverfunctions/dateyeardifference.md +64 -0
  93. package/user-documentation/solverfunctions/differencearrays.md +59 -0
  94. package/user-documentation/solverfunctions/disablesolverordinal.md +58 -0
  95. package/user-documentation/solverfunctions/distributionhistogram.md +96 -0
  96. package/user-documentation/solverfunctions/distributionhistogrambyobject.md +64 -0
  97. package/user-documentation/solverfunctions/enablesolverordinal.md +57 -0
  98. package/user-documentation/solverfunctions/entryinset.md +72 -0
  99. package/user-documentation/solverfunctions/euler.md +77 -0
  100. package/user-documentation/solverfunctions/exp.md +74 -0
  101. package/user-documentation/solverfunctions/findfirstvaluebyexactmatch.md +67 -0
  102. package/user-documentation/solverfunctions/findfirstvaluebystringincludes.md +67 -0
  103. package/user-documentation/solverfunctions/flatten.md +76 -0
  104. package/user-documentation/solverfunctions/floor.md +70 -0
  105. package/user-documentation/solverfunctions/gaussianelimination.md +75 -0
  106. package/user-documentation/solverfunctions/generatearrayofobjectsfromsets.md +70 -0
  107. package/user-documentation/solverfunctions/generatehtmlhexcolor.md +67 -0
  108. package/user-documentation/solverfunctions/getvalue.md +90 -0
  109. package/user-documentation/solverfunctions/getvaluearray.md +64 -0
  110. package/user-documentation/solverfunctions/getvalueobject.md +67 -0
  111. package/user-documentation/solverfunctions/hidesections.md +58 -0
  112. package/user-documentation/solverfunctions/if.md +109 -0
  113. package/user-documentation/solverfunctions/iterativeseries.md +107 -0
  114. package/user-documentation/solverfunctions/join.md +75 -0
  115. package/user-documentation/solverfunctions/joinraw.md +64 -0
  116. package/user-documentation/solverfunctions/largestinset.md +63 -0
  117. package/user-documentation/solverfunctions/leastsquares.md +66 -0
  118. package/user-documentation/solverfunctions/linest.md +58 -0
  119. package/user-documentation/solverfunctions/log.md +74 -0
  120. package/user-documentation/solverfunctions/logvalues.md +65 -0
  121. package/user-documentation/solverfunctions/match.md +71 -0
  122. package/user-documentation/solverfunctions/matrixinverse.md +67 -0
  123. package/user-documentation/solverfunctions/matrixmultiply.md +71 -0
  124. package/user-documentation/solverfunctions/matrixtranspose.md +72 -0
  125. package/user-documentation/solverfunctions/matrixvectormultiply.md +69 -0
  126. package/user-documentation/solverfunctions/max.md +73 -0
  127. package/user-documentation/solverfunctions/mean.md +63 -0
  128. package/user-documentation/solverfunctions/median.md +79 -0
  129. package/user-documentation/solverfunctions/min.md +73 -0
  130. package/user-documentation/solverfunctions/mode.md +66 -0
  131. package/user-documentation/solverfunctions/objectkeystoarray.md +66 -0
  132. package/user-documentation/solverfunctions/objectvaluessortbyexternalobjectarray.md +65 -0
  133. package/user-documentation/solverfunctions/objectvaluestoarray.md +67 -0
  134. package/user-documentation/solverfunctions/percent.md +75 -0
  135. package/user-documentation/solverfunctions/pi.md +77 -0
  136. package/user-documentation/solverfunctions/polynomialregression.md +69 -0
  137. package/user-documentation/solverfunctions/predict.md +71 -0
  138. package/user-documentation/solverfunctions/rad.md +85 -0
  139. package/user-documentation/solverfunctions/randomfloat.md +63 -0
  140. package/user-documentation/solverfunctions/randomfloatbetween.md +72 -0
  141. package/user-documentation/solverfunctions/randomfloatupto.md +65 -0
  142. package/user-documentation/solverfunctions/randominteger.md +56 -0
  143. package/user-documentation/solverfunctions/randomintegerbetween.md +72 -0
  144. package/user-documentation/solverfunctions/randomintegerupto.md +64 -0
  145. package/user-documentation/solverfunctions/refreshtabularsection.md +57 -0
  146. package/user-documentation/solverfunctions/resolvehtmlentities.md +64 -0
  147. package/user-documentation/solverfunctions/round.md +111 -0
  148. package/user-documentation/solverfunctions/runsolvers.md +49 -0
  149. package/user-documentation/solverfunctions/setconcatenate.md +64 -0
  150. package/user-documentation/solverfunctions/setgroupvisibility.md +60 -0
  151. package/user-documentation/solverfunctions/setsectionvisibility.md +59 -0
  152. package/user-documentation/solverfunctions/setsolverordinalenabled.md +59 -0
  153. package/user-documentation/solverfunctions/settabularrowlength.md +57 -0
  154. package/user-documentation/solverfunctions/setvalue.md +65 -0
  155. package/user-documentation/solverfunctions/showsections.md +58 -0
  156. package/user-documentation/solverfunctions/sin.md +83 -0
  157. package/user-documentation/solverfunctions/slice.md +80 -0
  158. package/user-documentation/solverfunctions/smallestinset.md +63 -0
  159. package/user-documentation/solverfunctions/sortarray.md +58 -0
  160. package/user-documentation/solverfunctions/sorthistogram.md +70 -0
  161. package/user-documentation/solverfunctions/sorthistogrambykeys.md +69 -0
  162. package/user-documentation/solverfunctions/sortset.md +75 -0
  163. package/user-documentation/solverfunctions/sqrt.md +85 -0
  164. package/user-documentation/solverfunctions/stdev.md +81 -0
  165. package/user-documentation/solverfunctions/stdeva.md +58 -0
  166. package/user-documentation/solverfunctions/stdevp.md +83 -0
  167. package/user-documentation/solverfunctions/stringcountsegments.md +66 -0
  168. package/user-documentation/solverfunctions/stringgetsegments.md +74 -0
  169. package/user-documentation/solverfunctions/subtractingsummation.md +66 -0
  170. package/user-documentation/solverfunctions/sum.md +78 -0
  171. package/user-documentation/solverfunctions/tan.md +78 -0
  172. package/user-documentation/solverfunctions/tofixed.md +75 -0
  173. package/user-documentation/solverfunctions/unionarrays.md +59 -0
  174. package/user-documentation/solverfunctions/uniquearray.md +58 -0
  175. package/user-documentation/solverfunctions/var.md +67 -0
  176. package/user-documentation/solverfunctions/vara.md +58 -0
  177. package/user-documentation/solverfunctions/varp.md +66 -0
  178. 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 &amp; 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 &amp; 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 &amp; 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('&#10;');
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('&#10;');
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 = {};