Skip to content

Commit 23ffb53

Browse files
committed
feat(challenge 49): implement holdable directive for button press functionality
1 parent 543770b commit 23ffb53

2 files changed

Lines changed: 74 additions & 4 deletions

File tree

apps/rxjs/49-hold-to-save-button/src/app/app.component.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,31 @@
1-
import { ChangeDetectionStrategy, Component } from '@angular/core';
1+
import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
2+
import { HoldableDirective } from './holdable.directive';
23

34
@Component({
4-
imports: [],
5+
imports: [HoldableDirective],
56
selector: 'app-root',
67
template: `
78
<main class="flex h-screen items-center justify-center">
89
<div
910
class="flex w-full max-w-screen-sm flex-col items-center gap-y-8 p-4">
1011
<button
11-
class="rounded bg-indigo-600 px-4 py-2 font-bold text-white transition-colors ease-in-out hover:bg-indigo-700">
12+
class="cursor-pointer rounded bg-indigo-600 px-4 py-2 font-bold text-white transition-colors ease-in-out hover:bg-indigo-700"
13+
holdable
14+
[duration]="2000"
15+
(complete)="onSend()"
16+
(progressUpdated)="progress.set($event)">
1217
Hold me
1318
</button>
1419
15-
<progress [value]="20" [max]="100"></progress>
20+
<progress [value]="progress()" [max]="100"></progress>
1621
</div>
1722
</main>
1823
`,
1924
changeDetection: ChangeDetectionStrategy.OnPush,
2025
})
2126
export class AppComponent {
27+
progress = signal(0);
28+
2229
onSend() {
2330
console.log('Save it!');
2431
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import {
2+
Directive,
3+
ElementRef,
4+
inject,
5+
input,
6+
OnDestroy,
7+
OnInit,
8+
output,
9+
} from '@angular/core';
10+
import {
11+
filter,
12+
fromEvent,
13+
interval,
14+
map,
15+
merge,
16+
Subscription,
17+
switchMap,
18+
take,
19+
takeUntil,
20+
tap,
21+
} from 'rxjs';
22+
23+
@Directive({ selector: '[holdable]' })
24+
export class HoldableDirective implements OnInit, OnDestroy {
25+
duration = input.required<number>();
26+
complete = output<void>();
27+
progressUpdated = output<number>();
28+
29+
private elementRef = inject(ElementRef);
30+
private sub: Subscription | null = null;
31+
32+
ngOnInit() {
33+
const element = this.elementRef.nativeElement as HTMLElement;
34+
const resetEvents$ = merge(
35+
fromEvent(element, 'mouseup'),
36+
fromEvent(element, 'mouseleave'),
37+
);
38+
39+
this.sub = fromEvent(element, 'mousedown')
40+
.pipe(
41+
switchMap(() => {
42+
return interval(10).pipe(
43+
takeUntil(resetEvents$),
44+
map((value) => (value + 1) * 10),
45+
tap((value) => {
46+
const progress = Math.round((value / this.duration()) * 100);
47+
this.progressUpdated.emit(progress);
48+
}),
49+
filter((value) => value >= this.duration()),
50+
take(1),
51+
tap(() => {
52+
this.complete.emit();
53+
}),
54+
);
55+
}),
56+
)
57+
.subscribe();
58+
}
59+
60+
ngOnDestroy() {
61+
this.sub?.unsubscribe();
62+
}
63+
}

0 commit comments

Comments
 (0)