UNPKG

@proangular/ngx-scroll-top

Version:

Configurable, lightweight back to top button for Angular projects.

239 lines (234 loc) 11.8 kB
import { trigger, transition, style, animate } from '@angular/animations'; import * as i0 from '@angular/core'; import { HostListener, HostBinding, Input, Component } from '@angular/core'; class NgxScrollTopComponent { /** * Background color of the back to top button (hex string). * * Default: `'#0D58C0'` (dark-blue) * * Example: `'#000'` or `'black'` * * Tip: Define any `'x'` css property available for `'background-color: x'` */ backgroundColor = '#0D58C0'; /** * Offset `px` from bottom of page when scrolled to bottom. For example this * can be used to make sure the back to top button never overlaps a footer. * * Default: `'0px'` * * Example: `'250px'` or `250` because my footer is 250px in height */ bottomOffset = '0px'; /** * The back to top button will not be displayed until the user scrolls to the * provided Y (vertical `px`) coordinate on the page. * * Default: `'420px'` * * Example: `'100px'` or `100` */ displayAtYPosition = '420px'; /** * The font color for the nested content within the back to top button. * * Default: `'#FFFFFF'` (white) * * Example: `'#000'` or `'black'` * * Tip: Define any `'x'` css property available for `'color: x'` */ fontColor = '#FFFFFF'; /** * The font size for the nested content within the back to top button. * * Default: `'16px'` * * Example: `'2rem'` or `'32px'` * * Tip: Define any `'x'` css property available for `'font-size: x'` */ fontSize = '16px'; /** * Height of back to top button in string px format. * * Default: `'25px'` */ height = '40px'; /** * Position on-screen where the back to top button is displayed. * * Default: `'right'` */ position = 'right'; /** * Width of back to top button in string px format. * * Default: `'25px'` */ width = '40px'; /** * Style the `z-index` for the back to top button as needed for correct layer * height adjustment. This can be useful when working with sticky headers. * * Default: `999` */ zIndex = 999; isHidden = true; defaultPadding = '16px'; styleBottom = this.defaultPadding; styleLeft = 'unset'; styleRight = this.defaultPadding; onWindowScroll() { this.updateIsHidden(); this.updatePosition(); } ngOnInit() { switch (this.position) { case 'left': this.styleRight = 'unset'; this.styleLeft = this.defaultPadding; break; case 'right': default: this.styleRight = this.defaultPadding; this.styleLeft = 'unset'; break; } } scrollTop() { w.scroll({ top: 0, left: 0, behavior: 'smooth', }); } updatePosition() { const useDefaultPosition = () => { this.styleBottom = this.defaultPadding; }; if (this.isHidden) { useDefaultPosition(); return; } try { const { document, scrollY } = w; const { documentElement: docEl } = document; const bottomY = docEl.scrollHeight - docEl.clientHeight; const bottomOffset = parsePxStringToInt(this.bottomOffset); const distanceFromBottom = bottomY - scrollY; const halfHeight = parsePxStringToInt(this.height) / 2; const defaultPadding = parsePxStringToInt(this.defaultPadding); if (distanceFromBottom + (halfHeight - defaultPadding) < bottomOffset) { // Scroll up button exceeded bottom offset, update position. this.styleBottom = `${bottomOffset - distanceFromBottom + defaultPadding}px`; } else { useDefaultPosition(); } } catch (error) { console.warn('Failed to update back to top button position dynamically.', error); useDefaultPosition(); } } updateIsHidden() { const { scrollY } = w; const displayAtYPosition = parsePxStringToInt(this.displayAtYPosition); if (this.isHidden && scrollY > displayAtYPosition) { this.isHidden = false; } else if (!this.isHidden && scrollY <= displayAtYPosition) { this.isHidden = true; } } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: NgxScrollTopComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.3", type: NgxScrollTopComponent, isStandalone: true, selector: "ngx-scroll-top", inputs: { backgroundColor: "backgroundColor", bottomOffset: "bottomOffset", displayAtYPosition: "displayAtYPosition", fontColor: "fontColor", fontSize: "fontSize", height: "height", position: "position", width: "width", zIndex: "zIndex" }, host: { listeners: { "window:scroll": "onWindowScroll()" }, properties: { "style.color": "this.fontColor", "style.font-size": "this.fontSize", "style.height": "this.height", "style.width": "this.width", "style.z-index": "this.zIndex", "style.bottom": "this.styleBottom", "style.left": "this.styleLeft", "style.right": "this.styleRight" } }, ngImport: i0, template: "@if (!isHidden) {\n <button\n (click)=\"scrollTop()\"\n [style.background-color]=\"backgroundColor\"\n [@easeInOutAnimation]\n >\n <div class=\"content\" #content>\n <ng-content></ng-content>\n </div>\n @if (!content?.innerHTML?.length) {\n <svg\n class=\"default-content\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <desc>\n https://fonts.gstatic.com/s/i/materialicons/expand_less/v12/24px.svg\n </desc>\n <path d=\"M0 0h24v24H0z\" fill=\"none\" />\n <path d=\"M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z\" />\n </svg>\n }\n </button>\n}\n", styles: [":host{position:fixed;transition:all .1s ease-in-out}div.content{font-size:inherit;color:inherit;display:flex;flex-direction:column;flex-wrap:nowrap;align-content:center;justify-content:center;align-items:center;overflow:hidden;border-radius:50%}div.content,div.content>*{width:100%;height:100%;max-width:100%;max-height:100%}button{font-size:inherit;overflow:hidden;display:inline-block;position:relative;transition:all .1s ease-in-out;color:inherit;box-shadow:0 3px 5px -1px #0003,0 6px 10px #00000024,0 1px 18px #0000001f;-webkit-user-select:none;user-select:none;cursor:pointer;outline:none;border:none;box-sizing:border-box;white-space:nowrap;text-decoration:none;vertical-align:baseline;text-align:center;margin:0;border-radius:50%;padding:0;flex-shrink:0}button,button>svg{width:100%;height:100%}svg.default-content{position:absolute;top:0;left:0}\n"], animations: [ trigger('easeInOutAnimation', [ transition(':enter', [ style({ opacity: 0 }), animate('200ms', style({ opacity: 1 })), ]), transition(':leave', [ style({ opacity: 1 }), animate('200ms', style({ opacity: 0 })), ]), ]), ] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.3", ngImport: i0, type: NgxScrollTopComponent, decorators: [{ type: Component, args: [{ selector: 'ngx-scroll-top', animations: [ trigger('easeInOutAnimation', [ transition(':enter', [ style({ opacity: 0 }), animate('200ms', style({ opacity: 1 })), ]), transition(':leave', [ style({ opacity: 1 }), animate('200ms', style({ opacity: 0 })), ]), ]), ], imports: [], standalone: true, template: "@if (!isHidden) {\n <button\n (click)=\"scrollTop()\"\n [style.background-color]=\"backgroundColor\"\n [@easeInOutAnimation]\n >\n <div class=\"content\" #content>\n <ng-content></ng-content>\n </div>\n @if (!content?.innerHTML?.length) {\n <svg\n class=\"default-content\"\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"currentColor\"\n aria-hidden=\"true\"\n focusable=\"false\"\n >\n <desc>\n https://fonts.gstatic.com/s/i/materialicons/expand_less/v12/24px.svg\n </desc>\n <path d=\"M0 0h24v24H0z\" fill=\"none\" />\n <path d=\"M12 8l-6 6 1.41 1.41L12 10.83l4.59 4.58L18 14z\" />\n </svg>\n }\n </button>\n}\n", styles: [":host{position:fixed;transition:all .1s ease-in-out}div.content{font-size:inherit;color:inherit;display:flex;flex-direction:column;flex-wrap:nowrap;align-content:center;justify-content:center;align-items:center;overflow:hidden;border-radius:50%}div.content,div.content>*{width:100%;height:100%;max-width:100%;max-height:100%}button{font-size:inherit;overflow:hidden;display:inline-block;position:relative;transition:all .1s ease-in-out;color:inherit;box-shadow:0 3px 5px -1px #0003,0 6px 10px #00000024,0 1px 18px #0000001f;-webkit-user-select:none;user-select:none;cursor:pointer;outline:none;border:none;box-sizing:border-box;white-space:nowrap;text-decoration:none;vertical-align:baseline;text-align:center;margin:0;border-radius:50%;padding:0;flex-shrink:0}button,button>svg{width:100%;height:100%}svg.default-content{position:absolute;top:0;left:0}\n"] }] }], propDecorators: { backgroundColor: [{ type: Input }], bottomOffset: [{ type: Input }], displayAtYPosition: [{ type: Input }], fontColor: [{ type: Input }, { type: HostBinding, args: ['style.color'] }], fontSize: [{ type: Input }, { type: HostBinding, args: ['style.font-size'] }], height: [{ type: Input }, { type: HostBinding, args: ['style.height'] }], position: [{ type: Input }], width: [{ type: Input }, { type: HostBinding, args: ['style.width'] }], zIndex: [{ type: Input }, { type: HostBinding, args: ['style.z-index'] }], styleBottom: [{ type: HostBinding, args: ['style.bottom'] }], styleLeft: [{ type: HostBinding, args: ['style.left'] }], styleRight: [{ type: HostBinding, args: ['style.right'] }], onWindowScroll: [{ type: HostListener, args: ['window:scroll', []] }] } }); const w = window; function parsePxStringToInt(value) { try { return parseInt(value.toString(), 10); } catch (error) { throw new Error(`Failed to parse value "${value}" with error: ${String(error)}`); } } // Components /** * Generated bundle index. Do not edit. */ export { NgxScrollTopComponent }; //# sourceMappingURL=proangular-ngx-scroll-top.mjs.map