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,532 @@
|
|
|
1
|
+
const libPictView = require('pict-view');
|
|
2
|
+
|
|
3
|
+
class PictViewFormEditorInputTypePicker extends libPictView
|
|
4
|
+
{
|
|
5
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
6
|
+
{
|
|
7
|
+
super(pFable, pOptions, pServiceHash);
|
|
8
|
+
this._ParentFormEditor = null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Open the floating InputType picker for an Input.
|
|
13
|
+
*
|
|
14
|
+
* Renders a categorized, searchable panel anchored near the InputType chip.
|
|
15
|
+
* The first option is always "DataType Default" which clears the InputType.
|
|
16
|
+
*
|
|
17
|
+
* @param {number} pSectionIndex - Index of the section
|
|
18
|
+
* @param {number} pGroupIndex - Index of the group
|
|
19
|
+
* @param {number} pRowIndex - Index of the row
|
|
20
|
+
* @param {number} pInputIndex - Index of the input within the row
|
|
21
|
+
*/
|
|
22
|
+
beginEditInputType(pSectionIndex, pGroupIndex, pRowIndex, pInputIndex)
|
|
23
|
+
{
|
|
24
|
+
let tmpHash = this._ParentFormEditor.Hash;
|
|
25
|
+
let tmpViewRef = this._ParentFormEditor._browserViewRef();
|
|
26
|
+
let tmpPickerId = `FormEditor-InputTypePicker-${tmpHash}`;
|
|
27
|
+
|
|
28
|
+
// Close any existing picker first
|
|
29
|
+
this.closeInputTypePicker();
|
|
30
|
+
|
|
31
|
+
// Resolve the current InputType from the Descriptor's PictForm
|
|
32
|
+
let tmpManifest = this._ParentFormEditor._resolveManifestData();
|
|
33
|
+
let tmpCurrentValue = '';
|
|
34
|
+
if (tmpManifest && tmpManifest.Sections)
|
|
35
|
+
{
|
|
36
|
+
let tmpSection = tmpManifest.Sections[pSectionIndex];
|
|
37
|
+
let tmpGroup = tmpSection && tmpSection.Groups ? tmpSection.Groups[pGroupIndex] : null;
|
|
38
|
+
let tmpRow = tmpGroup && tmpGroup.Rows ? tmpGroup.Rows[pRowIndex] : null;
|
|
39
|
+
if (tmpRow && Array.isArray(tmpRow.Inputs))
|
|
40
|
+
{
|
|
41
|
+
let tmpAddress = tmpRow.Inputs[pInputIndex];
|
|
42
|
+
if (typeof tmpAddress === 'string' && tmpManifest.Descriptors && tmpManifest.Descriptors[tmpAddress])
|
|
43
|
+
{
|
|
44
|
+
let tmpDescriptor = tmpManifest.Descriptors[tmpAddress];
|
|
45
|
+
if (tmpDescriptor.PictForm && tmpDescriptor.PictForm.InputType)
|
|
46
|
+
{
|
|
47
|
+
tmpCurrentValue = tmpDescriptor.PictForm.InputType;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Store the current edit context so commitEditInputType can use it
|
|
54
|
+
this._InputTypePickerContext =
|
|
55
|
+
{
|
|
56
|
+
SectionIndex: pSectionIndex,
|
|
57
|
+
GroupIndex: pGroupIndex,
|
|
58
|
+
RowIndex: pRowIndex,
|
|
59
|
+
InputIndex: pInputIndex,
|
|
60
|
+
CurrentValue: tmpCurrentValue
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// Build the picker HTML
|
|
64
|
+
let tmpPickerHTML = this._renderInputTypePicker(tmpCurrentValue, '');
|
|
65
|
+
|
|
66
|
+
// Get the chip element to position the picker near it
|
|
67
|
+
let tmpChipElementId = `FormEditor-InputInputType-${tmpHash}-${pSectionIndex}-${pGroupIndex}-${pRowIndex}-${pInputIndex}`;
|
|
68
|
+
|
|
69
|
+
// Append overlay + picker to document.body so they are not clipped
|
|
70
|
+
// by the form editor container's overflow:hidden.
|
|
71
|
+
if (typeof document !== 'undefined')
|
|
72
|
+
{
|
|
73
|
+
// Create the full-viewport overlay to catch dismiss clicks
|
|
74
|
+
let tmpOverlay = document.createElement('div');
|
|
75
|
+
tmpOverlay.id = tmpPickerId + '-Overlay';
|
|
76
|
+
tmpOverlay.className = 'pict-fe-inputtype-overlay';
|
|
77
|
+
tmpOverlay.onclick = function() { eval(tmpViewRef + '.closeInputTypePicker()'); };
|
|
78
|
+
// Prevent scroll events on the overlay from reaching the page
|
|
79
|
+
tmpOverlay.addEventListener('wheel', function(pEvent) { pEvent.preventDefault(); }, { passive: false });
|
|
80
|
+
|
|
81
|
+
let tmpPickerContainer = document.createElement('div');
|
|
82
|
+
tmpPickerContainer.id = tmpPickerId;
|
|
83
|
+
tmpPickerContainer.className = 'pict-fe-inputtype-picker';
|
|
84
|
+
tmpPickerContainer.innerHTML = tmpPickerHTML;
|
|
85
|
+
tmpPickerContainer.onclick = function(e) { e.stopPropagation(); };
|
|
86
|
+
// Contain scroll within the picker — allow internal scrolling but
|
|
87
|
+
// prevent scroll chaining to the page when at top/bottom boundary
|
|
88
|
+
tmpPickerContainer.addEventListener('wheel', function(pEvent)
|
|
89
|
+
{
|
|
90
|
+
pEvent.stopPropagation();
|
|
91
|
+
// Find the scrollable categories list inside the picker
|
|
92
|
+
let tmpScrollable = tmpPickerContainer.querySelector('.pict-fe-inputtype-picker-categories');
|
|
93
|
+
if (!tmpScrollable)
|
|
94
|
+
{
|
|
95
|
+
pEvent.preventDefault();
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
let tmpAtTop = (tmpScrollable.scrollTop <= 0) && (pEvent.deltaY < 0);
|
|
99
|
+
let tmpAtBottom = (tmpScrollable.scrollTop + tmpScrollable.clientHeight >= tmpScrollable.scrollHeight) && (pEvent.deltaY > 0);
|
|
100
|
+
if (tmpAtTop || tmpAtBottom)
|
|
101
|
+
{
|
|
102
|
+
pEvent.preventDefault();
|
|
103
|
+
}
|
|
104
|
+
}, { passive: false });
|
|
105
|
+
|
|
106
|
+
tmpOverlay.appendChild(tmpPickerContainer);
|
|
107
|
+
document.body.appendChild(tmpOverlay);
|
|
108
|
+
|
|
109
|
+
// Position the picker using fixed viewport coordinates
|
|
110
|
+
// anchored below the InputType chip (or properties panel button as fallback)
|
|
111
|
+
let tmpAnchorEl = document.getElementById(tmpChipElementId);
|
|
112
|
+
// If the inline chip doesn't exist (e.g. picker opened from properties panel), use the panel button
|
|
113
|
+
if (!tmpAnchorEl)
|
|
114
|
+
{
|
|
115
|
+
tmpAnchorEl = document.getElementById(`FormEditor-PropsInputTypeBtn-${tmpHash}`);
|
|
116
|
+
}
|
|
117
|
+
if (tmpAnchorEl && tmpAnchorEl.getBoundingClientRect)
|
|
118
|
+
{
|
|
119
|
+
let tmpAnchorRect = tmpAnchorEl.getBoundingClientRect();
|
|
120
|
+
|
|
121
|
+
tmpPickerContainer.style.position = 'fixed';
|
|
122
|
+
tmpPickerContainer.style.top = (tmpAnchorRect.bottom + 4) + 'px';
|
|
123
|
+
tmpPickerContainer.style.left = tmpAnchorRect.left + 'px';
|
|
124
|
+
|
|
125
|
+
// If the picker would overflow the right edge of the viewport,
|
|
126
|
+
// nudge it left
|
|
127
|
+
let tmpPickerWidth = 340;
|
|
128
|
+
if (tmpAnchorRect.left + tmpPickerWidth > window.innerWidth)
|
|
129
|
+
{
|
|
130
|
+
tmpPickerContainer.style.left = Math.max(8, window.innerWidth - tmpPickerWidth - 8) + 'px';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// If the picker would overflow the bottom of the viewport,
|
|
134
|
+
// open it upward instead
|
|
135
|
+
let tmpPickerMaxHeight = 420;
|
|
136
|
+
if (tmpAnchorRect.bottom + 4 + tmpPickerMaxHeight > window.innerHeight)
|
|
137
|
+
{
|
|
138
|
+
tmpPickerContainer.style.top = '';
|
|
139
|
+
tmpPickerContainer.style.bottom = (window.innerHeight - tmpAnchorRect.top + 4) + 'px';
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Focus the search input
|
|
144
|
+
let tmpSearchSet = this.pict.ContentAssignment.getElement(`#${tmpPickerId}-Search`);
|
|
145
|
+
let tmpSearch = (Array.isArray(tmpSearchSet) && tmpSearchSet.length > 0) ? tmpSearchSet[0] : tmpSearchSet;
|
|
146
|
+
if (tmpSearch && tmpSearch.focus)
|
|
147
|
+
{
|
|
148
|
+
tmpSearch.focus();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Render the inner HTML of the InputType picker panel.
|
|
155
|
+
*
|
|
156
|
+
* @param {string} pCurrentValue - The currently selected InputType Hash (empty for default)
|
|
157
|
+
* @param {string} pSearchQuery - Current search/filter text
|
|
158
|
+
* @return {string} The picker innerHTML
|
|
159
|
+
*/
|
|
160
|
+
_renderInputTypePicker(pCurrentValue, pSearchQuery)
|
|
161
|
+
{
|
|
162
|
+
let tmpHash = this._ParentFormEditor.Hash;
|
|
163
|
+
let tmpViewRef = this._ParentFormEditor._browserViewRef();
|
|
164
|
+
let tmpPickerId = `FormEditor-InputTypePicker-${tmpHash}`;
|
|
165
|
+
|
|
166
|
+
let tmpHTML = '';
|
|
167
|
+
|
|
168
|
+
// Search bar
|
|
169
|
+
tmpHTML += '<div class="pict-fe-inputtype-picker-search">';
|
|
170
|
+
tmpHTML += `<input type="text" class="pict-fe-inputtype-picker-search-input" id="${tmpPickerId}-Search" placeholder="Search input types\u2026" value="${this._ParentFormEditor._UtilitiesProvider._escapeAttr(pSearchQuery)}" oninput="${tmpViewRef}._onInputTypePickerSearch(this.value)" onkeydown="if(event.key==='Escape'){${tmpViewRef}.closeInputTypePicker();}" />`;
|
|
171
|
+
tmpHTML += '</div>';
|
|
172
|
+
|
|
173
|
+
// Filter definitions by search query
|
|
174
|
+
let tmpDefinitions = this._ParentFormEditor._UtilitiesProvider._filterInputTypeDefinitions(pSearchQuery);
|
|
175
|
+
|
|
176
|
+
// Separate prominent items from the rest
|
|
177
|
+
let tmpProminentItems = [];
|
|
178
|
+
let tmpRemainingItems = [];
|
|
179
|
+
for (let i = 0; i < tmpDefinitions.length; i++)
|
|
180
|
+
{
|
|
181
|
+
if (tmpDefinitions[i].Prominent)
|
|
182
|
+
{
|
|
183
|
+
tmpProminentItems.push(tmpDefinitions[i]);
|
|
184
|
+
}
|
|
185
|
+
else
|
|
186
|
+
{
|
|
187
|
+
tmpRemainingItems.push(tmpDefinitions[i]);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// --- Common section: DataType Default + prominent items ---
|
|
192
|
+
tmpHTML += '<div class="pict-fe-inputtype-picker-default">';
|
|
193
|
+
|
|
194
|
+
// DataType Default is always shown (not affected by search)
|
|
195
|
+
let tmpDefaultActive = (pCurrentValue === '') ? ' pict-fe-inputtype-picker-item-active' : '';
|
|
196
|
+
tmpHTML += `<div class="pict-fe-inputtype-picker-item${tmpDefaultActive}" onclick="${tmpViewRef}.commitEditInputType('')">`;
|
|
197
|
+
tmpHTML += '<div class="pict-fe-inputtype-picker-item-name">DataType Default</div>';
|
|
198
|
+
tmpHTML += '<div class="pict-fe-inputtype-picker-item-desc">Use the default template for this DataType</div>';
|
|
199
|
+
tmpHTML += '</div>';
|
|
200
|
+
|
|
201
|
+
// Prominent items appear right after DataType Default
|
|
202
|
+
for (let p = 0; p < tmpProminentItems.length; p++)
|
|
203
|
+
{
|
|
204
|
+
let tmpDef = tmpProminentItems[p];
|
|
205
|
+
let tmpActive = (tmpDef.Hash === pCurrentValue) ? ' pict-fe-inputtype-picker-item-active' : '';
|
|
206
|
+
let tmpPickerIcon = this._ParentFormEditor._IconographyProvider.getInputTypeIcon(tmpDef.Hash, 16);
|
|
207
|
+
|
|
208
|
+
tmpHTML += `<div class="pict-fe-inputtype-picker-item${tmpActive}" onclick="${tmpViewRef}.commitEditInputType('${this._ParentFormEditor._UtilitiesProvider._escapeAttr(tmpDef.Hash)}')">`;
|
|
209
|
+
tmpHTML += `<div class="pict-fe-inputtype-picker-item-name">${tmpPickerIcon ? '<span class="pict-fe-icon pict-fe-icon-picker">' + tmpPickerIcon + '</span>' : ''}${this._ParentFormEditor._UtilitiesProvider._escapeHTML(tmpDef.Name || tmpDef.Hash)}</div>`;
|
|
210
|
+
if (tmpDef.Description)
|
|
211
|
+
{
|
|
212
|
+
tmpHTML += `<div class="pict-fe-inputtype-picker-item-desc">${this._ParentFormEditor._UtilitiesProvider._escapeHTML(tmpDef.Description)}</div>`;
|
|
213
|
+
}
|
|
214
|
+
tmpHTML += '</div>';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
tmpHTML += '</div>';
|
|
218
|
+
|
|
219
|
+
// --- Categorized section: all non-prominent items ---
|
|
220
|
+
let tmpCategories = [];
|
|
221
|
+
let tmpCategorySeen = {};
|
|
222
|
+
let tmpCategoryItems = {};
|
|
223
|
+
|
|
224
|
+
for (let i = 0; i < tmpRemainingItems.length; i++)
|
|
225
|
+
{
|
|
226
|
+
let tmpDef = tmpRemainingItems[i];
|
|
227
|
+
let tmpCategory = tmpDef.Category || 'Other';
|
|
228
|
+
|
|
229
|
+
if (!tmpCategorySeen[tmpCategory])
|
|
230
|
+
{
|
|
231
|
+
tmpCategorySeen[tmpCategory] = true;
|
|
232
|
+
tmpCategories.push(tmpCategory);
|
|
233
|
+
tmpCategoryItems[tmpCategory] = [];
|
|
234
|
+
}
|
|
235
|
+
tmpCategoryItems[tmpCategory].push(tmpDef);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (tmpCategories.length === 0 && tmpProminentItems.length === 0 && pSearchQuery)
|
|
239
|
+
{
|
|
240
|
+
tmpHTML += '<div class="pict-fe-inputtype-picker-empty">No input types match your search.</div>';
|
|
241
|
+
}
|
|
242
|
+
else if (tmpCategories.length > 0)
|
|
243
|
+
{
|
|
244
|
+
tmpHTML += '<div class="pict-fe-inputtype-picker-categories">';
|
|
245
|
+
for (let c = 0; c < tmpCategories.length; c++)
|
|
246
|
+
{
|
|
247
|
+
let tmpCategory = tmpCategories[c];
|
|
248
|
+
let tmpItems = tmpCategoryItems[tmpCategory];
|
|
249
|
+
|
|
250
|
+
tmpHTML += '<div class="pict-fe-inputtype-picker-category">';
|
|
251
|
+
tmpHTML += `<div class="pict-fe-inputtype-picker-category-label">${this._ParentFormEditor._UtilitiesProvider._escapeHTML(tmpCategory)}</div>`;
|
|
252
|
+
|
|
253
|
+
for (let j = 0; j < tmpItems.length; j++)
|
|
254
|
+
{
|
|
255
|
+
let tmpDef = tmpItems[j];
|
|
256
|
+
let tmpActive = (tmpDef.Hash === pCurrentValue) ? ' pict-fe-inputtype-picker-item-active' : '';
|
|
257
|
+
let tmpCatIcon = this._ParentFormEditor._IconographyProvider.getInputTypeIcon(tmpDef.Hash, 16);
|
|
258
|
+
|
|
259
|
+
tmpHTML += `<div class="pict-fe-inputtype-picker-item${tmpActive}" onclick="${tmpViewRef}.commitEditInputType('${this._ParentFormEditor._UtilitiesProvider._escapeAttr(tmpDef.Hash)}')">`;
|
|
260
|
+
tmpHTML += `<div class="pict-fe-inputtype-picker-item-name">${tmpCatIcon ? '<span class="pict-fe-icon pict-fe-icon-picker">' + tmpCatIcon + '</span>' : ''}${this._ParentFormEditor._UtilitiesProvider._escapeHTML(tmpDef.Name || tmpDef.Hash)}</div>`;
|
|
261
|
+
if (tmpDef.Description)
|
|
262
|
+
{
|
|
263
|
+
tmpHTML += `<div class="pict-fe-inputtype-picker-item-desc">${this._ParentFormEditor._UtilitiesProvider._escapeHTML(tmpDef.Description)}</div>`;
|
|
264
|
+
}
|
|
265
|
+
tmpHTML += '</div>';
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
tmpHTML += '</div>';
|
|
269
|
+
}
|
|
270
|
+
tmpHTML += '</div>';
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return tmpHTML;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Handle search input changes in the InputType picker.
|
|
278
|
+
*
|
|
279
|
+
* @param {string} pQuery - The current search query
|
|
280
|
+
*/
|
|
281
|
+
_onInputTypePickerSearch(pQuery)
|
|
282
|
+
{
|
|
283
|
+
let tmpHash = this._ParentFormEditor.Hash;
|
|
284
|
+
let tmpPickerId = `FormEditor-InputTypePicker-${tmpHash}`;
|
|
285
|
+
|
|
286
|
+
if (!this._InputTypePickerContext)
|
|
287
|
+
{
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
let tmpCurrentValue = this._InputTypePickerContext.CurrentValue;
|
|
292
|
+
let tmpPickerHTML = this._renderInputTypePicker(tmpCurrentValue, pQuery);
|
|
293
|
+
|
|
294
|
+
let tmpPickerSet = this.pict.ContentAssignment.getElement(`#${tmpPickerId}`);
|
|
295
|
+
let tmpPicker = (Array.isArray(tmpPickerSet) && tmpPickerSet.length > 0) ? tmpPickerSet[0] : tmpPickerSet;
|
|
296
|
+
if (tmpPicker)
|
|
297
|
+
{
|
|
298
|
+
tmpPicker.innerHTML = tmpPickerHTML;
|
|
299
|
+
|
|
300
|
+
// Re-focus the search input and restore cursor position
|
|
301
|
+
let tmpSearchSet = this.pict.ContentAssignment.getElement(`#${tmpPickerId}-Search`);
|
|
302
|
+
let tmpSearch = (Array.isArray(tmpSearchSet) && tmpSearchSet.length > 0) ? tmpSearchSet[0] : tmpSearchSet;
|
|
303
|
+
if (tmpSearch && tmpSearch.focus)
|
|
304
|
+
{
|
|
305
|
+
tmpSearch.focus();
|
|
306
|
+
// Move cursor to end
|
|
307
|
+
if (tmpSearch.setSelectionRange)
|
|
308
|
+
{
|
|
309
|
+
let tmpLen = tmpSearch.value.length;
|
|
310
|
+
tmpSearch.setSelectionRange(tmpLen, tmpLen);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Close the floating InputType picker.
|
|
318
|
+
*/
|
|
319
|
+
closeInputTypePicker()
|
|
320
|
+
{
|
|
321
|
+
let tmpHash = this._ParentFormEditor.Hash;
|
|
322
|
+
let tmpOverlayId = `FormEditor-InputTypePicker-${tmpHash}-Overlay`;
|
|
323
|
+
|
|
324
|
+
let tmpOverlaySet = this.pict.ContentAssignment.getElement(`#${tmpOverlayId}`);
|
|
325
|
+
let tmpOverlay = (Array.isArray(tmpOverlaySet) && tmpOverlaySet.length > 0) ? tmpOverlaySet[0] : tmpOverlaySet;
|
|
326
|
+
if (tmpOverlay && tmpOverlay.parentNode)
|
|
327
|
+
{
|
|
328
|
+
tmpOverlay.parentNode.removeChild(tmpOverlay);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
this._InputTypePickerContext = null;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Commit the InputType selection from the picker, updating the Descriptor
|
|
336
|
+
* and re-rendering the visual editor.
|
|
337
|
+
*
|
|
338
|
+
* @param {string} pInputTypeHash - The selected InputType Hash, or '' for DataType Default
|
|
339
|
+
*/
|
|
340
|
+
commitEditInputType(pInputTypeHash)
|
|
341
|
+
{
|
|
342
|
+
if (!this._InputTypePickerContext)
|
|
343
|
+
{
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
let tmpContext = this._InputTypePickerContext;
|
|
348
|
+
|
|
349
|
+
// Close the picker
|
|
350
|
+
this.closeInputTypePicker();
|
|
351
|
+
|
|
352
|
+
let tmpManifest = this._ParentFormEditor._resolveManifestData();
|
|
353
|
+
if (!tmpManifest || !tmpManifest.Sections)
|
|
354
|
+
{
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
let tmpDescriptor = null;
|
|
359
|
+
|
|
360
|
+
if (tmpContext.IsTabular)
|
|
361
|
+
{
|
|
362
|
+
// Tabular column — resolve descriptor from ReferenceManifest
|
|
363
|
+
let tmpSection = tmpManifest.Sections[tmpContext.SectionIndex];
|
|
364
|
+
let tmpGroup = (tmpSection && Array.isArray(tmpSection.Groups)) ? tmpSection.Groups[tmpContext.GroupIndex] : null;
|
|
365
|
+
if (tmpGroup && tmpGroup.RecordManifest)
|
|
366
|
+
{
|
|
367
|
+
let tmpRefManifest = this._ParentFormEditor._ManifestOpsProvider._resolveReferenceManifest(tmpGroup.RecordManifest);
|
|
368
|
+
if (tmpRefManifest && tmpRefManifest.Descriptors && tmpRefManifest.Descriptors[tmpContext.ColumnAddress])
|
|
369
|
+
{
|
|
370
|
+
tmpDescriptor = tmpRefManifest.Descriptors[tmpContext.ColumnAddress];
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
else
|
|
375
|
+
{
|
|
376
|
+
// Regular input — resolve descriptor from main Descriptors
|
|
377
|
+
let tmpSection = tmpManifest.Sections[tmpContext.SectionIndex];
|
|
378
|
+
let tmpGroup = tmpSection && tmpSection.Groups ? tmpSection.Groups[tmpContext.GroupIndex] : null;
|
|
379
|
+
let tmpRow = tmpGroup && tmpGroup.Rows ? tmpGroup.Rows[tmpContext.RowIndex] : null;
|
|
380
|
+
if (tmpRow && Array.isArray(tmpRow.Inputs))
|
|
381
|
+
{
|
|
382
|
+
let tmpAddress = tmpRow.Inputs[tmpContext.InputIndex];
|
|
383
|
+
if (typeof tmpAddress === 'string' && tmpManifest.Descriptors && tmpManifest.Descriptors[tmpAddress])
|
|
384
|
+
{
|
|
385
|
+
tmpDescriptor = tmpManifest.Descriptors[tmpAddress];
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
if (tmpDescriptor)
|
|
391
|
+
{
|
|
392
|
+
if (!tmpDescriptor.PictForm)
|
|
393
|
+
{
|
|
394
|
+
tmpDescriptor.PictForm = {};
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (pInputTypeHash === '')
|
|
398
|
+
{
|
|
399
|
+
// "DataType Default" — remove the InputType property
|
|
400
|
+
delete tmpDescriptor.PictForm.InputType;
|
|
401
|
+
}
|
|
402
|
+
else
|
|
403
|
+
{
|
|
404
|
+
tmpDescriptor.PictForm.InputType = pInputTypeHash;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
this._ParentFormEditor.renderVisualEditor();
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Open the InputType picker for a tabular/RecordSet column descriptor.
|
|
413
|
+
* Reuses the same picker UI as regular inputs but resolves the descriptor
|
|
414
|
+
* from the ReferenceManifest instead of the main Descriptors.
|
|
415
|
+
*
|
|
416
|
+
* @param {number} pSectionIndex
|
|
417
|
+
* @param {number} pGroupIndex
|
|
418
|
+
* @param {string} pColumnAddress - The column address in the ReferenceManifest
|
|
419
|
+
*/
|
|
420
|
+
beginEditTabularInputType(pSectionIndex, pGroupIndex, pColumnAddress)
|
|
421
|
+
{
|
|
422
|
+
let tmpHash = this._ParentFormEditor.Hash;
|
|
423
|
+
let tmpViewRef = this._ParentFormEditor._browserViewRef();
|
|
424
|
+
let tmpPickerId = `FormEditor-InputTypePicker-${tmpHash}`;
|
|
425
|
+
|
|
426
|
+
// Close any existing picker first
|
|
427
|
+
this.closeInputTypePicker();
|
|
428
|
+
|
|
429
|
+
// Resolve the current InputType from the ReferenceManifest descriptor
|
|
430
|
+
let tmpManifest = this._ParentFormEditor._resolveManifestData();
|
|
431
|
+
let tmpCurrentValue = '';
|
|
432
|
+
if (tmpManifest && tmpManifest.Sections)
|
|
433
|
+
{
|
|
434
|
+
let tmpSection = tmpManifest.Sections[pSectionIndex];
|
|
435
|
+
let tmpGroup = (tmpSection && Array.isArray(tmpSection.Groups)) ? tmpSection.Groups[pGroupIndex] : null;
|
|
436
|
+
if (tmpGroup && tmpGroup.RecordManifest)
|
|
437
|
+
{
|
|
438
|
+
let tmpRefManifest = this._ParentFormEditor._ManifestOpsProvider._resolveReferenceManifest(tmpGroup.RecordManifest);
|
|
439
|
+
if (tmpRefManifest && tmpRefManifest.Descriptors && tmpRefManifest.Descriptors[pColumnAddress])
|
|
440
|
+
{
|
|
441
|
+
let tmpDescriptor = tmpRefManifest.Descriptors[pColumnAddress];
|
|
442
|
+
if (tmpDescriptor.PictForm && tmpDescriptor.PictForm.InputType)
|
|
443
|
+
{
|
|
444
|
+
tmpCurrentValue = tmpDescriptor.PictForm.InputType;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// Store context — mark as tabular so commitEditInputType knows to handle it
|
|
451
|
+
this._InputTypePickerContext =
|
|
452
|
+
{
|
|
453
|
+
SectionIndex: pSectionIndex,
|
|
454
|
+
GroupIndex: pGroupIndex,
|
|
455
|
+
ColumnAddress: pColumnAddress,
|
|
456
|
+
IsTabular: true,
|
|
457
|
+
CurrentValue: tmpCurrentValue
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
// Build the picker HTML
|
|
461
|
+
let tmpPickerHTML = this._renderInputTypePicker(tmpCurrentValue, '');
|
|
462
|
+
|
|
463
|
+
// Anchor near the properties panel InputType button
|
|
464
|
+
if (typeof document !== 'undefined')
|
|
465
|
+
{
|
|
466
|
+
let tmpOverlay = document.createElement('div');
|
|
467
|
+
tmpOverlay.id = tmpPickerId + '-Overlay';
|
|
468
|
+
tmpOverlay.className = 'pict-fe-inputtype-overlay';
|
|
469
|
+
tmpOverlay.onclick = function() { eval(tmpViewRef + '.closeInputTypePicker()'); };
|
|
470
|
+
tmpOverlay.addEventListener('wheel', function(pEvent) { pEvent.preventDefault(); }, { passive: false });
|
|
471
|
+
|
|
472
|
+
let tmpPickerContainer = document.createElement('div');
|
|
473
|
+
tmpPickerContainer.id = tmpPickerId;
|
|
474
|
+
tmpPickerContainer.className = 'pict-fe-inputtype-picker';
|
|
475
|
+
tmpPickerContainer.innerHTML = tmpPickerHTML;
|
|
476
|
+
tmpPickerContainer.onclick = function(e) { e.stopPropagation(); };
|
|
477
|
+
tmpPickerContainer.addEventListener('wheel', function(pEvent)
|
|
478
|
+
{
|
|
479
|
+
pEvent.stopPropagation();
|
|
480
|
+
let tmpScrollable = tmpPickerContainer.querySelector('.pict-fe-inputtype-picker-categories');
|
|
481
|
+
if (!tmpScrollable)
|
|
482
|
+
{
|
|
483
|
+
pEvent.preventDefault();
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
let tmpAtTop = (tmpScrollable.scrollTop <= 0) && (pEvent.deltaY < 0);
|
|
487
|
+
let tmpAtBottom = (tmpScrollable.scrollTop + tmpScrollable.clientHeight >= tmpScrollable.scrollHeight) && (pEvent.deltaY > 0);
|
|
488
|
+
if (tmpAtTop || tmpAtBottom)
|
|
489
|
+
{
|
|
490
|
+
pEvent.preventDefault();
|
|
491
|
+
}
|
|
492
|
+
}, { passive: false });
|
|
493
|
+
|
|
494
|
+
tmpOverlay.appendChild(tmpPickerContainer);
|
|
495
|
+
document.body.appendChild(tmpOverlay);
|
|
496
|
+
|
|
497
|
+
// Position anchored to the InputType button in the properties panel
|
|
498
|
+
let tmpAnchorEl = document.getElementById(`FormEditor-PropsInputTypeBtn-${tmpHash}`);
|
|
499
|
+
if (tmpAnchorEl && tmpAnchorEl.getBoundingClientRect)
|
|
500
|
+
{
|
|
501
|
+
let tmpAnchorRect = tmpAnchorEl.getBoundingClientRect();
|
|
502
|
+
|
|
503
|
+
tmpPickerContainer.style.position = 'fixed';
|
|
504
|
+
tmpPickerContainer.style.top = (tmpAnchorRect.bottom + 4) + 'px';
|
|
505
|
+
tmpPickerContainer.style.left = tmpAnchorRect.left + 'px';
|
|
506
|
+
|
|
507
|
+
let tmpPickerWidth = 340;
|
|
508
|
+
if (tmpAnchorRect.left + tmpPickerWidth > window.innerWidth)
|
|
509
|
+
{
|
|
510
|
+
tmpPickerContainer.style.left = Math.max(8, window.innerWidth - tmpPickerWidth - 8) + 'px';
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
let tmpPickerMaxHeight = 420;
|
|
514
|
+
if (tmpAnchorRect.bottom + 4 + tmpPickerMaxHeight > window.innerHeight)
|
|
515
|
+
{
|
|
516
|
+
tmpPickerContainer.style.top = '';
|
|
517
|
+
tmpPickerContainer.style.bottom = (window.innerHeight - tmpAnchorRect.top + 4) + 'px';
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Focus the search input
|
|
522
|
+
let tmpSearchSet = this.pict.ContentAssignment.getElement(`#${tmpPickerId}-Search`);
|
|
523
|
+
let tmpSearch = (Array.isArray(tmpSearchSet) && tmpSearchSet.length > 0) ? tmpSearchSet[0] : tmpSearchSet;
|
|
524
|
+
if (tmpSearch && tmpSearch.focus)
|
|
525
|
+
{
|
|
526
|
+
tmpSearch.focus();
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
module.exports = PictViewFormEditorInputTypePicker;
|