Skip to content

Commit

Permalink
feat: Angular 18 + enable usage with zoneless change detection (#1017)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: enable usage with zoneless change detection

Switched relevant parts to OnPush + Signals, so this will work with zoneless change detection enabled. I deliberately didn't use any of the new control flow syntax so this should technically still be compatible with Angular 16 (at least in a small test app with 16 it still worked for me)
  • Loading branch information
junglerobba authored Jun 1, 2024
1 parent 3d8ef10 commit 5461eb4
Show file tree
Hide file tree
Showing 8 changed files with 3,402 additions and 3,388 deletions.
6,634 changes: 3,315 additions & 3,319 deletions package-lock.json

Large diffs are not rendered by default.

30 changes: 15 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,23 @@
},
"private": true,
"devDependencies": {
"@angular-devkit/build-angular": "17.0.1",
"@angular-devkit/build-angular": "18.0.1",
"@angular-eslint/builder": "16.0.1",
"@angular-eslint/eslint-plugin": "16.0.1",
"@angular-eslint/eslint-plugin-template": "16.0.1",
"@angular-eslint/template-parser": "16.0.1",
"@angular/animations": "17.0.3",
"@angular/cli": "17.0.1",
"@angular/common": "17.0.3",
"@angular/compiler": "17.0.3",
"@angular/compiler-cli": "17.0.3",
"@angular/core": "17.0.3",
"@angular/forms": "17.0.3",
"@angular/language-service": "17.0.3",
"@angular/platform-browser": "17.0.3",
"@angular/platform-browser-dynamic": "17.0.3",
"@angular/router": "17.0.3",
"@ctrl/ngx-github-buttons": "8.0.0",
"@angular/animations": "18.0.0",
"@angular/cli": "18.0.1",
"@angular/common": "18.0.0",
"@angular/compiler": "18.0.0",
"@angular/compiler-cli": "18.0.0",
"@angular/core": "18.0.0",
"@angular/forms": "18.0.0",
"@angular/language-service": "18.0.0",
"@angular/platform-browser": "18.0.0",
"@angular/platform-browser-dynamic": "18.0.0",
"@angular/router": "18.0.0",
"@ctrl/ngx-github-buttons": "9.0.0",
"@types/jasmine": "4.3.1",
"@types/lodash-es": "4.17.7",
"@types/node": "20.1.4",
Expand All @@ -53,12 +53,12 @@
"karma-jasmine": "5.1.0",
"karma-jasmine-html-reporter": "2.1.0",
"lodash-es": "4.17.21",
"ng-packagr": "17.0.0",
"ng-packagr": "18.0.0",
"puppeteer": "20.2.0",
"rxjs": "7.8.1",
"ts-node": "10.9.1",
"tslib": "^2.5.0",
"typescript": "5.2.2",
"typescript": "5.4.5",
"zone.js": "0.14.2"
},
"release": {
Expand Down
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
Loading

0 comments on commit 5461eb4

Please sign in to comment.