UNPKG

ngx-fixed-footer

Version:

Angular directive that adds fixed footer without overlap

129 lines (122 loc) 6.04 kB
import * as i0 from '@angular/core'; import { InjectionToken, inject, DOCUMENT, ElementRef, Renderer2, signal, input, computed, effect, Directive } from '@angular/core'; const DEFAULT_CSS_ATTRIBUTE = 'padding'; const DEFAULT_CONTAINER_SELECTOR = '[role="main"]'; const DEFAULT_FIXED_FOOTER_OPTIONS = { containerSelector: DEFAULT_CONTAINER_SELECTOR, cssAttribute: DEFAULT_CSS_ATTRIBUTE }; const APP_FIXED_FOOTER_OPTIONS_TOKEN = new InjectionToken('[ngxFixedFooter] Options'); const provideFixedFooter = (options) => { return { provide: APP_FIXED_FOOTER_OPTIONS_TOKEN, useValue: { cssAttribute: options.cssAttribute || DEFAULT_CSS_ATTRIBUTE, containerSelector: options.containerSelector || DEFAULT_CONTAINER_SELECTOR } }; }; class NgxFixedFooterDirective { document = inject(DOCUMENT); el = inject(ElementRef); render = inject(Renderer2); options = inject(APP_FIXED_FOOTER_OPTIONS_TOKEN, { optional: true }) || DEFAULT_FIXED_FOOTER_OPTIONS; hasResizeObserver = typeof ResizeObserver !== 'undefined'; resizeObserver; offsetHeight = signal(undefined, ...(ngDevMode ? [{ debugName: "offsetHeight" }] : [])); prevContainerSelector = signal(undefined, ...(ngDevMode ? [{ debugName: "prevContainerSelector" }] : [])); containerSelector = input(this.options.containerSelector, ...(ngDevMode ? [{ debugName: "containerSelector" }] : [])); cssAttribute = input(this.options.cssAttribute, ...(ngDevMode ? [{ debugName: "cssAttribute" }] : [])); container = computed(() => { const selector = this.containerSelector() || this.options.containerSelector; return this.document.body.querySelector(selector); }, ...(ngDevMode ? [{ debugName: "container" }] : [])); constructor() { // swap selector effect(() => { if (!this.hasResizeObserver || !this.document) return; const containerSelector = this.containerSelector(); const offsetHeight = this.offsetHeight(); if (!containerSelector || typeof offsetHeight !== 'number') return; const cssAttribute = this.cssAttribute(); const prevContainerSelector = this.prevContainerSelector(); if (prevContainerSelector && prevContainerSelector !== containerSelector) { const prevContainer = this.document.body.querySelector(prevContainerSelector); if (prevContainer) { this.removeStyle(prevContainer, cssAttribute); } } const container = this.document.body.querySelector(containerSelector); if (container) { this.setStyle(container, cssAttribute, offsetHeight); this.prevContainerSelector.set(containerSelector); } }); // swap css attribute effect(() => { if (!this.hasResizeObserver || !this.document) return; const container = this.container(); const offsetHeight = this.offsetHeight(); if (!container || typeof offsetHeight !== 'number') return; const cssAttribute = this.cssAttribute(); this.removeStyle(container, cssAttribute === 'padding' ? 'margin' : 'padding'); this.setStyle(container, cssAttribute, offsetHeight); }); } ngOnInit() { if (this.hasResizeObserver && this.document) { this.resizeObserver = new ResizeObserver(() => this.checkHeight()); this.resizeObserver.observe(this.html); } } ngOnDestroy() { const container = this.container(); if (this.resizeObserver && this.document && container) { this.removeStyle(container, this.cssAttribute()); this.resizeObserver.unobserve(this.html); } } checkHeight() { const height = this.html.offsetHeight; const container = this.container(); if (this.offsetHeight() !== height && container) { this.setStyle(container, this.cssAttribute(), height); this.offsetHeight.set(height); } } removeStyle(container, cssAttribute) { if (!container) { throw new Error(`Cannot removeStyle to undefined container`); } this.render.setStyle(container, `${cssAttribute}-bottom`, ''); } setStyle(container, cssAttribute, height) { if (!container) { throw new Error(`Cannot setStyle to undefined container`); } this.render.setStyle(container, `${cssAttribute}-bottom`, height === 0 ? '' : `${height}px`); } get html() { return this.el.nativeElement; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NgxFixedFooterDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "20.1.7", type: NgxFixedFooterDirective, isStandalone: true, selector: "[ngxFixedFooter]", inputs: { containerSelector: { classPropertyName: "containerSelector", publicName: "containerSelector", isSignal: true, isRequired: false, transformFunction: null }, cssAttribute: { classPropertyName: "cssAttribute", publicName: "cssAttribute", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0 }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: NgxFixedFooterDirective, decorators: [{ type: Directive, args: [{ selector: '[ngxFixedFooter]' }] }], ctorParameters: () => [] }); /* * Public API Surface of ngx-fixed-footer */ /** * Generated bundle index. Do not edit. */ export { APP_FIXED_FOOTER_OPTIONS_TOKEN, DEFAULT_CONTAINER_SELECTOR, DEFAULT_CSS_ATTRIBUTE, DEFAULT_FIXED_FOOTER_OPTIONS, NgxFixedFooterDirective, provideFixedFooter }; //# sourceMappingURL=ngx-fixed-footer.mjs.map