wuic-framework-lib 1.3.2 → 1.7.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/fesm2022/{wuic-framework-lib-chart-list.component-DAP-TPJR.mjs → wuic-framework-lib-chart-list.component-Bl424n7x.mjs} +49 -37
- package/fesm2022/{wuic-framework-lib-code-editor.component-CLMDvOWs.mjs → wuic-framework-lib-code-editor.component-N6HMl2mf.mjs} +441 -277
- package/fesm2022/{wuic-framework-lib-designer.component-DA7abiLW.mjs → wuic-framework-lib-designer.component-Bd2fwl6H.mjs} +1948 -641
- package/fesm2022/{wuic-framework-lib-first-run-wizard.component-BLrUrfZ9.mjs → wuic-framework-lib-first-run-wizard.component-NN6cWVHL.mjs} +18 -20
- package/fesm2022/{wuic-framework-lib-framework-docs.component-DphKhTef.mjs → wuic-framework-lib-framework-docs.component-B867JAYe.mjs} +4205 -380
- package/fesm2022/{wuic-framework-lib-pivot-builder.component-BqUUwrJM.mjs → wuic-framework-lib-pivot-builder.component-DWukH0L-.mjs} +578 -66
- package/fesm2022/{wuic-framework-lib-prompt-dialog-fallback-host.component-DZrhwMH5.mjs → wuic-framework-lib-prompt-dialog-fallback-host.component-DWO1Rcgv.mjs} +2 -2
- package/fesm2022/wuic-framework-lib-report-designer.component-QcrU81m6.mjs +380 -0
- package/fesm2022/wuic-framework-lib-report-scaffold-dialog.component-U-zdOoRV.mjs +431 -0
- package/fesm2022/{wuic-framework-lib-report-viewer.component-CIN2fY0P.mjs → wuic-framework-lib-report-viewer.component-B4LhA3SL.mjs} +8 -13
- package/fesm2022/wuic-framework-lib-scene3d-designer.component-DjOfzKLo.mjs +4813 -0
- package/fesm2022/wuic-framework-lib-scene3d-light-baker-gpu-C96T9uIW.mjs +303 -0
- package/fesm2022/wuic-framework-lib-scene3d-shader-effect-DjHyCAFK.mjs +2807 -0
- package/fesm2022/wuic-framework-lib-scene3d-viewer.component-Dboz1fjl.mjs +1130 -0
- package/fesm2022/{wuic-framework-lib-scheduler-list.component-CdcI1Uor.mjs → wuic-framework-lib-scheduler-list.component--wY21R3o.mjs} +169 -53
- package/fesm2022/{wuic-framework-lib-spreadsheet-list-sf.component-j1HTRI_P.mjs → wuic-framework-lib-spreadsheet-list-sf.component-Ek30J8v-.mjs} +147 -106
- package/fesm2022/{wuic-framework-lib-stimulsoft-license.service-BIyxB4pO.mjs → wuic-framework-lib-stimulsoft-license.service-CsNNSFYs.mjs} +3 -3
- package/fesm2022/wuic-framework-lib-timeline-list.component-aqCH4P0G.mjs +805 -0
- package/fesm2022/wuic-framework-lib-workflow-designer.component-DMM8BeXm.mjs +9504 -0
- package/fesm2022/{wuic-framework-lib-wuic-framework-lib-BRujEwpR.mjs → wuic-framework-lib-wuic-framework-lib-dfLTqam5.mjs} +14713 -8403
- package/fesm2022/wuic-framework-lib.mjs +1 -1
- package/package.json +5 -2
- package/styles/_wuic-tokens.scss +82 -0
- package/styles/styles.scss +160 -0
- package/types/wuic-framework-lib.d.ts +5971 -3666
- package/fesm2022/wuic-framework-lib-report-designer.component-D_qbefud.mjs +0 -194
- package/fesm2022/wuic-framework-lib-workflow-designer.component-SE_7ZogW.mjs +0 -5114
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { inject, ElementRef,
|
|
3
|
-
import * as i1
|
|
2
|
+
import { EventEmitter, Output, Input, Component, inject, ElementRef, NgZone, ChangeDetectorRef, SecurityContext, ViewChild, HostBinding } from '@angular/core';
|
|
3
|
+
import * as i1 from '@angular/forms';
|
|
4
4
|
import { FormsModule } from '@angular/forms';
|
|
5
|
-
import * as i1 from '@materia-ui/ngx-monaco-editor';
|
|
5
|
+
import * as i1$1 from '@materia-ui/ngx-monaco-editor';
|
|
6
6
|
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
|
|
7
7
|
import { filter, take } from 'rxjs/operators';
|
|
8
8
|
import { Emitter } from 'monaco-editor/esm/vs/base/common/event';
|
|
9
|
-
import { W as WtoolboxService, M as MetadataProviderService,
|
|
10
|
-
import { NgClass, NgStyle } from '@angular/common';
|
|
9
|
+
import { W as WtoolboxService, M as MetadataProviderService, g as MetadatiColonna, o as MetaInfo, U as UserInfoService, h as DataProviderService } from './wuic-framework-lib-wuic-framework-lib-dfLTqam5.mjs';
|
|
10
|
+
import { NgTemplateOutlet, NgClass, NgStyle } from '@angular/common';
|
|
11
11
|
import * as i14 from 'primeng/contextmenu';
|
|
12
12
|
import { ContextMenuModule } from 'primeng/contextmenu';
|
|
13
13
|
import * as i4 from 'primeng/tree';
|
|
14
14
|
import { TreeModule } from 'primeng/tree';
|
|
15
|
-
import * as i1$
|
|
15
|
+
import * as i1$2 from '@angular/common/http';
|
|
16
16
|
import { HttpHeaders } from '@angular/common/http';
|
|
17
17
|
import { from, combineLatest } from 'rxjs';
|
|
18
18
|
import { parseFragment } from 'parse5';
|
|
19
|
-
import * as i1$
|
|
19
|
+
import * as i1$3 from '@angular/platform-browser';
|
|
20
20
|
import * as i3 from 'primeng/api';
|
|
21
21
|
|
|
22
22
|
class SqlModel {
|
|
@@ -92,9 +92,6 @@ class Statement {
|
|
|
92
92
|
}
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
// import { editor } from 'monaco-editor';
|
|
96
|
-
// import { LinkedList } from 'monaco-editor/esm/vs/base/common/linkedList';
|
|
97
|
-
// import { MenuId, MenuRegistry } from 'monaco-editor/esm/vs/platform/actions/common/actions';
|
|
98
95
|
class SqlProvider {
|
|
99
96
|
http;
|
|
100
97
|
currents;
|
|
@@ -124,8 +121,6 @@ class SqlProvider {
|
|
|
124
121
|
this.editor = editor;
|
|
125
122
|
this.editor['_onDropIntoEditor'] = this.editor['_register'](new DragDropInteractionEmitter$1(this.editor['_contributions'], this.editor['_deliveryQueue'], this.editor, this));
|
|
126
123
|
this.editor['onDropIntoEditor'] = this.editor['_onDropIntoEditor'].event;
|
|
127
|
-
// this._onContextMenu = this._register(new InteractionEmitter(this._contributions, this._deliveryQueue));
|
|
128
|
-
// this.onContextMenu = this._onContextMenu.event;
|
|
129
124
|
this.editor['_onContextMenu'] = this.editor['_register'](new ContextMenuInteractionEmitter$1(this.editor['_contributions'], this.editor['_deliveryQueue'], this.editor, this));
|
|
130
125
|
this.editor['onContextMenu'] = this.editor['_onContextMenu'].event;
|
|
131
126
|
}
|
|
@@ -196,10 +191,8 @@ class SqlProvider {
|
|
|
196
191
|
}
|
|
197
192
|
});
|
|
198
193
|
this.editorOptions.onNodeClick = (event, dataItem) => {
|
|
199
|
-
// debugger;
|
|
200
194
|
};
|
|
201
195
|
this.editorOptions.onInit = (editor) => {
|
|
202
|
-
// debugger;
|
|
203
196
|
this.editor = editor;
|
|
204
197
|
};
|
|
205
198
|
this.editorOptions.onNodeDragStart = (event, dataItem) => {
|
|
@@ -208,50 +201,11 @@ class SqlProvider {
|
|
|
208
201
|
event.dataTransfer.setData("text/plain", JSON.stringify(dataItem));
|
|
209
202
|
};
|
|
210
203
|
this.editorOptions.onNodeDragEnd = (event, dataItem) => {
|
|
211
|
-
// debugger;
|
|
212
204
|
};
|
|
213
205
|
// monaco.languages.registerSignatureHelpProvider('sql', {
|
|
214
206
|
// signatureHelpTriggerCharacters: ['(', ','],
|
|
215
207
|
// provideSignatureHelp: (model: monaco.editor.ITextModel, position: monaco.Position, token: any, context: any) => {
|
|
216
|
-
// var lastWord = model.getWordUntilPosition(position); //getWordUntilPosition //getWordAtPosition
|
|
217
|
-
// var originalText = model.getValueInRange(model.getFullModelRange());
|
|
218
|
-
// var fullText = this.sanitize(originalText);
|
|
219
|
-
// var range: IRange = {
|
|
220
|
-
// startLineNumber: position.lineNumber,
|
|
221
|
-
// endLineNumber: position.lineNumber,
|
|
222
|
-
// startColumn: lastWord.startColumn,
|
|
223
|
-
// endColumn: lastWord.endColumn,
|
|
224
|
-
// };
|
|
225
|
-
// var contextInfo = this.getContextInfo(originalText, position);
|
|
226
|
-
// this.parseStatements();
|
|
227
|
-
// var currentStatement = this.currents.statements.find(x => x.index == contextInfo.currentStmntIndex);
|
|
228
|
-
// if (currentStatement && currentStatement.words.length > 0
|
|
229
|
-
// && currentStatement.words[0].toUpperCase() == 'INSERT INTO') {
|
|
230
208
|
// } else {
|
|
231
|
-
// return {
|
|
232
|
-
// dispose: () => { },
|
|
233
|
-
// value: {
|
|
234
|
-
// activeParameter: 0,
|
|
235
|
-
// activeSignature: 0,
|
|
236
|
-
// signatures: [{
|
|
237
|
-
// label: 'string substr(string $string, int $start [, int $length])',
|
|
238
|
-
// parameters: [
|
|
239
|
-
// {
|
|
240
|
-
// label: 'string $string',
|
|
241
|
-
// documentation: 'The input string. Must be one character or longer.'
|
|
242
|
-
// },
|
|
243
|
-
// {
|
|
244
|
-
// label: 'int $start',
|
|
245
|
-
// documentation: "If "
|
|
246
|
-
// },
|
|
247
|
-
// {
|
|
248
|
-
// label: 'int $length',
|
|
249
|
-
// documentation: 'If '
|
|
250
|
-
// }
|
|
251
|
-
// ]
|
|
252
|
-
// }]
|
|
253
|
-
// }
|
|
254
|
-
// };
|
|
255
209
|
// }
|
|
256
210
|
// }
|
|
257
211
|
// });
|
|
@@ -387,7 +341,7 @@ class SqlProvider {
|
|
|
387
341
|
* @returns SQL sanificato in singola linea.
|
|
388
342
|
*/
|
|
389
343
|
sanitize(str) {
|
|
390
|
-
return str.
|
|
344
|
+
return str.replaceAll(/(\r\n|\n|\r|\t)/gm, " ").replaceAll(/ +(?= )/g, '');
|
|
391
345
|
}
|
|
392
346
|
normalizeStatement(previousWords) {
|
|
393
347
|
let skipNext = false;
|
|
@@ -567,8 +521,8 @@ class SqlProvider {
|
|
|
567
521
|
if (!levelBlock && position.column == 1 && position.lineNumber > 1) {
|
|
568
522
|
let levelLineBlock = parsedText.filter(y => y.find(z => z.lineNumber == position.lineNumber - 1));
|
|
569
523
|
if (levelLineBlock.length > 0) {
|
|
570
|
-
levelBlock = levelLineBlock
|
|
571
|
-
charToken = levelBlock
|
|
524
|
+
levelBlock = levelLineBlock.at(-1);
|
|
525
|
+
charToken = levelBlock.at(-1);
|
|
572
526
|
}
|
|
573
527
|
}
|
|
574
528
|
else {
|
|
@@ -576,7 +530,7 @@ class SqlProvider {
|
|
|
576
530
|
}
|
|
577
531
|
if (!levelBlock) {
|
|
578
532
|
// Fallback: pick last parsed row block so autocomplete can continue safely.
|
|
579
|
-
levelBlock = parsedText
|
|
533
|
+
levelBlock = parsedText.at(-1);
|
|
580
534
|
}
|
|
581
535
|
if (!charToken) {
|
|
582
536
|
charToken = levelBlock?.[levelBlock.length - 1];
|
|
@@ -634,7 +588,8 @@ class SqlProvider {
|
|
|
634
588
|
let statementIndex = 1;
|
|
635
589
|
let txtLines = originalText.split('\r\n');
|
|
636
590
|
let nestedStatements = this.getNestedStatements(originalText);
|
|
637
|
-
let parsedInput
|
|
591
|
+
let parsedInput;
|
|
592
|
+
this.processText(originalText, position);
|
|
638
593
|
let adjacentChars = '';
|
|
639
594
|
let x = txtLines[position.lineNumber - 1];
|
|
640
595
|
if (x.length >= position.column - 1) {
|
|
@@ -661,7 +616,7 @@ class SqlProvider {
|
|
|
661
616
|
absolutePosition = endPosition;
|
|
662
617
|
previousWords = previousWords.concat(line.split(' ').filter(x => x.length > 0).map(x => x.trim().toUpperCase()).map(x => { return { word: x, statementIndx: statementIndex }; }));
|
|
663
618
|
if (cleanString != line) {
|
|
664
|
-
previousNonNestedWords = previousNonNestedWords.concat(cleanString.substring(0, Math.min(position.column, line.length)).split(' ').filter(x => x.length > 0).map(x => x.trim().toUpperCase()).map(x => { return { word: x.
|
|
619
|
+
previousNonNestedWords = previousNonNestedWords.concat(cleanString.substring(0, Math.min(position.column, line.length)).split(' ').filter(x => x.length > 0).map(x => x.trim().toUpperCase()).map(x => { return { word: x.replaceAll('§', ''), statementIndx: statementIndex }; }));
|
|
665
620
|
}
|
|
666
621
|
else {
|
|
667
622
|
previousNonNestedWords = previousNonNestedWords.concat(previousWords);
|
|
@@ -679,7 +634,7 @@ class SqlProvider {
|
|
|
679
634
|
absolutePosition = endPosition;
|
|
680
635
|
previousWords = previousWords.concat(line.substring(0, Math.min(position.column, line.length)).split(' ').filter(x => x.length > 0).map(x => x.trim().toUpperCase()).map(x => { return { word: x, statementIndx: statementIndex }; }));
|
|
681
636
|
if (cleanString != line) {
|
|
682
|
-
previousNonNestedWords = previousNonNestedWords.concat(cleanString.substring(0, Math.min(position.column, line.length)).split(' ').filter(x => x.length > 0).map(x => x.trim().toUpperCase()).map(x => { return { word: x.
|
|
637
|
+
previousNonNestedWords = previousNonNestedWords.concat(cleanString.substring(0, Math.min(position.column, line.length)).split(' ').filter(x => x.length > 0).map(x => x.trim().toUpperCase()).map(x => { return { word: x.replaceAll('§', ''), statementIndx: statementIndex }; }));
|
|
683
638
|
}
|
|
684
639
|
else {
|
|
685
640
|
previousNonNestedWords = previousNonNestedWords.concat(previousWords);
|
|
@@ -734,8 +689,8 @@ class SqlProvider {
|
|
|
734
689
|
let normalized = this.normalizeStatement(contextWords.map((x, indx) => { return { token: x.word, tokenIndx: indx }; }));
|
|
735
690
|
currentStatementWords = normalized.map(x => x.token.replace("(", ""));
|
|
736
691
|
let keywordIndx = -1;
|
|
737
|
-
let matchingKeyWords = currentStatementWords.filter(x => sqlKeywords.
|
|
738
|
-
let matchingKeyWord = matchingKeyWords.length > 0 ? matchingKeyWords
|
|
692
|
+
let matchingKeyWords = currentStatementWords.filter(x => sqlKeywords.includes(x.toUpperCase()));
|
|
693
|
+
let matchingKeyWord = matchingKeyWords.length > 0 ? matchingKeyWords.at(-1) : '';
|
|
739
694
|
if (matchingKeyWord) {
|
|
740
695
|
keywordIndx = sqlKeywords.indexOf(matchingKeyWord.toUpperCase());
|
|
741
696
|
}
|
|
@@ -765,7 +720,7 @@ class SqlProvider {
|
|
|
765
720
|
let currentSubStmntWords = subStmnt.statement.words;
|
|
766
721
|
switch (subStmnt.key) {
|
|
767
722
|
case 'DECLARE':
|
|
768
|
-
if (currentSubStmntWords[currentSubStmntWords.length - 1].token.
|
|
723
|
+
if (!currentSubStmntWords[currentSubStmntWords.length - 1].token.includes('@')) {
|
|
769
724
|
return autocompleteContext.declaration;
|
|
770
725
|
}
|
|
771
726
|
else {
|
|
@@ -786,37 +741,38 @@ class SqlProvider {
|
|
|
786
741
|
console.log('table!!!');
|
|
787
742
|
return autocompleteContext.table;
|
|
788
743
|
});
|
|
789
|
-
case 'SELECT':
|
|
744
|
+
case 'SELECT': {
|
|
790
745
|
let previousWord;
|
|
791
746
|
let nextWord;
|
|
792
747
|
let previousWords = currentSubStmntWords.filter(x => x.tokenIndx < contextInfo.position.column - 1);
|
|
793
748
|
if (previousWords.length > 0) {
|
|
794
|
-
previousWord = previousWords
|
|
749
|
+
previousWord = previousWords.at(-1);
|
|
795
750
|
}
|
|
796
751
|
let nextWords = currentSubStmntWords.filter(x => x.tokenIndx >= contextInfo.position.column - 1);
|
|
797
752
|
if (nextWords.length > 0) {
|
|
798
753
|
nextWord = nextWords[0];
|
|
799
754
|
}
|
|
800
|
-
let selectLastToken = (currentSubStmntWords
|
|
801
|
-
if (selectLastToken.
|
|
755
|
+
let selectLastToken = (currentSubStmntWords.at(-1)?.token || '').trim();
|
|
756
|
+
if (selectLastToken.includes('.')) {
|
|
802
757
|
// Alias-qualified column completion (e.g. AppCit. or AppCit.Sta)
|
|
803
758
|
return autocompleteContext.columnWhereRestricted;
|
|
804
759
|
}
|
|
805
760
|
if (contextInfo.adjacentChars == '()' || (previousWord && nextWord && previousWord.token[previousWord.token.length - 1] == '(' && nextWord.token[0] == ')')) {
|
|
806
761
|
return autocompleteContext.columnSelect;
|
|
807
762
|
}
|
|
808
|
-
else if (currentSubStmntWords
|
|
763
|
+
else if (currentSubStmntWords.at(-1).token.includes(',') || currentSubStmntWords.at(-1).token.toUpperCase() == 'SELECT') {
|
|
809
764
|
return autocompleteContext.columnSelect;
|
|
810
765
|
}
|
|
811
766
|
else {
|
|
812
767
|
return autocompleteContext.statementFrom;
|
|
813
768
|
}
|
|
769
|
+
}
|
|
814
770
|
case 'FROM':
|
|
815
771
|
// After at least one table in FROM, allow partial clause prefixes
|
|
816
772
|
// (e.g. "I" => "INNER JOIN") while keeping schema suggestions otherwise.
|
|
817
773
|
return this.evaluateAutocompleteContext(contextInfo, subStmnt.statement, false, subStmnt.statement.sqlTables.length > 0 ? autocompleteContext.statementWhereJoin : null);
|
|
818
774
|
case 'AS':
|
|
819
|
-
if (matchingKeyWords.length > 1 && matchingKeyWords
|
|
775
|
+
if (matchingKeyWords.length > 1 && matchingKeyWords.at(-2) == 'WITH') {
|
|
820
776
|
return autocompleteContext.statementSelect;
|
|
821
777
|
}
|
|
822
778
|
else {
|
|
@@ -832,16 +788,18 @@ class SqlProvider {
|
|
|
832
788
|
if (schemaTableAliasToken) {
|
|
833
789
|
let schemaAndTableOrAlias = schemaTableAliasToken.token.split('.').filter(x => x.length > 0);
|
|
834
790
|
if (schemaAndTableOrAlias.length > 0) {
|
|
791
|
+
// intentionally empty: schema part already typed, fall through to next token checks
|
|
835
792
|
}
|
|
836
793
|
else {
|
|
837
794
|
return autocompleteContext.schema;
|
|
838
795
|
}
|
|
839
796
|
if (schemaAndTableOrAlias.length >= 1) {
|
|
797
|
+
// intentionally empty: table part already typed, fall through to join context checks
|
|
840
798
|
}
|
|
841
799
|
else {
|
|
842
800
|
return autocompleteContext.table;
|
|
843
801
|
}
|
|
844
|
-
if (currentSubStmntWords
|
|
802
|
+
if (currentSubStmntWords.at(-1).token.toUpperCase() == subStmnt.key.toUpperCase()) {
|
|
845
803
|
return autocompleteContext.schemaJoin;
|
|
846
804
|
}
|
|
847
805
|
else {
|
|
@@ -862,28 +820,24 @@ class SqlProvider {
|
|
|
862
820
|
case 'GROUP BY':
|
|
863
821
|
return autocompleteContext.columnGroupBy;
|
|
864
822
|
case 'SET':
|
|
865
|
-
if (currentSubStmntWords
|
|
823
|
+
if (currentSubStmntWords.at(-1).token.trim().toUpperCase() == 'SET') {
|
|
866
824
|
let updtStmnt = currentStmnt.subStatements.find(x => x.key == 'UPDATE');
|
|
867
825
|
if (updtStmnt && updtStmnt.statement.words.length > 1) {
|
|
868
|
-
// this.currents.table = updtStmnt.statement.sqlTables[updtStmnt.statement.sqlTables.length - 1];
|
|
869
826
|
return autocompleteContext.columnWhere;
|
|
870
827
|
}
|
|
871
828
|
else {
|
|
872
829
|
return autocompleteContext.variable;
|
|
873
830
|
}
|
|
874
831
|
}
|
|
832
|
+
else if (currentSubStmntWords.at(-1).token.trim().endsWith(',')) {
|
|
833
|
+
return autocompleteContext.columnWhere;
|
|
834
|
+
}
|
|
835
|
+
else if (currentSubStmntWords.at(-1).token.trim() == '=') {
|
|
836
|
+
return autocompleteContext.columnWhereAndVariables;
|
|
837
|
+
}
|
|
875
838
|
else {
|
|
876
|
-
|
|
877
|
-
return autocompleteContext.columnWhere;
|
|
878
|
-
}
|
|
879
|
-
else if (currentSubStmntWords[currentSubStmntWords.length - 1].token.trim() == '=') {
|
|
880
|
-
return autocompleteContext.columnWhereAndVariables;
|
|
881
|
-
}
|
|
882
|
-
else {
|
|
883
|
-
return autocompleteContext.assignmentOperator;
|
|
884
|
-
}
|
|
839
|
+
return autocompleteContext.assignmentOperator;
|
|
885
840
|
}
|
|
886
|
-
break;
|
|
887
841
|
}
|
|
888
842
|
}
|
|
889
843
|
}
|
|
@@ -892,7 +846,7 @@ class SqlProvider {
|
|
|
892
846
|
}
|
|
893
847
|
evaluateAutocompleteContext(contextInfo, statement, skipAs = false, defaultAutoCtx = null, contextDelegate = null) {
|
|
894
848
|
const lastToken = statement?.words?.length
|
|
895
|
-
? (statement.words
|
|
849
|
+
? (statement.words.at(-1).token || '').trim()
|
|
896
850
|
: '';
|
|
897
851
|
const lastDotIndex = lastToken.lastIndexOf('.');
|
|
898
852
|
if (lastDotIndex >= 0) {
|
|
@@ -907,18 +861,14 @@ class SqlProvider {
|
|
|
907
861
|
return defaultAutoCtx;
|
|
908
862
|
}
|
|
909
863
|
}
|
|
864
|
+
else if (statement.sqlTables.length > 0) {
|
|
865
|
+
return autocompleteContext.columnWhereRestricted;
|
|
866
|
+
}
|
|
867
|
+
else if (!skipAs) {
|
|
868
|
+
return autocompleteContext.table;
|
|
869
|
+
}
|
|
910
870
|
else {
|
|
911
|
-
|
|
912
|
-
return autocompleteContext.columnWhereRestricted;
|
|
913
|
-
}
|
|
914
|
-
else {
|
|
915
|
-
if (!skipAs) {
|
|
916
|
-
return autocompleteContext.table;
|
|
917
|
-
}
|
|
918
|
-
else {
|
|
919
|
-
return autocompleteContext.tableNoAs;
|
|
920
|
-
}
|
|
921
|
-
}
|
|
871
|
+
return autocompleteContext.tableNoAs;
|
|
922
872
|
}
|
|
923
873
|
}
|
|
924
874
|
else {
|
|
@@ -957,7 +907,7 @@ class SqlProvider {
|
|
|
957
907
|
* @returns Context specifico WHERE.
|
|
958
908
|
*/
|
|
959
909
|
evaluateAutocompleteWhereContext(currents, currentStatementWords, whereSubStatemnt) {
|
|
960
|
-
let lastToken = currentStatementWords
|
|
910
|
+
let lastToken = currentStatementWords.at(-1).trim().toUpperCase();
|
|
961
911
|
if (lastToken == 'AND' || lastToken == 'WHERE') {
|
|
962
912
|
return autocompleteContext.columnWhere;
|
|
963
913
|
}
|
|
@@ -1003,7 +953,6 @@ class SqlProvider {
|
|
|
1003
953
|
}
|
|
1004
954
|
let subStatement;
|
|
1005
955
|
let nestedStatements = this.getNestedStatements(statement);
|
|
1006
|
-
let z = nestedStatements[0];
|
|
1007
956
|
statementObj.statement = this.removeNestedStatements(statement, nestedStatements);
|
|
1008
957
|
let previousWords = statement.split(' ').filter(x => x.length > 0).map((x, splitIndx) => {
|
|
1009
958
|
return {
|
|
@@ -1023,8 +972,7 @@ class SqlProvider {
|
|
|
1023
972
|
const elementObj = sanitizedPreviousWords[indx];
|
|
1024
973
|
let element = elementObj.token;
|
|
1025
974
|
let indxOffset = 0;
|
|
1026
|
-
for (
|
|
1027
|
-
nested = nestedStatements[nestedIndex];
|
|
975
|
+
for (nested of nestedStatements) {
|
|
1028
976
|
if (nested.length > 1 && elementObj.tokenIndx >= nested.index && elementObj.tokenIndx <= nested.index + nested[1].length) {
|
|
1029
977
|
this.parseStatementRecursive(nested[1], indx, subStatement);
|
|
1030
978
|
let firstWordOutsideNested = sanitizedPreviousWords.find(x => x.tokenIndx > nested.index + nested[1].length);
|
|
@@ -1035,7 +983,7 @@ class SqlProvider {
|
|
|
1035
983
|
indx = indxOffset;
|
|
1036
984
|
continue;
|
|
1037
985
|
}
|
|
1038
|
-
if (sqlKeywords.
|
|
986
|
+
if (sqlKeywords.includes(element.toUpperCase())) {
|
|
1039
987
|
currentStatementTag = element;
|
|
1040
988
|
subStatement = new Statement(element, Object.keys(statementObj.subStatements).length + 1);
|
|
1041
989
|
subStatement.words.push(elementObj);
|
|
@@ -1102,7 +1050,7 @@ class SqlProvider {
|
|
|
1102
1050
|
}
|
|
1103
1051
|
return col;
|
|
1104
1052
|
});
|
|
1105
|
-
if (!this.currents.globalTables.
|
|
1053
|
+
if (!this.currents.globalTables.some(x => x.table.toUpperCase() == table.table.toUpperCase())) {
|
|
1106
1054
|
this.currents.globalTables.push(table);
|
|
1107
1055
|
}
|
|
1108
1056
|
return true;
|
|
@@ -1116,12 +1064,12 @@ class SqlProvider {
|
|
|
1116
1064
|
* @returns `true` se e stata aggiunta una dichiarazione.
|
|
1117
1065
|
*/
|
|
1118
1066
|
populateDeclarations(statement, found) {
|
|
1119
|
-
if (statement.
|
|
1067
|
+
if (statement.includes('@')) {
|
|
1120
1068
|
let decStr = statement.trim().toUpperCase().split(' ');
|
|
1121
1069
|
if (decStr.length >= 3) {
|
|
1122
1070
|
let dec = { paramName: decStr[1], paramtype: decStr[2], usertype: null };
|
|
1123
1071
|
let type;
|
|
1124
|
-
if (decStr[2].
|
|
1072
|
+
if (decStr[2].includes('.')) {
|
|
1125
1073
|
type = this.userDefTypes.find(x => x.table.toUpperCase() == decStr[2].split('.')[1] && x.schema.toUpperCase() == decStr[2].split('.')[0]);
|
|
1126
1074
|
}
|
|
1127
1075
|
else {
|
|
@@ -1130,7 +1078,7 @@ class SqlProvider {
|
|
|
1130
1078
|
if (type) {
|
|
1131
1079
|
dec.usertype = type;
|
|
1132
1080
|
}
|
|
1133
|
-
if (!this.currents.declarations.
|
|
1081
|
+
if (!this.currents.declarations.some(x => x.paramName.toUpperCase() == dec.paramName.toUpperCase())) {
|
|
1134
1082
|
this.currents.declarations.push(dec);
|
|
1135
1083
|
}
|
|
1136
1084
|
found = true;
|
|
@@ -1148,12 +1096,12 @@ class SqlProvider {
|
|
|
1148
1096
|
populateCurrentColumns(element, stmntIndx, statementObj, subStatement) {
|
|
1149
1097
|
let selStmnt = statementObj.subStatements.find(x => x.key == 'SELECT');
|
|
1150
1098
|
if (selStmnt) {
|
|
1151
|
-
let columns = selStmnt.statement.statement.replace('SELECT', '').trim().toUpperCase().split(',').filter(
|
|
1099
|
+
let columns = selStmnt.statement.statement.replace('SELECT', '').trim().toUpperCase().split(',').filter(Boolean).map(x => {
|
|
1152
1100
|
let col = null;
|
|
1153
1101
|
x = x.trim();
|
|
1154
1102
|
let ownerTbl;
|
|
1155
1103
|
let isAlias = false;
|
|
1156
|
-
if (x.
|
|
1104
|
+
if (x.includes('.')) {
|
|
1157
1105
|
let schema = x.split('.')[0].replace('[', '').replace(']', '').replace('(', '').replace(')', '').toUpperCase();
|
|
1158
1106
|
let ownerSchema = this.schemas.find(s => s.schema.toUpperCase() === schema);
|
|
1159
1107
|
if (!ownerSchema) {
|
|
@@ -1205,19 +1153,19 @@ class SqlProvider {
|
|
|
1205
1153
|
}
|
|
1206
1154
|
}
|
|
1207
1155
|
return col;
|
|
1208
|
-
}).filter(
|
|
1156
|
+
}).filter(Boolean);
|
|
1209
1157
|
columns.forEach(x => {
|
|
1210
1158
|
let normalizedColumn = x?.column?.column?.toUpperCase();
|
|
1211
1159
|
if (!normalizedColumn) {
|
|
1212
1160
|
return;
|
|
1213
1161
|
}
|
|
1214
1162
|
if (statementObj) {
|
|
1215
|
-
if (!statementObj.sqlColumns.
|
|
1163
|
+
if (!statementObj.sqlColumns.some(c => c?.column?.column?.toUpperCase() == normalizedColumn)) {
|
|
1216
1164
|
statementObj.sqlColumns.push(x);
|
|
1217
1165
|
}
|
|
1218
1166
|
}
|
|
1219
1167
|
if (subStatement) {
|
|
1220
|
-
if (!subStatement.sqlColumns.
|
|
1168
|
+
if (!subStatement.sqlColumns.some(c => c?.column?.column?.toUpperCase() == normalizedColumn)) {
|
|
1221
1169
|
subStatement.sqlColumns.push(x);
|
|
1222
1170
|
}
|
|
1223
1171
|
}
|
|
@@ -1279,7 +1227,7 @@ class SqlProvider {
|
|
|
1279
1227
|
populateCurrentSchemaTables(element, stmntIndx, found, statementObj, subStatement) {
|
|
1280
1228
|
let tblStr = element.trim().toUpperCase().split(' ');
|
|
1281
1229
|
if (tblStr.length > 0 && tblStr[0].split('.').length > 1) {
|
|
1282
|
-
let identifierParts = tblStr[0].replace('[', '').replace(']', '').split('.').filter(
|
|
1230
|
+
let identifierParts = tblStr[0].replace('[', '').replace(']', '').split('.').filter(Boolean);
|
|
1283
1231
|
let schemaName = '';
|
|
1284
1232
|
let tableName = '';
|
|
1285
1233
|
let dbName = '';
|
|
@@ -1331,11 +1279,11 @@ class SqlProvider {
|
|
|
1331
1279
|
let aliasCandidate = identifierParts[0] || '';
|
|
1332
1280
|
let matchTbl = statementObj.sqlTables.find(x => x.usedAlias.toUpperCase() === aliasCandidate.toUpperCase());
|
|
1333
1281
|
if (matchTbl) {
|
|
1334
|
-
if (!subStatement.sqlTables.
|
|
1282
|
+
if (!subStatement.sqlTables.some(x => x.table.table.toUpperCase() == matchTbl.table.table.toUpperCase() && x.table.schema.toUpperCase() == matchTbl.table.schema.toUpperCase())) {
|
|
1335
1283
|
subStatement.sqlTables.push({ table: matchTbl.table, usedAlias: matchTbl.usedAlias });
|
|
1336
1284
|
}
|
|
1337
1285
|
let matchSchema = statementObj.sqlModels.find(x => matchTbl.table.schema.toUpperCase() === x.schema.toUpperCase());
|
|
1338
|
-
if (matchSchema && !subStatement.sqlModels.
|
|
1286
|
+
if (matchSchema && !subStatement.sqlModels.some(x => matchTbl.table.schema.toUpperCase() === x.schema.toUpperCase())) {
|
|
1339
1287
|
subStatement.sqlModels.push(matchSchema);
|
|
1340
1288
|
}
|
|
1341
1289
|
}
|
|
@@ -1465,7 +1413,7 @@ class SqlProvider {
|
|
|
1465
1413
|
}
|
|
1466
1414
|
].filter(x => {
|
|
1467
1415
|
if (currentStatement) {
|
|
1468
|
-
if (currentStatement.subStatements.
|
|
1416
|
+
if (currentStatement.subStatements.some(s => !x.label.includes('JOIN') && x.label == s.key.toUpperCase())) {
|
|
1469
1417
|
return false;
|
|
1470
1418
|
}
|
|
1471
1419
|
}
|
|
@@ -1502,7 +1450,7 @@ class SqlProvider {
|
|
|
1502
1450
|
});
|
|
1503
1451
|
}
|
|
1504
1452
|
ci = ci.concat(selectFields.filter(x => {
|
|
1505
|
-
if (context == autocompleteContext.columnSelect && currentStatement.sqlColumns.
|
|
1453
|
+
if (context == autocompleteContext.columnSelect && currentStatement.sqlColumns.some(c => c?.column?.column?.toUpperCase() == x.column.toUpperCase())) {
|
|
1506
1454
|
return false;
|
|
1507
1455
|
}
|
|
1508
1456
|
return true;
|
|
@@ -1515,7 +1463,7 @@ class SqlProvider {
|
|
|
1515
1463
|
.replace('(', '')
|
|
1516
1464
|
.replace(')', '')
|
|
1517
1465
|
.split('.')
|
|
1518
|
-
.filter(
|
|
1466
|
+
.filter(Boolean)
|
|
1519
1467
|
.map(y => y.trim().toUpperCase());
|
|
1520
1468
|
if (!scaffoldedOnly && restrictedParts.length >= 3) {
|
|
1521
1469
|
let dbFilter = restrictedParts[0];
|
|
@@ -1526,7 +1474,7 @@ class SqlProvider {
|
|
|
1526
1474
|
&& x.table.toUpperCase() === tableFilter
|
|
1527
1475
|
&& (!dbFilter || colDb === dbFilter);
|
|
1528
1476
|
}
|
|
1529
|
-
if (restrictedParts.length >= 1 && restrictedToken.
|
|
1477
|
+
if (restrictedParts.length >= 1 && restrictedToken.includes('.')) {
|
|
1530
1478
|
let aliasFilter = restrictedParts[0];
|
|
1531
1479
|
return (x.tableAlias || '').toUpperCase() === aliasFilter;
|
|
1532
1480
|
}
|
|
@@ -1538,8 +1486,8 @@ class SqlProvider {
|
|
|
1538
1486
|
return true;
|
|
1539
1487
|
}).map(x => {
|
|
1540
1488
|
let restrictedToken = (currentSubStatement?.words?.[currentSubStatement.words.length - 1]?.token || '').trim();
|
|
1541
|
-
let isAliasQualifiedCompletion = context == autocompleteContext.columnWhereRestricted && restrictedToken.
|
|
1542
|
-
let aliasStmnts = currentStatement.subStatements.filter(s => s.key == 'FROM' || s.key.
|
|
1489
|
+
let isAliasQualifiedCompletion = context == autocompleteContext.columnWhereRestricted && restrictedToken.includes('.');
|
|
1490
|
+
let aliasStmnts = currentStatement.subStatements.filter(s => s.key == 'FROM' || s.key.includes('JOIN') || s.key == 'ON');
|
|
1543
1491
|
let aliasStmnt = aliasStmnts.find(y => y.statement.sqlTables.length > 0 &&
|
|
1544
1492
|
y.statement.sqlTables[0].table.schema.toUpperCase() == x.schema.toUpperCase() &&
|
|
1545
1493
|
y.statement.sqlTables[0].table.table.toUpperCase() == x.table.toUpperCase());
|
|
@@ -1554,11 +1502,13 @@ class SqlProvider {
|
|
|
1554
1502
|
else if (aliasStmnt) {
|
|
1555
1503
|
str = `${aliasStmnt.statement.sqlTables[0].usedAlias}.${x.column}`;
|
|
1556
1504
|
}
|
|
1557
|
-
|
|
1505
|
+
const columnDescriptionSuffix = x.columnDescription ? ` - ${x.columnDescription}` : '';
|
|
1506
|
+
let longDescr = `${x.column} (${x.type}, ${x.isNullable ? 'null' : 'not null'})${columnDescriptionSuffix}`;
|
|
1507
|
+
let nonPkeyKind = (x.parentRelation) ? monaco.languages.CompletionItemKind.Reference : monaco.languages.CompletionItemKind.Field;
|
|
1558
1508
|
return {
|
|
1559
1509
|
documentation: longDescr,
|
|
1560
1510
|
insertText: str,
|
|
1561
|
-
kind: x.isPkey ? monaco.languages.CompletionItemKind.Keyword :
|
|
1511
|
+
kind: x.isPkey ? monaco.languages.CompletionItemKind.Keyword : nonPkeyKind,
|
|
1562
1512
|
label: longDescr,
|
|
1563
1513
|
range: range
|
|
1564
1514
|
};
|
|
@@ -1587,21 +1537,6 @@ class SqlProvider {
|
|
|
1587
1537
|
break;
|
|
1588
1538
|
case autocompleteContext.comparisonOperator:
|
|
1589
1539
|
ci = comparisonOperators.filter(x => {
|
|
1590
|
-
// if (this.currents.column && this.currents.column.type) {
|
|
1591
|
-
// var sqlType = sqlTypes.find(x => x.name.toUpperCase() == this.currents.column.type.toUpperCase());
|
|
1592
|
-
// if (sqlType) {
|
|
1593
|
-
// switch (true) {
|
|
1594
|
-
// case sqlType.isString:
|
|
1595
|
-
// return ['=', '<>', '!=', '!', 'NOT', 'LIKE', 'IN', 'IS', 'IS NOT NULL'].indexOf(x) >= 0;
|
|
1596
|
-
// case sqlType.isNumber:
|
|
1597
|
-
// return ['=', '<>', '<', '<=', '>', '>=', '!=', '!', 'NOT', 'BETWEEN', 'IN', 'IS', 'IS NOT NULL'].indexOf(x) >= 0;
|
|
1598
|
-
// case sqlType.isBool:
|
|
1599
|
-
// return ['=', '<>', '!=', '!', 'NOT', 'IS', 'IS NOT NULL'].indexOf(x) >= 0;
|
|
1600
|
-
// default:
|
|
1601
|
-
// return true;
|
|
1602
|
-
// }
|
|
1603
|
-
// }
|
|
1604
|
-
// }
|
|
1605
1540
|
return true;
|
|
1606
1541
|
}).map(x => {
|
|
1607
1542
|
return {
|
|
@@ -1659,17 +1594,17 @@ class SqlProvider {
|
|
|
1659
1594
|
break;
|
|
1660
1595
|
case autocompleteContext.table:
|
|
1661
1596
|
case autocompleteContext.tableNoAs:
|
|
1662
|
-
case autocompleteContext.tableJoin:
|
|
1597
|
+
case autocompleteContext.tableJoin: {
|
|
1663
1598
|
let rawScopedToken = (currentSubStatement?.words?.[currentSubStatement.words.length - 1]?.token || '').trim();
|
|
1664
|
-
let currentSchema = currentStatement.sqlModels
|
|
1599
|
+
let currentSchema = currentStatement.sqlModels.at(-1)?.schema;
|
|
1665
1600
|
let scopedParts = rawScopedToken
|
|
1666
1601
|
.replace('[', '')
|
|
1667
1602
|
.replace(']', '')
|
|
1668
1603
|
.replace('(', '')
|
|
1669
1604
|
.replace(')', '')
|
|
1670
1605
|
.split('.')
|
|
1671
|
-
.filter(
|
|
1672
|
-
let isDbScopedSchema = !scaffoldedOnly && rawScopedToken.
|
|
1606
|
+
.filter(Boolean);
|
|
1607
|
+
let isDbScopedSchema = !scaffoldedOnly && rawScopedToken.includes('.');
|
|
1673
1608
|
if (context == autocompleteContext.tableJoin) {
|
|
1674
1609
|
currentStatement.sqlTables.forEach(tbl => {
|
|
1675
1610
|
this.parseTblRelations(currentStatement, tbl, ci, range, currentSchema);
|
|
@@ -1739,13 +1674,10 @@ class SqlProvider {
|
|
|
1739
1674
|
let txtLine = originalText.split('\n')[range.startLineNumber - 1];
|
|
1740
1675
|
let qualifiedSchema = scopedModel ? this.getModelSchemaName(scopedModel) : x.schema;
|
|
1741
1676
|
let startCol = txtLine.toUpperCase().indexOf(`${qualifiedSchema.toUpperCase()}.`);
|
|
1742
|
-
let tokenHasQualifier = rawScopedToken.
|
|
1743
|
-
let
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
: `${context != autocompleteContext.tableNoAs ? `${qualifiedSchema}.` : ''}${x.table}`)
|
|
1747
|
-
:
|
|
1748
|
-
``;
|
|
1677
|
+
let tokenHasQualifier = rawScopedToken.includes('.');
|
|
1678
|
+
let schemaPrefix = context != autocompleteContext.tableNoAs ? `${qualifiedSchema}.` : '';
|
|
1679
|
+
let qualifiedInsert = tokenHasQualifier ? x.table : `${schemaPrefix}${x.table}`;
|
|
1680
|
+
let insertTxt = (!x.alias || context == autocompleteContext.tableNoAs) ? qualifiedInsert : ``;
|
|
1749
1681
|
return {
|
|
1750
1682
|
documentation: x.table,
|
|
1751
1683
|
insertText: insertTxt,
|
|
@@ -1764,16 +1696,19 @@ class SqlProvider {
|
|
|
1764
1696
|
};
|
|
1765
1697
|
}
|
|
1766
1698
|
catch (error) {
|
|
1699
|
+
// Malformed table suggestion entry — skip this completion item
|
|
1700
|
+
// (return null) instead of breaking the whole autocomplete list.
|
|
1767
1701
|
debugger;
|
|
1768
1702
|
return null;
|
|
1769
1703
|
}
|
|
1770
1704
|
});
|
|
1771
1705
|
}
|
|
1772
1706
|
break;
|
|
1773
|
-
|
|
1707
|
+
}
|
|
1708
|
+
case autocompleteContext.declaration: {
|
|
1774
1709
|
let paramName = '@Param';
|
|
1775
1710
|
let paramIndx = 1;
|
|
1776
|
-
while (this.currents.declarations.
|
|
1711
|
+
while (this.currents.declarations.some(x => x.paramName.toUpperCase() === `${paramName}${paramIndx.toString()}`.toUpperCase())) {
|
|
1777
1712
|
paramIndx++;
|
|
1778
1713
|
}
|
|
1779
1714
|
paramName = `@Param${paramIndx}`;
|
|
@@ -1787,13 +1722,19 @@ class SqlProvider {
|
|
|
1787
1722
|
}
|
|
1788
1723
|
];
|
|
1789
1724
|
break;
|
|
1725
|
+
}
|
|
1790
1726
|
case autocompleteContext.sqlType:
|
|
1791
1727
|
ci = sqlTypes.map(x => {
|
|
1728
|
+
let sizeHint = '';
|
|
1729
|
+
if (x.isString && x.name.toUpperCase().includes("VAR")) {
|
|
1730
|
+
sizeHint = '(100)';
|
|
1731
|
+
}
|
|
1732
|
+
else if (x.isNumber && x.name.toUpperCase() == "DECIMAL") {
|
|
1733
|
+
sizeHint = '(18, 2)';
|
|
1734
|
+
}
|
|
1792
1735
|
return {
|
|
1793
1736
|
documentation: x.name,
|
|
1794
|
-
insertText: `${x.name} ${
|
|
1795
|
-
x.isNumber && x.name.toUpperCase() == "DECIMAL" ? '(18, 2)' :
|
|
1796
|
-
''}= null;\n`,
|
|
1737
|
+
insertText: `${x.name} ${sizeHint}= null;\n`,
|
|
1797
1738
|
kind: monaco.languages.CompletionItemKind.TypeParameter,
|
|
1798
1739
|
label: x.name,
|
|
1799
1740
|
range: range
|
|
@@ -1843,7 +1784,7 @@ class SqlProvider {
|
|
|
1843
1784
|
return modelAny.database;
|
|
1844
1785
|
}
|
|
1845
1786
|
let schema = model.schema || '';
|
|
1846
|
-
if (schema.
|
|
1787
|
+
if (schema.includes('.')) {
|
|
1847
1788
|
return schema.split('.')[0];
|
|
1848
1789
|
}
|
|
1849
1790
|
return null;
|
|
@@ -1858,7 +1799,7 @@ class SqlProvider {
|
|
|
1858
1799
|
return '';
|
|
1859
1800
|
}
|
|
1860
1801
|
let schema = model.schema || '';
|
|
1861
|
-
if (schema.
|
|
1802
|
+
if (schema.includes('.')) {
|
|
1862
1803
|
return schema.split('.').slice(1).join('.');
|
|
1863
1804
|
}
|
|
1864
1805
|
return schema;
|
|
@@ -1907,7 +1848,7 @@ class SqlProvider {
|
|
|
1907
1848
|
if (rel) {
|
|
1908
1849
|
relations.push(rel);
|
|
1909
1850
|
}
|
|
1910
|
-
if (col.parentRelation
|
|
1851
|
+
if (col.parentRelation?.tableFK.toUpperCase() == tbl.table.table.toUpperCase() && col.parentRelation.schemaFK.toUpperCase() == tbl.table.schema.toUpperCase() && (!schemaFilter || col.parentRelation.schemaPK.toUpperCase() == schemaFilter.toUpperCase())) {
|
|
1911
1852
|
relations.push(col.parentRelation);
|
|
1912
1853
|
}
|
|
1913
1854
|
});
|
|
@@ -1919,19 +1860,21 @@ class SqlProvider {
|
|
|
1919
1860
|
let aliasTblFK;
|
|
1920
1861
|
if (rel.schemaFK == tbl.table.schema && rel.tableFK == tbl.table.table) {
|
|
1921
1862
|
aliasTblPK = tblPK.alias ? tblPK.alias : `${tblPK.schema}.${tblPK.table}`;
|
|
1922
|
-
|
|
1863
|
+
const fkFallbackAlias = tblFK.alias ? tblFK.alias : `${tblFK.schema}.${tblFK.table}`;
|
|
1864
|
+
aliasTblFK = tbl.usedAlias ? tbl.usedAlias : fkFallbackAlias;
|
|
1923
1865
|
joinSuggestion = `\t${rel.schemaPK}.${rel.tablePK} AS ${aliasTblPK} ON \n\t${aliasTblFK}.${rel.columnFK} = \n\t${aliasTblPK}.${rel.columnPK}`;
|
|
1924
1866
|
}
|
|
1925
1867
|
else {
|
|
1926
|
-
|
|
1868
|
+
const pkFallbackAlias = tblPK.alias ? tblPK.alias : `${tblPK.schema}.${tblPK.table}`;
|
|
1869
|
+
aliasTblPK = tbl.usedAlias ? tbl.usedAlias : pkFallbackAlias;
|
|
1927
1870
|
aliasTblFK = tblFK.alias ? tblFK.alias : `${tblFK.schema}.${tblFK.table}`;
|
|
1928
1871
|
joinSuggestion = `\t${rel.schemaFK}.${rel.tableFK} AS ${aliasTblFK} ON \n\t${aliasTblPK}.${rel.columnPK} = \n\t${aliasTblFK}.${rel.columnFK}`;
|
|
1929
1872
|
}
|
|
1930
1873
|
ci.push({
|
|
1931
|
-
documentation: joinSuggestion.
|
|
1874
|
+
documentation: joinSuggestion.replaceAll(/(\r\n|\n|\r|\t)/gm, ' '),
|
|
1932
1875
|
insertText: `\n${joinSuggestion}`,
|
|
1933
1876
|
kind: monaco.languages.CompletionItemKind.Reference,
|
|
1934
|
-
label: joinSuggestion.
|
|
1877
|
+
label: joinSuggestion.replaceAll(/(\r\n|\n|\r|\t)/gm, ' '),
|
|
1935
1878
|
range: range
|
|
1936
1879
|
});
|
|
1937
1880
|
});
|
|
@@ -1946,10 +1889,9 @@ class SqlProvider {
|
|
|
1946
1889
|
let tbl;
|
|
1947
1890
|
this.schemas.forEach(schema => {
|
|
1948
1891
|
schema.tables.forEach(table => {
|
|
1949
|
-
if (table.alias
|
|
1892
|
+
if (table.alias?.toUpperCase() === alias.toUpperCase()) {
|
|
1950
1893
|
tbl = table;
|
|
1951
1894
|
tbl.schemaObj = schema;
|
|
1952
|
-
return;
|
|
1953
1895
|
}
|
|
1954
1896
|
});
|
|
1955
1897
|
if (tbl) {
|
|
@@ -1991,12 +1933,12 @@ let DragDropInteractionEmitter$1 = class DragDropInteractionEmitter extends Emit
|
|
|
1991
1933
|
let getDbPrefixFor = (schemaName, tableName, preferredDbName) => {
|
|
1992
1934
|
let owner = providerSchemas.find(m => {
|
|
1993
1935
|
if (preferredDbName) {
|
|
1994
|
-
let mDb = (m.database || ((m.schema || '').
|
|
1936
|
+
let mDb = (m.database || ((m.schema || '').includes('.') ? (m.schema || '').split('.')[0] : '') || '').toUpperCase();
|
|
1995
1937
|
if (mDb !== preferredDbName.toUpperCase()) {
|
|
1996
1938
|
return false;
|
|
1997
1939
|
}
|
|
1998
1940
|
}
|
|
1999
|
-
let mSchema = (m.schema || '').
|
|
1941
|
+
let mSchema = (m.schema || '').includes('.') ? (m.schema || '').split('.').slice(1).join('.') : (m.schema || '');
|
|
2000
1942
|
let schemaMatch = mSchema.toUpperCase() === (schemaName || '').toUpperCase();
|
|
2001
1943
|
if (!schemaMatch) {
|
|
2002
1944
|
return false;
|
|
@@ -2009,13 +1951,13 @@ let DragDropInteractionEmitter$1 = class DragDropInteractionEmitter extends Emit
|
|
|
2009
1951
|
if (!owner) {
|
|
2010
1952
|
return '';
|
|
2011
1953
|
}
|
|
2012
|
-
let db = owner.database || ((owner.schema || '').
|
|
1954
|
+
let db = owner.database || ((owner.schema || '').includes('.') ? (owner.schema || '').split('.')[0] : '');
|
|
2013
1955
|
return db ? `${db}.` : '';
|
|
2014
1956
|
};
|
|
2015
1957
|
if (dataItem.sqlModel) {
|
|
2016
1958
|
let modelAny = dataItem.sqlModel;
|
|
2017
|
-
let modelDb = modelAny.database || ((dataItem.sqlModel.schema || '').
|
|
2018
|
-
let modelSchema = (dataItem.sqlModel.schema || '').
|
|
1959
|
+
let modelDb = modelAny.database || ((dataItem.sqlModel.schema || '').includes('.') ? (dataItem.sqlModel.schema || '').split('.')[0] : '');
|
|
1960
|
+
let modelSchema = (dataItem.sqlModel.schema || '').includes('.')
|
|
2019
1961
|
? (dataItem.sqlModel.schema || '').split('.').slice(1).join('.')
|
|
2020
1962
|
: (dataItem.sqlModel.schema || '');
|
|
2021
1963
|
callback(modelDb ? `${modelDb}.${modelSchema}.` : `${modelSchema}.`);
|
|
@@ -2037,7 +1979,7 @@ let DragDropInteractionEmitter$1 = class DragDropInteractionEmitter extends Emit
|
|
|
2037
1979
|
}
|
|
2038
1980
|
else if (dataItem.sqlColumn) {
|
|
2039
1981
|
let suggestions = self.sqlProvider.getAutocompleteSuggestions(self.editor.getValue(), self.position);
|
|
2040
|
-
let colSugg = suggestions.suggestions.find(x => x.insertText.
|
|
1982
|
+
let colSugg = suggestions.suggestions.find(x => x.insertText.includes(dataItem.sqlColumn.column));
|
|
2041
1983
|
if (colSugg) {
|
|
2042
1984
|
callback(colSugg.insertText);
|
|
2043
1985
|
}
|
|
@@ -2049,8 +1991,8 @@ let DragDropInteractionEmitter$1 = class DragDropInteractionEmitter extends Emit
|
|
|
2049
1991
|
}
|
|
2050
1992
|
else if (dataItem.sqlRelation) {
|
|
2051
1993
|
let suggestions = self.sqlProvider.getAutocompleteSuggestions(self.editor.getValue(), self.position);
|
|
2052
|
-
if (suggestions
|
|
2053
|
-
let relSugg = suggestions.suggestions.find(x => x.insertText.
|
|
1994
|
+
if (suggestions?.suggestions) {
|
|
1995
|
+
let relSugg = suggestions.suggestions.find(x => x.insertText.includes(`${dataItem.sqlRelation.schemaPK}.${dataItem.sqlRelation.tablePK}`));
|
|
2054
1996
|
if (relSugg) {
|
|
2055
1997
|
callback(relSugg.insertText);
|
|
2056
1998
|
}
|
|
@@ -2070,10 +2012,6 @@ let DragDropInteractionEmitter$1 = class DragDropInteractionEmitter extends Emit
|
|
|
2070
2012
|
},
|
|
2071
2013
|
position: event.position
|
|
2072
2014
|
};
|
|
2073
|
-
// IMMUTABLE !!!!
|
|
2074
|
-
// event.event.dataTransfer.clearData();
|
|
2075
|
-
// event.event.dataTransfer.items.clear();
|
|
2076
|
-
// event.event.dataTransfer.setData('text/plain', 'pippo');
|
|
2077
2015
|
this.editor._contributions.onBeforeInteractionEvent();
|
|
2078
2016
|
super.fire(de);
|
|
2079
2017
|
}
|
|
@@ -2090,9 +2028,6 @@ let ContextMenuInteractionEmitter$1 = class ContextMenuInteractionEmitter extend
|
|
|
2090
2028
|
fire(event) {
|
|
2091
2029
|
event.event.preventDefault();
|
|
2092
2030
|
debugger;
|
|
2093
|
-
// event.event.stopPropagation();
|
|
2094
|
-
// this.editor._contributions.onBeforeInteractionEvent();
|
|
2095
|
-
// super.fire(event);
|
|
2096
2031
|
}
|
|
2097
2032
|
};
|
|
2098
2033
|
class ContextInfo {
|
|
@@ -2253,9 +2188,6 @@ class EditorOptions {
|
|
|
2253
2188
|
}
|
|
2254
2189
|
}
|
|
2255
2190
|
|
|
2256
|
-
// import { editor } from 'monaco-editor';
|
|
2257
|
-
// import { LinkedList } from 'monaco-editor/esm/vs/base/common/linkedList';
|
|
2258
|
-
// import { MenuId, MenuRegistry } from 'monaco-editor/esm/vs/platform/actions/common/actions';
|
|
2259
2191
|
/**
|
|
2260
2192
|
* Stub ambient delle classi rxjs core iniettato in Monaco come extra-lib.
|
|
2261
2193
|
*
|
|
@@ -2335,8 +2267,6 @@ class TSProvider {
|
|
|
2335
2267
|
this.editor = editor;
|
|
2336
2268
|
this.editor['_onDropIntoEditor'] = this.editor['_register'](new DragDropInteractionEmitter(this.editor['_contributions'], this.editor['_deliveryQueue'], this.editor, this));
|
|
2337
2269
|
this.editor['onDropIntoEditor'] = this.editor['_onDropIntoEditor'].event;
|
|
2338
|
-
// this._onContextMenu = this._register(new InteractionEmitter(this._contributions, this._deliveryQueue));
|
|
2339
|
-
// this.onContextMenu = this._onContextMenu.event;
|
|
2340
2270
|
this.editor['_onContextMenu'] = this.editor['_register'](new ContextMenuInteractionEmitter(this.editor['_contributions'], this.editor['_deliveryQueue'], this.editor, this));
|
|
2341
2271
|
this.editor['onContextMenu'] = this.editor['_onContextMenu'].event;
|
|
2342
2272
|
}
|
|
@@ -2354,6 +2284,15 @@ class TSProvider {
|
|
|
2354
2284
|
let self = this;
|
|
2355
2285
|
let extraLibs = [];
|
|
2356
2286
|
let libs = [];
|
|
2287
|
+
// Snippet custom dal chiamante (customEditorConfig.snippets, mappa
|
|
2288
|
+
// label -> testo): si SOMMANO ai base perche' il menu contestuale
|
|
2289
|
+
// "Snippets" e' l'unico canale di inserimento guidato — es. il
|
|
2290
|
+
// workflow-designer inietta voci per-nodo che compilano
|
|
2291
|
+
// `wtoolbox.getWorkflowRouteNodePayload('<nodeId>')` con l'id reale.
|
|
2292
|
+
// Mappa d'istanza (un TSProvider per editor): nessun effetto altrove.
|
|
2293
|
+
if (formFieldOptions?.snippets && typeof formFieldOptions.snippets === 'object') {
|
|
2294
|
+
Object.assign(this.snippets, formFieldOptions.snippets);
|
|
2295
|
+
}
|
|
2357
2296
|
let contextMenuItems = [
|
|
2358
2297
|
{
|
|
2359
2298
|
label: 'Snippets', icon: 'pi pi-receipt',
|
|
@@ -2370,10 +2309,8 @@ class TSProvider {
|
|
|
2370
2309
|
});
|
|
2371
2310
|
this.editorOptions.contextMenuItems = contextMenuItems;
|
|
2372
2311
|
this.editorOptions.onNodeClick = (event, dataItem) => {
|
|
2373
|
-
// debugger;
|
|
2374
2312
|
};
|
|
2375
2313
|
this.editorOptions.onInit = (editor) => {
|
|
2376
|
-
// debugger;
|
|
2377
2314
|
this.editor = editor;
|
|
2378
2315
|
};
|
|
2379
2316
|
this.editorOptions.onNodeDragStart = (event, dataItem) => {
|
|
@@ -2382,7 +2319,6 @@ class TSProvider {
|
|
|
2382
2319
|
event.dataTransfer.setData("text/plain", JSON.stringify(dataItem));
|
|
2383
2320
|
};
|
|
2384
2321
|
this.editorOptions.onNodeDragEnd = (event, dataItem) => {
|
|
2385
|
-
// debugger;
|
|
2386
2322
|
};
|
|
2387
2323
|
let compilerOptions = {
|
|
2388
2324
|
target: monaco.languages.typescript.ScriptTarget.ES2016,
|
|
@@ -2414,16 +2350,6 @@ class TSProvider {
|
|
|
2414
2350
|
noSyntaxValidation: false,
|
|
2415
2351
|
diagnosticCodesToIgnore: [1108, 1375, 1378],
|
|
2416
2352
|
});
|
|
2417
|
-
// //esportare i tipi usando angular.json (riduci i file esportati alle cartelle ...)
|
|
2418
|
-
// // "assets": [
|
|
2419
|
-
// // ....
|
|
2420
|
-
// // {
|
|
2421
|
-
// // "glob": "**/*",
|
|
2422
|
-
// // "input": "node_modules/@skylab/easygrid/lib",
|
|
2423
|
-
// // "output": "assets/"
|
|
2424
|
-
// // },
|
|
2425
|
-
// // ....
|
|
2426
|
-
// libs.push(this.http.get("assets/models/easygrid.server.d.ts", { responseType: 'text' }));
|
|
2427
2353
|
let headers = new HttpHeaders({
|
|
2428
2354
|
'Cache-Control': 'no-cache, no-store, must-revalidate, post-check=0, pre- check=0',
|
|
2429
2355
|
'Pragma': 'no-cache',
|
|
@@ -2432,7 +2358,7 @@ class TSProvider {
|
|
|
2432
2358
|
libs = MetadataProviderService.baseLibs.map(lib => {
|
|
2433
2359
|
return this.http.get(lib, { headers: headers, responseType: 'text' });
|
|
2434
2360
|
});
|
|
2435
|
-
if (formFieldOptions.extraLibs
|
|
2361
|
+
if (formFieldOptions.extraLibs?.length) {
|
|
2436
2362
|
libs.concat(formFieldOptions.extraLibs.map(lib => {
|
|
2437
2363
|
return this.http.get(lib, { headers: headers, responseType: 'text' });
|
|
2438
2364
|
}));
|
|
@@ -2441,15 +2367,15 @@ class TSProvider {
|
|
|
2441
2367
|
if (codeContext) {
|
|
2442
2368
|
let routeIdContextField = field.extras.customEditorConfig?.routeContextField;
|
|
2443
2369
|
let columnContextField = field.extras.customEditorConfig?.columnContextField;
|
|
2444
|
-
if (codeContext.
|
|
2445
|
-
let routeId = record[routeIdContextField]?.value;
|
|
2370
|
+
if (codeContext.includes('record: ') && routeIdContextField) {
|
|
2371
|
+
let routeId = record[routeIdContextField]?.value;
|
|
2446
2372
|
if (routeId) {
|
|
2447
2373
|
let metas = await metaSrv.getMetadatiById(routeId);
|
|
2448
2374
|
let currentColumnMeta = null;
|
|
2449
2375
|
if (columnContextField) {
|
|
2450
2376
|
currentColumnMeta = metas.find(m => m.mc_id == record[columnContextField].value);
|
|
2451
2377
|
}
|
|
2452
|
-
if (metas
|
|
2378
|
+
if (metas?.length) {
|
|
2453
2379
|
let routeName = metas[0]._Metadati_Tabelle.md_route_name.replaceAll(' ', '_');
|
|
2454
2380
|
let classProperties = '';
|
|
2455
2381
|
let classPropsArray = [];
|
|
@@ -2464,7 +2390,7 @@ class TSProvider {
|
|
|
2464
2390
|
let lookupMetas = await metaSrv.getMetadati(lookupRoute);
|
|
2465
2391
|
let nestedClassProperties = '';
|
|
2466
2392
|
let nestedClassPropsArray = [];
|
|
2467
|
-
if (lookupMetas
|
|
2393
|
+
if (lookupMetas?.length) {
|
|
2468
2394
|
lookupRoute = lookupRoute.replaceAll(' ', '_');
|
|
2469
2395
|
for (let cLookup of lookupMetas) {
|
|
2470
2396
|
nestedClassPropsArray.push(`${cLookup.mc_nome_colonna}: ${MetadataProviderService.getTSTypeFromMetaColumn(cLookup)};`);
|
|
@@ -2481,7 +2407,7 @@ class TSProvider {
|
|
|
2481
2407
|
}
|
|
2482
2408
|
// recursive function to get the type of the nested lookup objects
|
|
2483
2409
|
// ...
|
|
2484
|
-
if (codeContext.
|
|
2410
|
+
if (codeContext.includes('record: any')) {
|
|
2485
2411
|
classPropsArray.push(`${c.mc_nome_colonna}: ${MetadataProviderService.getTSTypeFromMetaColumn(c)};`);
|
|
2486
2412
|
}
|
|
2487
2413
|
else {
|
|
@@ -2498,13 +2424,13 @@ class TSProvider {
|
|
|
2498
2424
|
};
|
|
2499
2425
|
constructor();`);
|
|
2500
2426
|
})));
|
|
2501
|
-
if (codeContext.
|
|
2427
|
+
if (codeContext.includes('record: any')) {
|
|
2502
2428
|
codeContext = codeContext.replace('record: any', `record: ${routeName}`);
|
|
2503
2429
|
}
|
|
2504
2430
|
else {
|
|
2505
2431
|
codeContext = codeContext.replace('record: {[key: string]: BehaviorSubject<any>}', `record: {${compositeTypeDef}}`);
|
|
2506
2432
|
}
|
|
2507
|
-
if (currentColumnMeta
|
|
2433
|
+
if (currentColumnMeta?.mc_ui_column_type == 'lookupByID' && codeContext.includes('let newValue: any;')) {
|
|
2508
2434
|
codeContext = codeContext.replace('let newValue: any;', `let newValue: ${currentColumnMeta.mc_ui_lookup_entity_name};`);
|
|
2509
2435
|
}
|
|
2510
2436
|
}
|
|
@@ -2519,7 +2445,7 @@ class TSProvider {
|
|
|
2519
2445
|
let lines = data.split('\n');
|
|
2520
2446
|
// da migliorare con regex !!!
|
|
2521
2447
|
let parsedLines = lines.filter(x => {
|
|
2522
|
-
return x.
|
|
2448
|
+
return !x.includes('import '); // && x.indexOf('export interface ') < 0
|
|
2523
2449
|
}).map((line) => {
|
|
2524
2450
|
return line.replace('export declare', 'declare').replace('export interface', 'declare interface').replace('export class', 'declare class');
|
|
2525
2451
|
});
|
|
@@ -2568,33 +2494,13 @@ class DragDropInteractionEmitter extends Emitter {
|
|
|
2568
2494
|
callback(`${dataItem.sqlTable.schema}.${dataItem.sqlTable.table}`);
|
|
2569
2495
|
}
|
|
2570
2496
|
if (dataItem.parentTable) {
|
|
2571
|
-
//
|
|
2572
|
-
// let colSugg = suggestions.suggestions.find(x => x.label == '<All columns>');
|
|
2573
|
-
// if (colSugg) {
|
|
2574
|
-
// callback(colSugg.insertText);
|
|
2575
|
-
// } else {
|
|
2576
|
-
// callback(dataItem.parentTable.columns.map(x => x.column).join(','));
|
|
2577
|
-
// }
|
|
2497
|
+
// intentionally empty: parentTable drops have no plain-text representation
|
|
2578
2498
|
}
|
|
2579
2499
|
if (dataItem.sqlColumn) {
|
|
2580
|
-
//
|
|
2581
|
-
// let colSugg = suggestions.suggestions.find(x => x.insertText.indexOf(dataItem.sqlColumn.column) >= 0);
|
|
2582
|
-
// if (colSugg) {
|
|
2583
|
-
// callback(colSugg.insertText);
|
|
2584
|
-
// } else {
|
|
2585
|
-
// callback(`${dataItem.sqlColumn.schema}.${dataItem.sqlColumn.table}.${dataItem.sqlColumn.column}`);
|
|
2586
|
-
// }
|
|
2500
|
+
// intentionally empty: sqlColumn drops have no plain-text representation
|
|
2587
2501
|
}
|
|
2588
2502
|
if (dataItem.sqlRelation) {
|
|
2589
|
-
//
|
|
2590
|
-
// if (suggestions && suggestions.suggestions) {
|
|
2591
|
-
// let relSugg = suggestions.suggestions.find(x => x.insertText.indexOf(`${dataItem.sqlRelation.schemaPK}.${dataItem.sqlRelation.tablePK}`) >= 0);
|
|
2592
|
-
// if (relSugg) {
|
|
2593
|
-
// callback(relSugg.insertText);
|
|
2594
|
-
// } else {
|
|
2595
|
-
// callback(`${dataItem.sqlRelation.schemaPK}.${dataItem.sqlRelation.tablePK} ON ${dataItem.sqlRelation.schemaFK}.${dataItem.sqlRelation.tableFK}.${dataItem.sqlRelation.columnFK} = ${dataItem.sqlRelation.schemaPK}.${dataItem.sqlRelation.tablePK}.${dataItem.sqlRelation.columnPK}`);
|
|
2596
|
-
// }
|
|
2597
|
-
// }
|
|
2503
|
+
// intentionally empty: sqlRelation drops have no plain-text representation
|
|
2598
2504
|
}
|
|
2599
2505
|
}
|
|
2600
2506
|
}
|
|
@@ -2604,10 +2510,6 @@ class DragDropInteractionEmitter extends Emitter {
|
|
|
2604
2510
|
},
|
|
2605
2511
|
position: event.position
|
|
2606
2512
|
};
|
|
2607
|
-
// IMMUTABLE !!!!
|
|
2608
|
-
// event.event.dataTransfer.clearData();
|
|
2609
|
-
// event.event.dataTransfer.items.clear();
|
|
2610
|
-
// event.event.dataTransfer.setData('text/plain', 'pippo');
|
|
2611
2513
|
this.editor._contributions.onBeforeInteractionEvent();
|
|
2612
2514
|
super.fire(de);
|
|
2613
2515
|
}
|
|
@@ -2624,12 +2526,191 @@ class ContextMenuInteractionEmitter extends Emitter {
|
|
|
2624
2526
|
fire(event) {
|
|
2625
2527
|
event.event.preventDefault();
|
|
2626
2528
|
this.tsProvider.contextMenuEvent = event;
|
|
2627
|
-
// event.event.stopPropagation();
|
|
2628
|
-
// this.editor._contributions.onBeforeInteractionEvent();
|
|
2629
|
-
// super.fire(event);
|
|
2630
2529
|
}
|
|
2631
2530
|
}
|
|
2632
2531
|
|
|
2532
|
+
/**
|
|
2533
|
+
* Property-editor "a struttura" guidato da JSON SCHEMA — vista alternativa
|
|
2534
|
+
* (toggle) del code-editor in modalita' json: aggiunta/rimozione di
|
|
2535
|
+
* nodi/sottonodi TIPIZZATI in base allo schema, senza scrivere JSON a mano.
|
|
2536
|
+
*
|
|
2537
|
+
* Copertura schema v1 (subset pragmatico dei casi usati dagli schemi WUIC,
|
|
2538
|
+
* es. customEditorConfig/props_bag/scene3d shader effect):
|
|
2539
|
+
* - object con `properties` (+`required`): righe per le proprieta' presenti,
|
|
2540
|
+
* dropdown "aggiungi" per quelle mancanti, rimozione per le non-required;
|
|
2541
|
+
* - string (input testo; color picker se pattern hex #rrggbb; select se enum);
|
|
2542
|
+
* - number/integer (input numerico con min/max dallo schema);
|
|
2543
|
+
* - boolean (checkbox); const (badge readonly);
|
|
2544
|
+
* - array di number a lunghezza fissa (minItems=maxItems, es. direzioni xyz)
|
|
2545
|
+
* e array generici (item add/remove col tipo di `items`).
|
|
2546
|
+
*
|
|
2547
|
+
* Il valore viene MUTATO su una copia locale e riemesso integralmente a ogni
|
|
2548
|
+
* modifica (`valueChange`): il code-editor lo serializza nel model Monaco e
|
|
2549
|
+
* nel record, cosi' le due viste restano sempre allineate.
|
|
2550
|
+
*/
|
|
2551
|
+
class JsonSchemaTreeEditorComponent {
|
|
2552
|
+
/** JSON schema radice (draft-07 style: type/properties/required/enum/const/items). */
|
|
2553
|
+
schema = null;
|
|
2554
|
+
/** Valore corrente (oggetto JS gia' parsato). Lavora su una copia profonda. */
|
|
2555
|
+
value = null;
|
|
2556
|
+
/** Emesso a ogni mutazione con l'intero valore aggiornato. */
|
|
2557
|
+
valueChange = new EventEmitter();
|
|
2558
|
+
/** Copia locale mutabile del valore. */
|
|
2559
|
+
model = {};
|
|
2560
|
+
ngOnChanges() {
|
|
2561
|
+
try {
|
|
2562
|
+
this.model = this.value != null ? JSON.parse(JSON.stringify(this.value)) : {};
|
|
2563
|
+
}
|
|
2564
|
+
catch {
|
|
2565
|
+
this.model = {};
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
emit() {
|
|
2569
|
+
this.valueChange.emit(JSON.parse(JSON.stringify(this.model)));
|
|
2570
|
+
}
|
|
2571
|
+
// --- Introspezione schema -------------------------------------------------
|
|
2572
|
+
/** Righe (proprieta' PRESENTI nel valore) per un nodo object. */
|
|
2573
|
+
rows(schema, obj) {
|
|
2574
|
+
const props = schema?.properties || {};
|
|
2575
|
+
const required = Array.isArray(schema?.required) ? schema.required : [];
|
|
2576
|
+
return Object.keys(props)
|
|
2577
|
+
.filter((key) => obj != null && Object.hasOwn(obj, key))
|
|
2578
|
+
.map((key) => ({
|
|
2579
|
+
key,
|
|
2580
|
+
schema: props[key],
|
|
2581
|
+
kind: this.kindOf(props[key]),
|
|
2582
|
+
required: required.includes(key)
|
|
2583
|
+
}));
|
|
2584
|
+
}
|
|
2585
|
+
/** Proprieta' dello schema NON ancora presenti nel valore (per il dropdown "aggiungi"). */
|
|
2586
|
+
addable(schema, obj) {
|
|
2587
|
+
const props = schema?.properties || {};
|
|
2588
|
+
return Object.keys(props).filter((key) => !(obj != null && Object.hasOwn(obj, key)));
|
|
2589
|
+
}
|
|
2590
|
+
/** Classifica il tipo di editor per una property schema. */
|
|
2591
|
+
kindOf(propSchema) {
|
|
2592
|
+
if (!propSchema) {
|
|
2593
|
+
return 'text';
|
|
2594
|
+
}
|
|
2595
|
+
if (propSchema.const !== undefined) {
|
|
2596
|
+
return 'const';
|
|
2597
|
+
}
|
|
2598
|
+
if (Array.isArray(propSchema.enum)) {
|
|
2599
|
+
return 'enum';
|
|
2600
|
+
}
|
|
2601
|
+
const type = propSchema.type;
|
|
2602
|
+
if (type === 'boolean') {
|
|
2603
|
+
return 'boolean';
|
|
2604
|
+
}
|
|
2605
|
+
if (type === 'number' || type === 'integer') {
|
|
2606
|
+
return 'number';
|
|
2607
|
+
}
|
|
2608
|
+
if (type === 'object') {
|
|
2609
|
+
return 'object';
|
|
2610
|
+
}
|
|
2611
|
+
if (type === 'array') {
|
|
2612
|
+
const fixed = Number(propSchema.minItems) > 0 && propSchema.minItems === propSchema.maxItems
|
|
2613
|
+
&& (propSchema.items?.type === 'number' || propSchema.items?.type === 'integer');
|
|
2614
|
+
return fixed ? 'vector' : 'array';
|
|
2615
|
+
}
|
|
2616
|
+
if (type === 'string' && typeof propSchema.pattern === 'string' && propSchema.pattern.includes('#[0-9a-fA-F]{6}')) {
|
|
2617
|
+
return 'color';
|
|
2618
|
+
}
|
|
2619
|
+
return 'text';
|
|
2620
|
+
}
|
|
2621
|
+
/** Valore di default tipizzato per una property aggiunta dalla struttura. */
|
|
2622
|
+
defaultFor(propSchema) {
|
|
2623
|
+
if (!propSchema) {
|
|
2624
|
+
return '';
|
|
2625
|
+
}
|
|
2626
|
+
if (propSchema.default !== undefined) {
|
|
2627
|
+
return JSON.parse(JSON.stringify(propSchema.default));
|
|
2628
|
+
}
|
|
2629
|
+
if (propSchema.const !== undefined) {
|
|
2630
|
+
return propSchema.const;
|
|
2631
|
+
}
|
|
2632
|
+
if (Array.isArray(propSchema.enum)) {
|
|
2633
|
+
return propSchema.enum[0];
|
|
2634
|
+
}
|
|
2635
|
+
const type = propSchema.type;
|
|
2636
|
+
if (type === 'boolean') {
|
|
2637
|
+
return false;
|
|
2638
|
+
}
|
|
2639
|
+
if (type === 'number' || type === 'integer') {
|
|
2640
|
+
return typeof propSchema.minimum === 'number' ? propSchema.minimum : 0;
|
|
2641
|
+
}
|
|
2642
|
+
if (type === 'array') {
|
|
2643
|
+
const n = Number(propSchema.minItems) || 0;
|
|
2644
|
+
return Array.from({ length: n }, () => this.defaultFor(propSchema.items));
|
|
2645
|
+
}
|
|
2646
|
+
if (type === 'object') {
|
|
2647
|
+
const child = {};
|
|
2648
|
+
const required = Array.isArray(propSchema.required) ? propSchema.required : [];
|
|
2649
|
+
for (const key of required) {
|
|
2650
|
+
child[key] = this.defaultFor(propSchema.properties?.[key]);
|
|
2651
|
+
}
|
|
2652
|
+
return child;
|
|
2653
|
+
}
|
|
2654
|
+
if (this.kindOf(propSchema) === 'color') {
|
|
2655
|
+
return '#ffffff';
|
|
2656
|
+
}
|
|
2657
|
+
return '';
|
|
2658
|
+
}
|
|
2659
|
+
// --- Mutazioni ------------------------------------------------------------
|
|
2660
|
+
setProp(obj, key, value) {
|
|
2661
|
+
obj[key] = value;
|
|
2662
|
+
this.emit();
|
|
2663
|
+
}
|
|
2664
|
+
setNumberProp(obj, key, raw) {
|
|
2665
|
+
const parsed = Number(raw);
|
|
2666
|
+
obj[key] = Number.isFinite(parsed) ? parsed : 0;
|
|
2667
|
+
this.emit();
|
|
2668
|
+
}
|
|
2669
|
+
addProp(obj, key, propSchema, selectEl) {
|
|
2670
|
+
if (!key) {
|
|
2671
|
+
return;
|
|
2672
|
+
}
|
|
2673
|
+
obj[key] = this.defaultFor(propSchema);
|
|
2674
|
+
if (selectEl) {
|
|
2675
|
+
selectEl.value = '';
|
|
2676
|
+
}
|
|
2677
|
+
this.emit();
|
|
2678
|
+
}
|
|
2679
|
+
removeProp(obj, key) {
|
|
2680
|
+
delete obj[key];
|
|
2681
|
+
this.emit();
|
|
2682
|
+
}
|
|
2683
|
+
setVectorItem(arr, index, raw) {
|
|
2684
|
+
const parsed = Number(raw);
|
|
2685
|
+
arr[index] = Number.isFinite(parsed) ? parsed : 0;
|
|
2686
|
+
this.emit();
|
|
2687
|
+
}
|
|
2688
|
+
addArrayItem(arr, itemSchema) {
|
|
2689
|
+
arr.push(this.defaultFor(itemSchema));
|
|
2690
|
+
this.emit();
|
|
2691
|
+
}
|
|
2692
|
+
removeArrayItem(arr, index) {
|
|
2693
|
+
arr.splice(index, 1);
|
|
2694
|
+
this.emit();
|
|
2695
|
+
}
|
|
2696
|
+
/** Etichetta breve per gli hint (description dello schema, troncata). */
|
|
2697
|
+
hint(propSchema) {
|
|
2698
|
+
return String(propSchema?.description || '');
|
|
2699
|
+
}
|
|
2700
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: JsonSchemaTreeEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2701
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: JsonSchemaTreeEditorComponent, isStandalone: true, selector: "wuic-json-schema-tree-editor", inputs: { schema: "schema", value: "value" }, outputs: { valueChange: "valueChange" }, usesOnChanges: true, ngImport: i0, template: "<!-- Editor \"a struttura\": ricorsione via ng-template (nessun self-import). -->\n<ng-template #objectEditor let-ctx>\n @for (row of rows(ctx.schema, ctx.obj); track row.key) {\n <div class=\"jste-row\" [class.jste-row-object]=\"row.kind === 'object'\">\n <div class=\"jste-row-head\">\n <span class=\"jste-key\" [title]=\"hint(row.schema)\">{{ row.key }}</span>\n\n @switch (row.kind) {\n @case ('const') {\n <span class=\"jste-const\">{{ ctx.obj[row.key] }}</span>\n }\n @case ('enum') {\n <select class=\"jste-input\" [ngModel]=\"ctx.obj[row.key]\"\n (ngModelChange)=\"setProp(ctx.obj, row.key, $event)\">\n @for (opt of row.schema.enum; track opt) {\n <option [ngValue]=\"opt\">{{ opt }}</option>\n }\n </select>\n }\n @case ('boolean') {\n <input type=\"checkbox\" [ngModel]=\"ctx.obj[row.key]\"\n (ngModelChange)=\"setProp(ctx.obj, row.key, $event)\" />\n }\n @case ('number') {\n <input class=\"jste-input jste-number\" type=\"number\" step=\"any\"\n [min]=\"row.schema.minimum\" [max]=\"row.schema.maximum\"\n [ngModel]=\"ctx.obj[row.key]\"\n (ngModelChange)=\"setNumberProp(ctx.obj, row.key, $event)\" />\n }\n @case ('color') {\n <input class=\"jste-color\" type=\"color\" [ngModel]=\"ctx.obj[row.key]\"\n (ngModelChange)=\"setProp(ctx.obj, row.key, $event)\" />\n <span class=\"jste-const\">{{ ctx.obj[row.key] }}</span>\n }\n @case ('vector') {\n <span class=\"jste-vector\">\n @for (component of ctx.obj[row.key]; track $index) {\n <input class=\"jste-input jste-number\" type=\"number\" step=\"any\"\n [ngModel]=\"ctx.obj[row.key][$index]\"\n (ngModelChange)=\"setVectorItem(ctx.obj[row.key], $index, $event)\" />\n }\n </span>\n }\n @case ('text') {\n <input class=\"jste-input\" type=\"text\" [ngModel]=\"ctx.obj[row.key]\"\n (ngModelChange)=\"setProp(ctx.obj, row.key, $event)\" />\n }\n }\n\n @if (!row.required) {\n <button type=\"button\" class=\"jste-btn jste-remove\" (click)=\"removeProp(ctx.obj, row.key)\"\n title=\"Rimuovi\">\n <i class=\"pi pi-times\"></i>\n </button>\n }\n </div>\n\n @if (row.kind === 'object') {\n <div class=\"jste-children\">\n <ng-container *ngTemplateOutlet=\"objectEditor; context: { $implicit: { schema: row.schema, obj: ctx.obj[row.key] } }\"></ng-container>\n </div>\n }\n @if (row.kind === 'array') {\n <div class=\"jste-children\">\n @for (item of ctx.obj[row.key]; track $index) {\n <div class=\"jste-row-head\">\n <span class=\"jste-key\">[{{ $index }}]</span>\n @if (row.schema.items?.type === 'object') {\n <ng-container *ngTemplateOutlet=\"objectEditor; context: { $implicit: { schema: row.schema.items, obj: ctx.obj[row.key][$index] } }\"></ng-container>\n } @else if (row.schema.items?.type === 'number' || row.schema.items?.type === 'integer') {\n <input class=\"jste-input jste-number\" type=\"number\" step=\"any\"\n [ngModel]=\"ctx.obj[row.key][$index]\"\n (ngModelChange)=\"setVectorItem(ctx.obj[row.key], $index, $event)\" />\n } @else {\n <input class=\"jste-input\" type=\"text\" [ngModel]=\"ctx.obj[row.key][$index]\"\n (ngModelChange)=\"setProp(ctx.obj[row.key], $index, $event)\" />\n }\n <button type=\"button\" class=\"jste-btn jste-remove\" (click)=\"removeArrayItem(ctx.obj[row.key], $index)\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n }\n <button type=\"button\" class=\"jste-btn jste-add-item\" (click)=\"addArrayItem(ctx.obj[row.key], row.schema.items)\">\n <i class=\"pi pi-plus\"></i>\n </button>\n </div>\n }\n @if (hint(row.schema)) {\n <div class=\"jste-hint\">{{ hint(row.schema) }}</div>\n }\n </div>\n }\n\n @if (addable(ctx.schema, ctx.obj).length) {\n <div class=\"jste-add-row\">\n <select #addSelect class=\"jste-input jste-add-select\"\n (change)=\"addProp(ctx.obj, addSelect.value, ctx.schema.properties[addSelect.value], addSelect)\">\n <option value=\"\" disabled selected>+ proprieta'...</option>\n @for (key of addable(ctx.schema, ctx.obj); track key) {\n <option [value]=\"key\" [title]=\"hint(ctx.schema.properties[key])\">{{ key }}</option>\n }\n </select>\n </div>\n }\n</ng-template>\n\n<div class=\"jste-root\">\n <ng-container *ngTemplateOutlet=\"objectEditor; context: { $implicit: { schema: schema, obj: model } }\"></ng-container>\n</div>\n", styles: [".jste-root{height:100%;overflow:auto;padding:8px 10px;background:#1e1e1e;color:#d4d4d4;font-size:12.5px;font-family:Consolas,Courier New,monospace}.jste-row{margin:2px 0}.jste-row-head{display:flex;align-items:center;gap:8px;min-height:26px}.jste-key{color:#9cdcfe;min-width:110px}.jste-const{color:#b5cea8}.jste-input{background:#2a2a31;color:#d4d4d4;border:1px solid #3c3c46;border-radius:4px;padding:3px 6px;font-size:12.5px;font-family:inherit;min-width:0}.jste-number{width:84px}.jste-color{width:34px;height:24px;padding:0;border:1px solid #3c3c46;background:transparent}.jste-vector{display:inline-flex;gap:4px}.jste-btn{border:none;background:none;color:#8a8a95;cursor:pointer;padding:2px 4px;border-radius:4px}.jste-btn:hover{color:#e0e0e6;background:#34343c}.jste-remove i{font-size:11px}.jste-children{margin-left:18px;padding-left:10px;border-left:1px solid #34343c}.jste-row-object>.jste-row-head .jste-key{color:#dcdcaa;font-weight:600}.jste-hint{margin-left:118px;color:#6f6f7a;font-size:11px;line-height:1.3;max-width:460px}.jste-add-row{margin:4px 0 2px}.jste-add-select{color:#8fd18f}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
|
|
2702
|
+
}
|
|
2703
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: JsonSchemaTreeEditorComponent, decorators: [{
|
|
2704
|
+
type: Component,
|
|
2705
|
+
args: [{ selector: 'wuic-json-schema-tree-editor', standalone: true, imports: [FormsModule, NgTemplateOutlet], template: "<!-- Editor \"a struttura\": ricorsione via ng-template (nessun self-import). -->\n<ng-template #objectEditor let-ctx>\n @for (row of rows(ctx.schema, ctx.obj); track row.key) {\n <div class=\"jste-row\" [class.jste-row-object]=\"row.kind === 'object'\">\n <div class=\"jste-row-head\">\n <span class=\"jste-key\" [title]=\"hint(row.schema)\">{{ row.key }}</span>\n\n @switch (row.kind) {\n @case ('const') {\n <span class=\"jste-const\">{{ ctx.obj[row.key] }}</span>\n }\n @case ('enum') {\n <select class=\"jste-input\" [ngModel]=\"ctx.obj[row.key]\"\n (ngModelChange)=\"setProp(ctx.obj, row.key, $event)\">\n @for (opt of row.schema.enum; track opt) {\n <option [ngValue]=\"opt\">{{ opt }}</option>\n }\n </select>\n }\n @case ('boolean') {\n <input type=\"checkbox\" [ngModel]=\"ctx.obj[row.key]\"\n (ngModelChange)=\"setProp(ctx.obj, row.key, $event)\" />\n }\n @case ('number') {\n <input class=\"jste-input jste-number\" type=\"number\" step=\"any\"\n [min]=\"row.schema.minimum\" [max]=\"row.schema.maximum\"\n [ngModel]=\"ctx.obj[row.key]\"\n (ngModelChange)=\"setNumberProp(ctx.obj, row.key, $event)\" />\n }\n @case ('color') {\n <input class=\"jste-color\" type=\"color\" [ngModel]=\"ctx.obj[row.key]\"\n (ngModelChange)=\"setProp(ctx.obj, row.key, $event)\" />\n <span class=\"jste-const\">{{ ctx.obj[row.key] }}</span>\n }\n @case ('vector') {\n <span class=\"jste-vector\">\n @for (component of ctx.obj[row.key]; track $index) {\n <input class=\"jste-input jste-number\" type=\"number\" step=\"any\"\n [ngModel]=\"ctx.obj[row.key][$index]\"\n (ngModelChange)=\"setVectorItem(ctx.obj[row.key], $index, $event)\" />\n }\n </span>\n }\n @case ('text') {\n <input class=\"jste-input\" type=\"text\" [ngModel]=\"ctx.obj[row.key]\"\n (ngModelChange)=\"setProp(ctx.obj, row.key, $event)\" />\n }\n }\n\n @if (!row.required) {\n <button type=\"button\" class=\"jste-btn jste-remove\" (click)=\"removeProp(ctx.obj, row.key)\"\n title=\"Rimuovi\">\n <i class=\"pi pi-times\"></i>\n </button>\n }\n </div>\n\n @if (row.kind === 'object') {\n <div class=\"jste-children\">\n <ng-container *ngTemplateOutlet=\"objectEditor; context: { $implicit: { schema: row.schema, obj: ctx.obj[row.key] } }\"></ng-container>\n </div>\n }\n @if (row.kind === 'array') {\n <div class=\"jste-children\">\n @for (item of ctx.obj[row.key]; track $index) {\n <div class=\"jste-row-head\">\n <span class=\"jste-key\">[{{ $index }}]</span>\n @if (row.schema.items?.type === 'object') {\n <ng-container *ngTemplateOutlet=\"objectEditor; context: { $implicit: { schema: row.schema.items, obj: ctx.obj[row.key][$index] } }\"></ng-container>\n } @else if (row.schema.items?.type === 'number' || row.schema.items?.type === 'integer') {\n <input class=\"jste-input jste-number\" type=\"number\" step=\"any\"\n [ngModel]=\"ctx.obj[row.key][$index]\"\n (ngModelChange)=\"setVectorItem(ctx.obj[row.key], $index, $event)\" />\n } @else {\n <input class=\"jste-input\" type=\"text\" [ngModel]=\"ctx.obj[row.key][$index]\"\n (ngModelChange)=\"setProp(ctx.obj[row.key], $index, $event)\" />\n }\n <button type=\"button\" class=\"jste-btn jste-remove\" (click)=\"removeArrayItem(ctx.obj[row.key], $index)\">\n <i class=\"pi pi-times\"></i>\n </button>\n </div>\n }\n <button type=\"button\" class=\"jste-btn jste-add-item\" (click)=\"addArrayItem(ctx.obj[row.key], row.schema.items)\">\n <i class=\"pi pi-plus\"></i>\n </button>\n </div>\n }\n @if (hint(row.schema)) {\n <div class=\"jste-hint\">{{ hint(row.schema) }}</div>\n }\n </div>\n }\n\n @if (addable(ctx.schema, ctx.obj).length) {\n <div class=\"jste-add-row\">\n <select #addSelect class=\"jste-input jste-add-select\"\n (change)=\"addProp(ctx.obj, addSelect.value, ctx.schema.properties[addSelect.value], addSelect)\">\n <option value=\"\" disabled selected>+ proprieta'...</option>\n @for (key of addable(ctx.schema, ctx.obj); track key) {\n <option [value]=\"key\" [title]=\"hint(ctx.schema.properties[key])\">{{ key }}</option>\n }\n </select>\n </div>\n }\n</ng-template>\n\n<div class=\"jste-root\">\n <ng-container *ngTemplateOutlet=\"objectEditor; context: { $implicit: { schema: schema, obj: model } }\"></ng-container>\n</div>\n", styles: [".jste-root{height:100%;overflow:auto;padding:8px 10px;background:#1e1e1e;color:#d4d4d4;font-size:12.5px;font-family:Consolas,Courier New,monospace}.jste-row{margin:2px 0}.jste-row-head{display:flex;align-items:center;gap:8px;min-height:26px}.jste-key{color:#9cdcfe;min-width:110px}.jste-const{color:#b5cea8}.jste-input{background:#2a2a31;color:#d4d4d4;border:1px solid #3c3c46;border-radius:4px;padding:3px 6px;font-size:12.5px;font-family:inherit;min-width:0}.jste-number{width:84px}.jste-color{width:34px;height:24px;padding:0;border:1px solid #3c3c46;background:transparent}.jste-vector{display:inline-flex;gap:4px}.jste-btn{border:none;background:none;color:#8a8a95;cursor:pointer;padding:2px 4px;border-radius:4px}.jste-btn:hover{color:#e0e0e6;background:#34343c}.jste-remove i{font-size:11px}.jste-children{margin-left:18px;padding-left:10px;border-left:1px solid #34343c}.jste-row-object>.jste-row-head .jste-key{color:#dcdcaa;font-weight:600}.jste-hint{margin-left:118px;color:#6f6f7a;font-size:11px;line-height:1.3;max-width:460px}.jste-add-row{margin:4px 0 2px}.jste-add-select{color:#8fd18f}\n"] }]
|
|
2706
|
+
}], propDecorators: { schema: [{
|
|
2707
|
+
type: Input
|
|
2708
|
+
}], value: [{
|
|
2709
|
+
type: Input
|
|
2710
|
+
}], valueChange: [{
|
|
2711
|
+
type: Output
|
|
2712
|
+
}] } });
|
|
2713
|
+
|
|
2633
2714
|
const WUIC_COMMON_INPUTS = [
|
|
2634
2715
|
'record',
|
|
2635
2716
|
'field',
|
|
@@ -2718,11 +2799,11 @@ class HtmlProvider {
|
|
|
2718
2799
|
{ label: 'div', snippet: '<div>$0</div>', documentation: 'Container element' },
|
|
2719
2800
|
{ label: 'section', snippet: '<section>$0</section>', documentation: 'Sectioning content' },
|
|
2720
2801
|
{ label: 'article', snippet: '<article>$0</article>', documentation: 'Independent content block' },
|
|
2721
|
-
{ label: 'a', snippet: '<a href
|
|
2722
|
-
{ label: 'img', snippet: '<img src
|
|
2802
|
+
{ label: 'a', snippet: '<a href="$1">$0</a>', documentation: 'Anchor link' },
|
|
2803
|
+
{ label: 'img', snippet: '<img src="$1" alt="$2" />', documentation: 'Image with alt text' },
|
|
2723
2804
|
{ label: 'table', snippet: '<table>\\n <thead>\\n <tr><th>$1</th></tr>\\n </thead>\\n <tbody>\\n <tr><td>$0</td></tr>\\n </tbody>\\n</table>', documentation: 'Basic table template' },
|
|
2724
2805
|
{ label: 'ul', snippet: '<ul>\\n <li>$0</li>\\n</ul>', documentation: 'Unordered list' },
|
|
2725
|
-
{ label: 'form', snippet: '<form>\\n <label for
|
|
2806
|
+
{ label: 'form', snippet: '<form>\\n <label for="$1">$2</label>\\n <input id="$1" name="$1" />\\n</form>', documentation: 'Simple form template' }
|
|
2726
2807
|
];
|
|
2727
2808
|
constructor(editorOptions, componentSelectors = [], componentBindings = {}) {
|
|
2728
2809
|
this.editorOptions = editorOptions;
|
|
@@ -2836,7 +2917,7 @@ class HtmlProvider {
|
|
|
2836
2917
|
message: `Possible malformed tag '${firstToken}': missing '<' at start.`
|
|
2837
2918
|
});
|
|
2838
2919
|
}
|
|
2839
|
-
const inlineEventMatch =
|
|
2920
|
+
const inlineEventMatch = /\son[a-z]+\s*=/i.exec(line);
|
|
2840
2921
|
if (inlineEventMatch?.index != null) {
|
|
2841
2922
|
const startColumn = inlineEventMatch.index + 2;
|
|
2842
2923
|
markers.push({
|
|
@@ -2848,7 +2929,7 @@ class HtmlProvider {
|
|
|
2848
2929
|
message: 'Inline on* event handler detected.'
|
|
2849
2930
|
});
|
|
2850
2931
|
}
|
|
2851
|
-
const jsUrlMatch =
|
|
2932
|
+
const jsUrlMatch = /\b(?:href|src)\s*=\s*["']?\s*javascript:/i.exec(line);
|
|
2852
2933
|
if (jsUrlMatch?.index != null) {
|
|
2853
2934
|
const startColumn = jsUrlMatch.index + 1;
|
|
2854
2935
|
markers.push({
|
|
@@ -2882,7 +2963,6 @@ class HtmlProvider {
|
|
|
2882
2963
|
while ((match = tagRegex.exec(content)) !== null) {
|
|
2883
2964
|
const full = match[0];
|
|
2884
2965
|
const tag = (match[1] || '').toLowerCase();
|
|
2885
|
-
const attrs = match[2] || '';
|
|
2886
2966
|
const isClosing = full.startsWith('</');
|
|
2887
2967
|
const isSelfClosing = /\/\s*>$/.test(full) || voidTags.has(tag);
|
|
2888
2968
|
if (!isClosing && !isSelfClosing) {
|
|
@@ -2890,8 +2970,8 @@ class HtmlProvider {
|
|
|
2890
2970
|
continue;
|
|
2891
2971
|
}
|
|
2892
2972
|
if (isClosing) {
|
|
2893
|
-
const last = stack
|
|
2894
|
-
if (last
|
|
2973
|
+
const last = stack.at(-1);
|
|
2974
|
+
if (last?.tag === tag) {
|
|
2895
2975
|
stack.pop();
|
|
2896
2976
|
}
|
|
2897
2977
|
else {
|
|
@@ -2982,12 +3062,12 @@ class HtmlProvider {
|
|
|
2982
3062
|
pushMarker(tagOffset + 1 + bad.index, bad[0].length, monaco.MarkerSeverity.Error, 'Malformed Angular property binding. Use [prop]="...".');
|
|
2983
3063
|
}
|
|
2984
3064
|
// Broken event binding: (event=...
|
|
2985
|
-
const malformedEvent = /\([
|
|
3065
|
+
const malformedEvent = /\([^)\s=><"'`]*\s*=/g;
|
|
2986
3066
|
while ((bad = malformedEvent.exec(attrs)) !== null) {
|
|
2987
3067
|
pushMarker(tagOffset + 1 + bad.index, bad[0].length, monaco.MarkerSeverity.Error, 'Malformed Angular event binding. Use (event)="...".');
|
|
2988
3068
|
}
|
|
2989
3069
|
// Broken two-way binding: [(model=...
|
|
2990
|
-
const malformedTwoWay = /\[\([
|
|
3070
|
+
const malformedTwoWay = /\[\([^)\]]+\s*=/g;
|
|
2991
3071
|
while ((bad = malformedTwoWay.exec(attrs)) !== null) {
|
|
2992
3072
|
pushMarker(tagOffset + 1 + bad.index, bad[0].length, monaco.MarkerSeverity.Error, 'Malformed Angular two-way binding. Use [(prop)]="...".');
|
|
2993
3073
|
}
|
|
@@ -3065,7 +3145,7 @@ class HtmlProvider {
|
|
|
3065
3145
|
}));
|
|
3066
3146
|
suggestions.unshift(...wuicSuggestions);
|
|
3067
3147
|
}
|
|
3068
|
-
if (openTagContext
|
|
3148
|
+
if (openTagContext?.tagName.startsWith('wuic-')) {
|
|
3069
3149
|
const bindingSuggestions = this.buildAngularBindingSuggestions(openTagContext.tagName, range);
|
|
3070
3150
|
suggestions.unshift(...bindingSuggestions);
|
|
3071
3151
|
}
|
|
@@ -3074,7 +3154,7 @@ class HtmlProvider {
|
|
|
3074
3154
|
});
|
|
3075
3155
|
}
|
|
3076
3156
|
getOpenTagContext(linePrefix) {
|
|
3077
|
-
const match =
|
|
3157
|
+
const match = /<([\w-]+)\b([^<>]*)$/.exec(linePrefix);
|
|
3078
3158
|
if (!match) {
|
|
3079
3159
|
return null;
|
|
3080
3160
|
}
|
|
@@ -3165,13 +3245,13 @@ class HtmlProvider {
|
|
|
3165
3245
|
return '';
|
|
3166
3246
|
}
|
|
3167
3247
|
const normalized = input
|
|
3168
|
-
.
|
|
3169
|
-
.
|
|
3248
|
+
.replaceAll(/>\s+</g, '><')
|
|
3249
|
+
.replaceAll('\r\n', '\n')
|
|
3170
3250
|
.trim();
|
|
3171
3251
|
const withLineBreaks = normalized
|
|
3172
|
-
.
|
|
3173
|
-
.
|
|
3174
|
-
.
|
|
3252
|
+
.replaceAll('><', '>\n<')
|
|
3253
|
+
.replaceAll(/(<\/(div|section|article|main|aside|header|footer|nav|ul|ol|li|table|thead|tbody|tfoot|tr|td|th|form|fieldset|p|h[1-6]|pre|blockquote)>)/gi, '$1\n')
|
|
3254
|
+
.replaceAll(/(<(div|section|article|main|aside|header|footer|nav|ul|ol|li|table|thead|tbody|tfoot|tr|td|th|form|fieldset|p|h[1-6]|pre|blockquote)(\s[^>]*)?>)/gi, '\n$1')
|
|
3175
3255
|
.split('\n')
|
|
3176
3256
|
.map((line) => line.trim())
|
|
3177
3257
|
.filter((line) => line.length > 0);
|
|
@@ -3183,12 +3263,12 @@ class HtmlProvider {
|
|
|
3183
3263
|
const voidTags = new Set(['area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', 'track', 'wbr']);
|
|
3184
3264
|
for (const rawLine of withLineBreaks) {
|
|
3185
3265
|
const line = rawLine.trim();
|
|
3186
|
-
const closeMatch =
|
|
3266
|
+
const closeMatch = closingTag.exec(line);
|
|
3187
3267
|
if (closeMatch) {
|
|
3188
3268
|
indentLevel = Math.max(0, indentLevel - 1);
|
|
3189
3269
|
}
|
|
3190
3270
|
lines.push(`${' '.repeat(indentLevel)}${line}`);
|
|
3191
|
-
const openMatch =
|
|
3271
|
+
const openMatch = openingTag.exec(line);
|
|
3192
3272
|
const isSelf = selfClosing.test(line);
|
|
3193
3273
|
if (openMatch && !isSelf && !voidTags.has(openMatch[1].toLowerCase()) && !line.includes(`</${openMatch[1]}>`)) {
|
|
3194
3274
|
indentLevel += 1;
|
|
@@ -3251,7 +3331,6 @@ const WUIC_COMPONENT_SELECTORS = [
|
|
|
3251
3331
|
'wuic-workflow-runner'
|
|
3252
3332
|
];
|
|
3253
3333
|
|
|
3254
|
-
// import { TreeItemDragStartEvent, TreeItemDragEvent, TreeItemDropEvent, DropPosition, TreeItemAddRemoveArgs, TreeItem, NodeClickEvent } from '@progress/kendo-angular-treeview';
|
|
3255
3334
|
class CodeEditorComponent {
|
|
3256
3335
|
monacoLoaderService;
|
|
3257
3336
|
http;
|
|
@@ -3303,11 +3382,24 @@ class CodeEditorComponent {
|
|
|
3303
3382
|
* Proprieta di stato del componente per editor model value, usata dalla logica interna e dal template.
|
|
3304
3383
|
*/
|
|
3305
3384
|
editorModelValue = '';
|
|
3385
|
+
/**
|
|
3386
|
+
* Toggle "Struttura" per il json: se true (via @Input o
|
|
3387
|
+
* `customEditorConfig.jsonTreeToggle`) la toolbar espone il passaggio
|
|
3388
|
+
* Codice <-> property-editor treeview guidato dallo schema JSON
|
|
3389
|
+
* ({@link JsonSchemaTreeEditorComponent}). Le due viste restano allineate:
|
|
3390
|
+
* ogni modifica nella struttura riscrive model Monaco e record.
|
|
3391
|
+
*/
|
|
3392
|
+
jsonTreeToggle = false;
|
|
3393
|
+
/** true quando e' attiva la vista struttura (Monaco nascosto ma vivo). */
|
|
3394
|
+
treeViewActive = false;
|
|
3395
|
+
/** Schema JSON radice per la vista struttura (primo schema configurato). */
|
|
3396
|
+
jsonTreeSchema = null;
|
|
3397
|
+
/** Valore parsato passato alla vista struttura. */
|
|
3398
|
+
treeValue = null;
|
|
3306
3399
|
/**
|
|
3307
3400
|
* Proprieta di stato del componente per sample code, usata dalla logica interna e dal template.
|
|
3308
3401
|
*/
|
|
3309
3402
|
sampleCode = '';
|
|
3310
|
-
// originalCode = 'function x() { // TODO }';
|
|
3311
3403
|
/**
|
|
3312
3404
|
* Flag di stato che governa il comportamento UI/logico relativo a fullscreen.
|
|
3313
3405
|
*/
|
|
@@ -3420,6 +3512,16 @@ class CodeEditorComponent {
|
|
|
3420
3512
|
* (che e' tree-shaken nei build prod).
|
|
3421
3513
|
*/
|
|
3422
3514
|
hostElementRef = inject(ElementRef, { optional: true });
|
|
3515
|
+
/**
|
|
3516
|
+
* NgZone + ChangeDetectorRef via `inject()` (stesso idioma di hostElementRef,
|
|
3517
|
+
* per non toccare il costruttore a 7 argomenti posizionali). Servono al
|
|
3518
|
+
* subscribe di `isMonacoLoaded$`: il loader ngx-monaco emette dal callback di
|
|
3519
|
+
* script-load (FUORI da NgZone) e la continuation async del subscribe resta
|
|
3520
|
+
* fuori zona → `this.render = true` non triggera change detection e il
|
|
3521
|
+
* template `<ngx-monaco-editor>` non monta mai (editor mai creato).
|
|
3522
|
+
*/
|
|
3523
|
+
ngZone = inject(NgZone);
|
|
3524
|
+
cdRef = inject(ChangeDetectorRef);
|
|
3423
3525
|
t(resource, fallback) {
|
|
3424
3526
|
const translated = WtoolboxService.translationService?.instant?.(resource);
|
|
3425
3527
|
return translated && translated !== resource ? translated : fallback;
|
|
@@ -3496,8 +3598,6 @@ class CodeEditorComponent {
|
|
|
3496
3598
|
async suggest($event, ctx) {
|
|
3497
3599
|
$event.stopPropagation();
|
|
3498
3600
|
$event.preventDefault();
|
|
3499
|
-
// var resp = await WtoolboxService.promptDialog('Suggest', [{ name: 'prompt', caption: 'prompt', type: 'text' }]);
|
|
3500
|
-
// debugger;
|
|
3501
3601
|
const nextValue = String(this.field.mc_suggest_value_callback__fn(this.record, this.field, this.metaInfo, WtoolboxService) ?? '');
|
|
3502
3602
|
this.editorModelValue = nextValue;
|
|
3503
3603
|
this.record[this.field.mc_nome_colonna].next(nextValue);
|
|
@@ -3524,11 +3624,13 @@ class CodeEditorComponent {
|
|
|
3524
3624
|
// component instance on host element for tests that need direct access
|
|
3525
3625
|
// (toggle-monaco contract check, etc.).
|
|
3526
3626
|
try {
|
|
3527
|
-
(this.hostElementRef?.nativeElement)
|
|
3627
|
+
if (this.hostElementRef?.nativeElement) {
|
|
3628
|
+
this.hostElementRef.nativeElement.__wuicCmp = this;
|
|
3629
|
+
}
|
|
3528
3630
|
}
|
|
3529
3631
|
catch { /* defensive */ }
|
|
3530
3632
|
let formFieldOptions = {};
|
|
3531
|
-
if (this.field
|
|
3633
|
+
if (this.field?.extras.customEditorConfig) {
|
|
3532
3634
|
try {
|
|
3533
3635
|
formFieldOptions = this.field.extras.customEditorConfig;
|
|
3534
3636
|
}
|
|
@@ -3536,7 +3638,7 @@ class CodeEditorComponent {
|
|
|
3536
3638
|
console.error(`customEditorConfig for field ${this.field.mc_nome_colonna} error: ${e}`);
|
|
3537
3639
|
}
|
|
3538
3640
|
}
|
|
3539
|
-
this.editorOptions = { ...this.editorOptions, ...(formFieldOptions.editorOptions
|
|
3641
|
+
this.editorOptions = { ...this.editorOptions, ...(formFieldOptions.editorOptions) };
|
|
3540
3642
|
const configuredLanguage = String(formFieldOptions.editorOptions?.language || '').trim().toLowerCase();
|
|
3541
3643
|
if (configuredLanguage === 'json' || configuredLanguage === 'typescript' || configuredLanguage === 'sql' || configuredLanguage === 'csharp' || configuredLanguage === 'html') {
|
|
3542
3644
|
this.editorOptions.language = configuredLanguage;
|
|
@@ -3547,7 +3649,7 @@ class CodeEditorComponent {
|
|
|
3547
3649
|
this.editorOptions.wordWrap = this.editorOptions.wordWrap || 'on';
|
|
3548
3650
|
this.editorOptions.wrappingIndent = this.editorOptions.wrappingIndent || 'same';
|
|
3549
3651
|
this.editorOptions.suggest = {
|
|
3550
|
-
...(this.editorOptions.suggest
|
|
3652
|
+
...(this.editorOptions.suggest),
|
|
3551
3653
|
showStatusBar: false,
|
|
3552
3654
|
preview: false,
|
|
3553
3655
|
previewMode: 'subwordSmart',
|
|
@@ -3559,14 +3661,6 @@ class CodeEditorComponent {
|
|
|
3559
3661
|
}
|
|
3560
3662
|
this.bindRecordValue();
|
|
3561
3663
|
this.refreshHtmlPreview();
|
|
3562
|
-
// if (this.editorOptions.language === 'sql') {
|
|
3563
|
-
// this.sampleCode = '';
|
|
3564
|
-
// } else if (this.editorOptions.language === 'typescript') {
|
|
3565
|
-
// this.sampleCode = 'function x() {\nconsole.log("Hello world!");\n}';
|
|
3566
|
-
// } else if (this.editorOptions.language === 'json') {
|
|
3567
|
-
// this.sampleCode = '{\n\n}';
|
|
3568
|
-
// }
|
|
3569
|
-
// this.sampleCode = formFieldOptions.sampleCode || this.sampleCode;
|
|
3570
3664
|
this.monacoLoaderService.isMonacoLoaded$.pipe(filter(isLoaded => isLoaded), take(1)).subscribe(async () => {
|
|
3571
3665
|
if (this.editorOptions.language === 'sql') {
|
|
3572
3666
|
this.sqlProvider = new SqlProvider(this.http, this.editorOptions);
|
|
@@ -3578,6 +3672,10 @@ class CodeEditorComponent {
|
|
|
3578
3672
|
await this.tsProvider.registerTSProvider(formFieldOptions, this.field, this.codeContext, this.record, this.metaSrv, this.userInfo);
|
|
3579
3673
|
}
|
|
3580
3674
|
else if (this.editorOptions.language === 'json') {
|
|
3675
|
+
// Vista "Struttura" (property-editor treeview da schema): abilitabile
|
|
3676
|
+
// via @Input jsonTreeToggle o `customEditorConfig.jsonTreeToggle`.
|
|
3677
|
+
this.jsonTreeToggle = this.jsonTreeToggle || formFieldOptions.jsonTreeToggle === true;
|
|
3678
|
+
this.jsonTreeSchema = (formFieldOptions.schemas?.[0])?.schema || null;
|
|
3581
3679
|
// https://json-schema.org/learn/getting-started-step-by-step
|
|
3582
3680
|
// string.
|
|
3583
3681
|
// number.
|
|
@@ -3612,7 +3710,16 @@ class CodeEditorComponent {
|
|
|
3612
3710
|
this.htmlProvider = new HtmlProvider(this.editorOptions, WUIC_COMPONENT_SELECTORS, WUIC_COMPONENT_BINDINGS);
|
|
3613
3711
|
this.htmlProvider.registerHtmlProvider();
|
|
3614
3712
|
}
|
|
3615
|
-
|
|
3713
|
+
// FIX (2026-07-22): rientra in NgZone e forza CD. La emit di
|
|
3714
|
+
// isMonacoLoaded$ arriva dal callback script-load di ngx-monaco (fuori
|
|
3715
|
+
// zona) e gli `await` sopra mantengono la continuation fuori zona:
|
|
3716
|
+
// senza run()+detectChanges il flag non rirenderizza il template e
|
|
3717
|
+
// Monaco non viene mai istanziato (test toggle-monaco: render=false /
|
|
3718
|
+
// ngxCount=0 deterministico).
|
|
3719
|
+
this.ngZone.run(() => {
|
|
3720
|
+
this.render = true;
|
|
3721
|
+
this.cdRef.detectChanges();
|
|
3722
|
+
});
|
|
3616
3723
|
});
|
|
3617
3724
|
}
|
|
3618
3725
|
/**
|
|
@@ -3631,7 +3738,7 @@ class CodeEditorComponent {
|
|
|
3631
3738
|
wordWrap: this.editorOptions.wordWrap || 'on',
|
|
3632
3739
|
wrappingIndent: this.editorOptions.wrappingIndent || 'same',
|
|
3633
3740
|
suggest: {
|
|
3634
|
-
...(this.editorOptions.suggest
|
|
3741
|
+
...(this.editorOptions.suggest),
|
|
3635
3742
|
showStatusBar: false,
|
|
3636
3743
|
preview: false,
|
|
3637
3744
|
previewMode: 'subwordSmart',
|
|
@@ -3660,7 +3767,9 @@ class CodeEditorComponent {
|
|
|
3660
3767
|
*/
|
|
3661
3768
|
ngOnDestroy() {
|
|
3662
3769
|
try {
|
|
3663
|
-
|
|
3770
|
+
if (this.hostElementRef?.nativeElement) {
|
|
3771
|
+
delete this.hostElementRef.nativeElement.__wuicCmp;
|
|
3772
|
+
}
|
|
3664
3773
|
}
|
|
3665
3774
|
catch { /* defensive */ }
|
|
3666
3775
|
if (this.layoutRaf !== null) {
|
|
@@ -3701,7 +3810,7 @@ class CodeEditorComponent {
|
|
|
3701
3810
|
const hosts = [];
|
|
3702
3811
|
let current = start?.parentElement || null;
|
|
3703
3812
|
while (current) {
|
|
3704
|
-
const style =
|
|
3813
|
+
const style = globalThis.getComputedStyle(current);
|
|
3705
3814
|
const overflowY = style.overflowY || '';
|
|
3706
3815
|
const isScrollableY = (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay')
|
|
3707
3816
|
&& current.scrollHeight > current.clientHeight;
|
|
@@ -3772,6 +3881,59 @@ class CodeEditorComponent {
|
|
|
3772
3881
|
this.skipNextAutoFormat = true;
|
|
3773
3882
|
this.editor.getAction('editor.action.formatDocument')?.run().catch(() => undefined);
|
|
3774
3883
|
}
|
|
3884
|
+
/**
|
|
3885
|
+
* Toggle Codice <-> Struttura (json): attivando la struttura il testo
|
|
3886
|
+
* corrente viene parsato (JSON invalido -> toast e resta il codice);
|
|
3887
|
+
* Monaco resta VIVO ma nascosto, cosi' il rientro e' istantaneo.
|
|
3888
|
+
*/
|
|
3889
|
+
toggleJsonTree($event) {
|
|
3890
|
+
$event?.stopPropagation?.();
|
|
3891
|
+
$event?.preventDefault?.();
|
|
3892
|
+
if (!this.isJsonEditor || !this.jsonTreeToggle) {
|
|
3893
|
+
return;
|
|
3894
|
+
}
|
|
3895
|
+
if (this.treeViewActive) {
|
|
3896
|
+
this.treeViewActive = false;
|
|
3897
|
+
this.scheduleEditorLayout();
|
|
3898
|
+
return;
|
|
3899
|
+
}
|
|
3900
|
+
const raw = String(this.editor?.getValue?.() ?? this.editorModelValue ?? '');
|
|
3901
|
+
let parsed = {};
|
|
3902
|
+
if (raw.trim()) {
|
|
3903
|
+
try {
|
|
3904
|
+
parsed = JSON.parse(raw);
|
|
3905
|
+
}
|
|
3906
|
+
catch {
|
|
3907
|
+
WtoolboxService.messageNotificationService.add({
|
|
3908
|
+
severity: 'error',
|
|
3909
|
+
summary: 'json_format_error',
|
|
3910
|
+
detail: this.t('json_format_error_detail', 'JSON non valido: impossibile formattare.')
|
|
3911
|
+
});
|
|
3912
|
+
return;
|
|
3913
|
+
}
|
|
3914
|
+
}
|
|
3915
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
3916
|
+
WtoolboxService.messageNotificationService.add({
|
|
3917
|
+
severity: 'warn',
|
|
3918
|
+
summary: 'json_tree_root_object',
|
|
3919
|
+
detail: this.t('json_tree_root_object_detail', 'La vista struttura richiede un oggetto JSON alla radice.')
|
|
3920
|
+
});
|
|
3921
|
+
return;
|
|
3922
|
+
}
|
|
3923
|
+
this.treeValue = parsed;
|
|
3924
|
+
this.treeViewActive = true;
|
|
3925
|
+
}
|
|
3926
|
+
/**
|
|
3927
|
+
* Modifica dalla vista struttura: riscrive il JSON formattato nel model
|
|
3928
|
+
* Monaco e nel record (stesso percorso di formatJsonNow), cosi' le due
|
|
3929
|
+
* viste e il valore persistito restano allineati.
|
|
3930
|
+
*/
|
|
3931
|
+
onTreeValueChange(value) {
|
|
3932
|
+
const pretty = JSON.stringify(value ?? {}, null, 2);
|
|
3933
|
+
this.editorModelValue = pretty;
|
|
3934
|
+
this.editor?.setValue(pretty);
|
|
3935
|
+
this.record?.[this.field.mc_nome_colonna]?.next(pretty);
|
|
3936
|
+
}
|
|
3775
3937
|
/**
|
|
3776
3938
|
* Gestisce la logica operativa di `formatJsonNow` orchestrando parse/stringify JSON e sync nel model editor.
|
|
3777
3939
|
* @param $event Evento UI o payload evento che innesca il flusso del metodo.
|
|
@@ -3873,7 +4035,7 @@ class CodeEditorComponent {
|
|
|
3873
4035
|
const replacement = doc.createElement('div');
|
|
3874
4036
|
const ngIfAttr = node.getAttribute('*ngif') || node.getAttribute('*ngIf') || '';
|
|
3875
4037
|
const captionMatch = /getMetaColumn\(\s*['"]([^'"]+)['"]\s*\)/i.exec(ngIfAttr);
|
|
3876
|
-
if (captionMatch
|
|
4038
|
+
if (captionMatch?.[1]) {
|
|
3877
4039
|
replacement.setAttribute('data-wuic-caption', captionMatch[1]);
|
|
3878
4040
|
}
|
|
3879
4041
|
Array.from(node.attributes).forEach((attr) => replacement.setAttribute(attr.name, attr.value));
|
|
@@ -3947,7 +4109,7 @@ class CodeEditorComponent {
|
|
|
3947
4109
|
}
|
|
3948
4110
|
const ngIfAttr = current.getAttribute('*ngif') || current.getAttribute('*ngIf') || '';
|
|
3949
4111
|
const match = /getMetaColumn\(\s*['"]([^'"]+)['"]\s*\)/i.exec(ngIfAttr);
|
|
3950
|
-
if (match
|
|
4112
|
+
if (match?.[1]) {
|
|
3951
4113
|
return match[1];
|
|
3952
4114
|
}
|
|
3953
4115
|
current = current.parentElement;
|
|
@@ -3969,13 +4131,13 @@ class CodeEditorComponent {
|
|
|
3969
4131
|
children: this.mapToPrimeTreeNodes(node?.items || [])
|
|
3970
4132
|
}));
|
|
3971
4133
|
}
|
|
3972
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: CodeEditorComponent, deps: [{ token: i1.MonacoEditorLoaderService }, { token: i1$
|
|
3973
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: CodeEditorComponent, isStandalone: true, selector: "wuic-code-editor", inputs: { field: "field", record: "record", metaInfo: "metaInfo" }, host: { properties: { "style": "this.style" } }, viewQueries: [{ propertyName: "contextMenuAnchor", first: true, predicate: ["contextMenuAnchor"], descendants: true }, { propertyName: "editorRef", first: true, predicate: ["editorRef"], descendants: true }, { propertyName: "ctxs", first: true, predicate: ["ctxs"], descendants: true }], ngImport: i0, template: "@if (render) {\n<div [ngClass]=\"{'fullscreen': fullscreen, 'no-full-screen': !fullscreen }\">\n <div\n [ngStyle]=\"{width: sqlObjView ? '22%' : null, display: sqlObjView ? null : 'none', position: fullscreen ? 'absolute' : null, 'left': fullscreen ? '0px' : null, 'top': fullscreen ? '0px' : null }\"\n style=\"float: left; height: 100%; overflow: auto; background-color: #1e1e1e; z-index: 2000;\">\n <p-tree [value]=\"primeTreeNodes\">\n <ng-template let-node pTemplate=\"default\">\n <span style=\"-webkit-user-select: none; -ms-user-select: none; user-select: none;\" draggable=\"true\"\n (dragstart)=\"onTreeNodeDragStart($event, node.data)\" (click)=\"editorOptions.onNodeClick($event, node.data)\"\n (dragend)=\"onTreeNodeDragEnd($event, node.data)\">\n <span>\n {{ node.label }}\n </span>\n </span>\n </ng-template>\n </p-tree>\n </div>\n <div [ngStyle]=\"{width: sqlObjView ? '78%' : '100%', 'left': fullscreen ? (!sqlObjView ? '0px' : '22%') : null }\"\n style=\"height: 100%; float: left;\">\n <ul class=\"editor-btn\">\n <li>\n <div (click)=\"setFullScreen($event, this)\">\n <i\n [ngClass]=\"{'pi': true, 'pi-window-maximize': fullscreen == false, 'pi-window-minimize': fullscreen == true}\"></i>\n </div>\n </li>\n @if (editorOptions.language == 'sql') {\n <li>\n <div (click)=\"toggleSqlObjView($event, this)\">\n <i [ngClass]=\"{'pi': true, 'pi-server': true}\"></i>\n </div>\n </li>\n }\n @if (editorOptions.language == 'sql') {\n <li>\n <div (click)=\"testSql($event, this)\">\n <i [ngClass]=\"{'pi': true, 'pi-verified': true}\"></i>\n </div>\n </li>\n }\n @if (field.mc_suggest_value_callback) {\n <li>\n <div (click)=\"suggest($event, this)\">\n <i [ngClass]=\"{'pi': true, 'pi-question': true}\"></i>\n </div>\n </li>\n }\n @if (isHtmlEditor) {\n <li>\n <div (click)=\"formatHtmlNow($event)\" title=\"Format HTML\">\n <i [ngClass]=\"{'pi': true, 'pi-align-left': true}\"></i>\n </div>\n </li>\n <li>\n <div (click)=\"toggleHtmlPreview($event)\" title=\"Toggle HTML preview\">\n <i [ngClass]=\"{'pi': true, 'pi-eye': showHtmlPreview, 'pi-eye-slash': !showHtmlPreview}\"></i>\n </div>\n </li>\n }\n @if (isJsonEditor) {\n <li>\n <div (click)=\"formatJsonNow($event)\" title=\"Format JSON\">\n <i [ngClass]=\"{'pi': true, 'pi-code': true}\"></i>\n </div>\n </li>\n }\n </ul>\n <div class=\"editor-shell\">\n <div class=\"editor-pane\" [ngClass]=\"{'editor-pane--with-preview': isHtmlEditor && showHtmlPreview}\">\n <ngx-monaco-editor #editorRef [options]=\"editorOptions\" [ngModel]=\"editorModelValue\"\n (ngModelChange)=\"onEditorModelChange($event)\"\n [ngModelOptions]=\"{standalone: true}\"\n [ngStyle]=\"{'left': fullscreen ? (!sqlObjView ? '0px' : '22%') : null, width: '100%' }\"\n style=\"width: 100%; height: 100%;\"\n (onDropIntoEditor)=\"editorOptions.onDropIntoEditor($event)\"></ngx-monaco-editor>\n </div>\n @if (isHtmlEditor && showHtmlPreview) {\n <div class=\"html-preview-pane\">\n <div class=\"html-preview-pane__header\">HTML Pseudo-Preview (sanitized)</div>\n <div class=\"html-preview-pane__body\" [innerHTML]=\"sanitizedHtmlPreview\"></div>\n </div>\n }\n </div>\n </div>\n</div>\n}\n\n@if (editorRef?.editorContent?.nativeElement) {\n<p-contextMenu [target]=\"editorRef?.editorContent?.nativeElement\" [model]=\"editorOptions.contextMenuItems\"\n appendTo=\"body\" />\n}\n", styles: [".editor-btn{color:#fff;list-style-type:none;z-index:2001;visibility:visible;position:absolute;right:10px;top:10px;display:flex;gap:8px;margin:0;padding:0}.editor-btn li{list-style:none}.editor-btn li div{width:24px;height:24px;display:flex;align-items:center;justify-content:center;border:1px solid #334155;border-radius:4px;background:#0f172ad9;cursor:pointer}.fullscreen{position:fixed;inset:16px;z-index:3000;background:#1e1e1e;width:auto;height:auto}.fullscreen .editor-shell{position:absolute;inset:44px 0 0;height:auto}.fullscreen .editor-pane ngx-monaco-editor{position:relative;height:auto!important}.no-full-screen{width:100%;height:200px;position:relative}.editor-container{overflow:visible!important}.editor-shell{display:flex;width:100%;height:100%;min-height:0}.editor-pane{flex:1 1 auto;min-width:0;height:100%}.editor-pane--with-preview{flex:0 0 62%}.html-preview-pane{flex:0 0 38%;min-width:260px;border-left:1px solid #404040;background:#111827;color:#e5e7eb;display:flex;flex-direction:column}.html-preview-pane__header{font-size:12px;letter-spacing:.04em;text-transform:uppercase;padding:10px 12px;border-bottom:1px solid #374151;color:#9ca3af}.html-preview-pane__body{flex:1 1 auto;overflow:auto;padding:12px;background:#fff;color:#111827}:host ::ng-deep .html-preview-pane__body .wuic-preview-component{border:1px solid #cbd5e1;border-left:4px solid #2563eb;border-radius:6px;padding:8px 10px;margin-bottom:10px;background:#f8fafc}:host ::ng-deep .html-preview-pane__body .wuic-preview-component__title{font-weight:600;color:#0f172a;margin-bottom:4px}:host ::ng-deep .html-preview-pane__body .wuic-preview-component__content{margin-top:8px}:host ::ng-deep .html-preview-pane__body .wuic-preview-field__label{display:block;font-size:13px;font-weight:600;color:#0f172a;margin-bottom:6px}:host ::ng-deep .html-preview-pane__body .wuic-preview-field__input{display:block;width:100%;border:1px solid #3b82f6;border-radius:4px;min-height:36px;box-sizing:border-box;background:#fff}:host ::ng-deep .monaco-editor .suggest-widget,:host ::ng-deep .monaco-editor .parameter-hints-widget,:host ::ng-deep .monaco-editor .monaco-hover{z-index:4000!important}:host ::ng-deep .monaco-editor .suggest-widget{height:220px!important;margin-top:-30px!important}:host ::ng-deep .monaco-editor .suggest-widget .tree,:host ::ng-deep .monaco-editor .suggest-widget .monaco-list,:host ::ng-deep .monaco-editor .suggest-widget .monaco-list-rows,:host ::ng-deep .monaco-editor .suggest-widget .monaco-scrollable-element{height:186px!important;max-height:186px!important}:host ::ng-deep .monaco-editor .suggest-widget .monaco-list-rows{box-sizing:border-box;padding-bottom:20px!important}::-webkit-scrollbar{width:12px}::-webkit-scrollbar-track{box-shadow:inset 0 0 5px gray;border-radius:10px}::-webkit-scrollbar-thumb{background:#ffffff80;border-radius:10px;visibility:hidden}::-webkit-scrollbar-thumb:hover{visibility:visible}::-webkit-scrollbar-thumb:hover{background:#fffc}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MonacoEditorModule }, { kind: "component", type: i1.MonacoEditorComponent, selector: "ngx-monaco-editor", inputs: ["options", "uri"], outputs: ["init"] }, { kind: "ngmodule", type: ContextMenuModule }, { kind: "component", type: i14.ContextMenu, selector: "p-contextMenu, p-contextmenu, p-context-menu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "autoZIndex", "baseZIndex", "id", "breakpoint", "ariaLabel", "ariaLabelledBy", "pressDelay", "appendTo", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "directive", type: i3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: TreeModule }, { kind: "component", type: i4.Tree, selector: "p-tree", inputs: ["value", "selectionMode", "loadingMode", "selection", "styleClass", "contextMenu", "contextMenuSelectionMode", "contextMenuSelection", "draggableScope", "droppableScope", "draggableNodes", "droppableNodes", "metaKeySelection", "propagateSelectionUp", "propagateSelectionDown", "loading", "loadingIcon", "emptyMessage", "ariaLabel", "togglerAriaLabel", "ariaLabelledBy", "validateDrop", "filter", "filterInputAutoFocus", "filterBy", "filterMode", "filterOptions", "filterPlaceholder", "filteredNodes", "filterLocale", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "indentation", "_templateMap", "trackBy", "highlightOnSelect"], outputs: ["selectionChange", "contextMenuSelectionChange", "onNodeSelect", "onNodeUnselect", "onNodeExpand", "onNodeCollapse", "onNodeContextMenuSelect", "onNodeDoubleClick", "onNodeDrop", "onLazyLoad", "onScroll", "onScrollIndexChange", "onFilter"] }] });
|
|
4134
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: CodeEditorComponent, deps: [{ token: i1$1.MonacoEditorLoaderService }, { token: i1$2.HttpClient }, { token: MetadataProviderService }, { token: UserInfoService }, { token: DataProviderService }, { token: WtoolboxService }, { token: i1$3.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component });
|
|
4135
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.9", type: CodeEditorComponent, isStandalone: true, selector: "wuic-code-editor", inputs: { field: "field", record: "record", metaInfo: "metaInfo", jsonTreeToggle: "jsonTreeToggle" }, host: { properties: { "style": "this.style" } }, viewQueries: [{ propertyName: "contextMenuAnchor", first: true, predicate: ["contextMenuAnchor"], descendants: true }, { propertyName: "editorRef", first: true, predicate: ["editorRef"], descendants: true }, { propertyName: "ctxs", first: true, predicate: ["ctxs"], descendants: true }], ngImport: i0, template: "@if (render) {\n<div [ngClass]=\"{'fullscreen': fullscreen, 'no-full-screen': !fullscreen }\">\n <div\n [ngStyle]=\"{width: sqlObjView ? '22%' : null, display: sqlObjView ? null : 'none', position: fullscreen ? 'absolute' : null, 'left': fullscreen ? '0px' : null, 'top': fullscreen ? '0px' : null }\"\n style=\"float: left; height: 100%; overflow: auto; background-color: #1e1e1e; z-index: 2000;\">\n <p-tree [value]=\"primeTreeNodes\">\n <ng-template let-node pTemplate=\"default\">\n <span style=\"-webkit-user-select: none; -ms-user-select: none; user-select: none;\" draggable=\"true\"\n (dragstart)=\"onTreeNodeDragStart($event, node.data)\" (click)=\"editorOptions.onNodeClick($event, node.data)\"\n (dragend)=\"onTreeNodeDragEnd($event, node.data)\">\n <span>\n {{ node.label }}\n </span>\n </span>\n </ng-template>\n </p-tree>\n </div>\n <div [ngStyle]=\"{width: sqlObjView ? '78%' : '100%', 'left': fullscreen ? (!sqlObjView ? '0px' : '22%') : null }\"\n style=\"height: 100%; float: left;\">\n <ul class=\"editor-btn\">\n <li>\n <div (click)=\"setFullScreen($event, this)\">\n <i\n [ngClass]=\"{'pi': true, 'pi-window-maximize': fullscreen == false, 'pi-window-minimize': fullscreen == true}\"></i>\n </div>\n </li>\n @if (editorOptions.language == 'sql') {\n <li>\n <div (click)=\"toggleSqlObjView($event, this)\">\n <i [ngClass]=\"{'pi': true, 'pi-server': true}\"></i>\n </div>\n </li>\n }\n @if (editorOptions.language == 'sql') {\n <li>\n <div (click)=\"testSql($event, this)\">\n <i [ngClass]=\"{'pi': true, 'pi-verified': true}\"></i>\n </div>\n </li>\n }\n @if (field.mc_suggest_value_callback) {\n <li>\n <div (click)=\"suggest($event, this)\">\n <i [ngClass]=\"{'pi': true, 'pi-question': true}\"></i>\n </div>\n </li>\n }\n @if (isHtmlEditor) {\n <li>\n <div (click)=\"formatHtmlNow($event)\" title=\"Format HTML\">\n <i [ngClass]=\"{'pi': true, 'pi-align-left': true}\"></i>\n </div>\n </li>\n <li>\n <div (click)=\"toggleHtmlPreview($event)\" title=\"Toggle HTML preview\">\n <i [ngClass]=\"{'pi': true, 'pi-eye': showHtmlPreview, 'pi-eye-slash': !showHtmlPreview}\"></i>\n </div>\n </li>\n }\n @if (isJsonEditor) {\n <li>\n <div (click)=\"formatJsonNow($event)\" title=\"Format JSON\">\n <i [ngClass]=\"{'pi': true, 'pi-code': true}\"></i>\n </div>\n </li>\n }\n @if (isJsonEditor && jsonTreeToggle) {\n <li>\n <div (click)=\"toggleJsonTree($event)\" [title]=\"treeViewActive ? 'Codice' : 'Struttura'\">\n <i [ngClass]=\"{'pi': true, 'pi-sitemap': !treeViewActive, 'pi-pencil': treeViewActive}\"></i>\n </div>\n </li>\n }\n </ul>\n <div class=\"editor-shell\">\n <!-- Monaco resta VIVO anche in vista struttura (solo nascosto): il model\n e il record vengono riscritti a ogni modifica della struttura. -->\n <div class=\"editor-pane\" [ngClass]=\"{'editor-pane--with-preview': isHtmlEditor && showHtmlPreview}\"\n [style.display]=\"treeViewActive ? 'none' : null\">\n <ngx-monaco-editor #editorRef [options]=\"editorOptions\" [ngModel]=\"editorModelValue\"\n (ngModelChange)=\"onEditorModelChange($event)\"\n [ngModelOptions]=\"{standalone: true}\"\n [ngStyle]=\"{'left': fullscreen ? (!sqlObjView ? '0px' : '22%') : null, width: '100%' }\"\n style=\"width: 100%; height: 100%;\"\n (onDropIntoEditor)=\"editorOptions.onDropIntoEditor($event)\"></ngx-monaco-editor>\n </div>\n @if (treeViewActive) {\n <div class=\"editor-pane\">\n <wuic-json-schema-tree-editor\n [schema]=\"jsonTreeSchema\"\n [value]=\"treeValue\"\n (valueChange)=\"onTreeValueChange($event)\" />\n </div>\n }\n @if (isHtmlEditor && showHtmlPreview) {\n <div class=\"html-preview-pane\">\n <div class=\"html-preview-pane__header\">HTML Pseudo-Preview (sanitized)</div>\n <div class=\"html-preview-pane__body\" [innerHTML]=\"sanitizedHtmlPreview\"></div>\n </div>\n }\n </div>\n </div>\n</div>\n} @else {\n <!-- Monaco viene inizializzato in modo asincrono (loader + registrazione\n language provider) DOPO che il @defer del code-area-editor ha gia'\n terminato il fetch del chunk: senza questo placeholder la sezione\n collasserebbe a nulla in quel gap. Riserva l'altezza dell'editor\n (200px, come .no-full-screen) per evitare il salto di layout. -->\n <div class=\"code-editor-monaco-loading\">\n <i class=\"pi pi-spin pi-spinner\"></i> Inizializzazione editor\u2026\n </div>\n}\n\n@if (editorRef?.editorContent?.nativeElement) {\n<p-contextMenu [target]=\"editorRef?.editorContent?.nativeElement\" [model]=\"editorOptions.contextMenuItems\"\n appendTo=\"body\" />\n}\n", styles: [".editor-btn{color:#fff;list-style-type:none;z-index:2001;visibility:visible;position:absolute;right:10px;top:10px;display:flex;gap:8px;margin:0;padding:0}.editor-btn li{list-style:none}.editor-btn li div{width:24px;height:24px;display:flex;align-items:center;justify-content:center;border:1px solid #334155;border-radius:4px;background:#0f172ad9;cursor:pointer}.fullscreen{position:fixed;inset:16px;z-index:3000;background:#1e1e1e;width:auto;height:auto}.fullscreen .editor-shell{position:absolute;inset:44px 0 0;height:auto}.fullscreen .editor-pane ngx-monaco-editor{position:relative;height:auto!important}.no-full-screen{width:100%;height:200px;position:relative}.editor-container{overflow:visible!important}.editor-shell{display:flex;width:100%;height:100%;min-height:0}.editor-pane{flex:1 1 auto;min-width:0;height:100%}.editor-pane--with-preview{flex:0 0 62%}.code-editor-monaco-loading{display:flex;align-items:center;justify-content:center;gap:8px;height:200px;color:#64748b;font-size:.9rem;background:#f1f5f9;border:1px dashed #cbd5e1;border-radius:6px}.code-editor-monaco-loading .pi-spinner{font-size:1rem}.html-preview-pane{flex:0 0 38%;min-width:260px;border-left:1px solid #404040;background:#111827;color:#e5e7eb;display:flex;flex-direction:column}.html-preview-pane__header{font-size:12px;letter-spacing:.04em;text-transform:uppercase;padding:10px 12px;border-bottom:1px solid #374151;color:#9ca3af}.html-preview-pane__body{flex:1 1 auto;overflow:auto;padding:12px;background:#fff;color:#111827}:host ::ng-deep .html-preview-pane__body .wuic-preview-component{border:1px solid #cbd5e1;border-left:4px solid #2563eb;border-radius:6px;padding:8px 10px;margin-bottom:10px;background:#f8fafc}:host ::ng-deep .html-preview-pane__body .wuic-preview-component__title{font-weight:600;color:#0f172a;margin-bottom:4px}:host ::ng-deep .html-preview-pane__body .wuic-preview-component__content{margin-top:8px}:host ::ng-deep .html-preview-pane__body .wuic-preview-field__label{display:block;font-size:13px;font-weight:600;color:#0f172a;margin-bottom:6px}:host ::ng-deep .html-preview-pane__body .wuic-preview-field__input{display:block;width:100%;border:1px solid #3b82f6;border-radius:4px;min-height:36px;box-sizing:border-box;background:#fff}:host ::ng-deep .monaco-editor .suggest-widget,:host ::ng-deep .monaco-editor .parameter-hints-widget,:host ::ng-deep .monaco-editor .monaco-hover{z-index:4000!important}:host ::ng-deep .monaco-editor .suggest-widget{height:220px!important;margin-top:-30px!important}:host ::ng-deep .monaco-editor .suggest-widget .tree,:host ::ng-deep .monaco-editor .suggest-widget .monaco-list,:host ::ng-deep .monaco-editor .suggest-widget .monaco-list-rows,:host ::ng-deep .monaco-editor .suggest-widget .monaco-scrollable-element{height:186px!important;max-height:186px!important}:host ::ng-deep .monaco-editor .suggest-widget .monaco-list-rows{box-sizing:border-box;padding-bottom:20px!important}::-webkit-scrollbar{width:12px}::-webkit-scrollbar-track{box-shadow:inset 0 0 5px gray;border-radius:10px}::-webkit-scrollbar-thumb{background:#ffffff80;border-radius:10px;visibility:hidden}::-webkit-scrollbar-thumb:hover{visibility:visible;background:#fffc}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "ngmodule", type: MonacoEditorModule }, { kind: "component", type: i1$1.MonacoEditorComponent, selector: "ngx-monaco-editor", inputs: ["options", "uri"], outputs: ["init"] }, { kind: "ngmodule", type: ContextMenuModule }, { kind: "component", type: i14.ContextMenu, selector: "p-contextMenu, p-contextmenu, p-context-menu", inputs: ["model", "triggerEvent", "target", "global", "style", "styleClass", "autoZIndex", "baseZIndex", "id", "breakpoint", "ariaLabel", "ariaLabelledBy", "pressDelay", "appendTo", "motionOptions"], outputs: ["onShow", "onHide"] }, { kind: "directive", type: i3.PrimeTemplate, selector: "[pTemplate]", inputs: ["type", "pTemplate"] }, { kind: "ngmodule", type: TreeModule }, { kind: "component", type: i4.Tree, selector: "p-tree", inputs: ["value", "selectionMode", "loadingMode", "selection", "styleClass", "contextMenu", "contextMenuSelectionMode", "contextMenuSelection", "draggableScope", "droppableScope", "draggableNodes", "droppableNodes", "metaKeySelection", "propagateSelectionUp", "propagateSelectionDown", "loading", "loadingIcon", "emptyMessage", "ariaLabel", "togglerAriaLabel", "ariaLabelledBy", "validateDrop", "filter", "filterInputAutoFocus", "filterBy", "filterMode", "filterOptions", "filterPlaceholder", "filteredNodes", "filterLocale", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "indentation", "_templateMap", "trackBy", "highlightOnSelect"], outputs: ["selectionChange", "contextMenuSelectionChange", "onNodeSelect", "onNodeUnselect", "onNodeExpand", "onNodeCollapse", "onNodeContextMenuSelect", "onNodeDoubleClick", "onNodeDrop", "onLazyLoad", "onScroll", "onScrollIndexChange", "onFilter"] }, { kind: "component", type: JsonSchemaTreeEditorComponent, selector: "wuic-json-schema-tree-editor", inputs: ["schema", "value"], outputs: ["valueChange"] }] });
|
|
3974
4136
|
}
|
|
3975
4137
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImport: i0, type: CodeEditorComponent, decorators: [{
|
|
3976
4138
|
type: Component,
|
|
3977
|
-
args: [{ selector: 'wuic-code-editor', imports: [NgClass, NgStyle, FormsModule, MonacoEditorModule, ContextMenuModule, TreeModule], template: "@if (render) {\n<div [ngClass]=\"{'fullscreen': fullscreen, 'no-full-screen': !fullscreen }\">\n <div\n [ngStyle]=\"{width: sqlObjView ? '22%' : null, display: sqlObjView ? null : 'none', position: fullscreen ? 'absolute' : null, 'left': fullscreen ? '0px' : null, 'top': fullscreen ? '0px' : null }\"\n style=\"float: left; height: 100%; overflow: auto; background-color: #1e1e1e; z-index: 2000;\">\n <p-tree [value]=\"primeTreeNodes\">\n <ng-template let-node pTemplate=\"default\">\n <span style=\"-webkit-user-select: none; -ms-user-select: none; user-select: none;\" draggable=\"true\"\n (dragstart)=\"onTreeNodeDragStart($event, node.data)\" (click)=\"editorOptions.onNodeClick($event, node.data)\"\n (dragend)=\"onTreeNodeDragEnd($event, node.data)\">\n <span>\n {{ node.label }}\n </span>\n </span>\n </ng-template>\n </p-tree>\n </div>\n <div [ngStyle]=\"{width: sqlObjView ? '78%' : '100%', 'left': fullscreen ? (!sqlObjView ? '0px' : '22%') : null }\"\n style=\"height: 100%; float: left;\">\n <ul class=\"editor-btn\">\n <li>\n <div (click)=\"setFullScreen($event, this)\">\n <i\n [ngClass]=\"{'pi': true, 'pi-window-maximize': fullscreen == false, 'pi-window-minimize': fullscreen == true}\"></i>\n </div>\n </li>\n @if (editorOptions.language == 'sql') {\n <li>\n <div (click)=\"toggleSqlObjView($event, this)\">\n <i [ngClass]=\"{'pi': true, 'pi-server': true}\"></i>\n </div>\n </li>\n }\n @if (editorOptions.language == 'sql') {\n <li>\n <div (click)=\"testSql($event, this)\">\n <i [ngClass]=\"{'pi': true, 'pi-verified': true}\"></i>\n </div>\n </li>\n }\n @if (field.mc_suggest_value_callback) {\n <li>\n <div (click)=\"suggest($event, this)\">\n <i [ngClass]=\"{'pi': true, 'pi-question': true}\"></i>\n </div>\n </li>\n }\n @if (isHtmlEditor) {\n <li>\n <div (click)=\"formatHtmlNow($event)\" title=\"Format HTML\">\n <i [ngClass]=\"{'pi': true, 'pi-align-left': true}\"></i>\n </div>\n </li>\n <li>\n <div (click)=\"toggleHtmlPreview($event)\" title=\"Toggle HTML preview\">\n <i [ngClass]=\"{'pi': true, 'pi-eye': showHtmlPreview, 'pi-eye-slash': !showHtmlPreview}\"></i>\n </div>\n </li>\n }\n @if (isJsonEditor) {\n <li>\n <div (click)=\"formatJsonNow($event)\" title=\"Format JSON\">\n <i [ngClass]=\"{'pi': true, 'pi-code': true}\"></i>\n </div>\n </li>\n }\n </ul>\n <div class=\"editor-shell\">\n <div class=\"editor-pane\" [ngClass]=\"{'editor-pane--with-preview': isHtmlEditor && showHtmlPreview}\">\n <ngx-monaco-editor #editorRef [options]=\"editorOptions\" [ngModel]=\"editorModelValue\"\n (ngModelChange)=\"onEditorModelChange($event)\"\n [ngModelOptions]=\"{standalone: true}\"\n [ngStyle]=\"{'left': fullscreen ? (!sqlObjView ? '0px' : '22%') : null, width: '100%' }\"\n style=\"width: 100%; height: 100%;\"\n (onDropIntoEditor)=\"editorOptions.onDropIntoEditor($event)\"></ngx-monaco-editor>\n </div>\n @if (isHtmlEditor && showHtmlPreview) {\n <div class=\"html-preview-pane\">\n <div class=\"html-preview-pane__header\">HTML Pseudo-Preview (sanitized)</div>\n <div class=\"html-preview-pane__body\" [innerHTML]=\"sanitizedHtmlPreview\"></div>\n </div>\n }\n </div>\n </div>\n</div>\n}\n\n@if (editorRef?.editorContent?.nativeElement) {\n<p-contextMenu [target]=\"editorRef?.editorContent?.nativeElement\" [model]=\"editorOptions.contextMenuItems\"\n appendTo=\"body\" />\n}\n", styles: [".editor-btn{color:#fff;list-style-type:none;z-index:2001;visibility:visible;position:absolute;right:10px;top:10px;display:flex;gap:8px;margin:0;padding:0}.editor-btn li{list-style:none}.editor-btn li div{width:24px;height:24px;display:flex;align-items:center;justify-content:center;border:1px solid #334155;border-radius:4px;background:#0f172ad9;cursor:pointer}.fullscreen{position:fixed;inset:16px;z-index:3000;background:#1e1e1e;width:auto;height:auto}.fullscreen .editor-shell{position:absolute;inset:44px 0 0;height:auto}.fullscreen .editor-pane ngx-monaco-editor{position:relative;height:auto!important}.no-full-screen{width:100%;height:200px;position:relative}.editor-container{overflow:visible!important}.editor-shell{display:flex;width:100%;height:100%;min-height:0}.editor-pane{flex:1 1 auto;min-width:0;height:100%}.editor-pane--with-preview{flex:0 0 62%}.html-preview-pane{flex:0 0 38%;min-width:260px;border-left:1px solid #404040;background:#111827;color:#e5e7eb;display:flex;flex-direction:column}.html-preview-pane__header{font-size:12px;letter-spacing:.04em;text-transform:uppercase;padding:10px 12px;border-bottom:1px solid #374151;color:#9ca3af}.html-preview-pane__body{flex:1 1 auto;overflow:auto;padding:12px;background:#fff;color:#111827}:host ::ng-deep .html-preview-pane__body .wuic-preview-component{border:1px solid #cbd5e1;border-left:4px solid #2563eb;border-radius:6px;padding:8px 10px;margin-bottom:10px;background:#f8fafc}:host ::ng-deep .html-preview-pane__body .wuic-preview-component__title{font-weight:600;color:#0f172a;margin-bottom:4px}:host ::ng-deep .html-preview-pane__body .wuic-preview-component__content{margin-top:8px}:host ::ng-deep .html-preview-pane__body .wuic-preview-field__label{display:block;font-size:13px;font-weight:600;color:#0f172a;margin-bottom:6px}:host ::ng-deep .html-preview-pane__body .wuic-preview-field__input{display:block;width:100%;border:1px solid #3b82f6;border-radius:4px;min-height:36px;box-sizing:border-box;background:#fff}:host ::ng-deep .monaco-editor .suggest-widget,:host ::ng-deep .monaco-editor .parameter-hints-widget,:host ::ng-deep .monaco-editor .monaco-hover{z-index:4000!important}:host ::ng-deep .monaco-editor .suggest-widget{height:220px!important;margin-top:-30px!important}:host ::ng-deep .monaco-editor .suggest-widget .tree,:host ::ng-deep .monaco-editor .suggest-widget .monaco-list,:host ::ng-deep .monaco-editor .suggest-widget .monaco-list-rows,:host ::ng-deep .monaco-editor .suggest-widget .monaco-scrollable-element{height:186px!important;max-height:186px!important}:host ::ng-deep .monaco-editor .suggest-widget .monaco-list-rows{box-sizing:border-box;padding-bottom:20px!important}::-webkit-scrollbar{width:12px}::-webkit-scrollbar-track{box-shadow:inset 0 0 5px gray;border-radius:10px}::-webkit-scrollbar-thumb{background:#ffffff80;border-radius:10px;visibility:hidden}::-webkit-scrollbar-thumb:hover{visibility:visible
|
|
3978
|
-
}], ctorParameters: () => [{ type: i1.MonacoEditorLoaderService }, { type: i1$
|
|
4139
|
+
args: [{ selector: 'wuic-code-editor', imports: [NgClass, NgStyle, FormsModule, MonacoEditorModule, ContextMenuModule, TreeModule, JsonSchemaTreeEditorComponent], template: "@if (render) {\n<div [ngClass]=\"{'fullscreen': fullscreen, 'no-full-screen': !fullscreen }\">\n <div\n [ngStyle]=\"{width: sqlObjView ? '22%' : null, display: sqlObjView ? null : 'none', position: fullscreen ? 'absolute' : null, 'left': fullscreen ? '0px' : null, 'top': fullscreen ? '0px' : null }\"\n style=\"float: left; height: 100%; overflow: auto; background-color: #1e1e1e; z-index: 2000;\">\n <p-tree [value]=\"primeTreeNodes\">\n <ng-template let-node pTemplate=\"default\">\n <span style=\"-webkit-user-select: none; -ms-user-select: none; user-select: none;\" draggable=\"true\"\n (dragstart)=\"onTreeNodeDragStart($event, node.data)\" (click)=\"editorOptions.onNodeClick($event, node.data)\"\n (dragend)=\"onTreeNodeDragEnd($event, node.data)\">\n <span>\n {{ node.label }}\n </span>\n </span>\n </ng-template>\n </p-tree>\n </div>\n <div [ngStyle]=\"{width: sqlObjView ? '78%' : '100%', 'left': fullscreen ? (!sqlObjView ? '0px' : '22%') : null }\"\n style=\"height: 100%; float: left;\">\n <ul class=\"editor-btn\">\n <li>\n <div (click)=\"setFullScreen($event, this)\">\n <i\n [ngClass]=\"{'pi': true, 'pi-window-maximize': fullscreen == false, 'pi-window-minimize': fullscreen == true}\"></i>\n </div>\n </li>\n @if (editorOptions.language == 'sql') {\n <li>\n <div (click)=\"toggleSqlObjView($event, this)\">\n <i [ngClass]=\"{'pi': true, 'pi-server': true}\"></i>\n </div>\n </li>\n }\n @if (editorOptions.language == 'sql') {\n <li>\n <div (click)=\"testSql($event, this)\">\n <i [ngClass]=\"{'pi': true, 'pi-verified': true}\"></i>\n </div>\n </li>\n }\n @if (field.mc_suggest_value_callback) {\n <li>\n <div (click)=\"suggest($event, this)\">\n <i [ngClass]=\"{'pi': true, 'pi-question': true}\"></i>\n </div>\n </li>\n }\n @if (isHtmlEditor) {\n <li>\n <div (click)=\"formatHtmlNow($event)\" title=\"Format HTML\">\n <i [ngClass]=\"{'pi': true, 'pi-align-left': true}\"></i>\n </div>\n </li>\n <li>\n <div (click)=\"toggleHtmlPreview($event)\" title=\"Toggle HTML preview\">\n <i [ngClass]=\"{'pi': true, 'pi-eye': showHtmlPreview, 'pi-eye-slash': !showHtmlPreview}\"></i>\n </div>\n </li>\n }\n @if (isJsonEditor) {\n <li>\n <div (click)=\"formatJsonNow($event)\" title=\"Format JSON\">\n <i [ngClass]=\"{'pi': true, 'pi-code': true}\"></i>\n </div>\n </li>\n }\n @if (isJsonEditor && jsonTreeToggle) {\n <li>\n <div (click)=\"toggleJsonTree($event)\" [title]=\"treeViewActive ? 'Codice' : 'Struttura'\">\n <i [ngClass]=\"{'pi': true, 'pi-sitemap': !treeViewActive, 'pi-pencil': treeViewActive}\"></i>\n </div>\n </li>\n }\n </ul>\n <div class=\"editor-shell\">\n <!-- Monaco resta VIVO anche in vista struttura (solo nascosto): il model\n e il record vengono riscritti a ogni modifica della struttura. -->\n <div class=\"editor-pane\" [ngClass]=\"{'editor-pane--with-preview': isHtmlEditor && showHtmlPreview}\"\n [style.display]=\"treeViewActive ? 'none' : null\">\n <ngx-monaco-editor #editorRef [options]=\"editorOptions\" [ngModel]=\"editorModelValue\"\n (ngModelChange)=\"onEditorModelChange($event)\"\n [ngModelOptions]=\"{standalone: true}\"\n [ngStyle]=\"{'left': fullscreen ? (!sqlObjView ? '0px' : '22%') : null, width: '100%' }\"\n style=\"width: 100%; height: 100%;\"\n (onDropIntoEditor)=\"editorOptions.onDropIntoEditor($event)\"></ngx-monaco-editor>\n </div>\n @if (treeViewActive) {\n <div class=\"editor-pane\">\n <wuic-json-schema-tree-editor\n [schema]=\"jsonTreeSchema\"\n [value]=\"treeValue\"\n (valueChange)=\"onTreeValueChange($event)\" />\n </div>\n }\n @if (isHtmlEditor && showHtmlPreview) {\n <div class=\"html-preview-pane\">\n <div class=\"html-preview-pane__header\">HTML Pseudo-Preview (sanitized)</div>\n <div class=\"html-preview-pane__body\" [innerHTML]=\"sanitizedHtmlPreview\"></div>\n </div>\n }\n </div>\n </div>\n</div>\n} @else {\n <!-- Monaco viene inizializzato in modo asincrono (loader + registrazione\n language provider) DOPO che il @defer del code-area-editor ha gia'\n terminato il fetch del chunk: senza questo placeholder la sezione\n collasserebbe a nulla in quel gap. Riserva l'altezza dell'editor\n (200px, come .no-full-screen) per evitare il salto di layout. -->\n <div class=\"code-editor-monaco-loading\">\n <i class=\"pi pi-spin pi-spinner\"></i> Inizializzazione editor\u2026\n </div>\n}\n\n@if (editorRef?.editorContent?.nativeElement) {\n<p-contextMenu [target]=\"editorRef?.editorContent?.nativeElement\" [model]=\"editorOptions.contextMenuItems\"\n appendTo=\"body\" />\n}\n", styles: [".editor-btn{color:#fff;list-style-type:none;z-index:2001;visibility:visible;position:absolute;right:10px;top:10px;display:flex;gap:8px;margin:0;padding:0}.editor-btn li{list-style:none}.editor-btn li div{width:24px;height:24px;display:flex;align-items:center;justify-content:center;border:1px solid #334155;border-radius:4px;background:#0f172ad9;cursor:pointer}.fullscreen{position:fixed;inset:16px;z-index:3000;background:#1e1e1e;width:auto;height:auto}.fullscreen .editor-shell{position:absolute;inset:44px 0 0;height:auto}.fullscreen .editor-pane ngx-monaco-editor{position:relative;height:auto!important}.no-full-screen{width:100%;height:200px;position:relative}.editor-container{overflow:visible!important}.editor-shell{display:flex;width:100%;height:100%;min-height:0}.editor-pane{flex:1 1 auto;min-width:0;height:100%}.editor-pane--with-preview{flex:0 0 62%}.code-editor-monaco-loading{display:flex;align-items:center;justify-content:center;gap:8px;height:200px;color:#64748b;font-size:.9rem;background:#f1f5f9;border:1px dashed #cbd5e1;border-radius:6px}.code-editor-monaco-loading .pi-spinner{font-size:1rem}.html-preview-pane{flex:0 0 38%;min-width:260px;border-left:1px solid #404040;background:#111827;color:#e5e7eb;display:flex;flex-direction:column}.html-preview-pane__header{font-size:12px;letter-spacing:.04em;text-transform:uppercase;padding:10px 12px;border-bottom:1px solid #374151;color:#9ca3af}.html-preview-pane__body{flex:1 1 auto;overflow:auto;padding:12px;background:#fff;color:#111827}:host ::ng-deep .html-preview-pane__body .wuic-preview-component{border:1px solid #cbd5e1;border-left:4px solid #2563eb;border-radius:6px;padding:8px 10px;margin-bottom:10px;background:#f8fafc}:host ::ng-deep .html-preview-pane__body .wuic-preview-component__title{font-weight:600;color:#0f172a;margin-bottom:4px}:host ::ng-deep .html-preview-pane__body .wuic-preview-component__content{margin-top:8px}:host ::ng-deep .html-preview-pane__body .wuic-preview-field__label{display:block;font-size:13px;font-weight:600;color:#0f172a;margin-bottom:6px}:host ::ng-deep .html-preview-pane__body .wuic-preview-field__input{display:block;width:100%;border:1px solid #3b82f6;border-radius:4px;min-height:36px;box-sizing:border-box;background:#fff}:host ::ng-deep .monaco-editor .suggest-widget,:host ::ng-deep .monaco-editor .parameter-hints-widget,:host ::ng-deep .monaco-editor .monaco-hover{z-index:4000!important}:host ::ng-deep .monaco-editor .suggest-widget{height:220px!important;margin-top:-30px!important}:host ::ng-deep .monaco-editor .suggest-widget .tree,:host ::ng-deep .monaco-editor .suggest-widget .monaco-list,:host ::ng-deep .monaco-editor .suggest-widget .monaco-list-rows,:host ::ng-deep .monaco-editor .suggest-widget .monaco-scrollable-element{height:186px!important;max-height:186px!important}:host ::ng-deep .monaco-editor .suggest-widget .monaco-list-rows{box-sizing:border-box;padding-bottom:20px!important}::-webkit-scrollbar{width:12px}::-webkit-scrollbar-track{box-shadow:inset 0 0 5px gray;border-radius:10px}::-webkit-scrollbar-thumb{background:#ffffff80;border-radius:10px;visibility:hidden}::-webkit-scrollbar-thumb:hover{visibility:visible;background:#fffc}\n"] }]
|
|
4140
|
+
}], ctorParameters: () => [{ type: i1$1.MonacoEditorLoaderService }, { type: i1$2.HttpClient }, { type: MetadataProviderService }, { type: UserInfoService }, { type: DataProviderService }, { type: WtoolboxService }, { type: i1$3.DomSanitizer }], propDecorators: { style: [{
|
|
3979
4141
|
type: HostBinding,
|
|
3980
4142
|
args: ['style']
|
|
3981
4143
|
}], contextMenuAnchor: [{
|
|
@@ -3993,7 +4155,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.9", ngImpor
|
|
|
3993
4155
|
}], ctxs: [{
|
|
3994
4156
|
type: ViewChild,
|
|
3995
4157
|
args: ['ctxs']
|
|
4158
|
+
}], jsonTreeToggle: [{
|
|
4159
|
+
type: Input
|
|
3996
4160
|
}] } });
|
|
3997
4161
|
|
|
3998
4162
|
export { CodeEditorComponent };
|
|
3999
|
-
//# sourceMappingURL=wuic-framework-lib-code-editor.component-
|
|
4163
|
+
//# sourceMappingURL=wuic-framework-lib-code-editor.component-N6HMl2mf.mjs.map
|