Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 5592fff

Browse filesBrowse files
committed
refactor(toast): signal inputs, host bindings, cleanup
1 parent 80e108b commit 5592fff
Copy full SHA for 5592fff

File tree

Expand file treeCollapse file tree

7 files changed

+92
-88
lines changed
Filter options
Expand file treeCollapse file tree

7 files changed

+92
-88
lines changed
+6-4Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
import { Component, HostBinding, inject } from '@angular/core';
1+
import { Component, inject } from '@angular/core';
22
import { ToastComponent } from '../toast/toast.component';
33

44
@Component({
55
selector: 'c-toast-body',
66
template: '<ng-content />',
77
styleUrls: ['./toast-body.component.scss'],
8-
exportAs: 'cToastBody'
8+
exportAs: 'cToastBody',
9+
host: {
10+
class: 'toast-body',
11+
}
912
})
1013
export class ToastBodyComponent {
11-
toast? = inject(ToastComponent, { optional: true });
14+
readonly toast? = inject(ToastComponent, { optional: true });
1215

13-
@HostBinding('class.toast-body') toastBodyClass = true;
1416
}
+9-6Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
1-
import { Directive, HostListener, inject, Input } from '@angular/core';
1+
import { Directive, inject, input } from '@angular/core';
22
import { ToasterService } from './toaster/toaster.service';
3+
import type { ToastComponent } from './toast/toast.component';
34

45
@Directive({
56
selector: '[cToastClose]',
6-
exportAs: 'cToastClose'
7+
exportAs: 'cToastClose',
8+
host: {
9+
'(click)': 'toggleOpen($event)'
10+
}
711
})
812
export class ToastCloseDirective {
913
readonly #toasterService = inject(ToasterService);
1014

11-
@Input('cToastClose') toast: any;
15+
readonly cToastClose = input<ToastComponent>();
1216

13-
@HostListener('click', ['$event'])
14-
toggleOpen($event: any): void {
17+
toggleOpen($event: MouseEvent): void {
1518
$event.preventDefault();
16-
this.#toasterService.setState({ show: false, toast: this.toast });
19+
this.#toasterService.setState({ show: false, toast: this.cToastClose() });
1720
}
1821
}
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<ng-container>
22
<ng-content />
3-
@if (closeButton) {
4-
<button [cToastClose]="toast" [style]="{outline: 0}" aria-label="close" cButtonClose class="ms-auto"></button>
3+
@if (closeButton()) {
4+
<button [cToastClose]="toast()" [style]="{outline: 0}" aria-label="close" cButtonClose class="ms-auto"></button>
55
}
66
</ng-container>
+10-7Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, HostBinding, inject, Input } from '@angular/core';
1+
import { Component, inject, input, signal } from '@angular/core';
22

33
import { ButtonCloseDirective } from '../../button';
44
import { ToastComponent } from '../toast/toast.component';
@@ -8,16 +8,19 @@ import { ToastCloseDirective } from '../toast-close.directive';
88
selector: 'c-toast-header',
99
templateUrl: './toast-header.component.html',
1010
exportAs: 'cToastHeader',
11-
imports: [ToastCloseDirective, ButtonCloseDirective]
11+
imports: [ToastCloseDirective, ButtonCloseDirective],
12+
host: {
13+
class: 'toast-header'
14+
}
1215
})
1316
export class ToastHeaderComponent {
14-
toast? = inject(ToastComponent, { optional: true });
17+
readonly #toast = inject(ToastComponent, { optional: true });
18+
19+
readonly toast = signal(this.#toast ?? undefined);
1520

1621
/**
1722
* Add close button to a toast header
18-
* @type boolean
23+
* @return boolean
1924
*/
20-
@Input() closeButton = true;
21-
22-
@HostBinding('class.toast-header') toastHeaderClass = true;
25+
readonly closeButton = input(true);
2326
}

‎projects/coreui-angular/src/lib/toast/toast/toast.component.ts

Copy file name to clipboardExpand all lines: projects/coreui-angular/src/lib/toast/toast/toast.component.ts
+31-31Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,10 @@ import {
22
booleanAttribute,
33
ChangeDetectorRef,
44
Component,
5+
computed,
6+
effect,
57
ElementRef,
6-
HostBinding,
7-
HostListener,
88
inject,
9-
Input,
109
input,
1110
numberAttribute,
1211
OnDestroy,
@@ -47,47 +46,58 @@ type AnimateType = 'hide' | 'show';
4746
})
4847
])
4948
],
50-
host: { class: 'toast show' }
49+
host: {
50+
class: 'toast show',
51+
'[class]': 'hostClasses()',
52+
'(mouseover)': 'clearTimer()',
53+
'(mouseout)': 'setTimer()',
54+
'[@fadeInOut]': 'animateType',
55+
'[@.disabled]': 'animationDisabled()'
56+
}
5157
})
5258
export class ToastComponent implements OnInit, OnDestroy {
59+
readonly changeDetectorRef = inject(ChangeDetectorRef);
5360
readonly hostElement = inject(ElementRef);
5461
readonly renderer = inject(Renderer2);
5562
readonly toasterService = inject(ToasterService);
56-
readonly changeDetectorRef = inject(ChangeDetectorRef);
5763

5864
readonly dynamic = input<boolean>();
5965
readonly placement = input<TToasterPlacement>();
6066

6167
/**
6268
* Auto hide the toast.
63-
* @type boolean
69+
* @return boolean
6470
*/
6571
readonly autohide = input(true);
6672

6773
/**
6874
* Sets the color context of the component to one of CoreUI’s themed colors.
69-
* @type Colors
75+
* @return Colors
7076
*/
7177
readonly color = input<Colors>('');
7278

7379
/**
7480
* Delay hiding the toast (ms).
75-
* @type number
81+
* @return number
7682
*/
7783
readonly delay = input(5000, { transform: numberAttribute });
7884

7985
/**
8086
* Apply fade transition to the toast.
81-
* @type boolean
87+
* @return boolean
8288
*/
8389
readonly fade = input(true);
8490

8591
/**
8692
* Toggle the visibility of component.
87-
* @type boolean
93+
* @return boolean
8894
*/
95+
readonly visibleInput = input(false, { transform: booleanAttribute, alias: 'visible' });
96+
97+
readonly #visibleInputEffect = effect(() => {
98+
this.visible = this.visibleInput();
99+
});
89100

90-
@Input({ transform: booleanAttribute })
91101
set visible(value: boolean) {
92102
const newValue = value;
93103
if (this.#visible !== newValue) {
@@ -111,13 +121,13 @@ export class ToastComponent implements OnInit, OnDestroy {
111121

112122
/**
113123
* Event emitted on visibility change. [docs]
114-
* @type <boolean>
124+
* @return <boolean>
115125
*/
116126
readonly visibleChange = output<boolean>();
117127

118128
/**
119129
* Event emitted on timer tick. [docs]
120-
* @type number
130+
* @return number
121131
*/
122132
readonly timer = output<number>();
123133

@@ -137,33 +147,23 @@ export class ToastComponent implements OnInit, OnDestroy {
137147
this.changeDetectorRef.markForCheck();
138148
}
139149

140-
@HostBinding('@.disabled')
141-
get animationDisabled(): boolean {
150+
readonly animationDisabled = computed(() => {
142151
return !this.fade();
143-
}
152+
});
144153

145-
@HostBinding('@fadeInOut')
146154
get animateType(): AnimateType {
147155
return this.visible ? 'show' : 'hide';
148156
}
149157

150-
@HostListener('mouseover') onMouseOver(): void {
151-
this.clearTimer();
152-
}
153-
154-
@HostListener('mouseout') onMouseOut(): void {
155-
this.setTimer();
156-
}
157-
158-
@HostBinding('class')
159-
get hostClasses(): any {
158+
readonly hostClasses = computed(() => {
159+
const color = this.color();
160160
return {
161161
toast: true,
162162
show: true,
163-
[`bg-${this.color()}`]: !!this.color(),
164-
'border-0': !!this.color()
165-
};
166-
}
163+
[`bg-${color}`]: !!color,
164+
'border-0': !!color
165+
} as Record<string, boolean>;
166+
});
167167

168168
ngOnInit(): void {
169169
if (this.visible) {

‎projects/coreui-angular/src/lib/toast/toaster/toaster.component.ts

Copy file name to clipboardExpand all lines: projects/coreui-angular/src/lib/toast/toaster/toaster.component.ts
+33-37Lines changed: 33 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,17 @@
11
import {
2-
AfterContentChecked,
32
Component,
43
ComponentRef,
5-
ContentChildren,
4+
computed,
5+
contentChildren,
66
DestroyRef,
77
ElementRef,
8-
HostBinding,
98
inject,
109
Injector,
11-
Input,
10+
input,
1211
NgModuleRef,
1312
OnInit,
14-
QueryList,
1513
Renderer2,
16-
ViewChild,
14+
viewChild,
1715
ViewContainerRef
1816
} from '@angular/core';
1917
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
@@ -53,59 +51,58 @@ export type TToasterPlacement =
5351
templateUrl: './toaster.component.html',
5452
exportAs: 'cToaster',
5553
imports: [ToasterHostDirective],
56-
host: { class: 'toaster toast-container' }
54+
host: {
55+
class: 'toaster toast-container',
56+
'[class]': 'hostClasses()'
57+
}
5758
})
58-
export class ToasterComponent implements OnInit, AfterContentChecked {
59+
export class ToasterComponent implements OnInit {
5960
readonly #hostElement = inject(ElementRef);
6061
readonly #renderer = inject(Renderer2);
6162
readonly #toasterService = inject(ToasterService);
6263
readonly #destroyRef = inject(DestroyRef);
6364

6465
placements = Object.values(ToasterPlacement);
65-
toasts!: QueryList<ViewContainerRef>;
66-
toastsDynamic: any[] = [];
66+
toastsDynamic: ComponentRef<any>[] = [];
6767

6868
/**
6969
* Toaster placement
70-
* @type TToasterPlacement
70+
* @return TToasterPlacement
7171
*/
72-
@Input() placement: TToasterPlacement = ToasterPlacement.TopEnd;
72+
readonly placement = input<TToasterPlacement>(ToasterPlacement.TopEnd);
7373

7474
/**
7575
* Toaster position
76-
* @type (string | 'absolute' | 'fixed' | 'static')
76+
* @return (string | 'absolute' | 'fixed' | 'static')
7777
*/
78-
@Input() position: string | 'absolute' | 'fixed' | 'static' = 'absolute';
78+
readonly position = input<string | 'absolute' | 'fixed' | 'static'>('absolute');
7979

80-
@ViewChild(ToasterHostDirective, { static: true }) toasterHost!: ToasterHostDirective;
81-
@ContentChildren(ToastComponent, { read: ViewContainerRef }) contentToasts!: QueryList<ViewContainerRef>;
80+
readonly toasterHost = viewChild.required(ToasterHostDirective);
81+
readonly contentToasts = contentChildren(ToastComponent, { read: ViewContainerRef });
8282

83-
@HostBinding('class')
84-
get hostClasses(): any {
83+
readonly hostClasses = computed(() => {
84+
const placement = this.placement();
85+
const position = this.position();
8586
return {
8687
toaster: true,
8788
'toast-container': true,
88-
[`position-${this.position}`]: !!this.position,
89-
'top-0': this.placement.includes('top'),
90-
'top-50': this.placement.includes('middle'),
91-
'bottom-0': this.placement.includes('bottom'),
92-
'start-0': this.placement.includes('start'),
93-
'start-50': this.placement.includes('center'),
94-
'end-0': this.placement.includes('end'),
95-
'translate-middle-x': this.placement.includes('center') && !this.placement.includes('middle'),
96-
'translate-middle-y': this.placement.includes('middle') && !this.placement.includes('center'),
97-
'translate-middle': this.placement.includes('middle') && this.placement.includes('center')
89+
[`position-${position}`]: !!position,
90+
'top-0': placement.includes('top'),
91+
'top-50': placement.includes('middle'),
92+
'bottom-0': placement.includes('bottom'),
93+
'start-0': placement.includes('start'),
94+
'start-50': placement.includes('center'),
95+
'end-0': placement.includes('end'),
96+
'translate-middle-x': placement.includes('center') && !placement.includes('middle'),
97+
'translate-middle-y': placement.includes('middle') && !placement.includes('center'),
98+
'translate-middle': placement.includes('middle') && placement.includes('center')
9899
};
99-
}
100+
});
100101

101102
ngOnInit(): void {
102103
this.stateToasterSubscribe();
103104
}
104105

105-
ngAfterContentChecked(): void {
106-
this.toasts = this.contentToasts;
107-
}
108-
109106
public addToast(
110107
toast: any,
111108
props: any,
@@ -116,13 +113,13 @@ export class ToasterComponent implements OnInit, AfterContentChecked {
116113
projectableNodes?: Node[][];
117114
}
118115
): ComponentRef<any> {
119-
const componentRef: ComponentRef<any> = this.toasterHost.viewContainerRef.createComponent(toast, options);
116+
const componentRef: ComponentRef<any> = this.toasterHost().viewContainerRef.createComponent(toast, options);
120117
this.toastsDynamic.push(componentRef);
121118
const index = this.toastsDynamic.indexOf(componentRef);
122119
for (const [key, value] of Object.entries(props)) {
123120
componentRef.setInput(key, value);
124121
}
125-
componentRef.setInput('placement', this.placement);
122+
componentRef.setInput('placement', this.placement());
126123
componentRef.setInput('dynamic', true);
127124
componentRef.setInput('index', index);
128125
componentRef.setInput('visible', true);
@@ -139,8 +136,7 @@ export class ToasterComponent implements OnInit, AfterContentChecked {
139136
item.destroy();
140137
}
141138
});
142-
143-
this.toasts?.forEach((item) => {
139+
this.contentToasts()?.forEach((item) => {
144140
if (state.toast && item.element.nativeElement === state.toast.hostElement.nativeElement) {
145141
if (!state.toast.dynamic()) {
146142
state.toast['visible'] = false;

‎projects/coreui-angular/src/lib/toast/toaster/toaster.service.ts

Copy file name to clipboardExpand all lines: projects/coreui-angular/src/lib/toast/toaster/toaster.service.ts
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
22
import { BehaviorSubject } from 'rxjs';
33

44
import { TToasterPlacement } from './toaster.component';
5-
import { ToastComponent } from '../toast/toast.component';
5+
import { type ToastComponent } from '../toast/toast.component';
66

77
export interface IToasterAction {
88
placement?: TToasterPlacement;

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.