UNPKG

ngx-hide-on-scroll

Version:

Hide an element on scroll down or up in Angular.

172 lines (166 loc) 7.13 kB
import { EventEmitter, Directive, ElementRef, Renderer2, Inject, PLATFORM_ID, Input, Output, NgModule } from '@angular/core'; import { Subject, fromEvent } from 'rxjs'; import { takeUntil, throttleTime, map, pairwise, distinctUntilChanged, share, filter } from 'rxjs/operators'; import { isPlatformServer } from '@angular/common'; // Inspired by: https://netbasal.com/reactive-sticky-header-in-angular-12dbffb3f1d3 /** * The `ngxHideOnScroll` directive allows you to hide an html element (e.g. navbar) on scroll down and show it again on scroll up. */ class NgxHideOnScrollDirective { constructor(elementRef, renderer2, platformId) { this.elementRef = elementRef; this.renderer2 = renderer2; this.platformId = platformId; /** * `'Down'`: The element will be hidden on scroll down and it will be shown again on scroll up.<br/>`Up`: The element will be hidden on scroll up and it will be shown again on scroll down. */ this.hideOnScroll = 'Down'; /** * CSS class name added to the element to hide it. When this property is set, `propertyUsedToHide`, `valueWhenHidden`, and `valueWhenShown` have not effect. */ this.classNameWhenHidden = ''; /** * The CSS property used to hide/show the element. * * @default * 'transform' */ this.propertyUsedToHide = 'transform'; /** * The value of `propertyUsedToHide` when the element is hidden. * * @default * 'translateY(-100%)' */ this.valueWhenHidden = 'translateY(-100%)'; /** * The value of `propertyUsedToHide` when the element is shown. * * @default * 'translateY(0)' */ this.valueWhenShown = 'translateY(0)'; /** * The selector of the element you want to listen the scroll event, in case it is not the default browser scrolling element (`document.scrollingElement` or `document.documentElement`). For example [` .mat-sidenav-content`]( https://stackoverflow.com/a/52931772/12954396) if you are using [Angular Material Sidenav]( https://material.angular.io/components/sidenav) */ this.scrollingElementSelector = ''; /** * Emitted when the element is hidden. */ this.eventElementHidden = new EventEmitter(); /** * Emitted when the element is shown. */ this.eventElementShown = new EventEmitter(); this.unsubscribeNotifier = new Subject(); } ngAfterViewInit() { if (isPlatformServer(this.platformId)) { return; } let elementToListenScrollEvent; let scrollingElement; if (!this.scrollingElementSelector) { elementToListenScrollEvent = window; scrollingElement = this.getDefaultScrollingElement(); } else { scrollingElement = document.querySelector(this.scrollingElementSelector); if (!scrollingElement) { console.error(`NgxHideOnScroll: @Input() scrollingElementSelector\nElement with selector: "${this.scrollingElementSelector}" not found.`); return; } elementToListenScrollEvent = scrollingElement; } const scroll$ = fromEvent(elementToListenScrollEvent, 'scroll').pipe(takeUntil(this.unsubscribeNotifier), throttleTime(50), // only emit every 50 ms map(() => scrollingElement.scrollTop), // get vertical scroll position pairwise(), // look at this and the last emitted element // compare this and the last element to figure out scrolling direction map(([y1, y2]) => (y2 < y1 ? ScrollDirection.Up : ScrollDirection.Down)), distinctUntilChanged(), // only emit when scrolling direction changed share() // share a single subscription to the underlying sequence in case of multiple subscribers ); const scrollUp$ = scroll$.pipe(filter(direction => direction === ScrollDirection.Up)); const scrollDown$ = scroll$.pipe(filter(direction => direction === ScrollDirection.Down)); let scrollUpAction; let scrollDownAction; if (this.hideOnScroll === 'Up') { scrollUpAction = () => this.hideElement(); scrollDownAction = () => this.showElement(); } else { scrollUpAction = () => this.showElement(); scrollDownAction = () => this.hideElement(); } scrollUp$.subscribe(() => scrollUpAction()); scrollDown$.subscribe(() => scrollDownAction()); } ngOnDestroy() { this.unsubscribeNotifier.next(); this.unsubscribeNotifier.complete(); } hideElement() { const nativeElement = this.elementRef.nativeElement; if (this.classNameWhenHidden) { this.renderer2.addClass(nativeElement, this.classNameWhenHidden); } else { this.renderer2.setStyle(nativeElement, this.propertyUsedToHide, this.valueWhenHidden); } this.eventElementHidden.emit(); } showElement() { const nativeElement = this.elementRef.nativeElement; if (this.classNameWhenHidden) { this.renderer2.removeClass(nativeElement, this.classNameWhenHidden); } else { this.renderer2.setStyle(nativeElement, this.propertyUsedToHide, this.valueWhenShown); } this.eventElementShown.emit(); } getDefaultScrollingElement() { return (document.scrollingElement || document.documentElement); } } NgxHideOnScrollDirective.decorators = [ { type: Directive, args: [{ selector: '[ngxHideOnScroll]' },] } ]; NgxHideOnScrollDirective.ctorParameters = () => [ { type: ElementRef }, { type: Renderer2 }, { type: String, decorators: [{ type: Inject, args: [PLATFORM_ID,] }] } ]; NgxHideOnScrollDirective.propDecorators = { hideOnScroll: [{ type: Input }], classNameWhenHidden: [{ type: Input }], propertyUsedToHide: [{ type: Input }], valueWhenHidden: [{ type: Input }], valueWhenShown: [{ type: Input }], scrollingElementSelector: [{ type: Input }], eventElementHidden: [{ type: Output }], eventElementShown: [{ type: Output }] }; var ScrollDirection; (function (ScrollDirection) { ScrollDirection["Up"] = "Up"; ScrollDirection["Down"] = "Down"; })(ScrollDirection || (ScrollDirection = {})); class NgxHideOnScrollModule { } NgxHideOnScrollModule.decorators = [ { type: NgModule, args: [{ declarations: [NgxHideOnScrollDirective], imports: [], exports: [NgxHideOnScrollDirective] },] } ]; /* * Public API Surface of ngx-hide-on-scroll */ /** * Generated bundle index. Do not edit. */ export { NgxHideOnScrollDirective, NgxHideOnScrollModule }; //# sourceMappingURL=ngx-hide-on-scroll.js.map