@seniorsistemas/angular-components 19.9.2 → 19.9.3
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 +99 -13
- package/esm2022/dynamic-form/dynamic-form/components/structure/fieldset/fieldset.component.mjs +4 -3
- package/esm2022/fieldset/lib/fieldset/fieldset.component.mjs +45 -34
- package/fesm2022/seniorsistemas-angular-components-dynamic-form.mjs +4 -4
- package/fesm2022/seniorsistemas-angular-components-dynamic-form.mjs.map +1 -1
- package/fesm2022/seniorsistemas-angular-components-fieldset.mjs +44 -33
- package/fesm2022/seniorsistemas-angular-components-fieldset.mjs.map +1 -1
- package/fieldset/README.md +77 -15
- package/fieldset/lib/fieldset/fieldset.component.d.ts +28 -14
- package/package.json +13 -13
- package/schematics/collection.json +26 -1
- package/schematics/migrations/migrate-dynamic-form-v19/index.js +15 -34
- package/schematics/migrations/migrate-dynamic-form-v19/index.js.map +1 -1
- package/schematics/migrations/migrate-fieldset-body-template/index.d.ts +2 -0
- package/schematics/migrations/migrate-fieldset-body-template/index.js +421 -0
- package/schematics/migrations/migrate-fieldset-body-template/index.js.map +1 -0
- package/schematics/migrations/migrate-steps-to-stepper/index.js +1 -19
- package/schematics/migrations/migrate-steps-to-stepper/index.js.map +1 -1
- package/schematics/migrations.json +5 -0
- package/schematics/utils/ast-utils.d.ts +33 -1
- package/schematics/utils/ast-utils.js +175 -4
- package/schematics/utils/ast-utils.js.map +1 -1
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
import { trigger, transition, style, animate, state } from '@angular/animations';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { EventEmitter,
|
|
4
|
-
import { TemplateDirective } from '@seniorsistemas/angular-components/template';
|
|
5
|
-
import { InteractiveContentDirective } from '@seniorsistemas/angular-components/interactive-content';
|
|
3
|
+
import { input, contentChildren, computed, EventEmitter, model, signal, Component, Output, NgModule } from '@angular/core';
|
|
6
4
|
import { NgTemplateOutlet } from '@angular/common';
|
|
5
|
+
import { InteractiveContentDirective } from '@seniorsistemas/angular-components/interactive-content';
|
|
6
|
+
import { TemplateDirective } from '@seniorsistemas/angular-components/template';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* @description Componente de fieldset com legenda, ícone configurável e suporte a
|
|
10
|
-
* toggle (recolher/expandir) com animação. Suporta
|
|
11
|
-
*
|
|
10
|
+
* toggle (recolher/expandir) com animação. Suporta templates personalizados via
|
|
11
|
+
* diretiva `TemplateDirective`, com os tipos `'header'` (substitui a legenda) e
|
|
12
|
+
* `'body'` (conteúdo do fieldset).
|
|
13
|
+
*
|
|
14
|
+
* Prefira declarar o conteúdo em `sTemplate="body"` em vez de projetá-lo direto:
|
|
15
|
+
* o `ng-content` é instanciado pelo componente pai mesmo com o fieldset recolhido,
|
|
16
|
+
* disparando o `ngOnInit` dos filhos prematuramente. O template de corpo só é
|
|
17
|
+
* instanciado quando o fieldset está de fato ativo, e é obrigatório para que
|
|
18
|
+
* `destroyOnHide` funcione.
|
|
12
19
|
*
|
|
13
20
|
* @example
|
|
14
21
|
* ```html
|
|
15
22
|
* <s-fieldset legend="Dados pessoais" [toggleable]="true">
|
|
16
|
-
* <
|
|
23
|
+
* <ng-template sTemplate="body">
|
|
24
|
+
* <p>Conteúdo do fieldset</p>
|
|
25
|
+
* </ng-template>
|
|
17
26
|
* </s-fieldset>
|
|
18
27
|
* ```
|
|
19
28
|
*
|
|
@@ -21,33 +30,46 @@ import { NgTemplateOutlet } from '@angular/common';
|
|
|
21
30
|
*/
|
|
22
31
|
class FieldsetComponent {
|
|
23
32
|
/** @description Texto exibido como título do fieldset. @default '' */
|
|
24
|
-
legend = '';
|
|
33
|
+
legend = input('');
|
|
25
34
|
/** @description Habilita o botão de toggle que permite recolher e expandir o conteúdo. @default false */
|
|
26
|
-
toggleable = false;
|
|
35
|
+
toggleable = input(false);
|
|
27
36
|
/** @description Classe de ícone exibida ao lado da legenda (ex.: `'fas fa-cog'`). @default '' */
|
|
28
|
-
icon = '';
|
|
29
|
-
/** @description Quando `true`, remove o conteúdo do DOM ao recolher o fieldset. @default
|
|
30
|
-
destroyOnHide =
|
|
31
|
-
templateDirectives;
|
|
32
|
-
headerTemplate
|
|
37
|
+
icon = input('');
|
|
38
|
+
/** @description Quando `true`, remove o conteúdo do DOM ao recolher o fieldset. @default false */
|
|
39
|
+
destroyOnHide = input(false);
|
|
40
|
+
templateDirectives = contentChildren(TemplateDirective);
|
|
41
|
+
headerTemplate = computed(() => {
|
|
42
|
+
return this.templateDirectives()?.find((template) => template.type === 'header')?.template;
|
|
43
|
+
});
|
|
44
|
+
bodyTemplate = computed(() => {
|
|
45
|
+
return this.templateDirectives()?.find((template) => template.type === 'body')?.template;
|
|
46
|
+
});
|
|
33
47
|
/** @description Emitido imediatamente antes da animação de toggle iniciar, com o estado atual. */
|
|
34
48
|
beforeToggle = new EventEmitter();
|
|
35
49
|
/** @description Emitido após a animação de toggle concluir, com o novo estado. */
|
|
36
50
|
afterToggle = new EventEmitter();
|
|
37
|
-
active = true;
|
|
51
|
+
active = model(true);
|
|
52
|
+
/** @description `true` quando a animação de fechamento já terminou, aplicando `overflow-hidden` ao conteúdo. */
|
|
53
|
+
collapsed = signal(false);
|
|
38
54
|
ngAfterViewInit() {
|
|
39
|
-
|
|
55
|
+
const hasBodyTemplate = this.bodyTemplate();
|
|
56
|
+
if (!hasBodyTemplate && this.destroyOnHide()) {
|
|
57
|
+
console.warn('FieldsetComponent: O uso de destroyOnHide=true requer um template de corpo definido com sTemplate="body".');
|
|
58
|
+
}
|
|
40
59
|
}
|
|
41
60
|
toggleActive(originalEvent) {
|
|
42
|
-
if (!this.toggleable) {
|
|
61
|
+
if (!this.toggleable()) {
|
|
43
62
|
return;
|
|
44
63
|
}
|
|
45
|
-
this.beforeToggle.next({ collapsed: this.active, originalEvent });
|
|
46
|
-
this.active
|
|
47
|
-
this.afterToggle.next({ collapsed: this.active, originalEvent });
|
|
64
|
+
this.beforeToggle.next({ collapsed: this.active(), originalEvent });
|
|
65
|
+
this.active.set(!this.active());
|
|
66
|
+
this.afterToggle.next({ collapsed: this.active(), originalEvent });
|
|
67
|
+
}
|
|
68
|
+
onToggleAnimationDone(event) {
|
|
69
|
+
this.collapsed.set(event.toState === 'closed');
|
|
48
70
|
}
|
|
49
71
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: FieldsetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
50
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: FieldsetComponent, isStandalone: true, selector: "s-fieldset", inputs: { legend: "legend", toggleable: "toggleable", icon: "icon", destroyOnHide: "destroyOnHide" }, outputs: { beforeToggle: "beforeToggle", afterToggle: "afterToggle" }, queries: [{ propertyName: "templateDirectives", predicate: TemplateDirective }], ngImport: i0, template: "<fieldset\r\n class=\"
|
|
72
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "18.2.14", type: FieldsetComponent, isStandalone: true, selector: "s-fieldset", inputs: { legend: { classPropertyName: "legend", publicName: "legend", isSignal: true, isRequired: false, transformFunction: null }, toggleable: { classPropertyName: "toggleable", publicName: "toggleable", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, destroyOnHide: { classPropertyName: "destroyOnHide", publicName: "destroyOnHide", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { beforeToggle: "beforeToggle", afterToggle: "afterToggle", active: "activeChange" }, queries: [{ propertyName: "templateDirectives", predicate: TemplateDirective, isSignal: true }], ngImport: i0, template: "<fieldset\r\n class=\"mb-[10px] min-h-[4.5em] w-full min-w-0 rounded-xbig border border-grayscale-20 bg-grayscale-0 px-2 py-1.5\"\r\n>\r\n <legend\r\n class=\"ml-4 flex select-none items-center gap-1 px-1 font-open-sans text-sm font-bold text-grayscale-90\"\r\n [class.cursor-pointer]=\"toggleable()\"\r\n (sInteractiveContent)=\"toggleActive($event)\"\r\n >\r\n @if (toggleable()) {\r\n @if (active()) {\r\n <i class=\"fas fa-minus\"></i>\r\n } @else {\r\n <i class=\"fas fa-plus\"></i>\r\n }\r\n }\r\n @let _headerTemplate = headerTemplate();\r\n @if (_headerTemplate) {\r\n <ng-container *ngTemplateOutlet=\"_headerTemplate\"></ng-container>\r\n } @else {\r\n @if (icon()) {\r\n <i [class]=\"icon()\"></i>\r\n }\r\n {{ legend() }}\r\n }\r\n </legend>\r\n @if (destroyOnHide() ? (toggleable() ? active() : true) : true) {\r\n <div\r\n [@toggleAnimation]=\"destroyOnHide() ? null : active() ? 'open' : 'closed'\"\r\n (@toggleAnimation.done)=\"onToggleAnimationDone($event)\"\r\n [class.overflow-hidden]=\"collapsed()\"\r\n >\r\n @let _bodyTemplate = bodyTemplate();\r\n @if (_bodyTemplate) {\r\n <ng-container *ngTemplateOutlet=\"_bodyTemplate\"></ng-container>\r\n } @else {\r\n <ng-content></ng-content>\r\n }\r\n </div>\r\n }\r\n</fieldset>\r\n", styles: [":host{display:block}\n"], dependencies: [{ kind: "directive", type: InteractiveContentDirective, selector: "[sInteractiveContent]", inputs: ["focusable", "disabled", "stopPropagation"], outputs: ["sInteractiveContent"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }], animations: [
|
|
51
73
|
trigger('toggleAnimation', [
|
|
52
74
|
transition(':enter', [
|
|
53
75
|
style({ opacity: 0, height: 0 }),
|
|
@@ -79,19 +101,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
|
|
|
79
101
|
state('closed', style({ opacity: 0, height: 0 })),
|
|
80
102
|
transition('open <=> closed', animate('300ms ease')),
|
|
81
103
|
]),
|
|
82
|
-
], standalone: true, imports: [InteractiveContentDirective, NgTemplateOutlet], template: "<fieldset\r\n class=\"
|
|
83
|
-
}], propDecorators: {
|
|
84
|
-
type: Input
|
|
85
|
-
}], toggleable: [{
|
|
86
|
-
type: Input
|
|
87
|
-
}], icon: [{
|
|
88
|
-
type: Input
|
|
89
|
-
}], destroyOnHide: [{
|
|
90
|
-
type: Input
|
|
91
|
-
}], templateDirectives: [{
|
|
92
|
-
type: ContentChildren,
|
|
93
|
-
args: [TemplateDirective]
|
|
94
|
-
}], beforeToggle: [{
|
|
104
|
+
], standalone: true, imports: [InteractiveContentDirective, NgTemplateOutlet], template: "<fieldset\r\n class=\"mb-[10px] min-h-[4.5em] w-full min-w-0 rounded-xbig border border-grayscale-20 bg-grayscale-0 px-2 py-1.5\"\r\n>\r\n <legend\r\n class=\"ml-4 flex select-none items-center gap-1 px-1 font-open-sans text-sm font-bold text-grayscale-90\"\r\n [class.cursor-pointer]=\"toggleable()\"\r\n (sInteractiveContent)=\"toggleActive($event)\"\r\n >\r\n @if (toggleable()) {\r\n @if (active()) {\r\n <i class=\"fas fa-minus\"></i>\r\n } @else {\r\n <i class=\"fas fa-plus\"></i>\r\n }\r\n }\r\n @let _headerTemplate = headerTemplate();\r\n @if (_headerTemplate) {\r\n <ng-container *ngTemplateOutlet=\"_headerTemplate\"></ng-container>\r\n } @else {\r\n @if (icon()) {\r\n <i [class]=\"icon()\"></i>\r\n }\r\n {{ legend() }}\r\n }\r\n </legend>\r\n @if (destroyOnHide() ? (toggleable() ? active() : true) : true) {\r\n <div\r\n [@toggleAnimation]=\"destroyOnHide() ? null : active() ? 'open' : 'closed'\"\r\n (@toggleAnimation.done)=\"onToggleAnimationDone($event)\"\r\n [class.overflow-hidden]=\"collapsed()\"\r\n >\r\n @let _bodyTemplate = bodyTemplate();\r\n @if (_bodyTemplate) {\r\n <ng-container *ngTemplateOutlet=\"_bodyTemplate\"></ng-container>\r\n } @else {\r\n <ng-content></ng-content>\r\n }\r\n </div>\r\n }\r\n</fieldset>\r\n", styles: [":host{display:block}\n"] }]
|
|
105
|
+
}], propDecorators: { beforeToggle: [{
|
|
95
106
|
type: Output
|
|
96
107
|
}], afterToggle: [{
|
|
97
108
|
type: Output
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seniorsistemas-angular-components-fieldset.mjs","sources":["../../projects/angular-components/fieldset/src/lib/fieldset/fieldset.component.ts","../../projects/angular-components/fieldset/src/lib/fieldset/fieldset.component.html","../../projects/angular-components/fieldset/src/lib/fieldset/fieldset.module.ts","../../projects/angular-components/fieldset/src/seniorsistemas-angular-components-fieldset.ts"],"sourcesContent":["import { animate, state, style, transition, trigger } from '@angular/animations';\r\nimport {\r\n AfterViewInit,\r\n Component,\r\n ContentChildren,\r\n EventEmitter,\r\n Input,\r\n Output,\r\n QueryList,\r\n TemplateRef,\r\n} from '@angular/core';\r\n\r\nimport { TemplateDirective } from '@seniorsistemas/angular-components/template';\r\nimport { FieldSetToggle } from './fieldset.models';\r\nimport { InteractiveContentDirective } from '@seniorsistemas/angular-components/interactive-content';\r\nimport { NgTemplateOutlet } from '@angular/common';\r\n\r\n/**\r\n * @description Componente de fieldset com legenda, ícone configurável e suporte a\r\n * toggle (recolher/expandir) com animação. Suporta template personalizado para o\r\n * cabeçalho via diretiva `TemplateDirective` com tipo `'header'`.\r\n *\r\n * @example\r\n * ```html\r\n * <s-fieldset legend=\"Dados pessoais\" [toggleable]=\"true\">\r\n * <p>Conteúdo do fieldset</p>\r\n * </s-fieldset>\r\n * ```\r\n *\r\n * @category Inputs\r\n */\r\n@Component({\r\n selector: 's-fieldset',\r\n templateUrl: './fieldset.component.html',\r\n styles: [':host { display: block; }'],\r\n animations: [\r\n trigger('toggleAnimation', [\r\n transition(':enter', [\r\n style({ opacity: 0, height: 0 }),\r\n animate('300ms ease-out', style({ opacity: 1, height: '*' })),\r\n ]),\r\n transition(':leave', [\r\n style({ opacity: 1, height: '*' }),\r\n animate('300ms ease-in', style({ opacity: 0, height: 0 })),\r\n ]),\r\n state('open', style({ opacity: 1, height: '*' })),\r\n state('closed', style({ opacity: 0, height: 0 })),\r\n transition('open <=> closed', animate('300ms ease')),\r\n ]),\r\n ],\r\n standalone: true,\r\n imports: [InteractiveContentDirective, NgTemplateOutlet],\r\n})\r\nexport class FieldsetComponent implements AfterViewInit {\r\n /** @description Texto exibido como título do fieldset. @default '' */\r\n @Input() legend = '';\r\n /** @description Habilita o botão de toggle que permite recolher e expandir o conteúdo. @default false */\r\n @Input() toggleable = false;\r\n /** @description Classe de ícone exibida ao lado da legenda (ex.: `'fas fa-cog'`). @default '' */\r\n @Input() icon = '';\r\n /** @description Quando `true`, remove o conteúdo do DOM ao recolher o fieldset. @default true */\r\n @Input() destroyOnHide = true;\r\n @ContentChildren(TemplateDirective) readonly templateDirectives: QueryList<TemplateDirective> | undefined;\r\n headerTemplate: TemplateRef<any> | undefined;\r\n /** @description Emitido imediatamente antes da animação de toggle iniciar, com o estado atual. */\r\n @Output() beforeToggle = new EventEmitter<FieldSetToggle>();\r\n /** @description Emitido após a animação de toggle concluir, com o novo estado. */\r\n @Output() afterToggle = new EventEmitter<FieldSetToggle>();\r\n active = true;\r\n\r\n ngAfterViewInit(): void {\r\n this.headerTemplate = this.templateDirectives?.find((template) => template.type === 'header')?.template;\r\n }\r\n\r\n toggleActive(originalEvent: PointerEvent | KeyboardEvent) {\r\n if (!this.toggleable) {\r\n return;\r\n }\r\n this.beforeToggle.next({ collapsed: this.active, originalEvent });\r\n this.active = !this.active;\r\n this.afterToggle.next({ collapsed: this.active, originalEvent });\r\n }\r\n}\r\n","<fieldset\r\n class=\"bg-grayscale-0 border border-grayscale-20 rounded-xbig mb-[10px] min-h-[4.5em] px-2 py-1.5 w-full min-w-0\"\r\n>\r\n <legend\r\n class=\"flex items-center text-grayscale-90 font-open-sans text-sm font-bold px-1 gap-1 ml-4 select-none\"\r\n [class.cursor-pointer]=\"toggleable\"\r\n (sInteractiveContent)=\"toggleActive($event)\"\r\n >\r\n @if (toggleable) {\r\n @if (active) {\r\n <i class=\"fas fa-plus\"></i>\r\n } @else {\r\n <i class=\"fas fa-minus\"></i>\r\n }\r\n }\r\n @if (headerTemplate) {\r\n <ng-container *ngTemplateOutlet=\"headerTemplate\"></ng-container>\r\n } @else {\r\n @if (icon) {\r\n <i [class]=\"icon\"></i>\r\n }\r\n {{ legend }}\r\n }\r\n </legend>\r\n @if (destroyOnHide ? (toggleable ? active : true) : true) {\r\n <div [@toggleAnimation]=\"destroyOnHide ? null : active ? 'open' : 'closed'\">\r\n <ng-content></ng-content>\r\n </div>\r\n }\r\n</fieldset>\r\n","import { NgModule } from '@angular/core';\r\n\r\nimport { FieldsetComponent } from './fieldset.component';\r\n\r\n@NgModule({\r\n imports: [FieldsetComponent],\r\n exports: [FieldsetComponent],\r\n})\r\nexport class FieldsetModule {}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"seniorsistemas-angular-components-fieldset.mjs","sources":["../../projects/angular-components/fieldset/src/lib/fieldset/fieldset.component.ts","../../projects/angular-components/fieldset/src/lib/fieldset/fieldset.component.html","../../projects/angular-components/fieldset/src/lib/fieldset/fieldset.module.ts","../../projects/angular-components/fieldset/src/seniorsistemas-angular-components-fieldset.ts"],"sourcesContent":["import { animate, AnimationEvent, state, style, transition, trigger } from '@angular/animations';\r\nimport {\r\n AfterViewInit,\r\n Component,\r\n computed,\r\n contentChildren,\r\n EventEmitter,\r\n input,\r\n model,\r\n Output,\r\n signal,\r\n} from '@angular/core';\r\n\r\nimport { NgTemplateOutlet } from '@angular/common';\r\nimport { InteractiveContentDirective } from '@seniorsistemas/angular-components/interactive-content';\r\nimport { TemplateDirective } from '@seniorsistemas/angular-components/template';\r\nimport { FieldSetToggle } from './fieldset.models';\r\n\r\n/**\r\n * @description Componente de fieldset com legenda, ícone configurável e suporte a\r\n * toggle (recolher/expandir) com animação. Suporta templates personalizados via\r\n * diretiva `TemplateDirective`, com os tipos `'header'` (substitui a legenda) e\r\n * `'body'` (conteúdo do fieldset).\r\n *\r\n * Prefira declarar o conteúdo em `sTemplate=\"body\"` em vez de projetá-lo direto:\r\n * o `ng-content` é instanciado pelo componente pai mesmo com o fieldset recolhido,\r\n * disparando o `ngOnInit` dos filhos prematuramente. O template de corpo só é\r\n * instanciado quando o fieldset está de fato ativo, e é obrigatório para que\r\n * `destroyOnHide` funcione.\r\n *\r\n * @example\r\n * ```html\r\n * <s-fieldset legend=\"Dados pessoais\" [toggleable]=\"true\">\r\n * <ng-template sTemplate=\"body\">\r\n * <p>Conteúdo do fieldset</p>\r\n * </ng-template>\r\n * </s-fieldset>\r\n * ```\r\n *\r\n * @category Inputs\r\n */\r\n@Component({\r\n selector: 's-fieldset',\r\n templateUrl: './fieldset.component.html',\r\n styles: [':host { display: block; }'],\r\n animations: [\r\n trigger('toggleAnimation', [\r\n transition(':enter', [\r\n style({ opacity: 0, height: 0 }),\r\n animate('300ms ease-out', style({ opacity: 1, height: '*' })),\r\n ]),\r\n transition(':leave', [\r\n style({ opacity: 1, height: '*' }),\r\n animate('300ms ease-in', style({ opacity: 0, height: 0 })),\r\n ]),\r\n state('open', style({ opacity: 1, height: '*' })),\r\n state('closed', style({ opacity: 0, height: 0 })),\r\n transition('open <=> closed', animate('300ms ease')),\r\n ]),\r\n ],\r\n standalone: true,\r\n imports: [InteractiveContentDirective, NgTemplateOutlet],\r\n})\r\nexport class FieldsetComponent implements AfterViewInit {\r\n /** @description Texto exibido como título do fieldset. @default '' */\r\n legend = input('');\r\n /** @description Habilita o botão de toggle que permite recolher e expandir o conteúdo. @default false */\r\n toggleable = input(false);\r\n /** @description Classe de ícone exibida ao lado da legenda (ex.: `'fas fa-cog'`). @default '' */\r\n icon = input('');\r\n /** @description Quando `true`, remove o conteúdo do DOM ao recolher o fieldset. @default false */\r\n destroyOnHide = input(false);\r\n\r\n templateDirectives = contentChildren(TemplateDirective);\r\n headerTemplate = computed(() => {\r\n return this.templateDirectives()?.find((template) => template.type === 'header')?.template;\r\n });\r\n\r\n bodyTemplate = computed(() => {\r\n return this.templateDirectives()?.find((template) => template.type === 'body')?.template;\r\n });\r\n /** @description Emitido imediatamente antes da animação de toggle iniciar, com o estado atual. */\r\n @Output() beforeToggle = new EventEmitter<FieldSetToggle>();\r\n /** @description Emitido após a animação de toggle concluir, com o novo estado. */\r\n @Output() afterToggle = new EventEmitter<FieldSetToggle>();\r\n\r\n active = model(true);\r\n\r\n /** @description `true` quando a animação de fechamento já terminou, aplicando `overflow-hidden` ao conteúdo. */\r\n protected collapsed = signal(false);\r\n\r\n ngAfterViewInit() {\r\n const hasBodyTemplate = this.bodyTemplate();\r\n if (!hasBodyTemplate && this.destroyOnHide()) {\r\n console.warn(\r\n 'FieldsetComponent: O uso de destroyOnHide=true requer um template de corpo definido com sTemplate=\"body\".',\r\n );\r\n }\r\n }\r\n\r\n protected toggleActive(originalEvent: PointerEvent | KeyboardEvent) {\r\n if (!this.toggleable()) {\r\n return;\r\n }\r\n this.beforeToggle.next({ collapsed: this.active(), originalEvent });\r\n this.active.set(!this.active());\r\n this.afterToggle.next({ collapsed: this.active(), originalEvent });\r\n }\r\n\r\n protected onToggleAnimationDone(event: AnimationEvent) {\r\n this.collapsed.set(event.toState === 'closed');\r\n }\r\n}\r\n","<fieldset\r\n class=\"mb-[10px] min-h-[4.5em] w-full min-w-0 rounded-xbig border border-grayscale-20 bg-grayscale-0 px-2 py-1.5\"\r\n>\r\n <legend\r\n class=\"ml-4 flex select-none items-center gap-1 px-1 font-open-sans text-sm font-bold text-grayscale-90\"\r\n [class.cursor-pointer]=\"toggleable()\"\r\n (sInteractiveContent)=\"toggleActive($event)\"\r\n >\r\n @if (toggleable()) {\r\n @if (active()) {\r\n <i class=\"fas fa-minus\"></i>\r\n } @else {\r\n <i class=\"fas fa-plus\"></i>\r\n }\r\n }\r\n @let _headerTemplate = headerTemplate();\r\n @if (_headerTemplate) {\r\n <ng-container *ngTemplateOutlet=\"_headerTemplate\"></ng-container>\r\n } @else {\r\n @if (icon()) {\r\n <i [class]=\"icon()\"></i>\r\n }\r\n {{ legend() }}\r\n }\r\n </legend>\r\n @if (destroyOnHide() ? (toggleable() ? active() : true) : true) {\r\n <div\r\n [@toggleAnimation]=\"destroyOnHide() ? null : active() ? 'open' : 'closed'\"\r\n (@toggleAnimation.done)=\"onToggleAnimationDone($event)\"\r\n [class.overflow-hidden]=\"collapsed()\"\r\n >\r\n @let _bodyTemplate = bodyTemplate();\r\n @if (_bodyTemplate) {\r\n <ng-container *ngTemplateOutlet=\"_bodyTemplate\"></ng-container>\r\n } @else {\r\n <ng-content></ng-content>\r\n }\r\n </div>\r\n }\r\n</fieldset>\r\n","import { NgModule } from '@angular/core';\r\n\r\nimport { FieldsetComponent } from './fieldset.component';\r\n\r\n@NgModule({\r\n imports: [FieldsetComponent],\r\n exports: [FieldsetComponent],\r\n})\r\nexport class FieldsetModule {}\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/fieldset/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|
|
|
5
|
-
Componente de agrupamento de campos de formulário com legenda, ícone e suporte a toggle (recolher/expandir) com animação. Aceita
|
|
5
|
+
Componente de agrupamento de campos de formulário com legenda, ícone e suporte a toggle (recolher/expandir) com animação. Aceita templates customizados via `sTemplate="header"` (cabeçalho) e `sTemplate="body"` (conteúdo).
|
|
6
6
|
|
|
7
7
|
## Quando usar
|
|
8
8
|
|
|
@@ -19,8 +19,9 @@ Componente de agrupamento de campos de formulário com legenda, ícone e suporte
|
|
|
19
19
|
|
|
20
20
|
```typescript
|
|
21
21
|
import { FieldsetModule } from '@seniorsistemas/angular-components/fieldset';
|
|
22
|
+
import { TemplateDirective } from '@seniorsistemas/angular-components/template';
|
|
22
23
|
|
|
23
|
-
@Component({ standalone: true, imports: [FieldsetModule] })
|
|
24
|
+
@Component({ standalone: true, imports: [FieldsetModule, TemplateDirective] })
|
|
24
25
|
export class MeuComponent {}
|
|
25
26
|
```
|
|
26
27
|
|
|
@@ -28,10 +29,16 @@ export class MeuComponent {}
|
|
|
28
29
|
|
|
29
30
|
```html
|
|
30
31
|
<s-fieldset legend="Dados Pessoais">
|
|
31
|
-
<
|
|
32
|
+
<ng-template sTemplate="body">
|
|
33
|
+
<p>Conteúdo do fieldset</p>
|
|
34
|
+
</ng-template>
|
|
32
35
|
</s-fieldset>
|
|
33
36
|
```
|
|
34
37
|
|
|
38
|
+
> **Declare o conteúdo em `sTemplate="body"`.** Projetar direto via `ng-content` continua funcionando, mas o conteúdo é instanciado pelo componente pai mesmo com o fieldset recolhido — o que dispara o `ngOnInit` dos componentes filhos prematuramente. O template de corpo só é instanciado quando o fieldset está de fato ativo, e é obrigatório para que `destroyOnHide` funcione (sem ele o componente emite um aviso no console).
|
|
39
|
+
>
|
|
40
|
+
> Projetos existentes podem ser convertidos automaticamente com `ng update @seniorsistemas/angular-components` — veja [Migração automática](#migração-automática).
|
|
41
|
+
|
|
35
42
|
## API
|
|
36
43
|
|
|
37
44
|
### Inputs
|
|
@@ -41,7 +48,16 @@ export class MeuComponent {}
|
|
|
41
48
|
| `legend` | `string` | `''` | — | Texto exibido como título do fieldset |
|
|
42
49
|
| `icon` | `string` | `''` | — | Classe de ícone exibida ao lado da legenda (ex: `'fas fa-cog'`) |
|
|
43
50
|
| `toggleable` | `boolean` | `false` | — | Habilita o botão +/- para recolher e expandir com animação |
|
|
44
|
-
| `destroyOnHide` | `boolean` | `
|
|
51
|
+
| `destroyOnHide` | `boolean` | `false` | — | Remove o conteúdo do DOM ao recolher. Requer o conteúdo declarado em `sTemplate="body"` |
|
|
52
|
+
| `active` | `boolean` | `true` | — | Estado aberto/recolhido. Suporta two-way binding via `[(active)]` |
|
|
53
|
+
|
|
54
|
+
### Slots de conteúdo
|
|
55
|
+
|
|
56
|
+
| Slot | Descrição |
|
|
57
|
+
|------|-----------|
|
|
58
|
+
| `sTemplate="header"` | Substitui completamente o cabeçalho padrão (legenda + ícone). O ícone +/- do toggle continua sendo renderizado |
|
|
59
|
+
| `sTemplate="body"` | Conteúdo do fieldset, instanciado somente quando o fieldset está ativo |
|
|
60
|
+
| `ng-content` (default) | Fallback usado apenas quando não há `sTemplate="body"` |
|
|
45
61
|
|
|
46
62
|
### Outputs
|
|
47
63
|
|
|
@@ -65,10 +81,12 @@ type FieldSetToggle = {
|
|
|
65
81
|
|
|
66
82
|
```html
|
|
67
83
|
<s-fieldset legend="Dados Pessoais">
|
|
68
|
-
<
|
|
69
|
-
<
|
|
70
|
-
|
|
71
|
-
|
|
84
|
+
<ng-template sTemplate="body">
|
|
85
|
+
<div style="display: flex; flex-direction: column; gap: 12px;">
|
|
86
|
+
<input type="text" placeholder="Nome completo" />
|
|
87
|
+
<input type="text" placeholder="CPF" />
|
|
88
|
+
</div>
|
|
89
|
+
</ng-template>
|
|
72
90
|
</s-fieldset>
|
|
73
91
|
```
|
|
74
92
|
|
|
@@ -82,16 +100,44 @@ type FieldSetToggle = {
|
|
|
82
100
|
(beforeToggle)="onBeforeToggle($event)"
|
|
83
101
|
(afterToggle)="onAfterToggle($event)"
|
|
84
102
|
>
|
|
85
|
-
<
|
|
86
|
-
|
|
103
|
+
<ng-template sTemplate="body">
|
|
104
|
+
<input type="tel" placeholder="(00) 00000-0000" />
|
|
105
|
+
<input type="email" placeholder="email@empresa.com" />
|
|
106
|
+
</ng-template>
|
|
87
107
|
</s-fieldset>
|
|
88
108
|
```
|
|
89
109
|
|
|
90
110
|
### Colapsável preservando estado do formulário
|
|
91
111
|
|
|
112
|
+
Com `destroyOnHide=false` (padrão) o conteúdo permanece no DOM ao recolher, apenas oculto:
|
|
113
|
+
|
|
92
114
|
```html
|
|
93
|
-
<s-fieldset legend="Dados Opcionais" [toggleable]="true"
|
|
94
|
-
<
|
|
115
|
+
<s-fieldset legend="Dados Opcionais" [toggleable]="true">
|
|
116
|
+
<ng-template sTemplate="body">
|
|
117
|
+
<input formControlName="complemento" placeholder="Complemento" />
|
|
118
|
+
</ng-template>
|
|
119
|
+
</s-fieldset>
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Colapsável destruindo o conteúdo ao recolher
|
|
123
|
+
|
|
124
|
+
Use `destroyOnHide=true` para adiar a construção dos componentes filhos até a primeira abertura e liberá-los ao recolher. **Requer** o conteúdo em `sTemplate="body"`:
|
|
125
|
+
|
|
126
|
+
```html
|
|
127
|
+
<s-fieldset legend="Relatório Pesado" [toggleable]="true" [destroyOnHide]="true" [active]="false">
|
|
128
|
+
<ng-template sTemplate="body">
|
|
129
|
+
<app-relatorio-pesado></app-relatorio-pesado>
|
|
130
|
+
</ng-template>
|
|
131
|
+
</s-fieldset>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Controlando o estado por código
|
|
135
|
+
|
|
136
|
+
```html
|
|
137
|
+
<s-fieldset legend="Filtros" [toggleable]="true" [(active)]="filtrosAbertos">
|
|
138
|
+
<ng-template sTemplate="body">
|
|
139
|
+
<input formControlName="busca" />
|
|
140
|
+
</ng-template>
|
|
95
141
|
</s-fieldset>
|
|
96
142
|
```
|
|
97
143
|
|
|
@@ -106,7 +152,9 @@ type FieldSetToggle = {
|
|
|
106
152
|
<span style="font-size: 0.75rem; color: #dc2626;">(campos obrigatórios)</span>
|
|
107
153
|
</div>
|
|
108
154
|
</ng-template>
|
|
109
|
-
<
|
|
155
|
+
<ng-template sTemplate="body">
|
|
156
|
+
<input type="text" placeholder="Banco" />
|
|
157
|
+
</ng-template>
|
|
110
158
|
</s-fieldset>
|
|
111
159
|
```
|
|
112
160
|
|
|
@@ -115,14 +163,28 @@ type FieldSetToggle = {
|
|
|
115
163
|
```html
|
|
116
164
|
<div style="display: flex; flex-direction: column; gap: 16px;">
|
|
117
165
|
<s-fieldset legend="Dados Pessoais" icon="fas fa-user" [toggleable]="true">
|
|
118
|
-
|
|
166
|
+
<ng-template sTemplate="body">
|
|
167
|
+
<!-- campos -->
|
|
168
|
+
</ng-template>
|
|
119
169
|
</s-fieldset>
|
|
120
170
|
<s-fieldset legend="Endereço" icon="fas fa-map-marker-alt" [toggleable]="true">
|
|
121
|
-
|
|
171
|
+
<ng-template sTemplate="body">
|
|
172
|
+
<!-- campos -->
|
|
173
|
+
</ng-template>
|
|
122
174
|
</s-fieldset>
|
|
123
175
|
</div>
|
|
124
176
|
```
|
|
125
177
|
|
|
178
|
+
## Migração automática
|
|
179
|
+
|
|
180
|
+
Projetos que declaram o conteúdo via `ng-content` podem ser convertidos para `sTemplate="body"` automaticamente:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
ng update @seniorsistemas/angular-components
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
A migration `migration-v19-9-3-fieldset-body-template` envolve o conteúdo projetado de cada `<s-fieldset>` em `<ng-template sTemplate="body">`, mantém os templates de slot já existentes (como `sTemplate="header"`) fora do corpo e adiciona o `TemplateDirective` ao escopo do componente quando ele ainda não estiver disponível — inclusive verificando se já vem reexportado por um `SharedModule`. Fieldsets que já usam `sTemplate="body"` são ignorados, então a execução é idempotente.
|
|
187
|
+
|
|
126
188
|
## Acessibilidade
|
|
127
189
|
|
|
128
190
|
- O botão de toggle é ativável por teclado (Enter e Space)
|
|
@@ -1,16 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { AnimationEvent } from '@angular/animations';
|
|
2
|
+
import { AfterViewInit, EventEmitter } from '@angular/core';
|
|
2
3
|
import { TemplateDirective } from '@seniorsistemas/angular-components/template';
|
|
3
4
|
import { FieldSetToggle } from './fieldset.models';
|
|
4
5
|
import * as i0 from "@angular/core";
|
|
5
6
|
/**
|
|
6
7
|
* @description Componente de fieldset com legenda, ícone configurável e suporte a
|
|
7
|
-
* toggle (recolher/expandir) com animação. Suporta
|
|
8
|
-
*
|
|
8
|
+
* toggle (recolher/expandir) com animação. Suporta templates personalizados via
|
|
9
|
+
* diretiva `TemplateDirective`, com os tipos `'header'` (substitui a legenda) e
|
|
10
|
+
* `'body'` (conteúdo do fieldset).
|
|
11
|
+
*
|
|
12
|
+
* Prefira declarar o conteúdo em `sTemplate="body"` em vez de projetá-lo direto:
|
|
13
|
+
* o `ng-content` é instanciado pelo componente pai mesmo com o fieldset recolhido,
|
|
14
|
+
* disparando o `ngOnInit` dos filhos prematuramente. O template de corpo só é
|
|
15
|
+
* instanciado quando o fieldset está de fato ativo, e é obrigatório para que
|
|
16
|
+
* `destroyOnHide` funcione.
|
|
9
17
|
*
|
|
10
18
|
* @example
|
|
11
19
|
* ```html
|
|
12
20
|
* <s-fieldset legend="Dados pessoais" [toggleable]="true">
|
|
13
|
-
* <
|
|
21
|
+
* <ng-template sTemplate="body">
|
|
22
|
+
* <p>Conteúdo do fieldset</p>
|
|
23
|
+
* </ng-template>
|
|
14
24
|
* </s-fieldset>
|
|
15
25
|
* ```
|
|
16
26
|
*
|
|
@@ -18,22 +28,26 @@ import * as i0 from "@angular/core";
|
|
|
18
28
|
*/
|
|
19
29
|
export declare class FieldsetComponent implements AfterViewInit {
|
|
20
30
|
/** @description Texto exibido como título do fieldset. @default '' */
|
|
21
|
-
legend: string
|
|
31
|
+
legend: import("@angular/core").InputSignal<string>;
|
|
22
32
|
/** @description Habilita o botão de toggle que permite recolher e expandir o conteúdo. @default false */
|
|
23
|
-
toggleable: boolean
|
|
33
|
+
toggleable: import("@angular/core").InputSignal<boolean>;
|
|
24
34
|
/** @description Classe de ícone exibida ao lado da legenda (ex.: `'fas fa-cog'`). @default '' */
|
|
25
|
-
icon: string
|
|
26
|
-
/** @description Quando `true`, remove o conteúdo do DOM ao recolher o fieldset. @default
|
|
27
|
-
destroyOnHide: boolean
|
|
28
|
-
|
|
29
|
-
headerTemplate: TemplateRef<any> | undefined
|
|
35
|
+
icon: import("@angular/core").InputSignal<string>;
|
|
36
|
+
/** @description Quando `true`, remove o conteúdo do DOM ao recolher o fieldset. @default false */
|
|
37
|
+
destroyOnHide: import("@angular/core").InputSignal<boolean>;
|
|
38
|
+
templateDirectives: import("@angular/core").Signal<readonly TemplateDirective[]>;
|
|
39
|
+
headerTemplate: import("@angular/core").Signal<import("@angular/core").TemplateRef<any> | undefined>;
|
|
40
|
+
bodyTemplate: import("@angular/core").Signal<import("@angular/core").TemplateRef<any> | undefined>;
|
|
30
41
|
/** @description Emitido imediatamente antes da animação de toggle iniciar, com o estado atual. */
|
|
31
42
|
beforeToggle: EventEmitter<FieldSetToggle>;
|
|
32
43
|
/** @description Emitido após a animação de toggle concluir, com o novo estado. */
|
|
33
44
|
afterToggle: EventEmitter<FieldSetToggle>;
|
|
34
|
-
active: boolean
|
|
45
|
+
active: import("@angular/core").ModelSignal<boolean>;
|
|
46
|
+
/** @description `true` quando a animação de fechamento já terminou, aplicando `overflow-hidden` ao conteúdo. */
|
|
47
|
+
protected collapsed: import("@angular/core").WritableSignal<boolean>;
|
|
35
48
|
ngAfterViewInit(): void;
|
|
36
|
-
toggleActive(originalEvent: PointerEvent | KeyboardEvent): void;
|
|
49
|
+
protected toggleActive(originalEvent: PointerEvent | KeyboardEvent): void;
|
|
50
|
+
protected onToggleAnimationDone(event: AnimationEvent): void;
|
|
37
51
|
static ɵfac: i0.ɵɵFactoryDeclaration<FieldsetComponent, never>;
|
|
38
|
-
static ɵcmp: i0.ɵɵComponentDeclaration<FieldsetComponent, "s-fieldset", never, { "legend": { "alias": "legend"; "required": false; }; "toggleable": { "alias": "toggleable"; "required": false; }; "icon": { "alias": "icon"; "required": false; }; "destroyOnHide": { "alias": "destroyOnHide"; "required": false; }; }, { "beforeToggle": "beforeToggle"; "afterToggle": "afterToggle"; }, ["templateDirectives"], ["*"], true, never>;
|
|
52
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<FieldsetComponent, "s-fieldset", never, { "legend": { "alias": "legend"; "required": false; "isSignal": true; }; "toggleable": { "alias": "toggleable"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "destroyOnHide": { "alias": "destroyOnHide"; "required": false; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; }, { "beforeToggle": "beforeToggle"; "afterToggle": "afterToggle"; "active": "activeChange"; }, ["templateDirectives"], ["*"], true, never>;
|
|
39
53
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@seniorsistemas/angular-components",
|
|
3
|
-
"version": "19.9.
|
|
3
|
+
"version": "19.9.3",
|
|
4
4
|
"peerDependencies": {
|
|
5
5
|
"@angular/cdk": "^18.2.14",
|
|
6
6
|
"@angular/common": "^18.2.0",
|
|
@@ -97,36 +97,36 @@
|
|
|
97
97
|
"esm": "./esm2022/accordion/seniorsistemas-angular-components-accordion.mjs",
|
|
98
98
|
"default": "./fesm2022/seniorsistemas-angular-components-accordion.mjs"
|
|
99
99
|
},
|
|
100
|
-
"./alert": {
|
|
101
|
-
"types": "./alert/index.d.ts",
|
|
102
|
-
"esm2022": "./esm2022/alert/seniorsistemas-angular-components-alert.mjs",
|
|
103
|
-
"esm": "./esm2022/alert/seniorsistemas-angular-components-alert.mjs",
|
|
104
|
-
"default": "./fesm2022/seniorsistemas-angular-components-alert.mjs"
|
|
105
|
-
},
|
|
106
100
|
"./autocomplete": {
|
|
107
101
|
"types": "./autocomplete/index.d.ts",
|
|
108
102
|
"esm2022": "./esm2022/autocomplete/seniorsistemas-angular-components-autocomplete.mjs",
|
|
109
103
|
"esm": "./esm2022/autocomplete/seniorsistemas-angular-components-autocomplete.mjs",
|
|
110
104
|
"default": "./fesm2022/seniorsistemas-angular-components-autocomplete.mjs"
|
|
111
105
|
},
|
|
106
|
+
"./alert": {
|
|
107
|
+
"types": "./alert/index.d.ts",
|
|
108
|
+
"esm2022": "./esm2022/alert/seniorsistemas-angular-components-alert.mjs",
|
|
109
|
+
"esm": "./esm2022/alert/seniorsistemas-angular-components-alert.mjs",
|
|
110
|
+
"default": "./fesm2022/seniorsistemas-angular-components-alert.mjs"
|
|
111
|
+
},
|
|
112
112
|
"./badge": {
|
|
113
113
|
"types": "./badge/index.d.ts",
|
|
114
114
|
"esm2022": "./esm2022/badge/seniorsistemas-angular-components-badge.mjs",
|
|
115
115
|
"esm": "./esm2022/badge/seniorsistemas-angular-components-badge.mjs",
|
|
116
116
|
"default": "./fesm2022/seniorsistemas-angular-components-badge.mjs"
|
|
117
117
|
},
|
|
118
|
-
"./breadcrumb": {
|
|
119
|
-
"types": "./breadcrumb/index.d.ts",
|
|
120
|
-
"esm2022": "./esm2022/breadcrumb/seniorsistemas-angular-components-breadcrumb.mjs",
|
|
121
|
-
"esm": "./esm2022/breadcrumb/seniorsistemas-angular-components-breadcrumb.mjs",
|
|
122
|
-
"default": "./fesm2022/seniorsistemas-angular-components-breadcrumb.mjs"
|
|
123
|
-
},
|
|
124
118
|
"./bignumber-input": {
|
|
125
119
|
"types": "./bignumber-input/index.d.ts",
|
|
126
120
|
"esm2022": "./esm2022/bignumber-input/seniorsistemas-angular-components-bignumber-input.mjs",
|
|
127
121
|
"esm": "./esm2022/bignumber-input/seniorsistemas-angular-components-bignumber-input.mjs",
|
|
128
122
|
"default": "./fesm2022/seniorsistemas-angular-components-bignumber-input.mjs"
|
|
129
123
|
},
|
|
124
|
+
"./breadcrumb": {
|
|
125
|
+
"types": "./breadcrumb/index.d.ts",
|
|
126
|
+
"esm2022": "./esm2022/breadcrumb/seniorsistemas-angular-components-breadcrumb.mjs",
|
|
127
|
+
"esm": "./esm2022/breadcrumb/seniorsistemas-angular-components-breadcrumb.mjs",
|
|
128
|
+
"default": "./fesm2022/seniorsistemas-angular-components-breadcrumb.mjs"
|
|
129
|
+
},
|
|
130
130
|
"./button": {
|
|
131
131
|
"types": "./button/index.d.ts",
|
|
132
132
|
"esm2022": "./esm2022/button/seniorsistemas-angular-components-button.mjs",
|
|
@@ -1,4 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "../../node_modules/@angular-devkit/schematics/collection-schema.json",
|
|
3
|
-
"schematics": {
|
|
3
|
+
"schematics": {
|
|
4
|
+
"migrate-dynamic-form": {
|
|
5
|
+
"description": "Migra DynamicForm v18→v19: construtores → plain objects, enums → string literals, [fields] → [configs], Editor → provideEditorField(), estilos → angular.json",
|
|
6
|
+
"factory": "./migrations/migrate-dynamic-form-v19/index#migrate"
|
|
7
|
+
},
|
|
8
|
+
"migrate-steps-to-stepper": {
|
|
9
|
+
"description": "Migra Steps para Stepper: <s-steps> → <s-stepper>, import '@seniorsistemas/angular-components/steps' → 'stepper', StepsComponent → StepperComponent, e remove StepsModule (standalone) substituindo por StepperComponent",
|
|
10
|
+
"factory": "./migrations/migrate-steps-to-stepper/index#migrate"
|
|
11
|
+
},
|
|
12
|
+
"migrate-s-chips-outputs": {
|
|
13
|
+
"description": "Renomeia outputs legados do s-chips: onAdd→added, onRemove→removed, onChipClick→chipClicked, onFocus→focused, onBlur→focusLost",
|
|
14
|
+
"factory": "./migrations/migrate-s-chips-outputs/index#migrate"
|
|
15
|
+
},
|
|
16
|
+
"migrate-p-datepicker": {
|
|
17
|
+
"description": "Migra p-datepicker (PrimeNG) para s-datepicker (@seniorsistemas/angular-components)",
|
|
18
|
+
"factory": "./migrations/migrate-p-datepicker/index#migrate"
|
|
19
|
+
},
|
|
20
|
+
"migrate-p-editor": {
|
|
21
|
+
"description": "Migra p-editor (PrimeNG/Quill) para s-editor (@seniorsistemas/angular-components): renomeia tag e eventos, remove atributos sem equivalente, limpa configs legadas do DynamicForm e atualiza dependências do package.json",
|
|
22
|
+
"factory": "./migrations/migrate-p-editor/index#migrate"
|
|
23
|
+
},
|
|
24
|
+
"migrate-fieldset-body-template": {
|
|
25
|
+
"description": "Envolve o conteúdo projetado do s-fieldset em <ng-template sTemplate=\"body\">, mantendo templates de slot como sTemplate=\"header\" fora do body, e garante o TemplateDirective no escopo do componente (standalone ou via NgModule)",
|
|
26
|
+
"factory": "./migrations/migrate-fieldset-body-template/index#migrate"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
4
29
|
}
|
|
@@ -495,29 +495,29 @@ function migrateImports(content, originalImportedNames, editorDetected, filePath
|
|
|
495
495
|
const currentlyImported = getImportedNamesFromSenior(updated);
|
|
496
496
|
// Ensure every XxxFieldConfig interface introduced by isolated casts in Pass 2 is imported
|
|
497
497
|
for (const typeName of castTypesUsed) {
|
|
498
|
-
if (!isSymbolImportedFrom(updated, typeName, DYNAMIC_FORM_MODULE)) {
|
|
499
|
-
updated = ensureNamedImport(updated, typeName, DYNAMIC_FORM_MODULE);
|
|
498
|
+
if (!(0, ast_utils_1.isSymbolImportedFrom)(updated, typeName, DYNAMIC_FORM_MODULE)) {
|
|
499
|
+
updated = (0, ast_utils_1.ensureNamedImport)(updated, typeName, DYNAMIC_FORM_MODULE);
|
|
500
500
|
}
|
|
501
501
|
}
|
|
502
502
|
// Add v19 types as needed
|
|
503
503
|
if (dynamicTypeUsedAsAnnotation &&
|
|
504
504
|
!currentlyImported.has('DynamicType') &&
|
|
505
|
-
!isSymbolImportedFrom(updated, 'DynamicType', DYNAMIC_FORM_MODULE)) {
|
|
506
|
-
updated = ensureNamedImport(updated, 'DynamicType', DYNAMIC_FORM_MODULE);
|
|
505
|
+
!(0, ast_utils_1.isSymbolImportedFrom)(updated, 'DynamicType', DYNAMIC_FORM_MODULE)) {
|
|
506
|
+
updated = (0, ast_utils_1.ensureNamedImport)(updated, 'DynamicType', DYNAMIC_FORM_MODULE);
|
|
507
507
|
}
|
|
508
508
|
if (updated.includes('ExpandedDynamicType') &&
|
|
509
|
-
!isSymbolImportedFrom(updated, 'ExpandedDynamicType', DYNAMIC_FORM_MODULE)) {
|
|
510
|
-
updated = ensureNamedImport(updated, 'ExpandedDynamicType', DYNAMIC_FORM_MODULE);
|
|
509
|
+
!(0, ast_utils_1.isSymbolImportedFrom)(updated, 'ExpandedDynamicType', DYNAMIC_FORM_MODULE)) {
|
|
510
|
+
updated = (0, ast_utils_1.ensureNamedImport)(updated, 'ExpandedDynamicType', DYNAMIC_FORM_MODULE);
|
|
511
511
|
}
|
|
512
512
|
if (updated.includes('DynamicStructure') &&
|
|
513
|
-
!isSymbolImportedFrom(updated, 'DynamicStructure', DYNAMIC_FORM_MODULE)) {
|
|
514
|
-
updated = ensureNamedImport(updated, 'DynamicStructure', DYNAMIC_FORM_MODULE);
|
|
513
|
+
!(0, ast_utils_1.isSymbolImportedFrom)(updated, 'DynamicStructure', DYNAMIC_FORM_MODULE)) {
|
|
514
|
+
updated = (0, ast_utils_1.ensureNamedImport)(updated, 'DynamicStructure', DYNAMIC_FORM_MODULE);
|
|
515
515
|
}
|
|
516
516
|
if (updated.includes('dynamicTypeToDynamicStructure') &&
|
|
517
|
-
!isSymbolImportedFrom(updated, 'dynamicTypeToDynamicStructure', DYNAMIC_FORM_MODULE)) {
|
|
518
|
-
updated = ensureNamedImport(updated, 'dynamicTypeToDynamicStructure', DYNAMIC_FORM_MODULE);
|
|
517
|
+
!(0, ast_utils_1.isSymbolImportedFrom)(updated, 'dynamicTypeToDynamicStructure', DYNAMIC_FORM_MODULE)) {
|
|
518
|
+
updated = (0, ast_utils_1.ensureNamedImport)(updated, 'dynamicTypeToDynamicStructure', DYNAMIC_FORM_MODULE);
|
|
519
519
|
}
|
|
520
|
-
if (editorDetected && !isSymbolImportedFrom(updated, 'provideEditorField', EDITOR_MODULE)) {
|
|
520
|
+
if (editorDetected && !(0, ast_utils_1.isSymbolImportedFrom)(updated, 'provideEditorField', EDITOR_MODULE)) {
|
|
521
521
|
updated = (0, ast_utils_1.insertImportStatement)(updated, 'provideEditorField', EDITOR_MODULE);
|
|
522
522
|
}
|
|
523
523
|
return updated;
|
|
@@ -595,7 +595,7 @@ function addProviderToApp(tree, providerCall, importFrom, context) {
|
|
|
595
595
|
const updated = injectProvider(content, providerCall);
|
|
596
596
|
if (updated !== content) {
|
|
597
597
|
const symbolName = providerCall.replace(/\(.*\)$/, '');
|
|
598
|
-
const withImport = ensureNamedImport(updated, symbolName, importFrom);
|
|
598
|
+
const withImport = (0, ast_utils_1.ensureNamedImport)(updated, symbolName, importFrom);
|
|
599
599
|
tree.overwrite(candidate, withImport);
|
|
600
600
|
context.logger.info(`${candidate}: adicionado ${providerCall} aos providers.`);
|
|
601
601
|
return;
|
|
@@ -666,29 +666,10 @@ function isUsedAsType(content, symbolName) {
|
|
|
666
666
|
const regex = new RegExp(`\\b${symbolName}\\b(?!\\s*\\.)`);
|
|
667
667
|
return regex.test(withoutImports);
|
|
668
668
|
}
|
|
669
|
-
function isSymbolImportedFrom(content, symbolName, fromModule) {
|
|
670
|
-
const escapedModule = fromModule.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
671
|
-
const regex = new RegExp(`import\\s*\\{[^}]*\\b${symbolName}\\b[^}]*\\}\\s*from\\s*['"]${escapedModule}['"]`);
|
|
672
|
-
return regex.test(content);
|
|
673
|
-
}
|
|
674
|
-
function ensureNamedImport(content, symbolName, fromModule) {
|
|
675
|
-
if (isSymbolImportedFrom(content, symbolName, fromModule))
|
|
676
|
-
return content;
|
|
677
|
-
// Try to append to an existing import from the same module
|
|
678
|
-
const escapedModule = fromModule.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
679
|
-
const existingImportRegex = new RegExp(`(import\\s*\\{)([^}]*)(\\}\\s*from\\s*['"]${escapedModule}['"])`);
|
|
680
|
-
const match = existingImportRegex.exec(content);
|
|
681
|
-
if (match) {
|
|
682
|
-
const newNames = match[2].trimEnd();
|
|
683
|
-
const separator = newNames.endsWith(',') ? ' ' : ', ';
|
|
684
|
-
return content.replace(match[0], `${match[1]}${newNames}${separator}${symbolName}${match[3]}`);
|
|
685
|
-
}
|
|
686
|
-
return (0, ast_utils_1.insertImportStatement)(content, symbolName, fromModule);
|
|
687
|
-
}
|
|
688
669
|
function ensureMultipleImports(content, symbols, fromModule) {
|
|
689
670
|
let result = content;
|
|
690
671
|
for (const sym of symbols) {
|
|
691
|
-
result = ensureNamedImport(result, sym, fromModule);
|
|
672
|
+
result = (0, ast_utils_1.ensureNamedImport)(result, sym, fromModule);
|
|
692
673
|
}
|
|
693
674
|
return result;
|
|
694
675
|
}
|
|
@@ -734,7 +715,7 @@ function migrateBaseFieldInputs(content, filePath, context) {
|
|
|
734
715
|
return content;
|
|
735
716
|
let updated = (0, ast_utils_1.applyChanges)(content, changes);
|
|
736
717
|
// Add input() to @angular/core imports
|
|
737
|
-
updated = ensureNamedImport(updated, 'input', '@angular/core');
|
|
718
|
+
updated = (0, ast_utils_1.ensureNamedImport)(updated, 'input', '@angular/core');
|
|
738
719
|
// Remove Input from @angular/core if no longer used as decorator
|
|
739
720
|
if (!/@Input\b/.test(updated)) {
|
|
740
721
|
const updatedSf = ts.createSourceFile('_temp.ts', updated, ts.ScriptTarget.Latest, true);
|
|
@@ -913,7 +894,7 @@ function migrateFieldDefinitions(content, filePath, context) {
|
|
|
913
894
|
visit(sourceFile);
|
|
914
895
|
let updated = changes.length > 0 ? (0, ast_utils_1.applyChanges)(content, changes) : content;
|
|
915
896
|
if (needsDynamicTypeImport) {
|
|
916
|
-
updated = ensureNamedImport(updated, 'DynamicType', DYNAMIC_FORM_MODULE);
|
|
897
|
+
updated = (0, ast_utils_1.ensureNamedImport)(updated, 'DynamicType', DYNAMIC_FORM_MODULE);
|
|
917
898
|
}
|
|
918
899
|
return updated;
|
|
919
900
|
}
|