refactor: convierte los inputs con accessor a signals (input/model + effect)

This commit is contained in:
2026-07-27 23:33:29 +02:00
parent c046e6ac23
commit 3b640dab1b
74 changed files with 496 additions and 525 deletions

View File

@@ -3,9 +3,9 @@
<ng-container *ngIf="direction() === 'default'; else horizontal"> <ng-container *ngIf="direction() === 'default'; else horizontal">
<section <section
*ngFor="let item of items; index as i" *ngFor="let item of items(); index as i"
[class]=" [class]="
!!item.status && item.status === 'disabled' !!item.status() && item.status() === 'disabled'
? 'is-disabled' ? 'is-disabled'
: '' + setAccordionItemClasses(item, i) : '' + setAccordionItemClasses(item, i)
" "
@@ -47,12 +47,12 @@
></ng-container> ></ng-container>
<aside class="pi-accordion-item-header-content pi-accordion-item-header-content__right"> <aside class="pi-accordion-item-header-content pi-accordion-item-header-content__right">
<pi-chip-status <pi-chip-status
*ngIf="item.status" *ngIf="item.status()"
[status]="item.status" [status]="item.status()"
[label]="item.statusLabel()" [label]="item.statusLabel()"
[icon]="item.statusIcon()" [icon]="item.statusIcon()"
></pi-chip-status> ></pi-chip-status>
<span *ngIf="item.status"></span> <span *ngIf="item.status()"></span>
<ng-container *ngIf="hasForm(); else defaultIcon"> <ng-container *ngIf="hasForm(); else defaultIcon">
<pi-icon-button <pi-icon-button
(execute)="toggleState(item, i)" (execute)="toggleState(item, i)"
@@ -63,7 +63,7 @@
[hasPadding]="false" [hasPadding]="false"
[text]="'common.edit' | translate" [text]="'common.edit' | translate"
[class]="isItemExpanded(i) ? 'is-expanded' : 'is-collapsed'" [class]="isItemExpanded(i) ? 'is-expanded' : 'is-collapsed'"
[disabled]="!!item.status && item.status === 'disabled'" [disabled]="!!item.status() && item.status() === 'disabled'"
></pi-icon-button> ></pi-icon-button>
</ng-container> </ng-container>
<ng-template #defaultIcon> <ng-template #defaultIcon>
@@ -111,7 +111,7 @@
<ng-template #horizontal> <ng-template #horizontal>
<section class="pi-accordion-item-content pi-accordion-item-content__left"> <section class="pi-accordion-item-content pi-accordion-item-content__left">
<!-- HEADER --> <!-- HEADER -->
<ng-container *ngFor="let item of items; index as i"> <ng-container *ngFor="let item of items(); index as i">
<header <header
[class]=" [class]="
setAccordionItemClasses(item, i) + setAccordionItemClasses(item, i) +
@@ -126,11 +126,11 @@
></ng-container> ></ng-container>
<aside *ngIf="hasChevron()"> <aside *ngIf="hasChevron()">
<pi-chip-status <pi-chip-status
*ngIf="item.status && !isItemExpanded(i)" *ngIf="item.status() && !isItemExpanded(i)"
[status]="item.status" [status]="item.status()"
icon="exclamation" icon="exclamation"
></pi-chip-status> ></pi-chip-status>
<span *ngIf="item.status && !isItemExpanded(i)"></span> <span *ngIf="item.status() && !isItemExpanded(i)"></span>
<pi-icon <pi-icon
icon="chevron-forward" icon="chevron-forward"
[size]="appearance() === 'outline' ? 'x-small' : 'small'" [size]="appearance() === 'outline' ? 'x-small' : 'small'"
@@ -143,7 +143,7 @@
<section class="pi-accordion-item-content pi-accordion-item-content__right"> <section class="pi-accordion-item-content pi-accordion-item-content__right">
<!-- MAIN --> <!-- MAIN -->
<main <main
*ngFor="let item of items; index as i" *ngFor="let item of items(); index as i"
[class]=" [class]="
setAccordionItemClasses(item, i) + setAccordionItemClasses(item, i) +
' ' + ' ' +

View File

@@ -3,14 +3,12 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
ContentChildren,
HostBinding, HostBinding,
Input,
OnChanges, OnChanges,
QueryList,
SimpleChanges, SimpleChanges,
input, input,
output output,
contentChildren
} from '@angular/core'; } from '@angular/core';
import { padding, styleType, textColor } from '../../../styles'; import { padding, styleType, textColor } from '../../../styles';
import { PiAccordionItemDirective } from '../directives/accordion-content.directive'; import { PiAccordionItemDirective } from '../directives/accordion-content.directive';
@@ -52,7 +50,7 @@ export class PiAccordionComponent implements OnChanges, AfterContentInit {
return ['pi-accordion', colorClass, appearanceClass, directionClass, paddingClass].join(' '); return ['pi-accordion', colorClass, appearanceClass, directionClass, paddingClass].join(' ');
} }
@Input() expandedItemIndex!: number; readonly expandedItemIndex = input.required<number>();
readonly appearance = input<'default' | 'outline' | 'fill'>('default'); readonly appearance = input<'default' | 'outline' | 'fill'>('default');
readonly color = input<styleType>('white'); readonly color = input<styleType>('white');
readonly direction = input<'default' | 'horizontal'>('default'); readonly direction = input<'default' | 'horizontal'>('default');
@@ -63,7 +61,7 @@ export class PiAccordionComponent implements OnChanges, AfterContentInit {
readonly expandedItemIndexChange = output<number>(); readonly expandedItemIndexChange = output<number>();
@ContentChildren(PiAccordionItemDirective) items!: QueryList<PiAccordionItemDirective>; readonly items = contentChildren(PiAccordionItemDirective);
constructor(private cdr: ChangeDetectorRef) {} constructor(private cdr: ChangeDetectorRef) {}
@@ -77,27 +75,29 @@ export class PiAccordionComponent implements OnChanges, AfterContentInit {
} }
ngAfterContentInit(): void { ngAfterContentInit(): void {
this.items.changes.subscribe(() => { this.items().changes.subscribe(() => {
this.cdr.markForCheck(); this.cdr.markForCheck();
}); });
} }
toggleState = (item: PiAccordionItemDirective, index: number) => { toggleState = (item: PiAccordionItemDirective, index: number) => {
if (this.direction() === 'horizontal' && this.isItemExpanded(index)) return; if (this.direction() === 'horizontal' && this.isItemExpanded(index)) return;
if (item.status === 'disabled') return; if (item.status() === 'disabled') return;
if (this.expandedItemIndex === index) { const expandedItemIndex = this.expandedItemIndex();
if (expandedItemIndex === index) {
this.expandedItemIndex = null; this.expandedItemIndex = null;
} else { } else {
this.expandedItemIndex = index; this.expandedItemIndex = index;
} }
this.expandedItemIndexChange.emit(this.expandedItemIndex); this.expandedItemIndexChange.emit(expandedItemIndex);
}; };
isItemExpanded = (index: number) => this.expandedItemIndex === index; isItemExpanded = (index: number) => this.expandedItemIndex() === index;
setAccordionItemClasses(item: PiAccordionItemDirective, index: number): string { setAccordionItemClasses(item: PiAccordionItemDirective, index: number): string {
const expandedClass = this.isItemExpanded(index) ? 'is-expanded' : 'is-collapsed'; const expandedClass = this.isItemExpanded(index) ? 'is-expanded' : 'is-collapsed';
const statusClass = item.status && 'has-status' + ' ' + `is-${item.status}`; const status = item.status();
const statusClass = status && 'has-status' + ' ' + `is-${status}`;
const isConfidentialClass = item.isConfidential() ? 'is-confidential' : ''; const isConfidentialClass = item.isConfidential() ? 'is-confidential' : '';
return [expandedClass, statusClass, isConfidentialClass].join(' '); return [expandedClass, statusClass, isConfidentialClass].join(' ');
} }

View File

@@ -2,7 +2,6 @@ import {
AfterViewInit, AfterViewInit,
Directive, Directive,
HostBinding, HostBinding,
Input,
TemplateRef, TemplateRef,
input, input,
contentChild contentChild
@@ -53,7 +52,7 @@ export class PiAccordionFooterDirective {
export class PiAccordionItemDirective { export class PiAccordionItemDirective {
readonly title = input(''); readonly title = input('');
readonly subtitle = input(''); readonly subtitle = input('');
@Input() status!: status; readonly status = input.required<status>();
readonly statusLabel = input<string>(); readonly statusLabel = input<string>();
readonly statusIcon = input<string>(); readonly statusIcon = input<string>();
readonly isConfidential = input(false); readonly isConfidential = input(false);

View File

@@ -10,18 +10,18 @@
<section> <section>
<header> <header>
<h3 bold>{{ title() }}</h3> <h3 bold>{{ title() }}</h3>
<span *ngIf="message">{{ message }}</span> <span *ngIf="message()">{{ message() }}</span>
</header> </header>
<!-- Action --> <!-- Action -->
<pi-icon-button <pi-icon-button
*ngIf="buttonText" *ngIf="buttonText()"
(execute)="execute.emit()" (execute)="execute.emit()"
icon="chevron-forward" icon="chevron-forward"
iconColor="white" iconColor="white"
iconSize="small" iconSize="small"
[appearance]="mode() === 'light' ? 'ghost' : 'fill'" [appearance]="mode() === 'light' ? 'ghost' : 'fill'"
[hasPadding]="false" [hasPadding]="false"
>{{ buttonText }}</pi-icon-button >{{ buttonText() }}</pi-icon-button
> >
</section> </section>
</main> </main>

View File

@@ -1,10 +1,11 @@
import { Component, HostBinding, Input, input, output } from '@angular/core'; import { Component, HostBinding, input, output, ChangeDetectionStrategy } from '@angular/core';
import { PiBackgroundImageDirective } from '../../image/background-image.directive'; import { PiBackgroundImageDirective } from '../../image/background-image.directive';
import { PiTextWeightBoldDirective } from '../../text/directives/text-weights.directive'; import { PiTextWeightBoldDirective } from '../../text/directives/text-weights.directive';
import { NgIf } from '@angular/common'; import { NgIf } from '@angular/common';
import { PiIconButtonComponent } from '../../button/icon-button/icon-button.component'; import { PiIconButtonComponent } from '../../button/icon-button/icon-button.component';
@Component({ @Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'pi-banner', selector: 'pi-banner',
templateUrl: './banner.component.html', templateUrl: './banner.component.html',
styleUrls: ['./banner.component.scss'], styleUrls: ['./banner.component.scss'],
@@ -26,8 +27,8 @@ export class PiBannerComponent {
// Text // Text
readonly title = input<string>(); readonly title = input<string>();
@Input() message: string | undefined; readonly message = input<string>();
@Input() buttonText: string | undefined; readonly buttonText = input<string>();
// Image // Image
readonly pathImg = input<string>(); readonly pathImg = input<string>();

View File

@@ -14,7 +14,7 @@
appearanceClass appearanceClass
]" ]"
> >
<span *ngIf="text">{{ text }}</span> <span *ngIf="text()">{{ text() }}</span>
<svg *ngIf="hasIcon()" viewBox="0 0 6 9" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg *ngIf="hasIcon()" viewBox="0 0 6 9" fill="none" xmlns="http://www.w3.org/2000/svg">
<g> <g>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input, input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, input } from '@angular/core';
import { buttonSize, mode } from '../../../styles'; import { buttonSize, mode } from '../../../styles';
import { PiBaseButtonComponent } from '../base-button.component'; import { PiBaseButtonComponent } from '../base-button.component';
import { NgIf } from '@angular/common'; import { NgIf } from '@angular/common';
@@ -19,7 +19,7 @@ export class PiActionButtonComponent extends PiBaseButtonComponent {
readonly previous = input(false); readonly previous = input(false);
override readonly size = input<buttonSize>('medium'); override readonly size = input<buttonSize>('medium');
@Input() text: string | undefined; readonly text = input<string>();
readonly hasPadding = input(true); readonly hasPadding = input(true);
readonly hasIcon = input(true); readonly hasIcon = input(true);
readonly label = input<string>(); readonly label = input<string>();
@@ -44,7 +44,7 @@ export class PiActionButtonComponent extends PiBaseButtonComponent {
} }
get textClass(): string | null { get textClass(): string | null {
return this.text ? 'has-text' : null; return this.text() ? 'has-text' : null;
} }
get fontWeightClass(): string | null { get fontWeightClass(): string | null {

View File

@@ -20,7 +20,7 @@
modeClass modeClass
]" ]"
> >
<span *ngIf="text">{{ text }}</span> <span *ngIf="text()">{{ text() }}</span>
<ng-content></ng-content> <ng-content></ng-content>
<img src="/assets/icons/{{ icon() }}.svg" /></button <img src="/assets/icons/{{ icon() }}.svg" /></button
></ng-container> ></ng-container>
@@ -48,7 +48,7 @@
]" ]"
> >
<mat-icon *ngIf="previous()" [svgIcon]="icon()" alt="icon"></mat-icon> <mat-icon *ngIf="previous()" [svgIcon]="icon()" alt="icon"></mat-icon>
<span *ngIf="text">{{ text }}</span> <span *ngIf="text()">{{ text() }}</span>
<ng-content></ng-content> <ng-content></ng-content>
<mat-icon *ngIf="!previous()" [svgIcon]="icon()" alt="icon"></mat-icon></button <mat-icon *ngIf="!previous()" [svgIcon]="icon()" alt="icon"></mat-icon></button
></ng-template> ></ng-template>

View File

@@ -3,7 +3,6 @@ import {
Component, Component,
ElementRef, ElementRef,
HostBinding, HostBinding,
Input,
input, input,
viewChild viewChild
} from '@angular/core'; } from '@angular/core';
@@ -38,7 +37,7 @@ export class PiIconButtonComponent extends PiBaseButtonComponent {
readonly iconType = input<'fill' | 'default' | 'custom'>('default'); readonly iconType = input<'fill' | 'default' | 'custom'>('default');
readonly previous = input(false); readonly previous = input(false);
readonly width = input<buttonSize>('medium'); readonly width = input<buttonSize>('medium');
@Input() text: string | undefined; readonly text = input<string>();
readonly hasPadding = input(true); readonly hasPadding = input(true);
readonly mode = input<mode>('dark'); readonly mode = input<mode>('dark');
readonly isResponsive = input(false); readonly isResponsive = input(false);
@@ -59,7 +58,7 @@ export class PiIconButtonComponent extends PiBaseButtonComponent {
} }
get textClass(): string | null { get textClass(): string | null {
return this.text ? 'has-text' : 'pi-button__icon--single'; return this.text() ? 'has-text' : 'pi-button__icon--single';
} }
get iconPositionClass(): string | null { get iconPositionClass(): string | null {

View File

@@ -1,5 +1,5 @@
<div (click)="toggleSelected()"> <div (click)="toggleSelected()">
<span [class]="!isActive ? 'is-active' : ''"> <span [class]="!isActive() ? 'is-active' : ''">
<!-- Prefix --> <!-- Prefix -->
<ng-content select="pi-prefix-left"></ng-content> <ng-content select="pi-prefix-left"></ng-content>
@@ -13,7 +13,7 @@
<!-- Suffix --> <!-- Suffix -->
<ng-content select="pi-suffix-left"></ng-content> <ng-content select="pi-suffix-left"></ng-content>
</span> </span>
<span [class]="isActive ? 'is-active' : ''"> <span [class]="isActive() ? 'is-active' : ''">
<!-- Prefix --> <!-- Prefix -->
<ng-content select="pi-prefix-right"></ng-content> <ng-content select="pi-prefix-right"></ng-content>

View File

@@ -2,7 +2,6 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
HostBinding, HostBinding,
Input,
input, input,
output output
} from '@angular/core'; } from '@angular/core';
@@ -30,7 +29,7 @@ export class PiSwitchButtonComponent extends PiBaseButtonComponent {
].join(' '); ].join(' ');
} }
@Input() isActive = true; readonly isActive = input(true);
readonly toggledTo = output(); readonly toggledTo = output();
readonly contentType = input<'icon' | 'text'>('icon'); readonly contentType = input<'icon' | 'text'>('icon');
@@ -40,7 +39,7 @@ export class PiSwitchButtonComponent extends PiBaseButtonComponent {
]>(); ]>();
toggleSelected(): void { toggleSelected(): void {
if (this.isActive) { if (this.isActive()) {
this.isActive = false; this.isActive = false;
this.toggledTo.emit('off'); this.toggledTo.emit('off');
} else { } else {

View File

@@ -1,9 +1,9 @@
<button (click)="onClickHandler($event)" [disabled]="isDisabled()" class="pi-button__toggle"> <button (click)="onClickHandler($event)" [disabled]="isDisabled()" class="pi-button__toggle">
<mat-icon *ngIf="icon" [svgIcon]="icon" [color]="color()"></mat-icon> <mat-icon *ngIf="icon()" [svgIcon]="icon()" [color]="color()"></mat-icon>
<span>{{ text() }}</span> <span>{{ text() }}</span>
<ng-content></ng-content> <ng-content></ng-content>
</button> </button>
<span *ngIf="amount" <span *ngIf="amount()"
><small>{{ amount }}</small></span ><small>{{ amount() }}</small></span
> >

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input, input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, input } from '@angular/core';
import { buttonSize } from '../../../styles'; import { buttonSize } from '../../../styles';
import { PiBaseButtonComponent } from '../base-button.component'; import { PiBaseButtonComponent } from '../base-button.component';
import { NgIf } from '@angular/common'; import { NgIf } from '@angular/common';
@@ -28,9 +28,9 @@ export class PiToggleButtonComponent extends PiBaseButtonComponent {
} }
// Content // Content
@Input() icon: string | undefined; readonly icon = input<string>();
readonly text = input<string>(); readonly text = input<string>();
@Input() amount: number | undefined; readonly amount = input<number>();
// Skin // Skin
readonly width = input<buttonSize>('large'); readonly width = input<buttonSize>('large');

View File

@@ -1,5 +1,5 @@
// Angular // Angular
import { Directive, HostBinding, Input, OnInit, input, output } from '@angular/core'; import { Directive, HostBinding, OnInit, input, output } from '@angular/core';
// Style definitions // Style definitions
import { alignment, context, gap, mode, opacity, padding } from '../../styles'; import { alignment, context, gap, mode, opacity, padding } from '../../styles';
@@ -14,7 +14,7 @@ export abstract class PiBaseCardComponent implements OnInit {
readonly alignment = input.required<alignment>(); readonly alignment = input.required<alignment>();
readonly gap = input.required<gap>(); readonly gap = input.required<gap>();
readonly padding = input<padding>('xxl'); readonly padding = input<padding>('xxl');
@Input() linkLabel: string | undefined; readonly linkLabel = input<string>();
readonly opacity = input<opacity>(); readonly opacity = input<opacity>();
readonly innerPadding = input.required<padding>(); readonly innerPadding = input.required<padding>();
readonly context = input.required<context>(); readonly context = input.required<context>();
@@ -78,7 +78,7 @@ export abstract class PiBaseCardComponent implements OnInit {
} }
protected get hasLinkLabelClass(): string { protected get hasLinkLabelClass(): string {
return !!this.linkLabel ? 'has-link-label' : ''; return !!this.linkLabel() ? 'has-link-label' : '';
} }
protected get opacityClass(): string { protected get opacityClass(): string {

View File

@@ -5,9 +5,9 @@
<ng-content select="pi-card-left-content"> </ng-content> <ng-content select="pi-card-left-content"> </ng-content>
<ng-content select="pi-card-right-content"></ng-content> <ng-content select="pi-card-right-content"></ng-content>
<span *ngIf="isClickable() && !!linkLabel" (click)="execute.emit()"> <span *ngIf="isClickable() && !!linkLabel()" (click)="execute.emit()">
<a> <a>
{{ linkLabel }} {{ linkLabel() }}
<pi-icon icon="open-details" color="primary"></pi-icon> <pi-icon icon="open-details" color="primary"></pi-icon>
</a> </a>
</span> </span>

View File

@@ -7,7 +7,7 @@
appearance="ghost" appearance="ghost"
></pi-icon-button> ></pi-icon-button>
<section [class]="hasOverflow ? 'has-overflow' : ''" #carouselContentWrapper> <section [class]="hasOverflow ? 'has-overflow' : ''" #carouselContentWrapper>
<ng-container *ngFor="let item of carouselItems; index as i; trackBy: trackByFn"> <ng-container *ngFor="let item of carouselItems(); index as i; trackBy: trackByFn">
<section [class]="getItemClass(i)" (click)="selectItem(i)"> <section [class]="getItemClass(i)" (click)="selectItem(i)">
<ng-container *ngTemplateOutlet="item.templateRef"> </ng-container> <ng-container *ngTemplateOutlet="item.templateRef"> </ng-container>
</section> </section>

View File

@@ -5,18 +5,17 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
ContentChildren,
ElementRef, ElementRef,
HostBinding, HostBinding,
OnChanges, OnChanges,
OnDestroy, OnDestroy,
OnInit, OnInit,
QueryList,
Renderer2, Renderer2,
SimpleChanges, SimpleChanges,
input, input,
output, output,
viewChild viewChild,
contentChildren
} from '@angular/core'; } from '@angular/core';
// Shared // Shared
@@ -43,8 +42,9 @@ export class PiCarouselComponent
implements OnInit, OnChanges, AfterViewInit, AfterViewChecked, OnDestroy implements OnInit, OnChanges, AfterViewInit, AfterViewChecked, OnDestroy
{ {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const carouselItems = this.carouselItems();
const itemsNumberClass = const itemsNumberClass =
this.carouselItems && `pi-carousel__${this.carouselItems.length}-items`; carouselItems && `pi-carousel__${carouselItems.length}-items`;
const direction = this.direction(); const direction = this.direction();
const directionTypeClass = direction && `pi-carousel__${direction}`; const directionTypeClass = direction && `pi-carousel__${direction}`;
const colorClass = `pi-carousel__${this.color()}`; const colorClass = `pi-carousel__${this.color()}`;
@@ -131,8 +131,7 @@ export class PiCarouselComponent
/** /**
* carouselItems corresponds to the items that will be displayed in the carousel. * carouselItems corresponds to the items that will be displayed in the carousel.
*/ */
@ContentChildren(PiCarouselItemDirective, { descendants: true }) readonly carouselItems = contentChildren(PiCarouselItemDirective, { descendants: true });
carouselItems!: QueryList<PiCarouselItemDirective>;
/** /**
* carouselContentWrapper corresponds to the section that contains the carousel items and excludes the control buttons * carouselContentWrapper corresponds to the section that contains the carousel items and excludes the control buttons
@@ -216,13 +215,13 @@ export class PiCarouselComponent
* Returns true if the number of items is greater than the number of items per page. * Returns true if the number of items is greater than the number of items per page.
*/ */
get hasOverflow(): boolean { get hasOverflow(): boolean {
return this.carouselItems.length > this.itemsPerPage; return this.carouselItems().length > this.itemsPerPage;
} }
get canSwipeNext(): boolean { get canSwipeNext(): boolean {
return this.movePerPage() return this.movePerPage()
? this.currentItem + this.itemsPerPage < this.carouselItems.length ? this.currentItem + this.itemsPerPage < this.carouselItems().length
: this.currentItem < this.carouselItems.length - 1; : this.currentItem < this.carouselItems().length - 1;
} }
get canSwipePrev(): boolean { get canSwipePrev(): boolean {
@@ -258,7 +257,7 @@ export class PiCarouselComponent
this.setDragEventListeners(); this.setDragEventListeners();
} }
this.carouselItems.changes.pipe(takeUntil(this.destroyed$)).subscribe(() => { this.carouselItems().changes.pipe(takeUntil(this.destroyed$)).subscribe(() => {
this.cdr.markForCheck(); this.cdr.markForCheck();
}); });
} }
@@ -315,13 +314,13 @@ export class PiCarouselComponent
const itemsMobile = this.itemsMobile(); const itemsMobile = this.itemsMobile();
switch (this.device()) { switch (this.device()) {
case 'DESKTOP': case 'DESKTOP':
this.itemsPerPage = itemsDesktop ? itemsDesktop : this.carouselItems.length; this.itemsPerPage = itemsDesktop ? itemsDesktop : this.carouselItems().length;
break; break;
case 'TABLET': case 'TABLET':
this.itemsPerPage = itemsTablet ? itemsTablet : this.carouselItems.length; this.itemsPerPage = itemsTablet ? itemsTablet : this.carouselItems().length;
break; break;
case 'MOBILE': case 'MOBILE':
this.itemsPerPage = itemsMobile ? itemsMobile : this.carouselItems.length; this.itemsPerPage = itemsMobile ? itemsMobile : this.carouselItems().length;
break; break;
} }
} }
@@ -347,7 +346,7 @@ export class PiCarouselComponent
setPages(): void { setPages(): void {
let totalPages: number; let totalPages: number;
let pages: Array<number> = []; let pages: Array<number> = [];
totalPages = Math.ceil(this.carouselItems.length / this.itemsPerPage); totalPages = Math.ceil(this.carouselItems().length / this.itemsPerPage);
for (let i = 0; i < totalPages; i++) { for (let i = 0; i < totalPages; i++) {
pages.push(i); pages.push(i);
} }
@@ -472,13 +471,13 @@ export class PiCarouselComponent
private carouselTranslation(currentItem: number): void { private carouselTranslation(currentItem: number): void {
const type = this.type(); const type = this.type();
if (type === 'linear' && currentItem <= this.carouselItems.length - this.itemsPerPage) { if (type === 'linear' && currentItem <= this.carouselItems().length - this.itemsPerPage) {
this.carouselItemsTranslation(currentItem, `-${currentItem * 100}%`, this.direction()); this.carouselItemsTranslation(currentItem, `-${currentItem * 100}%`, this.direction());
} else if (type === 'symmetric') { } else if (type === 'symmetric') {
if (currentItem < this.numberOfSideItems) { if (currentItem < this.numberOfSideItems) {
// If we are at the beginning of the carousel // If we are at the beginning of the carousel
this.carouselItemsTranslation(currentItem, '0', this.direction()); this.carouselItemsTranslation(currentItem, '0', this.direction());
} else if (this.carouselItems.length - currentItem > this.numberOfSideItems) { } else if (this.carouselItems().length - currentItem > this.numberOfSideItems) {
// If we are at the end of the carousel // If we are at the end of the carousel
this.carouselItemsTranslation( this.carouselItemsTranslation(
currentItem, currentItem,
@@ -489,7 +488,7 @@ export class PiCarouselComponent
// If we are at the end of the carousel // If we are at the end of the carousel
this.carouselItemsTranslation( this.carouselItemsTranslation(
currentItem, currentItem,
`-${(this.carouselItems.length - this.itemsPerPage) * 100}%`, `-${(this.carouselItems().length - this.itemsPerPage) * 100}%`,
this.direction() this.direction()
); );
} }

View File

@@ -1,5 +1,5 @@
// Angular // Angular
import { Directive, HostBinding, Input, input } from '@angular/core'; import { Directive, HostBinding, input } from '@angular/core';
// Style definitions // Style definitions
import { ComponentFamilyClass, density, mode, styleType } from '../../styles'; import { ComponentFamilyClass, density, mode, styleType } from '../../styles';
@@ -11,7 +11,7 @@ export abstract class PiBaseChipComponent {
const colorClass = color && `pi-color__${color}`; const colorClass = color && `pi-color__${color}`;
const size = this.size(); const size = this.size();
const sizeClass = size && `pi-chip__size--${size}`; const sizeClass = size && `pi-chip__size--${size}`;
const affixClass = this.icon || this.value ? 'has-affix' : ''; const affixClass = this.icon() || this.value() ? 'has-affix' : '';
const affixAppearance = this.affixAppearance(); const affixAppearance = this.affixAppearance();
const appearanceClass = const appearanceClass =
affixAppearance && `pi-chip__affix-appearance--${affixAppearance}`; affixAppearance && `pi-chip__affix-appearance--${affixAppearance}`;
@@ -33,9 +33,9 @@ export abstract class PiBaseChipComponent {
].join(' '); ].join(' ');
} }
// Content // Content
@Input() label: string | undefined; readonly label = input<string>();
@Input() value: string | number | undefined; readonly value = input<string | number>();
@Input() icon: string | undefined; readonly icon = input<string>();
// Skin // Skin
readonly color = input<chipColor>(); readonly color = input<chipColor>();

View File

@@ -1,14 +1,14 @@
<span (click)="execute.emit()"> <span (click)="execute.emit()">
<div> <div>
<span class="pi-chip-label"> {{ label }}</span> <span class="pi-chip-label"> {{ label() }}</span>
<span class="pi-chip-affix" *ngIf="value || icon"> <span class="pi-chip-affix" *ngIf="value() || icon()">
<mat-icon <mat-icon
*ngIf="icon" *ngIf="icon()"
class="pi-chip-affix__icon" class="pi-chip-affix__icon"
[svgIcon]="icon" [svgIcon]="icon()"
[color]="color()" [color]="color()"
></mat-icon> ></mat-icon>
<span *ngIf="value" class="pi-chip-affix__value">{{ value }}</span> <span *ngIf="value()" class="pi-chip-affix__value">{{ value() }}</span>
</span> </span>
</div> </div>

View File

@@ -1,6 +1,6 @@
<span> <span>
<span class="pi-chip-label" *ngIf="label">{{ label }}</span> <span class="pi-chip-label" *ngIf="label()">{{ label() }}</span>
<span class="pi-chip-affix"> <span class="pi-chip-affix">
<mat-icon *ngIf="icon" [svgIcon]="icon" [color]="color()" class="pi-chip-affix__icon"></mat-icon> <mat-icon *ngIf="icon()" [svgIcon]="icon()" [color]="color()" class="pi-chip-affix__icon"></mat-icon>
</span> </span>
</span> </span>

View File

@@ -18,7 +18,7 @@ export class PiChipStatusComponent extends PiBaseChipComponent {
const statusValue = this.status(); const statusValue = this.status();
const statusClass = statusValue && `is-${statusValue}`; const statusClass = statusValue && `is-${statusValue}`;
const appearanceClass = `pi-chip-status__appearance--${this.appearance()}`; const appearanceClass = `pi-chip-status__appearance--${this.appearance()}`;
const hasLabelClass = !this.label ? 'pi-chip__single-icon' : ''; const hasLabelClass = !this.label() ? 'pi-chip__single-icon' : '';
const affixPosition = this.affixPosition(); const affixPosition = this.affixPosition();
const affixPositionClass = const affixPositionClass =
affixPosition && `pi-chip__affix-position--${affixPosition}`; affixPosition && `pi-chip__affix-position--${affixPosition}`;

View File

@@ -1,11 +1,11 @@
<span> <span>
<!--left --> <!--left -->
<label class="pi-chip-label" *ngIf="label">{{ label }}</label> <label class="pi-chip-label" *ngIf="label()">{{ label() }}</label>
<!--right --> <!--right -->
<aside> <aside>
<div class="pi-chip-affix"> <div class="pi-chip-affix">
<span class="pi-chip-affix__value">{{ value ? value : 'N/A' }}</span> <span class="pi-chip-affix__value">{{ value() ? value() : 'N/A' }}</span>
<span *ngIf="!!value" class="pi-chip-affix__unit">{{ unit() }}</span> <span *ngIf="!!value()" class="pi-chip-affix__unit">{{ unit() }}</span>
</div> </div>
</aside> </aside>
</span> </span>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input, input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, input } from '@angular/core';
import { PiBaseChipComponent } from '../base-chip.component'; import { PiBaseChipComponent } from '../base-chip.component';
import { unit } from '../../../util/models'; import { unit } from '../../../util/models';
import { NgIf } from '@angular/common'; import { NgIf } from '@angular/common';
@@ -12,8 +12,8 @@ import { NgIf } from '@angular/common';
}) })
export class PiChipValueComponent extends PiBaseChipComponent { export class PiChipValueComponent extends PiBaseChipComponent {
@HostBinding('class') get valueClasses(): string { @HostBinding('class') get valueClasses(): string {
const colorClass = this.value > 0 ? 'pi-color__success' : 'pi-color__warn'; const colorClass = this.value() > 0 ? 'pi-color__success' : 'pi-color__warn';
const hasLabelClass = !!this.label ? 'has-label' : ''; const hasLabelClass = !!this.label() ? 'has-label' : '';
const unitPosition = this.unitPosition(); const unitPosition = this.unitPosition();
const unitPositionClass = const unitPositionClass =
unitPosition && `pi-chip-value__unit-position--${unitPosition}`; unitPosition && `pi-chip-value__unit-position--${unitPosition}`;
@@ -26,7 +26,7 @@ export class PiChipValueComponent extends PiBaseChipComponent {
} }
// Content // Content
@Input() override value!: number; override readonly value = input.required<number>();
readonly unit = input.required<unit | string>(); readonly unit = input.required<unit | string>();
// Skin // Skin

View File

@@ -1,23 +1,23 @@
<span> <span>
<span class="pi-chip-label" *ngIf="label">{{ label }}</span> <span class="pi-chip-label" *ngIf="label()">{{ label() }}</span>
<span class="pi-chip-affix" *ngIf="icon || value"> <span class="pi-chip-affix" *ngIf="icon() || value()">
<ng-container *ngIf="icon"> <ng-container *ngIf="icon()">
<img <img
*ngIf="iconType() === 'custom'; else defaultIcon" *ngIf="iconType() === 'custom'; else defaultIcon"
src="/assets/icons/{{ icon }}.svg" src="/assets/icons/{{ icon() }}.svg"
class="pi-chip-affix__icon" class="pi-chip-affix__icon"
/> />
<ng-template #defaultIcon <ng-template #defaultIcon
><mat-icon ><mat-icon
[svgIcon]="icon" [svgIcon]="icon()"
[color]="appearance() !== 'fill' ? color() : ''" [color]="appearance() !== 'fill' ? color() : ''"
class="pi-chip-affix__icon" class="pi-chip-affix__icon"
></mat-icon ></mat-icon
></ng-template> ></ng-template>
</ng-container> </ng-container>
<span *ngIf="value" class="pi-chip-affix__value">{{ value }}</span> <span *ngIf="value()" class="pi-chip-affix__value">{{ value() }}</span>
</span> </span>
</span> </span>

View File

@@ -17,7 +17,7 @@ export class PiChipComponent extends PiBaseChipComponent {
const appearanceClass = appearance && `pi-chip__appearance--${appearance}`; const appearanceClass = appearance && `pi-chip__appearance--${appearance}`;
const iconType = this.iconType(); const iconType = this.iconType();
const iconTypeClass = iconType && `pi-chip__icon-type--${iconType}`; const iconTypeClass = iconType && `pi-chip__icon-type--${iconType}`;
const hasLabelClass = !this.label ? 'pi-chip__single-icon' : ''; const hasLabelClass = !this.label() ? 'pi-chip__single-icon' : '';
const affixPosition = this.affixPosition(); const affixPosition = this.affixPosition();
const affixPositionClass = const affixPositionClass =
affixPosition && `pi-chip__affix-position--${affixPosition}`; affixPosition && `pi-chip__affix-position--${affixPosition}`;

View File

@@ -1,11 +1,11 @@
<header> <header>
<div> <div>
<mat-icon *ngIf="icon; else valueTemplate" [svgIcon]="icon" [color]="color()"></mat-icon> <mat-icon *ngIf="icon(); else valueTemplate" [svgIcon]="icon()" [color]="color()"></mat-icon>
<ng-template #valueTemplate> <ng-template #valueTemplate>
<span>{{ value }}</span> <span>{{ value() }}</span>
</ng-template> </ng-template>
</div> </div>
<span *ngIf="icon && value">{{ value }}</span> <span *ngIf="icon() && value()">{{ value() }}</span>
</header> </header>
<label>{{ label() }}</label> <label>{{ label() }}</label>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input, input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, input } from '@angular/core';
import { ComponentFamilyClass, theming } from '../../../styles'; import { ComponentFamilyClass, theming } from '../../../styles';
import { NgIf } from '@angular/common'; import { NgIf } from '@angular/common';
import { MatIcon } from '@angular/material/icon'; import { MatIcon } from '@angular/material/icon';
@@ -14,8 +14,8 @@ export class PiTileComponent {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const color = this.color(); const color = this.color();
const colorClass = color && `pi-color__${color}`; const colorClass = color && `pi-color__${color}`;
const hasIconClass = !this.icon ? 'has-value' : ''; const hasIconClass = !this.icon() ? 'has-value' : '';
const hasIconAndValueClass = this.icon && this.value ? 'has-icon-and-value' : ''; const hasIconAndValueClass = this.icon() && this.value() ? 'has-icon-and-value' : '';
const flexDirectionValue = this.flexDirection(); const flexDirectionValue = this.flexDirection();
const flexDirection = flexDirectionValue && `pi-tile__flex-direction--${flexDirectionValue}`; const flexDirection = flexDirectionValue && `pi-tile__flex-direction--${flexDirectionValue}`;
return ['pi-tile', ComponentFamilyClass.Text, colorClass, flexDirection, hasIconClass].join( return ['pi-tile', ComponentFamilyClass.Text, colorClass, flexDirection, hasIconClass].join(
@@ -25,8 +25,8 @@ export class PiTileComponent {
// Content // Content
readonly label = input<string>(); readonly label = input<string>();
@Input() value: string | undefined; readonly value = input<string>();
@Input() icon: string | undefined; readonly icon = input<string>();
// Skin // Skin
readonly color = input<theming>('primary'); readonly color = input<theming>('primary');

View File

@@ -1,8 +1,8 @@
<!-- <!--
KEYS: KEYS:
--> -->
<header *ngIf="keysTemplateRef && mode() === 'list'" pi-grid-item context="keys"> <header *ngIf="keysTemplateRef() && mode() === 'list'" pi-grid-item context="keys">
<ng-container *ngTemplateOutlet="keysTemplateRef || defaultErrorTemplate"> </ng-container> <ng-container *ngTemplateOutlet="keysTemplateRef() || defaultErrorTemplate"> </ng-container>
</header> </header>
<!-- <!--
@@ -21,7 +21,7 @@
> >
<!-- Key --> <!-- Key -->
<ng-container <ng-container
*ngTemplateOutlet="mode() === 'grid' ? keysTemplateRef || defaultErrorTemplate : null" *ngTemplateOutlet="mode() === 'grid' ? keysTemplateRef() || defaultErrorTemplate : null"
> >
</ng-container> </ng-container>
@@ -52,7 +52,7 @@
> >
<section class="pi-grid-item__content"> <section class="pi-grid-item__content">
<!-- Keys --> <!-- Keys -->
<ng-container *ngTemplateOutlet="(mode() === 'grid' && keysTemplateRef) || null"> <ng-container *ngTemplateOutlet="(mode() === 'grid' && keysTemplateRef()) || null">
</ng-container> </ng-container>
<!-- Values --> <!-- Values -->

View File

@@ -1,7 +1,6 @@
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
ContentChild,
ElementRef, ElementRef,
HostBinding, HostBinding,
OnInit, OnInit,
@@ -74,8 +73,7 @@ export class PiDataGridComponent<T> {
readonly gridItemRef = viewChild<ElementRef<Element>>('gridItem'); readonly gridItemRef = viewChild<ElementRef<Element>>('gridItem');
@ContentChild('keysTemplate', { static: false }) readonly keysTemplateRef = contentChild<TemplateRef<unknown>>('keysTemplate');
keysTemplateRef: TemplateRef<unknown> | undefined;
readonly valuesTemplateRef = contentChild<TemplateRef<unknown>>('valuesTemplate'); readonly valuesTemplateRef = contentChild<TemplateRef<unknown>>('valuesTemplate');

View File

@@ -1,5 +1,5 @@
<ng-container *ngIf="!label(); else labelDividerTemplate"> <ng-container *ngIf="!label(); else labelDividerTemplate">
<pi-icon *ngIf="icon" [icon]="icon" [appearance]="iconType()" [isStroked]="isStroked()"></pi-icon> <pi-icon *ngIf="icon()" [icon]="icon()" [appearance]="iconType()" [isStroked]="isStroked()"></pi-icon>
<hr align="center" /> <hr align="center" />
</ng-container> </ng-container>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input, input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, input } from '@angular/core';
// Shared // Shared
import { alignment, gap, iconSize, textColor } from '../../../styles'; import { alignment, gap, iconSize, textColor } from '../../../styles';
@@ -19,7 +19,7 @@ export class PiDividerComponent {
const colorClass = color && `pi-color__${color}`; const colorClass = color && `pi-color__${color}`;
const gapValue = this.gap(); const gapValue = this.gap();
const gapClass = gapValue && `pi-divider__gap--${gapValue}`; const gapClass = gapValue && `pi-divider__gap--${gapValue}`;
const hasIconClass = this.icon ? 'has-icon' : ''; const hasIconClass = this.icon() ? 'has-icon' : '';
const heightClass = `pi-divider__height--${this.height()}`; const heightClass = `pi-divider__height--${this.height()}`;
const iconSizeValue = this.iconSize(); const iconSizeValue = this.iconSize();
const iconSizeClass = !!iconSizeValue && `pi-divider__icon-size--${iconSizeValue}`; const iconSizeClass = !!iconSizeValue && `pi-divider__icon-size--${iconSizeValue}`;
@@ -53,7 +53,7 @@ export class PiDividerComponent {
readonly direction = input<'horizontal' | 'vertical'>('horizontal'); readonly direction = input<'horizontal' | 'vertical'>('horizontal');
// Divider Icon // Divider Icon
@Input() icon!: string; readonly icon = input.required<string>();
readonly iconSize = input<iconSize>('x-large'); readonly iconSize = input<iconSize>('x-large');
readonly iconType = input<'fill' | 'default'>('default'); readonly iconType = input<'fill' | 'default'>('default');
readonly iconPosition = input<'before' | 'after'>('after'); readonly iconPosition = input<'before' | 'after'>('after');

View File

@@ -1,9 +1,10 @@
import { Component, HostBinding, Injector, Input, input, output } from '@angular/core'; import { Component, HostBinding, Injector, effect, input, output, ChangeDetectionStrategy } from '@angular/core';
import { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; import { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
import { ComponentFamilyClass } from '../../styles'; import { ComponentFamilyClass } from '../../styles';
import { PiFormControl } from '../../util/form-control'; import { PiFormControl } from '../../util/form-control';
@Component({ @Component({
changeDetection: ChangeDetectionStrategy.OnPush,
template: '', template: '',
providers: [ providers: [
{ {
@@ -36,26 +37,29 @@ export abstract class PiBaseFormFieldComponent extends PiFormControl {
readonly isControlStatusReactive = input(false); readonly isControlStatusReactive = input(false);
@Input() /**
set isDisabled(value: boolean) { * El estado deshabilitado tiene dos fuentes: esta entrada y el
this.disabled = value; * `setDisabledState` del ControlValueAccessor. `disabled` (heredado de
// Update control status reactively * PiFormControl) sigue siendo el estado efectivo que se debe leer.
if (this.isControlStatusReactive()) { */
if (this.disabled) { readonly isDisabled = input(false);
this.control.disable();
} else {
this.control.enable();
}
}
}
get isDisabled(): boolean {
return this.disabled;
}
readonly isActive = output<boolean>(); readonly isActive = output<boolean>();
constructor(injector: Injector) { constructor(injector: Injector) {
super(injector); super(injector);
effect(() => {
this.disabled = this.isDisabled();
// Update control status reactively
if (this.isControlStatusReactive()) {
if (this.disabled) {
this.control.disable();
} else {
this.control.enable();
}
}
});
} }
get control(): FormControl { get control(): FormControl {
@@ -75,6 +79,6 @@ export abstract class PiBaseFormFieldComponent extends PiFormControl {
} }
protected get isDisabledClass(): string { protected get isDisabledClass(): string {
return this.isDisabled ? 'is-disabled' : ''; return this.disabled ? 'is-disabled' : '';
} }
} }

View File

@@ -55,7 +55,7 @@ export class PiIncrementalNumberFieldComponent extends PiBaseFormFieldComponent
readonly showIncrementButtons = input<boolean>(true); readonly showIncrementButtons = input<boolean>(true);
incrementValue(): void { incrementValue(): void {
if (this.isDisabled) return if (this.disabled) return
let currentValue = Number(this.control.value); let currentValue = Number(this.control.value);
// Handle decimal numbers // Handle decimal numbers
const slot = this.slot(); const slot = this.slot();
@@ -67,7 +67,7 @@ export class PiIncrementalNumberFieldComponent extends PiBaseFormFieldComponent
} }
decrementValue(): void { decrementValue(): void {
if (this.isDisabled) return if (this.disabled) return
let currentValue = Number(this.control.value); let currentValue = Number(this.control.value);
// Handle decimal numbers // Handle decimal numbers
const slot = this.slot(); const slot = this.slot();
@@ -79,14 +79,14 @@ export class PiIncrementalNumberFieldComponent extends PiBaseFormFieldComponent
} }
isIncrementButtonDisabled(): boolean { isIncrementButtonDisabled(): boolean {
return this.isDisabled || this.control.value + this.slot() > this.max(); return this.disabled || this.control.value + this.slot() > this.max();
} }
isDecrementButtonDisabled(): boolean { isDecrementButtonDisabled(): boolean {
const min = this.min(); const min = this.min();
if (min || min === 0) { if (min || min === 0) {
return this.isDisabled || this.control.value - this.slot() < min; return this.disabled || this.control.value - this.slot() < min;
} }
return this.isDisabled; return this.disabled;
} }
} }

View File

@@ -4,7 +4,7 @@
<pi-suffix matSuffix> <pi-suffix matSuffix>
<pi-icon <pi-icon
[icon]="hide ? 'eye-off' : 'eye'" [icon]="hide ? 'eye-off' : 'eye'"
[color]="isDisabled ? 'grey' : 'black'" [color]="disabled ? 'grey' : 'black'"
size="medium" size="medium"
(click)="hide = !hide" (click)="hide = !hide"
[attr.aria-label]="'Hide password'" [attr.aria-label]="'Hide password'"

View File

@@ -1,6 +1,6 @@
<fieldset (click)="openField()" (keydown.enter)="searchClick.emit()"> <fieldset (click)="openField()" (keydown.enter)="searchClick.emit()">
<pi-icon <pi-icon
(click)="isExpanded ? searchClick.emit() : openField()" (click)="isExpanded() ? searchClick.emit() : openField()"
icon="search" icon="search"
color="primary" color="primary"
animation="clickable" animation="clickable"

View File

@@ -3,8 +3,10 @@ import {
Component, Component,
ElementRef, ElementRef,
HostBinding, HostBinding,
Input, Injector,
effect,
input, input,
model,
output, output,
viewChild viewChild
} from '@angular/core'; } from '@angular/core';
@@ -34,7 +36,7 @@ import { NgIf } from '@angular/common';
}) })
export class PiSearchFieldComponent extends PiBaseFormFieldComponent implements OnInit { export class PiSearchFieldComponent extends PiBaseFormFieldComponent implements OnInit {
@HostBinding('class') override get classes(): string { @HostBinding('class') override get classes(): string {
const isExpandedClass = this.isExpanded ? 'is-expanded' : 'is-collapsed'; const isExpandedClass = this.isExpanded() ? 'is-expanded' : 'is-collapsed';
return ['pi-field pi-form-field__search', isExpandedClass].join(' '); return ['pi-field pi-form-field__search', isExpandedClass].join(' ');
} }
@@ -46,34 +48,35 @@ export class PiSearchFieldComponent extends PiBaseFormFieldComponent implements
readonly isClosable = input(true); readonly isClosable = input(true);
private _isExpanded = false; /**
@Input() * `model` porque además de recibirse desde fuera, el propio componente la
get isExpanded() { * escribe (openField / closeField / valueChanges).
return this._isExpanded; */
} readonly isExpanded = model(false);
set isExpanded(value) { constructor(injector: Injector) {
this.isActive.emit(value); super(injector);
this._isExpanded = value; // Antes lo emitía el setter en cada asignación.
effect(() => this.isActive.emit(this.isExpanded()));
} }
ngOnInit(): void { ngOnInit(): void {
this.isExpanded = !!this.control.value || !this.isClosable(); this.isExpanded.set(!!this.control.value || !this.isClosable());
this.control.valueChanges.subscribe((value: any) => { this.control.valueChanges.subscribe((value: any) => {
if (value) { if (value) {
this.isExpanded = true; this.isExpanded.set(true);
} }
}); });
} }
openField(): void { openField(): void {
this.isExpanded = true; this.isExpanded.set(true);
this.inputRef().nativeElement.focus(); this.inputRef().nativeElement.focus();
} }
closeField(): void { closeField(): void {
if (this.isClosable()) { if (this.isClosable()) {
this.isExpanded = false; this.isExpanded.set(false);
} }
if (this.control.value) { if (this.control.value) {
this.control.reset(); this.control.reset();

View File

@@ -5,7 +5,7 @@
[formControl]="control" [formControl]="control"
[placeholder]="placeholder()" [placeholder]="placeholder()"
[name]="formControlName()" [name]="formControlName()"
[maxLength]="maxLength" [maxLength]="maxLength()"
matInput matInput
cdkTextareaAutosize cdkTextareaAutosize
#autosize="cdkTextareaAutosize" #autosize="cdkTextareaAutosize"
@@ -20,4 +20,4 @@
{{ text | translate }} {{ text | translate }}
</ng-container> </ng-container>
</mat-error> </mat-error>
<span *ngIf="maxLength !== 1e6">{{ control.value?.length || 0 }} / {{ maxLength }}</span> <span *ngIf="maxLength() !== 1e6">{{ control.value?.length || 0 }} / {{ maxLength() }}</span>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, input } from '@angular/core';
import { NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms'; import { NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';
import { PiBaseFormFieldComponent } from '../base-form-field.component'; import { PiBaseFormFieldComponent } from '../base-form-field.component';
import { MatFormField, MatLabel, MatError } from '@angular/material/form-field'; import { MatFormField, MatLabel, MatError } from '@angular/material/form-field';
@@ -37,5 +37,5 @@ import { TranslatePipe } from '@ngx-translate/core';
export class PiTextareaComponent extends PiBaseFormFieldComponent { export class PiTextareaComponent extends PiBaseFormFieldComponent {
@HostBinding('class') className = 'pi-form-field__textarea'; @HostBinding('class') className = 'pi-form-field__textarea';
@Input() maxLength: number = 1e6; readonly maxLength = input<number>(1e6);
} }

View File

@@ -1,5 +1,5 @@
<figure> <figure>
<a *ngIf="link; else image" [href]="link" target="_blank" <a *ngIf="link(); else image" [href]="link()" target="_blank"
><img ><img
src="https://d2m4265u67f2xu.cloudfront.net/assets/images/{{ pathImg() }}" src="https://d2m4265u67f2xu.cloudfront.net/assets/images/{{ pathImg() }}"
[alt]="pathImg() | sanitizerAltImage" [alt]="pathImg() | sanitizerAltImage"

View File

@@ -4,7 +4,6 @@ import {
Component, Component,
Directive, Directive,
HostBinding, HostBinding,
Input,
input input
} from '@angular/core'; } from '@angular/core';
@@ -65,7 +64,7 @@ export class PiHeadlineComponent {
} }
readonly alt = input<string>(); readonly alt = input<string>();
readonly hasContent = input(true); readonly hasContent = input(true);
@Input() link!: string | undefined; readonly link = input.required<string | undefined>();
readonly pathImg = input.required<string | undefined>(); readonly pathImg = input.required<string | undefined>();
// Skin // Skin

View File

@@ -1,7 +1,7 @@
<div> <div>
<mat-icon [svgIcon]="noteStatus?.icon"></mat-icon> <mat-icon [svgIcon]="noteStatus?.icon"></mat-icon>
<h5 s *ngIf="title">{{ title }}</h5> <h5 s *ngIf="title()">{{ title() }}</h5>
<small xs *ngIf="text">{{ text }}</small> <small xs *ngIf="text()">{{ text() }}</small>
<span><ng-content select="pi-note-label-content"></ng-content></span> <span><ng-content select="pi-note-label-content"></ng-content></span>
</div> </div>
<ng-content select="pi-note-action-content"></ng-content> <ng-content select="pi-note-action-content"></ng-content>

View File

@@ -3,7 +3,6 @@ import {
Component, Component,
Directive, Directive,
HostBinding, HostBinding,
Input,
OnInit, OnInit,
input input
} from '@angular/core'; } from '@angular/core';
@@ -80,8 +79,8 @@ export class PiNoteComponent implements OnInit {
} }
// Content // Content
@Input() title!: string | undefined; readonly title = input.required<string | undefined>();
@Input() text!: string | undefined; readonly text = input.required<string | undefined>();
// Behavior // Behavior
readonly status = input.required<status>(); readonly status = input.required<status>();

View File

@@ -1,8 +1,8 @@
<section> <section>
<pi-icon *ngIf="icon; else button" [icon]="icon" [color]="iconColor" size="large"></pi-icon> <pi-icon *ngIf="icon(); else button" [icon]="icon()" [color]="iconColor" size="large"></pi-icon>
<div> <div>
<h5 medium m>{{ message }}</h5> <h5 medium m>{{ message() }}</h5>
<span *ngIf="description" xs>{{ description }}</span> <span *ngIf="description()" xs>{{ description() }}</span>
</div> </div>
</section> </section>
<ng-template #button> <ng-template #button>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Inject, Input, Optional, forwardRef, input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, Inject, Optional, forwardRef, input } from '@angular/core';
import { MatSnackBarRef, MAT_SNACK_BAR_DATA } from '@angular/material/snack-bar'; import { MatSnackBarRef, MAT_SNACK_BAR_DATA } from '@angular/material/snack-bar';
import { styleType } from '../../../styles'; import { styleType } from '../../../styles';
import { NgIf } from '@angular/common'; import { NgIf } from '@angular/common';
@@ -28,20 +28,21 @@ export class PiSnackBarComponent {
@HostBinding('class') className = 'pi-snackbar'; @HostBinding('class') className = 'pi-snackbar';
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const TypeClass = this.type && `pi-snackbar__type--${this.type}`; const type = this.type();
const HasIconClass = this.icon && 'pi-snackbar__has--icon'; const TypeClass = type && `pi-snackbar__type--${type}`;
const IsDimsissibleClass = this.icon && 'pi-snackbar__is--dismissible'; const HasIconClass = this.icon() && 'pi-snackbar__has--icon';
const IsDimsissibleClass = this.icon() && 'pi-snackbar__is--dismissible';
return ['pi-snackbar', TypeClass, HasIconClass, IsDimsissibleClass].join(' '); return ['pi-snackbar', TypeClass, HasIconClass, IsDimsissibleClass].join(' ');
} }
@Input() message: string; readonly message = input<string>();
readonly text = input<string>(); readonly text = input<string>();
@Input() icon: string; readonly icon = input<string>();
@Input() type: 'success' | 'error' | 'info' = 'success'; readonly type = input<'success' | 'error' | 'info'>('success');
@Input() dismissible = false; readonly dismissible = input(false);
@Input() description: string; readonly description = input<string>();
iconColor!: styleType; iconColor!: styleType;
buttonColor!: styleType; buttonColor!: styleType;
@@ -56,7 +57,7 @@ export class PiSnackBarComponent {
this.type = data.type; this.type = data.type;
this.dismissible = data.dismissible; this.dismissible = data.dismissible;
this.description = data.description; this.description = data.description;
if (this.icon) { if (this.icon()) {
switch (data.type) { switch (data.type) {
case 'error': case 'error':
this.iconColor = 'warn'; this.iconColor = 'warn';

View File

@@ -5,7 +5,7 @@
density="200" density="200"
size="large" size="large"
(execute)="paginateToFirstPage()" (execute)="paginateToFirstPage()"
[disabled]="currentPage === 1" [disabled]="currentPage() === 1"
></pi-icon-button> ></pi-icon-button>
<pi-icon-button <pi-icon-button
@@ -15,7 +15,7 @@
density="200" density="200"
size="x-small" size="x-small"
[previous]="true" [previous]="true"
[disabled]="currentPage === 1" [disabled]="currentPage() === 1"
(execute)="paginateBack()" (execute)="paginateBack()"
>Previous</pi-icon-button >Previous</pi-icon-button
> >
@@ -33,7 +33,7 @@
density="200" density="200"
size="x-small" size="x-small"
(execute)="paginateNext()" (execute)="paginateNext()"
[disabled]="currentPage === totalPages() || isDisabled()" [disabled]="currentPage() === totalPages() || isDisabled()"
> >
Next Next
</pi-icon-button> </pi-icon-button>
@@ -41,7 +41,7 @@
<ng-template #defaultPaginator> <ng-template #defaultPaginator>
<span <span
*ngFor="let page of pages; let i = index" *ngFor="let page of pages; let i = index"
[class.is-active]="page === currentPage" [class.is-active]="page === currentPage()"
(click)="paginateTo(page)" (click)="paginateTo(page)"
> >
{{ page }}</span {{ page }}</span
@@ -50,7 +50,7 @@
<ng-template #advancedPaginator> <ng-template #advancedPaginator>
<div *ngFor="let page of firstPages; let i = index"> <div *ngFor="let page of firstPages; let i = index">
<span [class.is-active]="page === currentPage" (click)="paginateTo(page)"> <span [class.is-active]="page === currentPage()" (click)="paginateTo(page)">
{{ page }} {{ page }}
</span> </span>
</div> </div>
@@ -61,7 +61,7 @@
icon="more" icon="more"
></pi-icon> ></pi-icon>
<div *ngFor="let page of lastPages; let i = index"> <div *ngFor="let page of lastPages; let i = index">
<span [class.is-active]="page === currentPage" (click)="paginateTo(page)"> <span [class.is-active]="page === currentPage()" (click)="paginateTo(page)">
{{ page }} {{ page }}
</span> </span>
</div> </div>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, Input, OnInit, HostBinding, forwardRef, input, output } from '@angular/core'; import { ChangeDetectionStrategy, Component, OnInit, HostBinding, forwardRef, input, output } from '@angular/core';
import { PiIconButtonComponent } from '../../button/icon-button/icon-button.component'; import { PiIconButtonComponent } from '../../button/icon-button/icon-button.component';
import { NgIf, NgFor } from '@angular/common'; import { NgIf, NgFor } from '@angular/common';
import { PiIconComponent } from '../../icon/icon/icon.component'; import { PiIconComponent } from '../../icon/icon/icon.component';
@@ -19,7 +19,7 @@ import { PiIconComponent } from '../../icon/icon/icon.component';
}) })
export class PiPaginatorComponent implements OnInit { export class PiPaginatorComponent implements OnInit {
@HostBinding('class') className = 'pi-paginator'; @HostBinding('class') className = 'pi-paginator';
@Input() currentPage = 1; readonly currentPage = input(1);
readonly pageSize = input.required<number>(); readonly pageSize = input.required<number>();
readonly totalPages = input.required<number>(); readonly totalPages = input.required<number>();
readonly isDisabled = input.required<boolean>(); readonly isDisabled = input.required<boolean>();
@@ -59,9 +59,9 @@ export class PiPaginatorComponent implements OnInit {
} }
protected paginateBack(): void { protected paginateBack(): void {
if (this.currentPage > 1) { if (this.currentPage() > 1) {
const updatedCurrentPage = this.currentPage - 1; const updatedCurrentPage = this.currentPage() - 1;
if (this.firstPages.indexOf(this.currentPage) === 0) { if (this.firstPages.indexOf(this.currentPage()) === 0) {
this.firstPages = this.decrementPaginatorRangeBy(3); this.firstPages = this.decrementPaginatorRangeBy(3);
this.firstPagesRange = this.firstPagesRange - 3; this.firstPagesRange = this.firstPagesRange - 3;
} }
@@ -71,9 +71,9 @@ export class PiPaginatorComponent implements OnInit {
} }
protected paginateNext(): void { protected paginateNext(): void {
if (this.currentPage < this.totalPages()) { if (this.currentPage() < this.totalPages()) {
const updatedCurrentPage = this.currentPage + 1; const updatedCurrentPage = this.currentPage() + 1;
if (this.firstPages.indexOf(this.currentPage) >= 2) { if (this.firstPages.indexOf(this.currentPage()) >= 2) {
this.firstPagesRange = this.firstPagesRange + 3; this.firstPagesRange = this.firstPagesRange + 3;
this.firstPages = this.incrementPaginatorRangeBy(3); this.firstPages = this.incrementPaginatorRangeBy(3);
} }
@@ -83,11 +83,11 @@ export class PiPaginatorComponent implements OnInit {
} }
protected paginateTo(selectedPage: number): void { protected paginateTo(selectedPage: number): void {
if (this.isDisabled() && selectedPage > this.currentPage) { if (this.isDisabled() && selectedPage > this.currentPage()) {
return; return;
} else { } else {
this.currentPage = selectedPage; this.currentPage = selectedPage;
this.changePage.emit(this.currentPage); this.changePage.emit(this.currentPage());
} }
} }
@@ -102,7 +102,7 @@ export class PiPaginatorComponent implements OnInit {
this.firstPagesRange = this.firstPagesRange + 3; this.firstPagesRange = this.firstPagesRange + 3;
this.firstPages = this.incrementPaginatorRangeBy(3); this.firstPages = this.incrementPaginatorRangeBy(3);
this.currentPage = this.firstPages[0]; this.currentPage = this.firstPages[0];
this.changePage.emit(this.currentPage); this.changePage.emit(this.currentPage());
} }
} }

View File

@@ -4,7 +4,7 @@
<header> <header>
<div> <div>
<h2 m medium>{{ title() }}</h2> <h2 m medium>{{ title() }}</h2>
<h4 pi-grey light xs *ngIf="subtitle">{{ subtitle }}</h4> <h4 pi-grey light xs *ngIf="subtitle()">{{ subtitle() }}</h4>
</div> </div>
<aside> <aside>
<ng-content select="pi-panel-top-content"></ng-content> <ng-content select="pi-panel-top-content"></ng-content>
@@ -18,7 +18,7 @@
[hasPadding]="false" [hasPadding]="false"
[isResponsive]="false" [isResponsive]="false"
(execute)="onEdit()" (execute)="onEdit()"
[class]="isCollapsed ? 'is-collapsed' : 'is-expanded'" [class]="isCollapsed() ? 'is-collapsed' : 'is-expanded'"
[text]="'common.edit' | translate" [text]="'common.edit' | translate"
></pi-icon-button> ></pi-icon-button>
</aside> </aside>
@@ -28,7 +28,7 @@
<ng-content select="pi-panel-center-content"></ng-content> <ng-content select="pi-panel-center-content"></ng-content>
<!-- Bottom & Expansion --> <!-- Bottom & Expansion -->
<section [class]="isCollapsed ? 'is-collapsed' : 'is-expanded'"> <section [class]="isCollapsed() ? 'is-collapsed' : 'is-expanded'">
<ng-content select="pi-panel-expandable-content"></ng-content> <ng-content select="pi-panel-expandable-content"></ng-content>
</section> </section>
</form> </form>
@@ -44,7 +44,7 @@
<pi-icon <pi-icon
(click)="toggle()" (click)="toggle()"
icon="chevron-down" icon="chevron-down"
[class.is-collapsed]="isCollapsed" [class.is-collapsed]="isCollapsed()"
size="small" size="small"
></pi-icon> ></pi-icon>
</aside> </aside>
@@ -55,7 +55,7 @@
<!-- Bottom & Expansion --> <!-- Bottom & Expansion -->
<section <section
[class]="isCollapsed ? 'is-collapsed' : 'is-expanded'" [class]="isCollapsed() ? 'is-collapsed' : 'is-expanded'"
class="pi-panel-content pi-panel-content__expandable" class="pi-panel-content pi-panel-content__expandable"
> >
<ng-container *ngTemplateOutlet="expandableContentRef()"> </ng-container> <ng-container *ngTemplateOutlet="expandableContentRef()"> </ng-container>

View File

@@ -2,7 +2,6 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
HostBinding, HostBinding,
Input,
OnChanges, OnChanges,
OnInit, OnInit,
SimpleChanges, SimpleChanges,
@@ -60,9 +59,9 @@ export class PiExpansionPanelComponent extends PiBasePanelComponent implements O
} }
readonly type = input<'form' | 'custom'>('form'); readonly type = input<'form' | 'custom'>('form');
@Input() isCollapsed = false; readonly isCollapsed = input(false);
readonly title = input<string>(); readonly title = input<string>();
@Input() subtitle: string | undefined; readonly subtitle = input<string>();
readonly formGroup = input.required<FormGroup>(); readonly formGroup = input.required<FormGroup>();
readonly edit = output<void>(); readonly edit = output<void>();
@@ -82,12 +81,12 @@ export class PiExpansionPanelComponent extends PiBasePanelComponent implements O
toggle(): void { toggle(): void {
const status = this.status(); const status = this.status();
if (status !== 'disabled' && status !== 'info') this.isCollapsed = !this.isCollapsed; if (status !== 'disabled' && status !== 'info') this.isCollapsed = !this.isCollapsed();
} }
onEdit(): void { onEdit(): void {
if (this.status() !== 'disabled') { if (this.status() !== 'disabled') {
if (this.isCollapsed) this.toggle(); if (this.isCollapsed()) this.toggle();
this.edit.emit(); this.edit.emit();
} }
} }

View File

@@ -1,9 +1,9 @@
<section> <section>
<!-- Top --> <!-- Top -->
<header *ngIf="title"> <header *ngIf="title()">
<div> <div>
<h2 m medium>{{ title }}</h2> <h2 m medium>{{ title() }}</h2>
<h4 pi-grey light xs *ngIf="subtitle">{{ subtitle }}</h4> <h4 pi-grey light xs *ngIf="subtitle()">{{ subtitle() }}</h4>
</div> </div>
<ng-content select="pi-panel-top-content"></ng-content> <ng-content select="pi-panel-top-content"></ng-content>
</header> </header>

View File

@@ -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 { ComponentFamilyClass } from '../../../styles';
import { PiBasePanelComponent } from '../base-panel.component'; import { PiBasePanelComponent } from '../base-panel.component';
import { NgIf } from '@angular/common'; import { NgIf } from '@angular/common';
@@ -48,6 +48,6 @@ export class PiPanelComponent extends PiBasePanelComponent {
].join(' '); ].join(' ');
} }
@Input() title: string | undefined; readonly title = input<string>();
@Input() subtitle: string | undefined; readonly subtitle = input<string>();
} }

View File

@@ -1,18 +1,14 @@
import { import {
Component, Component,
ContentChildren,
ElementRef, ElementRef,
HostBinding, HostBinding,
Injector, Injector,
Input,
QueryList,
Renderer2, Renderer2,
TemplateRef, TemplateRef,
ViewChildren,
input, input,
contentChild, contentChild,
contentChildren, contentChildren,
viewChildren viewChildren, ChangeDetectionStrategy
} from '@angular/core'; } from '@angular/core';
import { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms'; import { FormControl, NG_VALUE_ACCESSOR } from '@angular/forms';
import { PiFormControl } from '../../util/form-control'; import { PiFormControl } from '../../util/form-control';
@@ -25,6 +21,7 @@ import {
} from './entities/select.model'; } from './entities/select.model';
@Component({ @Component({
changeDetection: ChangeDetectionStrategy.OnPush,
template: '', template: '',
providers: [ providers: [
{ {
@@ -105,7 +102,7 @@ export class PiBaseSelectComponent extends PiFormControl {
/** /**
* An optional icon for the select. * An optional icon for the select.
*/ */
@Input() icon: string | undefined; readonly icon = input<string>();
/** /**
* An optional icon for the select. * 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. * 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. * These components are typically passed into this component's content slot from outside.
*/ */
@ContentChildren(PiSelectOptionComponent) childrenOptionsComponents: readonly childrenOptionsComponents = contentChildren(PiSelectOptionComponent);
| QueryList<PiSelectOptionComponent>
| undefined;
/** /**
* A query list of native element references for the child option components * 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. * within this component's template.
* These are the default option components provided by the component itself. * These are the default option components provided by the component itself.
*/ */
@ViewChildren(PiSelectOptionComponent) readonly viewChildOptionsComponents = viewChildren(PiSelectOptionComponent);
viewChildOptionsComponents: QueryList<PiSelectOptionComponent>;
/** /**
* A query list of native element references for the child option components * A query list of native element references for the child option components

View File

@@ -145,8 +145,8 @@ export class PiSelectCarouselComponent extends PiBaseSelectComponent implements
ngAfterContentInit(): void { ngAfterContentInit(): void {
this.control.setValue(this.selectedOption); this.control.setValue(this.selectedOption);
merge( merge(
this.childrenOptionsComponents?.changes || [], this.childrenOptionsComponents()?.changes || [],
this.viewChildOptionsComponents?.changes || [], this.viewChildOptionsComponents()?.changes || [],
this.control.valueChanges this.control.valueChanges
).subscribe(() => { ).subscribe(() => {
Promise.resolve().then(() => { Promise.resolve().then(() => {
@@ -185,12 +185,13 @@ export class PiSelectCarouselComponent extends PiBaseSelectComponent implements
let childrenOptionsElementRef: QueryList<ElementRef>; let childrenOptionsElementRef: QueryList<ElementRef>;
// Check if we have child options that are content projected from a parent component. // Check if we have child options that are content projected from a parent component.
if (this.childrenOptionsComponents && this.childrenOptionsComponents.length > 0) { const childrenOptionsComponents = this.childrenOptionsComponents();
childrenOptionsComponent = this.childrenOptionsComponents.toArray(); if (childrenOptionsComponents && childrenOptionsComponents.length > 0) {
childrenOptionsComponent = childrenOptionsComponents;
childrenOptionsElementRef = this.childrenOptionsEl(); childrenOptionsElementRef = this.childrenOptionsEl();
} else { } else {
// If not, it means they're internally defined (default), so we fetch from viewChildren. // If not, it means they're internally defined (default), so we fetch from viewChildren.
childrenOptionsComponent = this.viewChildOptionsComponents?.toArray(); childrenOptionsComponent = this.viewChildOptionsComponents;
childrenOptionsElementRef = this.viewChildOptionsEl(); 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 the current child option matches the selected option, highlight it.
if (isSelected) { if (isSelected) {
this._renderer.addClass(childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected'); this._renderer.addClass(childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected');
childOption.isSelected = true; childOption.isSelected.set(true);
} else { } else {
// Otherwise, ensure it is not highlighted. // Otherwise, ensure it is not highlighted.
this._renderer.removeClass( this._renderer.removeClass(
childrenOptionsElementRef.get(index)?.nativeElement, childrenOptionsElementRef.get(index)?.nativeElement,
'is-selected' 'is-selected'
); );
childOption.isSelected = false; childOption.isSelected.set(false);
} }
}); });
this._cdr.markForCheck(); this._cdr.markForCheck();

View File

@@ -5,8 +5,8 @@
<!-- Button --> <!-- Button -->
<div> <div>
<pi-icon <pi-icon
*ngIf="icon" *ngIf="icon()"
[icon]="icon" [icon]="icon()"
class="pi-select-picked-option__custom-icon" class="pi-select-picked-option__custom-icon"
color="primary" color="primary"
size="medium" size="medium"
@@ -31,7 +31,7 @@
<ng-container *ngSwitchCase="'fill'"> <ng-container *ngSwitchCase="'fill'">
<fieldset (click)="onToggleDropdown()" class="pi-select-picked-option"> <fieldset (click)="onToggleDropdown()" class="pi-select-picked-option">
<pi-icon <pi-icon
[icon]="icon" [icon]="icon()"
class="pi-select-picked-option__custom-icon" class="pi-select-picked-option__custom-icon"
color="primary" color="primary"
size="x-large" size="x-large"
@@ -115,8 +115,8 @@
<!-- Only visible in a header of Button --> <!-- Only visible in a header of Button -->
<pi-icon <pi-icon
*ngIf="icon" *ngIf="icon()"
[icon]="icon" [icon]="icon()"
class="pi-select-options-menu-picked-option__custom-icon" class="pi-select-options-menu-picked-option__custom-icon"
[color]="color()" [color]="color()"
size="medium" size="medium"

View File

@@ -115,7 +115,9 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements
/** /**
* Retrieves a list of selected options. * Retrieves a list of selected options.
*/ */
@Input() get selectedOptions(): Array<any> { // 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<any> {
if (!this.controlValue) return []; if (!this.controlValue) return [];
return this.options().filter((option) => _.some(this.controlValue, option)); return this.options().filter((option) => _.some(this.controlValue, option));
} }
@@ -162,8 +164,8 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements
ngAfterContentInit(): void { ngAfterContentInit(): void {
this.control.setValue(this.selectedOptions); this.control.setValue(this.selectedOptions);
merge( merge(
this.childrenOptionsComponents?.changes || [], this.childrenOptionsComponents()?.changes || [],
this.viewChildOptionsComponents?.changes || [], this.viewChildOptionsComponents()?.changes || [],
this.control.valueChanges this.control.valueChanges
).subscribe(() => { ).subscribe(() => {
Promise.resolve().then(() => { Promise.resolve().then(() => {
@@ -247,12 +249,13 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements
let childrenOptionsElementRef: QueryList<ElementRef>; let childrenOptionsElementRef: QueryList<ElementRef>;
// Check if we have child options that are content projected from a parent component. // Check if we have child options that are content projected from a parent component.
if (this.childrenOptionsComponents && this.childrenOptionsComponents.length > 0) { const childrenOptionsComponents = this.childrenOptionsComponents();
childrenOptionsComponent = this.childrenOptionsComponents.toArray(); if (childrenOptionsComponents && childrenOptionsComponents.length > 0) {
childrenOptionsComponent = childrenOptionsComponents;
childrenOptionsElementRef = this.childrenOptionsEl(); childrenOptionsElementRef = this.childrenOptionsEl();
} else { } else {
// If not, it means they're internally defined (default), so we fetch from viewChildren. // If not, it means they're internally defined (default), so we fetch from viewChildren.
childrenOptionsComponent = this.viewChildOptionsComponents.toArray(); childrenOptionsComponent = this.viewChildOptionsComponents;
childrenOptionsElementRef = this.viewChildOptionsEl(); childrenOptionsElementRef = this.viewChildOptionsEl();
} }
@@ -262,13 +265,13 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements
); );
if (isSelected) { if (isSelected) {
this._renderer.addClass(childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected'); this._renderer.addClass(childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected');
childOption.isSelected = true; childOption.isSelected.set(true);
} else { } else {
this._renderer.removeClass( this._renderer.removeClass(
childrenOptionsElementRef.get(index)?.nativeElement, childrenOptionsElementRef.get(index)?.nativeElement,
'is-selected' 'is-selected'
); );
childOption.isSelected = false; childOption.isSelected.set(false);
} }
}); });
@@ -297,12 +300,13 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements
let childrenOptionsElementRef: QueryList<ElementRef>; let childrenOptionsElementRef: QueryList<ElementRef>;
// Check if we have child options that are content projected from a parent component. // Check if we have child options that are content projected from a parent component.
if (this.childrenOptionsComponents && this.childrenOptionsComponents.length > 0) { const childrenOptionsComponents = this.childrenOptionsComponents();
childrenOptionsComponent = this.childrenOptionsComponents.toArray(); if (childrenOptionsComponents && childrenOptionsComponents.length > 0) {
childrenOptionsComponent = childrenOptionsComponents;
childrenOptionsElementRef = this.childrenOptionsEl(); childrenOptionsElementRef = this.childrenOptionsEl();
} else { } else {
// If not, it means they're internally defined (default), so we fetch from viewChildren. // If not, it means they're internally defined (default), so we fetch from viewChildren.
childrenOptionsComponent = this.viewChildOptionsComponents.toArray(); childrenOptionsComponent = this.viewChildOptionsComponents;
childrenOptionsElementRef = this.viewChildOptionsEl(); childrenOptionsElementRef = this.viewChildOptionsEl();
} }

View File

@@ -7,21 +7,21 @@
class="pi-select-option__radio-button" class="pi-select-option__radio-button"
size="x-small" size="x-small"
icon="bullet" icon="bullet"
[isActive]="isSelected" [isActive]="isSelected()"
[color]="color()" [color]="color()"
[isDisabled]="isDisabled()" [isDisabled]="isDisabled()"
></pi-radio-button> ></pi-radio-button>
<span> <span>
<span class="pi-select-option__label">{{ label() | translate }}</span> <span class="pi-select-option__label">{{ label() | translate }}</span>
<span class="pi-select-option__description" *ngIf="description">{{ <span class="pi-select-option__description" *ngIf="description()">{{
description | translate description() | translate
}}</span> }}</span>
</span> </span>
</div> </div>
<!-- initials and icon cannot coexist --> <!-- initials and icon cannot coexist -->
<span *ngIf="initials" class="pi-select-option__initials">{{ initials | translate }}</span> <span *ngIf="initials()" class="pi-select-option__initials">{{ initials() | translate }}</span>
<mat-icon *ngIf="icon" [svgIcon]="icon" [color]="color()"></mat-icon> <mat-icon *ngIf="icon()" [svgIcon]="icon()" [color]="color()"></mat-icon>
</ng-container> </ng-container>
<!-- Column multiple --> <!-- Column multiple -->
@@ -29,16 +29,16 @@
<div> <div>
<mat-checkbox <mat-checkbox
class="pi-select-option__checkbox" class="pi-select-option__checkbox"
[checked]="isSelected" [checked]="isSelected()"
[color]="color()" [color]="color()"
></mat-checkbox> ></mat-checkbox>
<span class="pi-select-option__label">{{ label() | translate }}</span> <span class="pi-select-option__label">{{ label() | translate }}</span>
</div> </div>
<span *ngIf="initials" class="pi-select-option__initials">{{ initials | translate }}</span> <span *ngIf="initials()" class="pi-select-option__initials">{{ initials() | translate }}</span>
<mat-icon <mat-icon
class="pi-select-option__icon" class="pi-select-option__icon"
*ngIf="icon" *ngIf="icon()"
[svgIcon]="icon" [svgIcon]="icon()"
[color]="color()" [color]="color()"
></mat-icon> ></mat-icon>
</ng-container> </ng-container>
@@ -46,19 +46,19 @@
<!-- Flex wrap (No Picked Option) --> <!-- Flex wrap (No Picked Option) -->
<ng-container *ngSwitchCase="'flex-wrap'"> <ng-container *ngSwitchCase="'flex-wrap'">
<mat-icon <mat-icon
*ngIf="icon" *ngIf="icon()"
class="pi-select-option__icon" class="pi-select-option__icon"
[svgIcon]="icon" [svgIcon]="icon()"
[color]="color()" [color]="color()"
></mat-icon> ></mat-icon>
<span class="pi-select-option__label">{{ label() | translate }}</span> <span class="pi-select-option__label">{{ label() | translate }}</span>
<span class="pi-select-option__initials" *ngIf="initials">{{ initials | translate }}</span> <span class="pi-select-option__initials" *ngIf="initials()">{{ initials() | translate }}</span>
<pi-radio-button <pi-radio-button
*ngIf="hasRadio()" *ngIf="hasRadio()"
class="pi-select-option__radio-button" class="pi-select-option__radio-button"
icon="check" icon="check"
[isActive]="isSelected" [isActive]="isSelected()"
[color]="isSelected ? color() : 'white'" [color]="isSelected() ? color() : 'white'"
[isDisabled]="isDisabled()" [isDisabled]="isDisabled()"
></pi-radio-button> ></pi-radio-button>
</ng-container> </ng-container>

View File

@@ -1,10 +1,9 @@
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef,
Component, Component,
HostBinding, HostBinding,
Input, input,
input model
} from '@angular/core'; } from '@angular/core';
import { SelectColor, SelectOptionFlexDirection, actionIcon } from '../entities/select.model'; import { SelectColor, SelectOptionFlexDirection, actionIcon } from '../entities/select.model';
import { NgSwitch, NgSwitchCase, NgIf } from '@angular/common'; import { NgSwitch, NgSwitchCase, NgIf } from '@angular/common';
@@ -35,9 +34,9 @@ export class PiSelectOptionComponent {
const flexDirection = this.flexDirection(); const flexDirection = this.flexDirection();
const flexDirectionClass = const flexDirectionClass =
flexDirection && `pi-select-option__flex-direction--${flexDirection}`; 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 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 hasRadioClass = this.hasRadio() ? 'has-radio' : '';
const textAlign = this.textAlign(); const textAlign = this.textAlign();
const justifyClass = textAlign && `pi-select-option__text-align--${textAlign}`; const justifyClass = textAlign && `pi-select-option__text-align--${textAlign}`;
@@ -63,9 +62,9 @@ export class PiSelectOptionComponent {
// Content // Content
readonly id = input<string>(); readonly id = input<string>();
readonly label = input<string>(); readonly label = input<string>();
@Input() description: string | undefined; readonly description = input<string>();
@Input() initials: string | undefined; readonly initials = input<string>();
@Input() icon: string | undefined; readonly icon = input<string>();
readonly actionIcon = input<actionIcon>('chevron-forward'); readonly actionIcon = input<actionIcon>('chevron-forward');
readonly hasRadio = input(false); readonly hasRadio = input(false);
@@ -88,25 +87,13 @@ export class PiSelectOptionComponent {
*/ */
readonly type = input<'single' | 'multiple' | 'flex-wrap'>('single'); readonly type = input<'single' | 'multiple' | 'flex-wrap'>('single');
private _isSelected = false; /**
* `model` en vez de `input` porque los selects padre lo asignan de forma
@Input() set isSelected(newValue: boolean) { * imperativa sobre las instancias hijas (`childOption.isSelected.set(...)`).
if (this._isSelected !== newValue) { * El `markForCheck()` manual que hacía el setter anterior ya no hace falta:
this._isSelected = newValue; * al ser una señal, Angular marca el componente automáticamente.
this.onValueChanged(); */
} readonly isSelected = model(false);
}
get isSelected() {
return this._isSelected;
}
readonly isDisabled = input.required<boolean>(); readonly isDisabled = input.required<boolean>();
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();
}
} }

View File

@@ -23,7 +23,7 @@
<ng-container *ngSwitchCase="'fill'"> <ng-container *ngSwitchCase="'fill'">
<fieldset class="pi-select-picked-option"> <fieldset class="pi-select-picked-option">
<pi-icon <pi-icon
[icon]="icon" [icon]="icon()"
color="primary" color="primary"
size="x-large" size="x-large"
appearance="fill" appearance="fill"
@@ -91,8 +91,8 @@
<header *ngIf="pickedOptionType() === 'button'" class="pi-select-options-menu__picked-option"> <header *ngIf="pickedOptionType() === 'button'" class="pi-select-options-menu__picked-option">
<!-- Only visible in a header of Button --> <!-- Only visible in a header of Button -->
<pi-icon <pi-icon
*ngIf="icon" *ngIf="icon()"
[icon]="icon" [icon]="icon()"
class="pi-select-options-menu-picked-option__custom-icon" class="pi-select-options-menu-picked-option__custom-icon"
color="primary" color="primary"
size="medium" size="medium"

View File

@@ -161,8 +161,8 @@ export class PiSelectPeriodComponent extends PiBaseSelectComponent implements Af
ngAfterContentInit(): void { ngAfterContentInit(): void {
this.control.setValue(this.selectedOption); this.control.setValue(this.selectedOption);
merge( merge(
this.childrenOptionsComponents?.changes || [], this.childrenOptionsComponents()?.changes || [],
this.viewChildOptionsComponents?.changes || [], this.viewChildOptionsComponents()?.changes || [],
this.control.valueChanges this.control.valueChanges
).subscribe(() => { ).subscribe(() => {
Promise.resolve().then(() => { Promise.resolve().then(() => {
@@ -197,12 +197,13 @@ export class PiSelectPeriodComponent extends PiBaseSelectComponent implements Af
let childrenOptionsElementRef: QueryList<ElementRef>; let childrenOptionsElementRef: QueryList<ElementRef>;
// Check if we have child options that are content projected from a parent component. // Check if we have child options that are content projected from a parent component.
if (this.childrenOptionsComponents && this.childrenOptionsComponents.length > 0) { const childrenOptionsComponents = this.childrenOptionsComponents();
childrenOptionsComponent = this.childrenOptionsComponents.toArray(); if (childrenOptionsComponents && childrenOptionsComponents.length > 0) {
childrenOptionsComponent = childrenOptionsComponents;
childrenOptionsElementRef = this.childrenOptionsEl(); childrenOptionsElementRef = this.childrenOptionsEl();
} else { } else {
// If not, it means they're internally defined (default), so we fetch from viewChildren. // If not, it means they're internally defined (default), so we fetch from viewChildren.
childrenOptionsComponent = this.viewChildOptionsComponents?.toArray(); childrenOptionsComponent = this.viewChildOptionsComponents;
childrenOptionsElementRef = this.viewChildOptionsEl(); 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 the current child option matches the selected option, highlight it.
if (isSelected) { if (isSelected) {
this._renderer.addClass(childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected'); this._renderer.addClass(childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected');
childOption.isSelected = true; childOption.isSelected.set(true);
} else { } else {
// Otherwise, ensure it is not highlighted. // Otherwise, ensure it is not highlighted.
this._renderer.removeClass( this._renderer.removeClass(
childrenOptionsElementRef.get(index)?.nativeElement, childrenOptionsElementRef.get(index)?.nativeElement,
'is-selected' 'is-selected'
); );
childOption.isSelected = false; childOption.isSelected.set(false);
} }
}); });
this._cdr.markForCheck(); this._cdr.markForCheck();

View File

@@ -23,7 +23,7 @@
<ng-container *ngSwitchCase="'fill'"> <ng-container *ngSwitchCase="'fill'">
<fieldset class="pi-select-picked-option"> <fieldset class="pi-select-picked-option">
<pi-icon <pi-icon
[icon]="icon" [icon]="icon()"
color="primary" color="primary"
size="x-large" size="x-large"
appearance="fill" appearance="fill"
@@ -91,8 +91,8 @@
<header *ngIf="pickedOptionType() === 'button'" class="pi-select-options-menu__picked-option"> <header *ngIf="pickedOptionType() === 'button'" class="pi-select-options-menu__picked-option">
<!-- Only visible in a header of Button --> <!-- Only visible in a header of Button -->
<pi-icon <pi-icon
*ngIf="icon" *ngIf="icon()"
[icon]="icon" [icon]="icon()"
class="pi-select-options-menu-picked-option__custom-icon" class="pi-select-options-menu-picked-option__custom-icon"
color="primary" color="primary"
size="medium" size="medium"

View File

@@ -151,8 +151,8 @@ export class PiSelectComponent extends PiBaseSelectComponent implements AfterCon
ngAfterContentInit(): void { ngAfterContentInit(): void {
this.control.setValue(this.selectedOption); this.control.setValue(this.selectedOption);
merge( merge(
this.childrenOptionsComponents?.changes || [], this.childrenOptionsComponents()?.changes || [],
this.viewChildOptionsComponents?.changes || [], this.viewChildOptionsComponents()?.changes || [],
this.control.valueChanges this.control.valueChanges
).subscribe(() => { ).subscribe(() => {
Promise.resolve().then(() => { Promise.resolve().then(() => {
@@ -187,12 +187,13 @@ export class PiSelectComponent extends PiBaseSelectComponent implements AfterCon
let childrenOptionsElementRef: QueryList<ElementRef>; let childrenOptionsElementRef: QueryList<ElementRef>;
// Check if we have child options that are content projected from a parent component. // Check if we have child options that are content projected from a parent component.
if (this.childrenOptionsComponents && this.childrenOptionsComponents.length > 0) { const childrenOptionsComponents = this.childrenOptionsComponents();
childrenOptionsComponent = this.childrenOptionsComponents.toArray(); if (childrenOptionsComponents && childrenOptionsComponents.length > 0) {
childrenOptionsComponent = childrenOptionsComponents;
childrenOptionsElementRef = this.childrenOptionsEl(); childrenOptionsElementRef = this.childrenOptionsEl();
} else { } else {
// If not, it means they're internally defined (default), so we fetch from viewChildren. // If not, it means they're internally defined (default), so we fetch from viewChildren.
childrenOptionsComponent = this.viewChildOptionsComponents?.toArray(); childrenOptionsComponent = this.viewChildOptionsComponents;
childrenOptionsElementRef = this.viewChildOptionsEl(); 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 the current child option matches the selected option, highlight it.
if (isSelected) { if (isSelected) {
this._renderer.addClass(childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected'); this._renderer.addClass(childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected');
childOption.isSelected = true; childOption.isSelected.set(true);
} else { // Otherwise, ensure it is not highlighted. } else { // Otherwise, ensure it is not highlighted.
this._renderer.removeClass( this._renderer.removeClass(
childrenOptionsElementRef.get(index)?.nativeElement, childrenOptionsElementRef.get(index)?.nativeElement,
'is-selected' 'is-selected'
); );
childOption.isSelected = false; childOption.isSelected.set(false);
} }
}); });
this._cdr.markForCheck(); this._cdr.markForCheck();

View File

@@ -2,7 +2,6 @@
import { import {
Directive, Directive,
HostBinding, HostBinding,
Input,
input, input,
viewChild viewChild
} from '@angular/core'; } from '@angular/core';
@@ -24,7 +23,7 @@ export abstract class PiBaseSliderComponent {
} }
// Content // Content
@Input() label: string; readonly label = input<string>();
readonly thumbLabel = input<boolean>(true); readonly thumbLabel = input<boolean>(true);
readonly minValue = input<number>(0); readonly minValue = input<number>(0);
readonly maxValue = input<number>(100); readonly maxValue = input<number>(100);

View File

@@ -1,5 +1,5 @@
<span *ngIf="label"> <span *ngIf="label()">
<label>{{ label }}</label> <label>{{ label() }}</label>
<span>{{ control?.value }} {{ unit() }}</span> <span>{{ control?.value }} {{ unit() }}</span>
</span> </span>
<mat-slider <mat-slider

View File

@@ -13,8 +13,8 @@
[isDisabled]="isTabWrapperScrolledToStart" [isDisabled]="isTabWrapperScrolledToStart"
></pi-icon> ></pi-icon>
<section #tabWrapper> <section #tabWrapper>
<ng-container *ngIf="tabButtons; else customTabElements"> <ng-container *ngIf="tabButtons(); else customTabElements">
<ng-container *ngFor="let tab of tabButtons; let i = index"> <ng-container *ngFor="let tab of tabButtons(); let i = index">
<article <article
[class]="getTabElementClass(i, tab)" [class]="getTabElementClass(i, tab)"
(click)="tab.isDisabled ? null : onTabChange(i)" (click)="tab.isDisabled ? null : onTabChange(i)"
@@ -51,7 +51,7 @@
></pi-icon> ></pi-icon>
</header> </header>
<main> <main>
<ng-container *ngFor="let tab of tabContentItems; let i = index"> <ng-container *ngFor="let tab of tabContentItems(); let i = index">
<section [class]="getTabContentClass(i)"> <section [class]="getTabContentClass(i)">
<ng-container *ngTemplateOutlet="tab.templateRef"> </ng-container> <ng-container *ngTemplateOutlet="tab.templateRef"> </ng-container>
</section> </section>

View File

@@ -4,12 +4,10 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
ContentChildren,
ElementRef, ElementRef,
HostBinding, HostBinding,
Input, Input,
OnDestroy, OnDestroy,
QueryList,
Renderer2, Renderer2,
input, input,
output, output,
@@ -48,16 +46,9 @@ export class PiTabComponent implements AfterViewInit, AfterViewChecked, OnDestro
return ['pi-tab', hideTabsClass, appearanceClass, justifyClass].join(' '); return ['pi-tab', hideTabsClass, appearanceClass, justifyClass].join(' ');
} }
@Input() tabButtons: Array<TabButton>; readonly tabButtons = input<Array<TabButton>>();
private _defaultSelectedTab: number = 0;
@Input() readonly defaultSelectedTab = input(0);
get defaultSelectedTab(): number {
return this._defaultSelectedTab;
}
set defaultSelectedTab(value: number) {
this._defaultSelectedTab = value;
this.onTabChange(value);
}
readonly appearance = input<'default' | 'fill' | 'outline'>('default'); readonly appearance = input<'default' | 'fill' | 'outline'>('default');
@@ -72,8 +63,7 @@ export class PiTabComponent implements AfterViewInit, AfterViewChecked, OnDestro
readonly tabChange = output<number>(); readonly tabChange = output<number>();
@ContentChildren(PiTabContentDirective, { descendants: true }) readonly tabContentItems = contentChildren(PiTabContentDirective, { descendants: true });
tabContentItems!: QueryList<PiTabContentDirective>;
readonly tabElementItems = contentChildren(PiTabElementDirective, { descendants: true }); readonly tabElementItems = contentChildren(PiTabElementDirective, { descendants: true });
@@ -89,7 +79,12 @@ export class PiTabComponent implements AfterViewInit, AfterViewChecked, OnDestro
private cdr: ChangeDetectorRef, private cdr: ChangeDetectorRef,
private el: ElementRef, private el: ElementRef,
private renderer: Renderer2 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[] { get tabContentNativeElements(): HTMLElement[] {
return Array.from(this.el.nativeElement.querySelectorAll('.pi-tab__content')); return Array.from(this.el.nativeElement.querySelectorAll('.pi-tab__content'));
@@ -112,11 +107,9 @@ export class PiTabComponent implements AfterViewInit, AfterViewChecked, OnDestro
} }
ngAfterViewInit(): void { ngAfterViewInit(): void {
this.tabContentItems.changes.subscribe(() => { this.tabContentItems().changes.subscribe(() => {
this.cdr.markForCheck(); this.cdr.markForCheck();
}); });
this.onTabChange(this.defaultSelectedTab);
} }
ngAfterViewChecked(): void { ngAfterViewChecked(): void {

View File

@@ -1,5 +1,5 @@
// Angular // Angular
import { Directive, HostBinding, Input, OnInit, input } from '@angular/core'; import { Directive, HostBinding, OnInit, input } from '@angular/core';
// Style definitions // Style definitions
import { import {
@@ -46,15 +46,15 @@ export abstract class PiBaseTextViewerComponent implements OnInit {
readonly justify = input<justify>('flex-start'); readonly justify = input<justify>('flex-start');
readonly fontWeight = input<fontWeight>('300'); readonly fontWeight = input<fontWeight>('300');
readonly fontStyle = input<fontStyle>('normal'); readonly fontStyle = input<fontStyle>('normal');
@Input() icon: string; readonly icon = input<string>();
readonly iconSize = input<iconSize>('medium'); readonly iconSize = input<iconSize>('medium');
readonly iconColor = input<styleType>(); readonly iconColor = input<styleType>();
readonly iconAppearance = input<'stroke' | 'fill-stroke' | 'fill' | 'default' | 'custom'>('default'); readonly iconAppearance = input<'stroke' | 'fill-stroke' | 'fill' | 'default' | 'custom'>('default');
readonly isDisabled = input(false); readonly isDisabled = input(false);
// Content // Content
@Input() label!: string | unknown; readonly label = input.required<string | unknown>();
@Input() value!: string; readonly value = input.required<string>();
ngOnInit(): void { ngOnInit(): void {
this.alignmentClass; this.alignmentClass;
@@ -114,15 +114,15 @@ export abstract class PiBaseTextViewerComponent implements OnInit {
} }
protected get hasIconClass(): string | boolean { protected get hasIconClass(): string | boolean {
return this.icon ? 'has-icon' : ''; return this.icon() ? 'has-icon' : '';
} }
protected get hasLabelClass(): string | boolean { protected get hasLabelClass(): string | boolean {
return this.label ? 'has-label' : ''; return this.label() ? 'has-label' : '';
} }
protected get hasValueClass(): string | boolean { protected get hasValueClass(): string | boolean {
return this.value ? 'has-value' : ''; return this.value() ? 'has-value' : '';
} }
protected get iconSizeClass(): string | undefined { protected get iconSizeClass(): string | undefined {

View File

@@ -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]', }) @Directive({ selector: '[pi-text-centered]', })
export class PiTextCenteredDirective { export class PiTextCenteredDirective {
@@ -16,9 +16,9 @@ export class PiTextGroupDirective {
@Directive({ selector: '[pi-disabled]', }) @Directive({ selector: '[pi-disabled]', })
export class PiDisabledDirective implements OnChanges { export class PiDisabledDirective implements OnChanges {
@HostBinding('style.opacity') get opacity(): number { @HostBinding('style.opacity') get opacity(): number {
return this.isDisabled ? 0.2 : 1; return this.isDisabled() ? 0.2 : 1;
} }
@Input() isDisabled: boolean; readonly isDisabled = input<boolean>();
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
if (changes) { if (changes) {

View File

@@ -1,6 +1,6 @@
<mat-icon *ngIf="icon" [svgIcon]="icon" alt="icon" size="size"></mat-icon> <mat-icon *ngIf="icon()" [svgIcon]="icon()" alt="icon" size="size"></mat-icon>
<article> <article>
<label *ngIf="label">{{ label }}</label> <label *ngIf="label()">{{ label() }}</label>
<ng-container [ngSwitch]="getTemplate()"> <ng-container [ngSwitch]="getTemplate()">
<!-- Route template --> <!-- Route template -->
<a <a
@@ -11,15 +11,15 @@
[target]="target()" [target]="target()"
[queryParams]="queryParams()" [queryParams]="queryParams()"
> >
{{ value }} {{ value() }}
</a> </a>
<!-- Link template --> <!-- Link template -->
<a *ngSwitchCase="'link'" [href]="link()" [target]="target()"> <a *ngSwitchCase="'link'" [href]="link()" [target]="target()">
<span>{{ value }}</span> <span>{{ value() }}</span>
</a> </a>
<!-- OnClick template --> <!-- OnClick template -->
<a *ngSwitchCase="'onClick'"> <a *ngSwitchCase="'onClick'">
<span>{{ value }}</span> <span>{{ value() }}</span>
</a> </a>
</ng-container> </ng-container>
</article> </article>

View File

@@ -1,7 +1,7 @@
<mat-icon *ngIf="icon" [svgIcon]="icon" alt="icon" size="size" [color]="iconColor()"></mat-icon> <mat-icon *ngIf="icon()" [svgIcon]="icon()" alt="icon" size="size" [color]="iconColor()"></mat-icon>
<article> <article>
<label>{{ label }}</label> <label>{{ label() }}</label>
<span *ngIf="value"> <span *ngIf="value()">
{{ value }} {{ value() }}
</span> </span>
</article> </article>

View File

@@ -1,4 +1,4 @@
<h4 *ngIf="title">{{ title }}</h4> <h4 *ngIf="title()">{{ title() }}</h4>
<form [formGroup]="formGroup()"> <form [formGroup]="formGroup()">
<ng-content> </ng-content> <ng-content> </ng-content>
</form> </form>

View File

@@ -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 { FormGroup, ReactiveFormsModule } from '@angular/forms';
import { grid, WrapperTypeClass } from '../../../styles'; import { grid, WrapperTypeClass } from '../../../styles';
@@ -24,7 +24,7 @@ export class PiFormWrapper extends PiBaseWrapper {
} }
readonly formGroup = input.required<FormGroup>(); readonly formGroup = input.required<FormGroup>();
@Input() title: string | undefined; readonly title = input<string>();
readonly subtitle = input<string>(); readonly subtitle = input<string>();
override readonly grid = input<grid>('none'); override readonly grid = input<grid>('none');

View File

@@ -1,12 +1,12 @@
<header *ngIf="backLink"> <header *ngIf="backLink()">
<pi-text-viewer <pi-text-viewer
[label]="backLink.label" [label]="backLink().label"
icon="arrow-back" icon="arrow-back"
iconSize="medium" iconSize="medium"
[routerLink]="backLink.route" [routerLink]="backLink().route"
[routerLinkActive]="['isActive']" [routerLinkActive]="['isActive']"
[routerLinkActiveOptions]="{ exact: true }" [routerLinkActiveOptions]="{ exact: true }"
[queryParams]="backLink.queryParams" [queryParams]="backLink().queryParams"
></pi-text-viewer> ></pi-text-viewer>
<ng-content></ng-content> <ng-content></ng-content>

View File

@@ -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 { alignment, WrapperTypeClass } from '../../../styles';
import { PiBaseWrapper } from '../base.wrapper'; import { PiBaseWrapper } from '../base.wrapper';
@@ -49,7 +49,7 @@ export class PiHeaderWrapper extends PiBaseWrapper {
].join(' '); ].join(' ');
} }
@Input() backLink: backLink | undefined; readonly backLink = input<backLink>();
override readonly alignment = input<alignment>('flex-end'); override readonly alignment = input<alignment>('flex-end');
} }

View File

@@ -1,5 +1,5 @@
// Angular // Angular
import { Directive, HostBinding, Input, TemplateRef, input, contentChild } from '@angular/core'; import { Directive, HostBinding, TemplateRef, input, contentChild } from '@angular/core';
// Style definitions // Style definitions
import { MenuEntry, MenuItemText } from './entities/menu-entry.model'; import { MenuEntry, MenuItemText } from './entities/menu-entry.model';
@@ -12,16 +12,7 @@ export abstract class PiBaseMenuComponent {
readonly label = input<string>(); readonly label = input<string>();
_menuItems: Array<MenuEntry> = []; readonly menuItems = input<Array<MenuEntry>>([]);
@Input()
set menuItems(menuItems: MenuEntry[]) {
this._menuItems = menuItems;
}
get menuItems(): MenuEntry[] {
return this._menuItems;
}
/** /**
* Parent Menu * Parent Menu

View File

@@ -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]', }) @Directive({ selector: '[pi-disabled]', })
export class PiDisabledDirective implements OnChanges { export class PiDisabledDirective implements OnChanges {
@HostBinding('style.opacity') get opacity(): number { @HostBinding('style.opacity') get opacity(): number {
return this.isDisabled ? 0.33 : 1; return this.isDisabled() ? 0.33 : 1;
} }
@HostBinding('class') get classes(): string | boolean { @HostBinding('class') get classes(): string | boolean {
return [this.isDisabled && 'is-disabled'].join(' '); return [this.isDisabled() && 'is-disabled'].join(' ');
} }
@Input() isDisabled: boolean; readonly isDisabled = input<boolean>();
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
if (changes) { if (changes) {
@@ -21,10 +21,10 @@ export class PiDisabledDirective implements OnChanges {
@Directive({ selector: '[is-active]', }) @Directive({ selector: '[is-active]', })
export class PiIsActiveDirective { export class PiIsActiveDirective {
@HostBinding('class') get classes(): string | boolean { @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<boolean>();
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
if (changes) { if (changes) {
@@ -36,10 +36,10 @@ export class PiIsActiveDirective {
@Directive({ selector: '[is-visited]', }) @Directive({ selector: '[is-visited]', })
export class PiIsVisitedDirective { export class PiIsVisitedDirective {
@HostBinding('class') get classes(): string | boolean { @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<boolean>();
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
if (changes) { if (changes) {