diff --git a/content/blog/rxjs-scroll-position.md b/content/blog/rxjs-scroll-position.md new file mode 100644 index 00000000..4f9be6a5 --- /dev/null +++ b/content/blog/rxjs-scroll-position.md @@ -0,0 +1,24 @@ +--- +title: 'Tracking the scroll position in Angular | A practical guide to RxJS' +description: '' +published: true +publishedAt: 2020-12-14T19:20:51.808Z +updatedAt: 2020-12-14T19:20:51.808Z +tags: + - RxJS + - Angular +keywords: + - Scroll Position + - RxJS + - Angular +authors: + - 'Gary Großgarten' +github: https://github.com/garygrossgarten/observables +--- + +## Demo + +Here's a demo tracking the scrolltop of the container element. + +
+
\ No newline at end of file diff --git a/content/blog/simple-rxjs-keyboard-shortcuts.md b/content/blog/simple-rxjs-keyboard-shortcuts copy.md similarity index 100% rename from content/blog/simple-rxjs-keyboard-shortcuts.md rename to content/blog/simple-rxjs-keyboard-shortcuts copy.md diff --git a/package-lock.json b/package-lock.json index a8430f86..ab455d1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -716,6 +716,15 @@ "tslib": "^2.0.0" } }, + "@angular/cdk": { + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-11.0.2.tgz", + "integrity": "sha512-hdZ9UJVGgCFhdOuB4RPS1Ku45VSG/WfRjbyxu/7teYyFKqAvcd3vawkeQfZf+lExmFaeW43+5hnpu/JIlGTrSA==", + "requires": { + "parse5": "^5.0.0", + "tslib": "^2.0.0" + } + }, "@angular/cli": { "version": "11.0.1", "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-11.0.1.tgz", @@ -3804,6 +3813,14 @@ "tslib": "^2.0.0" } }, + "@garygrossgarten/shortcodes": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@garygrossgarten/shortcodes/-/shortcodes-0.0.1.tgz", + "integrity": "sha512-eKERVbWO7c3PlK+w0MzJGieqbduuT+LF9GmNu4UGNtZDyWT5m4wKmNZ4pKu3zI/K31UQN51xzIcJn3CgrOccmQ==", + "requires": { + "tslib": "^2.0.0" + } + }, "@istanbuljs/schema": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", diff --git a/package.json b/package.json index 158661f0..08811f96 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "private": true, "dependencies": { "@angular/animations": "11.0.0", + "@angular/cdk": "^11.0.2", "@angular/common": "11.0.0", "@angular/compiler": "11.0.0", "@angular/core": "11.0.0", @@ -44,6 +45,7 @@ "@angular/platform-browser-dynamic": "11.0.0", "@angular/router": "11.0.0", "@angular/service-worker": "11.0.0", + "@garygrossgarten/shortcodes": "0.0.1", "@scullyio/init": "1.0.1", "@scullyio/ng-lib": "1.0.0", "@scullyio/scully": "1.0.5", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index b6320730..28892b6f 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -21,6 +21,7 @@ import { NewsletterSignupModule } from '@components/newsletter-signup/newsletter import { NizSearchComponentModule } from '@components/search/search.module'; import { PipesModule } from '@pipes/pipes.module'; import { NizFooterModule } from '@components/footer/footer.module'; +import { ShortcodeModule } from '@garygrossgarten/shortcodes'; @NgModule({ declarations: [AppComponent], @@ -35,7 +36,7 @@ import { NizFooterModule } from '@components/footer/footer.module'; MarkdownModule.forRoot({ loader: HttpClient }), NizTabsModule, NizTabModule, - NizFooterModule, + NizFooterModule, NizNavbarModule, NizToolbarModule, NizInlineSvgModule, @@ -43,7 +44,14 @@ import { NizFooterModule } from '@components/footer/footer.module'; NizSearchComponentModule, PipesModule, NizToastModule, - NizMenuModule + NizMenuModule, + ShortcodeModule.forRoot([ + { + path: 'scroll', + loadChildren: () => + import('./demos/scroll/scroll.module').then((d) => d.ScrollModule), + }, + ]), ], providers: [], bootstrap: [AppComponent], diff --git a/src/app/demos/scroll/scroll.module.ts b/src/app/demos/scroll/scroll.module.ts new file mode 100644 index 00000000..805548a2 --- /dev/null +++ b/src/app/demos/scroll/scroll.module.ts @@ -0,0 +1,26 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { ScrollComponent } from './scrolltop.component'; +import { ScrollContainerComponent } from './scrollcontainer.component'; +import { ShortcodeModule } from '@garygrossgarten/shortcodes'; +import { ScrollPipe } from './scroll.pipe'; +import { ScrollDirectionPipe } from './scrolldirection.pipe'; +import { NizInlineSvgModule } from '@notiz/ngx-design'; + +@NgModule({ + declarations: [ + ScrollComponent, + ScrollPipe, + ScrollContainerComponent, + ScrollDirectionPipe + ], + imports: [ + CommonModule, + NizInlineSvgModule, + ShortcodeModule.forChild([ + { path: '', component: ScrollComponent }, + { path: 'container', component: ScrollContainerComponent }, + ]), + ], +}) +export class ScrollModule {} diff --git a/src/app/demos/scroll/scroll.pipe.ts b/src/app/demos/scroll/scroll.pipe.ts new file mode 100644 index 00000000..5687b7cb --- /dev/null +++ b/src/app/demos/scroll/scroll.pipe.ts @@ -0,0 +1,13 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { Observable } from 'rxjs'; +import { scrollPercent$ } from './scroll'; + +@Pipe({ + name: 'scroll', + pure: true, +}) +export class ScrollPipe implements PipeTransform { + transform(el: HTMLElement): Observable { + return scrollPercent$(el); + } +} diff --git a/src/app/demos/scroll/scroll.ts b/src/app/demos/scroll/scroll.ts new file mode 100644 index 00000000..f680f59e --- /dev/null +++ b/src/app/demos/scroll/scroll.ts @@ -0,0 +1,39 @@ +import { fromEvent, merge, zip } from 'rxjs'; +import { + distinctUntilChanged, + filter, + map, + pairwise, + startWith, +} from 'rxjs/operators'; + +export const pageScroll$ = () => + fromEvent(window, 'scroll').pipe( + map(() => document.documentElement.scrollTop) + ); + +export const scrolltop$ = (el: Element) => + merge(fromEvent(el, 'scroll')).pipe( + startWith(el.scrollTop), + map(() => el.scrollTop) + ); + +export const scrollHeight$ = (el: Element) => + merge(fromEvent(el, 'scroll')).pipe( + startWith(el.scrollHeight), + map(() => el.scrollHeight) + ); + +export const scrollPercent$ = (el: Element) => + zip(scrolltop$(el), scrollHeight$(el)).pipe( + filter(([top, height]) => height - el.clientHeight !== 0), + map(([top, height]) => (100 * top) / (height - el.clientHeight)), + startWith(0) + ); + +export const scrolldir$ = (el: Element) => + scrolltop$(el).pipe( + pairwise(), + map(([v1, v2]) => (v1 > v2 ? 'UP' : 'DOWN')), + distinctUntilChanged() + ); diff --git a/src/app/demos/scroll/scrollcontainer.component.ts b/src/app/demos/scroll/scrollcontainer.component.ts new file mode 100644 index 00000000..90c00d0e --- /dev/null +++ b/src/app/demos/scroll/scrollcontainer.component.ts @@ -0,0 +1,28 @@ +import { Component, OnInit } from '@angular/core'; + +@Component({ + selector: 'niz-scroll', + template: `
+ {{ (el | scroll | async)?.toFixed() }}% + +
+
+
+
`, +}) +export class ScrollContainerComponent implements OnInit { + constructor() {} + + ngOnInit(): void {} +} diff --git a/src/app/demos/scroll/scrolldirection.pipe.ts b/src/app/demos/scroll/scrolldirection.pipe.ts new file mode 100644 index 00000000..283c8139 --- /dev/null +++ b/src/app/demos/scroll/scrolldirection.pipe.ts @@ -0,0 +1,13 @@ +import { Pipe, PipeTransform } from '@angular/core'; +import { Observable } from 'rxjs'; +import { scrolldir$ } from './scroll'; + +@Pipe({ + name: 'scrolldir', + pure: true, +}) +export class ScrollDirectionPipe implements PipeTransform { + transform(el: HTMLElement): Observable { + return scrolldir$(el); + } +} diff --git a/src/app/demos/scroll/scrolltop.component.ts b/src/app/demos/scroll/scrolltop.component.ts new file mode 100644 index 00000000..538c4a05 --- /dev/null +++ b/src/app/demos/scroll/scrolltop.component.ts @@ -0,0 +1,16 @@ +import { Component, OnInit } from '@angular/core'; +import { map } from 'rxjs/operators'; +import { pageScroll$ } from './scroll'; + +@Component({ + selector: 'niz-scroll', + template: ` + Page Scrolltop: {{scroll$ | async}} + `, +}) +export class ScrollComponent implements OnInit { + scroll$ = pageScroll$().pipe(map((v) => v.toFixed())); + constructor() {} + + ngOnInit(): void {} +} diff --git a/src/app/pages/blog-post/blog-post.component.html b/src/app/pages/blog-post/blog-post.component.html index f1131292..a17f973a 100644 --- a/src/app/pages/blog-post/blog-post.component.html +++ b/src/app/pages/blog-post/blog-post.component.html @@ -41,7 +41,7 @@

-
+
diff --git a/src/app/pages/blog-post/blog-post.module.ts b/src/app/pages/blog-post/blog-post.module.ts index f7c3d034..d7d2aadb 100644 --- a/src/app/pages/blog-post/blog-post.module.ts +++ b/src/app/pages/blog-post/blog-post.module.ts @@ -18,6 +18,7 @@ import { RouterModule } from '@angular/router'; import { PipesModule } from '@pipes/pipes.module'; import { SeoModule } from '@components/seo/seo.module'; +import { ShortcodeModule } from '@garygrossgarten/shortcodes'; @NgModule({ declarations: [BlogPostComponent], @@ -37,6 +38,7 @@ import { SeoModule } from '@components/seo/seo.module'; NizChipModule, PipesModule, SeoModule, + ShortcodeModule ], }) export class BlogPostModule {} diff --git a/src/assets/img/arrow_downward-black-18dp.svg b/src/assets/img/arrow_downward-black-18dp.svg new file mode 100644 index 00000000..9b934b4b --- /dev/null +++ b/src/assets/img/arrow_downward-black-18dp.svg @@ -0,0 +1 @@ + \ No newline at end of file