Skip to content

Commit

Permalink
fix: cancel macrotask when element destroyed
Browse files Browse the repository at this point in the history
  • Loading branch information
splincode committed Feb 14, 2024
1 parent b079160 commit 96c0ed2
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 78 deletions.
18 changes: 10 additions & 8 deletions projects/addon-charts/components/arc-chart/arc-chart.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ import {
Input,
Output,
QueryList,
Self,
ViewChildren,
} from '@angular/core';
import {DomSanitizer, SafeValue} from '@angular/platform-browser';
import {tuiTypedFromEvent} from '@taiga-ui/cdk';
import {TuiDestroyService, tuiTypedFromEvent, tuiWatch} from '@taiga-ui/cdk';
import {TuiSizeXL} from '@taiga-ui/core';
import {merge, Observable, ReplaySubject} from 'rxjs';
import {map, startWith, switchMap, tap} from 'rxjs/operators';
import {merge, Observable, ReplaySubject, timer} from 'rxjs';
import {map, startWith, switchMap, takeUntil, tap} from 'rxjs/operators';

// 3/4 with 1% safety offset
const ARC = 0.76;
Expand Down Expand Up @@ -97,12 +98,13 @@ export class TuiArcChartComponent {
constructor(
@Inject(DomSanitizer) private readonly sanitizer: DomSanitizer,
@Inject(ChangeDetectorRef) cdr: ChangeDetectorRef,
@Self() @Inject(TuiDestroyService) destroy$: Observable<void>,
) {
// So initial animation works
setTimeout(() => {
this.initialized = true;
cdr.markForCheck();
});
timer(0)
.pipe(tuiWatch(cdr), takeUntil(destroy$))
.subscribe(() => {
this.initialized = true;
});
}

@HostBinding('style.width.rem')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
TuiMonth,
tuiTypedFromEvent,
TuiTypedMapper,
tuiZonefree,
} from '@taiga-ui/cdk';
import {
TUI_ANIMATIONS_DURATION,
Expand Down Expand Up @@ -221,10 +222,9 @@ export class TuiMobileCalendarComponent implements AfterViewInit {
this.activeYear = year;
this.scrollToActiveYear('smooth');

this.ngZone.runOutsideAngular(() => {
// Delay is required to run months scroll in the next frame to prevent flicker
setTimeout(() => this.scrollToActiveMonth());
});
timer(0)
.pipe(tuiZonefree(this.ngZone), takeUntil(this.destroy$))
.subscribe(() => this.scrollToActiveMonth());
}

readonly disabledItemHandlerMapper: TuiTypedMapper<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,22 @@ import {
Input,
NgZone,
QueryList,
Self,
ViewChild,
ViewChildren,
} from '@angular/core';
import {EMPTY_QUERY, TUI_IS_IOS, tuiPure, tuiZonefull} from '@taiga-ui/cdk';
import {
EMPTY_QUERY,
TUI_IS_IOS,
TuiDestroyService,
tuiPure,
tuiZonefull,
} from '@taiga-ui/cdk';
import {tuiSlideInTop} from '@taiga-ui/core';
import {TUI_MORE_WORD} from '@taiga-ui/kit';
import {Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {Observable, timer} from 'rxjs';
import {map, takeUntil} from 'rxjs/operators';

import {fakeSmoothScroll} from '../../ios.hacks';
import {TuiSheet, TuiSheetRequiredProps} from '../../sheet';
import {TUI_SHEET_SCROLL} from '../../sheet-tokens';
import {TUI_SHEET_ID} from '../sheet-heading/sheet-heading.component';
Expand Down Expand Up @@ -62,6 +68,7 @@ export class TuiSheetComponent<T> implements TuiSheetRequiredProps<T>, AfterView
@Inject(NgZone) private readonly zone: NgZone,
@Inject(TUI_IS_IOS) readonly isIos: boolean,
@Inject(TUI_MORE_WORD) readonly moreWord$: Observable<string>,
@Self() @Inject(TuiDestroyService) private readonly destroy$: Observable<void>,
) {}

get stops(): readonly number[] {
Expand Down Expand Up @@ -99,7 +106,17 @@ export class TuiSheetComponent<T> implements TuiSheetRequiredProps<T>, AfterView
const {nativeElement} = this.el;

if (this.isIos) {
fakeSmoothScroll(nativeElement, top - nativeElement.scrollTop - 16);
const offset = top - nativeElement.scrollTop - 16;

nativeElement.style.transition = 'none';
nativeElement.style.transform = `scaleX(-1) translate3d(0, ${offset}px, 0)`;

timer(0)
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
nativeElement.style.transition = '';
nativeElement.style.transform = '';
});
}

nativeElement.scrollTo({top, behavior: 'smooth'});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Directive, ElementRef, Inject, Self} from '@angular/core';
import {TUI_SCROLL_REF, TuiDestroyService} from '@taiga-ui/cdk';
import {Observable} from 'rxjs';
import {Observable, timer} from 'rxjs';
import {
distinctUntilChanged,
filter,
Expand Down Expand Up @@ -39,9 +39,12 @@ export class TuiSheetStopDirective {
nativeElement.classList.remove('_stuck'); // iOS
nativeElement.scrollTop = el.nativeElement.offsetTop;

setTimeout(() => {
nativeElement.style.overflow = '';
}, 100);
timer(100)
.pipe(takeUntil(destroy$))
// eslint-disable-next-line rxjs/no-nested-subscribe
.subscribe(() => {
nativeElement.style.overflow = '';
});
});
}
}
11 changes: 0 additions & 11 deletions projects/addon-mobile/components/sheet/ios.hacks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,3 @@ export function iosScrollFactory(

return concat(scroll$.pipe(take(1)), result$).pipe(tuiZonefree(zone), share());
}

// eslint-disable-next-line @typescript-eslint/naming-convention
export function fakeSmoothScroll({style}: HTMLElement, offset: number): void {
style.transition = 'none';
style.transform = `scaleX(-1) translate3d(0, ${offset}px, 0)`;

setTimeout(() => {
style.transition = '';
style.transform = '';
});
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import {Component, ElementRef, ViewChild} from '@angular/core';
import {Component, ElementRef, Inject, Self, ViewChild} from '@angular/core';
import {changeDetection} from '@demo/emulate/change-detection';
import {TuiDocExample} from '@taiga-ui/addon-doc';
import {TuiDestroyService} from '@taiga-ui/cdk';
import {TUI_EXPAND_LOADED, TuiSizeS} from '@taiga-ui/core';
import {Observable, timer} from 'rxjs';
import {takeUntil} from 'rxjs/operators';

@Component({
selector: 'example-accordion',
templateUrl: './accordion.template.html',
changeDetection,
providers: [TuiDestroyService],
})
export class ExampleTuiAccordionComponent {
@ViewChild('content')
Expand Down Expand Up @@ -61,19 +65,24 @@ export class ExampleTuiAccordionComponent {

size: TuiSizeS = this.sizeVariants[1];

constructor(
@Self() @Inject(TuiDestroyService) private readonly destroy$: Observable<void>,
) {}

onOpenChange(open: boolean): void {
this.open = open;

if (!this.async || !open) {
return;
}

setTimeout(() => {
const event = new CustomEvent(TUI_EXPAND_LOADED, {bubbles: true});

if (this.content) {
this.content.nativeElement.dispatchEvent(event);
}
}, 3000);
timer(3000)
.pipe(takeUntil(this.destroy$))
.subscribe(
() =>
this.content?.nativeElement.dispatchEvent(
new CustomEvent(TUI_EXPAND_LOADED, {bubbles: true}),
),
);
}
}
35 changes: 24 additions & 11 deletions projects/demo/src/modules/components/expand/expand.component.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
import {ChangeDetectorRef, Component, ElementRef, Inject, ViewChild} from '@angular/core';
import {
ChangeDetectorRef,
Component,
ElementRef,
Inject,
Self,
ViewChild,
} from '@angular/core';
import {changeDetection} from '@demo/emulate/change-detection';
import {TuiDocExample} from '@taiga-ui/addon-doc';
import {TuiDestroyService} from '@taiga-ui/cdk';
import {TUI_EXPAND_LOADED, TuiExpandComponent} from '@taiga-ui/core';
import {Observable, timer} from 'rxjs';
import {takeUntil} from 'rxjs/operators';

@Component({
selector: 'example-expand',
templateUrl: './expand.template.html',
styleUrls: ['./expand.style.less'],
changeDetection,
providers: [TuiDestroyService],
})
export class ExampleTuiExpandComponent {
@ViewChild(TuiExpandComponent, {read: ElementRef})
Expand All @@ -27,7 +38,10 @@ export class ExampleTuiExpandComponent {

delayed = false;

constructor(@Inject(ChangeDetectorRef) private readonly cdr: ChangeDetectorRef) {}
constructor(
@Inject(ChangeDetectorRef) private readonly cdr: ChangeDetectorRef,
@Self() @Inject(TuiDestroyService) private readonly destroy$: Observable<void>,
) {}

onExpandedChange(expanded: boolean): void {
this.expanded = expanded;
Expand All @@ -37,15 +51,14 @@ export class ExampleTuiExpandComponent {
return;
}

setTimeout(() => {
const event = new CustomEvent(TUI_EXPAND_LOADED, {bubbles: true});
timer(5000)
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
const event = new CustomEvent(TUI_EXPAND_LOADED, {bubbles: true});

this.delayed = false;
this.cdr.detectChanges();

if (this.expand) {
this.expand.nativeElement.dispatchEvent(event);
}
}, 5000);
this.delayed = false;
this.cdr.detectChanges();
this.expand?.nativeElement.dispatchEvent(event);
});
}
}
25 changes: 17 additions & 8 deletions projects/demo/src/modules/directives/for/examples/1/index.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,37 @@
import {Component} from '@angular/core';
import {Component, Inject, Self} from '@angular/core';
import {changeDetection} from '@demo/emulate/change-detection';
import {encapsulation} from '@demo/emulate/encapsulation';
import {BehaviorSubject} from 'rxjs';
import {TuiDestroyService} from '@taiga-ui/cdk';
import {BehaviorSubject, Observable, timer} from 'rxjs';
import {takeUntil} from 'rxjs/operators';

@Component({
selector: 'tui-for-example-1',
templateUrl: './index.html',
encapsulation,
changeDetection,
providers: [TuiDestroyService],
})
export class TuiForExample1 {
readonly items$ = new BehaviorSubject<readonly string[] | null>([]);

constructor(
@Self() @Inject(TuiDestroyService) private readonly destroy$: Observable<void>,
) {}

refresh(): void {
this.items$.next(null);

const delay = Math.round(Math.random() * 2000);

setTimeout(() => {
this.items$.next(
delay % 2
? []
: ['William "Bill" S. Preston Esq.', 'Ted "Theodore" Logan'],
timer(delay)
.pipe(takeUntil(this.destroy$))
.subscribe(() =>
this.items$.next(
delay % 2
? []
: ['William "Bill" S. Preston Esq.', 'Ted "Theodore" Logan'],
),
);
}, delay);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ import {
tuiDateStreamWithTransformer,
TuiInputDateOptions,
} from '@taiga-ui/kit/tokens';
import {combineLatest, Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {combineLatest, Observable, timer} from 'rxjs';
import {map, takeUntil} from 'rxjs/operators';

@Component({
selector: 'tui-input-date-time',
Expand Down Expand Up @@ -291,9 +291,11 @@ export class TuiInputDateTimeComponent
return;
}

setTimeout(() => {
this.nativeValue = this.trimTrailingSeparator(this.nativeValue);
});
timer(0)
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
this.nativeValue = this.trimTrailingSeparator(this.nativeValue);
});

if (
this.value[0] === null ||
Expand Down
16 changes: 9 additions & 7 deletions projects/kit/components/input-tag/input-tag.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@ import {TuiStringifiableItem} from '@taiga-ui/kit/classes';
import {FIXED_DROPDOWN_CONTROLLER_PROVIDER} from '@taiga-ui/kit/providers';
import {TuiStatus} from '@taiga-ui/kit/types';
import {PolymorpheusContent} from '@tinkoff/ng-polymorpheus';
import {Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {Observable, timer} from 'rxjs';
import {map, takeUntil} from 'rxjs/operators';

import {TUI_INPUT_TAG_OPTIONS, TuiInputTagOptions} from './input-tag.options';

Expand Down Expand Up @@ -459,11 +459,13 @@ export class TuiInputTagComponent

private scrollTo(scrollLeft = this.scrollBar?.nativeElement.scrollWidth): void {
// Allow change detection to run and add new tag to DOM
setTimeout(() => {
if (this.scrollBar) {
this.scrollBar.nativeElement.scrollLeft = scrollLeft || 0;
}
});
timer(0)
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
if (this.scrollBar) {
this.scrollBar.nativeElement.scrollLeft = scrollLeft || 0;
}
});
}

private filterValue(value: string[]): string[] {
Expand Down
16 changes: 9 additions & 7 deletions projects/kit/components/input-time/input-time.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ import {
import {TUI_SELECT_OPTION} from '@taiga-ui/kit/components/select-option';
import {FIXED_DROPDOWN_CONTROLLER_PROVIDER} from '@taiga-ui/kit/providers';
import {TUI_TIME_TEXTS} from '@taiga-ui/kit/tokens';
import {Observable} from 'rxjs';
import {map} from 'rxjs/operators';
import {Observable, timer} from 'rxjs';
import {map, takeUntil} from 'rxjs/operators';

import {TUI_INPUT_TIME_OPTIONS, TuiInputTimeOptions} from './input-time.options';

Expand Down Expand Up @@ -230,11 +230,13 @@ export class TuiInputTimeComponent

this.value = TuiTime.fromString(this.nativeValue);

setTimeout(() => {
if (this.nativeValue.endsWith('.') || this.nativeValue.endsWith(':')) {
this.nativeValue = this.nativeValue.slice(0, -1);
}
});
timer(0)
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
if (this.nativeValue.endsWith('.') || this.nativeValue.endsWith(':')) {
this.nativeValue = this.nativeValue.slice(0, -1);
}
});
}

onArrowUp(event: Event): void {
Expand Down

0 comments on commit 96c0ed2

Please sign in to comment.