generate-ui-cli 2.1.7 → 2.2.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/README.md +47 -0
- package/dist/commands/angular.js +221 -12
- package/dist/commands/generate.js +120 -2
- package/dist/commands/login.js +44 -17
- package/dist/generate-ui/routes.gen.js +4 -0
- package/dist/generators/angular/feature.generator.js +1112 -137
- package/dist/generators/angular/menu.generator.js +165 -0
- package/dist/index.js +19 -4
- package/dist/license/permissions.js +1 -1
- package/dist/license/token.js +19 -3
- package/dist/postinstall.js +7 -0
- package/dist/runtime/logger.js +29 -0
- package/package.json +1 -1
|
@@ -14,7 +14,7 @@ function generateFeature(schema, root, schemasRoot) {
|
|
|
14
14
|
const featureDir = path_1.default.join(root, folder);
|
|
15
15
|
fs_1.default.mkdirSync(featureDir, { recursive: true });
|
|
16
16
|
const appRoot = path_1.default.resolve(root, '..');
|
|
17
|
-
ensureUiComponents(appRoot);
|
|
17
|
+
ensureUiComponents(appRoot, schemasRoot);
|
|
18
18
|
const method = String(schema.api.method || '').toLowerCase();
|
|
19
19
|
const endpoint = String(schema.api.endpoint || '');
|
|
20
20
|
const baseUrl = String(schema.api.baseUrl || 'https://api.realworld.io/api');
|
|
@@ -54,11 +54,14 @@ function generateFeature(schema, root, schemasRoot) {
|
|
|
54
54
|
const componentPath = path_1.default.join(featureDir, `${fileBase}.component.ts`);
|
|
55
55
|
fs_1.default.writeFileSync(componentPath, `
|
|
56
56
|
import { Component } from '@angular/core'
|
|
57
|
-
import {
|
|
57
|
+
import { CommonModule } from '@angular/common'
|
|
58
58
|
import { FormBuilder, ReactiveFormsModule } from '@angular/forms'
|
|
59
59
|
import { UiCardComponent } from '../../ui/ui-card/ui-card.component'
|
|
60
|
-
import { UiFieldComponent } from '../../ui/ui-field/ui-field.component'
|
|
61
60
|
import { UiButtonComponent } from '../../ui/ui-button/ui-button.component'
|
|
61
|
+
import { UiSelectComponent } from '../../ui/ui-select/ui-select.component'
|
|
62
|
+
import { UiCheckboxComponent } from '../../ui/ui-checkbox/ui-checkbox.component'
|
|
63
|
+
import { UiInputComponent } from '../../ui/ui-input/ui-input.component'
|
|
64
|
+
import { UiTextareaComponent } from '../../ui/ui-textarea/ui-textarea.component'
|
|
62
65
|
import { ${name}Service } from './${fileBase}.service.gen'
|
|
63
66
|
import { ${name}Gen } from './${fileBase}.gen'
|
|
64
67
|
import screenSchema from '${schemaImportPath}'
|
|
@@ -67,13 +70,14 @@ import screenSchema from '${schemaImportPath}'
|
|
|
67
70
|
selector: 'app-${toKebab(name)}',
|
|
68
71
|
standalone: true,
|
|
69
72
|
imports: [
|
|
70
|
-
|
|
71
|
-
NgFor,
|
|
72
|
-
JsonPipe,
|
|
73
|
+
CommonModule,
|
|
73
74
|
ReactiveFormsModule,
|
|
74
75
|
UiCardComponent,
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
UiButtonComponent,
|
|
77
|
+
UiSelectComponent,
|
|
78
|
+
UiCheckboxComponent,
|
|
79
|
+
UiInputComponent,
|
|
80
|
+
UiTextareaComponent
|
|
77
81
|
],
|
|
78
82
|
templateUrl: './${fileBase}.component.html',
|
|
79
83
|
styleUrls: ['./${fileBase}.component.scss']
|
|
@@ -100,7 +104,11 @@ export class ${name}Component extends ${name}Gen {
|
|
|
100
104
|
.execute(pathParams, queryParams, body)
|
|
101
105
|
.subscribe({
|
|
102
106
|
next: result => {
|
|
103
|
-
|
|
107
|
+
const normalized =
|
|
108
|
+
result && typeof result === 'object' && 'body' in result
|
|
109
|
+
? (result as any).body
|
|
110
|
+
: result
|
|
111
|
+
this.result = normalized
|
|
104
112
|
this.loading = false
|
|
105
113
|
},
|
|
106
114
|
error: error => {
|
|
@@ -115,20 +123,17 @@ export class ${name}Component extends ${name}Gen {
|
|
|
115
123
|
}
|
|
116
124
|
|
|
117
125
|
getRows() {
|
|
118
|
-
const value = this.result
|
|
126
|
+
const value = this.unwrapResult(this.result)
|
|
119
127
|
if (Array.isArray(value)) return value
|
|
120
128
|
if (!value || typeof value !== 'object') return []
|
|
121
129
|
|
|
122
|
-
const commonKeys = ['data', 'items', 'results', 'list', 'records']
|
|
130
|
+
const commonKeys = ['data', 'items', 'results', 'list', 'records', 'products']
|
|
123
131
|
for (const key of commonKeys) {
|
|
124
132
|
if (Array.isArray(value[key])) return value[key]
|
|
125
133
|
}
|
|
126
134
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return []
|
|
135
|
+
const found = this.findFirstArray(value, 0, 5)
|
|
136
|
+
return found ?? []
|
|
132
137
|
}
|
|
133
138
|
|
|
134
139
|
getColumns() {
|
|
@@ -152,7 +157,7 @@ export class ${name}Component extends ${name}Gen {
|
|
|
152
157
|
return value
|
|
153
158
|
.replace(/[_-]/g, ' ')
|
|
154
159
|
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
155
|
-
.replace(
|
|
160
|
+
.replace(/\\b\\w/g, char => char.toUpperCase())
|
|
156
161
|
}
|
|
157
162
|
|
|
158
163
|
getCellValue(row: any, column: string) {
|
|
@@ -177,7 +182,7 @@ export class ${name}Component extends ${name}Gen {
|
|
|
177
182
|
)
|
|
178
183
|
}
|
|
179
184
|
|
|
180
|
-
|
|
185
|
+
formatValue(value: any): string {
|
|
181
186
|
if (value === null || value === undefined) return ''
|
|
182
187
|
if (typeof value === 'string' || typeof value === 'number') {
|
|
183
188
|
return String(value)
|
|
@@ -201,13 +206,42 @@ export class ${name}Component extends ${name}Gen {
|
|
|
201
206
|
}
|
|
202
207
|
|
|
203
208
|
getObjectRows() {
|
|
204
|
-
const value = this.result
|
|
209
|
+
const value = this.unwrapResult(this.result)
|
|
205
210
|
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
206
211
|
return []
|
|
207
212
|
}
|
|
208
213
|
return this.flattenObject(value)
|
|
209
214
|
}
|
|
210
215
|
|
|
216
|
+
hasObjectRows() {
|
|
217
|
+
return this.getObjectRows().length > 0
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
isSingleValue() {
|
|
221
|
+
const value = this.unwrapResult(this.result)
|
|
222
|
+
return (
|
|
223
|
+
value !== null &&
|
|
224
|
+
value !== undefined &&
|
|
225
|
+
(typeof value === 'string' ||
|
|
226
|
+
typeof value === 'number' ||
|
|
227
|
+
typeof value === 'boolean')
|
|
228
|
+
)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private unwrapResult(value: any) {
|
|
232
|
+
if (!value || typeof value !== 'object') return value
|
|
233
|
+
if (Object.prototype.hasOwnProperty.call(value, 'data')) {
|
|
234
|
+
return value.data
|
|
235
|
+
}
|
|
236
|
+
if (Object.prototype.hasOwnProperty.call(value, 'result')) {
|
|
237
|
+
return value.result
|
|
238
|
+
}
|
|
239
|
+
if (Object.prototype.hasOwnProperty.call(value, 'body')) {
|
|
240
|
+
return value.body
|
|
241
|
+
}
|
|
242
|
+
return value
|
|
243
|
+
}
|
|
244
|
+
|
|
211
245
|
private flattenObject(
|
|
212
246
|
value: Record<string, any>,
|
|
213
247
|
prefix = ''
|
|
@@ -224,6 +258,22 @@ export class ${name}Component extends ${name}Gen {
|
|
|
224
258
|
return rows
|
|
225
259
|
}
|
|
226
260
|
|
|
261
|
+
private findFirstArray(
|
|
262
|
+
value: any,
|
|
263
|
+
depth: number,
|
|
264
|
+
maxDepth: number
|
|
265
|
+
): any[] | null {
|
|
266
|
+
if (!value || depth > maxDepth) return null
|
|
267
|
+
if (Array.isArray(value)) return value
|
|
268
|
+
if (typeof value !== 'object') return null
|
|
269
|
+
|
|
270
|
+
for (const key of Object.keys(value)) {
|
|
271
|
+
const found = this.findFirstArray(value[key], depth + 1, maxDepth)
|
|
272
|
+
if (found) return found
|
|
273
|
+
}
|
|
274
|
+
return null
|
|
275
|
+
}
|
|
276
|
+
|
|
227
277
|
}
|
|
228
278
|
`);
|
|
229
279
|
/**
|
|
@@ -280,10 +330,19 @@ export class ${name}Gen {
|
|
|
280
330
|
}
|
|
281
331
|
|
|
282
332
|
protected isSelect(field: any) {
|
|
333
|
+
if (field?.ui === 'select' || field?.ui === 'dropdown') return true
|
|
283
334
|
return Array.isArray(field.options) && field.options.length > 0
|
|
284
335
|
}
|
|
285
336
|
|
|
337
|
+
protected getSelectOptions(field: any) {
|
|
338
|
+
if (Array.isArray(field.options) && field.options.length > 0) {
|
|
339
|
+
return field.options
|
|
340
|
+
}
|
|
341
|
+
return []
|
|
342
|
+
}
|
|
343
|
+
|
|
286
344
|
protected isCheckbox(field: any) {
|
|
345
|
+
if (field?.ui === 'select' || field?.ui === 'dropdown') return false
|
|
287
346
|
return field.type === 'boolean'
|
|
288
347
|
}
|
|
289
348
|
|
|
@@ -457,11 +516,13 @@ export class ${name}Service {
|
|
|
457
516
|
:host {
|
|
458
517
|
display: block;
|
|
459
518
|
padding: 24px;
|
|
519
|
+
min-height: 100vh;
|
|
460
520
|
}
|
|
461
521
|
|
|
462
522
|
.page {
|
|
463
523
|
display: grid;
|
|
464
524
|
gap: 16px;
|
|
525
|
+
min-height: calc(100vh - 48px);
|
|
465
526
|
}
|
|
466
527
|
|
|
467
528
|
.screen-description {
|
|
@@ -480,6 +541,17 @@ export class ${name}Service {
|
|
|
480
541
|
margin: 0 auto;
|
|
481
542
|
}
|
|
482
543
|
|
|
544
|
+
.form-field {
|
|
545
|
+
display: grid;
|
|
546
|
+
gap: 8px;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
.field-error {
|
|
550
|
+
color: #ef4444;
|
|
551
|
+
font-size: 12px;
|
|
552
|
+
margin-top: -4px;
|
|
553
|
+
}
|
|
554
|
+
|
|
483
555
|
.actions {
|
|
484
556
|
display: flex;
|
|
485
557
|
justify-content: flex-end;
|
|
@@ -501,10 +573,12 @@ export class ${name}Service {
|
|
|
501
573
|
|
|
502
574
|
.result-table {
|
|
503
575
|
margin-top: 20px;
|
|
504
|
-
|
|
576
|
+
max-width: 100%;
|
|
577
|
+
overflow: auto;
|
|
505
578
|
border-radius: 16px;
|
|
506
579
|
border: 1px solid #e2e8f0;
|
|
507
580
|
box-shadow: 0 20px 40px rgba(15, 23, 42, 0.08);
|
|
581
|
+
-webkit-overflow-scrolling: touch;
|
|
508
582
|
}
|
|
509
583
|
|
|
510
584
|
.result-card {
|
|
@@ -548,8 +622,74 @@ export class ${name}Service {
|
|
|
548
622
|
text-align: right;
|
|
549
623
|
}
|
|
550
624
|
|
|
625
|
+
.result-error {
|
|
626
|
+
margin-top: 24px;
|
|
627
|
+
padding: 16px 18px;
|
|
628
|
+
border-radius: 16px;
|
|
629
|
+
border: 1px solid rgba(239, 68, 68, 0.3);
|
|
630
|
+
background: #fff1f2;
|
|
631
|
+
color: #881337;
|
|
632
|
+
box-shadow: 0 10px 24px rgba(239, 68, 68, 0.15);
|
|
633
|
+
display: grid;
|
|
634
|
+
gap: 8px;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.result-error__body {
|
|
638
|
+
font-size: 13px;
|
|
639
|
+
color: #7f1d1d;
|
|
640
|
+
word-break: break-word;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
.result-raw {
|
|
644
|
+
margin-top: 24px;
|
|
645
|
+
padding: 16px 18px;
|
|
646
|
+
border-radius: 16px;
|
|
647
|
+
border: 1px dashed rgba(15, 23, 42, 0.18);
|
|
648
|
+
background: #f8fafc;
|
|
649
|
+
color: #0f172a;
|
|
650
|
+
display: grid;
|
|
651
|
+
gap: 10px;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
.result-raw summary {
|
|
655
|
+
cursor: pointer;
|
|
656
|
+
font-weight: 600;
|
|
657
|
+
color: #334155;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
.result-raw pre {
|
|
661
|
+
margin: 0;
|
|
662
|
+
padding: 12px;
|
|
663
|
+
border-radius: 12px;
|
|
664
|
+
background: #ffffff;
|
|
665
|
+
border: 1px solid rgba(15, 23, 42, 0.08);
|
|
666
|
+
font-size: 12px;
|
|
667
|
+
line-height: 1.4;
|
|
668
|
+
white-space: pre-wrap;
|
|
669
|
+
word-break: break-word;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
.result-single {
|
|
673
|
+
margin-top: 24px;
|
|
674
|
+
padding: 16px 18px;
|
|
675
|
+
border-radius: 16px;
|
|
676
|
+
border: 1px solid rgba(15, 23, 42, 0.08);
|
|
677
|
+
background: #ffffff;
|
|
678
|
+
color: #0f172a;
|
|
679
|
+
display: grid;
|
|
680
|
+
gap: 8px;
|
|
681
|
+
box-shadow: 0 8px 18px rgba(15, 23, 42, 0.08);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
.result-single__value {
|
|
685
|
+
font-size: 18px;
|
|
686
|
+
font-weight: 700;
|
|
687
|
+
color: #0f172a;
|
|
688
|
+
}
|
|
689
|
+
|
|
551
690
|
.data-table {
|
|
552
|
-
width:
|
|
691
|
+
width: max-content;
|
|
692
|
+
min-width: 100%;
|
|
553
693
|
border-collapse: collapse;
|
|
554
694
|
background: #ffffff;
|
|
555
695
|
font-size: 14px;
|
|
@@ -588,6 +728,17 @@ export class ${name}Service {
|
|
|
588
728
|
box-shadow: 0 6px 12px rgba(15, 23, 42, 0.16);
|
|
589
729
|
}
|
|
590
730
|
|
|
731
|
+
:host ::ng-deep ui-card .ui-card {
|
|
732
|
+
display: flex;
|
|
733
|
+
flex-direction: column;
|
|
734
|
+
max-height: calc(100vh - 160px);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
:host ::ng-deep ui-card .ui-card__body {
|
|
738
|
+
overflow: auto;
|
|
739
|
+
min-height: 0;
|
|
740
|
+
}
|
|
741
|
+
|
|
591
742
|
@media (max-width: 720px) {
|
|
592
743
|
:host {
|
|
593
744
|
padding: 18px;
|
|
@@ -737,6 +888,65 @@ function buildComponentHtml(options) {
|
|
|
737
888
|
</ui-button>
|
|
738
889
|
</div>
|
|
739
890
|
</ui-card>
|
|
891
|
+
|
|
892
|
+
<div class="result-table" *ngIf="isArrayResult()">
|
|
893
|
+
<table class="data-table">
|
|
894
|
+
<thead>
|
|
895
|
+
<tr>
|
|
896
|
+
<th *ngFor="let column of getColumns()">
|
|
897
|
+
{{ formatHeader(column) }}
|
|
898
|
+
</th>
|
|
899
|
+
</tr>
|
|
900
|
+
</thead>
|
|
901
|
+
<tbody>
|
|
902
|
+
<tr *ngFor="let row of getRows()">
|
|
903
|
+
<td *ngFor="let column of getColumns()">
|
|
904
|
+
<img
|
|
905
|
+
*ngIf="isImageCell(row, column)"
|
|
906
|
+
[src]="getCellValue(row, column)"
|
|
907
|
+
[alt]="formatHeader(column)"
|
|
908
|
+
class="cell-image"
|
|
909
|
+
/>
|
|
910
|
+
<span *ngIf="!isImageCell(row, column)">
|
|
911
|
+
{{ getCellValue(row, column) }}
|
|
912
|
+
</span>
|
|
913
|
+
</td>
|
|
914
|
+
</tr>
|
|
915
|
+
</tbody>
|
|
916
|
+
</table>
|
|
917
|
+
</div>
|
|
918
|
+
|
|
919
|
+
<div class="result-error" *ngIf="error">
|
|
920
|
+
<strong>Request failed.</strong>
|
|
921
|
+
<div class="result-error__body">
|
|
922
|
+
{{ error?.message || (error | json) }}
|
|
923
|
+
</div>
|
|
924
|
+
</div>
|
|
925
|
+
|
|
926
|
+
<div class="result-card" *ngIf="!isArrayResult() && hasObjectRows()">
|
|
927
|
+
<div class="result-card__grid">
|
|
928
|
+
<div class="result-card__row" *ngFor="let row of getObjectRows()">
|
|
929
|
+
<span class="result-card__label">
|
|
930
|
+
{{ formatHeader(row.key) }}
|
|
931
|
+
</span>
|
|
932
|
+
<span class="result-card__value">
|
|
933
|
+
{{ row.value }}
|
|
934
|
+
</span>
|
|
935
|
+
</div>
|
|
936
|
+
</div>
|
|
937
|
+
</div>
|
|
938
|
+
|
|
939
|
+
<div class="result-single" *ngIf="!isArrayResult() && isSingleValue()">
|
|
940
|
+
<strong>Result</strong>
|
|
941
|
+
<div class="result-single__value">
|
|
942
|
+
{{ formatValue(result) }}
|
|
943
|
+
</div>
|
|
944
|
+
</div>
|
|
945
|
+
|
|
946
|
+
<details class="result-raw" *ngIf="result">
|
|
947
|
+
<summary>Raw response</summary>
|
|
948
|
+
<pre>{{ result | json }}</pre>
|
|
949
|
+
</details>
|
|
740
950
|
</div>
|
|
741
951
|
`;
|
|
742
952
|
}
|
|
@@ -748,51 +958,52 @@ function buildComponentHtml(options) {
|
|
|
748
958
|
</p>
|
|
749
959
|
<form [formGroup]="form" (ngSubmit)="submit()">
|
|
750
960
|
<div class="form-grid">
|
|
751
|
-
<
|
|
752
|
-
|
|
753
|
-
[label]="field.label || field.name"
|
|
754
|
-
[hint]="field.hint"
|
|
755
|
-
[info]="field.info"
|
|
756
|
-
>
|
|
757
|
-
<select
|
|
961
|
+
<div class="form-field" *ngFor="let field of formFields">
|
|
962
|
+
<ui-select
|
|
758
963
|
*ngIf="isSelect(field)"
|
|
759
|
-
[
|
|
760
|
-
[
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
</select>
|
|
769
|
-
|
|
770
|
-
<textarea
|
|
964
|
+
[label]="field.label || field.name"
|
|
965
|
+
[hint]="field.hint"
|
|
966
|
+
[info]="field.info"
|
|
967
|
+
[controlName]="field.name"
|
|
968
|
+
[options]="getSelectOptions(field)"
|
|
969
|
+
[invalid]="isInvalid(field)"
|
|
970
|
+
></ui-select>
|
|
971
|
+
|
|
972
|
+
<ui-textarea
|
|
771
973
|
*ngIf="isTextarea(field)"
|
|
772
|
-
|
|
773
|
-
[
|
|
974
|
+
[label]="field.label || field.name"
|
|
975
|
+
[hint]="field.hint"
|
|
976
|
+
[info]="field.info"
|
|
977
|
+
[controlName]="field.name"
|
|
978
|
+
[rows]="3"
|
|
774
979
|
[placeholder]="field.placeholder || field.label || field.name"
|
|
775
|
-
[
|
|
776
|
-
></textarea>
|
|
980
|
+
[invalid]="isInvalid(field)"
|
|
981
|
+
></ui-textarea>
|
|
777
982
|
|
|
778
|
-
<
|
|
983
|
+
<ui-checkbox
|
|
779
984
|
*ngIf="isCheckbox(field)"
|
|
780
|
-
|
|
781
|
-
[
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
985
|
+
[label]="field.label || field.name"
|
|
986
|
+
[hint]="field.hint"
|
|
987
|
+
[info]="field.info"
|
|
988
|
+
[controlName]="field.name"
|
|
989
|
+
[invalid]="isInvalid(field)"
|
|
990
|
+
></ui-checkbox>
|
|
991
|
+
|
|
992
|
+
<ui-input
|
|
785
993
|
*ngIf="!isSelect(field) && !isTextarea(field) && !isCheckbox(field)"
|
|
994
|
+
[label]="field.label || field.name"
|
|
995
|
+
[hint]="field.hint"
|
|
996
|
+
[info]="field.info"
|
|
786
997
|
[type]="inputType(field)"
|
|
787
|
-
[
|
|
998
|
+
[controlName]="field.name"
|
|
788
999
|
[placeholder]="field.placeholder || field.label || field.name"
|
|
789
|
-
[
|
|
790
|
-
|
|
1000
|
+
[invalid]="isInvalid(field)"
|
|
1001
|
+
></ui-input>
|
|
791
1002
|
|
|
792
1003
|
<span class="field-error" *ngIf="isInvalid(field)">
|
|
793
1004
|
Campo obrigatório
|
|
794
1005
|
</span>
|
|
795
|
-
</
|
|
1006
|
+
</div>
|
|
796
1007
|
</div>
|
|
797
1008
|
<div class="actions">
|
|
798
1009
|
<ui-button
|
|
@@ -833,7 +1044,14 @@ function buildComponentHtml(options) {
|
|
|
833
1044
|
</table>
|
|
834
1045
|
</div>
|
|
835
1046
|
|
|
836
|
-
<div class="result-
|
|
1047
|
+
<div class="result-error" *ngIf="error">
|
|
1048
|
+
<strong>Request failed.</strong>
|
|
1049
|
+
<div class="result-error__body">
|
|
1050
|
+
{{ error?.message || (error | json) }}
|
|
1051
|
+
</div>
|
|
1052
|
+
</div>
|
|
1053
|
+
|
|
1054
|
+
<div class="result-card" *ngIf="!isArrayResult() && hasObjectRows()">
|
|
837
1055
|
<div class="result-card__grid">
|
|
838
1056
|
<div class="result-card__row" *ngFor="let row of getObjectRows()">
|
|
839
1057
|
<span class="result-card__label">
|
|
@@ -845,6 +1063,18 @@ function buildComponentHtml(options) {
|
|
|
845
1063
|
</div>
|
|
846
1064
|
</div>
|
|
847
1065
|
</div>
|
|
1066
|
+
|
|
1067
|
+
<div class="result-single" *ngIf="!isArrayResult() && isSingleValue()">
|
|
1068
|
+
<strong>Result</strong>
|
|
1069
|
+
<div class="result-single__value">
|
|
1070
|
+
{{ formatValue(result) }}
|
|
1071
|
+
</div>
|
|
1072
|
+
</div>
|
|
1073
|
+
|
|
1074
|
+
<details class="result-raw" *ngIf="result">
|
|
1075
|
+
<summary>Raw response</summary>
|
|
1076
|
+
<pre>{{ result | json }}</pre>
|
|
1077
|
+
</details>
|
|
848
1078
|
</div>
|
|
849
1079
|
`;
|
|
850
1080
|
}
|
|
@@ -878,7 +1108,7 @@ function httpCallForMethod(method) {
|
|
|
878
1108
|
return 'return this.http.get(url)';
|
|
879
1109
|
}
|
|
880
1110
|
}
|
|
881
|
-
function ensureUiComponents(appRoot) {
|
|
1111
|
+
function ensureUiComponents(appRoot, schemasRoot) {
|
|
882
1112
|
const uiRoot = path_1.default.join(appRoot, 'ui');
|
|
883
1113
|
const components = [
|
|
884
1114
|
{
|
|
@@ -916,11 +1146,22 @@ export class UiCardComponent {
|
|
|
916
1146
|
}
|
|
917
1147
|
|
|
918
1148
|
.ui-card {
|
|
919
|
-
border-radius:
|
|
920
|
-
background: #ffffff;
|
|
921
|
-
border: 1px solid
|
|
922
|
-
box-shadow:
|
|
923
|
-
padding:
|
|
1149
|
+
border-radius: 22px;
|
|
1150
|
+
background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);
|
|
1151
|
+
border: 1px solid rgba(15, 23, 42, 0.08);
|
|
1152
|
+
box-shadow: var(--shadow-card);
|
|
1153
|
+
padding: 30px;
|
|
1154
|
+
position: relative;
|
|
1155
|
+
overflow: hidden;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
.ui-card::before {
|
|
1159
|
+
content: "";
|
|
1160
|
+
position: absolute;
|
|
1161
|
+
inset: 0 0 auto 0;
|
|
1162
|
+
height: 6px;
|
|
1163
|
+
background: linear-gradient(90deg, var(--color-primary), var(--color-primary-strong), var(--color-accent));
|
|
1164
|
+
opacity: 0.65;
|
|
924
1165
|
}
|
|
925
1166
|
|
|
926
1167
|
.ui-card__header {
|
|
@@ -929,17 +1170,154 @@ export class UiCardComponent {
|
|
|
929
1170
|
|
|
930
1171
|
.ui-card__title {
|
|
931
1172
|
margin: 0;
|
|
932
|
-
font-size:
|
|
1173
|
+
font-size: 26px;
|
|
933
1174
|
font-weight: 700;
|
|
934
|
-
color:
|
|
1175
|
+
color: var(--bg-ink);
|
|
1176
|
+
letter-spacing: -0.02em;
|
|
935
1177
|
}
|
|
936
1178
|
|
|
937
1179
|
.ui-card__subtitle {
|
|
938
1180
|
margin: 8px 0 0;
|
|
939
|
-
font-size:
|
|
940
|
-
color:
|
|
941
|
-
letter-spacing: 0.
|
|
1181
|
+
font-size: 13px;
|
|
1182
|
+
color: var(--color-muted);
|
|
1183
|
+
letter-spacing: 0.16em;
|
|
1184
|
+
text-transform: uppercase;
|
|
1185
|
+
font-family: "Space Mono", "Courier New", monospace;
|
|
1186
|
+
}
|
|
1187
|
+
`
|
|
1188
|
+
},
|
|
1189
|
+
{
|
|
1190
|
+
name: 'ui-menu',
|
|
1191
|
+
template: '',
|
|
1192
|
+
html: `
|
|
1193
|
+
<nav class="ui-menu">
|
|
1194
|
+
<div class="ui-menu__brand">
|
|
1195
|
+
<span class="ui-menu__brand-pill"></span>
|
|
1196
|
+
<span class="ui-menu__brand-title">{{ title }}</span>
|
|
1197
|
+
</div>
|
|
1198
|
+
|
|
1199
|
+
<ng-container *ngFor="let group of menu.groups">
|
|
1200
|
+
<section
|
|
1201
|
+
class="ui-menu__group"
|
|
1202
|
+
*ngIf="!group.hidden && group.items.length"
|
|
1203
|
+
>
|
|
1204
|
+
<h3 class="ui-menu__group-title">{{ group.label }}</h3>
|
|
1205
|
+
<a
|
|
1206
|
+
class="ui-menu__item"
|
|
1207
|
+
*ngFor="let item of group.items"
|
|
1208
|
+
[routerLink]="item.route"
|
|
1209
|
+
routerLinkActive="active"
|
|
1210
|
+
[class.hidden]="item.hidden"
|
|
1211
|
+
>
|
|
1212
|
+
{{ item.label }}
|
|
1213
|
+
</a>
|
|
1214
|
+
</section>
|
|
1215
|
+
</ng-container>
|
|
1216
|
+
|
|
1217
|
+
<section
|
|
1218
|
+
class="ui-menu__group"
|
|
1219
|
+
*ngIf="menu.ungrouped.length"
|
|
1220
|
+
>
|
|
1221
|
+
<h3 class="ui-menu__group-title">Outros</h3>
|
|
1222
|
+
<a
|
|
1223
|
+
class="ui-menu__item"
|
|
1224
|
+
*ngFor="let item of menu.ungrouped"
|
|
1225
|
+
[routerLink]="item.route"
|
|
1226
|
+
routerLinkActive="active"
|
|
1227
|
+
[class.hidden]="item.hidden"
|
|
1228
|
+
>
|
|
1229
|
+
{{ item.label }}
|
|
1230
|
+
</a>
|
|
1231
|
+
</section>
|
|
1232
|
+
</nav>
|
|
1233
|
+
`,
|
|
1234
|
+
scss: `
|
|
1235
|
+
:host {
|
|
1236
|
+
display: block;
|
|
1237
|
+
height: 100%;
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
.ui-menu {
|
|
1241
|
+
position: sticky;
|
|
1242
|
+
top: 24px;
|
|
1243
|
+
align-self: flex-start;
|
|
1244
|
+
background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);
|
|
1245
|
+
border: 1px solid rgba(15, 23, 42, 0.08);
|
|
1246
|
+
box-shadow: var(--shadow-card);
|
|
1247
|
+
border-radius: 22px;
|
|
1248
|
+
padding: 22px;
|
|
1249
|
+
min-width: 220px;
|
|
1250
|
+
display: grid;
|
|
1251
|
+
gap: 22px;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
.ui-menu__brand {
|
|
1255
|
+
display: flex;
|
|
1256
|
+
align-items: center;
|
|
1257
|
+
gap: 10px;
|
|
1258
|
+
font-weight: 700;
|
|
1259
|
+
color: var(--bg-ink);
|
|
1260
|
+
font-size: 15px;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
.ui-menu__brand-pill {
|
|
1264
|
+
width: 14px;
|
|
1265
|
+
height: 14px;
|
|
1266
|
+
border-radius: 999px;
|
|
1267
|
+
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-strong));
|
|
1268
|
+
box-shadow: 0 6px 16px rgba(8, 145, 178, 0.35);
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
.ui-menu__group {
|
|
1272
|
+
display: grid;
|
|
1273
|
+
gap: 10px;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
.ui-menu__group-title {
|
|
1277
|
+
margin: 0;
|
|
1278
|
+
font-size: 11px;
|
|
1279
|
+
letter-spacing: 0.3em;
|
|
942
1280
|
text-transform: uppercase;
|
|
1281
|
+
color: var(--color-muted);
|
|
1282
|
+
font-family: "Space Mono", "Courier New", monospace;
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
.ui-menu__item {
|
|
1286
|
+
display: block;
|
|
1287
|
+
text-decoration: none;
|
|
1288
|
+
font-size: 14px;
|
|
1289
|
+
font-weight: 600;
|
|
1290
|
+
color: #1f2937;
|
|
1291
|
+
padding: 10px 14px;
|
|
1292
|
+
border-radius: 14px;
|
|
1293
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
|
|
1294
|
+
border: 1px solid transparent;
|
|
1295
|
+
white-space: nowrap;
|
|
1296
|
+
overflow: hidden;
|
|
1297
|
+
text-overflow: ellipsis;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
.ui-menu__item:hover {
|
|
1301
|
+
background: #f1f5f9;
|
|
1302
|
+
transform: translateX(2px);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
.ui-menu__item.active {
|
|
1306
|
+
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-strong));
|
|
1307
|
+
color: #ffffff;
|
|
1308
|
+
box-shadow: 0 12px 24px rgba(8, 145, 178, 0.3);
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
.ui-menu__item.hidden {
|
|
1312
|
+
display: none;
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
@media (max-width: 900px) {
|
|
1316
|
+
.ui-menu {
|
|
1317
|
+
position: static;
|
|
1318
|
+
width: 100%;
|
|
1319
|
+
min-width: auto;
|
|
1320
|
+
}
|
|
943
1321
|
}
|
|
944
1322
|
`
|
|
945
1323
|
},
|
|
@@ -997,15 +1375,16 @@ export class UiFieldComponent {
|
|
|
997
1375
|
|
|
998
1376
|
.ui-field {
|
|
999
1377
|
display: grid;
|
|
1000
|
-
gap:
|
|
1001
|
-
font-size:
|
|
1002
|
-
color: #
|
|
1378
|
+
gap: 10px;
|
|
1379
|
+
font-size: 13px;
|
|
1380
|
+
color: #1f2937;
|
|
1003
1381
|
}
|
|
1004
1382
|
|
|
1005
1383
|
.ui-field__label {
|
|
1006
1384
|
font-weight: 700;
|
|
1007
1385
|
line-height: 1.4;
|
|
1008
1386
|
word-break: break-word;
|
|
1387
|
+
letter-spacing: 0.01em;
|
|
1009
1388
|
}
|
|
1010
1389
|
|
|
1011
1390
|
.ui-field__hint {
|
|
@@ -1018,7 +1397,7 @@ export class UiFieldComponent {
|
|
|
1018
1397
|
width: 18px;
|
|
1019
1398
|
height: 18px;
|
|
1020
1399
|
border-radius: 999px;
|
|
1021
|
-
border: 1px solid
|
|
1400
|
+
border: 1px solid rgba(15, 23, 42, 0.2);
|
|
1022
1401
|
background: #ffffff;
|
|
1023
1402
|
color: #475569;
|
|
1024
1403
|
font-size: 11px;
|
|
@@ -1045,31 +1424,29 @@ export class UiFieldComponent {
|
|
|
1045
1424
|
}
|
|
1046
1425
|
|
|
1047
1426
|
:host ::ng-deep input,
|
|
1048
|
-
:host ::ng-deep textarea
|
|
1049
|
-
:host ::ng-deep select {
|
|
1427
|
+
:host ::ng-deep textarea {
|
|
1050
1428
|
width: 100%;
|
|
1051
|
-
min-height:
|
|
1052
|
-
border-radius:
|
|
1053
|
-
border: 1px solid
|
|
1429
|
+
min-height: 3.4rem;
|
|
1430
|
+
border-radius: 10px;
|
|
1431
|
+
border: 1px solid rgba(15, 23, 42, 0.12);
|
|
1054
1432
|
background: #ffffff;
|
|
1055
|
-
padding: 0.
|
|
1056
|
-
font-size:
|
|
1433
|
+
padding: 0.9rem 1.1rem;
|
|
1434
|
+
font-size: 15px;
|
|
1057
1435
|
font-weight: 500;
|
|
1058
1436
|
box-shadow: none;
|
|
1059
1437
|
outline: none;
|
|
1060
|
-
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
|
1438
|
+
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
|
|
1061
1439
|
}
|
|
1062
1440
|
|
|
1063
1441
|
:host ::ng-deep input:focus,
|
|
1064
|
-
:host ::ng-deep textarea:focus
|
|
1065
|
-
:
|
|
1066
|
-
|
|
1067
|
-
|
|
1442
|
+
:host ::ng-deep textarea:focus {
|
|
1443
|
+
border-color: var(--color-primary);
|
|
1444
|
+
box-shadow: 0 0 0 3px rgba(15, 118, 110, 0.2);
|
|
1445
|
+
transform: translateY(-1px);
|
|
1068
1446
|
}
|
|
1069
1447
|
|
|
1070
1448
|
:host ::ng-deep input.invalid,
|
|
1071
|
-
:host ::ng-deep textarea.invalid
|
|
1072
|
-
:host ::ng-deep select.invalid {
|
|
1449
|
+
:host ::ng-deep textarea.invalid {
|
|
1073
1450
|
border-color: #ef4444;
|
|
1074
1451
|
box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.18);
|
|
1075
1452
|
}
|
|
@@ -1079,27 +1456,13 @@ export class UiFieldComponent {
|
|
|
1079
1456
|
color: #94a3b8;
|
|
1080
1457
|
}
|
|
1081
1458
|
|
|
1082
|
-
:host ::ng-deep select {
|
|
1083
|
-
padding-right: 2.2rem;
|
|
1084
|
-
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='8' viewBox='0 0 14 8' fill='none'><path d='M1 1.5L7 6.5L13 1.5' stroke='%236b7280' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
|
|
1085
|
-
background-repeat: no-repeat;
|
|
1086
|
-
background-position: right 0.7rem center;
|
|
1087
|
-
background-size: 14px 8px;
|
|
1088
|
-
appearance: none;
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
:host ::ng-deep textarea {
|
|
1092
|
-
min-height: 5.5rem;
|
|
1093
|
-
resize: vertical;
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1096
1459
|
:host ::ng-deep input[type='checkbox'] {
|
|
1097
1460
|
width: 20px;
|
|
1098
1461
|
height: 20px;
|
|
1099
1462
|
padding: 0;
|
|
1100
1463
|
border-radius: 6px;
|
|
1101
1464
|
box-shadow: none;
|
|
1102
|
-
accent-color:
|
|
1465
|
+
accent-color: var(--color-primary);
|
|
1103
1466
|
}
|
|
1104
1467
|
|
|
1105
1468
|
.field-error {
|
|
@@ -1141,18 +1504,19 @@ export class UiButtonComponent {
|
|
|
1141
1504
|
scss: `
|
|
1142
1505
|
.ui-button {
|
|
1143
1506
|
border: none;
|
|
1144
|
-
border-radius:
|
|
1145
|
-
padding: 12px
|
|
1507
|
+
border-radius: 999px;
|
|
1508
|
+
padding: 12px 24px;
|
|
1146
1509
|
font-weight: 700;
|
|
1147
1510
|
font-size: 14px;
|
|
1511
|
+
letter-spacing: 0.02em;
|
|
1148
1512
|
cursor: pointer;
|
|
1149
|
-
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
1513
|
+
transition: transform 0.2s ease, box-shadow 0.2s ease, filter 0.2s ease;
|
|
1150
1514
|
}
|
|
1151
1515
|
|
|
1152
1516
|
.ui-button.primary {
|
|
1153
|
-
background: linear-gradient(135deg,
|
|
1517
|
+
background: linear-gradient(135deg, var(--color-primary), var(--color-primary-strong));
|
|
1154
1518
|
color: #ffffff;
|
|
1155
|
-
box-shadow: 0
|
|
1519
|
+
box-shadow: 0 12px 24px rgba(8, 145, 178, 0.3);
|
|
1156
1520
|
}
|
|
1157
1521
|
|
|
1158
1522
|
.ui-button.ghost {
|
|
@@ -1163,11 +1527,12 @@ export class UiButtonComponent {
|
|
|
1163
1527
|
.ui-button.danger {
|
|
1164
1528
|
background: linear-gradient(135deg, #ef4444, #f97316);
|
|
1165
1529
|
color: #fff;
|
|
1166
|
-
box-shadow: 0
|
|
1530
|
+
box-shadow: 0 10px 22px rgba(239, 68, 68, 0.25);
|
|
1167
1531
|
}
|
|
1168
1532
|
|
|
1169
1533
|
.ui-button:hover:not(:disabled) {
|
|
1170
1534
|
transform: translateY(-1px);
|
|
1535
|
+
filter: brightness(1.02);
|
|
1171
1536
|
}
|
|
1172
1537
|
|
|
1173
1538
|
.ui-button:disabled {
|
|
@@ -1176,39 +1541,641 @@ export class UiButtonComponent {
|
|
|
1176
1541
|
box-shadow: none;
|
|
1177
1542
|
}
|
|
1178
1543
|
`
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1544
|
+
},
|
|
1545
|
+
{
|
|
1546
|
+
name: 'ui-input',
|
|
1547
|
+
template: `
|
|
1548
|
+
import { Component, Input } from '@angular/core'
|
|
1549
|
+
import { NgIf } from '@angular/common'
|
|
1550
|
+
import {
|
|
1551
|
+
ControlContainer,
|
|
1552
|
+
FormGroupDirective,
|
|
1553
|
+
ReactiveFormsModule
|
|
1554
|
+
} from '@angular/forms'
|
|
1555
|
+
|
|
1556
|
+
@Component({
|
|
1557
|
+
selector: 'ui-input',
|
|
1558
|
+
standalone: true,
|
|
1559
|
+
imports: [NgIf, ReactiveFormsModule],
|
|
1560
|
+
viewProviders: [
|
|
1561
|
+
{ provide: ControlContainer, useExisting: FormGroupDirective }
|
|
1562
|
+
],
|
|
1563
|
+
templateUrl: './ui-input.component.html',
|
|
1564
|
+
styleUrls: ['./ui-input.component.scss']
|
|
1565
|
+
})
|
|
1566
|
+
export class UiInputComponent {
|
|
1567
|
+
@Input() label = ''
|
|
1568
|
+
@Input() hint = ''
|
|
1569
|
+
@Input() info = ''
|
|
1570
|
+
@Input() controlName = ''
|
|
1571
|
+
@Input() placeholder = ''
|
|
1572
|
+
@Input() type: 'text' | 'number' | 'email' | 'password' | 'search' | 'tel' | 'url' = 'text'
|
|
1573
|
+
@Input() invalid = false
|
|
1574
|
+
infoOpen = false
|
|
1575
|
+
|
|
1576
|
+
toggleInfo(event: MouseEvent) {
|
|
1577
|
+
event.preventDefault()
|
|
1578
|
+
event.stopPropagation()
|
|
1579
|
+
this.infoOpen = !this.infoOpen
|
|
1580
|
+
}
|
|
1210
1581
|
}
|
|
1211
|
-
|
|
1582
|
+
`,
|
|
1583
|
+
html: `
|
|
1584
|
+
<label class="ui-control">
|
|
1585
|
+
<span class="ui-control__label" *ngIf="label">
|
|
1586
|
+
{{ label }}
|
|
1587
|
+
<button
|
|
1588
|
+
class="ui-control__info"
|
|
1589
|
+
type="button"
|
|
1590
|
+
*ngIf="info"
|
|
1591
|
+
(click)="toggleInfo($event)"
|
|
1592
|
+
[attr.aria-expanded]="infoOpen"
|
|
1593
|
+
>
|
|
1594
|
+
i
|
|
1595
|
+
</button>
|
|
1596
|
+
</span>
|
|
1597
|
+
<div class="ui-control__info-panel" *ngIf="info && infoOpen">
|
|
1598
|
+
{{ info }}
|
|
1599
|
+
</div>
|
|
1600
|
+
<input
|
|
1601
|
+
class="ui-control__input"
|
|
1602
|
+
[type]="type"
|
|
1603
|
+
[formControlName]="controlName"
|
|
1604
|
+
[placeholder]="placeholder"
|
|
1605
|
+
[class.invalid]="invalid"
|
|
1606
|
+
/>
|
|
1607
|
+
<span class="ui-control__hint" *ngIf="hint && !info">{{ hint }}</span>
|
|
1608
|
+
</label>
|
|
1609
|
+
`,
|
|
1610
|
+
scss: `
|
|
1611
|
+
:host {
|
|
1612
|
+
display: block;
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1615
|
+
.ui-control {
|
|
1616
|
+
display: grid;
|
|
1617
|
+
gap: 10px;
|
|
1618
|
+
font-size: 13px;
|
|
1619
|
+
color: #1f2937;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
.ui-control__label {
|
|
1623
|
+
font-weight: 700;
|
|
1624
|
+
line-height: 1.4;
|
|
1625
|
+
word-break: break-word;
|
|
1626
|
+
letter-spacing: 0.01em;
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
.ui-control__hint {
|
|
1630
|
+
color: #94a3b8;
|
|
1631
|
+
font-size: 12px;
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
.ui-control__info {
|
|
1635
|
+
margin-left: 8px;
|
|
1636
|
+
width: 18px;
|
|
1637
|
+
height: 18px;
|
|
1638
|
+
border-radius: 999px;
|
|
1639
|
+
border: 1px solid rgba(15, 23, 42, 0.2);
|
|
1640
|
+
background: #ffffff;
|
|
1641
|
+
color: #475569;
|
|
1642
|
+
font-size: 11px;
|
|
1643
|
+
line-height: 1;
|
|
1644
|
+
display: inline-flex;
|
|
1645
|
+
align-items: center;
|
|
1646
|
+
justify-content: center;
|
|
1647
|
+
cursor: pointer;
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
.ui-control__info:hover {
|
|
1651
|
+
background: #f8fafc;
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
.ui-control__info-panel {
|
|
1655
|
+
margin-top: 8px;
|
|
1656
|
+
padding: 10px 12px;
|
|
1657
|
+
border-radius: 10px;
|
|
1658
|
+
background: #f8fafc;
|
|
1659
|
+
border: 1px solid #e2e8f0;
|
|
1660
|
+
color: #475569;
|
|
1661
|
+
font-size: 12px;
|
|
1662
|
+
line-height: 1.4;
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1665
|
+
.ui-control__input {
|
|
1666
|
+
width: 100%;
|
|
1667
|
+
min-height: 3.4rem;
|
|
1668
|
+
border-radius: 10px;
|
|
1669
|
+
border: 1px solid rgba(15, 23, 42, 0.12);
|
|
1670
|
+
background: #ffffff;
|
|
1671
|
+
padding: 0.9rem 1.1rem;
|
|
1672
|
+
font-size: 15px;
|
|
1673
|
+
font-weight: 500;
|
|
1674
|
+
box-shadow: none;
|
|
1675
|
+
outline: none;
|
|
1676
|
+
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
.ui-control__input:focus {
|
|
1680
|
+
border-color: var(--color-primary);
|
|
1681
|
+
box-shadow: 0 0 0 3px rgba(15, 118, 110, 0.2);
|
|
1682
|
+
transform: translateY(-1px);
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
.ui-control__input.invalid {
|
|
1686
|
+
border-color: #ef4444;
|
|
1687
|
+
box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.18);
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
.ui-control__input::placeholder {
|
|
1691
|
+
color: #94a3b8;
|
|
1692
|
+
}
|
|
1693
|
+
`
|
|
1694
|
+
},
|
|
1695
|
+
{
|
|
1696
|
+
name: 'ui-textarea',
|
|
1697
|
+
template: `
|
|
1698
|
+
import { Component, Input } from '@angular/core'
|
|
1699
|
+
import { NgIf } from '@angular/common'
|
|
1700
|
+
import {
|
|
1701
|
+
ControlContainer,
|
|
1702
|
+
FormGroupDirective,
|
|
1703
|
+
ReactiveFormsModule
|
|
1704
|
+
} from '@angular/forms'
|
|
1705
|
+
|
|
1706
|
+
@Component({
|
|
1707
|
+
selector: 'ui-textarea',
|
|
1708
|
+
standalone: true,
|
|
1709
|
+
imports: [NgIf, ReactiveFormsModule],
|
|
1710
|
+
viewProviders: [
|
|
1711
|
+
{ provide: ControlContainer, useExisting: FormGroupDirective }
|
|
1712
|
+
],
|
|
1713
|
+
templateUrl: './ui-textarea.component.html',
|
|
1714
|
+
styleUrls: ['./ui-textarea.component.scss']
|
|
1715
|
+
})
|
|
1716
|
+
export class UiTextareaComponent {
|
|
1717
|
+
@Input() label = ''
|
|
1718
|
+
@Input() hint = ''
|
|
1719
|
+
@Input() info = ''
|
|
1720
|
+
@Input() controlName = ''
|
|
1721
|
+
@Input() placeholder = ''
|
|
1722
|
+
@Input() rows = 3
|
|
1723
|
+
@Input() invalid = false
|
|
1724
|
+
infoOpen = false
|
|
1725
|
+
|
|
1726
|
+
toggleInfo(event: MouseEvent) {
|
|
1727
|
+
event.preventDefault()
|
|
1728
|
+
event.stopPropagation()
|
|
1729
|
+
this.infoOpen = !this.infoOpen
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
`,
|
|
1733
|
+
html: `
|
|
1734
|
+
<label class="ui-control">
|
|
1735
|
+
<span class="ui-control__label" *ngIf="label">
|
|
1736
|
+
{{ label }}
|
|
1737
|
+
<button
|
|
1738
|
+
class="ui-control__info"
|
|
1739
|
+
type="button"
|
|
1740
|
+
*ngIf="info"
|
|
1741
|
+
(click)="toggleInfo($event)"
|
|
1742
|
+
[attr.aria-expanded]="infoOpen"
|
|
1743
|
+
>
|
|
1744
|
+
i
|
|
1745
|
+
</button>
|
|
1746
|
+
</span>
|
|
1747
|
+
<div class="ui-control__info-panel" *ngIf="info && infoOpen">
|
|
1748
|
+
{{ info }}
|
|
1749
|
+
</div>
|
|
1750
|
+
<textarea
|
|
1751
|
+
class="ui-control__input"
|
|
1752
|
+
[formControlName]="controlName"
|
|
1753
|
+
[rows]="rows"
|
|
1754
|
+
[placeholder]="placeholder"
|
|
1755
|
+
[class.invalid]="invalid"
|
|
1756
|
+
></textarea>
|
|
1757
|
+
<span class="ui-control__hint" *ngIf="hint && !info">{{ hint }}</span>
|
|
1758
|
+
</label>
|
|
1759
|
+
`,
|
|
1760
|
+
scss: `
|
|
1761
|
+
:host {
|
|
1762
|
+
display: block;
|
|
1763
|
+
}
|
|
1764
|
+
|
|
1765
|
+
.ui-control {
|
|
1766
|
+
display: grid;
|
|
1767
|
+
gap: 10px;
|
|
1768
|
+
font-size: 13px;
|
|
1769
|
+
color: #1f2937;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
.ui-control__label {
|
|
1773
|
+
font-weight: 700;
|
|
1774
|
+
line-height: 1.4;
|
|
1775
|
+
word-break: break-word;
|
|
1776
|
+
letter-spacing: 0.01em;
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
.ui-control__hint {
|
|
1780
|
+
color: #94a3b8;
|
|
1781
|
+
font-size: 12px;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1784
|
+
.ui-control__info {
|
|
1785
|
+
margin-left: 8px;
|
|
1786
|
+
width: 18px;
|
|
1787
|
+
height: 18px;
|
|
1788
|
+
border-radius: 999px;
|
|
1789
|
+
border: 1px solid rgba(15, 23, 42, 0.2);
|
|
1790
|
+
background: #ffffff;
|
|
1791
|
+
color: #475569;
|
|
1792
|
+
font-size: 11px;
|
|
1793
|
+
line-height: 1;
|
|
1794
|
+
display: inline-flex;
|
|
1795
|
+
align-items: center;
|
|
1796
|
+
justify-content: center;
|
|
1797
|
+
cursor: pointer;
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
.ui-control__info:hover {
|
|
1801
|
+
background: #f8fafc;
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
.ui-control__info-panel {
|
|
1805
|
+
margin-top: 8px;
|
|
1806
|
+
padding: 10px 12px;
|
|
1807
|
+
border-radius: 10px;
|
|
1808
|
+
background: #f8fafc;
|
|
1809
|
+
border: 1px solid #e2e8f0;
|
|
1810
|
+
color: #475569;
|
|
1811
|
+
font-size: 12px;
|
|
1812
|
+
line-height: 1.4;
|
|
1813
|
+
}
|
|
1814
|
+
|
|
1815
|
+
.ui-control__input {
|
|
1816
|
+
width: 100%;
|
|
1817
|
+
min-height: 3.4rem;
|
|
1818
|
+
border-radius: 10px;
|
|
1819
|
+
border: 1px solid rgba(15, 23, 42, 0.12);
|
|
1820
|
+
background: #ffffff;
|
|
1821
|
+
padding: 0.9rem 1.1rem;
|
|
1822
|
+
font-size: 15px;
|
|
1823
|
+
font-weight: 500;
|
|
1824
|
+
box-shadow: none;
|
|
1825
|
+
outline: none;
|
|
1826
|
+
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
|
|
1827
|
+
}
|
|
1828
|
+
|
|
1829
|
+
.ui-control__input:focus {
|
|
1830
|
+
border-color: var(--color-primary);
|
|
1831
|
+
box-shadow: 0 0 0 3px rgba(15, 118, 110, 0.2);
|
|
1832
|
+
transform: translateY(-1px);
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
.ui-control__input.invalid {
|
|
1836
|
+
border-color: #ef4444;
|
|
1837
|
+
box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.18);
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
.ui-control__input::placeholder {
|
|
1841
|
+
color: #94a3b8;
|
|
1842
|
+
}
|
|
1843
|
+
`
|
|
1844
|
+
},
|
|
1845
|
+
{
|
|
1846
|
+
name: 'ui-select',
|
|
1847
|
+
template: `
|
|
1848
|
+
import { Component, Input } from '@angular/core'
|
|
1849
|
+
import { NgFor, NgIf } from '@angular/common'
|
|
1850
|
+
import {
|
|
1851
|
+
ControlContainer,
|
|
1852
|
+
FormGroupDirective,
|
|
1853
|
+
ReactiveFormsModule
|
|
1854
|
+
} from '@angular/forms'
|
|
1855
|
+
|
|
1856
|
+
@Component({
|
|
1857
|
+
selector: 'ui-select',
|
|
1858
|
+
standalone: true,
|
|
1859
|
+
imports: [NgFor, NgIf, ReactiveFormsModule],
|
|
1860
|
+
viewProviders: [
|
|
1861
|
+
{ provide: ControlContainer, useExisting: FormGroupDirective }
|
|
1862
|
+
],
|
|
1863
|
+
templateUrl: './ui-select.component.html',
|
|
1864
|
+
styleUrls: ['./ui-select.component.scss']
|
|
1865
|
+
})
|
|
1866
|
+
export class UiSelectComponent {
|
|
1867
|
+
@Input() label = ''
|
|
1868
|
+
@Input() hint = ''
|
|
1869
|
+
@Input() info = ''
|
|
1870
|
+
@Input() controlName = ''
|
|
1871
|
+
@Input() options: any[] = []
|
|
1872
|
+
@Input() invalid = false
|
|
1873
|
+
infoOpen = false
|
|
1874
|
+
|
|
1875
|
+
toggleInfo(event: MouseEvent) {
|
|
1876
|
+
event.preventDefault()
|
|
1877
|
+
event.stopPropagation()
|
|
1878
|
+
this.infoOpen = !this.infoOpen
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
`,
|
|
1882
|
+
html: `
|
|
1883
|
+
<label class="ui-control">
|
|
1884
|
+
<span class="ui-control__label" *ngIf="label">
|
|
1885
|
+
{{ label }}
|
|
1886
|
+
<button
|
|
1887
|
+
class="ui-control__info"
|
|
1888
|
+
type="button"
|
|
1889
|
+
*ngIf="info"
|
|
1890
|
+
(click)="toggleInfo($event)"
|
|
1891
|
+
[attr.aria-expanded]="infoOpen"
|
|
1892
|
+
>
|
|
1893
|
+
i
|
|
1894
|
+
</button>
|
|
1895
|
+
</span>
|
|
1896
|
+
<div class="ui-control__info-panel" *ngIf="info && infoOpen">
|
|
1897
|
+
{{ info }}
|
|
1898
|
+
</div>
|
|
1899
|
+
<select
|
|
1900
|
+
class="ui-control__select"
|
|
1901
|
+
[formControlName]="controlName"
|
|
1902
|
+
[class.invalid]="invalid"
|
|
1903
|
+
>
|
|
1904
|
+
<option *ngFor="let option of options" [value]="option">
|
|
1905
|
+
{{ option }}
|
|
1906
|
+
</option>
|
|
1907
|
+
</select>
|
|
1908
|
+
<span class="ui-control__hint" *ngIf="hint && !info">{{ hint }}</span>
|
|
1909
|
+
</label>
|
|
1910
|
+
`,
|
|
1911
|
+
scss: `
|
|
1912
|
+
:host {
|
|
1913
|
+
display: block;
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
.ui-control {
|
|
1917
|
+
display: grid;
|
|
1918
|
+
gap: 10px;
|
|
1919
|
+
font-size: 13px;
|
|
1920
|
+
color: #1f2937;
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
.ui-control__label {
|
|
1924
|
+
font-weight: 700;
|
|
1925
|
+
line-height: 1.4;
|
|
1926
|
+
word-break: break-word;
|
|
1927
|
+
letter-spacing: 0.01em;
|
|
1928
|
+
}
|
|
1929
|
+
|
|
1930
|
+
.ui-control__hint {
|
|
1931
|
+
color: #94a3b8;
|
|
1932
|
+
font-size: 12px;
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
.ui-control__info {
|
|
1936
|
+
margin-left: 8px;
|
|
1937
|
+
width: 18px;
|
|
1938
|
+
height: 18px;
|
|
1939
|
+
border-radius: 999px;
|
|
1940
|
+
border: 1px solid rgba(15, 23, 42, 0.2);
|
|
1941
|
+
background: #ffffff;
|
|
1942
|
+
color: #475569;
|
|
1943
|
+
font-size: 11px;
|
|
1944
|
+
line-height: 1;
|
|
1945
|
+
display: inline-flex;
|
|
1946
|
+
align-items: center;
|
|
1947
|
+
justify-content: center;
|
|
1948
|
+
cursor: pointer;
|
|
1949
|
+
}
|
|
1950
|
+
|
|
1951
|
+
.ui-control__info:hover {
|
|
1952
|
+
background: #f8fafc;
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
.ui-control__info-panel {
|
|
1956
|
+
margin-top: 8px;
|
|
1957
|
+
padding: 10px 12px;
|
|
1958
|
+
border-radius: 10px;
|
|
1959
|
+
background: #f8fafc;
|
|
1960
|
+
border: 1px solid #e2e8f0;
|
|
1961
|
+
color: #475569;
|
|
1962
|
+
font-size: 12px;
|
|
1963
|
+
line-height: 1.4;
|
|
1964
|
+
}
|
|
1965
|
+
|
|
1966
|
+
.ui-control__select {
|
|
1967
|
+
width: 100%;
|
|
1968
|
+
min-height: 3.4rem;
|
|
1969
|
+
border-radius: 10px;
|
|
1970
|
+
border: 1px solid rgba(15, 23, 42, 0.12);
|
|
1971
|
+
background: #ffffff;
|
|
1972
|
+
padding: 0.9rem 2.6rem 0.9rem 1.1rem;
|
|
1973
|
+
font-size: 15px;
|
|
1974
|
+
font-weight: 500;
|
|
1975
|
+
box-shadow: none;
|
|
1976
|
+
outline: none;
|
|
1977
|
+
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
|
|
1978
|
+
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='14' height='8' viewBox='0 0 14 8' fill='none'><path d='M1 1.5L7 6.5L13 1.5' stroke='%236b7280' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
|
|
1979
|
+
background-repeat: no-repeat;
|
|
1980
|
+
background-position: right 0.9rem center;
|
|
1981
|
+
background-size: 14px 8px;
|
|
1982
|
+
appearance: none;
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
.ui-control__select:focus {
|
|
1986
|
+
border-color: var(--color-primary);
|
|
1987
|
+
box-shadow: 0 0 0 3px rgba(15, 118, 110, 0.2);
|
|
1988
|
+
transform: translateY(-1px);
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
.ui-control__select.invalid {
|
|
1992
|
+
border-color: #ef4444;
|
|
1993
|
+
box-shadow: 0 0 0 2px rgba(239, 68, 68, 0.18);
|
|
1994
|
+
}
|
|
1995
|
+
`
|
|
1996
|
+
},
|
|
1997
|
+
{
|
|
1998
|
+
name: 'ui-checkbox',
|
|
1999
|
+
template: `
|
|
2000
|
+
import { Component, Input } from '@angular/core'
|
|
2001
|
+
import { NgIf } from '@angular/common'
|
|
2002
|
+
import {
|
|
2003
|
+
ControlContainer,
|
|
2004
|
+
FormGroupDirective,
|
|
2005
|
+
ReactiveFormsModule
|
|
2006
|
+
} from '@angular/forms'
|
|
2007
|
+
|
|
2008
|
+
@Component({
|
|
2009
|
+
selector: 'ui-checkbox',
|
|
2010
|
+
standalone: true,
|
|
2011
|
+
imports: [NgIf, ReactiveFormsModule],
|
|
2012
|
+
viewProviders: [
|
|
2013
|
+
{ provide: ControlContainer, useExisting: FormGroupDirective }
|
|
2014
|
+
],
|
|
2015
|
+
templateUrl: './ui-checkbox.component.html',
|
|
2016
|
+
styleUrls: ['./ui-checkbox.component.scss']
|
|
2017
|
+
})
|
|
2018
|
+
export class UiCheckboxComponent {
|
|
2019
|
+
@Input() label = ''
|
|
2020
|
+
@Input() hint = ''
|
|
2021
|
+
@Input() info = ''
|
|
2022
|
+
@Input() controlName = ''
|
|
2023
|
+
@Input() invalid = false
|
|
2024
|
+
infoOpen = false
|
|
2025
|
+
|
|
2026
|
+
toggleInfo(event: MouseEvent) {
|
|
2027
|
+
event.preventDefault()
|
|
2028
|
+
event.stopPropagation()
|
|
2029
|
+
this.infoOpen = !this.infoOpen
|
|
2030
|
+
}
|
|
2031
|
+
}
|
|
2032
|
+
`,
|
|
2033
|
+
html: `
|
|
2034
|
+
<label class="ui-control">
|
|
2035
|
+
<span class="ui-control__label" *ngIf="label">
|
|
2036
|
+
{{ label }}
|
|
2037
|
+
<button
|
|
2038
|
+
class="ui-control__info"
|
|
2039
|
+
type="button"
|
|
2040
|
+
*ngIf="info"
|
|
2041
|
+
(click)="toggleInfo($event)"
|
|
2042
|
+
[attr.aria-expanded]="infoOpen"
|
|
2043
|
+
>
|
|
2044
|
+
i
|
|
2045
|
+
</button>
|
|
2046
|
+
</span>
|
|
2047
|
+
<div class="ui-control__info-panel" *ngIf="info && infoOpen">
|
|
2048
|
+
{{ info }}
|
|
2049
|
+
</div>
|
|
2050
|
+
<input
|
|
2051
|
+
class="ui-control__checkbox"
|
|
2052
|
+
type="checkbox"
|
|
2053
|
+
[formControlName]="controlName"
|
|
2054
|
+
[class.invalid]="invalid"
|
|
2055
|
+
/>
|
|
2056
|
+
<span class="ui-control__hint" *ngIf="hint && !info">{{ hint }}</span>
|
|
2057
|
+
</label>
|
|
2058
|
+
`,
|
|
2059
|
+
scss: `
|
|
2060
|
+
:host {
|
|
2061
|
+
display: block;
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
.ui-control {
|
|
2065
|
+
display: grid;
|
|
2066
|
+
gap: 10px;
|
|
2067
|
+
font-size: 13px;
|
|
2068
|
+
color: #1f2937;
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
.ui-control__label {
|
|
2072
|
+
font-weight: 700;
|
|
2073
|
+
line-height: 1.4;
|
|
2074
|
+
word-break: break-word;
|
|
2075
|
+
letter-spacing: 0.01em;
|
|
2076
|
+
}
|
|
2077
|
+
|
|
2078
|
+
.ui-control__hint {
|
|
2079
|
+
color: #94a3b8;
|
|
2080
|
+
font-size: 12px;
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
.ui-control__info {
|
|
2084
|
+
margin-left: 8px;
|
|
2085
|
+
width: 18px;
|
|
2086
|
+
height: 18px;
|
|
2087
|
+
border-radius: 999px;
|
|
2088
|
+
border: 1px solid rgba(15, 23, 42, 0.2);
|
|
2089
|
+
background: #ffffff;
|
|
2090
|
+
color: #475569;
|
|
2091
|
+
font-size: 11px;
|
|
2092
|
+
line-height: 1;
|
|
2093
|
+
display: inline-flex;
|
|
2094
|
+
align-items: center;
|
|
2095
|
+
justify-content: center;
|
|
2096
|
+
cursor: pointer;
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
.ui-control__info:hover {
|
|
2100
|
+
background: #f8fafc;
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
.ui-control__info-panel {
|
|
2104
|
+
margin-top: 8px;
|
|
2105
|
+
padding: 10px 12px;
|
|
2106
|
+
border-radius: 10px;
|
|
2107
|
+
background: #f8fafc;
|
|
2108
|
+
border: 1px solid #e2e8f0;
|
|
2109
|
+
color: #475569;
|
|
2110
|
+
font-size: 12px;
|
|
2111
|
+
line-height: 1.4;
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
.ui-control__checkbox {
|
|
2115
|
+
width: 20px;
|
|
2116
|
+
height: 20px;
|
|
2117
|
+
padding: 0;
|
|
2118
|
+
border-radius: 6px;
|
|
2119
|
+
box-shadow: none;
|
|
2120
|
+
accent-color: var(--color-primary);
|
|
2121
|
+
}
|
|
2122
|
+
`
|
|
2123
|
+
}
|
|
2124
|
+
];
|
|
2125
|
+
fs_1.default.mkdirSync(uiRoot, { recursive: true });
|
|
2126
|
+
for (const component of components) {
|
|
2127
|
+
const componentDir = path_1.default.join(uiRoot, component.name);
|
|
2128
|
+
fs_1.default.mkdirSync(componentDir, { recursive: true });
|
|
2129
|
+
const base = component.name;
|
|
2130
|
+
const tsPath = path_1.default.join(componentDir, `${base}.component.ts`);
|
|
2131
|
+
const htmlPath = path_1.default.join(componentDir, `${base}.component.html`);
|
|
2132
|
+
const scssPath = path_1.default.join(componentDir, `${base}.component.scss`);
|
|
2133
|
+
const needsUiFieldUpdate = component.name === 'ui-field';
|
|
2134
|
+
const shouldOverwrite = (filePath, marker) => {
|
|
2135
|
+
if (!fs_1.default.existsSync(filePath))
|
|
2136
|
+
return true;
|
|
2137
|
+
if (needsUiFieldUpdate)
|
|
2138
|
+
return true;
|
|
2139
|
+
if (!marker)
|
|
2140
|
+
return true;
|
|
2141
|
+
const existing = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
2142
|
+
return !existing.includes(marker);
|
|
2143
|
+
};
|
|
2144
|
+
if (component.name === 'ui-menu') {
|
|
2145
|
+
const menuImportPath = schemasRoot
|
|
2146
|
+
? buildRelativeImportPath(componentDir, path_1.default.join(schemasRoot, 'menu.gen'))
|
|
2147
|
+
: './menu.gen';
|
|
2148
|
+
component.template = `
|
|
2149
|
+
import { Component, Input } from '@angular/core'
|
|
2150
|
+
import { NgFor, NgIf } from '@angular/common'
|
|
2151
|
+
import { RouterLink, RouterLinkActive } from '@angular/router'
|
|
2152
|
+
import { GeneratedMenu, generatedMenu } from '${menuImportPath}'
|
|
2153
|
+
|
|
2154
|
+
@Component({
|
|
2155
|
+
selector: 'ui-menu',
|
|
2156
|
+
standalone: true,
|
|
2157
|
+
imports: [NgIf, NgFor, RouterLink, RouterLinkActive],
|
|
2158
|
+
templateUrl: './ui-menu.component.html',
|
|
2159
|
+
styleUrls: ['./ui-menu.component.scss']
|
|
2160
|
+
})
|
|
2161
|
+
export class UiMenuComponent {
|
|
2162
|
+
@Input() menu: GeneratedMenu = generatedMenu
|
|
2163
|
+
@Input() title = 'Generate UI'
|
|
2164
|
+
}
|
|
2165
|
+
`;
|
|
2166
|
+
}
|
|
2167
|
+
if (shouldOverwrite(tsPath, 'infoOpen')) {
|
|
2168
|
+
fs_1.default.writeFileSync(tsPath, component.template.trimStart());
|
|
2169
|
+
}
|
|
2170
|
+
if (shouldOverwrite(htmlPath, 'ui-control__info-panel')) {
|
|
2171
|
+
fs_1.default.writeFileSync(htmlPath, component.html.trimStart());
|
|
2172
|
+
}
|
|
2173
|
+
if (shouldOverwrite(scssPath, 'ui-control__info-panel')) {
|
|
2174
|
+
fs_1.default.writeFileSync(scssPath, component.scss.trimStart());
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
}
|
|
2178
|
+
function toRouteSegment(operationId) {
|
|
1212
2179
|
if (!operationId)
|
|
1213
2180
|
return operationId;
|
|
1214
2181
|
return operationId[0].toLowerCase() + operationId.slice(1);
|
|
@@ -1243,6 +2210,14 @@ function buildSchemaImportPath(featureDir, schemasRoot, rawName) {
|
|
|
1243
2210
|
}
|
|
1244
2211
|
return relativePath;
|
|
1245
2212
|
}
|
|
2213
|
+
function buildRelativeImportPath(fromDir, targetFile) {
|
|
2214
|
+
let relativePath = path_1.default.relative(fromDir, targetFile);
|
|
2215
|
+
relativePath = toPosixPath(relativePath);
|
|
2216
|
+
if (!relativePath.startsWith('.')) {
|
|
2217
|
+
relativePath = `./${relativePath}`;
|
|
2218
|
+
}
|
|
2219
|
+
return relativePath;
|
|
2220
|
+
}
|
|
1246
2221
|
function toPosixPath(value) {
|
|
1247
2222
|
return value.split(path_1.default.sep).join(path_1.default.posix.sep);
|
|
1248
2223
|
}
|