@proangular/ngx-scroll-top
Version:
Configurable, lightweight back to top button for Angular projects.
239 lines (234 loc) • 11.8 kB
JavaScript
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