Skip to content

Commit d502de3

Browse files
committed
some code cleanup
1 parent 33defff commit d502de3

File tree

11 files changed

+293
-441
lines changed

11 files changed

+293
-441
lines changed

angular.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
"tsConfig": "tsconfig.app.json",
2727
"assets": ["src/favicon.ico", "src/assets"],
2828
"styles": ["src/styles.scss"],
29-
"scripts": []
29+
"polyfills": ["zone.js"]
3030
},
3131
"configurations": {
3232
"production": {
@@ -63,7 +63,8 @@
6363
"tsConfig": "tsconfig.spec.json",
6464
"runner": "karma",
6565
"coverage": true,
66-
"ui": false
66+
"ui": false,
67+
"runnerConfig": true
6768
}
6869
},
6970
"lint": {

src/app/bootstrap.toast.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ import { Toast } from '../lib/public_api';
44
@Component({
55
selector: '[bootstrap-toast-component]',
66
template: `
7-
<div class="toast" role="alert" [style.display]="state().value === 'inactive' ? 'none' : ''">
7+
<div class="toast" role="alert" [style.display]="state() === 'inactive' ? 'none' : ''">
88
<div class="toast-header">
99
<strong class="me-auto">{{ title || 'default header' }}</strong>
10-
@if (options.closeButton) {
10+
@if (options().closeButton) {
1111
<button type="button" class="btn-close" aria-label="Close" (click)="remove()"></button>
1212
}
1313
</div>
1414
<div class="toast-body">
15-
<div role="alert" [attr.aria-label]="message">
15+
<div role="alert" [attr.aria-label]="message()">
1616
{{ message || 'default message' }}
1717
</div>
1818
<div class="mt-2 pt-2 border-top">

src/app/home/home.component.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ describe('AppComponent', () => {
6666
const opened: ActiveToast<Toast> = app.openToast();
6767
opened.portal.instance.stickAround();
6868
opened.portal.instance.delayedHideToast();
69-
expect(opened.portal.instance.options.timeOut).toBe(1000);
69+
expect(opened.portal.instance.options().timeOut).toBe(1000);
7070
done();
7171
});
7272
it('should keep on mouse exit with extended timeout 0', done => {
@@ -76,7 +76,7 @@ describe('AppComponent', () => {
7676
const opened: ActiveToast<Toast> = app.openToast();
7777
opened.portal.instance.stickAround();
7878
opened.portal.instance.delayedHideToast();
79-
expect(opened.portal.instance.options.timeOut).toBe(0);
79+
expect(opened.portal.instance.options().timeOut).toBe(0);
8080
done();
8181
});
8282
it('should trigger onShown for openPinkToast', done => {

src/app/pink-toast/pink-toast.component.html

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,23 @@
1-
<div class="row" [style.display]="state().value === 'inactive' ? 'none' : ''">
1+
@let _options = options();
2+
3+
<div class="row" [style.display]="state() === 'inactive' ? 'none' : ''">
24
<div class="col-9">
3-
@if (title) {
4-
<div [class]="options.titleClass" [attr.aria-label]="title">
5-
{{ title }}
5+
@if (title()) {
6+
<div [class]="_options.titleClass" [attr.aria-label]="title()">
7+
{{ title() }}
68
</div>
79
}
8-
@if (message && options.enableHtml) {
9-
<div role="alert" [class]="options.messageClass" [innerHTML]="message"></div>
10+
@if (message() && _options.enableHtml) {
11+
<div role="alert" [class]="_options.messageClass" [innerHTML]="message()"></div>
1012
}
11-
@if (message && !options.enableHtml) {
12-
<div role="alert" [class]="options.messageClass" [attr.aria-label]="message">
13-
{{ message }}
13+
@if (message() && !_options.enableHtml) {
14+
<div role="alert" [class]="_options.messageClass" [attr.aria-label]="message()">
15+
{{ message() }}
1416
</div>
1517
}
1618
</div>
1719
<div class="col-3 text-right">
18-
@if (!options.closeButton) {
20+
@if (!_options.closeButton) {
1921
<a class="btn btn-pink btn-sm" (click)="action($event)">
2022
{{ undoString }}
2123
</a>
@@ -25,7 +27,7 @@
2527
</div>
2628
</div>
2729

28-
@if (options.progressBar) {
30+
@if (_options.progressBar) {
2931
<div>
3032
<div class="toast-progress" [style.width]="width() + '%'"></div>
3133
</div>

src/lib/timeouts.service.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { inject, Injectable, NgZone } from '@angular/core';
2+
3+
@Injectable({ providedIn: 'root' })
4+
export class TimeoutsService {
5+
protected ngZone? = inject(NgZone);
6+
7+
public setInterval(func: () => any, timeout: number): number {
8+
if (this.ngZone) {
9+
return this.ngZone.runOutsideAngular(() =>
10+
window.setInterval(() => this.runInsideAngular(func), timeout),
11+
);
12+
} else {
13+
return window.setInterval(() => func(), timeout);
14+
}
15+
}
16+
17+
public setTimeout(func: () => any, timeout?: number): number {
18+
if (this.ngZone) {
19+
return this.ngZone.runOutsideAngular(() =>
20+
window.setTimeout(() => this.runInsideAngular(func), timeout),
21+
);
22+
} else {
23+
return window.setTimeout(() => func(), timeout);
24+
}
25+
}
26+
27+
protected runInsideAngular(func: () => any) {
28+
if (this.ngZone) {
29+
this.ngZone.run(() => func());
30+
} else {
31+
func();
32+
}
33+
}
34+
}

src/lib/toastr/toast-noanimation/toast-noanimation.component.html

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,31 @@
1-
@if (options.closeButton) {
1+
@let _options = options();
2+
3+
@if (_options.closeButton) {
24
<button (click)="remove()" type="button" class="toast-close-button" aria-label="Close">
35
<span aria-hidden="true">&times;</span>
46
</button>
57
}
68

7-
@if (title) {
8-
<div [class]="options.titleClass" [attr.aria-label]="title">
9-
{{ title }}
9+
@if (title()) {
10+
<div [class]="_options.titleClass" [attr.aria-label]="title()">
11+
{{ title() }}
1012
@if (duplicatesCount) {
1113
<ng-container>[{{ duplicatesCount + 1 }}]</ng-container>
1214
}
1315
</div>
1416
}
1517

16-
@if (message && options.enableHtml) {
17-
<div role="alert" [class]="options.messageClass" [innerHTML]="message"></div>
18+
@if (message() && _options.enableHtml) {
19+
<div role="alert" [class]="_options.messageClass" [innerHTML]="message()"></div>
1820
}
1921

20-
@if (message && !options.enableHtml) {
21-
<div role="alert" [class]="options.messageClass" [attr.aria-label]="message">
22-
{{ message }}
22+
@if (message() && !_options.enableHtml) {
23+
<div role="alert" [class]="_options.messageClass" [attr.aria-label]="message()">
24+
{{ message() }}
2325
</div>
2426
}
2527

26-
@if (options.progressBar) {
28+
@if (_options.progressBar) {
2729
<div>
2830
<div class="toast-progress" [style.width]="width() + '%'"></div>
2931
</div>

src/lib/toastr/toast-noanimation/toast-noanimation.component.ts

Lines changed: 17 additions & 191 deletions
Original file line numberDiff line numberDiff line change
@@ -1,201 +1,27 @@
1-
import { ChangeDetectionStrategy, ModuleWithProviders, signal, inject } from '@angular/core';
2-
import {
3-
ApplicationRef,
4-
Component,
5-
HostBinding,
6-
HostListener,
7-
NgModule,
8-
OnDestroy,
9-
} from '@angular/core';
10-
11-
import { Subscription } from 'rxjs';
12-
13-
import {
14-
DefaultNoComponentGlobalConfig,
15-
GlobalConfig,
16-
IndividualConfig,
17-
ToastPackage,
18-
TOAST_CONFIG,
19-
} from '../toastr-config';
20-
import { ToastrService } from '../toastr.service';
1+
import { ChangeDetectionStrategy, ModuleWithProviders } from '@angular/core';
2+
import { Component, HostBinding, NgModule } from '@angular/core';
3+
import { DefaultNoComponentGlobalConfig, GlobalConfig, TOAST_CONFIG } from '../toastr-config';
4+
import { ToastBase } from '../toast.abstract';
215

226
@Component({
237
selector: '[toast-component]',
248
templateUrl: './toast-noanimation.component.html',
259
standalone: true,
2610
changeDetection: ChangeDetectionStrategy.OnPush,
11+
host: {
12+
'[class]': 'toastClasses()',
13+
'[style.display]': 'displayStyle()',
14+
'(mouseenter)': 'stickAround()',
15+
'(mouseleave)': 'delayedHideToast()',
16+
'(click)': 'tapToast()',
17+
},
2718
})
28-
export class ToastNoAnimation implements OnDestroy {
29-
protected toastrService = inject(ToastrService);
30-
toastPackage = inject(ToastPackage);
31-
protected appRef = inject(ApplicationRef);
32-
33-
message?: string | null;
34-
title?: string;
35-
options: IndividualConfig;
36-
duplicatesCount!: number;
37-
originalTimeout: number;
38-
/** width of progress bar */
39-
width = signal(-1);
40-
/** a combination of toast type and options.toastClass */
41-
@HostBinding('class') toastClasses = '';
42-
43-
/** hides component when waiting to be displayed */
44-
@HostBinding('style.display')
45-
get displayStyle() {
46-
if (this.state() === 'inactive') {
47-
return 'none';
48-
}
49-
50-
return null;
51-
}
52-
53-
/** controls animation */
54-
state = signal('inactive');
55-
private timeout: any;
56-
private intervalId: any;
57-
private hideTime!: number;
58-
private sub: Subscription;
59-
private sub1: Subscription;
60-
private sub2: Subscription;
61-
private sub3: Subscription;
62-
63-
constructor() {
64-
const toastPackage = this.toastPackage;
65-
66-
this.message = toastPackage.message;
67-
this.title = toastPackage.title;
68-
this.options = toastPackage.config;
69-
this.originalTimeout = toastPackage.config.timeOut;
70-
this.toastClasses = `${toastPackage.toastType} ${toastPackage.config.toastClass}`;
71-
this.sub = toastPackage.toastRef.afterActivate().subscribe(() => {
72-
this.activateToast();
73-
});
74-
this.sub1 = toastPackage.toastRef.manualClosed().subscribe(() => {
75-
this.remove();
76-
});
77-
this.sub2 = toastPackage.toastRef.timeoutReset().subscribe(() => {
78-
this.resetTimeout();
79-
});
80-
this.sub3 = toastPackage.toastRef.countDuplicate().subscribe(count => {
81-
this.duplicatesCount = count;
82-
});
83-
}
84-
ngOnDestroy() {
85-
this.sub.unsubscribe();
86-
this.sub1.unsubscribe();
87-
this.sub2.unsubscribe();
88-
this.sub3.unsubscribe();
89-
clearInterval(this.intervalId);
90-
clearTimeout(this.timeout);
91-
}
92-
/**
93-
* activates toast and sets timeout
94-
*/
95-
activateToast() {
96-
this.state.set('active');
97-
if (
98-
!(this.options.disableTimeOut === true || this.options.disableTimeOut === 'timeOut') &&
99-
this.options.timeOut
100-
) {
101-
this.timeout = setTimeout(() => {
102-
this.remove();
103-
}, this.options.timeOut);
104-
this.hideTime = new Date().getTime() + this.options.timeOut;
105-
if (this.options.progressBar) {
106-
this.intervalId = setInterval(() => this.updateProgress(), 10);
107-
}
108-
}
109-
if (this.options.onActivateTick) {
110-
this.appRef.tick();
111-
}
112-
}
113-
/**
114-
* updates progress bar width
115-
*/
116-
updateProgress() {
117-
if (this.width() === 0 || this.width() === 100 || !this.options.timeOut) {
118-
return;
119-
}
120-
const now = new Date().getTime();
121-
const remaining = this.hideTime - now;
122-
this.width.set((remaining / this.options.timeOut) * 100);
123-
if (this.options.progressAnimation === 'increasing') {
124-
this.width.update(width => 100 - width);
125-
}
126-
if (this.width() <= 0) {
127-
this.width.set(0);
128-
}
129-
if (this.width() >= 100) {
130-
this.width.set(100);
131-
}
132-
}
133-
134-
resetTimeout() {
135-
clearTimeout(this.timeout);
136-
clearInterval(this.intervalId);
137-
this.state.set('active');
138-
139-
this.options.timeOut = this.originalTimeout;
140-
this.timeout = setTimeout(() => this.remove(), this.originalTimeout);
141-
this.hideTime = new Date().getTime() + (this.originalTimeout || 0);
142-
this.width.set(-1);
143-
if (this.options.progressBar) {
144-
this.intervalId = setInterval(() => this.updateProgress(), 10);
145-
}
146-
}
147-
148-
/**
149-
* tells toastrService to remove this toast after animation time
150-
*/
151-
remove() {
152-
if (this.state() === 'removed') {
153-
return;
154-
}
155-
clearTimeout(this.timeout);
156-
this.state.set('removed');
157-
this.timeout = setTimeout(() => this.toastrService.remove(this.toastPackage.toastId));
158-
}
159-
@HostListener('click')
160-
tapToast() {
161-
if (this.state() === 'removed') {
162-
return;
163-
}
164-
this.toastPackage.triggerTap();
165-
if (this.options.tapToDismiss) {
166-
this.remove();
167-
}
168-
}
169-
@HostListener('mouseenter')
170-
stickAround() {
171-
if (this.state() === 'removed') {
172-
return;
173-
}
174-
clearTimeout(this.timeout);
175-
this.options.timeOut = 0;
176-
this.hideTime = 0;
177-
178-
// disable progressBar
179-
clearInterval(this.intervalId);
180-
this.width.set(0);
181-
}
182-
@HostListener('mouseleave')
183-
delayedHideToast() {
184-
if (
185-
this.options.disableTimeOut === true ||
186-
this.options.disableTimeOut === 'extendedTimeOut' ||
187-
this.options.extendedTimeOut === 0 ||
188-
this.state() === 'removed'
189-
) {
190-
return;
191-
}
192-
this.timeout = setTimeout(() => this.remove(), this.options.extendedTimeOut);
193-
this.options.timeOut = this.options.extendedTimeOut;
194-
this.hideTime = new Date().getTime() + (this.options.timeOut || 0);
195-
this.width.set(-1);
196-
if (this.options.progressBar) {
197-
this.intervalId = setInterval(() => this.updateProgress(), 10);
198-
}
19+
export class ToastNoAnimation extends ToastBase {
20+
override remove() {
21+
super.remove();
22+
this.timeout = this.timeoutsService.setTimeout(() =>
23+
this.toastrService.remove(this.toastPackage.toastId),
24+
);
19925
}
20026
}
20127

0 commit comments

Comments
 (0)