Skip to content

Commit

Permalink
fix: enable usage with zoneless change detection
Browse files Browse the repository at this point in the history
fixes #1015
  • Loading branch information
junglerobba committed May 31, 2024
1 parent ae12bdb commit d539693
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 54 deletions.
4 changes: 2 additions & 2 deletions src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NgModule } from '@angular/core';
import { NgModule, provideExperimentalZonelessChangeDetection } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
Expand Down Expand Up @@ -34,7 +34,7 @@ import { provideToastr } from '../lib/toastr/toast.provider';
ToastContainerDirective,
GhButtonModule,
],
providers: [provideToastr()],
providers: [provideToastr(), provideExperimentalZonelessChangeDetection()],
bootstrap: [AppComponent],
})
export class AppModule {}
2 changes: 1 addition & 1 deletion src/app/bootstrap.toast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Toast, ToastrService, ToastPackage } from '../lib/public_api';
@Component({
selector: '[bootstrap-toast-component]',
template: `
<div class="toast" role="alert" [style.display]="state.value === 'inactive' ? 'none' : ''">
<div class="toast" role="alert" [style.display]="state().value === 'inactive' ? 'none' : ''">
<div class="toast-header">
<strong class="me-auto">{{ title || 'default header' }}</strong>
<button
Expand Down
10 changes: 9 additions & 1 deletion src/app/home/home.component.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { Component, QueryList, Renderer2, ViewChildren, VERSION } from '@angular/core';
import {
Component,
QueryList,
Renderer2,
ViewChildren,
VERSION,
ChangeDetectionStrategy,
} from '@angular/core';
import { cloneDeep, random } from 'lodash-es';

import {
Expand Down Expand Up @@ -45,6 +52,7 @@ const types = ['success', 'error', 'info', 'warning'];
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class HomeComponent {
options: GlobalConfig;
Expand Down
2 changes: 1 addition & 1 deletion src/app/pink.toast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import { Toast, ToastrService, ToastPackage } from '../lib/public_api';
}
`],
template: `
<div class="row" [style.display]="state.value === 'inactive' ? 'none' : ''">
<div class="row" [style.display]="state().value === 'inactive' ? 'none' : ''">
<div class="col-9">
<div *ngIf="title" [class]="options.titleClass" [attr.aria-label]="title">
{{ title }}
Expand Down
47 changes: 24 additions & 23 deletions src/lib/toastr/toast-noanimation.component.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NgIf } from '@angular/common';
import { ModuleWithProviders } from '@angular/core';
import { ChangeDetectionStrategy, ModuleWithProviders, signal } from '@angular/core';
import {
ApplicationRef,
Component,
Expand Down Expand Up @@ -37,11 +37,12 @@ import { ToastrService } from './toastr.service';
{{ message }}
</div>
<div *ngIf="options.progressBar">
<div class="toast-progress" [style.width]="width + '%'"></div>
<div class="toast-progress" [style.width]="width() + '%'"></div>
</div>
`,
standalone: true,
imports: [NgIf]
imports: [NgIf],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ToastNoAnimation implements OnDestroy {
message?: string | null;
Expand All @@ -50,22 +51,22 @@ export class ToastNoAnimation implements OnDestroy {
duplicatesCount!: number;
originalTimeout: number;
/** width of progress bar */
width = -1;
width = signal(-1);
/** a combination of toast type and options.toastClass */
@HostBinding('class') toastClasses = '';

/** hides component when waiting to be displayed */
@HostBinding('style.display')
get displayStyle() {
if (this.state === 'inactive') {
if (this.state() === 'inactive') {
return 'none';
}

return null;
}

/** controls animation */
state = 'inactive';
state = signal('inactive');
private timeout: any;
private intervalId: any;
private hideTime!: number;
Expand Down Expand Up @@ -111,7 +112,7 @@ export class ToastNoAnimation implements OnDestroy {
* activates toast and sets timeout
*/
activateToast() {
this.state = 'active';
this.state.set('active');
if (!(this.options.disableTimeOut === true || this.options.disableTimeOut === 'timeOut') && this.options.timeOut) {
this.timeout = setTimeout(() => {
this.remove();
Expand All @@ -129,32 +130,32 @@ export class ToastNoAnimation implements OnDestroy {
* updates progress bar width
*/
updateProgress() {
if (this.width === 0 || this.width === 100 || !this.options.timeOut) {
if (this.width() === 0 || this.width() === 100 || !this.options.timeOut) {
return;
}
const now = new Date().getTime();
const remaining = this.hideTime - now;
this.width = (remaining / this.options.timeOut) * 100;
this.width.set((remaining / this.options.timeOut) * 100);
if (this.options.progressAnimation === 'increasing') {
this.width = 100 - this.width;
this.width.update(width => 100 - width);
}
if (this.width <= 0) {
this.width = 0;
if (this.width() <= 0) {
this.width.set(0);
}
if (this.width >= 100) {
this.width = 100;
if (this.width() >= 100) {
this.width.set(100);
}
}

resetTimeout() {
clearTimeout(this.timeout);
clearInterval(this.intervalId);
this.state = 'active';
this.state.set('active');

this.options.timeOut = this.originalTimeout;
this.timeout = setTimeout(() => this.remove(), this.originalTimeout);
this.hideTime = new Date().getTime() + (this.originalTimeout || 0);
this.width = -1;
this.width.set(-1);
if (this.options.progressBar) {
this.intervalId = setInterval(() => this.updateProgress(), 10);
}
Expand All @@ -164,18 +165,18 @@ export class ToastNoAnimation implements OnDestroy {
* tells toastrService to remove this toast after animation time
*/
remove() {
if (this.state === 'removed') {
if (this.state() === 'removed') {
return;
}
clearTimeout(this.timeout);
this.state = 'removed';
this.state.set('removed');
this.timeout = setTimeout(() =>
this.toastrService.remove(this.toastPackage.toastId),
);
}
@HostListener('click')
tapToast() {
if (this.state === 'removed') {
if (this.state() === 'removed') {
return;
}
this.toastPackage.triggerTap();
Expand All @@ -185,7 +186,7 @@ export class ToastNoAnimation implements OnDestroy {
}
@HostListener('mouseenter')
stickAround() {
if (this.state === 'removed') {
if (this.state() === 'removed') {
return;
}
clearTimeout(this.timeout);
Expand All @@ -194,14 +195,14 @@ export class ToastNoAnimation implements OnDestroy {

// disable progressBar
clearInterval(this.intervalId);
this.width = 0;
this.width.set(0);
}
@HostListener('mouseleave')
delayedHideToast() {
if (
(this.options.disableTimeOut === true || this.options.disableTimeOut === 'extendedTimeOut') ||
this.options.extendedTimeOut === 0 ||
this.state === 'removed'
this.state() === 'removed'
) {
return;
}
Expand All @@ -211,7 +212,7 @@ export class ToastNoAnimation implements OnDestroy {
);
this.options.timeOut = this.options.extendedTimeOut;
this.hideTime = new Date().getTime() + (this.options.timeOut || 0);
this.width = -1;
this.width.set(-1);
if (this.options.progressBar) {
this.intervalId = setInterval(() => this.updateProgress(), 10);
}
Expand Down
61 changes: 35 additions & 26 deletions src/lib/toastr/toast.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import {
trigger
} from '@angular/animations';
import {
ChangeDetectionStrategy,
Component,
HostBinding,
HostListener,
NgZone,
OnDestroy,
WritableSignal,
signal,
} from '@angular/core';
import { NgIf } from '@angular/common';
import { Subscription } from 'rxjs';
Expand All @@ -34,7 +37,7 @@ import { ToastrService } from './toastr.service';
{{ message }}
</div>
<div *ngIf="options.progressBar">
<div class="toast-progress" [style.width]="width + '%'"></div>
<div class="toast-progress" [style.width]="width() + '%'"></div>
</div>
`,
animations: [
Expand All @@ -49,6 +52,7 @@ import { ToastrService } from './toastr.service';
preserveWhitespaces: false,
standalone: true,
imports: [NgIf],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class Toast<ConfigPayload = any> implements OnDestroy {
message?: string | null;
Expand All @@ -57,19 +61,24 @@ export class Toast<ConfigPayload = any> implements OnDestroy {
duplicatesCount!: number;
originalTimeout: number;
/** width of progress bar */
width = -1;
width = signal(-1);
/** a combination of toast type and options.toastClass */
@HostBinding('class') toastClasses = '';
/** controls animation */
@HostBinding('@flyInOut') state!: {

state: WritableSignal<{
value: 'inactive' | 'active' | 'removed';
params: { easeTime: number | string; easing: string };
};
}>;

/** controls animation */
@HostBinding('@flyInOut') get _state() {
return this.state();
}

/** hides component when waiting to be displayed */
@HostBinding('style.display')
get displayStyle(): string | undefined {
if (this.state.value === 'inactive') {
if (this.state().value === 'inactive') {
return 'none';
}

Expand Down Expand Up @@ -106,13 +115,13 @@ export class Toast<ConfigPayload = any> implements OnDestroy {
this.sub3 = toastPackage.toastRef.countDuplicate().subscribe(count => {
this.duplicatesCount = count;
});
this.state = {
this.state = signal({
value: 'inactive',
params: {
easeTime: this.toastPackage.config.easeTime,
easing: 'ease-in',
},
};
});
}
ngOnDestroy() {
this.sub.unsubscribe();
Expand All @@ -126,7 +135,7 @@ export class Toast<ConfigPayload = any> implements OnDestroy {
* activates toast and sets timeout
*/
activateToast() {
this.state = { ...this.state, value: 'active' };
this.state.update(state => ({ ...state, value: 'active' }));
if (
!(this.options.disableTimeOut === true || this.options.disableTimeOut === 'timeOut') &&
this.options.timeOut
Expand All @@ -142,32 +151,32 @@ export class Toast<ConfigPayload = any> implements OnDestroy {
* updates progress bar width
*/
updateProgress() {
if (this.width === 0 || this.width === 100 || !this.options.timeOut) {
if (this.width() === 0 || this.width() === 100 || !this.options.timeOut) {
return;
}
const now = new Date().getTime();
const remaining = this.hideTime - now;
this.width = (remaining / this.options.timeOut) * 100;
this.width.set((remaining / this.options.timeOut) * 100);
if (this.options.progressAnimation === 'increasing') {
this.width = 100 - this.width;
this.width.update(width => 100 - width);
}
if (this.width <= 0) {
this.width = 0;
if (this.width() <= 0) {
this.width.set(0);
}
if (this.width >= 100) {
this.width = 100;
if (this.width() >= 100) {
this.width.set(100);
}
}

resetTimeout() {
clearTimeout(this.timeout);
clearInterval(this.intervalId);
this.state = { ...this.state, value: 'active' };
this.state.update(state => ({ ...state, value: 'active' }));

this.outsideTimeout(() => this.remove(), this.originalTimeout);
this.options.timeOut = this.originalTimeout;
this.hideTime = new Date().getTime() + (this.options.timeOut || 0);
this.width = -1;
this.width.set(-1);
if (this.options.progressBar) {
this.outsideInterval(() => this.updateProgress(), 10);
}
Expand All @@ -177,19 +186,19 @@ export class Toast<ConfigPayload = any> implements OnDestroy {
* tells toastrService to remove this toast after animation time
*/
remove() {
if (this.state.value === 'removed') {
if (this.state().value === 'removed') {
return;
}
clearTimeout(this.timeout);
this.state = { ...this.state, value: 'removed' };
this.state.update(state => ({ ...state, value: 'removed' }));
this.outsideTimeout(
() => this.toastrService.remove(this.toastPackage.toastId),
+this.toastPackage.config.easeTime
+this.toastPackage.config.easeTime,
);
}
@HostListener('click')
tapToast() {
if (this.state.value === 'removed') {
if (this.state().value === 'removed') {
return;
}
this.toastPackage.triggerTap();
Expand All @@ -199,7 +208,7 @@ export class Toast<ConfigPayload = any> implements OnDestroy {
}
@HostListener('mouseenter')
stickAround() {
if (this.state.value === 'removed') {
if (this.state().value === 'removed') {
return;
}

Expand All @@ -210,22 +219,22 @@ export class Toast<ConfigPayload = any> implements OnDestroy {

// disable progressBar
clearInterval(this.intervalId);
this.width = 0;
this.width.set(0);
}
}
@HostListener('mouseleave')
delayedHideToast() {
if (
(this.options.disableTimeOut === true || this.options.disableTimeOut === 'extendedTimeOut') ||
this.options.extendedTimeOut === 0 ||
this.state.value === 'removed'
this.state().value === 'removed'
) {
return;
}
this.outsideTimeout(() => this.remove(), this.options.extendedTimeOut);
this.options.timeOut = this.options.extendedTimeOut;
this.hideTime = new Date().getTime() + (this.options.timeOut || 0);
this.width = -1;
this.width.set(-1);
if (this.options.progressBar) {
this.outsideInterval(() => this.updateProgress(), 10);
}
Expand Down

0 comments on commit d539693

Please sign in to comment.