refactor: migra decoradores a signals (input/output/viewChild/contentChild)

This commit is contained in:
2026-07-27 22:01:52 +02:00
parent c84de249bf
commit c046e6ac23
140 changed files with 1679 additions and 1536 deletions

View File

@@ -1,7 +1,7 @@
<!-- HEADER OF THE WHOLE ACCORDION --> <!-- HEADER OF THE WHOLE ACCORDION -->
<ng-content select="pi-accordion-header"></ng-content> <ng-content select="pi-accordion-header"></ng-content>
<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]="
@@ -13,7 +13,7 @@
> >
<!-- TOPBAR --> <!-- TOPBAR -->
<div <div
*ngIf="item.isConfidential === true" *ngIf="item.isConfidential() === true"
class="pi-accordion-item-content pi-accordion-item-content__topbar" class="pi-accordion-item-content pi-accordion-item-content__topbar"
pi-dynamic-color pi-dynamic-color
[color]="isItemExpanded(i) ? 'highlighted' : 'grey'" [color]="isItemExpanded(i) ? 'highlighted' : 'grey'"
@@ -49,11 +49,11 @@
<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)"
appearance="ghost" appearance="ghost"
@@ -69,8 +69,8 @@
<ng-template #defaultIcon> <ng-template #defaultIcon>
<pi-icon <pi-icon
icon="chevron-forward" icon="chevron-forward"
[size]="appearance === 'outline' ? 'x-small' : 'small'" [size]="appearance() === 'outline' ? 'x-small' : 'small'"
[color]="color === 'white' ? 'grey' : color" [color]="color() === 'white' ? 'grey' : color()"
></pi-icon> ></pi-icon>
</ng-template> </ng-template>
</aside> </aside>
@@ -78,7 +78,7 @@
<!-- SUMMARY --> <!-- SUMMARY -->
<section <section
*ngIf="!!item?.summaryContent && !isItemExpanded(i)" *ngIf="!!item?.summaryContent() && !isItemExpanded(i)"
class="pi-accordion-item-content pi-accordion-item-content__summary" class="pi-accordion-item-content pi-accordion-item-content__summary"
> >
<ng-container <ng-container
@@ -124,7 +124,7 @@
[ngTemplateOutlet]="item?.customHeader?.templateRef || defaultHeader" [ngTemplateOutlet]="item?.customHeader?.templateRef || defaultHeader"
[ngTemplateOutletContext]="{ $implicit: item, index: i, isExpanded: isItemExpanded(i) }" [ngTemplateOutletContext]="{ $implicit: item, index: i, isExpanded: isItemExpanded(i) }"
></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"
@@ -133,8 +133,8 @@
<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'"
[color]="color === 'white' ? 'grey' : color" [color]="color() === 'white' ? 'grey' : color()"
></pi-icon> ></pi-icon>
</aside> </aside>
</header> </header>

View File

@@ -4,13 +4,13 @@ import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
ContentChildren, ContentChildren,
EventEmitter,
HostBinding, HostBinding,
Input, Input,
OnChanges, OnChanges,
Output,
QueryList, QueryList,
SimpleChanges, SimpleChanges,
input,
output
} 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';
@@ -41,25 +41,27 @@ import { TranslatePipe } from '@ngx-translate/core';
}) })
export class PiAccordionComponent implements OnChanges, AfterContentInit { export class PiAccordionComponent implements OnChanges, AfterContentInit {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const colorClass = `pi-accordion__${this.color}`; const colorClass = `pi-accordion__${this.color()}`;
const appearance = this.appearance();
const appearanceClass = const appearanceClass =
this.appearance !== 'default' && `pi-accordion__appearance--${this.appearance}`; appearance !== 'default' && `pi-accordion__appearance--${appearance}`;
const direction = this.direction();
const directionClass = const directionClass =
this.direction !== 'default' && `pi-accordion__direction--${this.direction}`; direction !== 'default' && `pi-accordion__direction--${direction}`;
const paddingClass = `pi-accordion__padding--${this.padding}`; const paddingClass = `pi-accordion__padding--${this.padding()}`;
return ['pi-accordion', colorClass, appearanceClass, directionClass, paddingClass].join(' '); return ['pi-accordion', colorClass, appearanceClass, directionClass, paddingClass].join(' ');
} }
@Input() expandedItemIndex!: number; @Input() expandedItemIndex!: number;
@Input() appearance: 'default' | 'outline' | 'fill' = 'default'; readonly appearance = input<'default' | 'outline' | 'fill'>('default');
@Input() color: styleType = 'white'; readonly color = input<styleType>('white');
@Input() direction: 'default' | 'horizontal' = 'default'; readonly direction = input<'default' | 'horizontal'>('default');
@Input() hasForm = false; readonly hasForm = input(false);
@Input() padding: padding = 'm'; readonly padding = input<padding>('m');
@Input() hasChevron = true; readonly hasChevron = input(true);
@Output() expandedItemIndexChange: EventEmitter<number> = new EventEmitter<number>(); readonly expandedItemIndexChange = output<number>();
@ContentChildren(PiAccordionItemDirective) items!: QueryList<PiAccordionItemDirective>; @ContentChildren(PiAccordionItemDirective) items!: QueryList<PiAccordionItemDirective>;
@@ -81,7 +83,7 @@ export class PiAccordionComponent implements OnChanges, AfterContentInit {
} }
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) { if (this.expandedItemIndex === index) {
this.expandedItemIndex = null; this.expandedItemIndex = null;
@@ -96,12 +98,12 @@ export class PiAccordionComponent implements OnChanges, AfterContentInit {
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 statusClass = item.status && 'has-status' + ' ' + `is-${item.status}`;
const isConfidentialClass = item.isConfidential ? 'is-confidential' : ''; const isConfidentialClass = item.isConfidential() ? 'is-confidential' : '';
return [expandedClass, statusClass, isConfidentialClass].join(' '); return [expandedClass, statusClass, isConfidentialClass].join(' ');
} }
getHeaderTextColor(): textColor { getHeaderTextColor(): textColor {
switch (this.color) { switch (this.color()) {
case 'primary': case 'primary':
return 'primary'; return 'primary';
case 'accent': case 'accent':

View File

@@ -1,10 +1,11 @@
import { import {
AfterViewInit, AfterViewInit,
ContentChild,
Directive, Directive,
HostBinding, HostBinding,
Input, Input,
TemplateRef, TemplateRef,
input,
contentChild
} from '@angular/core'; } from '@angular/core';
import { status } from '../../../util/status'; import { status } from '../../../util/status';
@@ -50,14 +51,13 @@ export class PiAccordionFooterDirective {
@Directive({ selector: 'pi-accordion-item', }) @Directive({ selector: 'pi-accordion-item', })
export class PiAccordionItemDirective { export class PiAccordionItemDirective {
@Input() title = ''; readonly title = input('');
@Input() subtitle = ''; readonly subtitle = input('');
@Input() status!: status; @Input() status!: status;
@Input() statusLabel: string; readonly statusLabel = input<string>();
@Input() statusIcon: string; readonly statusIcon = input<string>();
@Input() isConfidential = false; readonly isConfidential = input(false);
@ContentChild(PiAccordionItemContentDirective) content: PiAccordionItemContentDirective; readonly content = contentChild(PiAccordionItemContentDirective);
@ContentChild(PiAccordionSummaryContentDirective) readonly summaryContent = contentChild(PiAccordionSummaryContentDirective);
summaryContent: PiAccordionSummaryContentDirective; readonly customHeader = contentChild(PiAccordionItemHeaderDirective);
@ContentChild(PiAccordionItemHeaderDirective) customHeader: PiAccordionItemHeaderDirective;
} }

View File

@@ -1,15 +1,15 @@
<main <main
(click)="execute.emit()" (click)="execute.emit()"
pi-background-image pi-background-image
[isResponsive]="isImgResponsive" [isResponsive]="isImgResponsive()"
[image]="pathImg" [image]="pathImg()"
[extension]="extension" [extension]="extension()"
position="right center" position="right center"
size="contain" size="contain"
> >
<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 -->
@@ -19,7 +19,7 @@
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
> >

View File

@@ -1,4 +1,4 @@
import { Component, EventEmitter, HostBinding, Input, Output } from '@angular/core'; import { Component, HostBinding, Input, input, output } 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';
@@ -17,22 +17,22 @@ import { PiIconButtonComponent } from '../../button/icon-button/icon-button.comp
}) })
export class PiBannerComponent { export class PiBannerComponent {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const modeClass = `pi-banner__mode--${this.mode}`; const modeClass = `pi-banner__mode--${this.mode()}`;
return ['pi-banner', modeClass].join(' '); return ['pi-banner', modeClass].join(' ');
} }
// Skin // Skin
@Input() mode: 'dark' | 'light' = 'dark'; readonly mode = input<'dark' | 'light'>('dark');
// Text // Text
@Input() title: string | undefined; readonly title = input<string>();
@Input() message: string | undefined; @Input() message: string | undefined;
@Input() buttonText: string | undefined; @Input() buttonText: string | undefined;
// Image // Image
@Input() pathImg: string | undefined; readonly pathImg = input<string>();
@Input() isImgResponsive: boolean = true; readonly isImgResponsive = input<boolean>(true);
@Input() extension: 'jpg' | 'svg' | 'gif' = 'gif'; readonly extension = input<'jpg' | 'svg' | 'gif'>('gif');
@Output() execute: EventEmitter<MouseEvent> = new EventEmitter(); readonly execute = output<MouseEvent>();
} }

View File

@@ -1,7 +1,7 @@
<button <button
#button #button
(click)="onClickHandler($event)" (click)="onClickHandler($event)"
[disabled]="disabled" [disabled]="disabled()"
[class]="[ [class]="[
'pi-button', 'pi-button',
'pi-button__action', 'pi-button__action',
@@ -16,7 +16,7 @@
> >
<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>
<path <path
d="M1 1C4.5 4 5 4.38484 5 4.5C5 4.61516 4.5 5 1 8" d="M1 1C4.5 4 5 4.38484 5 4.5C5 4.61516 4.5 5 1 8"

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, Input, 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';
@@ -17,27 +17,30 @@ export class PiActionButtonComponent extends PiBaseButtonComponent {
); );
} }
@Input() previous = false; readonly previous = input(false);
@Input() override size: buttonSize = 'medium'; override readonly size = input<buttonSize>('medium');
@Input() text: string | undefined; @Input() text: string | undefined;
@Input() hasPadding = true; readonly hasPadding = input(true);
@Input() hasIcon = true; readonly hasIcon = input(true);
@Input() label: string | undefined; readonly label = input<string>();
@Input() buttonType: 'default' | 'menu-item' = 'default'; readonly buttonType = input<'default' | 'menu-item'>('default');
@Input() mode: mode = 'dark'; readonly mode = input<mode>('dark');
@Input() direction: 'back' | 'forward' = 'forward'; readonly direction = input<'back' | 'forward'>('forward');
@Input() fontWeight: '300' | '400' | '600' = '600'; readonly fontWeight = input<'300' | '400' | '600'>('600');
get buttonColorClass(): string | null { get buttonColorClass(): string | null {
return this.color ? `pi-color__${this.color}` : null; const color = this.color();
return color ? `pi-color__${color}` : null;
} }
get directionClass(): string | null { get directionClass(): string | null {
return this.direction ? `pi-action-button__direction--${this.direction}` : null; const direction = this.direction();
return direction ? `pi-action-button__direction--${direction}` : null;
} }
override get sizeClass(): string | null { override get sizeClass(): string | null {
return this.size ? `pi-button__${this.size}` : null; const size = this.size();
return size ? `pi-button__${size}` : null;
} }
get textClass(): string | null { get textClass(): string | null {
@@ -45,26 +48,30 @@ export class PiActionButtonComponent extends PiBaseButtonComponent {
} }
get fontWeightClass(): string | null { get fontWeightClass(): string | null {
return this.fontWeight ? `pi-action-button__font-weight--${this.fontWeight}` : ''; const fontWeight = this.fontWeight();
return fontWeight ? `pi-action-button__font-weight--${fontWeight}` : '';
} }
get hasPaddingClass(): string | null { get hasPaddingClass(): string | null {
return this.hasPadding ? null : 'pi-button__trimmed'; return this.hasPadding() ? null : 'pi-button__trimmed';
} }
get hasIconClass(): string | null { get hasIconClass(): string | null {
return this.hasIcon ? null : 'has-icon'; return this.hasIcon() ? null : 'has-icon';
} }
override get appearanceClass(): string | null { override get appearanceClass(): string | null {
return this.appearance !== 'fill' ? `pi-button__appearance--${this.appearance}` : null; const appearance = this.appearance();
return appearance !== 'fill' ? `pi-button__appearance--${appearance}` : null;
} }
get buttonTypeClass(): string | null { get buttonTypeClass(): string | null {
return this.buttonType !== 'default' ? `pi-button__type--${this.buttonType}` : null; const buttonType = this.buttonType();
return buttonType !== 'default' ? `pi-button__type--${buttonType}` : null;
} }
get modeClass(): string | null { get modeClass(): string | null {
return this.mode && `pi-color__mode--${this.mode}`; const modeValue = this.mode();
return modeValue && `pi-color__mode--${modeValue}`;
} }
} }

View File

@@ -1,18 +1,18 @@
// Angular // Angular
import { Directive, EventEmitter, Input, OnInit, Output } from '@angular/core'; import { Directive, OnInit, input, output } from '@angular/core';
import { buttonSize, styleType, buttonStyle, flexDirection } from '../../styles'; import { buttonSize, styleType, buttonStyle, flexDirection } from '../../styles';
// Style definitions // Style definitions
@Directive() @Directive()
export abstract class PiBaseButtonComponent implements OnInit { export abstract class PiBaseButtonComponent implements OnInit {
@Input() size: buttonSize = 'medium'; readonly size = input<buttonSize>('medium');
@Input() color: styleType = 'primary'; readonly color = input<styleType>('primary');
@Input() appearance: buttonStyle = 'fill'; readonly appearance = input<buttonStyle>('fill');
@Input() disabled = false; readonly disabled = input(false);
@Input() allowPropagation!: boolean; readonly allowPropagation = input.required<boolean>();
@Input() flexDirection!: flexDirection; readonly flexDirection = input.required<flexDirection>();
@Output() execute: EventEmitter<MouseEvent> = new EventEmitter(); readonly execute = output<MouseEvent>();
ngOnInit(): void { ngOnInit(): void {
this.sizeClass; this.sizeClass;
@@ -20,19 +20,19 @@ export abstract class PiBaseButtonComponent implements OnInit {
} }
protected get colorClass(): string { protected get colorClass(): string {
return `pi-color__${this.color}`; return `pi-color__${this.color()}`;
} }
protected get sizeClass(): string { protected get sizeClass(): string {
return `pi-button__${this.size}`; return `pi-button__${this.size()}`;
} }
protected get appearanceClass(): string { protected get appearanceClass(): string {
return `pi-button__appearance--${this.appearance}`; return `pi-button__appearance--${this.appearance()}`;
} }
protected get flexDirectionClass(): string { protected get flexDirectionClass(): string {
return `pi-button__flex-direction--${this.flexDirection}`; return `pi-button__flex-direction--${this.flexDirection()}`;
} }
protected get disabledClass(): string { protected get disabledClass(): string {
@@ -40,8 +40,8 @@ export abstract class PiBaseButtonComponent implements OnInit {
} }
onClickHandler(event: any): void { onClickHandler(event: any): void {
if (!this.disabled) { if (!this.disabled()) {
if (!this.allowPropagation) { if (!this.allowPropagation()) {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
} }

View File

@@ -1,14 +1,14 @@
import { Directive, HostBinding, Input } from '@angular/core'; import { Directive, HostBinding, input } from '@angular/core';
import { buttonSize } from '../../styles'; import { buttonSize } from '../../styles';
// Style // Style
@Directive({ selector: '[pi-ghost]', }) @Directive({ selector: '[pi-ghost]', })
export class PiGhostButtonDirective { export class PiGhostButtonDirective {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const hasPaddingClass = this.hasPadding ? '' : 'pi-button__trimmed'; const hasPaddingClass = this.hasPadding() ? '' : 'pi-button__trimmed';
return ['pi-button', 'pi-button__appearance--ghost', hasPaddingClass].join(' '); return ['pi-button', 'pi-button__appearance--ghost', hasPaddingClass].join(' ');
} }
@Input() hasPadding = true; readonly hasPadding = input(true);
} }
@Directive({ selector: '[pi-outline]', }) @Directive({ selector: '[pi-outline]', })
@@ -24,10 +24,11 @@ export class PiButtonSizeDirective {
return this.sizeClass; return this.sizeClass;
} }
@Input() size!: buttonSize; readonly size = input.required<buttonSize>();
protected get sizeClass(): string { protected get sizeClass(): string {
return !!this.size && `pi-button__${this.size}`; const size = this.size();
return !!size && `pi-button__${size}`;
} }
} }

View File

@@ -1,10 +1,9 @@
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
EventEmitter,
HostBinding, HostBinding,
Input, Input,
Output, output
} from '@angular/core'; } from '@angular/core';
import { ComponentFamilyClass } from '../../../styles'; import { ComponentFamilyClass } from '../../../styles';
import { PiBaseButtonComponent } from '../base-button.component'; import { PiBaseButtonComponent } from '../base-button.component';
@@ -24,5 +23,5 @@ export class PiChevronButtonComponent extends PiBaseButtonComponent {
); );
} }
@Output() toggle: EventEmitter<void> = new EventEmitter<void>(); readonly toggle = output<void>();
} }

View File

@@ -1,10 +1,9 @@
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
EventEmitter,
HostBinding, HostBinding,
Input, input,
Output, output
} from '@angular/core'; } from '@angular/core';
import { ComponentFamilyClass } from '../../../styles'; import { ComponentFamilyClass } from '../../../styles';
import { PiBaseButtonComponent } from '../base-button.component'; import { PiBaseButtonComponent } from '../base-button.component';
@@ -19,12 +18,12 @@ import { TranslatePipe } from '@ngx-translate/core';
}) })
export class PiCloseButtonComponent extends PiBaseButtonComponent { export class PiCloseButtonComponent extends PiBaseButtonComponent {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const hasLabelClass = this.hasLabel === false ? 'pi-close-button__single' : ''; const hasLabelClass = this.hasLabel() === false ? 'pi-close-button__single' : '';
return ['pi-close-button', ComponentFamilyClass.Button, this.sizeClass, hasLabelClass].join( return ['pi-close-button', ComponentFamilyClass.Button, this.sizeClass, hasLabelClass].join(
' ' ' '
); );
} }
@Input() hasLabel = true; readonly hasLabel = input(true);
@Output() toggle: EventEmitter<void> = new EventEmitter<void>(); readonly toggle = output<void>();
} }

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, EventEmitter, HostBinding, Output } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, output } from '@angular/core';
import { ComponentFamilyClass } from '../../../styles'; import { ComponentFamilyClass } from '../../../styles';
import { PiBaseButtonComponent } from '../base-button.component'; import { PiBaseButtonComponent } from '../base-button.component';
import { TranslatePipe } from '@ngx-translate/core'; import { TranslatePipe } from '@ngx-translate/core';
@@ -14,5 +14,5 @@ export class PiHamburgerComponent extends PiBaseButtonComponent {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
return ['pi-hamburger', ComponentFamilyClass.Button, this.colorClass].join(' '); return ['pi-hamburger', ComponentFamilyClass.Button, this.colorClass].join(' ');
} }
@Output() toggle: EventEmitter<void> = new EventEmitter<void>(); readonly toggle = output<void>();
} }

View File

@@ -1,4 +1,4 @@
<ng-container *ngIf="iconType === 'custom'; else defaultIcon" <ng-container *ngIf="iconType() === 'custom'; else defaultIcon"
><button ><button
#content #content
(click)="onClickHandler($event)" (click)="onClickHandler($event)"
@@ -8,7 +8,7 @@
size="size" size="size"
width="width" width="width"
text="text" text="text"
[disabled]="disabled" [disabled]="disabled()"
[class]="[ [class]="[
'pi-button', 'pi-button',
'pi-button__icon', 'pi-button__icon',
@@ -22,7 +22,7 @@
> >
<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>
<ng-template #defaultIcon <ng-template #defaultIcon
@@ -35,7 +35,7 @@
size="size" size="size"
width="width" width="width"
text="text" text="text"
[disabled]="disabled" [disabled]="disabled()"
[class]="[ [class]="[
'pi-button', 'pi-button',
'pi-button__icon', 'pi-button__icon',
@@ -47,8 +47,8 @@
modeClass modeClass
]" ]"
> >
<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

@@ -4,7 +4,8 @@ import {
ElementRef, ElementRef,
HostBinding, HostBinding,
Input, Input,
ViewChild, input,
viewChild
} from '@angular/core'; } 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';
@@ -20,8 +21,8 @@ import { MatIcon } from '@angular/material/icon';
}) })
export class PiIconButtonComponent extends PiBaseButtonComponent { export class PiIconButtonComponent extends PiBaseButtonComponent {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const customIconClass = this.iconType === 'custom' && 'pi-icon-button__custom-icon'; const customIconClass = this.iconType() === 'custom' && 'pi-icon-button__custom-icon';
const isResponsiveClass = this.isResponsive ? 'is-responsive' : ''; const isResponsiveClass = this.isResponsive() ? 'is-responsive' : '';
return [ return [
'pi-icon-button', 'pi-icon-button',
this.sizeClass, this.sizeClass,
@@ -31,28 +32,30 @@ export class PiIconButtonComponent extends PiBaseButtonComponent {
isResponsiveClass, isResponsiveClass,
].join(' '); ].join(' ');
} }
@ViewChild('content', { static: false }) readonly contentRef = viewChild.required<ElementRef>('content');
contentRef!: ElementRef;
@Input() icon: string | undefined; readonly icon = input<string>();
@Input() iconType: 'fill' | 'default' | 'custom' = 'default'; readonly iconType = input<'fill' | 'default' | 'custom'>('default');
@Input() previous = false; readonly previous = input(false);
@Input() width: buttonSize = 'medium'; readonly width = input<buttonSize>('medium');
@Input() text: string | undefined; @Input() text: string | undefined;
@Input() hasPadding = true; readonly hasPadding = input(true);
@Input() mode: mode = 'dark'; readonly mode = input<mode>('dark');
@Input() isResponsive = false; readonly isResponsive = input(false);
get buttonTypeClass(): string | null { get buttonTypeClass(): string | null {
return this.appearance ? `pi-button__appearance--${this.appearance}` : null; const appearance = this.appearance();
return appearance ? `pi-button__appearance--${appearance}` : null;
} }
get buttonColorClass(): string | null { get buttonColorClass(): string | null {
return this.color ? `pi-color__${this.color}` : null; const color = this.color();
return color ? `pi-color__${color}` : null;
} }
get widthClass(): string | null { get widthClass(): string | null {
return this.width ? `pi-button__${this.width}` : null; const width = this.width();
return width ? `pi-button__${width}` : null;
} }
get textClass(): string | null { get textClass(): string | null {
@@ -60,14 +63,15 @@ export class PiIconButtonComponent extends PiBaseButtonComponent {
} }
get iconPositionClass(): string | null { get iconPositionClass(): string | null {
return this.previous ? 'pi-button__icon--previous' : null; return this.previous() ? 'pi-button__icon--previous' : null;
} }
get hasPaddingClass(): string | null { get hasPaddingClass(): string | null {
return this.hasPadding ? null : 'pi-button__trimmed'; return this.hasPadding() ? null : 'pi-button__trimmed';
} }
get modeClass(): string | null { get modeClass(): string | null {
return this.mode && `pi-color__mode--${this.mode}`; const modeValue = this.mode();
return modeValue && `pi-color__mode--${modeValue}`;
} }
} }

View File

@@ -1,4 +1,4 @@
<span *ngIf="icon !== 'check'; else checkIconTemplate" (click)="execute.emit()"></span> <span *ngIf="icon() !== 'check'; else checkIconTemplate" (click)="execute.emit()"></span>
<ng-template #checkIconTemplate <ng-template #checkIconTemplate
><mat-icon size="size" svgIcon="check" color="color" (click)="execute.emit()"></mat-icon ><mat-icon size="size" svgIcon="check" color="color" (click)="execute.emit()"></mat-icon

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, input } from '@angular/core';
import { PiBaseButtonComponent } from '../base-button.component'; import { PiBaseButtonComponent } from '../base-button.component';
import { NgIf } from '@angular/common'; import { NgIf } from '@angular/common';
import { MatIcon } from '@angular/material/icon'; import { MatIcon } from '@angular/material/icon';
@@ -21,17 +21,17 @@ export class PiRadioButtonComponent extends PiBaseButtonComponent {
].join(' '); ].join(' ');
} }
@Input() icon: 'bullet' | 'check' = 'check'; readonly icon = input<'bullet' | 'check'>('check');
// Status // Status
@Input() isActive!: boolean; readonly isActive = input.required<boolean>();
@Input() isDisabled!: boolean; readonly isDisabled = input.required<boolean>();
get iconClass(): string | null { get iconClass(): string | null {
return this.icon === 'check' ? 'pi-radio-button__icon--check' : null; return this.icon() === 'check' ? 'pi-radio-button__icon--check' : null;
} }
get isActiveClass(): string | null { get isActiveClass(): string | null {
return this.isActive ? 'is-active' : null; return this.isActive() ? 'is-active' : null;
} }
} }

View File

@@ -5,8 +5,8 @@
<ng-container <ng-container
*ngTemplateOutlet=" *ngTemplateOutlet="
contentType === 'icon' ? iconTemplate : textTemplate; contentType() === 'icon' ? iconTemplate : textTemplate;
context: { $implicit: content[0] } context: { $implicit: content()[0] }
" "
></ng-container> ></ng-container>
@@ -19,8 +19,8 @@
<ng-container <ng-container
*ngTemplateOutlet=" *ngTemplateOutlet="
contentType === 'icon' ? iconTemplate : textTemplate; contentType() === 'icon' ? iconTemplate : textTemplate;
context: { $implicit: content[1] } context: { $implicit: content()[1] }
" "
></ng-container> ></ng-container>

View File

@@ -1,10 +1,10 @@
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
EventEmitter,
HostBinding, HostBinding,
Input, Input,
Output, input,
output
} from '@angular/core'; } from '@angular/core';
import { ComponentFamilyClass } from '../../../styles'; import { ComponentFamilyClass } from '../../../styles';
import { PiBaseButtonComponent } from '../base-button.component'; import { PiBaseButtonComponent } from '../base-button.component';
@@ -20,7 +20,7 @@ import { MatIcon } from '@angular/material/icon';
}) })
export class PiSwitchButtonComponent extends PiBaseButtonComponent { export class PiSwitchButtonComponent extends PiBaseButtonComponent {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const contentTypeClass = this.contentType === 'icon' ? 'has-icon' : ''; const contentTypeClass = this.contentType() === 'icon' ? 'has-icon' : '';
return [ return [
'pi-switch-button', 'pi-switch-button',
ComponentFamilyClass.Button, ComponentFamilyClass.Button,
@@ -31,10 +31,13 @@ export class PiSwitchButtonComponent extends PiBaseButtonComponent {
} }
@Input() isActive = true; @Input() isActive = true;
@Output() toggledTo = new EventEmitter(); readonly toggledTo = output();
@Input() contentType: 'icon' | 'text' = 'icon'; readonly contentType = input<'icon' | 'text'>('icon');
@Input() content!: [string, string]; readonly content = input.required<[
string,
string
]>();
toggleSelected(): void { toggleSelected(): void {
if (this.isActive) { if (this.isActive) {

View File

@@ -1,6 +1,6 @@
<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>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, Input, 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';
@@ -13,9 +13,10 @@ import { MatIcon } from '@angular/material/icon';
}) })
export class PiToggleButtonComponent extends PiBaseButtonComponent { export class PiToggleButtonComponent extends PiBaseButtonComponent {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const isActiveClass = this.isActive && 'is-active'; const isActiveClass = this.isActive() && 'is-active';
const isDisabledClass = this.isDisabled ? 'is-disabled' : ''; const isDisabledClass = this.isDisabled() ? 'is-disabled' : '';
const iconTypeClass = this.iconType !== 'default' && `pi-toggle-button__icon--${this.iconType}`; const iconType = this.iconType();
const iconTypeClass = iconType !== 'default' && `pi-toggle-button__icon--${iconType}`;
return [ return [
'pi-toggle-button', 'pi-toggle-button',
this.sizeClass, this.sizeClass,
@@ -28,14 +29,14 @@ export class PiToggleButtonComponent extends PiBaseButtonComponent {
// Content // Content
@Input() icon: string | undefined; @Input() icon: string | undefined;
@Input() text: string | undefined; readonly text = input<string>();
@Input() amount: number | undefined; @Input() amount: number | undefined;
// Skin // Skin
@Input() width: buttonSize = 'large'; readonly width = input<buttonSize>('large');
@Input() iconType: 'stroke' | 'default' = 'stroke'; readonly iconType = input<'stroke' | 'default'>('stroke');
// Status // Status
@Input() isActive!: boolean; readonly isActive = input.required<boolean>();
@Input() isDisabled!: boolean; readonly isDisabled = input.required<boolean>();
} }

View File

@@ -1,27 +1,27 @@
// Angular // Angular
import { Directive, EventEmitter, HostBinding, Input, OnInit, Output } from '@angular/core'; import { Directive, HostBinding, Input, 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';
@Directive() @Directive()
export abstract class PiBaseCardComponent implements OnInit { export abstract class PiBaseCardComponent implements OnInit {
@Input() hasShadow = true; readonly hasShadow = input(true);
@Input() isClickable = false; readonly isClickable = input(false);
@Input() isStroked = false; readonly isStroked = input(false);
@Input() color: 'primary' | 'accent' | 'highlighted' | 'grey' | 'white' = 'white'; readonly color = input<'primary' | 'accent' | 'highlighted' | 'grey' | 'white'>('white');
@Input() mode: mode = 'light'; readonly mode = input<mode>('light');
@Input() alignment!: alignment; readonly alignment = input.required<alignment>();
@Input() gap!: gap; readonly gap = input.required<gap>();
@Input() padding: padding = 'xxl'; readonly padding = input<padding>('xxl');
@Input() linkLabel: string | undefined; @Input() linkLabel: string | undefined;
@Input() opacity: opacity; readonly opacity = input<opacity>();
@Input() innerPadding!: padding; readonly innerPadding = input.required<padding>();
@Input() context!: context; readonly context = input.required<context>();
@Input() hasEqualPadding = false; readonly hasEqualPadding = input(false);
@Input() hasPadding = true; readonly hasPadding = input(true);
@Output() execute: EventEmitter<void> = new EventEmitter<void>(); readonly execute = output<void>();
ngOnInit(): void { ngOnInit(): void {
this.colorClass; this.colorClass;
@@ -37,39 +37,44 @@ export abstract class PiBaseCardComponent implements OnInit {
} }
protected get hasShadowClass(): string | boolean | undefined { protected get hasShadowClass(): string | boolean | undefined {
return this.hasShadow && 'has-shadow'; return this.hasShadow() && 'has-shadow';
} }
protected get isClickableClass(): string | undefined { protected get isClickableClass(): string | undefined {
return this.isClickable && 'is-clickable'; return this.isClickable() && 'is-clickable';
} }
protected get colorClass(): string { protected get colorClass(): string {
return this.color && `pi-color__${this.color}`; const color = this.color();
return color && `pi-color__${color}`;
} }
protected get alignmentClass(): string | undefined { protected get alignmentClass(): string | undefined {
return this.alignment && `pi-card__align-items--${this.alignment}`; const alignmentValue = this.alignment();
return alignmentValue && `pi-card__align-items--${alignmentValue}`;
} }
protected get gapClass(): string | undefined { protected get gapClass(): string | undefined {
return this.gap && `pi-card__gap--${this.gap}`; const gapValue = this.gap();
return gapValue && `pi-card__gap--${gapValue}`;
} }
protected get paddingClass(): string | undefined { protected get paddingClass(): string | undefined {
return this.padding && `pi-card__padding--${this.padding}`; const paddingValue = this.padding();
return paddingValue && `pi-card__padding--${paddingValue}`;
} }
protected get modeClass(): string | undefined { protected get modeClass(): string | undefined {
return this.mode ? `pi-color__mode--${this.mode}` : ''; const modeValue = this.mode();
return modeValue ? `pi-color__mode--${modeValue}` : '';
} }
protected get isStrokedClass(): string { protected get isStrokedClass(): string {
return this.isStroked ? 'is-stroked' : ''; return this.isStroked() ? 'is-stroked' : '';
} }
protected get hasEqualPaddingClass(): string { protected get hasEqualPaddingClass(): string {
return this.hasEqualPadding ? 'has-equal-inner-padding' : ''; return this.hasEqualPadding() ? 'has-equal-inner-padding' : '';
} }
protected get hasLinkLabelClass(): string { protected get hasLinkLabelClass(): string {
@@ -77,22 +82,25 @@ export abstract class PiBaseCardComponent implements OnInit {
} }
protected get opacityClass(): string { protected get opacityClass(): string {
return this.opacity ? `pi-color__opacity--${this.opacity}` : ''; const opacityValue = this.opacity();
return opacityValue ? `pi-color__opacity--${opacityValue}` : '';
} }
protected get innerPaddingClass(): string { protected get innerPaddingClass(): string {
return this.innerPadding ? `pi-card__inner-padding--${this.innerPadding}` : ''; const innerPadding = this.innerPadding();
return innerPadding ? `pi-card__inner-padding--${innerPadding}` : '';
} }
protected get isContectPublicClass(): string | undefined { protected get isContectPublicClass(): string | undefined {
return this.context === 'public' ? 'is-public' : ''; return this.context() === 'public' ? 'is-public' : '';
} }
protected get hasPaddingClass(): string | undefined { protected get hasPaddingClass(): string | undefined {
return !this.padding ? '' : 'has-padding'; return !this.padding() ? '' : 'has-padding';
} }
protected get contextClass(): string | undefined { protected get contextClass(): string | undefined {
return this.context && `is-${this.context}`; const contextValue = this.context();
return contextValue && `is-${contextValue}`;
} }
} }

View File

@@ -5,7 +5,7 @@
<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>

View File

@@ -1,6 +1,6 @@
<main> <main>
<pi-icon-button <pi-icon-button
*ngIf="hasOverflow && !hideSwipeButtons" *ngIf="hasOverflow && !hideSwipeButtons()"
[icon]="prevIcon" [icon]="prevIcon"
(execute)="prev()" (execute)="prev()"
[disabled]="!canSwipePrev" [disabled]="!canSwipePrev"
@@ -14,7 +14,7 @@
</ng-container> </ng-container>
</section> </section>
<pi-icon-button <pi-icon-button
*ngIf="hasOverflow && !hideSwipeButtons" *ngIf="hasOverflow && !hideSwipeButtons()"
[icon]="nextIcon" [icon]="nextIcon"
(execute)="next()" (execute)="next()"
[disabled]="!canSwipeNext" [disabled]="!canSwipeNext"
@@ -23,7 +23,7 @@
</main> </main>
<!-- Paginator --> <!-- Paginator -->
<footer *ngIf="hasOverflow && !hidePaginator"> <footer *ngIf="hasOverflow && !hidePaginator()">
<div <div
*ngFor="let i of pages; index as index" *ngFor="let i of pages; index as index"
[class]="index === currentPageIndex ? 'is-active' : ''" [class]="index === currentPageIndex ? 'is-active' : ''"

View File

@@ -7,17 +7,16 @@ import {
Component, Component,
ContentChildren, ContentChildren,
ElementRef, ElementRef,
EventEmitter,
HostBinding, HostBinding,
Input,
OnChanges, OnChanges,
OnDestroy, OnDestroy,
OnInit, OnInit,
Output,
QueryList, QueryList,
Renderer2, Renderer2,
SimpleChanges, SimpleChanges,
ViewChild, input,
output,
viewChild
} from '@angular/core'; } from '@angular/core';
// Shared // Shared
@@ -46,11 +45,13 @@ export class PiCarouselComponent
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const itemsNumberClass = const itemsNumberClass =
this.carouselItems && `pi-carousel__${this.carouselItems.length}-items`; this.carouselItems && `pi-carousel__${this.carouselItems.length}-items`;
const directionTypeClass = this.direction && `pi-carousel__${this.direction}`; const direction = this.direction();
const colorClass = `pi-carousel__${this.color}`; const directionTypeClass = direction && `pi-carousel__${direction}`;
const typeClass = `pi-carousel__${this.type}`; const colorClass = `pi-carousel__${this.color()}`;
const isDraggableClass = this.isDraggable ? 'is-draggable' : ''; const typeClass = `pi-carousel__${this.type()}`;
const justifyClass = this.justify && `pi-carousel__justify-content--${this.justify}`; const isDraggableClass = this.isDraggable() ? 'is-draggable' : '';
const justifyValue = this.justify();
const justifyClass = justifyValue && `pi-carousel__justify-content--${justifyValue}`;
return [ return [
ComponentFamilyClass.Carousel, ComponentFamilyClass.Carousel,
itemsNumberClass, itemsNumberClass,
@@ -63,24 +64,24 @@ export class PiCarouselComponent
} }
// Skin // Skin
@Input() type: 'symmetric' | 'linear' = 'linear'; readonly type = input<'symmetric' | 'linear'>('linear');
@Input() direction: 'horizontal' | 'vertical' = 'horizontal'; readonly direction = input<'horizontal' | 'vertical'>('horizontal');
@Input() color: theming = 'primary'; readonly color = input<theming>('primary');
@Input() justify!: justify; readonly justify = input.required<justify>();
@Input() device!: Device; readonly device = input.required<Device>();
/** /**
* If true, the carousel will not show the next and previous buttons. * If true, the carousel will not show the next and previous buttons.
*/ */
@Input() hideSwipeButtons = true; readonly hideSwipeButtons = input(true);
/** /**
* If true, the carousel will not show the pagination. * If true, the carousel will not show the pagination.
*/ */
@Input() hidePaginator = false; readonly hidePaginator = input(false);
// BEHAVIOUR // BEHAVIOUR
@@ -88,44 +89,44 @@ export class PiCarouselComponent
* If a number of items per device (itemsDesktop, itemsTablet, itemsMobile) is provided, * If a number of items per device (itemsDesktop, itemsTablet, itemsMobile) is provided,
* each item will take the total width of the carousel divided by the number of items. * each item will take the total width of the carousel divided by the number of items.
*/ */
@Input() itemsDesktop!: number; readonly itemsDesktop = input.required<number>();
/** /**
* If no number of items for tablet is provided, * If no number of items for tablet is provided,
* the number of items for desktop will be used. * the number of items for desktop will be used.
*/ */
@Input() itemsTablet: number; readonly itemsTablet = input<number>();
/** /**
* If no number of items for mobile is provided, * If no number of items for mobile is provided,
* the number of items for desktop will be used. * the number of items for desktop will be used.
*/ */
@Input() itemsMobile: number; readonly itemsMobile = input<number>();
/** /**
* If we want to set a custom width for each item, * If we want to set a custom width for each item,
* we can provide the width in pixels and this will override the number of items per device. * we can provide the width in pixels and this will override the number of items per device.
* The carousel will display per page the number of items that fit in the total width. * The carousel will display per page the number of items that fit in the total width.
*/ */
@Input() itemWidth!: number; readonly itemWidth = input.required<number>();
/** /**
* If true, the user will be able to drag the carousel to move across items. * If true, the user will be able to drag the carousel to move across items.
*/ */
@Input() isDraggable: boolean = true; readonly isDraggable = input<boolean>(true);
/** /**
* If true, when the user clicks on the next or previous button, * If true, when the user clicks on the next or previous button,
* the carousel will move to the next or previous page * the carousel will move to the next or previous page
* ie. currentItem will be incremented/decremented by itemsPerPage. * ie. currentItem will be incremented/decremented by itemsPerPage.
*/ */
@Input() movePerPage: boolean = false; readonly movePerPage = input<boolean>(false);
// OUTPUTS // OUTPUTS
/** /**
* Emits the index of the current item when it changes. * Emits the index of the current item when it changes.
*/ */
@Output() itemChanged: EventEmitter<number> = new EventEmitter<number>(); readonly itemChanged = output<number>();
/** /**
* carouselItems corresponds to the items that will be displayed in the carousel. * carouselItems corresponds to the items that will be displayed in the carousel.
@@ -136,8 +137,7 @@ export class PiCarouselComponent
/** /**
* 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
*/ */
@ViewChild('carouselContentWrapper', { read: ElementRef, static: false }) readonly carouselContentWrapper = viewChild.required('carouselContentWrapper', { read: ElementRef });
carouselContentWrapper!: ElementRef;
carouselItemsNativeElements: HTMLElement[] = []; carouselItemsNativeElements: HTMLElement[] = [];
@@ -187,21 +187,21 @@ export class PiCarouselComponent
* Icon displayed in the next control button. * Icon displayed in the next control button.
*/ */
get nextIcon(): string { get nextIcon(): string {
return this.direction === 'horizontal' ? 'chevron-forward' : 'chevron-down'; return this.direction() === 'horizontal' ? 'chevron-forward' : 'chevron-down';
} }
/** /**
* Icon displayed in the prev control button. * Icon displayed in the prev control button.
*/ */
get prevIcon(): string { get prevIcon(): string {
return this.direction === 'horizontal' ? 'chevron-back' : 'chevron-up'; return this.direction() === 'horizontal' ? 'chevron-back' : 'chevron-up';
} }
/** /**
* Returns the carousel content wrapper native element. * Returns the carousel content wrapper native element.
*/ */
get carouselContentElement(): HTMLElement { get carouselContentElement(): HTMLElement {
return this.carouselContentWrapper.nativeElement; return this.carouselContentWrapper().nativeElement;
} }
get tallestElementHeightPx(): number { get tallestElementHeightPx(): number {
@@ -220,13 +220,13 @@ export class PiCarouselComponent
} }
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 {
return this.movePerPage ? this.currentItem - this.itemsPerPage >= 0 : this.currentItem > 0; return this.movePerPage() ? this.currentItem - this.itemsPerPage >= 0 : this.currentItem > 0;
} }
/** /**
@@ -245,7 +245,7 @@ export class PiCarouselComponent
} }
ngOnChanges(simpleChanges: SimpleChanges): void { ngOnChanges(simpleChanges: SimpleChanges): void {
if (simpleChanges['itemWidth'] && !!this.carouselContentWrapper) { if (simpleChanges['itemWidth'] && !!this.carouselContentWrapper()) {
this.setItemsPerPage(); this.setItemsPerPage();
} }
} }
@@ -254,7 +254,7 @@ export class PiCarouselComponent
this.setItemsPerPage(); this.setItemsPerPage();
this.setPages(); this.setPages();
if (this.isDraggable) { if (this.isDraggable()) {
this.setDragEventListeners(); this.setDragEventListeners();
} }
@@ -284,7 +284,7 @@ export class PiCarouselComponent
} }
next(): void { next(): void {
if (this.movePerPage) { if (this.movePerPage()) {
this.currentPageIndex < this.pages.length - 1 this.currentPageIndex < this.pages.length - 1
? (this.currentItem = (this.currentPageIndex + 1) * this.itemsPerPage) ? (this.currentItem = (this.currentPageIndex + 1) * this.itemsPerPage)
: null; : null;
@@ -295,7 +295,7 @@ export class PiCarouselComponent
} }
prev(): void { prev(): void {
if (this.movePerPage) { if (this.movePerPage()) {
this.currentPageIndex > 0 this.currentPageIndex > 0
? (this.currentItem = (this.currentPageIndex - 1) * this.itemsPerPage) ? (this.currentItem = (this.currentPageIndex - 1) * this.itemsPerPage)
: null; : null;
@@ -306,18 +306,22 @@ export class PiCarouselComponent
} }
setItemsPerPage(): void { setItemsPerPage(): void {
if (!!this.itemWidth) { const itemWidth = this.itemWidth();
this.itemsPerPage = Math.floor(this.carouselContentElement.clientWidth / this.itemWidth) || 1; if (!!itemWidth) {
this.itemsPerPage = Math.floor(this.carouselContentElement.clientWidth / itemWidth) || 1;
} else { } else {
switch (this.device) { const itemsDesktop = this.itemsDesktop();
const itemsTablet = this.itemsTablet();
const itemsMobile = this.itemsMobile();
switch (this.device()) {
case 'DESKTOP': case 'DESKTOP':
this.itemsPerPage = this.itemsDesktop ? this.itemsDesktop : this.carouselItems.length; this.itemsPerPage = itemsDesktop ? itemsDesktop : this.carouselItems.length;
break; break;
case 'TABLET': case 'TABLET':
this.itemsPerPage = this.itemsTablet ? this.itemsTablet : this.carouselItems.length; this.itemsPerPage = itemsTablet ? itemsTablet : this.carouselItems.length;
break; break;
case 'MOBILE': case 'MOBILE':
this.itemsPerPage = this.itemsMobile ? this.itemsMobile : this.carouselItems.length; this.itemsPerPage = itemsMobile ? itemsMobile : this.carouselItems.length;
break; break;
} }
} }
@@ -335,7 +339,7 @@ export class PiCarouselComponent
selectItem(index: number): void { selectItem(index: number): void {
this.currentItem = index; this.currentItem = index;
if (this.hasOverflow && !this.movePerPage) { if (this.hasOverflow && !this.movePerPage()) {
this.carouselTranslation(index); this.carouselTranslation(index);
} }
} }
@@ -370,7 +374,7 @@ export class PiCarouselComponent
private setDragEventListeners(): void { private setDragEventListeners(): void {
this.mousedownListener = this.renderer.listen( this.mousedownListener = this.renderer.listen(
this.carouselContentWrapper.nativeElement, this.carouselContentWrapper().nativeElement,
'mousedown', 'mousedown',
(event) => { (event) => {
this.dragStart(event); this.dragStart(event);
@@ -392,7 +396,7 @@ export class PiCarouselComponent
}); });
this.touchstartListener = this.renderer.listen( this.touchstartListener = this.renderer.listen(
this.carouselContentWrapper.nativeElement, this.carouselContentWrapper().nativeElement,
'touchstart', 'touchstart',
(event) => { (event) => {
this.dragStart(event); this.dragStart(event);
@@ -433,7 +437,8 @@ export class PiCarouselComponent
* Sets a minimum dragging width based on either 80% of the item width if provided * Sets a minimum dragging width based on either 80% of the item width if provided
* Otherwise 80% of the width of one item in terms of the number of items displayed per page * Otherwise 80% of the width of one item in terms of the number of items displayed per page
*/ */
const minDraggingWidth = this.itemWidth ? 0.8 * this.itemWidth : 0.8 * (100 / this.itemsPerPage); const itemWidth = this.itemWidth();
const minDraggingWidth = itemWidth ? 0.8 * itemWidth : 0.8 * (100 / this.itemsPerPage);
this.isDragging = true; this.isDragging = true;
@@ -466,39 +471,40 @@ export class PiCarouselComponent
} }
private carouselTranslation(currentItem: number): void { private carouselTranslation(currentItem: number): void {
if (this.type === 'linear' && currentItem <= this.carouselItems.length - this.itemsPerPage) { const type = this.type();
this.carouselItemsTranslation(currentItem, `-${currentItem * 100}%`, this.direction); if (type === 'linear' && currentItem <= this.carouselItems.length - this.itemsPerPage) {
} else if (this.type === 'symmetric') { this.carouselItemsTranslation(currentItem, `-${currentItem * 100}%`, this.direction());
} 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,
`-${(currentItem - this.numberOfSideItems) * 100}%`, `-${(currentItem - this.numberOfSideItems) * 100}%`,
this.direction this.direction()
); );
} else { } else {
// 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()
); );
} }
} }
} }
private setStyleOnInit(): void { private setStyleOnInit(): void {
this.direction === 'horizontal' this.direction() === 'horizontal'
? this.setHorizontalCarouselStyle() ? this.setHorizontalCarouselStyle()
: this.setVerticalCarouselStyle(); : this.setVerticalCarouselStyle();
} }
private carouselItemsTranslation(currentItem: number, translation: string, axis: string) { private carouselItemsTranslation(currentItem: number, translation: string, axis: string) {
this.carouselItemsNativeElements.forEach((item, index) => { this.carouselItemsNativeElements.forEach((item, index) => {
const scale: number = index === currentItem && this.type === 'symmetric' ? 1.15 : 1; const scale: number = index === currentItem && this.type() === 'symmetric' ? 1.15 : 1;
const transform: string = const transform: string =
axis === 'horizontal' ? `translateX(${translation})` : `translateY(${translation})`; axis === 'horizontal' ? `translateX(${translation})` : `translateY(${translation})`;
this.renderer.setStyle(item, 'transform', `${transform} scale(${scale})`); this.renderer.setStyle(item, 'transform', `${transform} scale(${scale})`);
@@ -507,9 +513,10 @@ export class PiCarouselComponent
private setHorizontalCarouselStyle(): void { private setHorizontalCarouselStyle(): void {
this.carouselItemsNativeElements.forEach((item) => { this.carouselItemsNativeElements.forEach((item) => {
if (!!this.itemWidth) { const itemWidth = this.itemWidth();
this.renderer.setStyle(item, 'min-width', `${this.itemWidth}px`); if (!!itemWidth) {
this.renderer.setStyle(item, 'max-width', `${this.itemWidth}px`); this.renderer.setStyle(item, 'min-width', `${itemWidth}px`);
this.renderer.setStyle(item, 'max-width', `${itemWidth}px`);
this.renderer.setStyle(item, 'width', 'unset'); this.renderer.setStyle(item, 'width', 'unset');
} else { } else {
this.renderer.setStyle(item, 'min-width', `${100 / this.itemsPerPage}%`); this.renderer.setStyle(item, 'min-width', `${100 / this.itemsPerPage}%`);

View File

@@ -1,5 +1,5 @@
<ng-container *ngFor="let dataset of datasets ; let index = index"> <ng-container *ngFor="let dataset of datasets() ; let index = index">
<section [class]="hoveredDatasetIndex === index ? 'is-hovered' : ''" (mouseenter)="triggerDatasetHover.emit(index)" (mouseleave)="triggerDatasetHover.emit(-1)"> <section [class]="hoveredDatasetIndex() === index ? 'is-hovered' : ''" (mouseenter)="triggerDatasetHover.emit(index)" (mouseleave)="triggerDatasetHover.emit(-1)">
<div [style]="{'border-color': dataset.borderColor, 'background-color': dataset.backgroundColor}"></div> <div [style]="{'border-color': dataset.borderColor, 'background-color': dataset.backgroundColor}"></div>
<label [style.color]="dataset.borderColor" > <label [style.color]="dataset.borderColor" >
{{ dataset.label | translate }} {{ dataset.label | translate }}

View File

@@ -1,10 +1,9 @@
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
EventEmitter,
HostBinding, HostBinding,
Input, input,
Output output
} from '@angular/core'; } from '@angular/core';
import { Dataset } from '../../../util/models'; import { Dataset } from '../../../util/models';
import { NgFor } from '@angular/common'; import { NgFor } from '@angular/common';
@@ -19,13 +18,13 @@ import { TranslatePipe } from '@ngx-translate/core';
}) })
export class PiChartLegendComponent { export class PiChartLegendComponent {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const comparativeClass = this.isComparativeChart ? 'is-comparative' : ''; const comparativeClass = this.isComparativeChart() ? 'is-comparative' : '';
return ['pi-chart-legend', comparativeClass].join(' '); return ['pi-chart-legend', comparativeClass].join(' ');
} }
@Input() datasets!: Array<Dataset>; readonly datasets = input.required<Array<Dataset>>();
@Input() isComparativeChart: boolean = true; readonly isComparativeChart = input<boolean>(true);
@Input() hoveredDatasetIndex: number; readonly hoveredDatasetIndex = input<number>();
@Output() triggerDatasetHover: EventEmitter<number> = new EventEmitter<number>(); readonly triggerDatasetHover = output<number>();
} }

View File

@@ -2,8 +2,8 @@
<canvas #chart aria-label="Chart"></canvas> <canvas #chart aria-label="Chart"></canvas>
</div> </div>
<pi-chart-legend <pi-chart-legend
[datasets]="datasets" [datasets]="datasets()"
[isComparativeChart]="isComparativeChart" [isComparativeChart]="isComparativeChart()"
[hoveredDatasetIndex]="hoveredDatasetIndex" [hoveredDatasetIndex]="hoveredDatasetIndex"
(triggerDatasetHover)="datasetHoveredInLegend($event)" (triggerDatasetHover)="datasetHoveredInLegend($event)"
></pi-chart-legend> ></pi-chart-legend>

View File

@@ -4,9 +4,9 @@ import {
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
ElementRef, ElementRef,
Input,
OnInit, OnInit,
ViewChild, input,
viewChild
} from '@angular/core'; } from '@angular/core';
import { Device } from '../../../util/device-configs'; import { Device } from '../../../util/device-configs';
import { Dataset } from '../../../util/models'; import { Dataset } from '../../../util/models';
@@ -49,11 +49,11 @@ registerChartComponents();
imports: [PiChartLegendComponent], imports: [PiChartLegendComponent],
}) })
export class PiChartComponent implements OnInit, AfterViewInit { export class PiChartComponent implements OnInit, AfterViewInit {
@Input() type: 'line' | 'bar' = 'line'; readonly type = input<'line' | 'bar'>('line');
@Input() datasets!: Array<Dataset>; readonly datasets = input.required<Array<Dataset>>();
@Input() device!: Device; readonly device = input.required<Device>();
@Input() xAxisMarks: Array<string>; readonly xAxisMarks = input<Array<string>>();
@Input() isComparativeChart: boolean = true; readonly isComparativeChart = input<boolean>(true);
private _hoveredDatasetIndex: number; private _hoveredDatasetIndex: number;
set hoveredDatasetIndex(index: number) { set hoveredDatasetIndex(index: number) {
@@ -63,8 +63,7 @@ export class PiChartComponent implements OnInit, AfterViewInit {
return this._hoveredDatasetIndex; return this._hoveredDatasetIndex;
} }
@ViewChild('chart', { read: ElementRef<HTMLCanvasElement>, static: false }) readonly chartRef = viewChild.required('chart', { read: (ElementRef<HTMLCanvasElement>) });
chartRef!: ElementRef<HTMLCanvasElement>;
chart: Chart; chart: Chart;
@@ -74,7 +73,7 @@ export class PiChartComponent implements OnInit, AfterViewInit {
) {} ) {}
get completedDatasets() { get completedDatasets() {
return this.datasets.map((dataset) => { return this.datasets().map((dataset) => {
const datasetWithTranslatedLabels = { const datasetWithTranslatedLabels = {
...dataset, ...dataset,
label: this.translate.instant(dataset.label), label: this.translate.instant(dataset.label),
@@ -109,17 +108,17 @@ export class PiChartComponent implements OnInit, AfterViewInit {
} }
getLineChartConfigs(): void { getLineChartConfigs(): void {
const isComparativeChart = this.isComparativeChart; const isComparativeChart = this.isComparativeChart();
this.chart = new Chart(this.chartRef.nativeElement, { this.chart = new Chart(this.chartRef().nativeElement, {
type: 'line', type: 'line',
data: { data: {
labels: this.xAxisMarks, labels: this.xAxisMarks(),
datasets: this.completedDatasets, datasets: this.completedDatasets,
}, },
options: { options: {
responsive: true, responsive: true,
maintainAspectRatio: this.device === 'DESKTOP' ? true : false, maintainAspectRatio: this.device() === 'DESKTOP' ? true : false,
hover: { hover: {
mode: 'dataset', mode: 'dataset',
}, },

View File

@@ -1,5 +1,5 @@
// Angular // Angular
import { Directive, HostBinding, Input } from '@angular/core'; import { Directive, HostBinding, Input, input } from '@angular/core';
// Style definitions // Style definitions
import { ComponentFamilyClass, density, mode, styleType } from '../../styles'; import { ComponentFamilyClass, density, mode, styleType } from '../../styles';
@@ -7,15 +7,19 @@ import { chipColor } from './entities/chip.model';
@Directive() @Directive()
export abstract class PiBaseChipComponent { export abstract class PiBaseChipComponent {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const colorClass = this.color && `pi-color__${this.color}`; const color = this.color();
const sizeClass = this.size && `pi-chip__size--${this.size}`; const colorClass = color && `pi-color__${color}`;
const size = this.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 appearanceClass = const appearanceClass =
this.affixAppearance && `pi-chip__affix-appearance--${this.affixAppearance}`; affixAppearance && `pi-chip__affix-appearance--${affixAppearance}`;
const colorModeClass = this.mode && `pi-color__mode--${this.mode}`; const modeValue = this.mode();
const isDisabledClass = this.isDisabled ? 'is-disabled' : ''; const colorModeClass = modeValue && `pi-color__mode--${modeValue}`;
const isClosableClass = this.isClosable ? 'is-closable' : ''; const isDisabledClass = this.isDisabled() ? 'is-disabled' : '';
const fillClass = this.fill !== 'fit-content' && 'pi-chip__fill--fill-available'; const isClosableClass = this.isClosable() ? 'is-closable' : '';
const fillClass = this.fill() !== 'fit-content' && 'pi-chip__fill--fill-available';
return [ return [
ComponentFamilyClass.Chip, ComponentFamilyClass.Chip,
colorClass, colorClass,
@@ -34,19 +38,19 @@ export abstract class PiBaseChipComponent {
@Input() icon: string | undefined; @Input() icon: string | undefined;
// Skin // Skin
@Input() color: chipColor; readonly color = input<chipColor>();
@Input() mode: mode = 'light'; readonly mode = input<mode>('light');
@Input() size: 'small' | 'medium' | 'large' = 'small'; readonly size = input<'small' | 'medium' | 'large'>('small');
@Input() fill: 'fit-content' | 'fill-available' = 'fit-content'; readonly fill = input<'fit-content' | 'fill-available'>('fit-content');
// Affix // Affix
@Input() affixAppearance: 'fill' | 'ghost' = 'ghost'; readonly affixAppearance = input<'fill' | 'ghost'>('ghost');
@Input() affixPosition: 'previous' | 'after' = 'after'; readonly affixPosition = input<'previous' | 'after'>('after');
// Behavior // Behavior
@Input() isClosable: boolean = true; readonly isClosable = input<boolean>(true);
// Status // Status
@Input() isDisabled = false; readonly isDisabled = input(false);
} }

View File

@@ -6,16 +6,16 @@
*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>
<mat-icon <mat-icon
*ngIf="isClosable" *ngIf="isClosable()"
svgIcon="close" svgIcon="close"
[color]="color" [color]="color()"
class="pi-chip-close-icon" class="pi-chip-close-icon"
></mat-icon> ></mat-icon>
</span> </span>

View File

@@ -1,10 +1,9 @@
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
EventEmitter,
HostBinding, HostBinding,
Input, input,
Output, output
} from '@angular/core'; } from '@angular/core';
import { ComponentFamilyClass, mode, styleType } from '../../../styles'; import { ComponentFamilyClass, mode, styleType } from '../../../styles';
import { PiBaseChipComponent } from '../base-chip.component'; import { PiBaseChipComponent } from '../base-chip.component';
@@ -21,18 +20,20 @@ import { MatIcon } from '@angular/material/icon';
}) })
export class PiChipActionComponent extends PiBaseChipComponent { export class PiChipActionComponent extends PiBaseChipComponent {
@HostBinding('class') get actionClasses(): string { @HostBinding('class') get actionClasses(): string {
const hasActionIconClass = this.hasActionIcon && 'has-action'; const hasActionIconClass = this.hasActionIcon() && 'has-action';
const isActiveClass = this.isActive ? 'is-active' : ''; const isActiveClass = this.isActive() ? 'is-active' : '';
/** /**
* When the chip action is dismissible, the affix position will be always previous, * When the chip action is dismissible, the affix position will be always previous,
that's why we force the position--previous class binding that's why we force the position--previous class binding
*/ */
const affixPosition = this.affixPosition();
const isClosableAffixPositionClass = const isClosableAffixPositionClass =
this.isClosable && this.affixPosition === 'after' this.isClosable() && affixPosition === 'after'
? 'pi-chip__affix-position--previous' ? 'pi-chip__affix-position--previous'
: `pi-chip__affix-position--${this.affixPosition}`; : `pi-chip__affix-position--${affixPosition}`;
const affixPositionValue = this.affixPosition();
const affixPositionClass = const affixPositionClass =
this.affixPosition && `pi-chip__affix-position--${this.affixPosition}`; affixPositionValue && `pi-chip__affix-position--${affixPositionValue}`;
return [ return [
'pi-chip-action', 'pi-chip-action',
@@ -44,15 +45,15 @@ export class PiChipActionComponent extends PiBaseChipComponent {
].join(' '); ].join(' ');
} }
@Output() execute: EventEmitter<void> = new EventEmitter<void>(); readonly execute = output<void>();
// Content // Content
@Input() hasActionIcon: boolean = true; readonly hasActionIcon = input<boolean>(true);
// Skin // Skin
@Input() override size: 'small' | 'medium' | 'large' = 'small'; override readonly size = input<'small' | 'medium' | 'large'>('small');
@Input() override color: chipColor = 'primary'; override readonly color = input<chipColor>('primary');
// Status // Status
@Input() isActive!: boolean; readonly isActive = input.required<boolean>();
} }

View File

@@ -1,5 +1,5 @@
<span (click)="isClickable ? click.emit() : null"> <span (click)="isClickable() ? click.emit() : null">
<span class="pi-chip-date__weekday" *ngIf="size === 'large'">{{ dateValue?.weekDay }}</span> <span class="pi-chip-date__weekday" *ngIf="size() === 'large'">{{ dateValue?.weekDay }}</span>
<span class="pi-chip-date__day">{{ dateValue?.day }}</span> <span class="pi-chip-date__day">{{ dateValue?.day }}</span>
<span class="pi-chip-date__month">{{ dateValue?.month.toUpperCase() }}</span> <span class="pi-chip-date__month">{{ dateValue?.month.toUpperCase() }}</span>
</span> </span>

View File

@@ -1,10 +1,9 @@
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
EventEmitter,
HostBinding, HostBinding,
Input, input,
Output, output
} from '@angular/core'; } from '@angular/core';
import { ComponentFamilyClass, theming } from '../../../styles'; import { ComponentFamilyClass, theming } from '../../../styles';
import { Date } from '../../../util/models'; import { Date } from '../../../util/models';
@@ -22,13 +21,15 @@ import { NgIf } from '@angular/common';
}) })
export class PiChipDateComponent extends PiBaseChipComponent { export class PiChipDateComponent extends PiBaseChipComponent {
@HostBinding('class') get dateClasses(): string { @HostBinding('class') get dateClasses(): string {
const colorClass = this.color && `pi-color__${this.color}`; const color = this.color();
const isClickableClass = this.isClickable ? 'is-clickable' : ''; const colorClass = color && `pi-color__${color}`;
const isStrokeClass = !this.isClickable && !!this.isStroked && 'is-stroked'; const isClickableClass = this.isClickable() ? 'is-clickable' : '';
const isActiveClass = !!this.isActive ? 'is-active' : ''; const isStrokeClass = !this.isClickable() && !!this.isStroked() && 'is-stroked';
const hasWeekDayClass = this.size === 'large' ? 'has-weekday' : ''; const isActiveClass = !!this.isActive() ? 'is-active' : '';
const hasWeekDayClass = this.size() === 'large' ? 'has-weekday' : '';
const flexDirection = this.flexDirection();
const flexDirectionClass = const flexDirectionClass =
this.flexDirection && `pi-chip-date__flex-direction--${this.flexDirection}`; flexDirection && `pi-chip-date__flex-direction--${flexDirection}`;
return [ return [
'pi-chip-date', 'pi-chip-date',
ComponentFamilyClass.Chip, ComponentFamilyClass.Chip,
@@ -41,28 +42,28 @@ export class PiChipDateComponent extends PiBaseChipComponent {
].join(' '); ].join(' ');
} }
@Output() click: EventEmitter<void> = new EventEmitter<void>(); readonly click = output<void>();
// Content // Content
@Input() date!: string; // ISO format in UTC time zone readonly date = input.required<string>(); // ISO format in UTC time zone
// Skin // Skin
@Input() override color: theming = 'primary'; override readonly color = input<theming>('primary');
@Input() isStroked = false; readonly isStroked = input(false);
// Size - weekday only available in LARGE // Size - weekday only available in LARGE
// No weekday - available in SMALL and MEDIUM // No weekday - available in SMALL and MEDIUM
// row direction only available in S and M // row direction only available in S and M
@Input() override size: 'small' | 'medium' | 'large' = 'small'; override readonly size = input<'small' | 'medium' | 'large'>('small');
@Input() flexDirection: 'row' | 'column' = 'column'; readonly flexDirection = input<'row' | 'column'>('column');
// Status // Status
@Input() isClickable = true; readonly isClickable = input(true);
@Input() isActive!: boolean; readonly isActive = input.required<boolean>();
get dateValue(): Date { get dateValue(): Date {
const dateObj = DateTime.fromISO(this.date).setLocale('es'); const dateObj = DateTime.fromISO(this.date()).setLocale('es');
const isDateToday = dateObj.hasSame(DateTime.now(), 'day'); const isDateToday = dateObj.hasSame(DateTime.now(), 'day');
return { return {
day: dateObj.day.toString() || '', day: dateObj.day.toString() || '',

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

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, input } from '@angular/core';
// Shared // Shared
import { status } from '../../../util/status'; import { status } from '../../../util/status';
@@ -15,21 +15,23 @@ import { MatIcon } from '@angular/material/icon';
}) })
export class PiChipStatusComponent extends PiBaseChipComponent { export class PiChipStatusComponent extends PiBaseChipComponent {
@HostBinding('class') get statusClasses(): string { @HostBinding('class') get statusClasses(): string {
const statusClass = this.status && `is-${this.status}`; const statusValue = this.status();
const appearanceClass = `pi-chip-status__appearance--${this.appearance}`; const statusClass = statusValue && `is-${statusValue}`;
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 affixPositionClass = const affixPositionClass =
this.affixPosition && `pi-chip__affix-position--${this.affixPosition}`; affixPosition && `pi-chip__affix-position--${affixPosition}`;
return ['pi-chip-status', statusClass, appearanceClass, hasLabelClass, affixPositionClass].join( return ['pi-chip-status', statusClass, appearanceClass, hasLabelClass, affixPositionClass].join(
' ' ' '
); );
} }
// Skin // Skin
@Input() appearance: 'ghost' | 'fill' = 'fill'; readonly appearance = input<'ghost' | 'fill'>('fill');
@Input() override affixPosition: 'previous' | 'after' = 'previous'; override readonly affixPosition = input<'previous' | 'after'>('previous');
@Input() override size: 'small' | 'medium' = 'small'; override readonly size = input<'small' | 'medium'>('small');
/* green | red | yellow | blue | opacity */ /* green | red | yellow | blue | opacity */
@Input() status!: status; readonly status = input.required<status>();
} }

View File

@@ -1,4 +1,4 @@
<span (click)="isClosable ? execute.emit() : null" *ngIf="time && date; else singleContentTemplate"> <span (click)="isClosable() ? execute.emit() : null" *ngIf="time() && date(); else singleContentTemplate">
<div> <div>
<span class="pi-chip-affix"> <span class="pi-chip-affix">
<span class="pi-chip-affix__time"> {{ timeValue }}h </span> <span class="pi-chip-affix__time"> {{ timeValue }}h </span>
@@ -7,18 +7,18 @@
</div> </div>
<mat-icon <mat-icon
*ngIf="isClosable" *ngIf="isClosable()"
svgIcon="close" svgIcon="close"
[color]="color" [color]="color()"
class="pi-chip-close-icon" class="pi-chip-close-icon"
></mat-icon> ></mat-icon>
</span> </span>
<ng-template #singleContentTemplate> <ng-template #singleContentTemplate>
<span (click)="isClosable ? execute.emit() : null"> <span (click)="isClosable() ? execute.emit() : null">
<div> <div>
<span class="pi-chip-affix"> <span class="pi-chip-affix">
<span *ngIf="time; else dateTemplate" class="pi-chip-affix__time"> {{ timeValue }}h </span> <span *ngIf="time(); else dateTemplate" class="pi-chip-affix__time"> {{ timeValue }}h </span>
<ng-template #dateTemplate> <ng-template #dateTemplate>
<span class="pi-chip-affix__date"> {{ dateValue }} </span> <span class="pi-chip-affix__date"> {{ dateValue }} </span>
</ng-template> </ng-template>

View File

@@ -1,10 +1,9 @@
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
EventEmitter,
HostBinding, HostBinding,
Input, input,
Output, output
} from '@angular/core'; } from '@angular/core';
import { ComponentFamilyClass } from '../../../styles'; import { ComponentFamilyClass } from '../../../styles';
import { DateTime } from 'luxon'; import { DateTime } from 'luxon';
@@ -22,44 +21,45 @@ import { MatIcon } from '@angular/material/icon';
}) })
export class PiChipTimeComponent extends PiBaseChipComponent { export class PiChipTimeComponent extends PiBaseChipComponent {
@HostBinding('class') get dateClasses(): string { @HostBinding('class') get dateClasses(): string {
const colorClass = this.color && `pi-color__${this.color}`; const color = this.color();
const isActiveClass = this.isActive ? 'is-active' : ''; const colorClass = color && `pi-color__${color}`;
const affixClass = this.time && this.date ? 'has-affix' : ''; const isActiveClass = this.isActive() ? 'is-active' : '';
const affixClass = this.time() && this.date() ? 'has-affix' : '';
return ['pi-chip-time', ComponentFamilyClass.Chip, colorClass, isActiveClass, affixClass].join( return ['pi-chip-time', ComponentFamilyClass.Chip, colorClass, isActiveClass, affixClass].join(
' ' ' '
); );
} }
@Output() execute: EventEmitter<void> = new EventEmitter<void>(); readonly execute = output<void>();
// Content // Content
@Input() date!: string; // ISO format in UTC time zone readonly date = input.required<string>(); // ISO format in UTC time zone
@Input() time!: string; // ISO format in UTC time zone readonly time = input.required<string>(); // ISO format in UTC time zone
@Input() timezone: string = 'utc'; readonly timezone = input<string>('utc');
// Skin // Skin
@Input() override color: chipThemingColor = 'primary'; override readonly color = input<chipThemingColor>('primary');
/** /**
* On the chip time cases, we override the affix position property because * On the chip time cases, we override the affix position property because
it will be always used by the thime input and will be always previous it will be always used by the thime input and will be always previous
*/ */
@Input() override affixPosition: 'previous' = 'previous'; override readonly affixPosition = input<'previous'>('previous');
// time - available in S and M // time - available in S and M
// time + date - available in S // time + date - available in S
@Input() override size: 'small' | 'medium' = 'small'; override readonly size = input<'small' | 'medium'>('small');
// Status // Status
@Input() isActive!: boolean; readonly isActive = input.required<boolean>();
get dateObj(): DateTime { get dateObj(): DateTime {
return DateTime.fromISO(this.date, { zone: 'utc' }); return DateTime.fromISO(this.date(), { zone: 'utc' });
} }
get timeValue(): string { get timeValue(): string {
return this.dateObj.setZone(this.timezone).toFormat('H:mm'); return this.dateObj.setZone(this.timezone()).toFormat('H:mm');
} }
get dateValue(): string { get dateValue(): string {

View File

@@ -5,7 +5,7 @@
<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 } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, Input, 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';
@@ -14,10 +14,12 @@ 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 unitPositionClass = const unitPositionClass =
this.unitPosition && `pi-chip-value__unit-position--${this.unitPosition}`; unitPosition && `pi-chip-value__unit-position--${unitPosition}`;
const affixPosition = this.affixPosition();
const affixPositionClass = const affixPositionClass =
this.affixPosition && `pi-chip__affix-position--${this.affixPosition}`; affixPosition && `pi-chip__affix-position--${affixPosition}`;
return ['pi-chip-value', colorClass, hasLabelClass, unitPositionClass, affixPositionClass].join( return ['pi-chip-value', colorClass, hasLabelClass, unitPositionClass, affixPositionClass].join(
' ' ' '
); );
@@ -25,11 +27,11 @@ export class PiChipValueComponent extends PiBaseChipComponent {
// Content // Content
@Input() override value!: number; @Input() override value!: number;
@Input() unit!: unit | string; readonly unit = input.required<unit | string>();
// Skin // Skin
@Input() override size: 'medium' | 'large' = 'large'; override readonly size = input<'medium' | 'large'>('large');
// Unit // Unit
@Input() unitPosition: 'previous' | 'after' = 'after'; readonly unitPosition = input<'previous' | 'after'>('after');
} }

View File

@@ -4,7 +4,7 @@
<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"
/> />
@@ -12,7 +12,7 @@
<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>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, input } from '@angular/core';
import { theming } from '../../../styles'; import { theming } from '../../../styles';
import { PiBaseChipComponent } from '../base-chip.component'; import { PiBaseChipComponent } from '../base-chip.component';
import { NgIf } from '@angular/common'; import { NgIf } from '@angular/common';
@@ -13,18 +13,21 @@ import { MatIcon } from '@angular/material/icon';
}) })
export class PiChipComponent extends PiBaseChipComponent { export class PiChipComponent extends PiBaseChipComponent {
@HostBinding('class') get chipClasses(): string { @HostBinding('class') get chipClasses(): string {
const appearanceClass = this.appearance && `pi-chip__appearance--${this.appearance}`; const appearance = this.appearance();
const iconTypeClass = this.iconType && `pi-chip__icon-type--${this.iconType}`; const appearanceClass = appearance && `pi-chip__appearance--${appearance}`;
const iconType = this.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 affixPositionClass = const affixPositionClass =
this.affixPosition && `pi-chip__affix-position--${this.affixPosition}`; affixPosition && `pi-chip__affix-position--${affixPosition}`;
return [appearanceClass, iconTypeClass, hasLabelClass, affixPositionClass].join(' '); return [appearanceClass, iconTypeClass, hasLabelClass, affixPositionClass].join(' ');
} }
// Content // Content
@Input() iconType: 'custom' | 'default' = 'default'; readonly iconType = input<'custom' | 'default'>('default');
// Skin // Skin
@Input() appearance: 'outline' | 'fill' = 'outline'; readonly appearance = input<'outline' | 'fill'>('outline');
@Input() override color: theming = 'primary'; override readonly color = input<theming>('primary');
} }

View File

@@ -1,6 +1,6 @@
<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>
@@ -8,4 +8,4 @@
<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 } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, Input, 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';
@@ -12,21 +12,23 @@ import { MatIcon } from '@angular/material/icon';
}) })
export class PiTileComponent { export class PiTileComponent {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const colorClass = this.color && `pi-color__${this.color}`; const color = this.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 flexDirection = this.flexDirection && `pi-tile__flex-direction--${this.flexDirection}`; const flexDirectionValue = this.flexDirection();
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(
' ' ' '
); );
} }
// Content // Content
@Input() label: string | undefined; readonly label = input<string>();
@Input() value: string | undefined; @Input() value: string | undefined;
@Input() icon: string | undefined; @Input() icon: string | undefined;
// Skin // Skin
@Input() color: theming = 'primary'; readonly color = input<theming>('primary');
@Input() flexDirection: 'row' | 'column' = 'column'; readonly flexDirection = input<'row' | 'column'>('column');
} }

View File

@@ -1,7 +1,7 @@
<!-- <!--
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>
@@ -9,25 +9,25 @@
Data Direction: Data Direction:
COLUMN COLUMN
--> -->
<ng-container *ngIf="dataDirection === 'column'; else dataDirectionRowTemplate"> <ng-container *ngIf="dataDirection() === 'column'; else dataDirectionRowTemplate">
<!-- DataGrid (each iteration)--> <!-- DataGrid (each iteration)-->
<ng-container *ngFor="let value of data; index as i; trackBy: trackByFn"> <ng-container *ngFor="let value of data(); index as i; trackBy: trackByFn">
<div <div
pi-grid-item pi-grid-item
#gridItem #gridItem
[class.pi-card]="mode === 'grid'" [class.pi-card]="mode() === 'grid'"
[class.is-active]="index === i" [class.is-active]="index() === i"
(click)="clickItem.emit(value)" (click)="clickItem.emit(value)"
> >
<!-- Key --> <!-- Key -->
<ng-container <ng-container
*ngTemplateOutlet="mode === 'grid' ? keysTemplateRef || defaultErrorTemplate : null" *ngTemplateOutlet="mode() === 'grid' ? keysTemplateRef || defaultErrorTemplate : null"
> >
</ng-container> </ng-container>
<!-- Values --> <!-- Values -->
<ng-container <ng-container
[ngTemplateOutlet]="valuesTemplateRef || null" [ngTemplateOutlet]="valuesTemplateRef() || null"
[ngTemplateOutletContext]="{ $implicit: value, index: i }" [ngTemplateOutletContext]="{ $implicit: value, index: i }"
> >
</ng-container> </ng-container>
@@ -44,31 +44,31 @@
<section class="pi-data-grid__content"> <section class="pi-data-grid__content">
<div <div
id="pi-grid-item-{{ i }}" id="pi-grid-item-{{ i }}"
*ngFor="let value of data; index as i" *ngFor="let value of data(); index as i"
pi-grid-item pi-grid-item
#gridItem #gridItem
[class.pi-card]="mode === 'grid'" [class.pi-card]="mode() === 'grid'"
(click)="clickItem.emit(value)" (click)="clickItem.emit(value)"
> >
<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 -->
<ng-container <ng-container
[ngTemplateOutlet]="valuesTemplateRef || null" [ngTemplateOutlet]="valuesTemplateRef() || null"
[ngTemplateOutletContext]="{ $implicit: value, index: i }" [ngTemplateOutletContext]="{ $implicit: value, index: i }"
> >
</ng-container> </ng-container>
</section> </section>
<!-- Expandable Content --> <!-- Expandable Content -->
<aside *ngIf="isExpandable"> <aside *ngIf="isExpandable()">
<!-- Keys --> <!-- Keys -->
<header <header
class="pi-grid-item pi-grid-item__expandable-content pi-grid-item__expandable-content--keys-template" class="pi-grid-item pi-grid-item__expandable-content pi-grid-item__expandable-content--keys-template"
> >
<ng-container *ngTemplateOutlet="(mode === 'list' && expandableKeysTemplateRef) || null"> <ng-container *ngTemplateOutlet="(mode() === 'list' && expandableKeysTemplateRef()) || null">
</ng-container> </ng-container>
</header> </header>
@@ -76,7 +76,7 @@
class="pi-grid-item pi-grid-item__expandable-content pi-grid-item__expandable-content--values-template" class="pi-grid-item pi-grid-item__expandable-content pi-grid-item__expandable-content--values-template"
> >
<ng-container <ng-container
[ngTemplateOutlet]="expandableValuesTemplateRef || null" [ngTemplateOutlet]="expandableValuesTemplateRef() || null"
[ngTemplateOutletContext]="{ $implicit: value, index: i }" [ngTemplateOutletContext]="{ $implicit: value, index: i }"
> >
</ng-container> </ng-container>

View File

@@ -3,13 +3,13 @@ import {
Component, Component,
ContentChild, ContentChild,
ElementRef, ElementRef,
EventEmitter,
HostBinding, HostBinding,
Input,
OnInit, OnInit,
Output,
TemplateRef, TemplateRef,
ViewChild, input,
output,
viewChild,
contentChild
} from '@angular/core'; } from '@angular/core';
import { justify } from '../../../styles'; import { justify } from '../../../styles';
import { ListMode } from '../../../util/list-configs'; import { ListMode } from '../../../util/list-configs';
@@ -45,46 +45,43 @@ export class PiDataGridComponent<T> {
} }
// Content // Content
@Input() data: Array<T> | null = []; readonly data = input<Array<T> | null>([]);
@Input() columns!: columnsNumber | number | null; readonly columns = input.required<columnsNumber | number | null>();
@Input() mode: ListMode | null = 'list'; readonly mode = input<ListMode | null>('list');
@Input() content: 'key' | 'values' = 'values'; readonly content = input<'key' | 'values'>('values');
/** /**
* dataDirection = colummn: 'headings will be dispplayed at left side' * dataDirection = colummn: 'headings will be dispplayed at left side'
* dataDirection = row: 'headings will be dispplayed at top' * dataDirection = row: 'headings will be dispplayed at top'
**/ **/
@Input() rows!: rowsNumber | null; readonly rows = input.required<rowsNumber | null>();
@Input() index!: number; readonly index = input.required<number>();
// Status // Status
@Input() isActive!: boolean; readonly isActive = input.required<boolean>();
// Behavior // Behavior
@Input() isExpandable = false; readonly isExpandable = input(false);
// Behavior // Behavior
@Input() isEditable = false; readonly isEditable = input(false);
// Skin // Skin
@Input() dataDirection: 'row' | 'column' = 'row'; readonly dataDirection = input<'row' | 'column'>('row');
@Input() cardType: cardType | null = 'vertical'; readonly cardType = input<cardType | null>('vertical');
@Input() appearance: 'outline' | 'ghost' = 'outline'; readonly appearance = input<'outline' | 'ghost'>('outline');
@Input() hasPdding = true; readonly hasPdding = input(true);
@Input() justify: justify = 'space-between'; readonly justify = input<justify>('space-between');
@Output() clickItem: EventEmitter<T> = new EventEmitter<T>(); readonly clickItem = output<T>();
@ViewChild('gridItem', { static: false }) gridItemRef: ElementRef<Element> | undefined; readonly gridItemRef = viewChild<ElementRef<Element>>('gridItem');
@ContentChild('keysTemplate', { static: false }) @ContentChild('keysTemplate', { static: false })
keysTemplateRef: TemplateRef<unknown> | undefined; keysTemplateRef: TemplateRef<unknown> | undefined;
@ContentChild('valuesTemplate', { static: false }) readonly valuesTemplateRef = contentChild<TemplateRef<unknown>>('valuesTemplate');
valuesTemplateRef: TemplateRef<unknown> | undefined;
@ContentChild('expandableKeysTemplate', { static: false }) readonly expandableKeysTemplateRef = contentChild<TemplateRef<unknown>>('expandableKeysTemplate');
expandableKeysTemplateRef: TemplateRef<unknown> | undefined;
@ContentChild('expandableValuesTemplate', { static: false }) readonly expandableValuesTemplateRef = contentChild<TemplateRef<unknown>>('expandableValuesTemplate');
expandableValuesTemplateRef: TemplateRef<unknown> | undefined;
trackByFn(index: number): number | undefined { trackByFn(index: number): number | undefined {
return index; return index;
@@ -106,7 +103,8 @@ export class PiDataGridComponent<T> {
* *
*/ */
protected get appearanceClass(): string { protected get appearanceClass(): string {
return this.appearance ? `pi-data-grid__appearance--${this.appearance}` : ''; const appearance = this.appearance();
return appearance ? `pi-data-grid__appearance--${appearance}` : '';
} }
/** /**
@@ -127,7 +125,8 @@ export class PiDataGridComponent<T> {
* *
*/ */
protected get cardTypeClass(): string { protected get cardTypeClass(): string {
return this.mode === 'grid' && this.cardType ? `pi-data-grid__card-type--${this.cardType}` : ''; const cardTypeValue = this.cardType();
return this.mode() === 'grid' && cardTypeValue ? `pi-data-grid__card-type--${cardTypeValue}` : '';
} }
/** /**
@@ -138,7 +137,8 @@ export class PiDataGridComponent<T> {
* *
*/ */
protected get columnsClass(): string { protected get columnsClass(): string {
return this.columns && `pi-data-grid__columns--${this.columns}`; const columns = this.columns();
return columns && `pi-data-grid__columns--${columns}`;
} }
/** /**
@@ -149,8 +149,9 @@ export class PiDataGridComponent<T> {
* *
*/ */
protected get dataDirectionClass(): string { protected get dataDirectionClass(): string {
return this.mode === 'list' && this.dataDirection const dataDirection = this.dataDirection();
? `pi-data-grid__data-direction--${this.dataDirection}` return this.mode() === 'list' && dataDirection
? `pi-data-grid__data-direction--${dataDirection}`
: ''; : '';
} }
@@ -161,7 +162,7 @@ export class PiDataGridComponent<T> {
* *
*/ */
protected get isEditableClass(): string { protected get isEditableClass(): string {
return this.dataDirection === 'column' && this.isEditable ? 'is-editable' : ''; return this.dataDirection() === 'column' && this.isEditable() ? 'is-editable' : '';
} }
/** /**
@@ -171,7 +172,7 @@ export class PiDataGridComponent<T> {
* *
*/ */
protected get isExpandableClass(): string { protected get isExpandableClass(): string {
return this.dataDirection === 'row' && this.isExpandable ? 'is-expandable' : ''; return this.dataDirection() === 'row' && this.isExpandable() ? 'is-expandable' : '';
} }
/** /**
@@ -181,8 +182,9 @@ export class PiDataGridComponent<T> {
* *
*/ */
protected get justifyClass(): string { protected get justifyClass(): string {
return this.dataDirection === 'column' && this.justify const justifyValue = this.justify();
? `pi-data-grid__justify-content--${this.justify}` return this.dataDirection() === 'column' && justifyValue
? `pi-data-grid__justify-content--${justifyValue}`
: ''; : '';
} }
@@ -203,7 +205,8 @@ export class PiDataGridComponent<T> {
* *
*/ */
protected get modeClass(): string { protected get modeClass(): string {
return this.mode && `pi-data-grid__mode--${this.mode}`; const mode = this.mode();
return mode && `pi-data-grid__mode--${mode}`;
} }
/** /**
@@ -214,6 +217,7 @@ export class PiDataGridComponent<T> {
* *
*/ */
protected get rowsClass(): string { protected get rowsClass(): string {
return this.rows && `pi-data-grid__rows--${this.rows}`; const rows = this.rows();
return rows && `pi-data-grid__rows--${rows}`;
} }
} }

View File

@@ -1,25 +1,28 @@
import { AfterViewInit, Directive, ElementRef, HostBinding, Input } from '@angular/core'; import { AfterViewInit, Directive, ElementRef, HostBinding, input } from '@angular/core';
import { itemType } from '../entities/data-grid.model'; import { itemType } from '../entities/data-grid.model';
@Directive({ selector: '[pi-cell]', }) @Directive({ selector: '[pi-cell]', })
export class PiCellDirective implements AfterViewInit { export class PiCellDirective implements AfterViewInit {
@HostBinding(`class`) get cellType(): string | undefined { @HostBinding(`class`) get cellType(): string | undefined {
const textAlignClass = this.textAlign !== 'left' && `pi-cell__text-align--${this.textAlign}`; const textAlign = this.textAlign();
const indentClass = this.indent ? 'pi-cell__indented' : ''; const textAlignClass = textAlign !== 'left' && `pi-cell__text-align--${textAlign}`;
const typeClass = this.type !== 'default' && `pi-cell__type--${this.type}`; const indentClass = this.indent() ? 'pi-cell__indented' : '';
const type = this.type();
const typeClass = type !== 'default' && `pi-cell__type--${type}`;
return ['pi-cell', textAlignClass, indentClass, typeClass].join(' '); return ['pi-cell', textAlignClass, indentClass, typeClass].join(' ');
} }
@Input() type: itemType = 'default'; readonly type = input<itemType>('default');
@Input() indent = false; readonly indent = input(false);
@Input() index!: number; readonly index = input.required<number>();
@Input() textAlign: 'left' | 'center' | 'right' = 'left'; readonly textAlign = input<'left' | 'center' | 'right'>('left');
constructor(private elRef: ElementRef) {} constructor(private elRef: ElementRef) {}
ngAfterViewInit(): void { ngAfterViewInit(): void {
if (this.index !== undefined) { const index = this.index();
this.elRef?.nativeElement?.classList.add(`pi-cell__${this.index}`); if (index !== undefined) {
this.elRef?.nativeElement?.classList.add(`pi-cell__${index}`);
} }
} }
} }

View File

@@ -1,23 +1,24 @@
import { Directive, ElementRef, HostBinding, HostListener, Input, Renderer2 } from '@angular/core'; import { Directive, ElementRef, HostBinding, HostListener, Renderer2, input } from '@angular/core';
import { columnsNumber } from '../entities/data-grid.model'; import { columnsNumber } from '../entities/data-grid.model';
@Directive({ selector: '[pi-grid-item]', }) @Directive({ selector: '[pi-grid-item]', })
export class PiGridItemDirective { export class PiGridItemDirective {
@HostBinding(`class`) get gridItemType(): string { @HostBinding(`class`) get gridItemType(): string {
const isExpandableClass = this.isExpandable ? 'is-expandable' : ''; const isExpandableClass = this.isExpandable() ? 'is-expandable' : '';
const isSelectedClass = this.isSelected ? 'is-selected' : ''; const isSelectedClass = this.isSelected() ? 'is-selected' : '';
const contextClass = this.context && `pi-grid-item__context--${this.context}`; const context = this.context();
const contextClass = context && `pi-grid-item__context--${context}`;
return ['pi-grid-item', isExpandableClass, isSelectedClass, contextClass].join(' '); return ['pi-grid-item', isExpandableClass, isSelectedClass, contextClass].join(' ');
} }
@Input() isExpandable = false; readonly isExpandable = input(false);
@Input() isSelected = false; readonly isSelected = input(false);
@Input() context!: 'keys' | 'values'; readonly context = input.required<'keys' | 'values'>();
@Input() cells!: columnsNumber | null; readonly cells = input.required<columnsNumber | null>();
@Input() isActive!: boolean | null; readonly isActive = input.required<boolean | null>();
@Input() isVisited = false; readonly isVisited = input(false);
constructor(private elRef: ElementRef, private renderer: Renderer2) {} constructor(private elRef: ElementRef, private renderer: Renderer2) {}

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input, OnInit } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, OnInit, input } from '@angular/core';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
import { DataTableItem } from '../entities/data-table-item.model'; import { DataTableItem } from '../entities/data-table-item.model';
@@ -25,10 +25,10 @@ export class PiBaseDataTableComponent implements OnInit {
this.typeClass, this.typeClass,
].join(' '); ].join(' ');
} }
@Input() items: Array<DataTableItem>; readonly items = input<Array<DataTableItem>>();
@Input() dataDirection: 'row' | 'column' = 'row'; readonly dataDirection = input<'row' | 'column'>('row');
@Input() type: 'header' | 'data' = 'data'; readonly type = input<'header' | 'data'>('data');
@Input() hideValues = false; readonly hideValues = input(false);
constructor(private store: Store<DeviceConfigsState>) {} constructor(private store: Store<DeviceConfigsState>) {}
@@ -39,11 +39,11 @@ export class PiBaseDataTableComponent implements OnInit {
} }
get dataDirectionClass(): string { get dataDirectionClass(): string {
return `pi-base-data-table__data-direction--${this.dataDirection}`; return `pi-base-data-table__data-direction--${this.dataDirection()}`;
} }
get typeClass(): string | void { get typeClass(): string | void {
return this.type === 'header' && 'pi-base-data-table__type--header'; return this.type() === 'header' && 'pi-base-data-table__type--header';
} }
private hideHeader(): void { private hideHeader(): void {
@@ -51,8 +51,8 @@ export class PiBaseDataTableComponent implements OnInit {
.select(DeviceTypeSelectors.getDevice) .select(DeviceTypeSelectors.getDevice)
.pipe(take(1)) .pipe(take(1))
.subscribe((deviceType: string) => { .subscribe((deviceType: string) => {
if (deviceType !== 'DESKTOP' && this.hideValues) { if (deviceType !== 'DESKTOP' && this.hideValues()) {
delete this.items[0].values; delete this.items()[0].values;
} }
}); });
} }

View File

@@ -1,4 +1,4 @@
<ng-container [ngSwitch]="type"> <ng-container [ngSwitch]="type()">
<ng-container *ngSwitchCase="'amount'" [ngTemplateOutlet]="amount"> <ng-container *ngSwitchCase="'amount'" [ngTemplateOutlet]="amount">
</ng-container> </ng-container>
@@ -20,19 +20,19 @@
<ng-template #amount> <ng-template #amount>
<span> <span>
{{ display }} {{ display() }}
</span> </span>
</ng-template> </ng-template>
<ng-template #text> <ng-template #text>
<span> <span>
{{ display }} {{ display() }}
</span> </span>
</ng-template> </ng-template>
<ng-template #date> <ng-template #date>
<span> <span>
{{ display }} {{ display() }}
</span> </span>
</ng-template> </ng-template>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, input } from '@angular/core';
import { NgSwitch, NgSwitchCase, NgTemplateOutlet } from '@angular/common'; import { NgSwitch, NgSwitchCase, NgTemplateOutlet } from '@angular/common';
import { PiBaseDataTableComponent } from '../base-data-table/base-data-table.component'; import { PiBaseDataTableComponent } from '../base-data-table/base-data-table.component';
@@ -19,25 +19,25 @@ export class PiCellTypeComponent {
@HostBinding('class') className = 'pi-cell-type'; @HostBinding('class') className = 'pi-cell-type';
@HostBinding('class.pi-cell-type__amount') get isAmount(): boolean { @HostBinding('class.pi-cell-type__amount') get isAmount(): boolean {
return this.type === 'amount'; return this.type() === 'amount';
} }
@HostBinding('class.pi-cell-type__text') get isText(): boolean { @HostBinding('class.pi-cell-type__text') get isText(): boolean {
return this.type === 'text'; return this.type() === 'text';
} }
@HostBinding('class.pi-cell-type__date') get isDate(): boolean { @HostBinding('class.pi-cell-type__date') get isDate(): boolean {
return this.type === 'date'; return this.type() === 'date';
} }
@HostBinding('class.pi-cell-type__input') get isInput(): boolean { @HostBinding('class.pi-cell-type__input') get isInput(): boolean {
return this.type === 'input'; return this.type() === 'input';
} }
@HostBinding('class.pi-cell-type__action') get isAction(): boolean { @HostBinding('class.pi-cell-type__action') get isAction(): boolean {
return this.type === 'action'; return this.type() === 'action';
} }
@Input() type: PiDropdownSelectorTemplateType = 'text'; readonly type = input<PiDropdownSelectorTemplateType>('text');
/** /**
* Data to display * Data to display
*/ */
@Input() display: string | number; readonly display = input<string | number>();
} }

View File

@@ -1,9 +1,9 @@
<h4>{{ heading }}</h4> <h4>{{ heading() }}</h4>
<article #componentItem> <article #componentItem>
<ng-container *ngFor="let value of values; let i = index"> <ng-container *ngFor="let value of values(); let i = index">
<ng-content <ng-content
*ngTemplateOutlet="item; context: { value: value, index: i }" *ngTemplateOutlet="item(); context: { value: value, index: i }"
></ng-content> ></ng-content>
</ng-container> </ng-container>
</article> </article>

View File

@@ -2,12 +2,12 @@ import {
AfterViewInit, AfterViewInit,
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
ContentChild,
ElementRef, ElementRef,
HostBinding, HostBinding,
Input,
TemplateRef, TemplateRef,
ViewChild, input,
contentChild,
viewChild
} from '@angular/core'; } from '@angular/core';
import { textStyle, justify, borderType } from '../../../styles'; import { textStyle, justify, borderType } from '../../../styles';
import { Store } from '@ngrx/store'; import { Store } from '@ngrx/store';
@@ -33,14 +33,14 @@ export class PiDataTableItemComponent implements AfterViewInit {
return ['pi-data-table-item', this.textStyleClass, this.justifyClass].join(' '); return ['pi-data-table-item', this.textStyleClass, this.justifyClass].join(' ');
} }
@Input() heading: string; readonly heading = input<string>();
@Input() values: Array<string>; readonly values = input<Array<string>>();
@Input() textStyle: textStyle = 'regular'; readonly textStyle = input<textStyle>('regular');
@Input() justify: justify = 'flex-start'; readonly justify = input<justify>('flex-start');
@Input() borderType: borderType; readonly borderType = input<borderType>();
@ContentChild('item', { static: false }) item: TemplateRef<Element>; readonly item = contentChild<TemplateRef<Element>>('item');
@ViewChild('componentItem') componentItem: ElementRef; readonly componentItem = viewChild<ElementRef>('componentItem');
constructor(private store: Store<DeviceConfigsState>) {} constructor(private store: Store<DeviceConfigsState>) {}
@@ -49,16 +49,16 @@ export class PiDataTableItemComponent implements AfterViewInit {
} }
setCellClassByIndex(): void { setCellClassByIndex(): void {
this.values?.forEach((item: string, index: number) => { this.values()?.forEach((item: string, index: number) => {
this.componentItem.nativeElement.children[index].classList.add(`pi-cell__${index}`); this.componentItem().nativeElement.children[index].classList.add(`pi-cell__${index}`);
}); });
} }
get textStyleClass(): string { get textStyleClass(): string {
return `pi-data-table-item__text-style--${this.textStyle}`; return `pi-data-table-item__text-style--${this.textStyle()}`;
} }
get justifyClass(): string { get justifyClass(): string {
return `pi-data-table-item__justify--${this.justify}`; return `pi-data-table-item__justify--${this.justify()}`;
} }
} }

View File

@@ -1,10 +1,10 @@
<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>
<ng-template #labelDividerTemplate <ng-template #labelDividerTemplate
><hr align="center" /> ><hr align="center" />
<span>{{ label }}</span> <span>{{ label() }}</span>
<hr align="center" /> <hr align="center" />
</ng-template> </ng-template>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, Input, input } from '@angular/core';
// Shared // Shared
import { alignment, gap, iconSize, textColor } from '../../../styles'; import { alignment, gap, iconSize, textColor } from '../../../styles';
@@ -14,16 +14,21 @@ import { PiIconComponent } from '../../icon/icon/icon.component';
}) })
export class PiDividerComponent { export class PiDividerComponent {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const aligmentClass = `pi-divider__aligment--${this.aligment}`; const aligmentClass = `pi-divider__aligment--${this.aligment()}`;
const colorClass = this.color && `pi-color__${this.color}`; const color = this.color();
const gapClass = this.gap && `pi-divider__gap--${this.gap}`; const colorClass = color && `pi-color__${color}`;
const gapValue = this.gap();
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 iconSizeClass = !!this.iconSize && `pi-divider__icon-size--${this.iconSize}`; const iconSizeValue = this.iconSize();
const labelClass = this.label ? 'has-label' : ''; const iconSizeClass = !!iconSizeValue && `pi-divider__icon-size--${iconSizeValue}`;
const positionClass = !!this.iconPosition && `pi-divider__position--${this.iconPosition}`; const labelClass = this.label() ? 'has-label' : '';
const widthClass = `pi-divider__width--${this.width}`; const iconPosition = this.iconPosition();
const directionClass = !!this.direction && `pi-divider__direction--${this.direction}`; const positionClass = !!iconPosition && `pi-divider__position--${iconPosition}`;
const widthClass = `pi-divider__width--${this.width()}`;
const direction = this.direction();
const directionClass = !!direction && `pi-divider__direction--${direction}`;
return [ return [
'pi-divider', 'pi-divider',
aligmentClass, aligmentClass,
@@ -39,20 +44,20 @@ export class PiDividerComponent {
].join(' '); ].join(' ');
} }
@Input() width: 'xx-small' | 'x-small' | 'small' | 'medium' | 'large' = 'large'; readonly width = input<'xx-small' | 'x-small' | 'small' | 'medium' | 'large'>('large');
@Input() height: 'small' | 'medium' | 'large' = 'small'; readonly height = input<'small' | 'medium' | 'large'>('small');
// Color: 1st hr 2nd icon // Color: 1st hr 2nd icon
@Input() color!: textColor; readonly color = input.required<textColor>();
@Input() gap!: gap; readonly gap = input.required<gap>();
@Input() aligment: alignment = 'center'; readonly aligment = input<alignment>('center');
@Input() direction: 'horizontal' | 'vertical' = 'horizontal'; readonly direction = input<'horizontal' | 'vertical'>('horizontal');
// Divider Icon // Divider Icon
@Input() icon!: string; @Input() icon!: string;
@Input() iconSize: iconSize = 'x-large'; readonly iconSize = input<iconSize>('x-large');
@Input() iconType: 'fill' | 'default' = 'default'; readonly iconType = input<'fill' | 'default'>('default');
@Input() iconPosition: 'before' | 'after' = 'after'; readonly iconPosition = input<'before' | 'after'>('after');
@Input() isStroked = false; readonly isStroked = input(false);
@Input() label!: string; readonly label = input.required<string>();
} }

View File

@@ -1,4 +1,4 @@
import { Component, EventEmitter, HostBinding, Injector, Input, Output } from '@angular/core'; import { Component, HostBinding, Injector, Input, input, output } 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';
@@ -24,23 +24,23 @@ export abstract class PiBaseFormFieldComponent extends PiFormControl {
].join(' '); ].join(' ');
} }
@Input() formControlName!: string; readonly formControlName = input.required<string>();
@Input() placeholder: string | undefined; readonly placeholder = input<string>();
@Input() isAutofocus = false; readonly isAutofocus = input(false);
@Input() appearance: 'outline' | 'fill' = 'outline'; readonly appearance = input<'outline' | 'fill'>('outline');
@Input() enableErrorMessage = true; readonly enableErrorMessage = input(true);
@Input() isControlStatusReactive = false; readonly isControlStatusReactive = input(false);
@Input() @Input()
set isDisabled(value: boolean) { set isDisabled(value: boolean) {
this.disabled = value; this.disabled = value;
// Update control status reactively // Update control status reactively
if (this.isControlStatusReactive) { if (this.isControlStatusReactive()) {
if (this.disabled) { if (this.disabled) {
this.control.disable(); this.control.disable();
} else { } else {
@@ -52,14 +52,14 @@ export abstract class PiBaseFormFieldComponent extends PiFormControl {
return this.disabled; return this.disabled;
} }
@Output() isActive: EventEmitter<boolean> = new EventEmitter(); readonly isActive = output<boolean>();
constructor(injector: Injector) { constructor(injector: Injector) {
super(injector); super(injector);
} }
get control(): FormControl { get control(): FormControl {
return this.controlContainer.control?.get(this.formControlName) as FormControl; return this.controlContainer.control?.get(this.formControlName()) as FormControl;
} }
get hasError(): boolean { get hasError(): boolean {

View File

@@ -1,4 +1,4 @@
<mat-form-field [appearance]="appearance" class="pi-currency-field" floatLabel="always"> <mat-form-field [appearance]="appearance()" class="pi-currency-field" floatLabel="always">
<mat-label><ng-content select="pi-label"></ng-content></mat-label> <mat-label><ng-content select="pi-label"></ng-content></mat-label>
<ng-container matSuffix> <ng-container matSuffix>
@@ -8,15 +8,15 @@
<input <input
*ngIf="control" *ngIf="control"
[formControl]="control" [formControl]="control"
[placeholder]="placeholder" [placeholder]="placeholder()"
matInput matInput
pi-autofocus pi-autofocus
[isAutofocusSelected]="isAutofocus" [isAutofocusSelected]="isAutofocus()"
currencyMask currencyMask
[options]="customCurrencyMaskConfig" [options]="customCurrencyMaskConfig"
/> />
<mat-error *ngIf="enableErrorMessage && control.errors"> <mat-error *ngIf="enableErrorMessage() && control.errors">
<ng-container *ngFor="let text of control.errors['message']"> <ng-container *ngFor="let text of control.errors['message']">
{{ text | translate }} {{ text | translate }}
</ng-container> </ng-container>

View File

@@ -1,21 +1,21 @@
import { Directive, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; import { Directive, OnChanges, OnInit, SimpleChanges, input } from '@angular/core';
import { MatInput } from '@angular/material/input'; import { MatInput } from '@angular/material/input';
@Directive({ selector: '[pi-autofocus]', }) @Directive({ selector: '[pi-autofocus]', })
export class PiAutofocusDirective implements OnInit, OnChanges { export class PiAutofocusDirective implements OnInit, OnChanges {
@Input() isAutofocusSelected: boolean; readonly isAutofocusSelected = input<boolean>();
constructor(private matInput: MatInput) {} constructor(private matInput: MatInput) {}
ngOnInit() { ngOnInit() {
if (this.isAutofocusSelected) { if (this.isAutofocusSelected()) {
setTimeout(() => this.matInput.focus()); setTimeout(() => this.matInput.focus());
} }
} }
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
if (changes) { if (changes) {
if (this.isAutofocusSelected) this.matInput.focus(); if (this.isAutofocusSelected()) this.matInput.focus();
} }
} }
} }

View File

@@ -1,14 +1,16 @@
import { Directive, HostBinding, Input } from '@angular/core'; import { Directive, HostBinding, input } from '@angular/core';
@Directive({ selector: '[pi-checkbox-grid]', }) @Directive({ selector: '[pi-checkbox-grid]', })
export class PiCheckboxGridDirective { export class PiCheckboxGridDirective {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const columns = this.columns();
const columnClass = const columnClass =
this.columns && `pi-checkbox-grid__column pi-checkbox-grid__column--${this.columns}`; columns && `pi-checkbox-grid__column pi-checkbox-grid__column--${columns}`;
const fillClass = this.fill && `pi-checkbox-grid__fill pi-checkbox-grid__fill--${this.fill}`; const fill = this.fill();
const fillClass = fill && `pi-checkbox-grid__fill pi-checkbox-grid__fill--${fill}`;
return ['pi-checkbox-grid', columnClass, fillClass].join(' '); return ['pi-checkbox-grid', columnClass, fillClass].join(' ');
} }
@Input() columns: '1' | '2' | '3' | '4' = '2'; readonly columns = input<'1' | '2' | '3' | '4'>('2');
@Input() fill: 'fit-content' | 'fill-available' = 'fit-content'; readonly fill = input<'fit-content' | 'fill-available'>('fit-content');
} }

View File

@@ -1,19 +1,19 @@
import { Directive, ElementRef, HostBinding, Input, OnInit, Renderer2 } from '@angular/core'; import { Directive, ElementRef, HostBinding, OnInit, Renderer2, input } from '@angular/core';
@Directive({ selector: 'pi-label', }) @Directive({ selector: 'pi-label', })
export class PiLabelDirective implements OnInit { export class PiLabelDirective implements OnInit {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const validationClass = this.isRequired ? 'is-required' : ''; const validationClass = this.isRequired() ? 'is-required' : '';
return ['pi-label', validationClass].join(' '); return ['pi-label', validationClass].join(' ');
} }
@HostBinding('class') className = 'pi-label'; @HostBinding('class') className = 'pi-label';
@Input() isRequired: boolean = false; readonly isRequired = input<boolean>(false);
constructor(private renderer: Renderer2, private el: ElementRef) {} constructor(private renderer: Renderer2, private el: ElementRef) {}
ngOnInit() { ngOnInit() {
if (this.isRequired) { if (this.isRequired()) {
const span = this.renderer.createElement('span'); const span = this.renderer.createElement('span');
const text = this.renderer.createText('*'); const text = this.renderer.createText('*');

View File

@@ -1,17 +1,20 @@
import { Directive, HostBinding, Input } from '@angular/core'; import { Directive, HostBinding, input } from '@angular/core';
import { justify } from '../../../styles'; import { justify } from '../../../styles';
// Flex // Flex
@Directive({ selector: '[pi-radio-grid]', }) @Directive({ selector: '[pi-radio-grid]', })
export class PiRadioGridDirective { export class PiRadioGridDirective {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const typeClass = this.type && `pi-radio-grid__type--${this.type}`; const type = this.type();
const justifyClass = this.justify && `pi-radio-grid__justify--${this.justify}`; const typeClass = type && `pi-radio-grid__type--${type}`;
const columnsClass = this.columns && `pi-radio-grid__columns--${this.columns}`; const justifyValue = this.justify();
const justifyClass = justifyValue && `pi-radio-grid__justify--${justifyValue}`;
const columns = this.columns();
const columnsClass = columns && `pi-radio-grid__columns--${columns}`;
return ['pi-radio-grid', typeClass, justifyClass, columnsClass].join(' '); return ['pi-radio-grid', typeClass, justifyClass, columnsClass].join(' ');
} }
@Input() type: 'grid' | 'flex' = 'flex'; readonly type = input<'grid' | 'flex'>('flex');
@Input() justify: justify = 'flex-start'; readonly justify = input<justify>('flex-start');
@Input() columns: 1 | 2 | 3 | 4 = 1; readonly columns = input<1 | 2 | 3 | 4>(1);
} }

View File

@@ -1,4 +1,4 @@
<mat-form-field [appearance]="appearance" floatLabel="always"> <mat-form-field [appearance]="appearance()" floatLabel="always">
<mat-label><ng-content select="pi-label"></ng-content></mat-label> <mat-label><ng-content select="pi-label"></ng-content></mat-label>
<ng-container matSuffix> <ng-container matSuffix>
@@ -7,12 +7,12 @@
<input <input
[formControl]="control" [formControl]="control"
[placeholder]="placeholder" [placeholder]="placeholder()"
autocomplete="username email" autocomplete="username email"
type="email" type="email"
matInput matInput
pi-autofocus pi-autofocus
[isAutofocusSelected]="isAutofocus" [isAutofocusSelected]="isAutofocus()"
/> />
<mat-error *ngIf="control.errors"> <mat-error *ngIf="control.errors">

View File

@@ -1,6 +1,6 @@
<section> <section>
<pi-icon <pi-icon
[icon]="icon" [icon]="icon()"
color="primary" color="primary"
size="x-large" size="x-large"
appearance="fill" appearance="fill"
@@ -10,14 +10,14 @@
<mat-label><ng-content select="pi-label"></ng-content></mat-label> <mat-label><ng-content select="pi-label"></ng-content></mat-label>
<input <input
[formControl]="control" [formControl]="control"
[placeholder]="placeholder" [placeholder]="placeholder()"
matInput matInput
pi-autofocus pi-autofocus
[isAutofocusSelected]="isAutofocus" [isAutofocusSelected]="isAutofocus()"
/> />
</mat-form-field> </mat-form-field>
</section> </section>
<mat-error *ngIf="control.errors && enableErrorMessage"> <mat-error *ngIf="control.errors && enableErrorMessage()">
<ng-container *ngFor="let text of control.errors['message']"> <ng-container *ngFor="let text of control.errors['message']">
{{ text | translate }} {{ text | translate }}
</ng-container> </ng-container>

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 { iconSize } from '../../../styles'; import { iconSize } from '../../../styles';
import { PiBaseFormFieldComponent } from '../base-form-field.component'; import { PiBaseFormFieldComponent } from '../base-form-field.component';
@@ -39,6 +39,6 @@ export class PiIconFieldComponent extends PiBaseFormFieldComponent {
return ['pi-form-field__text-icon'].join(' '); return ['pi-form-field__text-icon'].join(' ');
} }
@Input() icon: string | undefined; readonly icon = input<string>();
@Input() iconSize: iconSize = 'x-large'; readonly iconSize = input<iconSize>('x-large');
} }

View File

@@ -1,9 +1,9 @@
<mat-form-field appearance="fill" floatLabel="always"> <mat-form-field appearance="fill" floatLabel="always">
<mat-label><ng-content select="pi-label"></ng-content></mat-label> <mat-label><ng-content select="pi-label"></ng-content></mat-label>
<pi-prefix [unit]="this.unit === '$' ? this.unit : null" matPrefix> <pi-prefix [unit]="this.unit() === '$' ? this.unit() : null" matPrefix>
<pi-icon <pi-icon
*ngIf="showIncrementButtons" *ngIf="showIncrementButtons()"
(click)="decrementValue()" (click)="decrementValue()"
[class.is-disabled]="isDecrementButtonDisabled()" [class.is-disabled]="isDecrementButtonDisabled()"
icon="substract" icon="substract"
@@ -13,9 +13,9 @@
></pi-icon> ></pi-icon>
</pi-prefix> </pi-prefix>
<pi-suffix [unit]="this.unit !== '$' ? this.unit : null" matSuffix> <pi-suffix [unit]="this.unit() !== '$' ? this.unit() : null" matSuffix>
<pi-icon <pi-icon
*ngIf="showIncrementButtons" *ngIf="showIncrementButtons()"
(click)="incrementValue()" (click)="incrementValue()"
[class.is-disabled]="isIncrementButtonDisabled()" [class.is-disabled]="isIncrementButtonDisabled()"
icon="add" icon="add"
@@ -27,11 +27,11 @@
<input <input
[formControl]="control" [formControl]="control"
[placeholder]="placeholder" [placeholder]="placeholder()"
matInput matInput
type="number" type="number"
pi-autofocus pi-autofocus
[isAutofocusSelected]="isAutofocus" [isAutofocusSelected]="isAutofocus()"
/> />
<mat-error *ngIf="control.errors"> <mat-error *ngIf="control.errors">

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Injectable, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, Injectable, input } from '@angular/core';
import { NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms'; import { NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';
import { unit } from '../../../util/models'; import { unit } from '../../../util/models';
import { PiBaseFormFieldComponent } from '../base-form-field.component'; import { PiBaseFormFieldComponent } from '../base-form-field.component';
@@ -44,24 +44,25 @@ import { TranslatePipe } from '@ngx-translate/core';
export class PiIncrementalNumberFieldComponent extends PiBaseFormFieldComponent { export class PiIncrementalNumberFieldComponent extends PiBaseFormFieldComponent {
@HostBinding('class') className = 'pi-form-field__incremental'; @HostBinding('class') className = 'pi-form-field__incremental';
@Input() slot: number = 1; readonly slot = input<number>(1);
@Input() max: number; readonly max = input<number>();
@Input() min: number = 0; readonly min = input<number>(0);
@Input() unit: unit = ''; readonly unit = input<unit>('');
@Input() showIncrementButtons: boolean = true; readonly showIncrementButtons = input<boolean>(true);
incrementValue(): void { incrementValue(): void {
if (this.isDisabled) return if (this.isDisabled) return
let currentValue = Number(this.control.value); let currentValue = Number(this.control.value);
// Handle decimal numbers // Handle decimal numbers
if (Number.isInteger(this.slot)) { const slot = this.slot();
this.control.setValue(currentValue + this.slot); if (Number.isInteger(slot)) {
this.control.setValue(currentValue + slot);
} else { } else {
this.control.setValue((currentValue + this.slot).toFixed(2)); this.control.setValue((currentValue + slot).toFixed(2));
} }
} }
@@ -69,20 +70,22 @@ export class PiIncrementalNumberFieldComponent extends PiBaseFormFieldComponent
if (this.isDisabled) return if (this.isDisabled) return
let currentValue = Number(this.control.value); let currentValue = Number(this.control.value);
// Handle decimal numbers // Handle decimal numbers
if (Number.isInteger(this.slot)) { const slot = this.slot();
this.control.setValue(currentValue - this.slot); if (Number.isInteger(slot)) {
this.control.setValue(currentValue - slot);
} else { } else {
this.control.setValue((currentValue - this.slot).toFixed(2)); this.control.setValue((currentValue - slot).toFixed(2));
} }
} }
isIncrementButtonDisabled(): boolean { isIncrementButtonDisabled(): boolean {
return this.isDisabled || this.control.value + this.slot > this.max; return this.isDisabled || this.control.value + this.slot() > this.max();
} }
isDecrementButtonDisabled(): boolean { isDecrementButtonDisabled(): boolean {
if (this.min || this.min === 0) { const min = this.min();
return this.isDisabled || this.control.value - this.slot < this.min; if (min || min === 0) {
return this.isDisabled || this.control.value - this.slot() < min;
} }
return this.isDisabled; return this.isDisabled;
} }

View File

@@ -4,12 +4,12 @@
<span>{{ 'common.min' | translate }}</span> <span>{{ 'common.min' | translate }}</span>
</pi-label> </pi-label>
<pi-incremental-number-field <pi-incremental-number-field
[slot]="step" [slot]="step()"
[unit]="unit" [unit]="unit()"
[min]="minValue" [min]="minValue()"
formControlName="from" formControlName="from"
[placeholder]="placeholder" [placeholder]="placeholder()"
[showIncrementButtons]="showIncrementButtons" [showIncrementButtons]="showIncrementButtons()"
> >
</pi-incremental-number-field> </pi-incremental-number-field>
</div> </div>
@@ -21,16 +21,16 @@
<span>{{ 'common.max' | translate }}</span> <span>{{ 'common.max' | translate }}</span>
</pi-label> </pi-label>
<pi-incremental-number-field <pi-incremental-number-field
[slot]="step" [slot]="step()"
[unit]="unit" [unit]="unit()"
[min]="minValue" [min]="minValue()"
formControlName="to" formControlName="to"
[placeholder]="placeholder" [placeholder]="placeholder()"
[showIncrementButtons]="showIncrementButtons" [showIncrementButtons]="showIncrementButtons()"
> >
</pi-incremental-number-field> </pi-incremental-number-field>
</div> </div>
<mat-error *ngIf="form.errors && showError"> <mat-error *ngIf="form.errors && showError()">
<ng-container *ngFor="let text of form.errors['message']"> <ng-container *ngFor="let text of form.errors['message']">
{{ text | translate }} {{ text | translate }}
</ng-container> </ng-container>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, input } from '@angular/core';
import { FormGroup, FormGroupDirective, ReactiveFormsModule } from '@angular/forms'; import { FormGroup, FormGroupDirective, ReactiveFormsModule } from '@angular/forms';
import { unit } from '../../../util/models'; import { unit } from '../../../util/models';
import { PiLabelDirective } from '../directives/label.directive'; import { PiLabelDirective } from '../directives/label.directive';
@@ -29,18 +29,18 @@ export class PiIncrementalRangeFieldComponent {
@HostBinding('class') className = @HostBinding('class') className =
'pi-field pi-form-field__incremental pi-form-field__incremental--range'; 'pi-field pi-form-field__incremental pi-form-field__incremental--range';
@Input() formGroupName!: string; readonly formGroupName = input.required<string>();
@Input() minValue!: number; readonly minValue = input.required<number>();
@Input() maxValue!: number; readonly maxValue = input.required<number>();
@Input() unit!: unit; readonly unit = input.required<unit>();
@Input() step!: number; readonly step = input.required<number>();
@Input() showError = false; readonly showError = input(false);
@Input() showIncrementButtons: boolean = true; readonly showIncrementButtons = input<boolean>(true);
@Input() placeholder: string | undefined; readonly placeholder = input<string>();
constructor(private rootFormGroup: FormGroupDirective) {} constructor(private rootFormGroup: FormGroupDirective) {}
get form() { get form() {
return this.rootFormGroup.control.get(this.formGroupName) as FormGroup; return this.rootFormGroup.control.get(this.formGroupName()) as FormGroup;
} }
} }

View File

@@ -1,4 +1,4 @@
<mat-form-field [appearance]="appearance" floatLabel="always"> <mat-form-field [appearance]="appearance()" floatLabel="always">
<mat-label><ng-content select="pi-label"></ng-content></mat-label> <mat-label><ng-content select="pi-label"></ng-content></mat-label>
<ng-container matPrefix> <ng-container matPrefix>
@@ -11,11 +11,11 @@
<input <input
[formControl]="control" [formControl]="control"
[placeholder]="placeholder" [placeholder]="placeholder()"
matInput matInput
type="number" type="number"
pi-autofocus pi-autofocus
[isAutofocusSelected]="isAutofocus" [isAutofocusSelected]="isAutofocus()"
/> />
<mat-error *ngIf="control.errors"> <mat-error *ngIf="control.errors">

View File

@@ -1,4 +1,4 @@
<mat-form-field [appearance]="appearance" floatLabel="always"> <mat-form-field [appearance]="appearance()" floatLabel="always">
<mat-label><ng-content select="pi-label"></ng-content></mat-label> <mat-label><ng-content select="pi-label"></ng-content></mat-label>
<pi-suffix matSuffix> <pi-suffix matSuffix>
@@ -14,12 +14,12 @@
<input <input
[formControl]="control" [formControl]="control"
[placeholder]="placeholder" [placeholder]="placeholder()"
[type]="hide ? 'password' : 'text'" [type]="hide ? 'password' : 'text'"
autocomplete="current-password" autocomplete="current-password"
matInput matInput
pi-autofocus pi-autofocus
[isAutofocusSelected]="isAutofocus" [isAutofocusSelected]="isAutofocus()"
/> />
<mat-error *ngIf="control.errors"> <mat-error *ngIf="control.errors">

View File

@@ -1,4 +1,4 @@
<mat-form-field [appearance]="appearance" floatLabel="always"> <mat-form-field [appearance]="appearance()" floatLabel="always">
<mat-label><ng-content select="pi-label"></ng-content></mat-label> <mat-label><ng-content select="pi-label"></ng-content></mat-label>
<ng-container matSuffix> <ng-container matSuffix>
@@ -7,11 +7,11 @@
<input <input
[formControl]="control" [formControl]="control"
[placeholder]="placeholder" [placeholder]="placeholder()"
type="tel" type="tel"
matInput matInput
pi-autofocus pi-autofocus
[isAutofocusSelected]="isAutofocus" [isAutofocusSelected]="isAutofocus()"
/> />
<mat-error *ngIf="control.errors"> <mat-error *ngIf="control.errors">

View File

@@ -10,12 +10,12 @@
(focus)="onFocus()" (focus)="onFocus()"
(blur)="onBlur()" (blur)="onBlur()"
[formControl]="control" [formControl]="control"
[placeholder]="placeholder" [placeholder]="placeholder()"
type="search" type="search"
/> />
</fieldset> </fieldset>
<pi-icon <pi-icon
*ngIf="isClosable" *ngIf="isClosable()"
(click)="closeField()" (click)="closeField()"
icon="close" icon="close"
size="small" size="small"

View File

@@ -2,11 +2,11 @@ import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
ElementRef, ElementRef,
EventEmitter,
HostBinding, HostBinding,
Input, Input,
Output, input,
ViewChild, output,
viewChild
} from '@angular/core'; } from '@angular/core';
import { NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms'; import { NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';
import { OnInit } from '@angular/core'; import { OnInit } from '@angular/core';
@@ -38,14 +38,13 @@ export class PiSearchFieldComponent extends PiBaseFormFieldComponent implements
return ['pi-field pi-form-field__search', isExpandedClass].join(' '); return ['pi-field pi-form-field__search', isExpandedClass].join(' ');
} }
@Output() searchClick: EventEmitter<void> = new EventEmitter(); readonly searchClick = output<void>();
@Output() resetClick: EventEmitter<void> = new EventEmitter(); readonly resetClick = output<void>();
@ViewChild('input', { read: ElementRef, static: true }) readonly inputRef = viewChild.required('input', { read: ElementRef });
inputRef!: ElementRef;
@Input() isClosable = true; readonly isClosable = input(true);
private _isExpanded = false; private _isExpanded = false;
@Input() @Input()
@@ -59,7 +58,7 @@ export class PiSearchFieldComponent extends PiBaseFormFieldComponent implements
} }
ngOnInit(): void { ngOnInit(): void {
this.isExpanded = !!this.control.value || !this.isClosable; this.isExpanded = !!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 = true;
@@ -69,11 +68,11 @@ export class PiSearchFieldComponent extends PiBaseFormFieldComponent implements
openField(): void { openField(): void {
this.isExpanded = true; this.isExpanded = true;
this.inputRef.nativeElement.focus(); this.inputRef().nativeElement.focus();
} }
closeField(): void { closeField(): void {
if (this.isClosable) { if (this.isClosable()) {
this.isExpanded = false; this.isExpanded = false;
} }
if (this.control.value) { if (this.control.value) {

View File

@@ -1,4 +1,4 @@
<label [for]="formControlName + 'checkbox'"> <label [for]="formControlName() + 'checkbox'">
<input [formControl]="control" type="checkbox" [id]="formControlName + 'checkbox'" /> <input [formControl]="control" type="checkbox" [id]="formControlName() + 'checkbox'" />
<span></span> <span></span>
</label> </label>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, OnChanges, OnInit, SimpleChanges, 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';
@@ -22,7 +22,7 @@ export class PiSlideToggleComponent extends PiBaseFormFieldComponent implements
return ['pi-slide-toggle', isCheckedClass, this.isDisabledClass].join(' '); return ['pi-slide-toggle', isCheckedClass, this.isDisabledClass].join(' ');
} }
@Input() label: string | undefined; readonly label = input<string>();
isChecked!: boolean; isChecked!: boolean;

View File

@@ -1,4 +1,4 @@
<mat-form-field [appearance]="appearance" floatLabel="always"> <mat-form-field [appearance]="appearance()" floatLabel="always">
<mat-label><ng-content select="pi-label"></ng-content></mat-label> <mat-label><ng-content select="pi-label"></ng-content></mat-label>
<ng-container matSuffix> <ng-container matSuffix>
@@ -7,10 +7,10 @@
<input <input
[formControl]="control" [formControl]="control"
[placeholder]="placeholder" [placeholder]="placeholder()"
matInput matInput
pi-autofocus pi-autofocus
[isAutofocusSelected]="isAutofocus" [isAutofocusSelected]="isAutofocus()"
/> />
<mat-error *ngIf="control.errors"> <mat-error *ngIf="control.errors">

View File

@@ -1,10 +1,10 @@
<mat-form-field [appearance]="appearance" floatLabel="always"> <mat-form-field [appearance]="appearance()" floatLabel="always">
<mat-label><ng-content select="pi-label"></ng-content></mat-label> <mat-label><ng-content select="pi-label"></ng-content></mat-label>
<textarea <textarea
[formControl]="control" [formControl]="control"
[placeholder]="placeholder" [placeholder]="placeholder()"
[name]="formControlName" [name]="formControlName()"
[maxLength]="maxLength" [maxLength]="maxLength"
matInput matInput
cdkTextareaAutosize cdkTextareaAutosize
@@ -12,10 +12,10 @@
cdkAutosizeMinRows="4" cdkAutosizeMinRows="4"
cdkAutosizeMaxRows="2" cdkAutosizeMaxRows="2"
pi-autofocus pi-autofocus
[isAutofocusSelected]="isAutofocus" [isAutofocusSelected]="isAutofocus()"
></textarea> ></textarea>
</mat-form-field> </mat-form-field>
<mat-error *ngIf="control.errors && enableErrorMessage"> <mat-error *ngIf="control.errors && enableErrorMessage()">
<ng-container *ngFor="let text of control.errors['message']"> <ng-container *ngFor="let text of control.errors['message']">
{{ text | translate }} {{ text | translate }}
</ng-container> </ng-container>

View File

@@ -1,5 +1,5 @@
<img *ngIf="type === 'custom'; else defaultIcon" src="/assets/icons/{{ icon }}.svg" /> <img *ngIf="type() === 'custom'; else defaultIcon" src="/assets/icons/{{ icon() }}.svg" />
<ng-template #defaultIcon <ng-template #defaultIcon
><mat-icon size="size" [svgIcon]="icon" color="color"></mat-icon ><mat-icon size="size" [svgIcon]="icon()" color="color"></mat-icon
></ng-template> ></ng-template>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, input } from '@angular/core';
// Shared // Shared
import { import {
@@ -20,14 +20,20 @@ import { MatIcon } from '@angular/material/icon';
}) })
export class PiIconComponent { export class PiIconComponent {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const colorClass = this.color && `pi-color__${this.color}`; const color = this.color();
const sizeClass = this.size && `pi-icon__${this.size}`; const colorClass = color && `pi-color__${color}`;
const animationClass = this.animation && `pi-icon__animation--${this.animation}`; const size = this.size();
const sizeClass = size && `pi-icon__${size}`;
const animationValue = this.animation();
const animationClass = animationValue && `pi-icon__animation--${animationValue}`;
const appearance = this.appearance();
const appearanceClass = const appearanceClass =
this.appearance !== 'default' ? `pi-icon__appearance--${this.appearance}` : ''; appearance !== 'default' ? `pi-icon__appearance--${appearance}` : '';
const typeClass = this.type !== 'default' ? `pi-icon__type--${this.type}` : ''; const type = this.type();
const borderRadiusClass = this.borderRadius && `pi-icon__border-radius--${this.borderRadius}`; const typeClass = type !== 'default' ? `pi-icon__type--${type}` : '';
const strokeClass = this.isStroked === true && 'is-stroked'; const borderRadiusValue = this.borderRadius();
const borderRadiusClass = borderRadiusValue && `pi-icon__border-radius--${borderRadiusValue}`;
const strokeClass = this.isStroked() === true && 'is-stroked';
return [ return [
'pi-icon', 'pi-icon',
ComponentFamilyClass.Text, ComponentFamilyClass.Text,
@@ -40,12 +46,12 @@ export class PiIconComponent {
typeClass, typeClass,
].join(' '); ].join(' ');
} }
@Input() color: styleType; readonly color = input<styleType>();
@Input() icon: string; readonly icon = input<string>();
@Input() size: iconSize = 'medium'; readonly size = input<iconSize>('medium');
@Input() animation: animation; readonly animation = input<animation>();
@Input() appearance: 'fill' | 'default' = 'default'; readonly appearance = input<'fill' | 'default'>('default');
@Input() type: 'custom' | 'default' = 'default'; readonly type = input<'custom' | 'default'>('default');
@Input() borderRadius: borderRadius; readonly borderRadius = input<borderRadius>();
@Input() isStroked = false; readonly isStroked = input(false);
} }

View File

@@ -4,9 +4,9 @@ import {
Directive, Directive,
ElementRef, ElementRef,
HostBinding, HostBinding,
Input,
Renderer2, Renderer2,
OnDestroy, OnDestroy,
input
} from '@angular/core'; } from '@angular/core';
// RxJS // RxJS
@@ -19,26 +19,18 @@ import { takeUntil } from 'rxjs/operators';
@Directive({ selector: '[pi-background-image]', }) @Directive({ selector: '[pi-background-image]', })
export class PiBackgroundImageDirective implements AfterViewInit, OnDestroy { export class PiBackgroundImageDirective implements AfterViewInit, OnDestroy {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const animationClass = this.animation && `pi-background-image__animation--${this.animation}`; const animation = this.animation();
const animationClass = animation && `pi-background-image__animation--${animation}`;
return ['pi-image', 'pi-background-image', animationClass].join(' '); return ['pi-image', 'pi-background-image', animationClass].join(' ');
} }
@Input() extension: 'jpg' | 'png' | 'svg' | 'gif' | 'webp' = 'svg'; readonly extension = input<'jpg' | 'png' | 'svg' | 'gif' | 'webp'>('svg');
@Input() animation: 'infinite'; readonly animation = input<'infinite'>();
@Input() image: string | undefined; readonly image = input<string>();
@Input() isResponsive = false; readonly isResponsive = input(false);
@Input() origin: 'content-box' | 'border-box' = 'content-box'; readonly origin = input<'content-box' | 'border-box'>('content-box');
@Input() size: 'auto' | 'contain' | 'cover' = 'cover'; readonly size = input<'auto' | 'contain' | 'cover'>('cover');
@Input() position: readonly position = input<'left top' | 'left center' | 'left bottom' | 'right top' | 'right center' | 'right bottom' | 'center top' | 'center center' | 'center bottom'>('center center');
| 'left top'
| 'left center'
| 'left bottom'
| 'right top'
| 'right center'
| 'right bottom'
| 'center top'
| 'center center'
| 'center bottom' = 'center center';
private destroy$: Subject<void> = new Subject<void>(); private destroy$: Subject<void> = new Subject<void>();
@@ -63,25 +55,25 @@ export class PiBackgroundImageDirective implements AfterViewInit, OnDestroy {
const el = this.element.nativeElement; const el = this.element.nativeElement;
this.renderer.setStyle(el, 'background-repeat', 'no-repeat'); this.renderer.setStyle(el, 'background-repeat', 'no-repeat');
this.renderer.setStyle(el, 'background-position', this.position); this.renderer.setStyle(el, 'background-position', this.position());
this.renderer.setStyle(el, 'background-size', this.size); this.renderer.setStyle(el, 'background-size', this.size());
if (this.origin === 'content-box') { if (this.origin() === 'content-box') {
this.renderer.setStyle(el, 'background-origin', 'content-box'); this.renderer.setStyle(el, 'background-origin', 'content-box');
} else { } else {
this.renderer.setStyle(el, 'background-origin', 'border-box'); this.renderer.setStyle(el, 'background-origin', 'border-box');
} }
if (this.isResponsive) { if (this.isResponsive()) {
this.renderer.setStyle( this.renderer.setStyle(
el, el,
'background-image', 'background-image',
`url(https://d2m4265u67f2xu.cloudfront.net/assets/images/${this.image}-${device}.${this.extension})` `url(https://d2m4265u67f2xu.cloudfront.net/assets/images/${this.image()}-${device}.${this.extension()})`
); );
} else { } else {
this.renderer.setStyle( this.renderer.setStyle(
el, el,
'background-image', 'background-image',
`url(https://d2m4265u67f2xu.cloudfront.net/assets/images/${this.image}.${this.extension})` `url(https://d2m4265u67f2xu.cloudfront.net/assets/images/${this.image()}.${this.extension()})`
); );
} }
} }

View File

@@ -1,21 +1,21 @@
<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"
width="100%" width="100%"
height="auto" height="auto"
/></a> /></a>
<ng-template #image> <ng-template #image>
<img <img
src="https://d2m4265u67f2xu.cloudfront.net/assets/images/{{ pathImg }}" src="https://d2m4265u67f2xu.cloudfront.net/assets/images/{{ pathImg() }}"
[alt]="pathImg | sanitizerAltImage" [alt]="pathImg() | sanitizerAltImage"
width="100%" width="100%"
height="auto" height="auto"
/> />
</ng-template> </ng-template>
</figure> </figure>
<aside *ngIf="hasContent"> <aside *ngIf="hasContent()">
<ng-content select="pi-headline-label-content"></ng-content> <ng-content select="pi-headline-label-content"></ng-content>
<ng-content select="pi-headline-description-content"></ng-content> <ng-content select="pi-headline-description-content"></ng-content>
</aside> </aside>

View File

@@ -5,6 +5,7 @@ import {
Directive, Directive,
HostBinding, HostBinding,
Input, Input,
input
} from '@angular/core'; } from '@angular/core';
// Shared // Shared
@@ -38,12 +39,18 @@ export class PiHeadlineDescriptionContentDirective {}
}) })
export class PiHeadlineComponent { export class PiHeadlineComponent {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const alignmentClass = this.alignment && `pi-headline__align--${this.alignment}`; const alignmentValue = this.alignment();
const flexDirectionClass = this.flexDirection && `pi-headline__flex--${this.flexDirection}`; const alignmentClass = alignmentValue && `pi-headline__align--${alignmentValue}`;
const gapClass = this.gap && `pi-headline__gap--${this.gap}`; const flexDirectionValue = this.flexDirection();
const paddingClass = this.padding && `pi-headline__padding--${this.padding}`; const flexDirectionClass = flexDirectionValue && `pi-headline__flex--${flexDirectionValue}`;
const typeClass = this.type !== 'default' && `pi-headline__type--${this.type}`; const gapValue = this.gap();
const widthClass = this.width && `pi-headline__width--${this.width}`; const gapClass = gapValue && `pi-headline__gap--${gapValue}`;
const padding = this.padding();
const paddingClass = padding && `pi-headline__padding--${padding}`;
const type = this.type();
const typeClass = type !== 'default' && `pi-headline__type--${type}`;
const width = this.width();
const widthClass = width && `pi-headline__width--${width}`;
return [ return [
ComponentFamilyClass.Image, ComponentFamilyClass.Image,
@@ -56,24 +63,16 @@ export class PiHeadlineComponent {
paddingClass, paddingClass,
].join(' '); ].join(' ');
} }
@Input() alt: string | undefined; readonly alt = input<string>();
@Input() hasContent = true; readonly hasContent = input(true);
@Input() link!: string | undefined; @Input() link!: string | undefined;
@Input() pathImg!: string | undefined; readonly pathImg = input.required<string | undefined>();
// Skin // Skin
@Input() alignment: alignment = 'center'; readonly alignment = input<alignment>('center');
@Input() flexDirection: flexDirection = 'column'; readonly flexDirection = input<flexDirection>('column');
@Input() gap!: gap | undefined; readonly gap = input.required<gap | undefined>();
@Input() type: 'logo' | 'default' | 'avatar' = 'default'; readonly type = input<'logo' | 'default' | 'avatar'>('default');
@Input() padding: 'xxxxl' | 'xxxl' | 'xxl' | 'xl' | 'l' | 'm'; readonly padding = input<'xxxxl' | 'xxxl' | 'xxl' | 'xl' | 'l' | 'm'>();
@Input() width: readonly width = input<'quarter' | 'third' | 'half' | 'fifth' | 'sixth' | 'three-quarters' | 'two-thirds'>();
| 'quarter'
| 'third'
| 'half'
| 'fifth'
| 'sixth'
| 'three-quarters'
| 'two-thirds'
| undefined;
} }

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, Directive, HostBinding, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, Directive, HostBinding, input } from '@angular/core';
import { import {
alignment, alignment,
@@ -61,69 +61,77 @@ export class PiListItemComponent {
].join(' '); ].join(' ');
} }
@Input() alignment: alignment = 'center'; readonly alignment = input<alignment>('center');
@Input() appearance: 'default' | 'fill' = 'default'; readonly appearance = input<'default' | 'fill'>('default');
@Input() borderType!: borderType; readonly borderType = input.required<borderType>();
@Input() color!: styleType; readonly color = input.required<styleType>();
@Input() isActive = false; readonly isActive = input(false);
@Input() isClickable = false; readonly isClickable = input(false);
@Input() isDisabled = false; readonly isDisabled = input(false);
@Input() isResponsive = false; readonly isResponsive = input(false);
@Input() isStroked = false; readonly isStroked = input(false);
@Input() maxWidth: 'full' | 'half' | 'third' = 'full'; readonly maxWidth = input<'full' | 'half' | 'third'>('full');
@Input() mode: mode = 'light'; readonly mode = input<mode>('light');
@Input() opacity: opacity; readonly opacity = input<opacity>();
@Input() padding: padding = 'xl'; readonly padding = input<padding>('xl');
protected get alignmentClass(): string { protected get alignmentClass(): string {
return this.alignment ? `pi-list-item__align-items--${this.alignment}` : ''; const alignmentValue = this.alignment();
return alignmentValue ? `pi-list-item__align-items--${alignmentValue}` : '';
} }
protected get borderTypeClass(): string { protected get borderTypeClass(): string {
return this.borderType ? `pi-list-item__border--${this.borderType}` : ''; const borderTypeValue = this.borderType();
return borderTypeValue ? `pi-list-item__border--${borderTypeValue}` : '';
} }
protected get colorClass(): string { protected get colorClass(): string {
return this.color ? `pi-color__${this.color}` : ''; const color = this.color();
return color ? `pi-color__${color}` : '';
} }
protected get maxWidthClass(): string { protected get maxWidthClass(): string {
return this.maxWidth !== 'full' && `pi-list-item__max-width--${this.maxWidth}`; const maxWidth = this.maxWidth();
return maxWidth !== 'full' && `pi-list-item__max-width--${maxWidth}`;
} }
protected get isActiveClass(): string { protected get isActiveClass(): string {
return this.isActive ? 'is-active' : ''; return this.isActive() ? 'is-active' : '';
} }
protected get isClickableClass(): string { protected get isClickableClass(): string {
return this.isClickable ? 'is-clickable' : ''; return this.isClickable() ? 'is-clickable' : '';
} }
protected get isDisabledClass(): string { protected get isDisabledClass(): string {
return this.isDisabled ? 'is-disabled' : ''; return this.isDisabled() ? 'is-disabled' : '';
} }
protected get isResponsiveClass(): string { protected get isResponsiveClass(): string {
return this.isResponsive ? 'is-responsive' : ''; return this.isResponsive() ? 'is-responsive' : '';
} }
protected get isStrokedClass(): string { protected get isStrokedClass(): string {
return this.isStroked && this.appearance === 'fill' ? 'is-stroked' : ''; return this.isStroked() && this.appearance() === 'fill' ? 'is-stroked' : '';
} }
protected get paddingClass(): string { protected get paddingClass(): string {
return this.padding ? `pi-list-item__padding--${this.padding}` : ''; const paddingValue = this.padding();
return paddingValue ? `pi-list-item__padding--${paddingValue}` : '';
} }
protected get appearanceClass(): string { protected get appearanceClass(): string {
return this.appearance !== 'default' ? `pi-list-item__appearance--${this.appearance}` : ''; const appearance = this.appearance();
return appearance !== 'default' ? `pi-list-item__appearance--${appearance}` : '';
} }
protected get opacityClass(): string { protected get opacityClass(): string {
return this.opacity ? `pi-color__opacity--${this.opacity}` : ''; const opacityValue = this.opacity();
return opacityValue ? `pi-color__opacity--${opacityValue}` : '';
} }
protected get modeClass(): string | undefined { protected get modeClass(): string | undefined {
return this.mode ? `pi-color__mode--${this.mode}` : ''; const modeValue = this.mode();
return modeValue ? `pi-color__mode--${modeValue}` : '';
} }
} }

View File

@@ -5,6 +5,7 @@ import {
HostBinding, HostBinding,
Input, Input,
OnInit, OnInit,
input
} from '@angular/core'; } from '@angular/core';
import { NoteDataService } from '../infrastructure/note-data.service'; import { NoteDataService } from '../infrastructure/note-data.service';
import { NoteStatus } from '../entities/note.model'; import { NoteStatus } from '../entities/note.model';
@@ -41,18 +42,27 @@ export class PiNoteActionContentDirective {}
}) })
export class PiNoteComponent implements OnInit { export class PiNoteComponent implements OnInit {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const alignmentClass = this.alignment && `pi-note__align-items--${this.alignment}`; const alignmentValue = this.alignment();
const alignmentClass = alignmentValue && `pi-note__align-items--${alignmentValue}`;
const flexDirection = this.flexDirection();
const flexDirectionClass = const flexDirectionClass =
this.flexDirection && `pi-note__flex-direction--${this.flexDirection}`; flexDirection && `pi-note__flex-direction--${flexDirection}`;
const justifyContentClass = this.justify && `pi-note__justify-content--${this.justify}`; const justify = this.justify();
const gapClass = this.gap && `pi-note__gap--${this.gap}`; const justifyContentClass = justify && `pi-note__justify-content--${justify}`;
const isResponsiveClass = this.isResponsive && 'is-responsive'; const gapValue = this.gap();
const isStickyClass = this.isSticky && 'is-sticky'; const gapClass = gapValue && `pi-note__gap--${gapValue}`;
const modeClass = this.mode && `pi-note__mode--${this.mode}`; const isResponsiveClass = this.isResponsive() && 'is-responsive';
const positionClass = this.position && `pi-note__position--${this.position}`; const isStickyClass = this.isSticky() && 'is-sticky';
const statusClass = this.status && `pi-note__status--${this.status}`; const mode = this.mode();
const appearanceClass = this.appearance && `pi-note__appearance--${this.appearance}`; const modeClass = mode && `pi-note__mode--${mode}`;
const typeClass = this.type && `pi-note__${this.type}`; const position = this.position();
const positionClass = position && `pi-note__position--${position}`;
const statusValue = this.status();
const statusClass = statusValue && `pi-note__status--${statusValue}`;
const appearance = this.appearance();
const appearanceClass = appearance && `pi-note__appearance--${appearance}`;
const type = this.type();
const typeClass = type && `pi-note__${type}`;
return [ return [
'pi-note', 'pi-note',
alignmentClass, alignmentClass,
@@ -74,19 +84,19 @@ export class PiNoteComponent implements OnInit {
@Input() text!: string | undefined; @Input() text!: string | undefined;
// Behavior // Behavior
@Input() status!: status; readonly status = input.required<status>();
// Skin // Skin
@Input() alignment: alignment = 'center'; readonly alignment = input<alignment>('center');
@Input() justify: 'center' | 'space-between' = 'space-between'; readonly justify = input<'center' | 'space-between'>('space-between');
@Input() flexDirection: 'row' | 'column' = 'row'; readonly flexDirection = input<'row' | 'column'>('row');
@Input() gap!: gap | undefined; readonly gap = input.required<gap | undefined>();
@Input() isResponsive = false; readonly isResponsive = input(false);
@Input() isSticky = false; readonly isSticky = input(false);
@Input() mode: 'dark' | 'light' = 'light'; readonly mode = input<'dark' | 'light'>('light');
@Input() position: 'top' | 'bottom' = 'bottom'; readonly position = input<'top' | 'bottom'>('bottom');
@Input() appearance: 'fill' | 'ghost' = 'fill'; readonly appearance = input<'fill' | 'ghost'>('fill');
@Input() type: 'status' | 'simple' = 'status'; readonly type = input<'status' | 'simple'>('status');
noteStatus!: NoteStatus; noteStatus!: NoteStatus;
@@ -98,6 +108,6 @@ export class PiNoteComponent implements OnInit {
getNoteStatus(): NoteStatus { getNoteStatus(): NoteStatus {
const noteStatusOptions = this.noteDataService.getNoteStatusData(); const noteStatusOptions = this.noteDataService.getNoteStatusData();
return noteStatusOptions.find((noteStatus) => this.status === noteStatus.statusType); return noteStatusOptions.find((noteStatus) => this.status() === noteStatus.statusType);
} }
} }

View File

@@ -7,6 +7,6 @@
</section> </section>
<ng-template #button> <ng-template #button>
<button (click)="dismiss()" pi-ghost> <button (click)="dismiss()" pi-ghost>
{{ text }} {{ text() }}
</button> </button>
</ng-template> </ng-template>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Inject, Input, Optional, forwardRef } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, Inject, Input, 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';
@@ -37,7 +37,7 @@ export class PiSnackBarComponent {
@Input() message: string; @Input() message: string;
@Input() text: string; readonly text = input<string>();
@Input() icon: string; @Input() icon: string;
@Input() type: 'success' | 'error' | 'info' = 'success'; @Input() type: 'success' | 'error' | 'info' = 'success';
@Input() dismissible = false; @Input() dismissible = false;

View File

@@ -22,7 +22,7 @@
<footer> <footer>
<ng-container <ng-container
*ngIf="totalPages < 5; then defaultPaginator; else advancedPaginator" *ngIf="totalPages() < 5; then defaultPaginator; else advancedPaginator"
></ng-container> ></ng-container>
</footer> </footer>
@@ -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>
@@ -56,7 +56,7 @@
</div> </div>
<!-- when total pages > 6 || the difference is > 6 --> <!-- when total pages > 6 || the difference is > 6 -->
<pi-icon <pi-icon
*ngIf="totalPages > 6 && totalPages - firstPages[firstPages.length - 1] > 6" *ngIf="totalPages() > 6 && totalPages() - firstPages[firstPages.length - 1] > 6"
(click)="movePages()" (click)="movePages()"
icon="more" icon="more"
></pi-icon> ></pi-icon>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, EventEmitter, Component, Output, Input, OnInit, HostBinding, forwardRef } from '@angular/core'; import { ChangeDetectionStrategy, Component, Input, 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';
@@ -20,11 +20,11 @@ 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; @Input() currentPage = 1;
@Input() pageSize!: number; readonly pageSize = input.required<number>();
@Input() totalPages!: number; readonly totalPages = input.required<number>();
@Input() isDisabled!: boolean; readonly isDisabled = input.required<boolean>();
@Output() changePage = new EventEmitter(); readonly changePage = output();
firstPages!: number[]; firstPages!: number[];
@@ -35,7 +35,7 @@ export class PiPaginatorComponent implements OnInit {
} }
get pages() { get pages() {
return [...new Array(this.totalPages).keys()].map((el) => el + 1); return [...new Array(this.totalPages()).keys()].map((el) => el + 1);
} }
// get firstPages() { // get firstPages() {
@@ -51,11 +51,11 @@ export class PiPaginatorComponent implements OnInit {
// keys(o: object): string[]; // keys(o: object): string[];
get lastPages() { get lastPages() {
return [...new Array(this.totalPages).keys()].slice(-3).map((el) => el + 1); return [...new Array(this.totalPages()).keys()].slice(-3).map((el) => el + 1);
} }
protected initFirstPages(): void { protected initFirstPages(): void {
this.firstPages = [...new Array(this.totalPages).slice(0, 3).keys()].map((el) => el + 1); this.firstPages = [...new Array(this.totalPages()).slice(0, 3).keys()].map((el) => el + 1);
} }
protected paginateBack(): void { protected paginateBack(): void {
@@ -71,7 +71,7 @@ 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;
@@ -83,7 +83,7 @@ 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;
@@ -98,7 +98,7 @@ export class PiPaginatorComponent implements OnInit {
} }
protected movePages(): void { protected movePages(): void {
if (!this.isDisabled) { if (!this.isDisabled()) {
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];
@@ -107,13 +107,13 @@ export class PiPaginatorComponent implements OnInit {
} }
private incrementPaginatorRangeBy(range: number): Array<number> { private incrementPaginatorRangeBy(range: number): Array<number> {
return [...new Array(this.totalPages).keys()] return [...new Array(this.totalPages()).keys()]
.slice(this.firstPagesRange, this.firstPagesRange + range) .slice(this.firstPagesRange, this.firstPagesRange + range)
.map((el) => el + 1); .map((el) => el + 1);
} }
private decrementPaginatorRangeBy(range: number): Array<number> { private decrementPaginatorRangeBy(range: number): Array<number> {
return [...new Array(this.totalPages).keys()] return [...new Array(this.totalPages()).keys()]
.slice(this.firstPagesRange - range, this.firstPagesRange) .slice(this.firstPagesRange - range, this.firstPagesRange)
.map((el) => el + 1); .map((el) => el + 1);
} }

View File

@@ -1,5 +1,5 @@
// Angular // Angular
import { Directive, Input, OnInit } from '@angular/core'; import { Directive, OnInit, input } from '@angular/core';
import { import {
alignment, alignment,
borderRadius, borderRadius,
@@ -14,25 +14,25 @@ import { status } from '../../util/status';
@Directive() @Directive()
export abstract class PiBasePanelComponent implements OnInit { export abstract class PiBasePanelComponent implements OnInit {
@Input() alignment: alignment = 'center'; readonly alignment = input<alignment>('center');
@Input() borderRadius: borderRadius = 'large'; readonly borderRadius = input<borderRadius>('large');
@Input() color: styleType = 'white'; readonly color = input<styleType>('white');
@Input() flexDirection: flexDirection = 'column'; readonly flexDirection = input<flexDirection>('column');
@Input() isStroked = false; readonly isStroked = input(false);
@Input() hasShadow = false; readonly hasShadow = input(false);
@Input() justify: justify = 'center'; readonly justify = input<justify>('center');
@Input() width: 'quarter' | 'third' | 'half' | 'fifth' | 'sixth' | 'two-thirds' | undefined; readonly width = input<'quarter' | 'third' | 'half' | 'fifth' | 'sixth' | 'two-thirds'>();
// TODO: remove on the advisor dashboard refactor // TODO: remove on the advisor dashboard refactor
@Input() height!: 'small' | 'medium' | 'large' | 'x-large' | undefined; readonly height = input.required<'small' | 'medium' | 'large' | 'x-large' | undefined>();
@Input() outline!: styleType; readonly outline = input.required<styleType>();
@Input() padding: padding = 'l'; readonly padding = input<padding>('l');
@Input() grid: 'half' | 'quarter' | 'reverse-quarter'; readonly grid = input<'half' | 'quarter' | 'reverse-quarter'>();
@Input() gap: gap = 'l'; readonly gap = input<gap>('l');
@Input() mode: 'dark' | 'light' = 'dark'; readonly mode = input<'dark' | 'light'>('dark');
@Input() innerPadding!: padding; readonly innerPadding = input.required<padding>();
@Input() opacity: opacity; readonly opacity = input<opacity>();
@Input() status!: status | ''; readonly status = input.required<status | ''>();
ngOnInit(): void { ngOnInit(): void {
this.alignmentClass; this.alignmentClass;
@@ -51,72 +51,86 @@ export abstract class PiBasePanelComponent implements OnInit {
} }
protected get widthClass(): string | undefined { protected get widthClass(): string | undefined {
return this.width && `pi-panel__width--${this.width}`; const width = this.width();
return width && `pi-panel__width--${width}`;
} }
protected get alignmentClass(): string { protected get alignmentClass(): string {
return this.alignment && `pi-panel__align--${this.alignment}`; const alignmentValue = this.alignment();
return alignmentValue && `pi-panel__align--${alignmentValue}`;
} }
protected get justifyClass(): string { protected get justifyClass(): string {
return this.justify && `pi-panel__justify--${this.justify}`; const justifyValue = this.justify();
return justifyValue && `pi-panel__justify--${justifyValue}`;
} }
protected get flexDirectionClass(): string { protected get flexDirectionClass(): string {
return this.flexDirection && `pi-panel__flex-direction--${this.flexDirection}`; const flexDirectionValue = this.flexDirection();
return flexDirectionValue && `pi-panel__flex-direction--${flexDirectionValue}`;
} }
protected get borderRadiusClass(): string { protected get borderRadiusClass(): string {
return this.borderRadius && `pi-panel__border-radius--${this.borderRadius}`; const borderRadiusValue = this.borderRadius();
return borderRadiusValue && `pi-panel__border-radius--${borderRadiusValue}`;
} }
protected get colorClass(): string { protected get colorClass(): string {
return `pi-color__${this.color}`; return `pi-color__${this.color()}`;
} }
protected get hasShadowClass(): string | boolean { protected get hasShadowClass(): string | boolean {
return this.hasShadow && 'has-shadow'; return this.hasShadow() && 'has-shadow';
} }
protected get heightClass(): string | undefined { protected get heightClass(): string | undefined {
return this.height && `pi-panel__height--${this.height}`; const height = this.height();
return height && `pi-panel__height--${height}`;
} }
protected get outlineClass(): string { protected get outlineClass(): string {
return this.outline && `is-stroked pi-panel__border-color--${this.outline}`; const outline = this.outline();
return outline && `is-stroked pi-panel__border-color--${outline}`;
} }
protected get paddingClass(): string { protected get paddingClass(): string {
return this.padding ? `pi-panel__padding--${this.padding}` : ''; const paddingValue = this.padding();
return paddingValue ? `pi-panel__padding--${paddingValue}` : '';
} }
protected get gridClass(): string { protected get gridClass(): string {
return this.grid && `pi-panel__grid--${this.grid}`; const grid = this.grid();
return grid && `pi-panel__grid--${grid}`;
} }
protected get isStrokedClass(): string { protected get isStrokedClass(): string {
return this.isStroked ? 'is-stroked' : ''; return this.isStroked() ? 'is-stroked' : '';
} }
protected get gapClass(): string { protected get gapClass(): string {
return this.gap && `pi-panel__gap--${this.gap}`; const gapValue = this.gap();
return gapValue && `pi-panel__gap--${gapValue}`;
} }
protected get statusClass(): string { protected get statusClass(): string {
return this.status && `is-${this.status}`; const statusValue = this.status();
return statusValue && `is-${statusValue}`;
} }
protected get modeClass(): string { protected get modeClass(): string {
return this.mode && `pi-color__mode--${this.mode}`; const mode = this.mode();
return mode && `pi-color__mode--${mode}`;
} }
protected get innerPaddingClass(): string { protected get innerPaddingClass(): string {
return this.innerPadding const innerPadding = this.innerPadding();
? `pi-panel__inner-padding pi-panel__inner-padding--${this.innerPadding}` return innerPadding
? `pi-panel__inner-padding pi-panel__inner-padding--${innerPadding}`
: ''; : '';
} }
protected get opacityClass(): string { protected get opacityClass(): string {
return this.opacity ? `pi-color__opacity--${this.opacity}` : ''; const opacityValue = this.opacity();
return opacityValue ? `pi-color__opacity--${opacityValue}` : '';
} }
} }

View File

@@ -1,9 +1,9 @@
<ng-container *ngIf="type === 'form'; else customPanelTemplate"> <ng-container *ngIf="type() === 'form'; else customPanelTemplate">
<form [formGroup]="formGroup"> <form [formGroup]="formGroup()">
<!-- Top --> <!-- Top -->
<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>
@@ -38,7 +38,7 @@
<ng-template #customPanelTemplate> <ng-template #customPanelTemplate>
<!-- Top --> <!-- Top -->
<header class="pi-panel-content pi-panel-content__top"> <header class="pi-panel-content pi-panel-content__top">
<ng-container *ngTemplateOutlet="topContentRef"></ng-container> <ng-container *ngTemplateOutlet="topContentRef()"></ng-container>
<aside> <aside>
<pi-divider direction="vertical" color="grey" height="medium"></pi-divider> <pi-divider direction="vertical" color="grey" height="medium"></pi-divider>
<pi-icon <pi-icon
@@ -51,13 +51,13 @@
</header> </header>
<!-- Center --> <!-- Center -->
<ng-container *ngTemplateOutlet="centerContentRef"></ng-container> <ng-container *ngTemplateOutlet="centerContentRef()"></ng-container>
<!-- 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>
</section> </section>
</ng-template> </ng-template>

View File

@@ -1,15 +1,15 @@
import { import {
ChangeDetectionStrategy, ChangeDetectionStrategy,
Component, Component,
ContentChild,
EventEmitter,
HostBinding, HostBinding,
Input, Input,
OnChanges, OnChanges,
OnInit, OnInit,
Output,
SimpleChanges, SimpleChanges,
TemplateRef, TemplateRef,
input,
output,
contentChild
} from '@angular/core'; } from '@angular/core';
import { FormGroup, ReactiveFormsModule } from '@angular/forms'; import { FormGroup, ReactiveFormsModule } from '@angular/forms';
import { ComponentFamilyClass } from '../../../styles'; import { ComponentFamilyClass } from '../../../styles';
@@ -47,7 +47,8 @@ import { TranslatePipe } from '@ngx-translate/core';
}) })
export class PiExpansionPanelComponent extends PiBasePanelComponent implements OnInit, OnChanges { export class PiExpansionPanelComponent extends PiBasePanelComponent implements OnInit, OnChanges {
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const typeClass = this.type && `pi-panel__expansion--type-${this.type}`; const type = this.type();
const typeClass = type && `pi-panel__expansion--type-${type}`;
return [ return [
'pi-panel__expansion', 'pi-panel__expansion',
ComponentFamilyClass.Panel, ComponentFamilyClass.Panel,
@@ -58,24 +59,21 @@ export class PiExpansionPanelComponent extends PiBasePanelComponent implements O
].join(' '); ].join(' ');
} }
@Input() type: 'form' | 'custom' = 'form'; readonly type = input<'form' | 'custom'>('form');
@Input() isCollapsed = false; @Input() isCollapsed = false;
@Input() title: string | undefined; readonly title = input<string>();
@Input() subtitle: string | undefined; @Input() subtitle: string | undefined;
@Input() formGroup!: FormGroup; readonly formGroup = input.required<FormGroup>();
@Output() edit: EventEmitter<void> = new EventEmitter<void>(); readonly edit = output<void>();
@ContentChild('topContent', { static: false }) readonly topContentRef = contentChild<TemplateRef<unknown>>('topContent');
topContentRef: TemplateRef<unknown> | undefined;
@ContentChild('centerContent', { static: false }) readonly centerContentRef = contentChild<TemplateRef<unknown>>('centerContent');
centerContentRef: TemplateRef<unknown> | undefined;
@ContentChild('expandableContent', { static: false }) readonly expandableContentRef = contentChild<TemplateRef<unknown>>('expandableContent');
expandableContentRef: TemplateRef<unknown> | undefined;
override ngOnInit(): void { override ngOnInit(): void {
if (this.status === 'disabled') this.isCollapsed = true; if (this.status() === 'disabled') this.isCollapsed = true;
} }
ngOnChanges({ status }: SimpleChanges): void { ngOnChanges({ status }: SimpleChanges): void {
@@ -83,11 +81,12 @@ export class PiExpansionPanelComponent extends PiBasePanelComponent implements O
} }
toggle(): void { toggle(): void {
if (this.status !== 'disabled' && this.status !== 'info') this.isCollapsed = !this.isCollapsed; const status = this.status();
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,4 +1,4 @@
import { Directive, HostBinding, Input, OnInit } from '@angular/core'; import { Directive, HostBinding, OnInit, input } from '@angular/core';
import { padding } from '../../styles'; import { padding } from '../../styles';
@Directive() @Directive()
@@ -7,14 +7,15 @@ export abstract class PiBasePanelContentDirective implements OnInit {
return ['pi-panel-content', this.innerPaddingClass].join(' '); return ['pi-panel-content', this.innerPaddingClass].join(' ');
} }
@Input() innerPadding: padding; readonly innerPadding = input<padding>();
ngOnInit(): void { ngOnInit(): void {
this.innerPaddingClass; this.innerPaddingClass;
} }
protected get innerPaddingClass(): string { protected get innerPaddingClass(): string {
return this.innerPadding ? `pi-panel-content__inner-padding--${this.innerPadding}` : ''; const innerPadding = this.innerPadding();
return innerPadding ? `pi-panel-content__inner-padding--${innerPadding}` : '';
} }
} }

View File

@@ -1,2 +1,2 @@
<h5>{{ label }}</h5> <h5>{{ label() }}</h5>
<mat-progress-bar mode="determinate" [value]="value"></mat-progress-bar> <mat-progress-bar mode="determinate" [value]="value()"></mat-progress-bar>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input, forwardRef } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, forwardRef, input } from '@angular/core';
import { MatProgressBar } from '@angular/material/progress-bar'; import { MatProgressBar } from '@angular/material/progress-bar';
@@ -12,6 +12,6 @@ import { MatProgressBar } from '@angular/material/progress-bar';
export class PiProgressBarComponent { export class PiProgressBarComponent {
@HostBinding('class') className = 'pi-progress-bar'; @HostBinding('class') className = 'pi-progress-bar';
@Input() label!: string; readonly label = input.required<string>();
@Input() value!: number; readonly value = input.required<number>();
} }

View File

@@ -1,29 +1,29 @@
<!-- Progress spinner --> <!-- Progress spinner -->
<mat-progress-spinner <mat-progress-spinner
mode="determinate" mode="determinate"
[diameter]="diameter" [diameter]="diameter()"
[strokeWidth]="strokeWidth" [strokeWidth]="strokeWidth()"
[value]="value" [value]="value()"
> >
</mat-progress-spinner> </mat-progress-spinner>
<!-- Value --> <!-- Value -->
<span <span
*ngIf="appearance === 'compact'; else defaultValue" *ngIf="appearance() === 'compact'; else defaultValue"
[style.width]="diameter + 'px'" [style.width]="diameter() + 'px'"
[style.height]="diameter + 'px'" [style.height]="diameter() + 'px'"
[style.margin-top]="-diameter + 'px'" [style.margin-top]="-diameter() + 'px'"
[style.line-height]="diameter + 'px'" [style.line-height]="diameter() + 'px'"
> >
<span>{{ value }}</span <span>{{ value() }}</span
><span>%</span> ><span>%</span>
</span> </span>
<!-- Value --> <!-- Value -->
<ng-template #defaultValue> <ng-template #defaultValue>
<span> <span>
<span>{{ value }}</span <span>{{ value() }}</span
><span>%</span> ><span>%</span>
</span> </span>
<span>{{ label }}</span> <span>{{ label() }}</span>
</ng-template> </ng-template>

View File

@@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core'; import { ChangeDetectionStrategy, Component, HostBinding, input } from '@angular/core';
import { MatProgressSpinner } from '@angular/material/progress-spinner'; import { MatProgressSpinner } from '@angular/material/progress-spinner';
import { NgIf } from '@angular/common'; import { NgIf } from '@angular/common';
import { PiProgressBarComponent } from '../progress-bar/progress-bar.component'; import { PiProgressBarComponent } from '../progress-bar/progress-bar.component';
@@ -18,13 +18,17 @@ export class PiProgressSpinnerComponent {
className = 'pi-spinner pi-spinner__progress'; className = 'pi-spinner pi-spinner__progress';
@HostBinding('class') get classes(): string { @HostBinding('class') get classes(): string {
const appearance = this.appearance();
const appearanceClass = const appearanceClass =
this.appearance !== 'default' && `pi-spinner__appearance--${this.appearance}`; appearance !== 'default' && `pi-spinner__appearance--${appearance}`;
const strokeWidthClass = this.strokeWidth && `pi-spinner__stroke--${this.strokeWidth}`; const strokeWidth = this.strokeWidth();
const strokeWidthClass = strokeWidth && `pi-spinner__stroke--${strokeWidth}`;
const diameter = this.diameter();
const smallDiameterClass = const smallDiameterClass =
this.diameter === 60 || (this.diameter === 70 && 'pi-spinner__diameter--small'); diameter === 60 || (diameter === 70 && 'pi-spinner__diameter--small');
const diameterValue = this.diameter();
const largeDiameterClass = const largeDiameterClass =
this.diameter === 110 || (this.diameter === 130 && 'pi-spinner__diameter--large'); diameterValue === 110 || (diameterValue === 130 && 'pi-spinner__diameter--large');
return [ return [
'pi-spinner', 'pi-spinner',
'pi-spinner__progress', 'pi-spinner__progress',
@@ -35,9 +39,9 @@ export class PiProgressSpinnerComponent {
].join(' '); ].join(' ');
} }
@Input() value!: number; readonly value = input.required<number>();
@Input() label!: string; readonly label = input.required<string>();
@Input() appearance: 'compact' | 'default' = 'default'; readonly appearance = input<'compact' | 'default'>('default');
@Input() strokeWidth: 2 | 3 | 4 | 5 | 6 | 7 | 8 = 6; readonly strokeWidth = input<2 | 3 | 4 | 5 | 6 | 7 | 8>(6);
@Input() diameter!: number; readonly diameter = input.required<number>();
} }

View File

@@ -1,6 +1,5 @@
import { import {
Component, Component,
ContentChild,
ContentChildren, ContentChildren,
ElementRef, ElementRef,
HostBinding, HostBinding,
@@ -10,6 +9,10 @@ import {
Renderer2, Renderer2,
TemplateRef, TemplateRef,
ViewChildren, ViewChildren,
input,
contentChild,
contentChildren,
viewChildren
} 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';
@@ -44,12 +47,12 @@ export class PiBaseSelectComponent extends PiFormControl {
/** /**
* Name of the form control. * Name of the form control.
*/ */
@Input() formControlName!: string; readonly formControlName = input.required<string>();
/** /**
* Property used for comparisons. * Property used for comparisons.
*/ */
@Input() keyIdProperty!: string; readonly keyIdProperty = input.required<string>();
// ====================== // ======================
// SKIN // SKIN
@@ -58,47 +61,47 @@ export class PiBaseSelectComponent extends PiFormControl {
/** /**
* Specifies the layout for the menu. * Specifies the layout for the menu.
*/ */
@Input() menuType: 'default' | 'flex-wrap' | 'custom-grid' = 'default'; readonly menuType = input<'default' | 'flex-wrap' | 'custom-grid'>('default');
/** /**
* Defines the type of the select component. * Defines the type of the select component.
*/ */
@Input() type: 'default' | 'dropdown' = 'default'; readonly type = input<'default' | 'dropdown'>('default');
/** /**
* The appearance style of the select component. * The appearance style of the select component.
*/ */
@Input() appearance: 'fill' | 'outline' | 'ghost' = 'fill'; readonly appearance = input<'fill' | 'outline' | 'ghost'>('fill');
/** /**
* The type of the input. * The type of the input.
*/ */
@Input() pickedOptionType: 'button' | 'input' = 'input'; readonly pickedOptionType = input<'button' | 'input'>('input');
/** /**
* Defines the color of the select component and its child components. * Defines the color of the select component and its child components.
*/ */
@Input() color: SelectColor = 'primary'; readonly color = input<SelectColor>('primary');
/** /**
* Defines the color of the select component and its child components. * Defines the color of the select component and its child components.
*/ */
@Input() menuColor: 'white' | 'transparent' = 'white'; readonly menuColor = input<'white' | 'transparent'>('white');
/** /**
* The flex direction property binded in the select component is used to bind * The flex direction property binded in the select component is used to bind
* the pre definded select option flex direction property. * the pre definded select option flex direction property.
*/ */
@Input() flexDirection: SelectOptionFlexDirection = 'row'; readonly flexDirection = input<SelectOptionFlexDirection>('row');
/** /**
* The text align property defines if the picked option text in type dropdown cases and * The text align property defines if the picked option text in type dropdown cases and
* can extends the text alignment from the selec option when we use Back End data. * can extends the text alignment from the selec option when we use Back End data.
*/ */
@Input() textAlign: 'left' | 'center' = 'left'; readonly textAlign = input<'left' | 'center'>('left');
/** /**
* An optional icon for the select. * An optional icon for the select.
*/ */
@@ -107,7 +110,7 @@ export class PiBaseSelectComponent extends PiFormControl {
/** /**
* An optional icon for the select. * An optional icon for the select.
*/ */
@Input() actionIcon: actionIcon = 'chevron-forward'; readonly actionIcon = input<actionIcon>('chevron-forward');
// ====================== // ======================
// CONTENT & DATA // CONTENT & DATA
@@ -116,28 +119,28 @@ export class PiBaseSelectComponent extends PiFormControl {
/** /**
* List of selectable options. * List of selectable options.
*/ */
@Input() options: Array<any> = []; readonly options = input<Array<any>>([]);
/** /**
* An optional label for the select. * An optional label for the select.
*/ */
@Input() label: string | undefined; readonly label = input<string>();
/** /**
* Placeholder for the select when no option is selected. * Placeholder for the select when no option is selected.
*/ */
@Input() placeholder: string | undefined; readonly placeholder = input<string>();
/** /**
* Defines the select an select option size in picked option type button cases * Defines the select an select option size in picked option type button cases
*/ */
@Input() size: 'small' | 'medium' = 'medium'; readonly size = input<'small' | 'medium'>('medium');
/** /**
* Defines if the picked option is responsive or not (ie. if it disappears in small devices or not) * Defines if the picked option is responsive or not (ie. if it disappears in small devices or not)
*/ */
@Input() isResponsive = true; readonly isResponsive = input(true);
// ====================== // ======================
// CONTENT PROJECTIONS // CONTENT PROJECTIONS
@@ -146,13 +149,12 @@ export class PiBaseSelectComponent extends PiFormControl {
/** /**
* Provides a reference to the custom template for the options if provided. * Provides a reference to the custom template for the options if provided.
*/ */
@ContentChild('optionTemplate', { static: false }) readonly optionTemplateRef = contentChild.required<TemplateRef<any>>('optionTemplate');
optionTemplateRef!: TemplateRef<any>;
/** /**
* Provides a reference to the custom template for the selected item if provided. * Provides a reference to the custom template for the selected item if provided.
*/ */
@ContentChild('selectedTemplate') selectedTemplateRef!: TemplateRef<Element>; readonly selectedTemplateRef = contentChild.required<TemplateRef<Element>>('selectedTemplate');
/** /**
* 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.
@@ -167,8 +169,7 @@ export class PiBaseSelectComponent extends PiFormControl {
* that are content projected from an external consumer. * that are content projected from an external consumer.
* This provides direct access to their corresponding DOM elements. * This provides direct access to their corresponding DOM elements.
*/ */
@ContentChildren(PiSelectOptionComponent, { read: ElementRef }) readonly childrenOptionsEl = contentChildren(PiSelectOptionComponent, { read: ElementRef });
childrenOptionsEl!: QueryList<ElementRef>;
/** /**
* A query list of all the child option components that are directly defined (view children) * A query list of all the child option components that are directly defined (view children)
@@ -183,8 +184,7 @@ export class PiBaseSelectComponent extends PiFormControl {
* that are directly defined (view children) within this component's template. * that are directly defined (view children) within this component's template.
* This provides direct access to their corresponding DOM elements. * This provides direct access to their corresponding DOM elements.
*/ */
@ViewChildren(PiSelectOptionComponent, { read: ElementRef }) readonly viewChildOptionsEl = viewChildren(PiSelectOptionComponent, { read: ElementRef });
viewChildOptionsEl: QueryList<ElementRef>;
constructor( constructor(
protected el: ElementRef, protected el: ElementRef,
@@ -202,7 +202,7 @@ export class PiBaseSelectComponent extends PiFormControl {
* Returns the FormControl associated with the formControlName. * Returns the FormControl associated with the formControlName.
*/ */
get control(): FormControl { get control(): FormControl {
return this.controlContainer.control?.get(this.formControlName) as FormControl; return this.controlContainer.control?.get(this.formControlName()) as FormControl;
} }
/** /**
@@ -261,17 +261,17 @@ export class PiBaseSelectComponent extends PiFormControl {
get selectOptionSize(): SelectOptionSize { get selectOptionSize(): SelectOptionSize {
switch (true) { switch (true) {
case this.type === 'dropdown' && this.pickedOptionType === 'input': case this.type() === 'dropdown' && this.pickedOptionType() === 'input':
return SelectOptionSize.Small; return SelectOptionSize.Small;
case this.type === 'dropdown' && this.pickedOptionType === 'button' && this.size === 'medium': case this.type() === 'dropdown' && this.pickedOptionType() === 'button' && this.size() === 'medium':
return SelectOptionSize.Medium; return SelectOptionSize.Medium;
case this.type === 'dropdown' && this.pickedOptionType === 'button' && this.size === 'small': case this.type() === 'dropdown' && this.pickedOptionType() === 'button' && this.size() === 'small':
return SelectOptionSize.Small; return SelectOptionSize.Small;
case this.type === 'default' && this.menuType === 'flex-wrap': case this.type() === 'default' && this.menuType() === 'flex-wrap':
return SelectOptionSize.Large; return SelectOptionSize.Large;
case this.type === 'default' && this.menuType === 'default' && this.appearance === 'ghost': case this.type() === 'default' && this.menuType() === 'default' && this.appearance() === 'ghost':
return SelectOptionSize.Medium; return SelectOptionSize.Medium;
case this.type === 'default' && this.menuType === 'default' && this.appearance !== 'ghost': case this.type() === 'default' && this.menuType() === 'default' && this.appearance() !== 'ghost':
return SelectOptionSize.Small; return SelectOptionSize.Small;
default: default:
return SelectOptionSize.Medium; return SelectOptionSize.Medium;
@@ -287,10 +287,11 @@ export class PiBaseSelectComponent extends PiFormControl {
* Manage the Select & Select Multiple appearance property * Manage the Select & Select Multiple appearance property
*/ */
protected get appearanceClass(): string | undefined { protected get appearanceClass(): string | undefined {
const appearance = this.appearance();
return ( return (
this.menuType !== 'custom-grid' && this.menuType() !== 'custom-grid' &&
this.appearance && appearance &&
`pi-select__appearance--${this.appearance}` `pi-select__appearance--${appearance}`
); );
} }
@@ -306,7 +307,8 @@ export class PiBaseSelectComponent extends PiFormControl {
* color depending on the user profile. * color depending on the user profile.
*/ */
protected get colorClass(): string | undefined { protected get colorClass(): string | undefined {
return this.menuType !== 'custom-grid' && this.color && `pi-color__${this.color}`; const color = this.color();
return this.menuType() !== 'custom-grid' && color && `pi-color__${color}`;
} }
/** /**
@@ -315,8 +317,9 @@ export class PiBaseSelectComponent extends PiFormControl {
* This property can be modified from the default one (white) specially when type !== 'dropdown'. * This property can be modified from the default one (white) specially when type !== 'dropdown'.
*/ */
protected get menuColorClass(): string | undefined { protected get menuColorClass(): string | undefined {
return this.menuType !== 'custom-grid' && this.menuColor !== 'white' const menuColor = this.menuColor();
? `pi-select__menu-color--${this.menuColor}` return this.menuType() !== 'custom-grid' && menuColor !== 'white'
? `pi-select__menu-color--${menuColor}`
: ''; : '';
} }
@@ -332,9 +335,10 @@ export class PiBaseSelectComponent extends PiFormControl {
* to display the select options as a mosaic in the page * to display the select options as a mosaic in the page
*/ */
protected get menuTypeClass(): string | undefined { protected get menuTypeClass(): string | undefined {
return this.menuType === 'default' const menuType = this.menuType();
return menuType === 'default'
? 'pi-select__menu-type--column' ? 'pi-select__menu-type--column'
: `pi-select__menu-type--${this.menuType}`; : `pi-select__menu-type--${menuType}`;
} }
/** /**
@@ -350,11 +354,12 @@ export class PiBaseSelectComponent extends PiFormControl {
* it means that the picked option emulate a Button. * it means that the picked option emulate a Button.
*/ */
protected get pickedOptionTypeClass(): string | undefined { protected get pickedOptionTypeClass(): string | undefined {
const pickedOptionType = this.pickedOptionType();
return ( return (
this.type === 'dropdown' && this.type() === 'dropdown' &&
this.menuType !== 'custom-grid' && this.menuType() !== 'custom-grid' &&
this.pickedOptionType && pickedOptionType &&
`pi-select__picked-option-type--${this.pickedOptionType}` `pi-select__picked-option-type--${pickedOptionType}`
); );
} }
@@ -379,7 +384,8 @@ export class PiBaseSelectComponent extends PiFormControl {
* in a static positioned menu. * in a static positioned menu.
*/ */
protected get typeClass(): string | undefined { protected get typeClass(): string | undefined {
return this.type && `pi-select__type--${this.type}`; const type = this.type();
return type && `pi-select__type--${type}`;
} }
/** /**
@@ -399,10 +405,12 @@ export class PiBaseSelectComponent extends PiFormControl {
* *
*/ */
protected get textAlignClass(): string | boolean { protected get textAlignClass(): string | boolean {
return (this.textAlign !== 'left' && this.pickedOptionType === 'button') || const menuType = this.menuType();
(this.menuType === 'flex-wrap' && this.flexDirection === 'row') || const textAlign = this.textAlign();
this.menuType === 'default' return (textAlign !== 'left' && this.pickedOptionType() === 'button') ||
? `pi-select__text-align--${this.textAlign}` (menuType === 'flex-wrap' && this.flexDirection() === 'row') ||
menuType === 'default'
? `pi-select__text-align--${textAlign}`
: ''; : '';
} }
@@ -415,7 +423,8 @@ export class PiBaseSelectComponent extends PiFormControl {
* *
*/ */
protected get sizeClass(): string | boolean { protected get sizeClass(): string | boolean {
return this.pickedOptionType === 'button' && this.size ? `pi-select__${this.size}` : ''; const size = this.size();
return this.pickedOptionType() === 'button' && size ? `pi-select__${size}` : '';
} }
/** /**
@@ -426,6 +435,6 @@ export class PiBaseSelectComponent extends PiFormControl {
* When the Select Picked Option Type === Button * When the Select Picked Option Type === Button
*/ */
protected get isResponsiveClass(): string | boolean { protected get isResponsiveClass(): string | boolean {
return this.pickedOptionType === 'button' && this.isResponsive ? 'is-responsive' : ''; return this.pickedOptionType() === 'button' && this.isResponsive() ? 'is-responsive' : '';
} }
} }

View File

@@ -1,23 +1,23 @@
<pi-carousel <pi-carousel
class="pi-select-options-menu" class="pi-select-options-menu"
color="highlighted" color="highlighted"
[device]="device" [device]="device()"
[itemsDesktop]="itemsDesktop" [itemsDesktop]="itemsDesktop()"
[itemsTablet]="itemsTablet" [itemsTablet]="itemsTablet()"
[itemWidth]="itemWidth" [itemWidth]="itemWidth()"
[hideSwipeButtons]="hideSwipeButtons" [hideSwipeButtons]="hideSwipeButtons()"
[isDraggable]="isDraggable" [isDraggable]="isDraggable()"
[hidePaginator]="true" [hidePaginator]="true"
[movePerPage]="movePerPage" [movePerPage]="movePerPage()"
[justify]="justifyCarousel" [justify]="justifyCarousel()"
(itemChanged)="onItemChanged($event)" (itemChanged)="onItemChanged($event)"
> >
<ng-template pi-carousel-item *ngFor="let option of options; index as i; trackBy: trackByFn"> <ng-template pi-carousel-item *ngFor="let option of options(); index as i; trackBy: trackByFn">
<span class="pi-select__carousel--option"> <span class="pi-select__carousel--option">
<!-- Default options template menu for static data --> <!-- Default options template menu for static data -->
<ng-template #defaultOption> <ng-template #defaultOption>
<pi-select-option <pi-select-option
[id]="option?.[keyIdProperty] || option?.id" [id]="option?.[keyIdProperty()] || option?.id"
[label]="option?.name | translate" [label]="option?.name | translate"
[initials]="option?.initials" [initials]="option?.initials"
[icon]="option?.icon" [icon]="option?.icon"
@@ -29,7 +29,7 @@
</ng-template> </ng-template>
<!-- Default options template menu for server data --> <!-- Default options template menu for server data -->
<ng-container <ng-container
[ngTemplateOutlet]="optionTemplateRef || defaultOption" [ngTemplateOutlet]="optionTemplateRef() || defaultOption"
[ngTemplateOutletContext]="{ $implicit: option, index: i }" [ngTemplateOutletContext]="{ $implicit: option, index: i }"
> >
</ng-container> </ng-container>

View File

@@ -6,9 +6,9 @@ import {
ElementRef, ElementRef,
HostBinding, HostBinding,
Injector, Injector,
Input,
QueryList, QueryList,
Renderer2, Renderer2,
input
} from '@angular/core'; } from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms'; import { NG_VALUE_ACCESSOR } from '@angular/forms';
import { TranslateService, TranslatePipe } from '@ngx-translate/core'; import { TranslateService, TranslatePipe } from '@ngx-translate/core';
@@ -62,23 +62,23 @@ export class PiSelectCarouselComponent extends PiBaseSelectComponent implements
} }
//CAROUSEL PROPERTIES //CAROUSEL PROPERTIES
@Input() device: Device; readonly device = input<Device>();
@Input() itemsDesktop!: number; readonly itemsDesktop = input.required<number>();
@Input() itemsTablet: number; readonly itemsTablet = input<number>();
@Input() itemsMobile: number; readonly itemsMobile = input<number>();
@Input() itemWidth: number; readonly itemWidth = input<number>();
@Input() movePerPage: boolean; readonly movePerPage = input<boolean>();
@Input() hideSwipeButtons: boolean; readonly hideSwipeButtons = input<boolean>();
@Input() isDraggable: boolean; readonly isDraggable = input<boolean>();
@Input() justifyCarousel: justify; readonly justifyCarousel = input<justify>();
/** /**
* Component's constructor. Initializes the component with required services. * Component's constructor. Initializes the component with required services.
@@ -110,7 +110,7 @@ export class PiSelectCarouselComponent extends PiBaseSelectComponent implements
* @returns The selected option. * @returns The selected option.
*/ */
get selectedOption(): any { get selectedOption(): any {
return this.options.find((option) => _.isEqual(this.controlValue, option)); return this.options().find((option) => _.isEqual(this.controlValue, option));
} }
/** /**
@@ -166,7 +166,7 @@ export class PiSelectCarouselComponent extends PiBaseSelectComponent implements
} }
onItemChanged(event: number) { onItemChanged(event: number) {
this.onSelectOption(this.options[event]); this.onSelectOption(this.options()[event]);
} }
/** /**
@@ -187,17 +187,17 @@ export class PiSelectCarouselComponent extends PiBaseSelectComponent implements
// 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) { if (this.childrenOptionsComponents && this.childrenOptionsComponents.length > 0) {
childrenOptionsComponent = this.childrenOptionsComponents.toArray(); childrenOptionsComponent = this.childrenOptionsComponents.toArray();
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?.toArray();
childrenOptionsElementRef = this.viewChildOptionsEl; childrenOptionsElementRef = this.viewChildOptionsEl();
} }
// Update the visual state of each child option. // Update the visual state of each child option.
childrenOptionsComponent?.forEach((childOption, index) => { childrenOptionsComponent?.forEach((childOption, index) => {
const isSelected = selectedOption const isSelected = selectedOption
? selectedOption[this.keyIdProperty] === childOption['id'] ? selectedOption[this.keyIdProperty()] === childOption['id']
: false; : false;
// 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) {

View File

@@ -1,6 +1,6 @@
<input hidden [formControl]="control" /> <input hidden [formControl]="control" />
<ng-container *ngIf="type === 'dropdown'"> <ng-container *ngIf="type() === 'dropdown'">
<ng-container *ngIf="pickedOptionType === 'button'; else pickedOptionInputTemplate"> <ng-container *ngIf="pickedOptionType() === 'button'; else pickedOptionInputTemplate">
<fieldset (click)="onToggleDropdown()" class="pi-select-picked-option"> <fieldset (click)="onToggleDropdown()" class="pi-select-picked-option">
<!-- Button --> <!-- Button -->
<div> <div>
@@ -15,18 +15,18 @@
[isDisabled]="disabled" [isDisabled]="disabled"
></pi-icon> ></pi-icon>
<span class="pi-select-picked-option__label">{{ <span class="pi-select-picked-option__label">{{
(selectedOptionsNames?.join(', ') | truncate : 33) || (label | translate) (selectedOptionsNames?.join(', ') | truncate : 33) || (label() | translate)
}}</span> }}</span>
</div> </div>
<pi-icon <pi-icon
class="pi-select-picked-option__action-icon" class="pi-select-picked-option__action-icon"
[icon]="actionIcon" [icon]="actionIcon()"
size="small" size="small"
></pi-icon> ></pi-icon>
</fieldset> </fieldset>
</ng-container> </ng-container>
<ng-template #pickedOptionInputTemplate> <ng-template #pickedOptionInputTemplate>
<ng-container [ngSwitch]="appearance"> <ng-container [ngSwitch]="appearance()">
<!-- Input Fill Appearance --> <!-- Input Fill Appearance -->
<ng-container *ngSwitchCase="'fill'"> <ng-container *ngSwitchCase="'fill'">
<fieldset (click)="onToggleDropdown()" class="pi-select-picked-option"> <fieldset (click)="onToggleDropdown()" class="pi-select-picked-option">
@@ -42,14 +42,14 @@
<!-- Input field --> <!-- Input field -->
<div> <div>
<span> <span>
<label class="pi-select-picked-option__label">{{ label | translate }}</label> <label class="pi-select-picked-option__label">{{ label() | translate }}</label>
<ng-template #defaultSelected> <ng-template #defaultSelected>
<span class="pi-select-picked-option__placeholder">{{ <span class="pi-select-picked-option__placeholder">{{
selectedOptionsNames?.join(', ') | truncate : 33 || (placeholder | translate) selectedOptionsNames?.join(', ') | truncate : 33 || (placeholder() | translate)
}}</span> }}</span>
</ng-template> </ng-template>
<ng-container <ng-container
[ngTemplateOutlet]="selectedTemplateRef || defaultSelected" [ngTemplateOutlet]="selectedTemplateRef() || defaultSelected"
[ngTemplateOutletContext]="{ $implicit: controlValue }" [ngTemplateOutletContext]="{ $implicit: controlValue }"
> >
</ng-container> </ng-container>
@@ -57,7 +57,7 @@
<pi-icon <pi-icon
class="pi-select-picked-option__action-icon" class="pi-select-picked-option__action-icon"
[icon]="actionIcon" [icon]="actionIcon()"
size="small" size="small"
></pi-icon> ></pi-icon>
</div> </div>
@@ -68,16 +68,16 @@
<ng-container *ngSwitchCase="'outline'"> <ng-container *ngSwitchCase="'outline'">
<fieldset (click)="onToggleDropdown()" class="pi-select-picked-option"> <fieldset (click)="onToggleDropdown()" class="pi-select-picked-option">
<!-- Input field --> <!-- Input field -->
<label class="pi-select-picked-option__label">{{ label | translate }}</label> <label class="pi-select-picked-option__label">{{ label() | translate }}</label>
<div> <div>
<span> <span>
<ng-template #defaultSelected> <ng-template #defaultSelected>
<span class="pi-select-picked-option__placeholder">{{ <span class="pi-select-picked-option__placeholder">{{
selectedOptionsNames?.join(', ') | truncate : 33 || (placeholder | translate) selectedOptionsNames?.join(', ') | truncate : 33 || (placeholder() | translate)
}}</span> }}</span>
</ng-template> </ng-template>
<ng-container <ng-container
[ngTemplateOutlet]="selectedTemplateRef || defaultSelected" [ngTemplateOutlet]="selectedTemplateRef() || defaultSelected"
[ngTemplateOutletContext]="{ $implicit: controlValue }" [ngTemplateOutletContext]="{ $implicit: controlValue }"
> >
</ng-container> </ng-container>
@@ -85,7 +85,7 @@
<pi-icon <pi-icon
class="pi-select-picked-option__action-icon" class="pi-select-picked-option__action-icon"
[icon]="actionIcon" [icon]="actionIcon()"
size="small" size="small"
></pi-icon> ></pi-icon>
</div> </div>
@@ -98,49 +98,49 @@
<!-- Options menu --> <!-- Options menu -->
<nav class="pi-select-options-menu"> <nav class="pi-select-options-menu">
<!-- Select All Options --> <!-- Select All Options -->
<fieldset *ngIf="hasSelectAllOptions" class="pi-select-options-menu__select-all"> <fieldset *ngIf="hasSelectAllOptions()" class="pi-select-options-menu__select-all">
<mat-checkbox <mat-checkbox
pi-dynamic-color pi-dynamic-color
[color]="color" [color]="color()"
[checked]="areAllEnabledOptionsSelected" [checked]="areAllEnabledOptionsSelected"
(change)="onSelectAllOptions($event)" (change)="onSelectAllOptions($event)"
> >
</mat-checkbox> </mat-checkbox>
<label pi-dynamic-color [color]="color"> <label pi-dynamic-color [color]="color()">
{{ selectAllLabel }} {{ selectAllLabel() }}
</label> </label>
</fieldset> </fieldset>
<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]="color" [color]="color()"
size="medium" size="medium"
pi-disabled pi-disabled
[isDisabled]="disabled" [isDisabled]="disabled"
></pi-icon> ></pi-icon>
<span class="pi-select-options-menu-picked-option__placeholder">{{ <span class="pi-select-options-menu-picked-option__placeholder">{{
(selectedOptionsNames?.join(', ') | truncate : 33) || (placeholder | translate) (selectedOptionsNames?.join(', ') | truncate : 33) || (placeholder() | translate)
}}</span> }}</span>
<pi-icon <pi-icon
(click)="onToggleDropdown()" (click)="onToggleDropdown()"
class="pi-select-options-menu-picked-option__action-icon" class="pi-select-options-menu-picked-option__action-icon"
[icon]="actionIcon" [icon]="actionIcon()"
[class.pi-select-options-menu-picked-option__action-icon--chevron]=" [class.pi-select-options-menu-picked-option__action-icon--chevron]="
actionIcon === 'chevron-forward' actionIcon() === 'chevron-forward'
" "
size="small" size="small"
></pi-icon> ></pi-icon>
</header> </header>
<!-- List of options --> <!-- List of options -->
<div class="pi-select-options-menu__options"> <div class="pi-select-options-menu__options">
<span *ngFor="let option of options; index as i" (click)="onSelectOption(option)"> <span *ngFor="let option of options(); index as i" (click)="onSelectOption(option)">
<!-- Default options template menu for static data --> <!-- Default options template menu for static data -->
<ng-template #defaultOption> <ng-template #defaultOption>
<pi-select-option <pi-select-option
@@ -149,20 +149,20 @@
[label]="option?.name | translate" [label]="option?.name | translate"
[initials]="option.initials ? option.initials : ''" [initials]="option.initials ? option.initials : ''"
[isSelected]="option === selectedOptions.includes(option)" [isSelected]="option === selectedOptions.includes(option)"
[flexDirection]="flexDirection" [flexDirection]="flexDirection()"
[size]="selectOptionSize" [size]="selectOptionSize"
></pi-select-option> ></pi-select-option>
</ng-template> </ng-template>
<!-- Default options template menu for server data --> <!-- Default options template menu for server data -->
<ng-container <ng-container
[ngTemplateOutlet]="optionTemplateRef || defaultOption" [ngTemplateOutlet]="optionTemplateRef() || defaultOption"
[ngTemplateOutletContext]="{ $implicit: option, index: i }" [ngTemplateOutletContext]="{ $implicit: option, index: i }"
> >
</ng-container> </ng-container>
</span> </span>
</div> </div>
<footer *ngIf="type === 'dropdown'"> <footer *ngIf="type() === 'dropdown'">
<span> <span>
<button (click)="onClickReset()" pi-ghost pi-primary w100>Borrar</button> <button (click)="onClickReset()" pi-ghost pi-primary w100>Borrar</button>
<button (click)="onClickSave()" pi-primary w100>Guardar</button> <button (click)="onClickSave()" pi-primary w100>Guardar</button>

View File

@@ -9,6 +9,7 @@ import {
Input, Input,
QueryList, QueryList,
Renderer2, Renderer2,
input
} from '@angular/core'; } from '@angular/core';
import { NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms'; import { NG_VALUE_ACCESSOR, ReactiveFormsModule } from '@angular/forms';
import { TranslateService, TranslatePipe } from '@ngx-translate/core'; import { TranslateService, TranslatePipe } from '@ngx-translate/core';
@@ -81,18 +82,18 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements
* Specifies the maximum number of selectable options. * Specifies the maximum number of selectable options.
* Defaults to `Infinity` if not provided. * Defaults to `Infinity` if not provided.
*/ */
@Input() maxLength: number | undefined = Infinity; readonly maxLength = input<number | undefined>(Infinity);
/** /**
* Specifies whether the select component has an option to select all options at once. * Specifies whether the select component has an option to select all options at once.
* Defaults to `false` if not provided. * Defaults to `false` if not provided.
*/ */
@Input() hasSelectAllOptions = false; readonly hasSelectAllOptions = input(false);
/** /**
* Provides the label to display next to the select all option. * Provides the label to display next to the select all option.
*/ */
@Input() selectAllLabel: string; readonly selectAllLabel = input<string>();
constructor( constructor(
private readonly translateService: TranslateService, private readonly translateService: TranslateService,
@@ -116,7 +117,7 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements
*/ */
@Input() get selectedOptions(): Array<any> { @Input() 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));
} }
/** /**
@@ -127,7 +128,7 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements
} }
get enabledOptions(): Array<any> { get enabledOptions(): Array<any> {
return this.options.filter((option) => !option.disabled); return this.options().filter((option) => !option.disabled);
} }
get areAllEnabledOptionsSelected(): boolean { get areAllEnabledOptionsSelected(): boolean {
@@ -135,7 +136,7 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements
} }
get areAllOptionsSelected(): boolean { get areAllOptionsSelected(): boolean {
return this.options.length === this.selectedOptions.length; return this.options().length === this.selectedOptions.length;
} }
/** /**
@@ -200,12 +201,12 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements
if (option.disabled) return; if (option.disabled) return;
const selectedOptions: any[] = [...(this.selectedOptions || [])]; const selectedOptions: any[] = [...(this.selectedOptions || [])];
const optionIndex = selectedOptions.findIndex( const optionIndex = selectedOptions.findIndex(
(id) => id[this.keyIdProperty] === option[this.keyIdProperty] (id) => id[this.keyIdProperty()] === option[this.keyIdProperty()]
); );
if (optionIndex !== -1) { if (optionIndex !== -1) {
selectedOptions.splice(optionIndex, 1); selectedOptions.splice(optionIndex, 1);
} else if (selectedOptions.length < (this.maxLength || Infinity)) { } else if (selectedOptions.length < (this.maxLength() || Infinity)) {
selectedOptions.push(option); selectedOptions.push(option);
} else { } else {
return; return;
@@ -248,16 +249,16 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements
// 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) { if (this.childrenOptionsComponents && this.childrenOptionsComponents.length > 0) {
childrenOptionsComponent = this.childrenOptionsComponents.toArray(); childrenOptionsComponent = this.childrenOptionsComponents.toArray();
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.toArray();
childrenOptionsElementRef = this.viewChildOptionsEl; childrenOptionsElementRef = this.viewChildOptionsEl();
} }
childrenOptionsComponent?.forEach((childOption, index) => { childrenOptionsComponent?.forEach((childOption, index) => {
const isSelected = selectedOptions.some( const isSelected = selectedOptions.some(
(selectedOption) => selectedOption[this.keyIdProperty] === childOption['id'] (selectedOption) => selectedOption[this.keyIdProperty()] === childOption['id']
); );
if (isSelected) { if (isSelected) {
this._renderer.addClass(childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected'); this._renderer.addClass(childrenOptionsElementRef.get(index)?.nativeElement, 'is-selected');
@@ -289,7 +290,7 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements
*/ */
private updateChildrenOptionsDisabledState(selectedOptions: any[]): void { private updateChildrenOptionsDisabledState(selectedOptions: any[]): void {
// Disable all options if the maximum number of selections has been reached. // Disable all options if the maximum number of selections has been reached.
const disableOthers = selectedOptions.length >= (this.maxLength || Infinity); const disableOthers = selectedOptions.length >= (this.maxLength() || Infinity);
// Array of child option components to work with. // Array of child option components to work with.
let childrenOptionsComponent: Array<PiSelectOptionComponent>; let childrenOptionsComponent: Array<PiSelectOptionComponent>;
// Reference to the child option elements for DOM manipulation. // Reference to the child option elements for DOM manipulation.
@@ -298,19 +299,19 @@ export class PiSelectMultipleComponent extends PiBaseSelectComponent implements
// 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) { if (this.childrenOptionsComponents && this.childrenOptionsComponents.length > 0) {
childrenOptionsComponent = this.childrenOptionsComponents.toArray(); childrenOptionsComponent = this.childrenOptionsComponents.toArray();
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.toArray();
childrenOptionsElementRef = this.viewChildOptionsEl; childrenOptionsElementRef = this.viewChildOptionsEl();
} }
childrenOptionsComponent?.forEach((childOption, index) => { childrenOptionsComponent?.forEach((childOption, index) => {
const isSelected = selectedOptions.some( const isSelected = selectedOptions.some(
(selectedOption) => selectedOption[this.keyIdProperty] === childOption['id'] (selectedOption) => selectedOption[this.keyIdProperty()] === childOption['id']
); );
const isDisabled = childOption.isDisabled; const isDisabled = childOption.isDisabled();
if ((!isSelected && disableOthers) || isDisabled) { if ((!isSelected && disableOthers) || isDisabled) {
this._renderer.addClass(childrenOptionsElementRef.get(index)?.nativeElement, 'is-disabled'); this._renderer.addClass(childrenOptionsElementRef.get(index)?.nativeElement, 'is-disabled');
} else { } else {

Some files were not shown because too many files have changed in this diff Show More