pict-section-formeditor 1.0.10 → 1.0.11
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/package.json +6 -6
- package/source/Pict-Section-FormEditor-DefaultConfiguration.js +254 -2
- package/source/providers/Pict-Provider-FormEditorManifestOps.js +29 -0
- package/source/providers/Pict-Provider-FormEditorRendering.js +270 -4
- package/source/views/PictView-FormEditor.js +757 -6
- package/test/Pict-Section-FormEditor_tests.js +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pict-section-formeditor",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "Pict visual editor for pict-section-form configurations",
|
|
5
5
|
"main": "source/Pict-Section-FormEditor.js",
|
|
6
6
|
"scripts": {
|
|
@@ -25,15 +25,15 @@
|
|
|
25
25
|
"homepage": "https://github.com/stevenvelozo/pict-section-formeditor#readme",
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"pict-section-code": "^1.0.3",
|
|
28
|
-
"pict-section-content": "^0.0.
|
|
29
|
-
"pict-section-form": "^1.0.
|
|
30
|
-
"pict-section-markdowneditor": "^1.0.
|
|
28
|
+
"pict-section-content": "^0.0.9",
|
|
29
|
+
"pict-section-form": "^1.0.194",
|
|
30
|
+
"pict-section-markdowneditor": "^1.0.6",
|
|
31
31
|
"pict-section-objecteditor": "^1.0.1",
|
|
32
32
|
"pict-view": "^1.0.67"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"pict": "^1.0.
|
|
36
|
-
"quackage": "^1.0.
|
|
35
|
+
"pict": "^1.0.357",
|
|
36
|
+
"quackage": "^1.0.63"
|
|
37
37
|
},
|
|
38
38
|
"mocha": {
|
|
39
39
|
"diff": true,
|
|
@@ -10,8 +10,8 @@ module.exports = (
|
|
|
10
10
|
// Address in AppData where the form configuration manifest lives
|
|
11
11
|
ManifestDataAddress: false,
|
|
12
12
|
|
|
13
|
-
// Which tab is active by default: 'visual', 'objecteditor', 'json'
|
|
14
|
-
ActiveTab: '
|
|
13
|
+
// Which tab is active by default: 'formoverview', 'visual', 'objecteditor', 'json', etc.
|
|
14
|
+
ActiveTab: 'formoverview',
|
|
15
15
|
|
|
16
16
|
// Extended descriptor properties to display in the Input properties panel.
|
|
17
17
|
// Each entry defines a custom field that maps to a dot-notation address
|
|
@@ -3740,6 +3740,258 @@ module.exports = (
|
|
|
3740
3740
|
.pict-fe-help-body .pict-content-code-wrap .attr-name { color: #986801 !important; }
|
|
3741
3741
|
.pict-fe-help-body .pict-content .pict-content-code-wrap .attr-value,
|
|
3742
3742
|
.pict-fe-help-body .pict-content-code-wrap .attr-value { color: #50A14F !important; }
|
|
3743
|
+
|
|
3744
|
+
/* ---- JSON Tab Header ---- */
|
|
3745
|
+
.pict-fe-json-header
|
|
3746
|
+
{
|
|
3747
|
+
display: flex;
|
|
3748
|
+
align-items: center;
|
|
3749
|
+
padding: 6px 12px;
|
|
3750
|
+
border-bottom: 1px solid #E8E0D4;
|
|
3751
|
+
background: #FDFCFA;
|
|
3752
|
+
}
|
|
3753
|
+
.pict-fe-json-readonly-label
|
|
3754
|
+
{
|
|
3755
|
+
display: flex;
|
|
3756
|
+
align-items: center;
|
|
3757
|
+
gap: 6px;
|
|
3758
|
+
font-size: 12px;
|
|
3759
|
+
color: #3D3229;
|
|
3760
|
+
cursor: pointer;
|
|
3761
|
+
user-select: none;
|
|
3762
|
+
}
|
|
3763
|
+
.pict-fe-json-readonly-label input[type="checkbox"]
|
|
3764
|
+
{
|
|
3765
|
+
cursor: pointer;
|
|
3766
|
+
}
|
|
3767
|
+
|
|
3768
|
+
/* ---- Form Overview Tab ---- */
|
|
3769
|
+
.pict-fe-overview-header
|
|
3770
|
+
{
|
|
3771
|
+
display: flex;
|
|
3772
|
+
align-items: center;
|
|
3773
|
+
justify-content: space-between;
|
|
3774
|
+
padding: 8px 12px;
|
|
3775
|
+
border-bottom: 1px solid #E8E0D4;
|
|
3776
|
+
}
|
|
3777
|
+
.pict-fe-overview-title
|
|
3778
|
+
{
|
|
3779
|
+
font-size: 15px;
|
|
3780
|
+
font-weight: 600;
|
|
3781
|
+
color: #3D3229;
|
|
3782
|
+
}
|
|
3783
|
+
.pict-fe-overview-labels
|
|
3784
|
+
{
|
|
3785
|
+
display: flex;
|
|
3786
|
+
align-items: center;
|
|
3787
|
+
gap: 6px;
|
|
3788
|
+
padding: 6px 6px 2px 6px;
|
|
3789
|
+
}
|
|
3790
|
+
.pict-fe-overview-label
|
|
3791
|
+
{
|
|
3792
|
+
font-size: 10px;
|
|
3793
|
+
font-weight: 600;
|
|
3794
|
+
text-transform: uppercase;
|
|
3795
|
+
letter-spacing: 0.5px;
|
|
3796
|
+
color: #8A7F72;
|
|
3797
|
+
padding: 0 6px;
|
|
3798
|
+
}
|
|
3799
|
+
.pict-fe-overview-actions-spacer
|
|
3800
|
+
{
|
|
3801
|
+
width: 64px;
|
|
3802
|
+
flex-shrink: 0;
|
|
3803
|
+
}
|
|
3804
|
+
.pict-fe-overview-tree
|
|
3805
|
+
{
|
|
3806
|
+
display: flex;
|
|
3807
|
+
flex-direction: column;
|
|
3808
|
+
padding: 4px 6px;
|
|
3809
|
+
}
|
|
3810
|
+
.pict-fe-overview-row
|
|
3811
|
+
{
|
|
3812
|
+
display: flex;
|
|
3813
|
+
align-items: center;
|
|
3814
|
+
gap: 6px;
|
|
3815
|
+
padding: 3px 6px;
|
|
3816
|
+
border-radius: 3px;
|
|
3817
|
+
transition: background 0.1s;
|
|
3818
|
+
}
|
|
3819
|
+
.pict-fe-overview-row:hover
|
|
3820
|
+
{
|
|
3821
|
+
background: #F5F0E8;
|
|
3822
|
+
}
|
|
3823
|
+
.pict-fe-overview-section
|
|
3824
|
+
{
|
|
3825
|
+
font-weight: 600;
|
|
3826
|
+
margin-top: 6px;
|
|
3827
|
+
}
|
|
3828
|
+
.pict-fe-overview-section:first-child
|
|
3829
|
+
{
|
|
3830
|
+
margin-top: 0;
|
|
3831
|
+
}
|
|
3832
|
+
.pict-fe-overview-group
|
|
3833
|
+
{
|
|
3834
|
+
font-weight: 500;
|
|
3835
|
+
}
|
|
3836
|
+
.pict-fe-overview-input
|
|
3837
|
+
{
|
|
3838
|
+
font-weight: 400;
|
|
3839
|
+
}
|
|
3840
|
+
.pict-fe-overview-indent
|
|
3841
|
+
{
|
|
3842
|
+
flex-shrink: 0;
|
|
3843
|
+
}
|
|
3844
|
+
.pict-fe-overview-depth-0
|
|
3845
|
+
{
|
|
3846
|
+
width: 0px;
|
|
3847
|
+
}
|
|
3848
|
+
.pict-fe-overview-depth-1
|
|
3849
|
+
{
|
|
3850
|
+
width: 20px;
|
|
3851
|
+
border-left: 2px solid #E8E0D4;
|
|
3852
|
+
margin-left: 6px;
|
|
3853
|
+
height: 100%;
|
|
3854
|
+
}
|
|
3855
|
+
.pict-fe-overview-depth-2
|
|
3856
|
+
{
|
|
3857
|
+
width: 40px;
|
|
3858
|
+
border-left: 2px solid #E8E0D4;
|
|
3859
|
+
margin-left: 26px;
|
|
3860
|
+
height: 100%;
|
|
3861
|
+
}
|
|
3862
|
+
.pict-fe-overview-icon
|
|
3863
|
+
{
|
|
3864
|
+
flex-shrink: 0;
|
|
3865
|
+
display: flex;
|
|
3866
|
+
align-items: center;
|
|
3867
|
+
width: 16px;
|
|
3868
|
+
}
|
|
3869
|
+
.pict-fe-overview-field
|
|
3870
|
+
{
|
|
3871
|
+
padding: 3px 6px;
|
|
3872
|
+
border: 1px solid #E8E3DA;
|
|
3873
|
+
border-radius: 3px;
|
|
3874
|
+
font-size: 12px;
|
|
3875
|
+
font-family: inherit;
|
|
3876
|
+
color: #3D3229;
|
|
3877
|
+
background: #FFF;
|
|
3878
|
+
box-sizing: border-box;
|
|
3879
|
+
transition: border-color 0.15s, box-shadow 0.15s;
|
|
3880
|
+
min-width: 0;
|
|
3881
|
+
}
|
|
3882
|
+
.pict-fe-overview-field:focus
|
|
3883
|
+
{
|
|
3884
|
+
outline: none;
|
|
3885
|
+
border-color: #9E6B47;
|
|
3886
|
+
box-shadow: 0 0 0 2px rgba(158, 107, 71, 0.15);
|
|
3887
|
+
}
|
|
3888
|
+
.pict-fe-overview-field-name
|
|
3889
|
+
{
|
|
3890
|
+
flex: 2;
|
|
3891
|
+
min-width: 80px;
|
|
3892
|
+
}
|
|
3893
|
+
.pict-fe-overview-field-hash
|
|
3894
|
+
{
|
|
3895
|
+
flex: 2;
|
|
3896
|
+
min-width: 60px;
|
|
3897
|
+
font-family: monospace;
|
|
3898
|
+
font-size: 11px;
|
|
3899
|
+
color: #8A7F72;
|
|
3900
|
+
}
|
|
3901
|
+
.pict-fe-overview-field-address
|
|
3902
|
+
{
|
|
3903
|
+
flex: 3;
|
|
3904
|
+
min-width: 80px;
|
|
3905
|
+
font-family: monospace;
|
|
3906
|
+
font-size: 11px;
|
|
3907
|
+
color: #6B7F5A;
|
|
3908
|
+
}
|
|
3909
|
+
.pict-fe-overview-actions
|
|
3910
|
+
{
|
|
3911
|
+
display: flex;
|
|
3912
|
+
gap: 4px;
|
|
3913
|
+
flex-shrink: 0;
|
|
3914
|
+
}
|
|
3915
|
+
.pict-fe-overview-empty
|
|
3916
|
+
{
|
|
3917
|
+
padding: 24px;
|
|
3918
|
+
text-align: center;
|
|
3919
|
+
color: #8A7F72;
|
|
3920
|
+
font-size: 13px;
|
|
3921
|
+
}
|
|
3922
|
+
.pict-fe-overview-empty-inline
|
|
3923
|
+
{
|
|
3924
|
+
color: #8A7F72;
|
|
3925
|
+
font-size: 12px;
|
|
3926
|
+
font-style: italic;
|
|
3927
|
+
padding: 4px 0;
|
|
3928
|
+
}
|
|
3929
|
+
.pict-fe-overview-row-separator
|
|
3930
|
+
{
|
|
3931
|
+
display: flex;
|
|
3932
|
+
align-items: center;
|
|
3933
|
+
gap: 6px;
|
|
3934
|
+
padding: 2px 6px;
|
|
3935
|
+
margin-top: 2px;
|
|
3936
|
+
}
|
|
3937
|
+
.pict-fe-overview-row-separator-label
|
|
3938
|
+
{
|
|
3939
|
+
font-size: 10px;
|
|
3940
|
+
color: #8A7F72;
|
|
3941
|
+
text-transform: uppercase;
|
|
3942
|
+
letter-spacing: 0.5px;
|
|
3943
|
+
white-space: nowrap;
|
|
3944
|
+
font-weight: 600;
|
|
3945
|
+
}
|
|
3946
|
+
.pict-fe-overview-row-separator-line
|
|
3947
|
+
{
|
|
3948
|
+
flex: 1;
|
|
3949
|
+
height: 1px;
|
|
3950
|
+
background: #D4C9B8;
|
|
3951
|
+
border: none;
|
|
3952
|
+
}
|
|
3953
|
+
.pict-fe-overview-column
|
|
3954
|
+
{
|
|
3955
|
+
font-weight: 400;
|
|
3956
|
+
}
|
|
3957
|
+
.pict-fe-overview-layout-badge
|
|
3958
|
+
{
|
|
3959
|
+
display: inline-block;
|
|
3960
|
+
font-size: 10px;
|
|
3961
|
+
padding: 1px 6px;
|
|
3962
|
+
border-radius: 3px;
|
|
3963
|
+
background: #E8E0D4;
|
|
3964
|
+
color: #6B5D4F;
|
|
3965
|
+
white-space: nowrap;
|
|
3966
|
+
font-weight: 600;
|
|
3967
|
+
}
|
|
3968
|
+
.pict-fe-overview-column-empty
|
|
3969
|
+
{
|
|
3970
|
+
display: flex;
|
|
3971
|
+
align-items: center;
|
|
3972
|
+
gap: 6px;
|
|
3973
|
+
padding: 2px 6px;
|
|
3974
|
+
}
|
|
3975
|
+
.pict-fe-overview-row[draggable]
|
|
3976
|
+
{
|
|
3977
|
+
cursor: grab;
|
|
3978
|
+
}
|
|
3979
|
+
.pict-fe-overview-row[draggable]:active
|
|
3980
|
+
{
|
|
3981
|
+
cursor: grabbing;
|
|
3982
|
+
}
|
|
3983
|
+
.pict-fe-overview-dragging
|
|
3984
|
+
{
|
|
3985
|
+
opacity: 0.4;
|
|
3986
|
+
}
|
|
3987
|
+
.pict-fe-overview-drop-above
|
|
3988
|
+
{
|
|
3989
|
+
border-top: 2px solid #8B6914;
|
|
3990
|
+
}
|
|
3991
|
+
.pict-fe-overview-drop-below
|
|
3992
|
+
{
|
|
3993
|
+
border-bottom: 2px solid #8B6914;
|
|
3994
|
+
}
|
|
3743
3995
|
`,
|
|
3744
3996
|
|
|
3745
3997
|
Templates:
|
|
@@ -1406,6 +1406,35 @@ class FormEditorManifestOps extends libPictProvider
|
|
|
1406
1406
|
}
|
|
1407
1407
|
}
|
|
1408
1408
|
|
|
1409
|
+
/**
|
|
1410
|
+
* Normalize row numbers across all groups in the manifest.
|
|
1411
|
+
* Fixes gaps in row numbering (e.g. rows 5, 8, 12 become 1, 2, 3).
|
|
1412
|
+
* Called after importing or pasting JSON to clean up messy manifests.
|
|
1413
|
+
*/
|
|
1414
|
+
normalizeAllRowNumbers()
|
|
1415
|
+
{
|
|
1416
|
+
let tmpManifest = this._resolveManifestData();
|
|
1417
|
+
if (!tmpManifest || !Array.isArray(tmpManifest.Sections))
|
|
1418
|
+
{
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
this._ensureSectionGroups();
|
|
1423
|
+
|
|
1424
|
+
for (let i = 0; i < tmpManifest.Sections.length; i++)
|
|
1425
|
+
{
|
|
1426
|
+
let tmpSection = tmpManifest.Sections[i];
|
|
1427
|
+
if (!tmpSection || !Array.isArray(tmpSection.Groups))
|
|
1428
|
+
{
|
|
1429
|
+
continue;
|
|
1430
|
+
}
|
|
1431
|
+
for (let j = 0; j < tmpSection.Groups.length; j++)
|
|
1432
|
+
{
|
|
1433
|
+
this._reindexGroupRows(i, j);
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1409
1438
|
/**
|
|
1410
1439
|
* Swap two Descriptor keys in the manifest's Descriptors object to
|
|
1411
1440
|
* control intra-row input ordering (Object.keys insertion order).
|
|
@@ -22,15 +22,17 @@ class FormEditorRendering extends libPictProvider
|
|
|
22
22
|
|
|
23
23
|
// Tab bar
|
|
24
24
|
tmpHTML += '<div class="pict-fe-tabbar">';
|
|
25
|
-
tmpHTML += `<button class="pict-fe-tab pict-fe-tab-active" id="FormEditor-Tab-
|
|
25
|
+
tmpHTML += `<button class="pict-fe-tab pict-fe-tab-active" id="FormEditor-Tab-FormOverview-${tmpHash}" onclick="${tmpViewRef}.switchTab('formoverview')">Form Overview</button>`;
|
|
26
|
+
tmpHTML += `<button class="pict-fe-tab" id="FormEditor-Tab-Visual-${tmpHash}" onclick="${tmpViewRef}.switchTab('visual')">Visual Editor</button>`;
|
|
26
27
|
tmpHTML += `<button class="pict-fe-tab" id="FormEditor-Tab-SolverEditor-${tmpHash}" onclick="${tmpViewRef}.switchTab('solvereditor')">Solver Editor</button>`;
|
|
27
28
|
tmpHTML += `<button class="pict-fe-tab" id="FormEditor-Tab-Solvers-${tmpHash}" onclick="${tmpViewRef}.switchTab('solvers')">Solvers</button>`;
|
|
28
29
|
tmpHTML += `<button class="pict-fe-tab" id="FormEditor-Tab-ListData-${tmpHash}" onclick="${tmpViewRef}.switchTab('listdata')">List Data</button>`;
|
|
29
30
|
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
31
|
tmpHTML += `<button class="pict-fe-tab" id="FormEditor-Tab-JSON-${tmpHash}" onclick="${tmpViewRef}.switchTab('json')">JSON</button>`;
|
|
32
32
|
tmpHTML += `<button class="pict-fe-tab" id="FormEditor-Tab-Import-${tmpHash}" onclick="${tmpViewRef}.switchTab('import')">Import</button>`;
|
|
33
|
-
|
|
33
|
+
// Object Editor and Preview tabs are hidden by default but panels remain for programmatic access
|
|
34
|
+
tmpHTML += `<button class="pict-fe-tab" id="FormEditor-Tab-ObjectEditor-${tmpHash}" onclick="${tmpViewRef}.switchTab('objecteditor')" style="display:none;">Object Editor</button>`;
|
|
35
|
+
tmpHTML += `<button class="pict-fe-tab" id="FormEditor-Tab-Preview-${tmpHash}" onclick="${tmpViewRef}.switchTab('preview')" style="display:none;">Preview</button>`;
|
|
34
36
|
tmpHTML += '</div>';
|
|
35
37
|
|
|
36
38
|
// Editor layout: tab content panels + resize handle + properties panel
|
|
@@ -39,8 +41,13 @@ class FormEditorRendering extends libPictProvider
|
|
|
39
41
|
// Tab content panels (stacked, only one active at a time)
|
|
40
42
|
tmpHTML += '<div class="pict-fe-editor-content">';
|
|
41
43
|
|
|
44
|
+
// Form Overview panel
|
|
45
|
+
tmpHTML += `<div class="pict-fe-tabcontent pict-fe-tabcontent-active" id="FormEditor-Panel-FormOverview-${tmpHash}">`;
|
|
46
|
+
tmpHTML += `<div id="FormEditor-FormOverviewTab-Container-${tmpHash}"></div>`;
|
|
47
|
+
tmpHTML += '</div>';
|
|
48
|
+
|
|
42
49
|
// Visual editor panel
|
|
43
|
-
tmpHTML += `<div class="pict-fe-tabcontent
|
|
50
|
+
tmpHTML += `<div class="pict-fe-tabcontent" id="FormEditor-Panel-Visual-${tmpHash}"></div>`;
|
|
44
51
|
|
|
45
52
|
// Solver editor tab panel
|
|
46
53
|
tmpHTML += `<div class="pict-fe-tabcontent" id="FormEditor-Panel-SolverEditor-${tmpHash}">`;
|
|
@@ -69,6 +76,9 @@ class FormEditorRendering extends libPictProvider
|
|
|
69
76
|
|
|
70
77
|
// JSON panel
|
|
71
78
|
tmpHTML += `<div class="pict-fe-tabcontent" id="FormEditor-Panel-JSON-${tmpHash}">`;
|
|
79
|
+
tmpHTML += '<div class="pict-fe-json-header">';
|
|
80
|
+
tmpHTML += `<label class="pict-fe-json-readonly-label"><input type="checkbox" id="FormEditor-JSON-ReadOnly-${tmpHash}" checked onchange="${tmpViewRef}._toggleJSONReadOnly(this.checked)" /> Read Only</label>`;
|
|
81
|
+
tmpHTML += '</div>';
|
|
72
82
|
tmpHTML += `<div id="FormEditor-CodeEditor-Container-${tmpHash}"></div>`;
|
|
73
83
|
tmpHTML += '</div>';
|
|
74
84
|
|
|
@@ -663,6 +673,262 @@ class FormEditorRendering extends libPictProvider
|
|
|
663
673
|
}
|
|
664
674
|
}
|
|
665
675
|
|
|
676
|
+
renderFormOverviewTabPanel()
|
|
677
|
+
{
|
|
678
|
+
let tmpParent = this._ParentFormEditor;
|
|
679
|
+
let tmpHash = tmpParent.Hash;
|
|
680
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
681
|
+
let tmpManifest = tmpParent._resolveManifestData();
|
|
682
|
+
|
|
683
|
+
if (!tmpManifest)
|
|
684
|
+
{
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// Ensure Groups are populated on Sections (they may be auto-generated from Descriptors)
|
|
689
|
+
tmpParent._ManifestOpsProvider._ensureSectionGroups();
|
|
690
|
+
|
|
691
|
+
let tmpHTML = '';
|
|
692
|
+
|
|
693
|
+
// Header
|
|
694
|
+
tmpHTML += '<div class="pict-fe-overview-header">';
|
|
695
|
+
tmpHTML += '<div class="pict-fe-overview-title">Form Overview</div>';
|
|
696
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-primary" onclick="${tmpViewRef}._formOverviewAddSection()">`;
|
|
697
|
+
tmpHTML += `<span class="pict-fe-icon pict-fe-icon-add">${tmpParent._IconographyProvider.getIcon('Action', 'Add', 12)}</span> Add Section</button>`;
|
|
698
|
+
tmpHTML += '</div>';
|
|
699
|
+
|
|
700
|
+
// Column labels
|
|
701
|
+
tmpHTML += '<div class="pict-fe-overview-labels">';
|
|
702
|
+
tmpHTML += '<span class="pict-fe-overview-indent pict-fe-overview-depth-0"></span>';
|
|
703
|
+
tmpHTML += '<span class="pict-fe-overview-icon"></span>';
|
|
704
|
+
tmpHTML += '<span class="pict-fe-overview-label pict-fe-overview-field-name">Name</span>';
|
|
705
|
+
tmpHTML += '<span class="pict-fe-overview-label pict-fe-overview-field-hash">Hash</span>';
|
|
706
|
+
tmpHTML += '<span class="pict-fe-overview-label pict-fe-overview-field-address">Address</span>';
|
|
707
|
+
tmpHTML += '<span class="pict-fe-overview-actions-spacer"></span>';
|
|
708
|
+
tmpHTML += '</div>';
|
|
709
|
+
|
|
710
|
+
// Tree body
|
|
711
|
+
tmpHTML += '<div class="pict-fe-overview-tree">';
|
|
712
|
+
|
|
713
|
+
let tmpSections = tmpManifest.Sections;
|
|
714
|
+
if (!Array.isArray(tmpSections) || tmpSections.length === 0)
|
|
715
|
+
{
|
|
716
|
+
tmpHTML += '<div class="pict-fe-overview-empty">No sections defined. Click "Add Section" to begin.</div>';
|
|
717
|
+
}
|
|
718
|
+
else
|
|
719
|
+
{
|
|
720
|
+
for (let i = 0; i < tmpSections.length; i++)
|
|
721
|
+
{
|
|
722
|
+
tmpHTML += this._renderOverviewSection(tmpSections[i], i, tmpManifest);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
tmpHTML += '</div>'; // overview-tree
|
|
727
|
+
|
|
728
|
+
this.pict.ContentAssignment.assignContent(`#FormEditor-FormOverviewTab-Container-${tmpHash}`, tmpHTML);
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
_renderOverviewSection(pSection, pSectionIndex, pManifest)
|
|
732
|
+
{
|
|
733
|
+
let tmpParent = this._ParentFormEditor;
|
|
734
|
+
let tmpHash = tmpParent.Hash;
|
|
735
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
736
|
+
|
|
737
|
+
let tmpSName = tmpParent._UtilitiesProvider._escapeAttr(pSection.Name || '');
|
|
738
|
+
let tmpSHash = tmpParent._UtilitiesProvider._escapeAttr(pSection.Hash || '');
|
|
739
|
+
|
|
740
|
+
let tmpHTML = '';
|
|
741
|
+
|
|
742
|
+
// Section row
|
|
743
|
+
tmpHTML += `<div class="pict-fe-overview-row pict-fe-overview-section" draggable="true" ondragstart="${tmpViewRef}._formOverviewDragStart(event,'section',${pSectionIndex})" ondragover="${tmpViewRef}._formOverviewDragOver(event,'section',${pSectionIndex})" ondrop="${tmpViewRef}._formOverviewDrop(event,'section',${pSectionIndex})" ondragend="${tmpViewRef}._formOverviewDragEnd(event)">`;
|
|
744
|
+
tmpHTML += '<span class="pict-fe-overview-indent pict-fe-overview-depth-0"></span>';
|
|
745
|
+
tmpHTML += `<span class="pict-fe-overview-icon">${tmpParent._IconographyProvider.getIcon('Section', 'Default', 14)}</span>`;
|
|
746
|
+
tmpHTML += `<input class="pict-fe-overview-field pict-fe-overview-field-name" id="FormEditor-Overview-Section-Name-${tmpHash}-${pSectionIndex}" type="text" value="${tmpSName}" placeholder="Section Name" onblur="${tmpViewRef}._formOverviewCommitField('section',${pSectionIndex},-1,-1,'Name',this.value)" onkeydown="${tmpViewRef}._formOverviewHandleKeydown(event,'section',${pSectionIndex},-1,-1,'Name')" />`;
|
|
747
|
+
tmpHTML += `<input class="pict-fe-overview-field pict-fe-overview-field-hash" id="FormEditor-Overview-Section-Hash-${tmpHash}-${pSectionIndex}" type="text" value="${tmpSHash}" placeholder="Hash" onblur="${tmpViewRef}._formOverviewCommitField('section',${pSectionIndex},-1,-1,'Hash',this.value)" onkeydown="${tmpViewRef}._formOverviewHandleKeydown(event,'section',${pSectionIndex},-1,-1,'Hash')" />`;
|
|
748
|
+
tmpHTML += '<span class="pict-fe-overview-field-address"></span>'; // empty spacer to keep columns aligned
|
|
749
|
+
tmpHTML += '<div class="pict-fe-overview-actions">';
|
|
750
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm" onclick="${tmpViewRef}._formOverviewAddGroup(${pSectionIndex})" title="Add Group">+ Group</button>`;
|
|
751
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm pict-fe-btn-danger" onclick="${tmpViewRef}._formOverviewRemoveSection(${pSectionIndex})" title="Remove section">\u00D7</button>`;
|
|
752
|
+
tmpHTML += '</div>';
|
|
753
|
+
tmpHTML += '</div>';
|
|
754
|
+
|
|
755
|
+
// Groups for this section
|
|
756
|
+
let tmpGroups = pSection.Groups;
|
|
757
|
+
if (Array.isArray(tmpGroups))
|
|
758
|
+
{
|
|
759
|
+
for (let j = 0; j < tmpGroups.length; j++)
|
|
760
|
+
{
|
|
761
|
+
tmpHTML += this._renderOverviewGroup(tmpGroups[j], pSectionIndex, j, pManifest);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
return tmpHTML;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
_renderOverviewGroup(pGroup, pSectionIndex, pGroupIndex, pManifest)
|
|
769
|
+
{
|
|
770
|
+
let tmpParent = this._ParentFormEditor;
|
|
771
|
+
let tmpHash = tmpParent.Hash;
|
|
772
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
773
|
+
|
|
774
|
+
let tmpGName = tmpParent._UtilitiesProvider._escapeAttr(pGroup.Name || '');
|
|
775
|
+
let tmpGHash = tmpParent._UtilitiesProvider._escapeAttr(pGroup.Hash || '');
|
|
776
|
+
|
|
777
|
+
let tmpIsTabular = (pGroup.Layout === 'Tabular' || pGroup.Layout === 'RecordSet');
|
|
778
|
+
|
|
779
|
+
let tmpHTML = '';
|
|
780
|
+
|
|
781
|
+
// Group row
|
|
782
|
+
tmpHTML += `<div class="pict-fe-overview-row pict-fe-overview-group" draggable="true" ondragstart="${tmpViewRef}._formOverviewDragStart(event,'group',${pSectionIndex},${pGroupIndex})" ondragover="${tmpViewRef}._formOverviewDragOver(event,'group',${pSectionIndex},${pGroupIndex})" ondrop="${tmpViewRef}._formOverviewDrop(event,'group',${pSectionIndex},${pGroupIndex})" ondragend="${tmpViewRef}._formOverviewDragEnd(event)">`;
|
|
783
|
+
tmpHTML += '<span class="pict-fe-overview-indent pict-fe-overview-depth-1"></span>';
|
|
784
|
+
tmpHTML += `<span class="pict-fe-overview-icon">${tmpParent._IconographyProvider.getIcon('Group', 'Default', 14)}</span>`;
|
|
785
|
+
tmpHTML += `<input class="pict-fe-overview-field pict-fe-overview-field-name" id="FormEditor-Overview-Group-Name-${tmpHash}-${pSectionIndex}-${pGroupIndex}" type="text" value="${tmpGName}" placeholder="Group Name" onblur="${tmpViewRef}._formOverviewCommitField('group',${pSectionIndex},${pGroupIndex},-1,'Name',this.value)" onkeydown="${tmpViewRef}._formOverviewHandleKeydown(event,'group',${pSectionIndex},${pGroupIndex},-1,'Name')" />`;
|
|
786
|
+
tmpHTML += `<input class="pict-fe-overview-field pict-fe-overview-field-hash" id="FormEditor-Overview-Group-Hash-${tmpHash}-${pSectionIndex}-${pGroupIndex}" type="text" value="${tmpGHash}" placeholder="Hash" onblur="${tmpViewRef}._formOverviewCommitField('group',${pSectionIndex},${pGroupIndex},-1,'Hash',this.value)" onkeydown="${tmpViewRef}._formOverviewHandleKeydown(event,'group',${pSectionIndex},${pGroupIndex},-1,'Hash')" />`;
|
|
787
|
+
tmpHTML += '<span class="pict-fe-overview-field-address"></span>'; // empty spacer
|
|
788
|
+
tmpHTML += '<div class="pict-fe-overview-actions">';
|
|
789
|
+
if (tmpIsTabular)
|
|
790
|
+
{
|
|
791
|
+
tmpHTML += `<span class="pict-fe-overview-layout-badge">\u229E ${pGroup.Layout}</span>`;
|
|
792
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm" onclick="${tmpViewRef}._formOverviewAddColumn(${pSectionIndex},${pGroupIndex})" title="Add Column">+ Col</button>`;
|
|
793
|
+
}
|
|
794
|
+
else
|
|
795
|
+
{
|
|
796
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm" onclick="${tmpViewRef}._formOverviewAddRow(${pSectionIndex},${pGroupIndex})" title="Add Row">+ Row</button>`;
|
|
797
|
+
}
|
|
798
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm pict-fe-btn-danger" onclick="${tmpViewRef}._formOverviewRemoveGroup(${pSectionIndex},${pGroupIndex})" title="Remove group">\u00D7</button>`;
|
|
799
|
+
tmpHTML += '</div>';
|
|
800
|
+
tmpHTML += '</div>';
|
|
801
|
+
|
|
802
|
+
if (tmpIsTabular)
|
|
803
|
+
{
|
|
804
|
+
// Tabular/RecordSet group: render columns from the ReferenceManifest
|
|
805
|
+
tmpHTML += this._renderOverviewTabularColumns(pGroup, pSectionIndex, pGroupIndex);
|
|
806
|
+
}
|
|
807
|
+
else
|
|
808
|
+
{
|
|
809
|
+
// Regular group: render inputs grouped by rows
|
|
810
|
+
tmpHTML += this._renderOverviewGroupInputs(pGroup, pSectionIndex, pGroupIndex, pManifest);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
return tmpHTML;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
_renderOverviewGroupInputs(pGroup, pSectionIndex, pGroupIndex, pManifest)
|
|
817
|
+
{
|
|
818
|
+
let tmpParent = this._ParentFormEditor;
|
|
819
|
+
let tmpHash = tmpParent.Hash;
|
|
820
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
821
|
+
|
|
822
|
+
let tmpHTML = '';
|
|
823
|
+
|
|
824
|
+
// Inputs for this group (derived from Descriptors via rows)
|
|
825
|
+
let tmpRows = tmpParent._ManifestOpsProvider.getRowsForGroupByIndex(pSectionIndex, pGroupIndex);
|
|
826
|
+
for (let k = 0; k < tmpRows.length; k++)
|
|
827
|
+
{
|
|
828
|
+
let tmpRow = tmpRows[k];
|
|
829
|
+
if (!tmpRow || !Array.isArray(tmpRow.Inputs))
|
|
830
|
+
{
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// Row separator
|
|
835
|
+
tmpHTML += '<div class="pict-fe-overview-row-separator">';
|
|
836
|
+
tmpHTML += '<span class="pict-fe-overview-indent pict-fe-overview-depth-2"></span>';
|
|
837
|
+
tmpHTML += `<span class="pict-fe-overview-row-separator-label">Row ${k + 1}</span>`;
|
|
838
|
+
tmpHTML += '<span class="pict-fe-overview-row-separator-line"></span>';
|
|
839
|
+
tmpHTML += '<div class="pict-fe-overview-actions">';
|
|
840
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm" onclick="${tmpViewRef}._formOverviewAddInputToRow(${pSectionIndex},${pGroupIndex},${k})" title="Add input to this row">+ Input</button>`;
|
|
841
|
+
tmpHTML += '</div>';
|
|
842
|
+
tmpHTML += '</div>';
|
|
843
|
+
|
|
844
|
+
for (let m = 0; m < tmpRow.Inputs.length; m++)
|
|
845
|
+
{
|
|
846
|
+
let tmpAddress = tmpRow.Inputs[m];
|
|
847
|
+
let tmpDescriptor = (pManifest.Descriptors && pManifest.Descriptors[tmpAddress]) ? pManifest.Descriptors[tmpAddress] : null;
|
|
848
|
+
let tmpIName = tmpParent._UtilitiesProvider._escapeAttr(tmpDescriptor ? (tmpDescriptor.Name || '') : '');
|
|
849
|
+
let tmpIHash = tmpParent._UtilitiesProvider._escapeAttr(tmpDescriptor ? (tmpDescriptor.Hash || tmpAddress) : tmpAddress);
|
|
850
|
+
let tmpIAddr = tmpParent._UtilitiesProvider._escapeAttr(tmpAddress);
|
|
851
|
+
|
|
852
|
+
tmpHTML += `<div class="pict-fe-overview-row pict-fe-overview-input" draggable="true" ondragstart="${tmpViewRef}._formOverviewDragStart(event,'input',${pSectionIndex},${pGroupIndex},${k},${m})" ondragover="${tmpViewRef}._formOverviewDragOver(event,'input',${pSectionIndex},${pGroupIndex},${k},${m})" ondrop="${tmpViewRef}._formOverviewDrop(event,'input',${pSectionIndex},${pGroupIndex},${k},${m})" ondragend="${tmpViewRef}._formOverviewDragEnd(event)">`;
|
|
853
|
+
tmpHTML += '<span class="pict-fe-overview-indent pict-fe-overview-depth-2"></span>';
|
|
854
|
+
let tmpType = tmpDescriptor ? (tmpDescriptor.DataType || 'String') : 'String';
|
|
855
|
+
let tmpDTIcon = tmpParent._IconographyProvider.getDataTypeIcon(tmpType, 12);
|
|
856
|
+
if (!tmpDTIcon)
|
|
857
|
+
{
|
|
858
|
+
tmpDTIcon = tmpParent._IconographyProvider.getIcon('Input', 'Default', 12);
|
|
859
|
+
}
|
|
860
|
+
tmpHTML += `<span class="pict-fe-overview-icon">${tmpDTIcon}</span>`;
|
|
861
|
+
tmpHTML += `<input class="pict-fe-overview-field pict-fe-overview-field-name" id="FormEditor-Overview-Input-Name-${tmpHash}-${pSectionIndex}-${pGroupIndex}-${k}-${m}" type="text" value="${tmpIName}" placeholder="Input Name" onblur="${tmpViewRef}._formOverviewCommitField('input',${pSectionIndex},${pGroupIndex},${k},'Name',this.value,${m})" onkeydown="${tmpViewRef}._formOverviewHandleKeydown(event,'input',${pSectionIndex},${pGroupIndex},${k},'Name',${m})" />`;
|
|
862
|
+
tmpHTML += `<input class="pict-fe-overview-field pict-fe-overview-field-hash" id="FormEditor-Overview-Input-Hash-${tmpHash}-${pSectionIndex}-${pGroupIndex}-${k}-${m}" type="text" value="${tmpIHash}" placeholder="Hash" onblur="${tmpViewRef}._formOverviewCommitField('input',${pSectionIndex},${pGroupIndex},${k},'Hash',this.value,${m})" onkeydown="${tmpViewRef}._formOverviewHandleKeydown(event,'input',${pSectionIndex},${pGroupIndex},${k},'Hash',${m})" />`;
|
|
863
|
+
tmpHTML += `<input class="pict-fe-overview-field pict-fe-overview-field-address" id="FormEditor-Overview-Input-Address-${tmpHash}-${pSectionIndex}-${pGroupIndex}-${k}-${m}" type="text" value="${tmpIAddr}" placeholder="Data Address" onblur="${tmpViewRef}._formOverviewCommitField('input',${pSectionIndex},${pGroupIndex},${k},'Address',this.value,${m})" onkeydown="${tmpViewRef}._formOverviewHandleKeydown(event,'input',${pSectionIndex},${pGroupIndex},${k},'Address',${m})" />`;
|
|
864
|
+
tmpHTML += '<div class="pict-fe-overview-actions">';
|
|
865
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm pict-fe-btn-danger" onclick="${tmpViewRef}._formOverviewRemoveInput(${pSectionIndex},${pGroupIndex},${k},${m})" title="Remove input">\u00D7</button>`;
|
|
866
|
+
tmpHTML += '</div>';
|
|
867
|
+
tmpHTML += '</div>';
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
return tmpHTML;
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
_renderOverviewTabularColumns(pGroup, pSectionIndex, pGroupIndex)
|
|
875
|
+
{
|
|
876
|
+
let tmpParent = this._ParentFormEditor;
|
|
877
|
+
let tmpHash = tmpParent.Hash;
|
|
878
|
+
let tmpViewRef = tmpParent._browserViewRef();
|
|
879
|
+
|
|
880
|
+
let tmpHTML = '';
|
|
881
|
+
|
|
882
|
+
if (!pGroup.RecordManifest)
|
|
883
|
+
{
|
|
884
|
+
tmpHTML += '<div class="pict-fe-overview-row pict-fe-overview-column-empty">';
|
|
885
|
+
tmpHTML += '<span class="pict-fe-overview-indent pict-fe-overview-depth-2"></span>';
|
|
886
|
+
tmpHTML += '<span class="pict-fe-overview-empty-inline">(No manifest bound)</span>';
|
|
887
|
+
tmpHTML += '</div>';
|
|
888
|
+
return tmpHTML;
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
let tmpRefManifest = tmpParent._ManifestOpsProvider._resolveReferenceManifest(pGroup.RecordManifest);
|
|
892
|
+
if (!tmpRefManifest || !tmpRefManifest.Descriptors)
|
|
893
|
+
{
|
|
894
|
+
tmpHTML += '<div class="pict-fe-overview-row pict-fe-overview-column-empty">';
|
|
895
|
+
tmpHTML += '<span class="pict-fe-overview-indent pict-fe-overview-depth-2"></span>';
|
|
896
|
+
tmpHTML += `<span class="pict-fe-overview-empty-inline">(Manifest "${pGroup.RecordManifest}" not found)</span>`;
|
|
897
|
+
tmpHTML += '</div>';
|
|
898
|
+
return tmpHTML;
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
let tmpColumnAddresses = Object.keys(tmpRefManifest.Descriptors);
|
|
902
|
+
for (let c = 0; c < tmpColumnAddresses.length; c++)
|
|
903
|
+
{
|
|
904
|
+
let tmpColAddr = tmpColumnAddresses[c];
|
|
905
|
+
let tmpColDesc = tmpRefManifest.Descriptors[tmpColAddr];
|
|
906
|
+
let tmpColName = tmpParent._UtilitiesProvider._escapeAttr(tmpColDesc ? (tmpColDesc.Name || '') : '');
|
|
907
|
+
let tmpColHash = tmpParent._UtilitiesProvider._escapeAttr(tmpColDesc ? (tmpColDesc.Hash || tmpColAddr) : tmpColAddr);
|
|
908
|
+
let tmpEscColAddr = tmpParent._UtilitiesProvider._escapeAttr(tmpColAddr);
|
|
909
|
+
|
|
910
|
+
tmpHTML += `<div class="pict-fe-overview-row pict-fe-overview-column" draggable="true" ondragstart="${tmpViewRef}._formOverviewDragStart(event,'column',${pSectionIndex},${pGroupIndex},0,${c})" ondragover="${tmpViewRef}._formOverviewDragOver(event,'column',${pSectionIndex},${pGroupIndex},0,${c})" ondrop="${tmpViewRef}._formOverviewDrop(event,'column',${pSectionIndex},${pGroupIndex},0,${c})" ondragend="${tmpViewRef}._formOverviewDragEnd(event)">`;
|
|
911
|
+
tmpHTML += '<span class="pict-fe-overview-indent pict-fe-overview-depth-2"></span>';
|
|
912
|
+
|
|
913
|
+
let tmpType = tmpColDesc ? (tmpColDesc.DataType || 'String') : 'String';
|
|
914
|
+
let tmpDTIcon = tmpParent._IconographyProvider.getDataTypeIcon(tmpType, 12);
|
|
915
|
+
if (!tmpDTIcon)
|
|
916
|
+
{
|
|
917
|
+
tmpDTIcon = tmpParent._IconographyProvider.getIcon('Input', 'Default', 12);
|
|
918
|
+
}
|
|
919
|
+
tmpHTML += `<span class="pict-fe-overview-icon">${tmpDTIcon}</span>`;
|
|
920
|
+
tmpHTML += `<input class="pict-fe-overview-field pict-fe-overview-field-name" id="FormEditor-Overview-Column-Name-${tmpHash}-${pSectionIndex}-${pGroupIndex}-${c}" type="text" value="${tmpColName}" placeholder="Column Name" onblur="${tmpViewRef}._formOverviewCommitField('column',${pSectionIndex},${pGroupIndex},-1,'Name',this.value,${c})" onkeydown="${tmpViewRef}._formOverviewHandleKeydown(event,'column',${pSectionIndex},${pGroupIndex},-1,'Name',${c})" />`;
|
|
921
|
+
tmpHTML += `<input class="pict-fe-overview-field pict-fe-overview-field-hash" id="FormEditor-Overview-Column-Hash-${tmpHash}-${pSectionIndex}-${pGroupIndex}-${c}" type="text" value="${tmpColHash}" placeholder="Hash" onblur="${tmpViewRef}._formOverviewCommitField('column',${pSectionIndex},${pGroupIndex},-1,'Hash',this.value,${c})" onkeydown="${tmpViewRef}._formOverviewHandleKeydown(event,'column',${pSectionIndex},${pGroupIndex},-1,'Hash',${c})" />`;
|
|
922
|
+
tmpHTML += '<span class="pict-fe-overview-field-address"></span>'; // spacer — columns use Descriptor key as address
|
|
923
|
+
tmpHTML += '<div class="pict-fe-overview-actions">';
|
|
924
|
+
tmpHTML += `<button class="pict-fe-btn pict-fe-btn-sm pict-fe-btn-danger" onclick="${tmpViewRef}._formOverviewRemoveColumn(${pSectionIndex},${pGroupIndex},'${tmpEscColAddr}')" title="Remove column">\u00D7</button>`;
|
|
925
|
+
tmpHTML += '</div>';
|
|
926
|
+
tmpHTML += '</div>';
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
return tmpHTML;
|
|
930
|
+
}
|
|
931
|
+
|
|
666
932
|
_renderRow(pRow, pSectionIndex, pGroupIndex, pRowIndex)
|
|
667
933
|
{
|
|
668
934
|
let tmpParent = this._ParentFormEditor;
|