From 3b640dab1bb2e20029b35f07daf7d9d4c247908d Mon Sep 17 00:00:00 2001 From: Josep Piguillem Date: Mon, 27 Jul 2026 23:33:29 +0200 Subject: [PATCH] refactor: convierte los inputs con accessor a signals (input/model + effect) --- .../accordion/accordion.component.html | 22 +-- .../accordion/accordion.component.ts | 24 ++-- .../directives/accordion-content.directive.ts | 3 +- .../ui/banner/banner/banner.component.html | 6 +- .../lib/ui/banner/banner/banner.component.ts | 7 +- .../action-button.component.html | 2 +- .../action-button/action-button.component.ts | 6 +- .../icon-button/icon-button.component.html | 4 +- .../icon-button/icon-button.component.ts | 5 +- .../switch-button.component.html | 4 +- .../switch-button/switch-button.component.ts | 5 +- .../toggle-button.component.html | 6 +- .../toggle-button/toggle-button.component.ts | 6 +- .../ui/src/lib/ui/card/base-card.component.ts | 6 +- .../src/lib/ui/card/card/card.component.html | 4 +- .../carousel/carousel/carousel.component.html | 2 +- .../carousel/carousel/carousel.component.ts | 33 +++-- .../ui/src/lib/ui/chip/base-chip.component.ts | 10 +- .../chip-action/chip-action.component.html | 10 +- .../chip-status/chip-status.component.html | 4 +- .../chip/chip-status/chip-status.component.ts | 2 +- .../chip/chip-value/chip-value.component.html | 6 +- .../chip/chip-value/chip-value.component.ts | 8 +- .../src/lib/ui/chip/chip/chip.component.html | 12 +- .../ui/src/lib/ui/chip/chip/chip.component.ts | 2 +- .../src/lib/ui/chip/tile/tile.component.html | 6 +- .../ui/src/lib/ui/chip/tile/tile.component.ts | 10 +- .../data-grid/data-grid.component.html | 8 +- .../data-grid/data-grid.component.ts | 4 +- .../ui/divider/divider/divider.component.html | 2 +- .../ui/divider/divider/divider.component.ts | 6 +- .../form-field/base-form-field.component.ts | 38 +++--- .../incremental-number-field.component.ts | 10 +- .../password-field.component.html | 2 +- .../search-field/search-field.component.html | 2 +- .../search-field/search-field.component.ts | 85 ++++++------ .../textarea/textarea.component.html | 4 +- .../form-field/textarea/textarea.component.ts | 4 +- .../ui/image/headline/headline.component.html | 2 +- .../ui/image/headline/headline.component.ts | 3 +- .../src/lib/ui/note/note/note.component.html | 4 +- .../ui/src/lib/ui/note/note/note.component.ts | 5 +- .../snack-bar/snack-bar.component.html | 6 +- .../snack-bar/snack-bar.component.ts | 21 +-- .../paginator/paginator.component.html | 12 +- .../paginator/paginator.component.ts | 22 +-- .../expansion-panel.component.html | 10 +- .../expansion-panel.component.ts | 9 +- .../lib/ui/panel/panel/panel.component.html | 6 +- .../src/lib/ui/panel/panel/panel.component.ts | 6 +- .../lib/ui/select/base-select.component.ts | 18 +-- .../select-carousel.component.ts | 15 ++- .../select-multiple.component.html | 10 +- .../select-multiple.component.ts | 126 +++++++++--------- .../select-option.component.html | 28 ++-- .../select-option/select-option.component.ts | 97 ++++++-------- .../select-period.component.html | 6 +- .../select-period/select-period.component.ts | 15 ++- .../ui/select/select/select.component.html | 6 +- .../lib/ui/select/select/select.component.ts | 15 ++- .../lib/ui/slider/base-slider.component.ts | 3 +- .../ui/slider/slider/slider.component.html | 4 +- libs/ui/src/lib/ui/tab/tab/tab.component.html | 6 +- libs/ui/src/lib/ui/tab/tab/tab.component.ts | 101 +++++++------- .../lib/ui/text/base-text-viewer.component.ts | 14 +- .../text/directives/text-styles.directive.ts | 6 +- .../text-viewer-link.component.html | 10 +- .../text-viewer/text-viewer.component.html | 8 +- .../src/lib/ui/wrapper/form/form.wrapper.html | 2 +- .../src/lib/ui/wrapper/form/form.wrapper.ts | 4 +- .../lib/ui/wrapper/header/header.wrapper.html | 8 +- .../lib/ui/wrapper/header/header.wrapper.ts | 4 +- .../src/lib/util/menu/base.menu.component.ts | 13 +- .../status/directives/status.directive.ts | 16 +-- 74 files changed, 496 insertions(+), 525 deletions(-) diff --git a/libs/ui/src/lib/ui/accordion/accordion/accordion.component.html b/libs/ui/src/lib/ui/accordion/accordion/accordion.component.html index 61133e6..3d3a92f 100644 --- a/libs/ui/src/lib/ui/accordion/accordion/accordion.component.html +++ b/libs/ui/src/lib/ui/accordion/accordion/accordion.component.html @@ -3,9 +3,9 @@
@@ -55,7 +55,7 @@
diff --git a/libs/ui/src/lib/ui/panel/expansion-panel/expansion-panel.component.ts b/libs/ui/src/lib/ui/panel/expansion-panel/expansion-panel.component.ts index da52e72..066ec19 100644 --- a/libs/ui/src/lib/ui/panel/expansion-panel/expansion-panel.component.ts +++ b/libs/ui/src/lib/ui/panel/expansion-panel/expansion-panel.component.ts @@ -2,7 +2,6 @@ import { ChangeDetectionStrategy, Component, HostBinding, - Input, OnChanges, OnInit, SimpleChanges, @@ -60,9 +59,9 @@ export class PiExpansionPanelComponent extends PiBasePanelComponent implements O } readonly type = input<'form' | 'custom'>('form'); - @Input() isCollapsed = false; + readonly isCollapsed = input(false); readonly title = input(); - @Input() subtitle: string | undefined; + readonly subtitle = input(); readonly formGroup = input.required(); readonly edit = output(); @@ -82,12 +81,12 @@ export class PiExpansionPanelComponent extends PiBasePanelComponent implements O toggle(): void { const status = this.status(); - if (status !== 'disabled' && status !== 'info') this.isCollapsed = !this.isCollapsed; + if (status !== 'disabled' && status !== 'info') this.isCollapsed = !this.isCollapsed(); } onEdit(): void { if (this.status() !== 'disabled') { - if (this.isCollapsed) this.toggle(); + if (this.isCollapsed()) this.toggle(); this.edit.emit(); } } diff --git a/libs/ui/src/lib/ui/panel/panel/panel.component.html b/libs/ui/src/lib/ui/panel/panel/panel.component.html index 938ecc5..9e46555 100644 --- a/libs/ui/src/lib/ui/panel/panel/panel.component.html +++ b/libs/ui/src/lib/ui/panel/panel/panel.component.html @@ -1,9 +1,9 @@
-
+
-

{{ title }}

-

{{ subtitle }}

+

{{ title() }}

+

{{ subtitle() }}

diff --git a/libs/ui/src/lib/ui/panel/panel/panel.component.ts b/libs/ui/src/lib/ui/panel/panel/panel.component.ts index 005ebe8..92d76ae 100644 --- a/libs/ui/src/lib/ui/panel/panel/panel.component.ts +++ b/libs/ui/src/lib/ui/panel/panel/panel.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, input } from '@angular/core'; import { ComponentFamilyClass } from '../../../styles'; import { PiBasePanelComponent } from '../base-panel.component'; import { NgIf } from '@angular/common'; @@ -48,6 +48,6 @@ export class PiPanelComponent extends PiBasePanelComponent { ].join(' '); } - @Input() title: string | undefined; - @Input() subtitle: string | undefined; + readonly title = input(); + readonly subtitle = input(); } diff --git a/libs/ui/src/lib/ui/select/base-select.component.ts b/libs/ui/src/lib/ui/select/base-select.component.ts index 227faff..67c13f8 100644 --- a/libs/ui/src/lib/ui/select/base-select.component.ts +++ b/libs/ui/src/lib/ui/select/base-select.component.ts @@ -1,18 +1,14 @@ import { Component, - ContentChildren, ElementRef, HostBinding, Injector, - Input, - QueryList, Renderer2, TemplateRef, - ViewChildren, input, contentChild, contentChildren, - viewChildren + viewChildren, ChangeDetectionStrategy } from '@angular/core'; import { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; import { PiFormControl } from '../../util/form-control'; @@ -24,7 +20,8 @@ import { actionIcon, } from './entities/select.model'; -@Component({ +@Component({ + changeDetection: ChangeDetectionStrategy.OnPush, template: '', providers: [ { @@ -105,7 +102,7 @@ export class PiBaseSelectComponent extends PiFormControl { /** * An optional icon for the select. */ - @Input() icon: string | undefined; + readonly icon = input(); /** * An optional icon for the select. @@ -160,9 +157,7 @@ export class PiBaseSelectComponent extends PiFormControl { * A query list of all the child option components that are content projected from an external consumer. * These components are typically passed into this component's content slot from outside. */ - @ContentChildren(PiSelectOptionComponent) childrenOptionsComponents: - | QueryList - | undefined; + readonly childrenOptionsComponents = contentChildren(PiSelectOptionComponent); /** * A query list of native element references for the child option components @@ -176,8 +171,7 @@ export class PiBaseSelectComponent extends PiFormControl { * within this component's template. * These are the default option components provided by the component itself. */ - @ViewChildren(PiSelectOptionComponent) - viewChildOptionsComponents: QueryList; + readonly viewChildOptionsComponents = viewChildren(PiSelectOptionComponent); /** * A query list of native element references for the child option components diff --git a/libs/ui/src/lib/ui/select/select-carousel/select-carousel.component.ts b/libs/ui/src/lib/ui/select/select-carousel/select-carousel.component.ts index 1bbdbf8..1619735 100644 --- a/libs/ui/src/lib/ui/select/select-carousel/select-carousel.component.ts +++ b/libs/ui/src/lib/ui/select/select-carousel/select-carousel.component.ts @@ -145,8 +145,8 @@ export class PiSelectCarouselComponent extends PiBaseSelectComponent implements ngAfterContentInit(): void { this.control.setValue(this.selectedOption); merge( - this.childrenOptionsComponents?.changes || [], - this.viewChildOptionsComponents?.changes || [], + this.childrenOptionsComponents()?.changes || [], + this.viewChildOptionsComponents()?.changes || [], this.control.valueChanges ).subscribe(() => { Promise.resolve().then(() => { @@ -185,12 +185,13 @@ export class PiSelectCarouselComponent extends PiBaseSelectComponent implements let childrenOptionsElementRef: QueryList; // Check if we have child options that are content projected from a parent component. - if (this.childrenOptionsComponents && this.childrenOptionsComponents.length > 0) { - childrenOptionsComponent = this.childrenOptionsComponents.toArray(); + const childrenOptionsComponents = this.childrenOptionsComponents(); + if (childrenOptionsComponents && childrenOptionsComponents.length > 0) { + childrenOptionsComponent = childrenOptionsComponents; childrenOptionsElementRef = this.childrenOptionsEl(); } else { // If not, it means they're internally defined (default), so we fetch from viewChildren. - childrenOptionsComponent = this.viewChildOptionsComponents?.toArray(); + childrenOptionsComponent = this.viewChildOptionsComponents; childrenOptionsElementRef = this.viewChildOptionsEl(); } @@ -202,14 +203,14 @@ export class PiSelectCarouselComponent extends PiBaseSelectComponent implements // If the current child option matches the selected option, highlight it. if (isSelected) { this._renderer.addClass(childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected'); - childOption.isSelected = true; + childOption.isSelected.set(true); } else { // Otherwise, ensure it is not highlighted. this._renderer.removeClass( childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected' ); - childOption.isSelected = false; + childOption.isSelected.set(false); } }); this._cdr.markForCheck(); diff --git a/libs/ui/src/lib/ui/select/select-multiple/select-multiple.component.html b/libs/ui/src/lib/ui/select/select-multiple/select-multiple.component.html index 937391b..22aa5d1 100644 --- a/libs/ui/src/lib/ui/select/select-multiple/select-multiple.component.html +++ b/libs/ui/src/lib/ui/select/select-multiple/select-multiple.component.html @@ -5,8 +5,8 @@
{ + // Sólo tenía getter, así que el @Input() original nunca pudo asignarse desde + // fuera: es un valor derivado, no una entrada. + get selectedOptions(): Array { if (!this.controlValue) return []; return this.options().filter((option) => _.some(this.controlValue, option)); } @@ -162,8 +164,8 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements ngAfterContentInit(): void { this.control.setValue(this.selectedOptions); merge( - this.childrenOptionsComponents?.changes || [], - this.viewChildOptionsComponents?.changes || [], + this.childrenOptionsComponents()?.changes || [], + this.viewChildOptionsComponents()?.changes || [], this.control.valueChanges ).subscribe(() => { Promise.resolve().then(() => { @@ -247,12 +249,13 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements let childrenOptionsElementRef: QueryList; // Check if we have child options that are content projected from a parent component. - if (this.childrenOptionsComponents && this.childrenOptionsComponents.length > 0) { - childrenOptionsComponent = this.childrenOptionsComponents.toArray(); + const childrenOptionsComponents = this.childrenOptionsComponents(); + if (childrenOptionsComponents && childrenOptionsComponents.length > 0) { + childrenOptionsComponent = childrenOptionsComponents; childrenOptionsElementRef = this.childrenOptionsEl(); } else { // If not, it means they're internally defined (default), so we fetch from viewChildren. - childrenOptionsComponent = this.viewChildOptionsComponents.toArray(); + childrenOptionsComponent = this.viewChildOptionsComponents; childrenOptionsElementRef = this.viewChildOptionsEl(); } @@ -262,13 +265,13 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements ); if (isSelected) { this._renderer.addClass(childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected'); - childOption.isSelected = true; + childOption.isSelected.set(true); } else { this._renderer.removeClass( childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected' ); - childOption.isSelected = false; + childOption.isSelected.set(false); } }); @@ -297,12 +300,13 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements let childrenOptionsElementRef: QueryList; // Check if we have child options that are content projected from a parent component. - if (this.childrenOptionsComponents && this.childrenOptionsComponents.length > 0) { - childrenOptionsComponent = this.childrenOptionsComponents.toArray(); + const childrenOptionsComponents = this.childrenOptionsComponents(); + if (childrenOptionsComponents && childrenOptionsComponents.length > 0) { + childrenOptionsComponent = childrenOptionsComponents; childrenOptionsElementRef = this.childrenOptionsEl(); } else { // If not, it means they're internally defined (default), so we fetch from viewChildren. - childrenOptionsComponent = this.viewChildOptionsComponents.toArray(); + childrenOptionsComponent = this.viewChildOptionsComponents; childrenOptionsElementRef = this.viewChildOptionsEl(); } diff --git a/libs/ui/src/lib/ui/select/select-option/select-option.component.html b/libs/ui/src/lib/ui/select/select-option/select-option.component.html index e751e23..c2346de 100644 --- a/libs/ui/src/lib/ui/select/select-option/select-option.component.html +++ b/libs/ui/src/lib/ui/select/select-option/select-option.component.html @@ -7,21 +7,21 @@ class="pi-select-option__radio-button" size="x-small" icon="bullet" - [isActive]="isSelected" + [isActive]="isSelected()" [color]="color()" [isDisabled]="isDisabled()" > {{ label() | translate }} - {{ - description | translate + {{ + description() | translate }}
- {{ initials | translate }} - + {{ initials() | translate }} + @@ -29,16 +29,16 @@
{{ label() | translate }}
- {{ initials | translate }} + {{ initials() | translate }} @@ -46,19 +46,19 @@ {{ label() | translate }} - {{ initials | translate }} + {{ initials() | translate }} diff --git a/libs/ui/src/lib/ui/select/select-option/select-option.component.ts b/libs/ui/src/lib/ui/select/select-option/select-option.component.ts index 03850d8..2ac109c 100644 --- a/libs/ui/src/lib/ui/select/select-option/select-option.component.ts +++ b/libs/ui/src/lib/ui/select/select-option/select-option.component.ts @@ -1,49 +1,48 @@ -import { - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - HostBinding, - Input, - input +import { + ChangeDetectionStrategy, + Component, + HostBinding, + input, + model } from '@angular/core'; -import { SelectColor, SelectOptionFlexDirection, actionIcon } from '../entities/select.model'; -import { NgSwitch, NgSwitchCase, NgIf } from '@angular/common'; -import { PiRadioButtonComponent } from '../../button/radio-button/radio-button.component'; -import { MatIcon } from '@angular/material/icon'; -import { MatCheckbox } from '@angular/material/checkbox'; +import { SelectColor, SelectOptionFlexDirection, actionIcon } from '../entities/select.model'; +import { NgSwitch, NgSwitchCase, NgIf } from '@angular/common'; +import { PiRadioButtonComponent } from '../../button/radio-button/radio-button.component'; +import { MatIcon } from '@angular/material/icon'; +import { MatCheckbox } from '@angular/material/checkbox'; import { TranslatePipe } from '@ngx-translate/core'; -@Component({ - selector: 'pi-select-option', - templateUrl: './select-option.component.html', - styleUrls: ['./select-option.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - NgSwitch, - NgSwitchCase, - NgIf, - PiRadioButtonComponent, - MatIcon, - MatCheckbox, - TranslatePipe, - ], +@Component({ + selector: 'pi-select-option', + templateUrl: './select-option.component.html', + styleUrls: ['./select-option.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgSwitch, + NgSwitchCase, + NgIf, + PiRadioButtonComponent, + MatIcon, + MatCheckbox, + TranslatePipe, + ], }) export class PiSelectOptionComponent { @HostBinding('class') get classes(): string { - const color = this.color(); + const color = this.color(); const colorClass = color && `pi-color__${color}`; - const flexDirection = this.flexDirection(); + const flexDirection = this.flexDirection(); const flexDirectionClass = flexDirection && `pi-select-option__flex-direction--${flexDirection}`; - const hasDescriptionClass = this.description ? 'has-description' : ''; + const hasDescriptionClass = this.description() ? 'has-description' : ''; const hasCheckboxClass = this.type() === 'multiple' ? 'has-checkbox' : ''; - const hasIconClass = this.icon ? 'has-icon' : ''; + const hasIconClass = this.icon() ? 'has-icon' : ''; const hasRadioClass = this.hasRadio() ? 'has-radio' : ''; - const textAlign = this.textAlign(); + const textAlign = this.textAlign(); const justifyClass = textAlign && `pi-select-option__text-align--${textAlign}`; - const size = this.size(); + const size = this.size(); const sizeClass = size && `pi-select-option__${size}`; - const type = this.type(); + const type = this.type(); const typeClass = type && `pi-select-option__type--${type}`; return [ @@ -63,9 +62,9 @@ export class PiSelectOptionComponent { // Content readonly id = input(); readonly label = input(); - @Input() description: string | undefined; - @Input() initials: string | undefined; - @Input() icon: string | undefined; + readonly description = input(); + readonly initials = input(); + readonly icon = input(); readonly actionIcon = input('chevron-forward'); readonly hasRadio = input(false); @@ -88,25 +87,13 @@ export class PiSelectOptionComponent { */ readonly type = input<'single' | 'multiple' | 'flex-wrap'>('single'); - private _isSelected = false; - - @Input() set isSelected(newValue: boolean) { - if (this._isSelected !== newValue) { - this._isSelected = newValue; - this.onValueChanged(); - } - } - get isSelected() { - return this._isSelected; - } + /** + * `model` en vez de `input` porque los selects padre lo asignan de forma + * imperativa sobre las instancias hijas (`childOption.isSelected.set(...)`). + * El `markForCheck()` manual que hacía el setter anterior ya no hace falta: + * al ser una señal, Angular marca el componente automáticamente. + */ + readonly isSelected = model(false); readonly isDisabled = input.required(); - - constructor(private readonly _cdr: ChangeDetectorRef) {} - - onValueChanged() { - // In order to update the value displayed in the component we still - // need to manually request change detection. - this._cdr.markForCheck(); - } } diff --git a/libs/ui/src/lib/ui/select/select-period/select-period.component.html b/libs/ui/src/lib/ui/select/select-period/select-period.component.html index 26c7e24..5084358 100644 --- a/libs/ui/src/lib/ui/select/select-period/select-period.component.html +++ b/libs/ui/src/lib/ui/select/select-period/select-period.component.html @@ -23,7 +23,7 @@
{ Promise.resolve().then(() => { @@ -197,12 +197,13 @@ export class PiSelectPeriodComponent extends PiBaseSelectComponent implements Af let childrenOptionsElementRef: QueryList; // Check if we have child options that are content projected from a parent component. - if (this.childrenOptionsComponents && this.childrenOptionsComponents.length > 0) { - childrenOptionsComponent = this.childrenOptionsComponents.toArray(); + const childrenOptionsComponents = this.childrenOptionsComponents(); + if (childrenOptionsComponents && childrenOptionsComponents.length > 0) { + childrenOptionsComponent = childrenOptionsComponents; childrenOptionsElementRef = this.childrenOptionsEl(); } else { // If not, it means they're internally defined (default), so we fetch from viewChildren. - childrenOptionsComponent = this.viewChildOptionsComponents?.toArray(); + childrenOptionsComponent = this.viewChildOptionsComponents; childrenOptionsElementRef = this.viewChildOptionsEl(); } @@ -214,14 +215,14 @@ export class PiSelectPeriodComponent extends PiBaseSelectComponent implements Af // If the current child option matches the selected option, highlight it. if (isSelected) { this._renderer.addClass(childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected'); - childOption.isSelected = true; + childOption.isSelected.set(true); } else { // Otherwise, ensure it is not highlighted. this._renderer.removeClass( childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected' ); - childOption.isSelected = false; + childOption.isSelected.set(false); } }); this._cdr.markForCheck(); diff --git a/libs/ui/src/lib/ui/select/select/select.component.html b/libs/ui/src/lib/ui/select/select/select.component.html index bda3c8b..399a9d1 100644 --- a/libs/ui/src/lib/ui/select/select/select.component.html +++ b/libs/ui/src/lib/ui/select/select/select.component.html @@ -23,7 +23,7 @@
{ Promise.resolve().then(() => { @@ -187,12 +187,13 @@ export class PiSelectComponent extends PiBaseSelectComponent implements AfterCon let childrenOptionsElementRef: QueryList; // Check if we have child options that are content projected from a parent component. - if (this.childrenOptionsComponents && this.childrenOptionsComponents.length > 0) { - childrenOptionsComponent = this.childrenOptionsComponents.toArray(); + const childrenOptionsComponents = this.childrenOptionsComponents(); + if (childrenOptionsComponents && childrenOptionsComponents.length > 0) { + childrenOptionsComponent = childrenOptionsComponents; childrenOptionsElementRef = this.childrenOptionsEl(); } else { // If not, it means they're internally defined (default), so we fetch from viewChildren. - childrenOptionsComponent = this.viewChildOptionsComponents?.toArray(); + childrenOptionsComponent = this.viewChildOptionsComponents; childrenOptionsElementRef = this.viewChildOptionsEl(); } @@ -204,13 +205,13 @@ export class PiSelectComponent extends PiBaseSelectComponent implements AfterCon // If the current child option matches the selected option, highlight it. if (isSelected) { this._renderer.addClass(childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected'); - childOption.isSelected = true; + childOption.isSelected.set(true); } else { // Otherwise, ensure it is not highlighted. this._renderer.removeClass( childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected' ); - childOption.isSelected = false; + childOption.isSelected.set(false); } }); this._cdr.markForCheck(); diff --git a/libs/ui/src/lib/ui/slider/base-slider.component.ts b/libs/ui/src/lib/ui/slider/base-slider.component.ts index e65a185..c2fe610 100644 --- a/libs/ui/src/lib/ui/slider/base-slider.component.ts +++ b/libs/ui/src/lib/ui/slider/base-slider.component.ts @@ -2,7 +2,6 @@ import { Directive, HostBinding, - Input, input, viewChild } from '@angular/core'; @@ -24,7 +23,7 @@ export abstract class PiBaseSliderComponent { } // Content - @Input() label: string; + readonly label = input(); readonly thumbLabel = input(true); readonly minValue = input(0); readonly maxValue = input(100); diff --git a/libs/ui/src/lib/ui/slider/slider/slider.component.html b/libs/ui/src/lib/ui/slider/slider/slider.component.html index 79c699d..f95755c 100644 --- a/libs/ui/src/lib/ui/slider/slider/slider.component.html +++ b/libs/ui/src/lib/ui/slider/slider/slider.component.html @@ -1,5 +1,5 @@ - - + + {{ control?.value }} {{ unit() }}
- - + +
- +
diff --git a/libs/ui/src/lib/ui/tab/tab/tab.component.ts b/libs/ui/src/lib/ui/tab/tab/tab.component.ts index 4773488..c422eae 100644 --- a/libs/ui/src/lib/ui/tab/tab/tab.component.ts +++ b/libs/ui/src/lib/ui/tab/tab/tab.component.ts @@ -1,63 +1,54 @@ -import { - AfterViewChecked, - AfterViewInit, - ChangeDetectionStrategy, - ChangeDetectorRef, - Component, - ContentChildren, - ElementRef, - HostBinding, - Input, - OnDestroy, - QueryList, - Renderer2, - input, - output, - contentChildren, - viewChild +import { + AfterViewChecked, + AfterViewInit, + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + ElementRef, + HostBinding, + Input, + OnDestroy, + Renderer2, + input, + output, + contentChildren, + viewChild } from '@angular/core'; import { TabButton } from '../entities/tab.model'; -import { PiTabContentDirective, PiTabElementDirective } from '../tab.directive'; -import { NgIf, NgFor, NgTemplateOutlet } from '@angular/common'; -import { PiIconComponent } from '../../icon/icon/icon.component'; -import { PiDisabledDirective } from '../../../util/status/directives/status.directive'; -import { PiDisabledDirective as PiDisabledDirective_1 } from '../../text/directives/text-styles.directive'; +import { PiTabContentDirective, PiTabElementDirective } from '../tab.directive'; +import { NgIf, NgFor, NgTemplateOutlet } from '@angular/common'; +import { PiIconComponent } from '../../icon/icon/icon.component'; +import { PiDisabledDirective } from '../../../util/status/directives/status.directive'; +import { PiDisabledDirective as PiDisabledDirective_1 } from '../../text/directives/text-styles.directive'; import { TranslatePipe } from '@ngx-translate/core'; -@Component({ - selector: 'pi-tab', - templateUrl: './tab.component.html', - styleUrls: ['./tab.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ - NgIf, - PiIconComponent, - PiDisabledDirective, - PiDisabledDirective_1, - NgFor, - NgTemplateOutlet, - TranslatePipe, - ], +@Component({ + selector: 'pi-tab', + templateUrl: './tab.component.html', + styleUrls: ['./tab.component.scss'], + changeDetection: ChangeDetectionStrategy.OnPush, + imports: [ + NgIf, + PiIconComponent, + PiDisabledDirective, + PiDisabledDirective_1, + NgFor, + NgTemplateOutlet, + TranslatePipe, + ], }) export class PiTabComponent implements AfterViewInit, AfterViewChecked, OnDestroy { @HostBinding('class') get classes(): string { const appearanceClass = `pi-tab__appearance--${this.appearance()}`; const hideTabsClass = this.hideTabs() ? 'hide-tabs' : ''; - const justify = this.justify(); + const justify = this.justify(); const justifyClass = justify ? `pi-tab__justify-content--${justify}` : ''; return ['pi-tab', hideTabsClass, appearanceClass, justifyClass].join(' '); } - @Input() tabButtons: Array; - private _defaultSelectedTab: number = 0; - @Input() - get defaultSelectedTab(): number { - return this._defaultSelectedTab; - } - set defaultSelectedTab(value: number) { - this._defaultSelectedTab = value; - this.onTabChange(value); - } + readonly tabButtons = input>(); + + readonly defaultSelectedTab = input(0); readonly appearance = input<'default' | 'fill' | 'outline'>('default'); @@ -72,8 +63,7 @@ export class PiTabComponent implements AfterViewInit, AfterViewChecked, OnDestro readonly tabChange = output(); - @ContentChildren(PiTabContentDirective, { descendants: true }) - tabContentItems!: QueryList; + readonly tabContentItems = contentChildren(PiTabContentDirective, { descendants: true }); readonly tabElementItems = contentChildren(PiTabElementDirective, { descendants: true }); @@ -89,7 +79,12 @@ export class PiTabComponent implements AfterViewInit, AfterViewChecked, OnDestro private cdr: ChangeDetectorRef, private el: ElementRef, private renderer: Renderer2 - ) {} + ) { + // Antes lo hacía el setter de `defaultSelectedTab` (más la llamada de + // ngAfterViewInit). El efecto cubre ambos: se ejecuta al inicializar y + // en cada cambio de la entrada. + effect(() => this.onTabChange(this.defaultSelectedTab())); + } get tabContentNativeElements(): HTMLElement[] { return Array.from(this.el.nativeElement.querySelectorAll('.pi-tab__content')); @@ -112,11 +107,9 @@ export class PiTabComponent implements AfterViewInit, AfterViewChecked, OnDestro } ngAfterViewInit(): void { - this.tabContentItems.changes.subscribe(() => { + this.tabContentItems().changes.subscribe(() => { this.cdr.markForCheck(); }); - - this.onTabChange(this.defaultSelectedTab); } ngAfterViewChecked(): void { @@ -190,7 +183,7 @@ export class PiTabComponent implements AfterViewInit, AfterViewChecked, OnDestro } private updateIsTabWrapperScrolledToEnd(): void { - const tabWrapperElementRef = this.tabWrapperElementRef(); + const tabWrapperElementRef = this.tabWrapperElementRef(); this.isTabWrapperScrolledToEnd = tabWrapperElementRef?.nativeElement?.scrollWidth - tabWrapperElementRef?.nativeElement?.scrollLeft === diff --git a/libs/ui/src/lib/ui/text/base-text-viewer.component.ts b/libs/ui/src/lib/ui/text/base-text-viewer.component.ts index 02afeea..c76d466 100644 --- a/libs/ui/src/lib/ui/text/base-text-viewer.component.ts +++ b/libs/ui/src/lib/ui/text/base-text-viewer.component.ts @@ -1,5 +1,5 @@ // Angular -import { Directive, HostBinding, Input, OnInit, input } from '@angular/core'; +import { Directive, HostBinding, OnInit, input } from '@angular/core'; // Style definitions import { @@ -46,15 +46,15 @@ export abstract class PiBaseTextViewerComponent implements OnInit { readonly justify = input('flex-start'); readonly fontWeight = input('300'); readonly fontStyle = input('normal'); - @Input() icon: string; + readonly icon = input(); readonly iconSize = input('medium'); readonly iconColor = input(); readonly iconAppearance = input<'stroke' | 'fill-stroke' | 'fill' | 'default' | 'custom'>('default'); readonly isDisabled = input(false); // Content - @Input() label!: string | unknown; - @Input() value!: string; + readonly label = input.required(); + readonly value = input.required(); ngOnInit(): void { this.alignmentClass; @@ -114,15 +114,15 @@ export abstract class PiBaseTextViewerComponent implements OnInit { } protected get hasIconClass(): string | boolean { - return this.icon ? 'has-icon' : ''; + return this.icon() ? 'has-icon' : ''; } protected get hasLabelClass(): string | boolean { - return this.label ? 'has-label' : ''; + return this.label() ? 'has-label' : ''; } protected get hasValueClass(): string | boolean { - return this.value ? 'has-value' : ''; + return this.value() ? 'has-value' : ''; } protected get iconSizeClass(): string | undefined { diff --git a/libs/ui/src/lib/ui/text/directives/text-styles.directive.ts b/libs/ui/src/lib/ui/text/directives/text-styles.directive.ts index 443d1c5..e4cd303 100644 --- a/libs/ui/src/lib/ui/text/directives/text-styles.directive.ts +++ b/libs/ui/src/lib/ui/text/directives/text-styles.directive.ts @@ -1,4 +1,4 @@ -import { Directive, HostBinding, Input, OnChanges, SimpleChanges, input } from '@angular/core'; +import { Directive, HostBinding, OnChanges, SimpleChanges, input } from '@angular/core'; @Directive({ selector: '[pi-text-centered]', }) export class PiTextCenteredDirective { @@ -16,9 +16,9 @@ export class PiTextGroupDirective { @Directive({ selector: '[pi-disabled]', }) export class PiDisabledDirective implements OnChanges { @HostBinding('style.opacity') get opacity(): number { - return this.isDisabled ? 0.2 : 1; + return this.isDisabled() ? 0.2 : 1; } - @Input() isDisabled: boolean; + readonly isDisabled = input(); ngOnChanges(changes: SimpleChanges): void { if (changes) { diff --git a/libs/ui/src/lib/ui/text/text-viewer-link/text-viewer-link.component.html b/libs/ui/src/lib/ui/text/text-viewer-link/text-viewer-link.component.html index 878239f..a9543f7 100644 --- a/libs/ui/src/lib/ui/text/text-viewer-link/text-viewer-link.component.html +++ b/libs/ui/src/lib/ui/text/text-viewer-link/text-viewer-link.component.html @@ -1,6 +1,6 @@ - +
diff --git a/libs/ui/src/lib/ui/text/text-viewer/text-viewer.component.html b/libs/ui/src/lib/ui/text/text-viewer/text-viewer.component.html index eddb30e..a391a33 100644 --- a/libs/ui/src/lib/ui/text/text-viewer/text-viewer.component.html +++ b/libs/ui/src/lib/ui/text/text-viewer/text-viewer.component.html @@ -1,7 +1,7 @@ - +
- - - {{ value }} + + + {{ value() }}
diff --git a/libs/ui/src/lib/ui/wrapper/form/form.wrapper.html b/libs/ui/src/lib/ui/wrapper/form/form.wrapper.html index 866357a..88aa38b 100644 --- a/libs/ui/src/lib/ui/wrapper/form/form.wrapper.html +++ b/libs/ui/src/lib/ui/wrapper/form/form.wrapper.html @@ -1,4 +1,4 @@ -

{{ title }}

+

{{ title() }}

diff --git a/libs/ui/src/lib/ui/wrapper/form/form.wrapper.ts b/libs/ui/src/lib/ui/wrapper/form/form.wrapper.ts index 561779f..333ba39 100644 --- a/libs/ui/src/lib/ui/wrapper/form/form.wrapper.ts +++ b/libs/ui/src/lib/ui/wrapper/form/form.wrapper.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, HostBinding, Input, input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, input } from '@angular/core'; import { FormGroup, ReactiveFormsModule } from '@angular/forms'; import { grid, WrapperTypeClass } from '../../../styles'; @@ -24,7 +24,7 @@ export class PiFormWrapper extends PiBaseWrapper { } readonly formGroup = input.required(); - @Input() title: string | undefined; + readonly title = input(); readonly subtitle = input(); override readonly grid = input('none'); diff --git a/libs/ui/src/lib/ui/wrapper/header/header.wrapper.html b/libs/ui/src/lib/ui/wrapper/header/header.wrapper.html index c42fd85..5542237 100644 --- a/libs/ui/src/lib/ui/wrapper/header/header.wrapper.html +++ b/libs/ui/src/lib/ui/wrapper/header/header.wrapper.html @@ -1,12 +1,12 @@ -
+
diff --git a/libs/ui/src/lib/ui/wrapper/header/header.wrapper.ts b/libs/ui/src/lib/ui/wrapper/header/header.wrapper.ts index b4a972a..04526a8 100644 --- a/libs/ui/src/lib/ui/wrapper/header/header.wrapper.ts +++ b/libs/ui/src/lib/ui/wrapper/header/header.wrapper.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, Directive, HostBinding, Input, input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Directive, HostBinding, input } from '@angular/core'; import { alignment, WrapperTypeClass } from '../../../styles'; import { PiBaseWrapper } from '../base.wrapper'; @@ -49,7 +49,7 @@ export class PiHeaderWrapper extends PiBaseWrapper { ].join(' '); } - @Input() backLink: backLink | undefined; + readonly backLink = input(); override readonly alignment = input('flex-end'); } diff --git a/libs/ui/src/lib/util/menu/base.menu.component.ts b/libs/ui/src/lib/util/menu/base.menu.component.ts index 2fbe1ca..b20a355 100644 --- a/libs/ui/src/lib/util/menu/base.menu.component.ts +++ b/libs/ui/src/lib/util/menu/base.menu.component.ts @@ -1,5 +1,5 @@ // Angular -import { Directive, HostBinding, Input, TemplateRef, input, contentChild } from '@angular/core'; +import { Directive, HostBinding, TemplateRef, input, contentChild } from '@angular/core'; // Style definitions import { MenuEntry, MenuItemText } from './entities/menu-entry.model'; @@ -12,16 +12,7 @@ export abstract class PiBaseMenuComponent { readonly label = input(); - _menuItems: Array = []; - - @Input() - set menuItems(menuItems: MenuEntry[]) { - this._menuItems = menuItems; - } - - get menuItems(): MenuEntry[] { - return this._menuItems; - } + readonly menuItems = input>([]); /** * Parent Menu diff --git a/libs/ui/src/lib/util/status/directives/status.directive.ts b/libs/ui/src/lib/util/status/directives/status.directive.ts index d14e1dc..5482bc8 100644 --- a/libs/ui/src/lib/util/status/directives/status.directive.ts +++ b/libs/ui/src/lib/util/status/directives/status.directive.ts @@ -1,15 +1,15 @@ -import { Directive, OnChanges, HostBinding, Input, SimpleChanges } from '@angular/core'; +import { Directive, OnChanges, HostBinding, SimpleChanges, input } from '@angular/core'; @Directive({ selector: '[pi-disabled]', }) export class PiDisabledDirective implements OnChanges { @HostBinding('style.opacity') get opacity(): number { - return this.isDisabled ? 0.33 : 1; + return this.isDisabled() ? 0.33 : 1; } @HostBinding('class') get classes(): string | boolean { - return [this.isDisabled && 'is-disabled'].join(' '); + return [this.isDisabled() && 'is-disabled'].join(' '); } - @Input() isDisabled: boolean; + readonly isDisabled = input(); ngOnChanges(changes: SimpleChanges): void { if (changes) { @@ -21,10 +21,10 @@ export class PiDisabledDirective implements OnChanges { @Directive({ selector: '[is-active]', }) export class PiIsActiveDirective { @HostBinding('class') get classes(): string | boolean { - return [this.isActive ? 'is-active' : '', 'pi-status'].join(' '); + return [this.isActive() ? 'is-active' : '', 'pi-status'].join(' '); } - @Input() isActive!: boolean; + readonly isActive = input.required(); ngOnChanges(changes: SimpleChanges): void { if (changes) { @@ -36,10 +36,10 @@ export class PiIsActiveDirective { @Directive({ selector: '[is-visited]', }) export class PiIsVisitedDirective { @HostBinding('class') get classes(): string | boolean { - return [this.isVisited ? 'is-visited' : '', 'pi-status'].join(' '); + return [this.isVisited() ? 'is-visited' : '', 'pi-status'].join(' '); } - @Input() isVisited!: boolean; + readonly isVisited = input.required(); ngOnChanges(changes: SimpleChanges): void { if (changes) {