diff --git a/apps/rxjs/49-hold-to-save-button/src/app/app.component.ts b/apps/rxjs/49-hold-to-save-button/src/app/app.component.ts index 8f0dbbc70..6bd8ae3fe 100644 --- a/apps/rxjs/49-hold-to-save-button/src/app/app.component.ts +++ b/apps/rxjs/49-hold-to-save-button/src/app/app.component.ts @@ -1,24 +1,31 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component, signal } from '@angular/core'; +import { HoldableDirective } from './holdable.directive'; @Component({ - imports: [], + imports: [HoldableDirective], selector: 'app-root', template: `
- +
`, changeDetection: ChangeDetectionStrategy.OnPush, }) export class AppComponent { + progress = signal(0); + onSend() { console.log('Save it!'); } diff --git a/apps/rxjs/49-hold-to-save-button/src/app/holdable.directive.ts b/apps/rxjs/49-hold-to-save-button/src/app/holdable.directive.ts new file mode 100644 index 000000000..7eccf3b4e --- /dev/null +++ b/apps/rxjs/49-hold-to-save-button/src/app/holdable.directive.ts @@ -0,0 +1,63 @@ +import { + Directive, + ElementRef, + inject, + input, + OnDestroy, + OnInit, + output, +} from '@angular/core'; +import { + filter, + fromEvent, + interval, + map, + merge, + Subscription, + switchMap, + take, + takeUntil, + tap, +} from 'rxjs'; + +@Directive({ selector: '[holdable]' }) +export class HoldableDirective implements OnInit, OnDestroy { + duration = input.required(); + complete = output(); + progressUpdated = output(); + + private elementRef = inject(ElementRef); + private sub: Subscription | null = null; + + ngOnInit() { + const element = this.elementRef.nativeElement as HTMLElement; + const resetEvents$ = merge( + fromEvent(element, 'mouseup'), + fromEvent(element, 'mouseleave'), + ); + + this.sub = fromEvent(element, 'mousedown') + .pipe( + switchMap(() => { + return interval(10).pipe( + takeUntil(resetEvents$), + map((value) => (value + 1) * 10), + tap((value) => { + const progress = Math.round((value / this.duration()) * 100); + this.progressUpdated.emit(progress); + }), + filter((value) => value >= this.duration()), + take(1), + tap(() => { + this.complete.emit(); + }), + ); + }), + ) + .subscribe(); + } + + ngOnDestroy() { + this.sub?.unsubscribe(); + } +}